]> git.earman.xyz Git - sensor-watch.git/commitdiff
'movement' -> 'legacy' to signal things we still need to bring in
authorJoey Castillo <joeycastillo@utexas.edu>
Wed, 27 Nov 2024 15:56:50 +0000 (10:56 -0500)
committerJoey Castillo <joeycastillo@utexas.edu>
Wed, 27 Nov 2024 15:56:50 +0000 (10:56 -0500)
441 files changed:
Makefile
legacy/alt_fw/alt_time.h [new file with mode: 0644]
legacy/alt_fw/backer.h [new file with mode: 0644]
legacy/alt_fw/deep_space_now.h [new file with mode: 0644]
legacy/alt_fw/focus.h [new file with mode: 0644]
legacy/alt_fw/the_athlete.h [new file with mode: 0644]
legacy/alt_fw/the_backpacker.h [new file with mode: 0644]
legacy/alt_fw/the_stargazer.h [new file with mode: 0644]
legacy/alt_fw/timers.h [new file with mode: 0644]
legacy/lib/TOTP/LICENSE [new file with mode: 0644]
legacy/lib/TOTP/README.md [new file with mode: 0644]
legacy/lib/TOTP/TOTP.c [new file with mode: 0644]
legacy/lib/TOTP/TOTP.h [new file with mode: 0644]
legacy/lib/TOTP/example.c [new file with mode: 0644]
legacy/lib/TOTP/sha1.c [new file with mode: 0644]
legacy/lib/TOTP/sha1.h [new file with mode: 0644]
legacy/lib/TOTP/sha256.c [new file with mode: 0644]
legacy/lib/TOTP/sha256.h [new file with mode: 0644]
legacy/lib/TOTP/sha512.c [new file with mode: 0644]
legacy/lib/TOTP/sha512.h [new file with mode: 0644]
legacy/lib/astrolib/LICENSE.txt [new file with mode: 0755]
legacy/lib/astrolib/README.md [new file with mode: 0644]
legacy/lib/astrolib/astrolib.c [new file with mode: 0644]
legacy/lib/astrolib/astrolib.h [new file with mode: 0644]
legacy/lib/base32/base32.c [new file with mode: 0644]
legacy/lib/base32/base32.h [new file with mode: 0644]
legacy/lib/morsecalc/calc.c [new file with mode: 0644]
legacy/lib/morsecalc/calc.h [new file with mode: 0644]
legacy/lib/morsecalc/calc_fns.c [new file with mode: 0644]
legacy/lib/morsecalc/calc_fns.h [new file with mode: 0644]
legacy/lib/morsecalc/calc_strtof.c [new file with mode: 0644]
legacy/lib/morsecalc/morsecalc_display.c [new file with mode: 0644]
legacy/lib/morsecalc/morsecalc_display.h [new file with mode: 0644]
legacy/lib/morsecalc/test_morsecalc.c [new file with mode: 0644]
legacy/lib/smallchesslib/smallchesslib.h [new file with mode: 0644]
legacy/lib/vsop87/LICENSE.txt [new file with mode: 0755]
legacy/lib/vsop87/README.md [new file with mode: 0755]
legacy/lib/vsop87/vsop87a_micro.c [new file with mode: 0755]
legacy/lib/vsop87/vsop87a_micro.h [new file with mode: 0755]
legacy/lib/vsop87/vsop87a_milli.c [new file with mode: 0755]
legacy/lib/vsop87/vsop87a_milli.h [new file with mode: 0755]
legacy/movement_faces.h [new file with mode: 0644]
legacy/watch_faces/clock/close_enough_clock_face.c [new file with mode: 0644]
legacy/watch_faces/clock/close_enough_clock_face.h [new file with mode: 0644]
legacy/watch_faces/clock/day_night_percentage_face.c [new file with mode: 0644]
legacy/watch_faces/clock/day_night_percentage_face.h [new file with mode: 0644]
legacy/watch_faces/clock/decimal_time_face.c [new file with mode: 0644]
legacy/watch_faces/clock/decimal_time_face.h [new file with mode: 0644]
legacy/watch_faces/clock/french_revolutionary_face.c [new file with mode: 0644]
legacy/watch_faces/clock/french_revolutionary_face.h [new file with mode: 0644]
legacy/watch_faces/clock/mars_time_face.c [new file with mode: 0644]
legacy/watch_faces/clock/mars_time_face.h [new file with mode: 0644]
legacy/watch_faces/clock/minimal_clock_face.c [new file with mode: 0644]
legacy/watch_faces/clock/minimal_clock_face.h [new file with mode: 0644]
legacy/watch_faces/clock/minute_repeater_decimal_face.c [new file with mode: 0644]
legacy/watch_faces/clock/minute_repeater_decimal_face.h [new file with mode: 0644]
legacy/watch_faces/clock/repetition_minute_face.c [new file with mode: 0644]
legacy/watch_faces/clock/repetition_minute_face.h [new file with mode: 0644]
legacy/watch_faces/clock/simple_clock_bin_led_face.c [new file with mode: 0644]
legacy/watch_faces/clock/simple_clock_bin_led_face.h [new file with mode: 0644]
legacy/watch_faces/clock/weeknumber_clock_face.c [new file with mode: 0644]
legacy/watch_faces/clock/weeknumber_clock_face.h [new file with mode: 0644]
legacy/watch_faces/clock/world_clock2_face.c [new file with mode: 0644]
legacy/watch_faces/clock/world_clock2_face.h [new file with mode: 0644]
legacy/watch_faces/clock/wyoscan_face.c [new file with mode: 0644]
legacy/watch_faces/clock/wyoscan_face.h [new file with mode: 0644]
legacy/watch_faces/complication/activity_face.c [new file with mode: 0644]
legacy/watch_faces/complication/activity_face.h [new file with mode: 0644]
legacy/watch_faces/complication/astronomy_face.c [new file with mode: 0644]
legacy/watch_faces/complication/astronomy_face.h [new file with mode: 0644]
legacy/watch_faces/complication/blinky_face.c [new file with mode: 0644]
legacy/watch_faces/complication/blinky_face.h [new file with mode: 0644]
legacy/watch_faces/complication/breathing_face.c [new file with mode: 0644]
legacy/watch_faces/complication/breathing_face.h [new file with mode: 0644]
legacy/watch_faces/complication/butterfly_game_face.c [new file with mode: 0644]
legacy/watch_faces/complication/butterfly_game_face.h [new file with mode: 0644]
legacy/watch_faces/complication/couch_to_5k_face.c [new file with mode: 0644]
legacy/watch_faces/complication/couch_to_5k_face.h [new file with mode: 0644]
legacy/watch_faces/complication/counter_face.c [new file with mode: 0644]
legacy/watch_faces/complication/counter_face.h [new file with mode: 0644]
legacy/watch_faces/complication/databank_face.c [new file with mode: 0644]
legacy/watch_faces/complication/databank_face.h [new file with mode: 0644]
legacy/watch_faces/complication/day_one_face.c [new file with mode: 0644]
legacy/watch_faces/complication/day_one_face.h [new file with mode: 0644]
legacy/watch_faces/complication/deadline_face.c [new file with mode: 0644]
legacy/watch_faces/complication/deadline_face.h [new file with mode: 0644]
legacy/watch_faces/complication/discgolf_face.c [new file with mode: 0644]
legacy/watch_faces/complication/discgolf_face.h [new file with mode: 0644]
legacy/watch_faces/complication/dual_timer_face.c [new file with mode: 0644]
legacy/watch_faces/complication/dual_timer_face.h [new file with mode: 0644]
legacy/watch_faces/complication/endless_runner_face.c [new file with mode: 0644]
legacy/watch_faces/complication/endless_runner_face.h [new file with mode: 0644]
legacy/watch_faces/complication/flashlight_face.c [new file with mode: 0644]
legacy/watch_faces/complication/flashlight_face.h [new file with mode: 0644]
legacy/watch_faces/complication/geomancy_face.c [new file with mode: 0644]
legacy/watch_faces/complication/geomancy_face.h [new file with mode: 0644]
legacy/watch_faces/complication/habit_face.c [new file with mode: 0644]
legacy/watch_faces/complication/habit_face.h [new file with mode: 0644]
legacy/watch_faces/complication/higher_lower_game_face.c [new file with mode: 0755]
legacy/watch_faces/complication/higher_lower_game_face.h [new file with mode: 0755]
legacy/watch_faces/complication/interval_face.c [new file with mode: 0644]
legacy/watch_faces/complication/interval_face.h [new file with mode: 0644]
legacy/watch_faces/complication/invaders_face.c [new file with mode: 0644]
legacy/watch_faces/complication/invaders_face.h [new file with mode: 0644]
legacy/watch_faces/complication/kitchen_conversions_face.c [new file with mode: 0644]
legacy/watch_faces/complication/kitchen_conversions_face.h [new file with mode: 0644]
legacy/watch_faces/complication/menstrual_cycle_face.c [new file with mode: 0644]
legacy/watch_faces/complication/menstrual_cycle_face.h [new file with mode: 0644]
legacy/watch_faces/complication/metronome_face.c [new file with mode: 0644]
legacy/watch_faces/complication/metronome_face.h [new file with mode: 0644]
legacy/watch_faces/complication/moon_phase_face.c [new file with mode: 0644]
legacy/watch_faces/complication/moon_phase_face.h [new file with mode: 0644]
legacy/watch_faces/complication/morsecalc_face.c [new file with mode: 0644]
legacy/watch_faces/complication/morsecalc_face.h [new file with mode: 0644]
legacy/watch_faces/complication/orrery_face.c [new file with mode: 0644]
legacy/watch_faces/complication/orrery_face.h [new file with mode: 0644]
legacy/watch_faces/complication/periodic_face.c [new file with mode: 0644]
legacy/watch_faces/complication/periodic_face.h [new file with mode: 0644]
legacy/watch_faces/complication/planetary_hours_face.c [new file with mode: 0644]
legacy/watch_faces/complication/planetary_hours_face.h [new file with mode: 0644]
legacy/watch_faces/complication/planetary_time_face.c [new file with mode: 0644]
legacy/watch_faces/complication/planetary_time_face.h [new file with mode: 0644]
legacy/watch_faces/complication/probability_face.c [new file with mode: 0644]
legacy/watch_faces/complication/probability_face.h [new file with mode: 0644]
legacy/watch_faces/complication/pulsometer_face.c [new file with mode: 0644]
legacy/watch_faces/complication/pulsometer_face.h [new file with mode: 0644]
legacy/watch_faces/complication/randonaut_face.c [new file with mode: 0644]
legacy/watch_faces/complication/randonaut_face.h [new file with mode: 0644]
legacy/watch_faces/complication/ratemeter_face.c [new file with mode: 0644]
legacy/watch_faces/complication/ratemeter_face.h [new file with mode: 0644]
legacy/watch_faces/complication/rpn_calculator_alt_face.c [new file with mode: 0644]
legacy/watch_faces/complication/rpn_calculator_alt_face.h [new file with mode: 0644]
legacy/watch_faces/complication/rpn_calculator_face.c [new file with mode: 0644]
legacy/watch_faces/complication/rpn_calculator_face.h [new file with mode: 0644]
legacy/watch_faces/complication/sailing_face.c [new file with mode: 0644]
legacy/watch_faces/complication/sailing_face.h [new file with mode: 0644]
legacy/watch_faces/complication/ships_bell_face.c [new file with mode: 0644]
legacy/watch_faces/complication/ships_bell_face.h [new file with mode: 0644]
legacy/watch_faces/complication/simon_face.c [new file with mode: 0644]
legacy/watch_faces/complication/simon_face.h [new file with mode: 0644]
legacy/watch_faces/complication/simple_calculator_face.c [new file with mode: 0644]
legacy/watch_faces/complication/simple_calculator_face.h [new file with mode: 0644]
legacy/watch_faces/complication/simple_coin_flip_face.c [new file with mode: 0644]
legacy/watch_faces/complication/simple_coin_flip_face.h [new file with mode: 0644]
legacy/watch_faces/complication/smallchess_face.c [new file with mode: 0644]
legacy/watch_faces/complication/smallchess_face.h [new file with mode: 0644]
legacy/watch_faces/complication/solstice_face.c [new file with mode: 0644]
legacy/watch_faces/complication/solstice_face.h [new file with mode: 0644]
legacy/watch_faces/complication/stopwatch_face.c [new file with mode: 0644]
legacy/watch_faces/complication/stopwatch_face.h [new file with mode: 0644]
legacy/watch_faces/complication/tachymeter_face.c [new file with mode: 0644]
legacy/watch_faces/complication/tachymeter_face.h [new file with mode: 0644]
legacy/watch_faces/complication/tally_face.c [new file with mode: 0644]
legacy/watch_faces/complication/tally_face.h [new file with mode: 0644]
legacy/watch_faces/complication/tarot_face.c [new file with mode: 0644]
legacy/watch_faces/complication/tarot_face.h [new file with mode: 0644]
legacy/watch_faces/complication/tempchart_face.c [new file with mode: 0644]
legacy/watch_faces/complication/tempchart_face.h [new file with mode: 0644]
legacy/watch_faces/complication/time_left_face.c [new file with mode: 0644]
legacy/watch_faces/complication/time_left_face.h [new file with mode: 0644]
legacy/watch_faces/complication/timer_face.c [new file with mode: 0644]
legacy/watch_faces/complication/timer_face.h [new file with mode: 0644]
legacy/watch_faces/complication/tomato_face.c [new file with mode: 0644]
legacy/watch_faces/complication/tomato_face.h [new file with mode: 0644]
legacy/watch_faces/complication/toss_up_face.c [new file with mode: 0644]
legacy/watch_faces/complication/toss_up_face.h [new file with mode: 0644]
legacy/watch_faces/complication/totp_face.c [new file with mode: 0644]
legacy/watch_faces/complication/totp_face.h [new file with mode: 0644]
legacy/watch_faces/complication/totp_face_lfs.c [new file with mode: 0644]
legacy/watch_faces/complication/totp_face_lfs.h [new file with mode: 0644]
legacy/watch_faces/complication/tuning_tones_face.c [new file with mode: 0644]
legacy/watch_faces/complication/tuning_tones_face.h [new file with mode: 0644]
legacy/watch_faces/complication/wake_face.c [new file with mode: 0644]
legacy/watch_faces/complication/wake_face.h [new file with mode: 0644]
legacy/watch_faces/complication/wareki_face.c [new file with mode: 0644]
legacy/watch_faces/complication/wareki_face.h [new file with mode: 0644]
legacy/watch_faces/complication/wordle_face.c [new file with mode: 0644]
legacy/watch_faces/complication/wordle_face.h [new file with mode: 0644]
legacy/watch_faces/complication/wordle_face_dict.h [new file with mode: 0644]
legacy/watch_faces/demo/beeps_face.c [new file with mode: 0644]
legacy/watch_faces/demo/beeps_face.h [new file with mode: 0644]
legacy/watch_faces/demo/demo_face.c [new file with mode: 0644]
legacy/watch_faces/demo/demo_face.h [new file with mode: 0644]
legacy/watch_faces/demo/frequency_correction_face.c [new file with mode: 0644]
legacy/watch_faces/demo/frequency_correction_face.h [new file with mode: 0644]
legacy/watch_faces/demo/hello_there_face.c [new file with mode: 0644]
legacy/watch_faces/demo/hello_there_face.h [new file with mode: 0644]
legacy/watch_faces/demo/lis2dw_logging_face.c [new file with mode: 0644]
legacy/watch_faces/demo/lis2dw_logging_face.h [new file with mode: 0644]
legacy/watch_faces/sensor/accelerometer_data_acquisition_face.c [new file with mode: 0644]
legacy/watch_faces/sensor/accelerometer_data_acquisition_face.h [new file with mode: 0644]
legacy/watch_faces/sensor/alarm_thermometer_face.c [new file with mode: 0644]
legacy/watch_faces/sensor/alarm_thermometer_face.h [new file with mode: 0644]
legacy/watch_faces/sensor/lightmeter_face.c [new file with mode: 0644]
legacy/watch_faces/sensor/lightmeter_face.h [new file with mode: 0644]
legacy/watch_faces/sensor/minmax_face.c [new file with mode: 0644]
legacy/watch_faces/sensor/minmax_face.h [new file with mode: 0644]
legacy/watch_faces/sensor/thermistor_testing_face.c [new file with mode: 0644]
legacy/watch_faces/sensor/thermistor_testing_face.h [new file with mode: 0644]
legacy/watch_faces/settings/finetune_face.c [new file with mode: 0644]
legacy/watch_faces/settings/finetune_face.h [new file with mode: 0644]
legacy/watch_faces/settings/nanosec_face.c [new file with mode: 0644]
legacy/watch_faces/settings/nanosec_face.h [new file with mode: 0644]
legacy/watch_faces/settings/place_face.h [new file with mode: 0644]
legacy/watch_faces/settings/save_load_face.c [new file with mode: 0644]
legacy/watch_faces/settings/save_load_face.h [new file with mode: 0644]
legacy/watch_faces/settings/set_time_hackwatch_face.c [new file with mode: 0644]
legacy/watch_faces/settings/set_time_hackwatch_face.h [new file with mode: 0644]
lib/base64/.gitignore [new file with mode: 0644]
lib/base64/LICENSE [new file with mode: 0644]
lib/base64/README.md [new file with mode: 0644]
lib/base64/base64.c [new file with mode: 0644]
lib/base64/base64.h [new file with mode: 0644]
lib/chirpy_tx/chirpy_tx.c [new file with mode: 0644]
lib/chirpy_tx/chirpy_tx.h [new file with mode: 0644]
lib/chirpy_tx/test/test_main.c [new file with mode: 0644]
lib/chirpy_tx/test/unity.c [new file with mode: 0644]
lib/chirpy_tx/test/unity.h [new file with mode: 0644]
lib/chirpy_tx/test/unity_internals.h [new file with mode: 0644]
lib/sunriset/sunriset.c [new file with mode: 0644]
lib/sunriset/sunriset.h [new file with mode: 0644]
movement/alt_fw/alt_time.h [deleted file]
movement/alt_fw/backer.h [deleted file]
movement/alt_fw/deep_space_now.h [deleted file]
movement/alt_fw/focus.h [deleted file]
movement/alt_fw/the_athlete.h [deleted file]
movement/alt_fw/the_backpacker.h [deleted file]
movement/alt_fw/the_stargazer.h [deleted file]
movement/alt_fw/timers.h [deleted file]
movement/lib/TOTP/LICENSE [deleted file]
movement/lib/TOTP/README.md [deleted file]
movement/lib/TOTP/TOTP.c [deleted file]
movement/lib/TOTP/TOTP.h [deleted file]
movement/lib/TOTP/example.c [deleted file]
movement/lib/TOTP/sha1.c [deleted file]
movement/lib/TOTP/sha1.h [deleted file]
movement/lib/TOTP/sha256.c [deleted file]
movement/lib/TOTP/sha256.h [deleted file]
movement/lib/TOTP/sha512.c [deleted file]
movement/lib/TOTP/sha512.h [deleted file]
movement/lib/astrolib/LICENSE.txt [deleted file]
movement/lib/astrolib/README.md [deleted file]
movement/lib/astrolib/astrolib.c [deleted file]
movement/lib/astrolib/astrolib.h [deleted file]
movement/lib/base32/base32.c [deleted file]
movement/lib/base32/base32.h [deleted file]
movement/lib/base64/.gitignore [deleted file]
movement/lib/base64/LICENSE [deleted file]
movement/lib/base64/README.md [deleted file]
movement/lib/base64/base64.c [deleted file]
movement/lib/base64/base64.h [deleted file]
movement/lib/chirpy_tx/chirpy_tx.c [deleted file]
movement/lib/chirpy_tx/chirpy_tx.h [deleted file]
movement/lib/chirpy_tx/test/test_main.c [deleted file]
movement/lib/chirpy_tx/test/unity.c [deleted file]
movement/lib/chirpy_tx/test/unity.h [deleted file]
movement/lib/chirpy_tx/test/unity_internals.h [deleted file]
movement/lib/morsecalc/calc.c [deleted file]
movement/lib/morsecalc/calc.h [deleted file]
movement/lib/morsecalc/calc_fns.c [deleted file]
movement/lib/morsecalc/calc_fns.h [deleted file]
movement/lib/morsecalc/calc_strtof.c [deleted file]
movement/lib/morsecalc/morsecalc_display.c [deleted file]
movement/lib/morsecalc/morsecalc_display.h [deleted file]
movement/lib/morsecalc/test_morsecalc.c [deleted file]
movement/lib/smallchesslib/smallchesslib.h [deleted file]
movement/lib/sunriset/sunriset.c [deleted file]
movement/lib/sunriset/sunriset.h [deleted file]
movement/lib/vsop87/LICENSE.txt [deleted file]
movement/lib/vsop87/README.md [deleted file]
movement/lib/vsop87/vsop87a_micro.c [deleted file]
movement/lib/vsop87/vsop87a_micro.h [deleted file]
movement/lib/vsop87/vsop87a_milli.c [deleted file]
movement/lib/vsop87/vsop87a_milli.h [deleted file]
movement/movement_faces.h [deleted file]
movement/watch_faces/clock/close_enough_clock_face.c [deleted file]
movement/watch_faces/clock/close_enough_clock_face.h [deleted file]
movement/watch_faces/clock/day_night_percentage_face.c [deleted file]
movement/watch_faces/clock/day_night_percentage_face.h [deleted file]
movement/watch_faces/clock/decimal_time_face.c [deleted file]
movement/watch_faces/clock/decimal_time_face.h [deleted file]
movement/watch_faces/clock/french_revolutionary_face.c [deleted file]
movement/watch_faces/clock/french_revolutionary_face.h [deleted file]
movement/watch_faces/clock/mars_time_face.c [deleted file]
movement/watch_faces/clock/mars_time_face.h [deleted file]
movement/watch_faces/clock/minimal_clock_face.c [deleted file]
movement/watch_faces/clock/minimal_clock_face.h [deleted file]
movement/watch_faces/clock/minute_repeater_decimal_face.c [deleted file]
movement/watch_faces/clock/minute_repeater_decimal_face.h [deleted file]
movement/watch_faces/clock/repetition_minute_face.c [deleted file]
movement/watch_faces/clock/repetition_minute_face.h [deleted file]
movement/watch_faces/clock/simple_clock_bin_led_face.c [deleted file]
movement/watch_faces/clock/simple_clock_bin_led_face.h [deleted file]
movement/watch_faces/clock/weeknumber_clock_face.c [deleted file]
movement/watch_faces/clock/weeknumber_clock_face.h [deleted file]
movement/watch_faces/clock/world_clock2_face.c [deleted file]
movement/watch_faces/clock/world_clock2_face.h [deleted file]
movement/watch_faces/clock/wyoscan_face.c [deleted file]
movement/watch_faces/clock/wyoscan_face.h [deleted file]
movement/watch_faces/complication/activity_face.c [deleted file]
movement/watch_faces/complication/activity_face.h [deleted file]
movement/watch_faces/complication/astronomy_face.c [deleted file]
movement/watch_faces/complication/astronomy_face.h [deleted file]
movement/watch_faces/complication/blinky_face.c [deleted file]
movement/watch_faces/complication/blinky_face.h [deleted file]
movement/watch_faces/complication/breathing_face.c [deleted file]
movement/watch_faces/complication/breathing_face.h [deleted file]
movement/watch_faces/complication/butterfly_game_face.c [deleted file]
movement/watch_faces/complication/butterfly_game_face.h [deleted file]
movement/watch_faces/complication/couch_to_5k_face.c [deleted file]
movement/watch_faces/complication/couch_to_5k_face.h [deleted file]
movement/watch_faces/complication/counter_face.c [deleted file]
movement/watch_faces/complication/counter_face.h [deleted file]
movement/watch_faces/complication/databank_face.c [deleted file]
movement/watch_faces/complication/databank_face.h [deleted file]
movement/watch_faces/complication/day_one_face.c [deleted file]
movement/watch_faces/complication/day_one_face.h [deleted file]
movement/watch_faces/complication/deadline_face.c [deleted file]
movement/watch_faces/complication/deadline_face.h [deleted file]
movement/watch_faces/complication/discgolf_face.c [deleted file]
movement/watch_faces/complication/discgolf_face.h [deleted file]
movement/watch_faces/complication/dual_timer_face.c [deleted file]
movement/watch_faces/complication/dual_timer_face.h [deleted file]
movement/watch_faces/complication/endless_runner_face.c [deleted file]
movement/watch_faces/complication/endless_runner_face.h [deleted file]
movement/watch_faces/complication/flashlight_face.c [deleted file]
movement/watch_faces/complication/flashlight_face.h [deleted file]
movement/watch_faces/complication/geomancy_face.c [deleted file]
movement/watch_faces/complication/geomancy_face.h [deleted file]
movement/watch_faces/complication/habit_face.c [deleted file]
movement/watch_faces/complication/habit_face.h [deleted file]
movement/watch_faces/complication/higher_lower_game_face.c [deleted file]
movement/watch_faces/complication/higher_lower_game_face.h [deleted file]
movement/watch_faces/complication/interval_face.c [deleted file]
movement/watch_faces/complication/interval_face.h [deleted file]
movement/watch_faces/complication/invaders_face.c [deleted file]
movement/watch_faces/complication/invaders_face.h [deleted file]
movement/watch_faces/complication/kitchen_conversions_face.c [deleted file]
movement/watch_faces/complication/kitchen_conversions_face.h [deleted file]
movement/watch_faces/complication/menstrual_cycle_face.c [deleted file]
movement/watch_faces/complication/menstrual_cycle_face.h [deleted file]
movement/watch_faces/complication/metronome_face.c [deleted file]
movement/watch_faces/complication/metronome_face.h [deleted file]
movement/watch_faces/complication/moon_phase_face.c [deleted file]
movement/watch_faces/complication/moon_phase_face.h [deleted file]
movement/watch_faces/complication/morsecalc_face.c [deleted file]
movement/watch_faces/complication/morsecalc_face.h [deleted file]
movement/watch_faces/complication/orrery_face.c [deleted file]
movement/watch_faces/complication/orrery_face.h [deleted file]
movement/watch_faces/complication/periodic_face.c [deleted file]
movement/watch_faces/complication/periodic_face.h [deleted file]
movement/watch_faces/complication/planetary_hours_face.c [deleted file]
movement/watch_faces/complication/planetary_hours_face.h [deleted file]
movement/watch_faces/complication/planetary_time_face.c [deleted file]
movement/watch_faces/complication/planetary_time_face.h [deleted file]
movement/watch_faces/complication/probability_face.c [deleted file]
movement/watch_faces/complication/probability_face.h [deleted file]
movement/watch_faces/complication/pulsometer_face.c [deleted file]
movement/watch_faces/complication/pulsometer_face.h [deleted file]
movement/watch_faces/complication/randonaut_face.c [deleted file]
movement/watch_faces/complication/randonaut_face.h [deleted file]
movement/watch_faces/complication/ratemeter_face.c [deleted file]
movement/watch_faces/complication/ratemeter_face.h [deleted file]
movement/watch_faces/complication/rpn_calculator_alt_face.c [deleted file]
movement/watch_faces/complication/rpn_calculator_alt_face.h [deleted file]
movement/watch_faces/complication/rpn_calculator_face.c [deleted file]
movement/watch_faces/complication/rpn_calculator_face.h [deleted file]
movement/watch_faces/complication/sailing_face.c [deleted file]
movement/watch_faces/complication/sailing_face.h [deleted file]
movement/watch_faces/complication/ships_bell_face.c [deleted file]
movement/watch_faces/complication/ships_bell_face.h [deleted file]
movement/watch_faces/complication/simon_face.c [deleted file]
movement/watch_faces/complication/simon_face.h [deleted file]
movement/watch_faces/complication/simple_calculator_face.c [deleted file]
movement/watch_faces/complication/simple_calculator_face.h [deleted file]
movement/watch_faces/complication/simple_coin_flip_face.c [deleted file]
movement/watch_faces/complication/simple_coin_flip_face.h [deleted file]
movement/watch_faces/complication/smallchess_face.c [deleted file]
movement/watch_faces/complication/smallchess_face.h [deleted file]
movement/watch_faces/complication/solstice_face.c [deleted file]
movement/watch_faces/complication/solstice_face.h [deleted file]
movement/watch_faces/complication/stopwatch_face.c [deleted file]
movement/watch_faces/complication/stopwatch_face.h [deleted file]
movement/watch_faces/complication/tachymeter_face.c [deleted file]
movement/watch_faces/complication/tachymeter_face.h [deleted file]
movement/watch_faces/complication/tally_face.c [deleted file]
movement/watch_faces/complication/tally_face.h [deleted file]
movement/watch_faces/complication/tarot_face.c [deleted file]
movement/watch_faces/complication/tarot_face.h [deleted file]
movement/watch_faces/complication/tempchart_face.c [deleted file]
movement/watch_faces/complication/tempchart_face.h [deleted file]
movement/watch_faces/complication/time_left_face.c [deleted file]
movement/watch_faces/complication/time_left_face.h [deleted file]
movement/watch_faces/complication/timer_face.c [deleted file]
movement/watch_faces/complication/timer_face.h [deleted file]
movement/watch_faces/complication/tomato_face.c [deleted file]
movement/watch_faces/complication/tomato_face.h [deleted file]
movement/watch_faces/complication/toss_up_face.c [deleted file]
movement/watch_faces/complication/toss_up_face.h [deleted file]
movement/watch_faces/complication/totp_face.c [deleted file]
movement/watch_faces/complication/totp_face.h [deleted file]
movement/watch_faces/complication/totp_face_lfs.c [deleted file]
movement/watch_faces/complication/totp_face_lfs.h [deleted file]
movement/watch_faces/complication/tuning_tones_face.c [deleted file]
movement/watch_faces/complication/tuning_tones_face.h [deleted file]
movement/watch_faces/complication/wake_face.c [deleted file]
movement/watch_faces/complication/wake_face.h [deleted file]
movement/watch_faces/complication/wareki_face.c [deleted file]
movement/watch_faces/complication/wareki_face.h [deleted file]
movement/watch_faces/complication/wordle_face.c [deleted file]
movement/watch_faces/complication/wordle_face.h [deleted file]
movement/watch_faces/complication/wordle_face_dict.h [deleted file]
movement/watch_faces/demo/beeps_face.c [deleted file]
movement/watch_faces/demo/beeps_face.h [deleted file]
movement/watch_faces/demo/demo_face.c [deleted file]
movement/watch_faces/demo/demo_face.h [deleted file]
movement/watch_faces/demo/frequency_correction_face.c [deleted file]
movement/watch_faces/demo/frequency_correction_face.h [deleted file]
movement/watch_faces/demo/hello_there_face.c [deleted file]
movement/watch_faces/demo/hello_there_face.h [deleted file]
movement/watch_faces/demo/lis2dw_logging_face.c [deleted file]
movement/watch_faces/demo/lis2dw_logging_face.h [deleted file]
movement/watch_faces/sensor/accelerometer_data_acquisition_face.c [deleted file]
movement/watch_faces/sensor/accelerometer_data_acquisition_face.h [deleted file]
movement/watch_faces/sensor/alarm_thermometer_face.c [deleted file]
movement/watch_faces/sensor/alarm_thermometer_face.h [deleted file]
movement/watch_faces/sensor/lightmeter_face.c [deleted file]
movement/watch_faces/sensor/lightmeter_face.h [deleted file]
movement/watch_faces/sensor/minmax_face.c [deleted file]
movement/watch_faces/sensor/minmax_face.h [deleted file]
movement/watch_faces/sensor/thermistor_testing_face.c [deleted file]
movement/watch_faces/sensor/thermistor_testing_face.h [deleted file]
movement/watch_faces/settings/finetune_face.c [deleted file]
movement/watch_faces/settings/finetune_face.h [deleted file]
movement/watch_faces/settings/nanosec_face.c [deleted file]
movement/watch_faces/settings/nanosec_face.h [deleted file]
movement/watch_faces/settings/place_face.h [deleted file]
movement/watch_faces/settings/save_load_face.c [deleted file]
movement/watch_faces/settings/save_load_face.h [deleted file]
movement/watch_faces/settings/set_time_hackwatch_face.c [deleted file]
movement/watch_faces/settings/set_time_hackwatch_face.h [deleted file]

index 088e6b4e90611d47736e090a756969006aa38e9d..af2e91b412aa29ae25d9175f88bbdf165be19447 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -43,9 +43,9 @@ INCLUDES += \
   -I./utz \
   -I./filesystem \
   -I./shell \
-  -I./movement/lib/sunriset \
-  -I./movement/lib/chirpy_tx \
-  -I./movement/lib/base64 \
+  -I./lib/sunriset \
+  -I./lib/chirpy_tx \
+  -I./lib/base64 \
   -I./watch-library/shared/watch \
   -I./watch-library/shared/driver \
   -I./watch-faces/clock \
@@ -64,9 +64,9 @@ SRCS += \
   ./utz/zones.c \
   ./shell/shell.c \
   ./shell/shell_cmd_list.c \
-  ./movement/lib/sunriset/sunriset.c \
-  ./movement/lib/chirpy_tx/chirpy_tx.c \
-  ./movement/lib/base64/base64.c \
+  ./lib/sunriset/sunriset.c \
+  ./lib/chirpy_tx/chirpy_tx.c \
+  ./lib/base64/base64.c \
   ./watch-library/shared/driver/thermistor_driver.c \
   ./watch-library/shared/watch/watch_common_buzzer.c \
   ./watch-library/shared/watch/watch_common_display.c \
diff --git a/legacy/alt_fw/alt_time.h b/legacy/alt_fw/alt_time.h
new file mode 100644 (file)
index 0000000..956928d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 MOVEMENT_CONFIG_H_
+#define MOVEMENT_CONFIG_H_
+
+#include "movement_faces.h"
+
+const watch_face_t watch_faces[] = {
+    simple_clock_face,
+    beats_face,
+    day_one_face,
+
+    preferences_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/legacy/alt_fw/backer.h b/legacy/alt_fw/backer.h
new file mode 100644 (file)
index 0000000..ff04222
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 MOVEMENT_CONFIG_H_
+#define MOVEMENT_CONFIG_H_
+
+#include "movement_faces.h"
+
+const watch_face_t watch_faces[] = {
+    simple_clock_face,
+    world_clock_face,
+    sunrise_sunset_face,
+    moon_phase_face,
+    temperature_display_face,
+
+    preferences_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/legacy/alt_fw/deep_space_now.h b/legacy/alt_fw/deep_space_now.h
new file mode 100644 (file)
index 0000000..d2096a6
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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 MOVEMENT_CONFIG_H_
+#define MOVEMENT_CONFIG_H_
+
+#include "movement_faces.h"
+
+// Preset Goldstone (GO), Madrid (MA), and Canberra (CA) time zones.
+// Also prepopulate the Day One register with Voyager 1's launch (September 5, 1977)
+
+#define MOVEMENT_CUSTOM_BOOT_COMMANDS() { \
+    /* Standard Time */\
+    watch_store_backup_data(0x1e0c0c, 4);\
+    watch_store_backup_data(0x010115, 5);\
+    watch_store_backup_data(0x130105, 6);\
+    /* Daylight Saving Time */\
+    /*\
+    watch_store_backup_data(0x1f0c0c, 4);\
+    watch_store_backup_data(0x020115, 5);\
+    watch_store_backup_data(0x110105, 6);\
+    */\
+    watch_store_backup_data(0x0597b9, 2);\
+}
+
+const watch_face_t watch_faces[] = {
+    simple_clock_face,
+    mars_time_face,
+    world_clock_face,
+    world_clock_face,
+    world_clock_face,
+    day_one_face,
+
+    preferences_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/legacy/alt_fw/focus.h b/legacy/alt_fw/focus.h
new file mode 100644 (file)
index 0000000..a8fd573
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 MOVEMENT_CONFIG_H_
+#define MOVEMENT_CONFIG_H_
+
+#include "movement_faces.h"
+
+const watch_face_t watch_faces[] = {
+    simple_clock_face,
+    tomato_face,
+    stopwatch_face,
+    countdown_face,
+    wake_face, // added by @joshber 2022-07-23, per @joeycastillo
+
+    preferences_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/legacy/alt_fw/the_athlete.h b/legacy/alt_fw/the_athlete.h
new file mode 100644 (file)
index 0000000..660a304
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 MOVEMENT_CONFIG_H_
+#define MOVEMENT_CONFIG_H_
+
+#include "movement_faces.h"
+
+const watch_face_t watch_faces[] = {
+    simple_clock_face,
+    stopwatch_face,
+    countdown_face,
+    counter_face,
+    pulsometer_face,
+
+    preferences_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/legacy/alt_fw/the_backpacker.h b/legacy/alt_fw/the_backpacker.h
new file mode 100644 (file)
index 0000000..29622cc
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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 MOVEMENT_CONFIG_H_
+#define MOVEMENT_CONFIG_H_
+
+#include "movement_faces.h"
+
+const watch_face_t watch_faces[] = {
+    simple_clock_face,
+    sunrise_sunset_face,
+    moon_phase_face,
+    temperature_display_face,
+    temperature_logging_face,
+    blinky_face,
+
+    preferences_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/legacy/alt_fw/the_stargazer.h b/legacy/alt_fw/the_stargazer.h
new file mode 100644 (file)
index 0000000..844c680
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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 MOVEMENT_CONFIG_H_
+#define MOVEMENT_CONFIG_H_
+
+#include "movement_faces.h"
+
+#define MOVEMENT_DEFAULT_RED_COLOR 0xF
+#define MOVEMENT_DEFAULT_GREEN_COLOR 0x0
+
+const watch_face_t watch_faces[] = {
+    simple_clock_face,
+    astronomy_face,
+    sunrise_sunset_face,
+    moon_phase_face,
+
+    preferences_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/legacy/alt_fw/timers.h b/legacy/alt_fw/timers.h
new file mode 100644 (file)
index 0000000..4d36c66
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 MOVEMENT_CONFIG_H_
+#define MOVEMENT_CONFIG_H_
+
+#include "movement_faces.h"
+
+const watch_face_t watch_faces[] = {
+    simple_clock_face,
+    wake_face,
+    interval_face,
+    stopwatch_face,
+    sunrise_sunset_face,
+
+    preferences_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/legacy/lib/TOTP/LICENSE b/legacy/lib/TOTP/LICENSE
new file mode 100644 (file)
index 0000000..6de4c0f
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Weravech
+
+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.
diff --git a/legacy/lib/TOTP/README.md b/legacy/lib/TOTP/README.md
new file mode 100644 (file)
index 0000000..bb23122
--- /dev/null
@@ -0,0 +1,68 @@
+TOTP Pure C Library
+====================
+
+Library to generate Time-based One-Time Passwords.
+
+Implements the Time-based One-Time Password algorithm specified in [RFC 6238](https://tools.ietf.org/html/rfc6238). 
+
+Supports different time steps and is compatible with tokens that use the same standard (including software ones, like the Google Authenticator app).
+
+The code is made of :
+
+- [TOTP-MCU](https://github.com/Netthaw/TOTP-MCU) for `TimeStruct2Timestamp`, `getCodeFromTimestamp`, `getCodeFromTimeStruct`, part of `getCodeFromSteps` and `TOTP_HMAC_SHA*` functions
+- [mbedtls](https://github.com/Mbed-TLS/mbedtls) for SHA1/SHA224/SHA256/SHA384/SHA512 implementations
+- [this project](https://github.com/mygityf/cipher/blob/master/cipher/hmac.c) as an inspiration for writing the code to compute the TOTP using the key and the text to hash
+
+
+
+Supported algorithms are SHA1/SHA224/SHA256/SHA384/SHA512.
+
+
+
+Installation & usage:
+--------------------
+First include header to your file
+```c
+#include "TOTP.h"
+```
+After included, define key ex. Key is ```MyLegoDoor```
+- Note: The format of hmacKey is array of hexadecimal bytes.
+- Most websites provide the key encoded in base32 - RFC3548/RFC4648, either upper or lower case. You can use [this site](https://cryptii.com/pipes/base32-to-hex) to convert the base32 string to hex (make sure you upcase it first if it's lowercase and remove all whitespaces).
+```c
+uint8_t hmacKey[] = {0x4d, 0x79, 0x4c, 0x65, 0x67, 0x6f, 0x44, 0x6f, 0x6f, 0x72};               // Secret key
+```
+Instantiate the TOTP class by providing the secret hmacKey, the length of the hmacKey, the Timestep between codes and the algorithm used (most of the time, `SHA1`).
+```c
+TOTP(hmacKey, 10, 30, SHA1);                                     // Secret key, Secret key length, Timestep (30s), Algorithm
+```
+Use the ```getCodeFromTimestamp()``` function to get a TOTP from a unix epoch timestamp
+```c
+uint32_t newCode = getCodeFromTimestamp(1557414000);       // Current timestamp since Unix epoch in seconds
+```
+Or ```getCodeFromTimeStruct()``` if you want to get a TOTP from a tm struct (Time Struct in C), 
+```c
+struct tm datetime;
+datetime.tm_hour = 9;
+datetime.tm_min = 0;
+datetime.tm_sec = 0;
+datetime.tm_mday = 13;
+datetime.tm_mon = 5;
+datetime.tm_year = 2019;
+uint32_t newCode = getCodeFromTimeStruct(datetime);
+```
+
+If the provided unix timestamp isn't in UTC±0, use ```setTimezone()``` before ```getCodeFromTimestamp()``` or ```getCodeFromTimeStruct()``` to offset the time.
+
+```c
+setTimezone(9);                                            // Set timezone +9 Japan
+```
+
+You can see an example in example.c (compile it with `gcc -o example example.c sha1.c sha256.c sha512.c TOTP.c -I.`)
+
+Thanks to:
+----------
+
+* Netthaw, https://github.com/Netthaw/TOTP-MCU
+* Mbed-TLS, https://github.com/Mbed-TLS/mbedtls
+* mygityf, https://github.com/mygityf/cipher/blob/master/cipher/hmac.c
+* susam, https://github.com/susam/mintotp
diff --git a/legacy/lib/TOTP/TOTP.c b/legacy/lib/TOTP/TOTP.c
new file mode 100644 (file)
index 0000000..7f60133
--- /dev/null
@@ -0,0 +1,69 @@
+#include "TOTP.h"\r
+#include "sha1.h"\r
+#include "sha256.h"\r
+#include "sha512.h"\r
+#include <stdio.h>\r
+\r
+uint8_t* _hmacKey;\r
+uint8_t _keyLength;\r
+uint8_t _timeZoneOffset;\r
+uint32_t _timeStep;\r
+hmac_alg _algorithm;\r
+\r
+// Init the library with the private key, its length, the timeStep duration and the algorithm that should be used\r
+void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep, hmac_alg algorithm) {\r
+    _hmacKey = hmacKey;\r
+    _keyLength = keyLength;\r
+    _timeStep = timeStep;\r
+    _algorithm = algorithm;\r
+}\r
+\r
+void setTimezone(uint8_t timezone){\r
+    _timeZoneOffset = timezone;\r
+}\r
+\r
+static uint32_t TimeStruct2Timestamp(struct tm time){\r
+    //time.tm_mon -= 1;\r
+    //time.tm_year -= 1900;\r
+    return mktime(&(time)) - (_timeZoneOffset * 3600) - 2208988800;\r
+}\r
+\r
+// Generate a code, using the timestamp provided\r
+uint32_t getCodeFromTimestamp(uint32_t timeStamp) {\r
+    uint32_t steps = timeStamp / _timeStep;\r
+    return getCodeFromSteps(steps);\r
+}\r
+\r
+// Generate a code, using the timestamp provided\r
+uint32_t getCodeFromTimeStruct(struct tm time) {\r
+    return getCodeFromTimestamp(TimeStruct2Timestamp(time));\r
+}\r
+\r
+// Generate a code, using the number of steps provided\r
+uint32_t getCodeFromSteps(uint32_t steps) {\r
+    // STEP 0, map the number of steps in a 8-bytes array (counter value)\r
+    uint8_t _byteArray[8];\r
+    _byteArray[0] = 0x00;\r
+    _byteArray[1] = 0x00;\r
+    _byteArray[2] = 0x00;\r
+    _byteArray[3] = 0x00;\r
+    _byteArray[4] = (uint8_t)((steps >> 24) & 0xFF);\r
+    _byteArray[5] = (uint8_t)((steps >> 16) & 0xFF);\r
+    _byteArray[6] = (uint8_t)((steps >> 8) & 0XFF);\r
+    _byteArray[7] = (uint8_t)((steps & 0XFF));\r
+\r
+    switch(_algorithm){\r
+        case SHA1:\r
+            return(TOTP_HMAC_SHA1(_hmacKey, _keyLength, _byteArray, 8));\r
+        case SHA224:\r
+            return(TOTP_HMAC_SHA256(_hmacKey, _keyLength, _byteArray, 8, 1));\r
+        case SHA256:\r
+            return(TOTP_HMAC_SHA256(_hmacKey, _keyLength, _byteArray, 8, 0));\r
+        case SHA384:\r
+            return(TOTP_HMAC_SHA512(_hmacKey, _keyLength, _byteArray, 8, 1));\r
+        case SHA512:\r
+            return(TOTP_HMAC_SHA512(_hmacKey, _keyLength, _byteArray, 8, 0));\r
+        default:\r
+            return(0);\r
+    }\r
+}\r
diff --git a/legacy/lib/TOTP/TOTP.h b/legacy/lib/TOTP/TOTP.h
new file mode 100644 (file)
index 0000000..adde66d
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef TOTP_H_\r
+#define TOTP_H_\r
+\r
+#include <inttypes.h>\r
+#include "time.h"\r
+\r
+typedef enum __attribute__ ((__packed__)) {\r
+    SHA1,\r
+    SHA224,\r
+    SHA256,\r
+    SHA384,\r
+    SHA512\r
+} hmac_alg;\r
+\r
+void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep, hmac_alg algorithm);\r
+void setTimezone(uint8_t timezone);\r
+uint32_t getCodeFromTimestamp(uint32_t timeStamp);\r
+uint32_t getCodeFromTimeStruct(struct tm time);\r
+uint32_t getCodeFromSteps(uint32_t steps);\r
+\r
+#endif // TOTP_H_\r
diff --git a/legacy/lib/TOTP/example.c b/legacy/lib/TOTP/example.c
new file mode 100644 (file)
index 0000000..a218ff8
--- /dev/null
@@ -0,0 +1,27 @@
+#include "TOTP.h"\r
+#include <stdio.h>\r
+\r
+/**\r
+ * example.c\r
+ */\r
+void main(void)\r
+{\r
+       uint8_t hmacKey[] = {0x4d, 0x79, 0x4c, 0x65, 0x67, 0x6f, 0x44, 0x6f, 0x6f, 0x72};               // Secret key\r
+       TOTP(hmacKey, 10, 7200, SHA1);                                                                        // Secret key, Key length, Timestep (7200s - 2hours)\r
+\r
+       setTimezone(9);                                                                                 // Set timezone\r
+       uint32_t newCode = getCodeFromTimestamp(1557414000);                                            // Timestamp Now\r
+\r
+       ///////////////// For struct tm //////////////////\r
+    // struct tm datetime;\r
+    // datetime.tm_hour = 9;\r
+    // datetime.tm_min = 0;\r
+    // datetime.tm_sec = 0;\r
+    // datetime.tm_mday = 13;\r
+    // datetime.tm_mon = 5;\r
+    // datetime.tm_year = 2019;\r
+       //  uint32_t newCode = getCodeFromTimeStruct(datetime);\r
+       ///////////////////////////////////////////////////\r
+\r
+       printf("Code : %06u\n",newCode);\r
+}\r
diff --git a/legacy/lib/TOTP/sha1.c b/legacy/lib/TOTP/sha1.c
new file mode 100644 (file)
index 0000000..84fc83d
--- /dev/null
@@ -0,0 +1,398 @@
+/*\r
+ *  FIPS-180-1 compliant SHA-1 implementation\r
+ *\r
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
+ *  SPDX-License-Identifier: Apache-2.0\r
+ *\r
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+ *  not use this file except in compliance with the License.\r
+ *  You may obtain a copy of the License at\r
+ *\r
+ *  http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ *\r
+ *  This file is part of mbed TLS (https://tls.mbed.org)\r
+ */\r
+/*\r
+ *  The SHA-1 standard was published by NIST in 1993.\r
+ *\r
+ *  http://www.itl.nist.gov/fipspubs/fip180-1.htm\r
+ */\r
+\r
+#include "sha1.h"\r
+#include <string.h>\r
+#include <stdio.h>\r
+\r
+/* Implementation that should never be optimized out by the compiler */\r
+static void mbedtls_zeroize( void *v, size_t n ) {\r
+    volatile unsigned char *p = v; while( n-- ) *p++ = 0;\r
+}\r
+\r
+/*\r
+ * 32-bit integer manipulation macros (big endian)\r
+ */\r
+#ifndef GET_UINT32_BE\r
+#define GET_UINT32_BE(n,b,i)                            \\r
+{                                                       \\r
+    (n) = ( (uint32_t) (b)[(i)    ] << 24 )             \\r
+        | ( (uint32_t) (b)[(i) + 1] << 16 )             \\r
+        | ( (uint32_t) (b)[(i) + 2] <<  8 )             \\r
+        | ( (uint32_t) (b)[(i) + 3]       );            \\r
+}\r
+#endif\r
+\r
+#ifndef PUT_UINT32_BE\r
+#define PUT_UINT32_BE(n,b,i)                            \\r
+{                                                       \\r
+    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \\r
+    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \\r
+    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \\r
+    (b)[(i) + 3] = (unsigned char) ( (n)       );       \\r
+}\r
+#endif\r
+\r
+void mbedtls_sha1_init( mbedtls_sha1_context *ctx )\r
+{\r
+    memset( ctx, 0, sizeof( mbedtls_sha1_context ) );\r
+}\r
+\r
+void mbedtls_sha1_free( mbedtls_sha1_context *ctx )\r
+{\r
+    if( ctx == NULL )\r
+        return;\r
+\r
+    mbedtls_zeroize( ctx, sizeof( mbedtls_sha1_context ) );\r
+}\r
+\r
+/*\r
+ * SHA-1 context setup\r
+ */\r
+void mbedtls_sha1_starts( mbedtls_sha1_context *ctx )\r
+{\r
+    ctx->total[0] = 0;\r
+    ctx->total[1] = 0;\r
+\r
+    ctx->state[0] = 0x67452301;\r
+    ctx->state[1] = 0xEFCDAB89;\r
+    ctx->state[2] = 0x98BADCFE;\r
+    ctx->state[3] = 0x10325476;\r
+    ctx->state[4] = 0xC3D2E1F0;\r
+}\r
+\r
+void mbedtls_sha1_process( mbedtls_sha1_context *ctx, const unsigned char data[SHA1_BLOCK_LENGTH] )\r
+{\r
+    uint32_t temp, W[16], A, B, C, D, E;\r
+\r
+    GET_UINT32_BE( W[ 0], data,  0 );\r
+    GET_UINT32_BE( W[ 1], data,  4 );\r
+    GET_UINT32_BE( W[ 2], data,  8 );\r
+    GET_UINT32_BE( W[ 3], data, 12 );\r
+    GET_UINT32_BE( W[ 4], data, 16 );\r
+    GET_UINT32_BE( W[ 5], data, 20 );\r
+    GET_UINT32_BE( W[ 6], data, 24 );\r
+    GET_UINT32_BE( W[ 7], data, 28 );\r
+    GET_UINT32_BE( W[ 8], data, 32 );\r
+    GET_UINT32_BE( W[ 9], data, 36 );\r
+    GET_UINT32_BE( W[10], data, 40 );\r
+    GET_UINT32_BE( W[11], data, 44 );\r
+    GET_UINT32_BE( W[12], data, 48 );\r
+    GET_UINT32_BE( W[13], data, 52 );\r
+    GET_UINT32_BE( W[14], data, 56 );\r
+    GET_UINT32_BE( W[15], data, 60 );\r
+\r
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))\r
+\r
+#define R(t)                                            \\r
+(                                                       \\r
+    temp = W[( t -  3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \\r
+           W[( t - 14 ) & 0x0F] ^ W[  t       & 0x0F],  \\r
+    ( W[t & 0x0F] = S(temp,1) )                         \\r
+)\r
+\r
+#define P(a,b,c,d,e,x)                                  \\r
+{                                                       \\r
+    e += S(a,5) + F(b,c,d) + K + x; b = S(b,30);        \\r
+}\r
+\r
+    A = ctx->state[0];\r
+    B = ctx->state[1];\r
+    C = ctx->state[2];\r
+    D = ctx->state[3];\r
+    E = ctx->state[4];\r
+\r
+#define F(x,y,z) (z ^ (x & (y ^ z)))\r
+#define K 0x5A827999\r
+\r
+    P( A, B, C, D, E, W[0]  );\r
+    P( E, A, B, C, D, W[1]  );\r
+    P( D, E, A, B, C, W[2]  );\r
+    P( C, D, E, A, B, W[3]  );\r
+    P( B, C, D, E, A, W[4]  );\r
+    P( A, B, C, D, E, W[5]  );\r
+    P( E, A, B, C, D, W[6]  );\r
+    P( D, E, A, B, C, W[7]  );\r
+    P( C, D, E, A, B, W[8]  );\r
+    P( B, C, D, E, A, W[9]  );\r
+    P( A, B, C, D, E, W[10] );\r
+    P( E, A, B, C, D, W[11] );\r
+    P( D, E, A, B, C, W[12] );\r
+    P( C, D, E, A, B, W[13] );\r
+    P( B, C, D, E, A, W[14] );\r
+    P( A, B, C, D, E, W[15] );\r
+    P( E, A, B, C, D, R(16) );\r
+    P( D, E, A, B, C, R(17) );\r
+    P( C, D, E, A, B, R(18) );\r
+    P( B, C, D, E, A, R(19) );\r
+\r
+#undef K\r
+#undef F\r
+\r
+#define F(x,y,z) (x ^ y ^ z)\r
+#define K 0x6ED9EBA1\r
+\r
+    P( A, B, C, D, E, R(20) );\r
+    P( E, A, B, C, D, R(21) );\r
+    P( D, E, A, B, C, R(22) );\r
+    P( C, D, E, A, B, R(23) );\r
+    P( B, C, D, E, A, R(24) );\r
+    P( A, B, C, D, E, R(25) );\r
+    P( E, A, B, C, D, R(26) );\r
+    P( D, E, A, B, C, R(27) );\r
+    P( C, D, E, A, B, R(28) );\r
+    P( B, C, D, E, A, R(29) );\r
+    P( A, B, C, D, E, R(30) );\r
+    P( E, A, B, C, D, R(31) );\r
+    P( D, E, A, B, C, R(32) );\r
+    P( C, D, E, A, B, R(33) );\r
+    P( B, C, D, E, A, R(34) );\r
+    P( A, B, C, D, E, R(35) );\r
+    P( E, A, B, C, D, R(36) );\r
+    P( D, E, A, B, C, R(37) );\r
+    P( C, D, E, A, B, R(38) );\r
+    P( B, C, D, E, A, R(39) );\r
+\r
+#undef K\r
+#undef F\r
+\r
+#define F(x,y,z) ((x & y) | (z & (x | y)))\r
+#define K 0x8F1BBCDC\r
+\r
+    P( A, B, C, D, E, R(40) );\r
+    P( E, A, B, C, D, R(41) );\r
+    P( D, E, A, B, C, R(42) );\r
+    P( C, D, E, A, B, R(43) );\r
+    P( B, C, D, E, A, R(44) );\r
+    P( A, B, C, D, E, R(45) );\r
+    P( E, A, B, C, D, R(46) );\r
+    P( D, E, A, B, C, R(47) );\r
+    P( C, D, E, A, B, R(48) );\r
+    P( B, C, D, E, A, R(49) );\r
+    P( A, B, C, D, E, R(50) );\r
+    P( E, A, B, C, D, R(51) );\r
+    P( D, E, A, B, C, R(52) );\r
+    P( C, D, E, A, B, R(53) );\r
+    P( B, C, D, E, A, R(54) );\r
+    P( A, B, C, D, E, R(55) );\r
+    P( E, A, B, C, D, R(56) );\r
+    P( D, E, A, B, C, R(57) );\r
+    P( C, D, E, A, B, R(58) );\r
+    P( B, C, D, E, A, R(59) );\r
+\r
+#undef K\r
+#undef F\r
+\r
+#define F(x,y,z) (x ^ y ^ z)\r
+#define K 0xCA62C1D6\r
+\r
+    P( A, B, C, D, E, R(60) );\r
+    P( E, A, B, C, D, R(61) );\r
+    P( D, E, A, B, C, R(62) );\r
+    P( C, D, E, A, B, R(63) );\r
+    P( B, C, D, E, A, R(64) );\r
+    P( A, B, C, D, E, R(65) );\r
+    P( E, A, B, C, D, R(66) );\r
+    P( D, E, A, B, C, R(67) );\r
+    P( C, D, E, A, B, R(68) );\r
+    P( B, C, D, E, A, R(69) );\r
+    P( A, B, C, D, E, R(70) );\r
+    P( E, A, B, C, D, R(71) );\r
+    P( D, E, A, B, C, R(72) );\r
+    P( C, D, E, A, B, R(73) );\r
+    P( B, C, D, E, A, R(74) );\r
+    P( A, B, C, D, E, R(75) );\r
+    P( E, A, B, C, D, R(76) );\r
+    P( D, E, A, B, C, R(77) );\r
+    P( C, D, E, A, B, R(78) );\r
+    P( B, C, D, E, A, R(79) );\r
+\r
+#undef K\r
+#undef F\r
+\r
+    ctx->state[0] += A;\r
+    ctx->state[1] += B;\r
+    ctx->state[2] += C;\r
+    ctx->state[3] += D;\r
+    ctx->state[4] += E;\r
+}\r
+\r
+/*\r
+ * SHA-1 process buffer\r
+ */\r
+void mbedtls_sha1_update( mbedtls_sha1_context *ctx, const unsigned char *input, size_t ilen )\r
+{\r
+    size_t fill;\r
+    uint32_t left;\r
+\r
+    if( ilen == 0 )\r
+        return;\r
+\r
+    left = ctx->total[0] & 0x3F;\r
+    fill = 64 - left;\r
+\r
+    ctx->total[0] += (uint32_t) ilen;\r
+    ctx->total[0] &= 0xFFFFFFFF;\r
+\r
+    if( ctx->total[0] < (uint32_t) ilen )\r
+        ctx->total[1]++;\r
+\r
+    if( left && ilen >= fill )\r
+    {\r
+        memcpy( (void *) (ctx->buffer + left), input, fill );\r
+        mbedtls_sha1_process( ctx, ctx->buffer );\r
+        input += fill;\r
+        ilen  -= fill;\r
+        left = 0;\r
+    }\r
+\r
+    while( ilen >= 64 )\r
+    {\r
+        mbedtls_sha1_process( ctx, input );\r
+        input += 64;\r
+        ilen  -= 64;\r
+    }\r
+\r
+    if( ilen > 0 )\r
+        memcpy( (void *) (ctx->buffer + left), input, ilen );\r
+}\r
+\r
+static const unsigned char sha1_padding[SHA1_BLOCK_LENGTH] =\r
+{\r
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
+};\r
+\r
+/*\r
+ * SHA-1 final digest\r
+ */\r
+void mbedtls_sha1_finish( mbedtls_sha1_context *ctx, unsigned char output[SHA1_DIGEST_LENGTH] )\r
+{\r
+    uint32_t last, padn;\r
+    uint32_t high, low;\r
+    unsigned char msglen[8];\r
+\r
+    high = ( ctx->total[0] >> 29 )\r
+         | ( ctx->total[1] <<  3 );\r
+    low  = ( ctx->total[0] <<  3 );\r
+\r
+    PUT_UINT32_BE( high, msglen, 0 );\r
+    PUT_UINT32_BE( low,  msglen, 4 );\r
+\r
+    last = ctx->total[0] & 0x3F;\r
+    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );\r
+\r
+    mbedtls_sha1_update( ctx, sha1_padding, padn );\r
+    mbedtls_sha1_update( ctx, msglen, 8 );\r
+\r
+    PUT_UINT32_BE( ctx->state[0], output,  0 );\r
+    PUT_UINT32_BE( ctx->state[1], output,  4 );\r
+    PUT_UINT32_BE( ctx->state[2], output,  8 );\r
+    PUT_UINT32_BE( ctx->state[3], output, 12 );\r
+    PUT_UINT32_BE( ctx->state[4], output, 16 );\r
+}\r
+\r
+/*\r
+ * output = SHA-1( input buffer )\r
+ */\r
+void mbedtls_sha1( const unsigned char *input, size_t ilen, unsigned char output[SHA1_DIGEST_LENGTH] )\r
+{\r
+    mbedtls_sha1_context ctx;\r
+\r
+    mbedtls_sha1_init( &ctx );\r
+    mbedtls_sha1_starts( &ctx );\r
+    mbedtls_sha1_update( &ctx, input, ilen );\r
+    mbedtls_sha1_finish( &ctx, output );\r
+    mbedtls_sha1_free( &ctx );\r
+}\r
+\r
+/*\r
+* Compute HMAC_SHA1 using key, key length, text to hash, size of the text, and output buffer\r
+*/\r
+void HMAC_SHA1(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t out[SHA1_DIGEST_LENGTH]){\r
+\r
+  uint8_t i;\r
+  uint8_t k_ipad[SHA1_BLOCK_LENGTH]; /* inner padding - key XORd with ipad */\r
+  uint8_t k_opad[SHA1_BLOCK_LENGTH]; /* outer padding - key XORd with opad */\r
+  uint8_t buffer[SHA1_BLOCK_LENGTH + SHA1_DIGEST_LENGTH];\r
+\r
+  /* start out by storing key in pads */\r
+  memset(k_ipad, 0, sizeof(k_ipad));\r
+  memset(k_opad, 0, sizeof(k_opad));\r
+\r
+  if (key_length <= SHA1_BLOCK_LENGTH) {\r
+      memcpy(k_ipad, key, key_length);\r
+      memcpy(k_opad, key, key_length);\r
+  }\r
+\r
+  else {\r
+      mbedtls_sha1(key, key_length, k_ipad);\r
+      memcpy(k_opad, k_ipad, SHA1_BLOCK_LENGTH);\r
+  }\r
+\r
+  /* XOR key with ipad and opad values */\r
+  for (i = 0; i < SHA1_BLOCK_LENGTH; i++) {\r
+      k_ipad[i] ^= HMAC_IPAD;\r
+      k_opad[i] ^= HMAC_OPAD;\r
+  }\r
+  \r
+  // perform inner SHA1\r
+  memcpy(buffer, k_ipad, SHA1_BLOCK_LENGTH);\r
+  memcpy(buffer + SHA1_BLOCK_LENGTH, in, n);\r
+  mbedtls_sha1(buffer, SHA1_BLOCK_LENGTH + n, out);\r
+  \r
+  memset(buffer, 0, SHA1_BLOCK_LENGTH + n);\r
+\r
+  // perform outer SHA1\r
+  memcpy(buffer, k_opad, SHA1_BLOCK_LENGTH);\r
+  memcpy(buffer + SHA1_BLOCK_LENGTH, out, SHA1_DIGEST_LENGTH);\r
+  mbedtls_sha1(buffer, SHA1_BLOCK_LENGTH + SHA1_DIGEST_LENGTH, out);\r
+}\r
+/*\r
+* Compute TOTP_HMAC_SHA1 using key, key length, text to hash, size of the text\r
+*/\r
+uint32_t TOTP_HMAC_SHA1(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n){\r
+    // STEP 1, get the HMAC-SHA1 hash from counter and key\r
+    uint8_t hash[SHA1_DIGEST_LENGTH];\r
+    HMAC_SHA1(key, key_length, in, n, hash);\r
+\r
+    // STEP 2, apply dynamic truncation to obtain a 4-bytes string\r
+    uint32_t truncated_hash = 0;\r
+    uint8_t _offset = hash[SHA1_DIGEST_LENGTH - 1] & 0xF;\r
+    uint8_t j;\r
+    for (j = 0; j < 4; ++j) {\r
+        truncated_hash <<= 8;\r
+        truncated_hash  |= hash[_offset + j];\r
+    }\r
+\r
+    // STEP 3, compute the OTP value\r
+    truncated_hash &= 0x7FFFFFFF;    //Disabled\r
+    truncated_hash %= 1000000;\r
+\r
+    return truncated_hash;\r
+}\r
diff --git a/legacy/lib/TOTP/sha1.h b/legacy/lib/TOTP/sha1.h
new file mode 100644 (file)
index 0000000..2743c5a
--- /dev/null
@@ -0,0 +1,98 @@
+/**\r
+ * \file sha1.h\r
+ *\r
+ * \brief SHA-1 cryptographic hash function\r
+ *\r
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
+ *  SPDX-License-Identifier: Apache-2.0\r
+ *\r
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+ *  not use this file except in compliance with the License.\r
+ *  You may obtain a copy of the License at\r
+ *\r
+ *  http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ *\r
+ *  This file is part of mbed TLS (https://tls.mbed.org)\r
+ */\r
+#ifndef MBEDTLS_SHA1_H\r
+#define MBEDTLS_SHA1_H\r
+\r
+#define SHA1_DIGEST_LENGTH 20\r
+#define SHA1_BLOCK_LENGTH 64\r
+#define HMAC_IPAD 0x36\r
+#define HMAC_OPAD 0x5c\r
+\r
+#include <stddef.h>\r
+#include <stdint.h>\r
+\r
+/**\r
+ * \brief          SHA-1 context structure\r
+ */\r
+typedef struct\r
+{\r
+    uint32_t total[2];          /*!< number of bytes processed  */\r
+    uint32_t state[5];          /*!< intermediate digest state  */\r
+    unsigned char buffer[SHA1_BLOCK_LENGTH];   /*!< data block being processed */\r
+}\r
+mbedtls_sha1_context;\r
+\r
+/**\r
+ * \brief          Initialize SHA-1 context\r
+ *\r
+ * \param ctx      SHA-1 context to be initialized\r
+ */\r
+void mbedtls_sha1_init( mbedtls_sha1_context *ctx );\r
+\r
+/**\r
+ * \brief          Clear SHA-1 context\r
+ *\r
+ * \param ctx      SHA-1 context to be cleared\r
+ */\r
+void mbedtls_sha1_free( mbedtls_sha1_context *ctx );\r
+\r
+/**\r
+ * \brief          SHA-1 context setup\r
+ *\r
+ * \param ctx      context to be initialized\r
+ */\r
+void mbedtls_sha1_starts( mbedtls_sha1_context *ctx );\r
+\r
+/**\r
+ * \brief          SHA-1 process buffer\r
+ *\r
+ * \param ctx      SHA-1 context\r
+ * \param input    buffer holding the  data\r
+ * \param ilen     length of the input data\r
+ */\r
+void mbedtls_sha1_update( mbedtls_sha1_context *ctx, const unsigned char *input, size_t ilen );\r
+\r
+/**\r
+ * \brief          SHA-1 final digest\r
+ *\r
+ * \param ctx      SHA-1 context\r
+ * \param output   SHA-1 checksum result\r
+ */\r
+void mbedtls_sha1_finish( mbedtls_sha1_context *ctx, unsigned char output[SHA1_DIGEST_LENGTH] );\r
+\r
+/* Internal use */\r
+void mbedtls_sha1_process( mbedtls_sha1_context *ctx, const unsigned char data[SHA1_BLOCK_LENGTH] );\r
+\r
+/**\r
+ * \brief          Output = SHA-1( input buffer )\r
+ *\r
+ * \param input    buffer holding the  data\r
+ * \param ilen     length of the input data\r
+ * \param output   SHA-1 checksum result\r
+ */\r
+void mbedtls_sha1( const unsigned char *input, size_t ilen, unsigned char output[SHA1_DIGEST_LENGTH] );\r
+void HMAC_SHA1(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t out[SHA1_DIGEST_LENGTH]);\r
+uint32_t TOTP_HMAC_SHA1(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n);\r
+\r
+\r
+#endif /* mbedtls_sha1.h */\r
diff --git a/legacy/lib/TOTP/sha256.c b/legacy/lib/TOTP/sha256.c
new file mode 100644 (file)
index 0000000..cb83f53
--- /dev/null
@@ -0,0 +1,372 @@
+/*\r
+ *  FIPS-180-2 compliant SHA-256 implementation\r
+ *\r
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
+ *  SPDX-License-Identifier: Apache-2.0\r
+ *\r
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+ *  not use this file except in compliance with the License.\r
+ *  You may obtain a copy of the License at\r
+ *\r
+ *  http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ *\r
+ *  This file is part of mbed TLS (https://tls.mbed.org)\r
+ */\r
+/*\r
+ *  The SHA-256 Secure Hash Standard was published by NIST in 2002.\r
+ *\r
+ *  http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf\r
+ */\r
+\r
+#include "sha256.h"\r
+\r
+#include <string.h>\r
+#include <stdio.h>\r
+\r
+/* Implementation that should never be optimized out by the compiler */\r
+static void mbedtls_zeroize( void *v, size_t n ) {\r
+    volatile unsigned char *p = v; while( n-- ) *p++ = 0;\r
+}\r
+\r
+/*\r
+ * 32-bit integer manipulation macros (big endian)\r
+ */\r
+#ifndef GET_UINT32_BE\r
+#define GET_UINT32_BE(n,b,i)                            \\r
+do {                                                    \\r
+    (n) = ( (uint32_t) (b)[(i)    ] << 24 )             \\r
+        | ( (uint32_t) (b)[(i) + 1] << 16 )             \\r
+        | ( (uint32_t) (b)[(i) + 2] <<  8 )             \\r
+        | ( (uint32_t) (b)[(i) + 3]       );            \\r
+} while( 0 )\r
+#endif\r
+\r
+#ifndef PUT_UINT32_BE\r
+#define PUT_UINT32_BE(n,b,i)                            \\r
+do {                                                    \\r
+    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \\r
+    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \\r
+    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \\r
+    (b)[(i) + 3] = (unsigned char) ( (n)       );       \\r
+} while( 0 )\r
+#endif\r
+\r
+void mbedtls_sha256_init( mbedtls_sha256_context *ctx )\r
+{\r
+    memset( ctx, 0, sizeof( mbedtls_sha256_context ) );\r
+}\r
+\r
+void mbedtls_sha256_free( mbedtls_sha256_context *ctx )\r
+{\r
+    if( ctx == NULL )\r
+        return;\r
+\r
+    mbedtls_zeroize( ctx, sizeof( mbedtls_sha256_context ) );\r
+}\r
+\r
+void mbedtls_sha256_clone( mbedtls_sha256_context *dst,\r
+                           const mbedtls_sha256_context *src )\r
+{\r
+    *dst = *src;\r
+}\r
+\r
+/*\r
+ * SHA-256 context setup\r
+ */\r
+void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, int is224 )\r
+{\r
+    ctx->total[0] = 0;\r
+    ctx->total[1] = 0;\r
+\r
+    if( is224 == 0 )\r
+    {\r
+        /* SHA-256 */\r
+        ctx->state[0] = 0x6A09E667;\r
+        ctx->state[1] = 0xBB67AE85;\r
+        ctx->state[2] = 0x3C6EF372;\r
+        ctx->state[3] = 0xA54FF53A;\r
+        ctx->state[4] = 0x510E527F;\r
+        ctx->state[5] = 0x9B05688C;\r
+        ctx->state[6] = 0x1F83D9AB;\r
+        ctx->state[7] = 0x5BE0CD19;\r
+    }\r
+    else\r
+    {\r
+        /* SHA-224 */\r
+        ctx->state[0] = 0xC1059ED8;\r
+        ctx->state[1] = 0x367CD507;\r
+        ctx->state[2] = 0x3070DD17;\r
+        ctx->state[3] = 0xF70E5939;\r
+        ctx->state[4] = 0xFFC00B31;\r
+        ctx->state[5] = 0x68581511;\r
+        ctx->state[6] = 0x64F98FA7;\r
+        ctx->state[7] = 0xBEFA4FA4;\r
+    }\r
+\r
+    ctx->is224 = is224;\r
+}\r
+\r
+static const uint32_t K[] =\r
+{\r
+    0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,\r
+    0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,\r
+    0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,\r
+    0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,\r
+    0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,\r
+    0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,\r
+    0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,\r
+    0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,\r
+    0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,\r
+    0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,\r
+    0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,\r
+    0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,\r
+    0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,\r
+    0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,\r
+    0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,\r
+    0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,\r
+};\r
+\r
+#define SHR(x,n) ((x & 0xFFFFFFFF) >> n)\r
+#define ROTR(x,n) (SHR(x,n) | (x << (32 - n)))\r
+\r
+#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^  SHR(x, 3))\r
+#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^  SHR(x,10))\r
+\r
+#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22))\r
+#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25))\r
+\r
+#define F0(x,y,z) ((x & y) | (z & (x | y)))\r
+#define F1(x,y,z) (z ^ (x & (y ^ z)))\r
+\r
+#define R(t)                                    \\r
+(                                               \\r
+    W[t] = S1(W[t -  2]) + W[t -  7] +          \\r
+           S0(W[t - 15]) + W[t - 16]            \\r
+)\r
+\r
+#define P(a,b,c,d,e,f,g,h,x,K)                  \\r
+{                                               \\r
+    temp1 = h + S3(e) + F1(e,f,g) + K + x;      \\r
+    temp2 = S2(a) + F0(a,b,c);                  \\r
+    d += temp1; h = temp1 + temp2;              \\r
+}\r
+\r
+void mbedtls_sha256_process( mbedtls_sha256_context *ctx, const unsigned char data[SHA256_BLOCK_LENGTH] )\r
+{\r
+    uint32_t temp1, temp2, W[64];\r
+    uint32_t A[8];\r
+    unsigned int i;\r
+\r
+    for( i = 0; i < 8; i++ )\r
+        A[i] = ctx->state[i];\r
+\r
+    for( i = 0; i < 16; i++ )\r
+        GET_UINT32_BE( W[i], data, 4 * i );\r
+\r
+    for( i = 0; i < 16; i += 8 )\r
+    {\r
+        P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i+0], K[i+0] );\r
+        P( A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i+1], K[i+1] );\r
+        P( A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i+2], K[i+2] );\r
+        P( A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i+3], K[i+3] );\r
+        P( A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i+4], K[i+4] );\r
+        P( A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i+5], K[i+5] );\r
+        P( A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i+6], K[i+6] );\r
+        P( A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i+7], K[i+7] );\r
+    }\r
+\r
+    for( i = 16; i < 64; i += 8 )\r
+    {\r
+        P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], R(i+0), K[i+0] );\r
+        P( A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], R(i+1), K[i+1] );\r
+        P( A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], R(i+2), K[i+2] );\r
+        P( A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], R(i+3), K[i+3] );\r
+        P( A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], R(i+4), K[i+4] );\r
+        P( A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], R(i+5), K[i+5] );\r
+        P( A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], R(i+6), K[i+6] );\r
+        P( A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], R(i+7), K[i+7] );\r
+    }\r
+\r
+    for( i = 0; i < 8; i++ )\r
+        ctx->state[i] += A[i];\r
+}\r
+\r
+/*\r
+ * SHA-256 process buffer\r
+ */\r
+void mbedtls_sha256_update( mbedtls_sha256_context *ctx, const unsigned char *input,\r
+                    size_t ilen )\r
+{\r
+    size_t fill;\r
+    uint32_t left;\r
+\r
+    if( ilen == 0 )\r
+        return;\r
+\r
+    left = ctx->total[0] & 0x3F;\r
+    fill = 64 - left;\r
+\r
+    ctx->total[0] += (uint32_t) ilen;\r
+    ctx->total[0] &= 0xFFFFFFFF;\r
+\r
+    if( ctx->total[0] < (uint32_t) ilen )\r
+        ctx->total[1]++;\r
+\r
+    if( left && ilen >= fill )\r
+    {\r
+        memcpy( (void *) (ctx->buffer + left), input, fill );\r
+        mbedtls_sha256_process( ctx, ctx->buffer );\r
+        input += fill;\r
+        ilen  -= fill;\r
+        left = 0;\r
+    }\r
+\r
+    while( ilen >= 64 )\r
+    {\r
+        mbedtls_sha256_process( ctx, input );\r
+        input += 64;\r
+        ilen  -= 64;\r
+    }\r
+\r
+    if( ilen > 0 )\r
+        memcpy( (void *) (ctx->buffer + left), input, ilen );\r
+}\r
+\r
+static const unsigned char sha256_padding[SHA256_BLOCK_LENGTH] =\r
+{\r
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
+};\r
+\r
+/*\r
+ * SHA-256 final digest\r
+ */\r
+void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, unsigned char* output )\r
+{\r
+    uint32_t last, padn;\r
+    uint32_t high, low;\r
+    unsigned char msglen[8];\r
+\r
+    high = ( ctx->total[0] >> 29 )\r
+         | ( ctx->total[1] <<  3 );\r
+    low  = ( ctx->total[0] <<  3 );\r
+\r
+    PUT_UINT32_BE( high, msglen, 0 );\r
+    PUT_UINT32_BE( low,  msglen, 4 );\r
+\r
+    last = ctx->total[0] & 0x3F;\r
+    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );\r
+\r
+    mbedtls_sha256_update( ctx, sha256_padding, padn );\r
+    mbedtls_sha256_update( ctx, msglen, 8 );\r
+\r
+    PUT_UINT32_BE( ctx->state[0], output,  0 );\r
+    PUT_UINT32_BE( ctx->state[1], output,  4 );\r
+    PUT_UINT32_BE( ctx->state[2], output,  8 );\r
+    PUT_UINT32_BE( ctx->state[3], output, 12 );\r
+    PUT_UINT32_BE( ctx->state[4], output, 16 );\r
+    PUT_UINT32_BE( ctx->state[5], output, 20 );\r
+    PUT_UINT32_BE( ctx->state[6], output, 24 );\r
+\r
+    if( ctx->is224 == 0 )\r
+        PUT_UINT32_BE( ctx->state[7], output, 28 );\r
+}\r
+\r
+/*\r
+ * output = SHA-256( input buffer )\r
+ */\r
+void mbedtls_sha256( const unsigned char *input, size_t ilen,\r
+             unsigned char* output, int is224 )\r
+{\r
+    mbedtls_sha256_context ctx;\r
+\r
+    mbedtls_sha256_init( &ctx );\r
+    mbedtls_sha256_starts( &ctx, is224 );\r
+    mbedtls_sha256_update( &ctx, input, ilen );\r
+    mbedtls_sha256_finish( &ctx, output );\r
+    mbedtls_sha256_free( &ctx );\r
+}\r
+\r
+/*\r
+* Compute HMAC_SHA224/256 using key, key length, text to hash, size of the text, output buffer and a switch for SHA224\r
+*/\r
+void HMAC_SHA256(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t* out, int is224){\r
+  int digest_length = SHA256_DIGEST_LENGTH;\r
+  if (is224 == 1) {\r
+    digest_length = SHA224_DIGEST_LENGTH;\r
+  }\r
+  \r
+  uint8_t i;\r
+  uint8_t k_ipad[SHA256_BLOCK_LENGTH]; /* inner padding - key XORd with ipad */\r
+  uint8_t k_opad[SHA256_BLOCK_LENGTH]; /* outer padding - key XORd with opad */\r
+  uint8_t buffer[SHA256_BLOCK_LENGTH + digest_length];\r
+\r
+  /* start out by storing key in pads */\r
+  memset(k_ipad, 0, sizeof(k_ipad));\r
+  memset(k_opad, 0, sizeof(k_opad));\r
+\r
+  if (key_length <= SHA256_BLOCK_LENGTH) {\r
+      memcpy(k_ipad, key, key_length);\r
+      memcpy(k_opad, key, key_length);\r
+  }\r
+\r
+  else {\r
+      mbedtls_sha256(key, key_length, k_ipad, is224);\r
+      memcpy(k_opad, k_ipad, SHA256_BLOCK_LENGTH);\r
+  }\r
+\r
+  /* XOR key with ipad and opad values */\r
+  for (i = 0; i < SHA256_BLOCK_LENGTH; i++) {\r
+      k_ipad[i] ^= HMAC_IPAD;\r
+      k_opad[i] ^= HMAC_OPAD;\r
+  }\r
+  \r
+  // perform inner SHA256\r
+  memcpy(buffer, k_ipad, SHA256_BLOCK_LENGTH);\r
+  memcpy(buffer + SHA256_BLOCK_LENGTH, in, n);\r
+  mbedtls_sha256(buffer, SHA256_BLOCK_LENGTH + n, out, is224);\r
+  \r
+  memset(buffer, 0, SHA256_BLOCK_LENGTH + n);\r
+\r
+  // perform outer SHA256\r
+  memcpy(buffer, k_opad, SHA256_BLOCK_LENGTH);\r
+  memcpy(buffer + SHA256_BLOCK_LENGTH, out, digest_length);\r
+  mbedtls_sha256(buffer, SHA256_BLOCK_LENGTH + digest_length, out, is224);\r
+}\r
+\r
+/*\r
+* Compute TOTP_HMAC_SHA224/256 using key, key length, text to hash, size of the text and a switch for SHA224\r
+*/\r
+uint32_t TOTP_HMAC_SHA256(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, int is224){\r
+    int digest_length = SHA256_DIGEST_LENGTH;\r
+    if (is224 == 1) {\r
+      digest_length = SHA224_DIGEST_LENGTH;\r
+    }\r
+\r
+    // STEP 1, get the HMAC-SHA256 hash from counter and key\r
+    uint8_t hash[digest_length];\r
+    HMAC_SHA256(key, key_length, in, n, hash, is224);\r
+\r
+    // STEP 2, apply dynamic truncation to obtain a 4-bytes string\r
+    uint32_t truncated_hash = 0;\r
+    uint8_t _offset = hash[digest_length - 1] & 0xF;\r
+    uint8_t j;\r
+    for (j = 0; j < 4; ++j) {\r
+        truncated_hash <<= 8;\r
+        truncated_hash  |= hash[_offset + j];\r
+    }\r
+\r
+    // STEP 3, compute the OTP value\r
+    truncated_hash &= 0x7FFFFFFF;    //Disabled\r
+    truncated_hash %= 1000000;\r
+\r
+    return truncated_hash;\r
+}
\ No newline at end of file
diff --git a/legacy/lib/TOTP/sha256.h b/legacy/lib/TOTP/sha256.h
new file mode 100644 (file)
index 0000000..2f0febc
--- /dev/null
@@ -0,0 +1,112 @@
+/**\r
+ * \file sha256.h\r
+ *\r
+ * \brief SHA-224 and SHA-256 cryptographic hash function\r
+ *\r
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
+ *  SPDX-License-Identifier: Apache-2.0\r
+ *\r
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+ *  not use this file except in compliance with the License.\r
+ *  You may obtain a copy of the License at\r
+ *\r
+ *  http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ *\r
+ *  This file is part of mbed TLS (https://tls.mbed.org)\r
+ */\r
+#ifndef MBEDTLS_SHA256_H\r
+#define MBEDTLS_SHA256_H\r
+\r
+#define SHA224_DIGEST_LENGTH 28\r
+#define SHA256_DIGEST_LENGTH 32\r
+#define SHA256_BLOCK_LENGTH 64\r
+#define HMAC_IPAD 0x36\r
+#define HMAC_OPAD 0x5c\r
+\r
+#include <stddef.h>\r
+#include <stdint.h>\r
+\r
+/**\r
+ * \brief          SHA-256 context structure\r
+ */\r
+typedef struct\r
+{\r
+    uint32_t total[2];          /*!< number of bytes processed  */\r
+    uint32_t state[8];          /*!< intermediate digest state  */\r
+    unsigned char buffer[SHA256_BLOCK_LENGTH];   /*!< data block being processed */\r
+    int is224;                  /*!< 0 => SHA-256, else SHA-224 */\r
+}\r
+mbedtls_sha256_context;\r
+\r
+/**\r
+ * \brief          Initialize SHA-256 context\r
+ *\r
+ * \param ctx      SHA-256 context to be initialized\r
+ */\r
+void mbedtls_sha256_init( mbedtls_sha256_context *ctx );\r
+\r
+/**\r
+ * \brief          Clear SHA-256 context\r
+ *\r
+ * \param ctx      SHA-256 context to be cleared\r
+ */\r
+void mbedtls_sha256_free( mbedtls_sha256_context *ctx );\r
+\r
+/**\r
+ * \brief          Clone (the state of) a SHA-256 context\r
+ *\r
+ * \param dst      The destination context\r
+ * \param src      The context to be cloned\r
+ */\r
+void mbedtls_sha256_clone( mbedtls_sha256_context *dst,\r
+                           const mbedtls_sha256_context *src );\r
+\r
+/**\r
+ * \brief          SHA-256 context setup\r
+ *\r
+ * \param ctx      context to be initialized\r
+ * \param is224    0 = use SHA256, 1 = use SHA224\r
+ */\r
+void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, int is224 );\r
+\r
+/**\r
+ * \brief          SHA-256 process buffer\r
+ *\r
+ * \param ctx      SHA-256 context\r
+ * \param input    buffer holding the  data\r
+ * \param ilen     length of the input data\r
+ */\r
+void mbedtls_sha256_update( mbedtls_sha256_context *ctx, const unsigned char *input,\r
+                    size_t ilen );\r
+\r
+/**\r
+ * \brief          SHA-256 final digest\r
+ *\r
+ * \param ctx      SHA-256 context\r
+ * \param output   SHA-224/256 checksum result\r
+ */\r
+void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, unsigned char* output );\r
+\r
+/* Internal use */\r
+void mbedtls_sha256_process( mbedtls_sha256_context *ctx, const unsigned char data[SHA256_BLOCK_LENGTH] );\r
+\r
+/**\r
+ * \brief          Output = SHA-256( input buffer )\r
+ *\r
+ * \param input    buffer holding the  data\r
+ * \param ilen     length of the input data\r
+ * \param output   SHA-224/256 checksum result\r
+ * \param is224    0 = use SHA256, 1 = use SHA224\r
+ */\r
+void mbedtls_sha256( const unsigned char *input, size_t ilen,\r
+           unsigned char* output, int is224 );\r
+void HMAC_SHA256(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t* out, int is224);\r
+uint32_t TOTP_HMAC_SHA256(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, int is224);\r
+\r
+#endif /* mbedtls_sha256.h */\r
diff --git a/legacy/lib/TOTP/sha512.c b/legacy/lib/TOTP/sha512.c
new file mode 100644 (file)
index 0000000..d35462b
--- /dev/null
@@ -0,0 +1,422 @@
+/*\r
+ *  FIPS-180-2 compliant SHA-384/512 implementation\r
+ *\r
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
+ *  SPDX-License-Identifier: Apache-2.0\r
+ *\r
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+ *  not use this file except in compliance with the License.\r
+ *  You may obtain a copy of the License at\r
+ *\r
+ *  http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ *\r
+ *  This file is part of mbed TLS (https://tls.mbed.org)\r
+ */\r
+/*\r
+ *  The SHA-512 Secure Hash Standard was published by NIST in 2002.\r
+ *\r
+ *  http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf\r
+ */\r
+\r
+#include "sha512.h"\r
+\r
+#include <string.h>\r
+#include <stdio.h>\r
+\r
+#if defined(_MSC_VER) || defined(__WATCOMC__)\r
+  #define UL64(x) x##ui64\r
+#else\r
+  #define UL64(x) x##ULL\r
+#endif\r
+\r
+/* Implementation that should never be optimized out by the compiler */\r
+static void mbedtls_zeroize( void *v, size_t n ) {\r
+    volatile unsigned char *p = v; while( n-- ) *p++ = 0;\r
+}\r
+\r
+/*\r
+ * 64-bit integer manipulation macros (big endian)\r
+ */\r
+#ifndef GET_UINT64_BE\r
+#define GET_UINT64_BE(n,b,i)                            \\r
+{                                                       \\r
+    (n) = ( (uint64_t) (b)[(i)    ] << 56 )       \\r
+        | ( (uint64_t) (b)[(i) + 1] << 48 )       \\r
+        | ( (uint64_t) (b)[(i) + 2] << 40 )       \\r
+        | ( (uint64_t) (b)[(i) + 3] << 32 )       \\r
+        | ( (uint64_t) (b)[(i) + 4] << 24 )       \\r
+        | ( (uint64_t) (b)[(i) + 5] << 16 )       \\r
+        | ( (uint64_t) (b)[(i) + 6] <<  8 )       \\r
+        | ( (uint64_t) (b)[(i) + 7]       );      \\r
+}\r
+#endif /* GET_UINT64_BE */\r
+\r
+#ifndef PUT_UINT64_BE\r
+#define PUT_UINT64_BE(n,b,i)                            \\r
+{                                                       \\r
+    (b)[(i)    ] = (unsigned char) ( (n) >> 56 );       \\r
+    (b)[(i) + 1] = (unsigned char) ( (n) >> 48 );       \\r
+    (b)[(i) + 2] = (unsigned char) ( (n) >> 40 );       \\r
+    (b)[(i) + 3] = (unsigned char) ( (n) >> 32 );       \\r
+    (b)[(i) + 4] = (unsigned char) ( (n) >> 24 );       \\r
+    (b)[(i) + 5] = (unsigned char) ( (n) >> 16 );       \\r
+    (b)[(i) + 6] = (unsigned char) ( (n) >>  8 );       \\r
+    (b)[(i) + 7] = (unsigned char) ( (n)       );       \\r
+}\r
+#endif /* PUT_UINT64_BE */\r
+\r
+/*\r
+ * Round constants\r
+ */\r
+static const uint64_t K[80] =\r
+{\r
+    UL64(0x428A2F98D728AE22),  UL64(0x7137449123EF65CD),\r
+    UL64(0xB5C0FBCFEC4D3B2F),  UL64(0xE9B5DBA58189DBBC),\r
+    UL64(0x3956C25BF348B538),  UL64(0x59F111F1B605D019),\r
+    UL64(0x923F82A4AF194F9B),  UL64(0xAB1C5ED5DA6D8118),\r
+    UL64(0xD807AA98A3030242),  UL64(0x12835B0145706FBE),\r
+    UL64(0x243185BE4EE4B28C),  UL64(0x550C7DC3D5FFB4E2),\r
+    UL64(0x72BE5D74F27B896F),  UL64(0x80DEB1FE3B1696B1),\r
+    UL64(0x9BDC06A725C71235),  UL64(0xC19BF174CF692694),\r
+    UL64(0xE49B69C19EF14AD2),  UL64(0xEFBE4786384F25E3),\r
+    UL64(0x0FC19DC68B8CD5B5),  UL64(0x240CA1CC77AC9C65),\r
+    UL64(0x2DE92C6F592B0275),  UL64(0x4A7484AA6EA6E483),\r
+    UL64(0x5CB0A9DCBD41FBD4),  UL64(0x76F988DA831153B5),\r
+    UL64(0x983E5152EE66DFAB),  UL64(0xA831C66D2DB43210),\r
+    UL64(0xB00327C898FB213F),  UL64(0xBF597FC7BEEF0EE4),\r
+    UL64(0xC6E00BF33DA88FC2),  UL64(0xD5A79147930AA725),\r
+    UL64(0x06CA6351E003826F),  UL64(0x142929670A0E6E70),\r
+    UL64(0x27B70A8546D22FFC),  UL64(0x2E1B21385C26C926),\r
+    UL64(0x4D2C6DFC5AC42AED),  UL64(0x53380D139D95B3DF),\r
+    UL64(0x650A73548BAF63DE),  UL64(0x766A0ABB3C77B2A8),\r
+    UL64(0x81C2C92E47EDAEE6),  UL64(0x92722C851482353B),\r
+    UL64(0xA2BFE8A14CF10364),  UL64(0xA81A664BBC423001),\r
+    UL64(0xC24B8B70D0F89791),  UL64(0xC76C51A30654BE30),\r
+    UL64(0xD192E819D6EF5218),  UL64(0xD69906245565A910),\r
+    UL64(0xF40E35855771202A),  UL64(0x106AA07032BBD1B8),\r
+    UL64(0x19A4C116B8D2D0C8),  UL64(0x1E376C085141AB53),\r
+    UL64(0x2748774CDF8EEB99),  UL64(0x34B0BCB5E19B48A8),\r
+    UL64(0x391C0CB3C5C95A63),  UL64(0x4ED8AA4AE3418ACB),\r
+    UL64(0x5B9CCA4F7763E373),  UL64(0x682E6FF3D6B2B8A3),\r
+    UL64(0x748F82EE5DEFB2FC),  UL64(0x78A5636F43172F60),\r
+    UL64(0x84C87814A1F0AB72),  UL64(0x8CC702081A6439EC),\r
+    UL64(0x90BEFFFA23631E28),  UL64(0xA4506CEBDE82BDE9),\r
+    UL64(0xBEF9A3F7B2C67915),  UL64(0xC67178F2E372532B),\r
+    UL64(0xCA273ECEEA26619C),  UL64(0xD186B8C721C0C207),\r
+    UL64(0xEADA7DD6CDE0EB1E),  UL64(0xF57D4F7FEE6ED178),\r
+    UL64(0x06F067AA72176FBA),  UL64(0x0A637DC5A2C898A6),\r
+    UL64(0x113F9804BEF90DAE),  UL64(0x1B710B35131C471B),\r
+    UL64(0x28DB77F523047D84),  UL64(0x32CAAB7B40C72493),\r
+    UL64(0x3C9EBE0A15C9BEBC),  UL64(0x431D67C49C100D4C),\r
+    UL64(0x4CC5D4BECB3E42B6),  UL64(0x597F299CFC657E2A),\r
+    UL64(0x5FCB6FAB3AD6FAEC),  UL64(0x6C44198C4A475817)\r
+};\r
+\r
+void mbedtls_sha512_init( mbedtls_sha512_context *ctx )\r
+{\r
+    memset( ctx, 0, sizeof( mbedtls_sha512_context ) );\r
+}\r
+\r
+void mbedtls_sha512_free( mbedtls_sha512_context *ctx )\r
+{\r
+    if( ctx == NULL )\r
+        return;\r
+\r
+    mbedtls_zeroize( ctx, sizeof( mbedtls_sha512_context ) );\r
+}\r
+\r
+void mbedtls_sha512_clone( mbedtls_sha512_context *dst,\r
+                           const mbedtls_sha512_context *src )\r
+{\r
+    *dst = *src;\r
+}\r
+\r
+/*\r
+ * SHA-512 context setup\r
+ */\r
+void mbedtls_sha512_starts( mbedtls_sha512_context *ctx, int is384 )\r
+{\r
+    ctx->total[0] = 0;\r
+    ctx->total[1] = 0;\r
+\r
+    if( is384 == 0 )\r
+    {\r
+        /* SHA-512 */\r
+        ctx->state[0] = UL64(0x6A09E667F3BCC908);\r
+        ctx->state[1] = UL64(0xBB67AE8584CAA73B);\r
+        ctx->state[2] = UL64(0x3C6EF372FE94F82B);\r
+        ctx->state[3] = UL64(0xA54FF53A5F1D36F1);\r
+        ctx->state[4] = UL64(0x510E527FADE682D1);\r
+        ctx->state[5] = UL64(0x9B05688C2B3E6C1F);\r
+        ctx->state[6] = UL64(0x1F83D9ABFB41BD6B);\r
+        ctx->state[7] = UL64(0x5BE0CD19137E2179);\r
+    }\r
+    else\r
+    {\r
+        /* SHA-384 */\r
+        ctx->state[0] = UL64(0xCBBB9D5DC1059ED8);\r
+        ctx->state[1] = UL64(0x629A292A367CD507);\r
+        ctx->state[2] = UL64(0x9159015A3070DD17);\r
+        ctx->state[3] = UL64(0x152FECD8F70E5939);\r
+        ctx->state[4] = UL64(0x67332667FFC00B31);\r
+        ctx->state[5] = UL64(0x8EB44A8768581511);\r
+        ctx->state[6] = UL64(0xDB0C2E0D64F98FA7);\r
+        ctx->state[7] = UL64(0x47B5481DBEFA4FA4);\r
+    }\r
+\r
+    ctx->is384 = is384;\r
+}\r
+\r
+void mbedtls_sha512_process( mbedtls_sha512_context *ctx, const unsigned char data[SHA512_BLOCK_LENGTH] )\r
+{\r
+    int i;\r
+    uint64_t temp1, temp2, W[80];\r
+    uint64_t A, B, C, D, E, F, G, H;\r
+\r
+#define SHR(x,n) (x >> n)\r
+#define ROTR(x,n) (SHR(x,n) | (x << (64 - n)))\r
+\r
+#define S0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^  SHR(x, 7))\r
+#define S1(x) (ROTR(x,19) ^ ROTR(x,61) ^  SHR(x, 6))\r
+\r
+#define S2(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39))\r
+#define S3(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41))\r
+\r
+#define F0(x,y,z) ((x & y) | (z & (x | y)))\r
+#define F1(x,y,z) (z ^ (x & (y ^ z)))\r
+\r
+#define P(a,b,c,d,e,f,g,h,x,K)                  \\r
+{                                               \\r
+    temp1 = h + S3(e) + F1(e,f,g) + K + x;      \\r
+    temp2 = S2(a) + F0(a,b,c);                  \\r
+    d += temp1; h = temp1 + temp2;              \\r
+}\r
+\r
+    for( i = 0; i < 16; i++ )\r
+    {\r
+        GET_UINT64_BE( W[i], data, i << 3 );\r
+    }\r
+\r
+    for( ; i < 80; i++ )\r
+    {\r
+        W[i] = S1(W[i -  2]) + W[i -  7] +\r
+               S0(W[i - 15]) + W[i - 16];\r
+    }\r
+\r
+    A = ctx->state[0];\r
+    B = ctx->state[1];\r
+    C = ctx->state[2];\r
+    D = ctx->state[3];\r
+    E = ctx->state[4];\r
+    F = ctx->state[5];\r
+    G = ctx->state[6];\r
+    H = ctx->state[7];\r
+    i = 0;\r
+\r
+    do\r
+    {\r
+        P( A, B, C, D, E, F, G, H, W[i], K[i] ); i++;\r
+        P( H, A, B, C, D, E, F, G, W[i], K[i] ); i++;\r
+        P( G, H, A, B, C, D, E, F, W[i], K[i] ); i++;\r
+        P( F, G, H, A, B, C, D, E, W[i], K[i] ); i++;\r
+        P( E, F, G, H, A, B, C, D, W[i], K[i] ); i++;\r
+        P( D, E, F, G, H, A, B, C, W[i], K[i] ); i++;\r
+        P( C, D, E, F, G, H, A, B, W[i], K[i] ); i++;\r
+        P( B, C, D, E, F, G, H, A, W[i], K[i] ); i++;\r
+    }\r
+    while( i < 80 );\r
+\r
+    ctx->state[0] += A;\r
+    ctx->state[1] += B;\r
+    ctx->state[2] += C;\r
+    ctx->state[3] += D;\r
+    ctx->state[4] += E;\r
+    ctx->state[5] += F;\r
+    ctx->state[6] += G;\r
+    ctx->state[7] += H;\r
+}\r
+\r
+/*\r
+ * SHA-512 process buffer\r
+ */\r
+void mbedtls_sha512_update( mbedtls_sha512_context *ctx, const unsigned char *input,\r
+                    size_t ilen )\r
+{\r
+    size_t fill;\r
+    unsigned int left;\r
+\r
+    if( ilen == 0 )\r
+        return;\r
+\r
+    left = (unsigned int) (ctx->total[0] & 0x7F);\r
+    fill = 128 - left;\r
+\r
+    ctx->total[0] += (uint64_t) ilen;\r
+\r
+    if( ctx->total[0] < (uint64_t) ilen )\r
+        ctx->total[1]++;\r
+\r
+    if( left && ilen >= fill )\r
+    {\r
+        memcpy( (void *) (ctx->buffer + left), input, fill );\r
+        mbedtls_sha512_process( ctx, ctx->buffer );\r
+        input += fill;\r
+        ilen  -= fill;\r
+        left = 0;\r
+    }\r
+\r
+    while( ilen >= 128 )\r
+    {\r
+        mbedtls_sha512_process( ctx, input );\r
+        input += 128;\r
+        ilen  -= 128;\r
+    }\r
+\r
+    if( ilen > 0 )\r
+        memcpy( (void *) (ctx->buffer + left), input, ilen );\r
+}\r
+\r
+static const unsigned char sha512_padding[SHA512_BLOCK_LENGTH] =\r
+{\r
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
+};\r
+\r
+/*\r
+ * SHA-512 final digest\r
+ */\r
+void mbedtls_sha512_finish( mbedtls_sha512_context *ctx, unsigned char* output )\r
+{\r
+    size_t last, padn;\r
+    uint64_t high, low;\r
+    unsigned char msglen[16];\r
+\r
+    high = ( ctx->total[0] >> 61 )\r
+         | ( ctx->total[1] <<  3 );\r
+    low  = ( ctx->total[0] <<  3 );\r
+\r
+    PUT_UINT64_BE( high, msglen, 0 );\r
+    PUT_UINT64_BE( low,  msglen, 8 );\r
+\r
+    last = (size_t)( ctx->total[0] & 0x7F );\r
+    padn = ( last < 112 ) ? ( 112 - last ) : ( 240 - last );\r
+\r
+    mbedtls_sha512_update( ctx, sha512_padding, padn );\r
+    mbedtls_sha512_update( ctx, msglen, 16 );\r
+\r
+    PUT_UINT64_BE( ctx->state[0], output,  0 );\r
+    PUT_UINT64_BE( ctx->state[1], output,  8 );\r
+    PUT_UINT64_BE( ctx->state[2], output, 16 );\r
+    PUT_UINT64_BE( ctx->state[3], output, 24 );\r
+    PUT_UINT64_BE( ctx->state[4], output, 32 );\r
+    PUT_UINT64_BE( ctx->state[5], output, 40 );\r
+\r
+    if( ctx->is384 == 0 )\r
+    {\r
+        PUT_UINT64_BE( ctx->state[6], output, 48 );\r
+        PUT_UINT64_BE( ctx->state[7], output, 56 );\r
+    }\r
+}\r
+\r
+/*\r
+ * output = SHA-512( input buffer )\r
+ */\r
+void mbedtls_sha512( const unsigned char *input, size_t ilen,\r
+             unsigned char* output, int is384 )\r
+{\r
+    mbedtls_sha512_context ctx;\r
+\r
+    mbedtls_sha512_init( &ctx );\r
+    mbedtls_sha512_starts( &ctx, is384 );\r
+    mbedtls_sha512_update( &ctx, input, ilen );\r
+    mbedtls_sha512_finish( &ctx, output );\r
+    mbedtls_sha512_free( &ctx );\r
+}\r
+\r
+/*\r
+* Compute HMAC_SHA384/512 using key, key length, text to hash, size of the text, output buffer and a switch for SHA384\r
+*/\r
+void HMAC_SHA512(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t* out, int is384){\r
+  int digest_length = SHA512_DIGEST_LENGTH;\r
+  if (is384 == 1) {\r
+    digest_length = SHA384_DIGEST_LENGTH;\r
+  }\r
+\r
+  uint8_t i;\r
+  uint8_t k_ipad[SHA512_BLOCK_LENGTH]; /* inner padding - key XORd with ipad */\r
+  uint8_t k_opad[SHA512_BLOCK_LENGTH]; /* outer padding - key XORd with opad */\r
+  uint8_t buffer[SHA512_BLOCK_LENGTH + digest_length];\r
+\r
+  /* start out by storing key in pads */\r
+  memset(k_ipad, 0, sizeof(k_ipad));\r
+  memset(k_opad, 0, sizeof(k_opad));\r
+\r
+  if (key_length <= SHA512_BLOCK_LENGTH) {\r
+      memcpy(k_ipad, key, key_length);\r
+      memcpy(k_opad, key, key_length);\r
+  }\r
+\r
+  else {\r
+      mbedtls_sha512(key, key_length, k_ipad, is384);\r
+      memcpy(k_opad, k_ipad, SHA512_BLOCK_LENGTH);\r
+  }\r
+\r
+  /* XOR key with ipad and opad values */\r
+  for (i = 0; i < SHA512_BLOCK_LENGTH; i++) {\r
+      k_ipad[i] ^= HMAC_IPAD;\r
+      k_opad[i] ^= HMAC_OPAD;\r
+  }\r
+  \r
+  // perform inner SHA512\r
+  memcpy(buffer, k_ipad, SHA512_BLOCK_LENGTH);\r
+  memcpy(buffer + SHA512_BLOCK_LENGTH, in, n);\r
+  mbedtls_sha512(buffer, SHA512_BLOCK_LENGTH + n, out, is384);\r
+  \r
+  memset(buffer, 0, SHA512_BLOCK_LENGTH + n);\r
+\r
+  // perform outer SHA512\r
+  memcpy(buffer, k_opad, SHA512_BLOCK_LENGTH);\r
+  memcpy(buffer + SHA512_BLOCK_LENGTH, out, digest_length);\r
+  mbedtls_sha512(buffer, SHA512_BLOCK_LENGTH + digest_length, out, is384);\r
+}\r
+\r
+/*\r
+* Compute TOTP_HMAC_SHA384/512 using key, key length, text to hash, size of the text and a switch for SHA384\r
+*/\r
+uint32_t TOTP_HMAC_SHA512(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, int is384){\r
+    int digest_length = SHA512_DIGEST_LENGTH;\r
+    if (is384 == 1) {\r
+      digest_length = SHA384_DIGEST_LENGTH;\r
+    }\r
+\r
+    // STEP 1, get the HMAC-SHA512 hash from counter and key\r
+    uint8_t hash[digest_length];\r
+    HMAC_SHA512(key, key_length, in, n, hash, is384);\r
+\r
+    // STEP 2, apply dynamic truncation to obtain a 4-bytes string\r
+    uint32_t truncated_hash = 0;\r
+    uint8_t _offset = hash[digest_length - 1] & 0xF;\r
+    uint8_t j;\r
+    for (j = 0; j < 4; ++j) {\r
+        truncated_hash <<= 8;\r
+        truncated_hash  |= hash[_offset + j];\r
+    }\r
+\r
+    // STEP 3, compute the OTP value\r
+    truncated_hash &= 0x7FFFFFFF;    //Disabled\r
+    truncated_hash %= 1000000;\r
+\r
+    return truncated_hash;\r
+}
\ No newline at end of file
diff --git a/legacy/lib/TOTP/sha512.h b/legacy/lib/TOTP/sha512.h
new file mode 100644 (file)
index 0000000..991ebfa
--- /dev/null
@@ -0,0 +1,119 @@
+/**\r
+ * \file sha512.h\r
+ *\r
+ * \brief SHA-384 and SHA-512 cryptographic hash function\r
+ *\r
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
+ *  SPDX-License-Identifier: Apache-2.0\r
+ *\r
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+ *  not use this file except in compliance with the License.\r
+ *  You may obtain a copy of the License at\r
+ *\r
+ *  http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ *\r
+ *  This file is part of mbed TLS (https://tls.mbed.org)\r
+ */\r
+#ifndef MBEDTLS_SHA512_H\r
+#define MBEDTLS_SHA512_H\r
+\r
+#define SHA384_DIGEST_LENGTH 48\r
+#define SHA512_DIGEST_LENGTH 64\r
+#define SHA512_BLOCK_LENGTH 128\r
+#define HMAC_IPAD 0x36\r
+#define HMAC_OPAD 0x5c\r
+\r
+#include <stddef.h>\r
+#include <stdint.h>\r
+\r
+/**\r
+ * \brief          SHA-512 context structure\r
+ */\r
+typedef struct\r
+{\r
+    uint64_t total[2];          /*!< number of bytes processed  */\r
+    uint64_t state[8];          /*!< intermediate digest state  */\r
+    unsigned char buffer[SHA512_BLOCK_LENGTH];  /*!< data block being processed */\r
+    int is384;                  /*!< 0 => SHA-512, else SHA-384 */\r
+}\r
+mbedtls_sha512_context;\r
+\r
+/**\r
+ * \brief          Initialize SHA-512 context\r
+ *\r
+ * \param ctx      SHA-512 context to be initialized\r
+ */\r
+void mbedtls_sha512_init( mbedtls_sha512_context *ctx );\r
+\r
+/**\r
+ * \brief          Clear SHA-512 context\r
+ *\r
+ * \param ctx      SHA-512 context to be cleared\r
+ */\r
+void mbedtls_sha512_free( mbedtls_sha512_context *ctx );\r
+\r
+/**\r
+ * \brief          Clone (the state of) a SHA-512 context\r
+ *\r
+ * \param dst      The destination context\r
+ * \param src      The context to be cloned\r
+ */\r
+void mbedtls_sha512_clone( mbedtls_sha512_context *dst,\r
+                           const mbedtls_sha512_context *src );\r
+\r
+/**\r
+ * \brief          SHA-512 context setup\r
+ *\r
+ * \param ctx      context to be initialized\r
+ * \param is384    0 = use SHA512, 1 = use SHA384\r
+ */\r
+void mbedtls_sha512_starts( mbedtls_sha512_context *ctx, int is384 );\r
+\r
+/**\r
+ * \brief          SHA-512 process buffer\r
+ *\r
+ * \param ctx      SHA-512 context\r
+ * \param input    buffer holding the  data\r
+ * \param ilen     length of the input data\r
+ */\r
+void mbedtls_sha512_update( mbedtls_sha512_context *ctx, const unsigned char *input,\r
+                    size_t ilen );\r
+\r
+/**\r
+ * \brief          SHA-512 final digest\r
+ *\r
+ * \param ctx      SHA-512 context\r
+ * \param output   SHA-384/512 checksum result\r
+ */\r
+void mbedtls_sha512_finish( mbedtls_sha512_context *ctx, unsigned char* output );\r
+\r
+/**\r
+ * \brief          Output = SHA-512( input buffer )\r
+ *\r
+ * \param input    buffer holding the  data\r
+ * \param ilen     length of the input data\r
+ * \param output   SHA-384/512 checksum result\r
+ * \param is384    0 = use SHA512, 1 = use SHA384\r
+ */\r
+void mbedtls_sha512( const unsigned char *input, size_t ilen,\r
+             unsigned char* output, int is384 );\r
+\r
+/**\r
+ * \brief          Checkup routine\r
+ *\r
+ * \return         0 if successful, or 1 if the test failed\r
+ */\r
+int mbedtls_sha512_self_test( int verbose );\r
+\r
+/* Internal use */\r
+void mbedtls_sha512_process( mbedtls_sha512_context *ctx, const unsigned char data[SHA512_BLOCK_LENGTH] );\r
+void HMAC_SHA512(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t* out, int is384);\r
+uint32_t TOTP_HMAC_SHA512(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, int is384);\r
+\r
+#endif /* mbedtls_sha512.h */\r
diff --git a/legacy/lib/astrolib/LICENSE.txt b/legacy/lib/astrolib/LICENSE.txt
new file mode 100755 (executable)
index 0000000..26356b0
--- /dev/null
@@ -0,0 +1,9 @@
+Public domain\r
+\r
+THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+SOFTWARE.\r
diff --git a/legacy/lib/astrolib/README.md b/legacy/lib/astrolib/README.md
new file mode 100644 (file)
index 0000000..d63bc46
--- /dev/null
@@ -0,0 +1,9 @@
+# Astrolib
+
+This is a relatively straightforward C port of Greg Miller's [JavaScript astro library](https://github.com/gmiller123456/astrogreg), done by Joey Castillo, for the Sensor Watch Astronomy watch face.
+
+He released his work into the public domain and so I release this into the public domain as well.
+
+I have tested the output of this library against [NASA's Horizons system](https://ssd.jpl.nasa.gov/horizons/app.html#/) and found the results to match (within reason, as we're using a truncated version of VSOP87). Moon calculations still seem a bit iffy, but it doesn't surprise me seeing as there are three calculations involved and the error could stack up.
+
+THIS 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.
diff --git a/legacy/lib/astrolib/astrolib.c b/legacy/lib/astrolib/astrolib.c
new file mode 100644 (file)
index 0000000..f1bacc5
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * Partial C port of Greg Miller's public domain astro library (gmiller@gregmiller.net) 2019
+ * https://github.com/gmiller123456/astrogreg
+ * 
+ * Ported by Joey Castillo for Sensor Watch
+ * https://github.com/joeycastillo/Sensor-Watch/
+ *
+ * Public Domain
+ *
+ * THIS 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.
+ */
+
+#include <math.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "astrolib.h"
+#include "vsop87a_milli.h"
+
+double astro_convert_utc_to_tt(double jd) ;
+double astro_get_GMST(double ut1);
+astro_cartesian_coordinates_t astro_subtract_cartesian(astro_cartesian_coordinates_t a, astro_cartesian_coordinates_t b);
+astro_cartesian_coordinates_t astro_rotate_from_vsop_to_J2000(astro_cartesian_coordinates_t c);
+astro_matrix_t astro_get_x_rotation_matrix(double r);
+astro_matrix_t astro_get_y_rotation_matrix(double r);
+astro_matrix_t astro_get_z_rotation_matrix(double r);
+astro_matrix_t astro_transpose_matrix(astro_matrix_t m);
+astro_matrix_t astro_dot_product(astro_matrix_t a, astro_matrix_t b);
+astro_matrix_t astro_get_precession_matrix(double jd);
+astro_cartesian_coordinates_t astro_matrix_multiply(astro_cartesian_coordinates_t v, astro_matrix_t m);
+astro_cartesian_coordinates_t astro_convert_geodedic_latlon_to_ITRF_XYZ(double lat, double lon, double height);
+astro_cartesian_coordinates_t astro_convert_ITRF_to_GCRS(astro_cartesian_coordinates_t r, double ut1);
+astro_cartesian_coordinates_t astro_convert_coordinates_from_meters_to_AU(astro_cartesian_coordinates_t c);
+astro_cartesian_coordinates_t astro_get_observer_geocentric_coords(double jd, double lat, double lon);
+astro_cartesian_coordinates_t astro_get_body_coordinates(astro_body_t bodyNum, double et);
+astro_cartesian_coordinates_t astro_get_body_coordinates_light_time_adjusted(astro_body_t body, astro_cartesian_coordinates_t origin, double t);
+astro_equatorial_coordinates_t astro_convert_cartesian_to_polar(astro_cartesian_coordinates_t xyz);
+
+//Special "Math.floor()" function used by convertDateToJulianDate()
+static double _astro_special_floor(double d) {
+    if(d > 0) {
+        return floor(d);
+    }
+    return floor(d) - 1;
+}
+
+double astro_convert_date_to_julian_date(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
+    if (month < 3){
+        year = year - 1;
+        month = month + 12;
+    }
+
+    double b = 0;
+    if (!(year < 1582 || (year == 1582 && (month < 10 || (month == 10 && day < 5))))) {
+        double a = _astro_special_floor(year / 100.0);
+        b = 2 - a + _astro_special_floor(a / 4.0);
+    }
+
+    double jd = _astro_special_floor(365.25 * (year + 4716)) + _astro_special_floor(30.6001 * (month + 1)) + day + b - 1524.5;
+    jd += hour / 24.0;
+    jd += minute / 24.0 / 60.0;
+    jd += second / 24.0 / 60.0 / 60.0;
+
+    return jd;
+}
+
+//Return all values in radians.
+//The positions are adjusted for the parallax of the Earth, and the offset of the observer from the Earth's center
+//All input and output angles are in radians!
+astro_equatorial_coordinates_t astro_get_ra_dec(double jd, astro_body_t body, double lat, double lon, bool calculate_precession) {
+    double jdTT = astro_convert_utc_to_tt(jd);
+    double t = astro_convert_jd_to_julian_millenia_since_j2000(jdTT);
+    
+    // Get current position of Earth and the target body
+    astro_cartesian_coordinates_t earth_coords = astro_get_body_coordinates(ASTRO_BODY_EARTH, t);
+    astro_cartesian_coordinates_t body_coords = astro_get_body_coordinates_light_time_adjusted(body, earth_coords, t);
+
+    // Convert to Geocentric coordinate
+    body_coords = astro_subtract_cartesian(body_coords, earth_coords);
+
+    //Rotate ecliptic coordinates to J2000 coordinates
+    body_coords = astro_rotate_from_vsop_to_J2000(body_coords);
+
+    astro_matrix_t precession;
+    // TODO: rotate body for precession, nutation and bias
+    if(calculate_precession) {
+        precession = astro_get_precession_matrix(jdTT);
+        body_coords = astro_matrix_multiply(body_coords, precession);
+    }
+
+    //Convert to topocentric
+    astro_cartesian_coordinates_t observerXYZ = astro_get_observer_geocentric_coords(jdTT, lat, lon);
+
+    if(calculate_precession) {
+        //TODO: rotate observerXYZ for precession, nutation and bias
+        astro_matrix_t precessionInv = astro_transpose_matrix(precession);
+        observerXYZ = astro_matrix_multiply(observerXYZ, precessionInv);
+    }
+
+    body_coords = astro_subtract_cartesian(body_coords, observerXYZ);
+
+    //Convert to topocentric RA DEC by converting from cartesian coordinates to polar coordinates
+    astro_equatorial_coordinates_t retval = astro_convert_cartesian_to_polar(body_coords);
+    
+    retval.declination = M_PI/2.0 - retval.declination;  //Dec.  Offset to make 0 the equator, and the poles +/-90 deg
+    if(retval.right_ascension < 0) retval.right_ascension += 2*M_PI; //Ensure RA is positive
+    
+    return retval;
+}
+
+//Converts a Julian Date in UTC to Terrestrial Time (TT)
+double astro_convert_utc_to_tt(double jd) {
+    //Leap seconds are hard coded, should be updated from the IERS website for other times
+    
+    //TAI = UTC + leap seconds (e.g. 32)
+    //TT=TAI + 32.184
+
+    //return jd + (32.0 + 32.184) / 24.0 / 60.0 / 60.0;
+    return jd + (37.0 + 32.184) / 24.0 / 60.0 / 60.0;
+
+    /*
+    https://data.iana.org/time-zones/tzdb-2018a/leap-seconds.list
+    2272060800  10  # 1 Jan 1972
+    2287785600  11  # 1 Jul 1972
+    2303683200  12  # 1 Jan 1973
+    2335219200  13  # 1 Jan 1974
+    2366755200  14  # 1 Jan 1975
+    2398291200  15  # 1 Jan 1976
+    2429913600  16  # 1 Jan 1977
+    2461449600  17  # 1 Jan 1978
+    2492985600  18  # 1 Jan 1979
+    2524521600  19  # 1 Jan 1980
+    2571782400  20  # 1 Jul 1981
+    2603318400  21  # 1 Jul 1982
+    2634854400  22  # 1 Jul 1983
+    2698012800  23  # 1 Jul 1985
+    2776982400  24  # 1 Jan 1988
+    2840140800  25  # 1 Jan 1990
+    2871676800  26  # 1 Jan 1991
+    2918937600  27  # 1 Jul 1992
+    2950473600  28  # 1 Jul 1993
+    2982009600  29  # 1 Jul 1994
+    3029443200  30  # 1 Jan 1996
+    3076704000  31  # 1 Jul 1997
+    3124137600  32  # 1 Jan 1999
+    3345062400  33  # 1 Jan 2006
+    3439756800  34  # 1 Jan 2009
+    3550089600  35  # 1 Jul 2012
+    3644697600  36  # 1 Jul 2015
+    3692217600  37  # 1 Jan 2017
+    */
+}
+
+double astro_convert_jd_to_julian_millenia_since_j2000(double jd) {
+    return (jd - 2451545.0) / 365250.0;
+}
+
+astro_cartesian_coordinates_t astro_subtract_cartesian(astro_cartesian_coordinates_t a, astro_cartesian_coordinates_t b) {
+    astro_cartesian_coordinates_t retval;
+
+    retval.x = a.x - b.x;
+    retval.y = a.y - b.y;
+    retval.z = a.z - b.z;
+
+    return retval;
+}
+
+// Performs the rotation from ecliptic coordinates to J2000 coordinates for the given vector x
+astro_cartesian_coordinates_t astro_rotate_from_vsop_to_J2000(astro_cartesian_coordinates_t c) {
+    /* From VSOP87.doc
+        X        +1.000000000000  +0.000000440360  -0.000000190919   X
+        Y     =  -0.000000479966  +0.917482137087  -0.397776982902   Y
+        Z FK5     0.000000000000  +0.397776982902  +0.917482137087   Z VSOP87A
+    */
+    astro_cartesian_coordinates_t t;
+    t.x = c.x + c.y * 0.000000440360 + c.z * -0.000000190919;
+    t.y = c.x * -0.000000479966 + c.y * 0.917482137087 + c.z * -0.397776982902;
+    t.z = c.y * 0.397776982902 + c.z * 0.917482137087;
+
+    return t;
+}
+
+double astro_get_GMST(double ut1) {
+    double D = ut1 - 2451545.0;
+    double T = D/36525.0;
+    double gmst = fmod((280.46061837 + 360.98564736629*D + 0.000387933*T*T - T*T*T/38710000.0), 360.0);
+
+    if(gmst<0) {
+        gmst+=360;
+    }
+
+    return gmst/15;
+}
+
+static astro_matrix_t _astro_get_empty_matrix() {
+    astro_matrix_t t;
+    for(uint8_t i = 0; i < 3 ; i++) {
+        for(uint8_t j = 0 ; j < 3 ; j++) {
+            t.elements[i][j] = 0;
+        }
+    }
+    return t;
+}
+
+//Gets a rotation matrix about the x axis.  Angle R is in radians
+astro_matrix_t astro_get_x_rotation_matrix(double r) {
+    astro_matrix_t t = _astro_get_empty_matrix();
+
+    t.elements[0][0]=1;
+    t.elements[0][1]=0;
+    t.elements[0][2]=0;
+    t.elements[1][0]=0;
+    t.elements[1][1]=cos(r);
+    t.elements[1][2]=sin(r);
+    t.elements[2][0]=0;
+    t.elements[2][1]=-sin(r);
+    t.elements[2][2]=cos(r);
+
+    return t;
+}
+
+//Gets a rotation matrix about the y axis.  Angle R is in radians
+astro_matrix_t astro_get_y_rotation_matrix(double r) {
+    astro_matrix_t t = _astro_get_empty_matrix();
+
+    t.elements[0][0]=cos(r);
+    t.elements[0][1]=0;
+    t.elements[0][2]=-sin(r);
+    t.elements[1][0]=0;
+    t.elements[1][1]=1;
+    t.elements[1][2]=0;
+    t.elements[2][0]=sin(r);
+    t.elements[2][1]=0;
+    t.elements[2][2]=cos(r);
+
+    return t;
+}
+
+//Gets a rotation matrix about the z axis.  Angle R is in radians
+astro_matrix_t astro_get_z_rotation_matrix(double r) {
+    astro_matrix_t t = _astro_get_empty_matrix();
+
+    t.elements[0][0]=cos(r);
+    t.elements[0][1]=sin(r);
+    t.elements[0][2]=0;
+    t.elements[1][0]=-sin(r);
+    t.elements[1][1]=cos(r);
+    t.elements[1][2]=0;
+    t.elements[2][0]=0;
+    t.elements[2][1]=0;
+    t.elements[2][2]=1;
+
+    return t;
+}
+
+void astro_print_matrix(char * title, astro_matrix_t matrix);
+void astro_print_matrix(char * title, astro_matrix_t matrix) {
+    printf("%s\n", title);
+    for(uint8_t i = 0; i < 3 ; i++) {
+        printf("\t");
+        for(uint8_t j = 0 ; j < 3 ; j++) {
+            printf("%12f", matrix.elements[i][j]);
+        }
+        printf("\n");
+    }
+    printf("\n");
+}
+
+astro_matrix_t astro_dot_product(astro_matrix_t a, astro_matrix_t b) {
+    astro_matrix_t retval;
+
+    for(uint8_t i = 0; i < 3 ; i++) {
+        for(uint8_t j = 0 ; j < 3 ; j++) {
+            double temp = 0;
+            for(uint8_t k = 0; k < 3 ; k++) {
+                temp += a.elements[i][k] * b.elements[k][j];
+            }
+            retval.elements[i][j]=temp;
+        }
+    }
+
+    return retval;
+}
+
+astro_matrix_t astro_transpose_matrix(astro_matrix_t m) {
+    astro_matrix_t retval;
+    for(uint8_t i = 0; i < 3 ; i++) {
+        for(uint8_t j = 0 ; j < 3 ; j++) {
+            retval.elements[i][j] = m.elements[j][i];
+        }
+    }
+    return retval;
+}
+
+astro_matrix_t astro_get_precession_matrix(double jd) {
+    //2006 IAU Precession.  Implemented from IERS Technical Note No 36 ch5.
+    //https://www.iers.org/SharedDocs/Publikationen/EN/IERS/Publications/tn/TechnNote36/tn36_043.pdf?__blob=publicationFile&v=1
+
+    double t = (jd - 2451545.0) / 36525.0;  //5.2
+    const double Arcsec2Radians = M_PI/180.0/60.0/60.0; //Converts arc seconds used in equations below to radians
+
+    double e0 = 84381.406 * Arcsec2Radians; //5.6.4
+    double omegaA = e0 + ((-0.025754 + (0.0512623 +    (-0.00772503 + (-0.000000467 + 0.0000003337*t) * t) * t) * t) * t) * Arcsec2Radians; //5.39
+    double psiA = ((5038.481507 +      (-1.0790069 + (-0.00114045 + (0.000132851 - 0.0000000951*t) * t) * t) * t) * t) * Arcsec2Radians; //5.39
+    double chiA = ((10.556403 + (-2.3814292 + (-0.00121197 + (0.000170663 - 0.0000000560*t) * t) * t) * t) * t) * Arcsec2Radians; //5.40
+    //Rotation matrix from 5.4.5
+    //(R1(−e0) · R3(psiA) · R1(omegaA) · R3(−chiA))
+    //Above eq rotates from "of date" to J2000, so we reverse the signs to go from J2000 to "of date"
+    astro_matrix_t m1 = astro_get_x_rotation_matrix(e0);
+    astro_matrix_t m2 = astro_get_z_rotation_matrix(-psiA);
+    astro_matrix_t m3 = astro_get_x_rotation_matrix(-omegaA);
+    astro_matrix_t m4 = astro_get_z_rotation_matrix(chiA);
+
+    astro_matrix_t m5 = astro_dot_product(m4, m3);
+    astro_matrix_t m6 = astro_dot_product(m5, m2);
+    astro_matrix_t precessionMatrix = astro_dot_product(m6, m1);
+
+    return precessionMatrix;
+}
+
+astro_cartesian_coordinates_t astro_matrix_multiply(astro_cartesian_coordinates_t v, astro_matrix_t m) {
+    astro_cartesian_coordinates_t t;
+
+    t.x = v.x*m.elements[0][0] + v.y*m.elements[0][1] + v.z*m.elements[0][2];
+    t.y = v.x*m.elements[1][0] + v.y*m.elements[1][1] + v.z*m.elements[1][2];
+    t.z = v.x*m.elements[2][0] + v.y*m.elements[2][1] + v.z*m.elements[2][2];
+
+    return t;
+}
+
+//Converts cartesian XYZ coordinates to polar (e.g. J2000 xyz to Right Accention and Declication)
+astro_equatorial_coordinates_t astro_convert_cartesian_to_polar(astro_cartesian_coordinates_t xyz) {
+    astro_equatorial_coordinates_t t;
+
+    t.distance = sqrt(xyz.x * xyz.x + xyz.y * xyz.y + xyz.z * xyz.z);
+    t.declination = acos(xyz.z / t.distance);
+    t.right_ascension = atan2(xyz.y, xyz.x);
+
+    if(t.declination < 0) t.declination += 2 * M_PI;
+
+    if(t.right_ascension < 0) t.right_ascension += 2 * M_PI;
+
+    return t;
+}
+
+//Convert Geodedic Lat Lon to geocentric XYZ position vector
+//All angles are input as radians
+astro_cartesian_coordinates_t astro_convert_geodedic_latlon_to_ITRF_XYZ(double lat, double lon, double height) {
+    //Algorithm from Explanatory Supplement to the Astronomical Almanac 3rd ed. P294
+    const double a = 6378136.6;
+    const double f = 1 / 298.25642;
+
+    const double C = sqrt(((cos(lat)*cos(lat)) + (1.0-f)*(1.0-f) * (sin(lat)*sin(lat))));
+
+    const double S = (1-f)*(1-f)*C;
+    
+    double h = height;
+
+    astro_cartesian_coordinates_t r;
+    r.x = (a*C+h) * cos(lat) * cos(lon);
+    r.y = (a*C+h) * cos(lat) * sin(lon);
+    r.z = (a*S+h) * sin(lat);
+    
+    return r;
+}
+
+//Convert position vector to celestial "of date" system.
+//g(t)=R3(-GAST) r
+//(Remember to use UT1 for GAST, not ET)
+//All angles are input and output as radians
+astro_cartesian_coordinates_t astro_convert_ITRF_to_GCRS(astro_cartesian_coordinates_t r, double ut1) {
+    //This is a simple rotation matrix implemenation about the Z axis, rotation angle is -GMST
+
+    double GMST = astro_get_GMST(ut1);
+    GMST =- GMST * 15.0 * M_PI / 180.0;
+
+    astro_matrix_t m = astro_get_z_rotation_matrix(GMST);
+    astro_cartesian_coordinates_t t = astro_matrix_multiply(r, m);
+
+    return t;
+}
+
+astro_cartesian_coordinates_t astro_convert_coordinates_from_meters_to_AU(astro_cartesian_coordinates_t c) {
+    astro_cartesian_coordinates_t t;
+    
+    t.x = c.x / 1.49597870691E+11;
+    t.y = c.y / 1.49597870691E+11;
+    t.z = c.z / 1.49597870691E+11;
+    
+    return t;
+}
+
+astro_cartesian_coordinates_t astro_get_observer_geocentric_coords(double jd, double lat, double lon) {
+    astro_cartesian_coordinates_t r = astro_convert_geodedic_latlon_to_ITRF_XYZ(lat, lon,0);
+    r = astro_convert_ITRF_to_GCRS(r, jd);
+    r = astro_convert_coordinates_from_meters_to_AU(r);
+    
+    return r;
+}
+
+//Returns a body's cartesian coordinates centered on the Sun.
+//Requires vsop87a_milli_js, if you wish to use a different version of VSOP87, replace the class name vsop87a_milli below
+astro_cartesian_coordinates_t astro_get_body_coordinates(astro_body_t body, double et) {
+    astro_cartesian_coordinates_t retval = {0};
+    double coords[3];
+    switch(body) {
+        case ASTRO_BODY_SUN: 
+            return retval; //Sun is at the center for vsop87a
+        case ASTRO_BODY_MERCURY:
+             vsop87a_milli_getMercury(et, coords);
+             break;
+        case ASTRO_BODY_VENUS:
+             vsop87a_milli_getVenus(et, coords);
+             break;
+        case ASTRO_BODY_EARTH:
+             vsop87a_milli_getEarth(et, coords);
+             break;
+        case ASTRO_BODY_MARS:
+             vsop87a_milli_getMars(et, coords);
+             break;
+        case ASTRO_BODY_JUPITER:
+             vsop87a_milli_getJupiter(et, coords);
+             break;
+        case ASTRO_BODY_SATURN:
+             vsop87a_milli_getSaturn(et, coords);
+             break;
+        case ASTRO_BODY_URANUS:
+             vsop87a_milli_getUranus(et, coords);
+             break;
+        case ASTRO_BODY_NEPTUNE:
+             vsop87a_milli_getNeptune(et, coords);
+             break;
+        case ASTRO_BODY_EMB:
+             vsop87a_milli_getEmb(et, coords);
+             break;
+        case ASTRO_BODY_MOON:
+            {
+                double earth_coords[3];
+                double emb_coords[3];
+                vsop87a_milli_getEarth(et, earth_coords);
+                vsop87a_milli_getEmb(et, emb_coords);
+                vsop87a_milli_getMoon(earth_coords, emb_coords, coords);
+            }
+             break;
+    }
+
+    retval.x = coords[0];
+    retval.y = coords[1];
+    retval.z = coords[2];
+
+    return retval;
+}
+
+astro_cartesian_coordinates_t astro_get_body_coordinates_light_time_adjusted(astro_body_t body, astro_cartesian_coordinates_t origin, double t) {
+    //Get current position of body
+    astro_cartesian_coordinates_t body_coords = astro_get_body_coordinates(body, t);
+
+    double newT = t;
+
+    for(uint8_t i = 0 ; i < 2 ; i++) {
+        //Calculate light time to body
+        body_coords = astro_subtract_cartesian(body_coords, origin);
+        double distance = sqrt(body_coords.x*body_coords.x + body_coords.y*body_coords.y + body_coords.z*body_coords.z);
+        distance *= 1.496e+11; //Convert from AU to meters
+        double lightTime = distance / 299792458.0;
+
+        //Convert light time to Julian Millenia, and subtract it from the original value of t
+        newT -= lightTime / 24.0 / 60.0 / 60.0 / 365250.0;  
+        //Recalculate body position adjusted for light time
+        body_coords = astro_get_body_coordinates(body, newT);
+    }
+
+    return body_coords;
+}
+
+astro_horizontal_coordinates_t astro_ra_dec_to_alt_az(double jd, double lat, double lon, double ra, double dec) {
+    double GMST = astro_get_GMST(jd) * M_PI/180.0 * 15.0;
+    double h = GMST + lon - ra;
+
+    double sina = sin(dec)*sin(lat) + cos(dec)*cos(h)*cos(lat);
+    double a = asin(sina);
+
+    double cosAz = (sin(dec)*cos(lat) - cos(dec)*cos(h)*sin(lat)) / cos(a);
+    double Az = acos(cosAz);
+
+    if(sin(h) > 0) Az = 2.0*M_PI - Az;
+
+    astro_horizontal_coordinates_t retval;
+    retval.altitude = a;
+    retval.azimuth = Az;
+
+    return retval;
+}
+
+double astro_degrees_to_radians(double degrees) {
+    return degrees * M_PI / 180;
+}
+
+double astro_radians_to_degrees(double radians) {
+    return radians * 180.0 / M_PI;
+}
+
+astro_angle_dms_t astro_radians_to_dms(double radians) {
+    astro_angle_dms_t retval;
+    int8_t sign = (radians < 0) ? -1 : 1;
+    double degrees = fabs(astro_radians_to_degrees(radians));
+
+    retval.degrees = (uint16_t)degrees;
+    double temp = 60.0 * (degrees - retval.degrees);
+    retval.minutes = (uint8_t)temp;
+    retval.seconds = (uint8_t)round(60.0 * (temp - retval.minutes));
+
+    if (retval.seconds > 59) {
+        retval.seconds = 0.0;
+        retval.minutes++;
+    }
+
+    if (retval.minutes > 59) {
+        retval.minutes = 0;
+        retval.degrees++;
+    }
+
+    degrees *= sign;
+
+    return retval;
+}
+
+astro_angle_hms_t astro_radians_to_hms(double radians) {
+    astro_angle_hms_t retval;
+    double degrees = astro_radians_to_degrees(radians);
+    double temp = degrees / 15.0;
+
+    retval.hours = (uint8_t)temp;
+    temp = 60.0 * (temp - retval.hours);
+    retval.minutes = (uint8_t)temp;
+    retval.seconds = (uint8_t)round(60.0 * (temp - retval.minutes));
+
+    if (retval.seconds > 59) {
+        retval.seconds = 0;
+        retval.minutes++;
+    }
+
+    if (retval.minutes > 59) {
+        retval.minutes = 0;
+        retval.hours++;
+    }
+
+    return retval;
+}
diff --git a/legacy/lib/astrolib/astrolib.h b/legacy/lib/astrolib/astrolib.h
new file mode 100644 (file)
index 0000000..2523ead
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Partial C port of Greg Miller's public domain astro library (gmiller@gregmiller.net) 2019
+ * https://github.com/gmiller123456/astrogreg
+ *
+ * Ported by Joey Castillo for Sensor Watch
+ * https://github.com/joeycastillo/Sensor-Watch/
+ *
+ * Public Domain
+ *
+ * THIS 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 ASTROLIB_H_
+#define ASTROLIB_H_
+
+typedef enum {
+    ASTRO_BODY_SUN = 0,
+    ASTRO_BODY_MERCURY,
+    ASTRO_BODY_VENUS,
+    ASTRO_BODY_EARTH,
+    ASTRO_BODY_MARS,
+    ASTRO_BODY_JUPITER,
+    ASTRO_BODY_SATURN,
+    ASTRO_BODY_URANUS,
+    ASTRO_BODY_NEPTUNE,
+    ASTRO_BODY_EMB,
+    ASTRO_BODY_MOON
+} astro_body_t;
+
+typedef struct {
+    double elements[3][3];
+} astro_matrix_t;
+
+typedef struct {
+    double x;
+    double y;
+    double z;
+} astro_cartesian_coordinates_t;
+
+typedef struct {
+    double right_ascension;
+    double declination;
+    double distance;
+} astro_equatorial_coordinates_t;
+
+typedef struct {
+    double altitude;
+    double azimuth;
+} astro_horizontal_coordinates_t;
+
+typedef struct {
+    int16_t degrees;
+    uint8_t minutes;
+    uint8_t seconds; // you may want this to be a float, watch just can't display any more digits
+} astro_angle_dms_t;
+
+typedef struct {
+    uint8_t hours;
+    uint8_t minutes;
+    uint8_t seconds; // you may want this to be a float, watch just can't display any more digits
+} astro_angle_hms_t;
+
+// Convert a date to a julian date. Must be in UTC+0 time zone!
+double astro_convert_date_to_julian_date(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
+
+// Converts a Julan Date to Julian Millenia since J2000, which is what VSOP87 expects as input.
+double astro_convert_jd_to_julian_millenia_since_j2000(double jd);
+
+// Get right ascension / declination for a given body in the list above.
+astro_equatorial_coordinates_t astro_get_ra_dec(double jd, astro_body_t bodyNum, double lat, double lon, bool calculate_precession);
+
+// Convert right ascension / declination to altitude/azimuth for a given location.
+astro_horizontal_coordinates_t astro_ra_dec_to_alt_az(double jd, double lat, double lon, double ra, double dec);
+
+// these are self-explanatory
+double astro_degrees_to_radians(double degrees);
+double astro_radians_to_degrees(double radians);
+astro_angle_dms_t astro_radians_to_dms(double radians);
+astro_angle_hms_t astro_radians_to_hms(double radians);
+
+#endif // ASTROLIB_H_
diff --git a/legacy/lib/base32/base32.c b/legacy/lib/base32/base32.c
new file mode 100644 (file)
index 0000000..548f9a4
--- /dev/null
@@ -0,0 +1,221 @@
+/**
+ * base32 (de)coder implementation as specified by RFC4648.
+ *
+ * Copyright (c) 2010 Adrien Kunysz
+ *
+ * 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.
+ **/
+
+#include <assert.h>  // assert()
+#include <limits.h>  // CHAR_BIT
+
+#include "base32.h"
+
+/**
+ * Let this be a sequence of plain data before encoding:
+ *
+ *  01234567 01234567 01234567 01234567 01234567
+ * +--------+--------+--------+--------+--------+
+ * |< 0 >< 1| >< 2 ><|.3 >< 4.|>< 5 ><.|6 >< 7 >|
+ * +--------+--------+--------+--------+--------+
+ *
+ * There are 5 octets of 8 bits each in each sequence.
+ * There are 8 blocks of 5 bits each in each sequence.
+ *
+ * You probably want to refer to that graph when reading the algorithms in this
+ * file. We use "octet" instead of "byte" intentionnaly as we really work with
+ * 8 bits quantities. This implementation will probably not work properly on
+ * systems that don't have exactly 8 bits per (unsigned) char.
+ **/
+
+static size_t min(size_t x, size_t y)
+{
+       return x < y ? x : y;
+}
+
+static const unsigned char PADDING_CHAR = '=';
+
+/**
+ * Pad the given buffer with len padding characters.
+ */
+static void pad(unsigned char *buf, int len)
+{
+       for (int i = 0; i < len; i++)
+               buf[i] = PADDING_CHAR;
+}
+
+/**
+ * This convert a 5 bits value into a base32 character.
+ * Only the 5 least significant bits are used.
+ */
+static unsigned char encode_char(unsigned char c)
+{
+       static unsigned char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+       return base32[c & 0x1F];  // 0001 1111
+}
+
+/**
+ * Decode given character into a 5 bits value. 
+ * Returns -1 iff the argument given was an invalid base32 character
+ * or a padding character.
+ */
+static int decode_char(unsigned char c)
+{
+       int retval = -1;
+
+       if (c >= 'A' && c <= 'Z')
+               retval = c - 'A';
+       if (c >= '2' && c <= '7')
+               retval = c - '2' + 26;
+
+       assert(retval == -1 || ((retval & 0x1F) == retval));
+
+       return  retval;
+}
+
+/**
+ * Given a block id between 0 and 7 inclusive, this will return the index of
+ * the octet in which this block starts. For example, given 3 it will return 1
+ * because block 3 starts in octet 1:
+ *
+ * +--------+--------+
+ * | ......<|.3 >....|
+ * +--------+--------+
+ *  octet 1 | octet 2
+ */
+static int get_octet(int block)
+{
+       assert(block >= 0 && block < 8);
+       return (block*5) / 8;
+}
+
+/**
+ * Given a block id between 0 and 7 inclusive, this will return how many bits
+ * we can drop at the end of the octet in which this block starts. 
+ * For example, given block 0 it will return 3 because there are 3 bits
+ * we don't care about at the end:
+ *
+ *  +--------+-
+ *  |< 0 >...|
+ *  +--------+-
+ *
+ * Given block 1, it will return -2 because there
+ * are actually two bits missing to have a complete block:
+ *
+ *  +--------+-
+ *  |.....< 1|..
+ *  +--------+-
+ **/
+static int get_offset(int block)
+{
+       assert(block >= 0 && block < 8);
+       return (8 - 5 - (5*block) % 8);
+}
+
+/**
+ * Like "b >> offset" but it will do the right thing with negative offset.
+ * We need this as bitwise shifting by a negative offset is undefined
+ * behavior.
+ */
+static unsigned char shift_right(unsigned char byte, int offset)
+{
+       if (offset > 0)
+               return byte >>  offset;
+       else
+               return byte << -offset;
+}
+
+static unsigned char shift_left(unsigned char byte, int offset)
+{
+       return shift_right(byte, - offset);
+}
+
+/**
+ * Encode a sequence. A sequence is no longer than 5 octets by definition.
+ * Thus passing a length greater than 5 to this function is an error. Encoding
+ * sequences shorter than 5 octets is supported and padding will be added to the
+ * output as per the specification.
+ */
+static void encode_sequence(const unsigned char *plain, int len, unsigned char *coded)
+{
+       assert(CHAR_BIT == 8);  // not sure this would work otherwise
+       assert(len >= 0 && len <= 5);
+
+       for (int block = 0; block < 8; block++) {
+               int octet = get_octet(block);  // figure out which octet this block starts in
+               int junk = get_offset(block);  // how many bits do we drop from this octet?
+
+               if (octet >= len) { // we hit the end of the buffer
+                       pad(&coded[block], 8 - block);
+                       return;
+               }
+
+               unsigned char c = shift_right(plain[octet], junk);  // first part
+
+               if (junk < 0  // is there a second part?
+               &&  octet < len - 1)  // is there still something to read?
+               {
+                       c |= shift_right(plain[octet+1], 8 + junk);
+               }
+               coded[block] = encode_char(c);
+       }
+}
+
+void base32_encode(const unsigned char *plain, size_t len, unsigned char *coded)
+{
+       // All the hard work is done in encode_sequence(),
+       // here we just need to feed it the data sequence by sequence.
+       for (size_t i = 0, j = 0; i < len; i += 5, j += 8) {
+               encode_sequence(&plain[i], min(len - i, 5), &coded[j]);
+       }
+}
+
+static int decode_sequence(const unsigned char *coded, unsigned char *plain)
+{
+       assert(CHAR_BIT == 8);
+       assert(coded && plain);
+
+       plain[0] = 0;
+       for (int block = 0; block < 8; block++) {
+               int offset = get_offset(block);
+               int octet = get_octet(block);
+
+               int c = decode_char(coded[block]);
+               if (c < 0)  // invalid char, stop here
+                       return octet;
+
+               plain[octet] |= shift_left(c, offset);
+               if (offset < 0) {  // does this block overflows to next octet?
+                       assert(octet < 4);
+                       plain[octet+1] = shift_left(c, 8 + offset);
+               }
+       }
+       return 5;
+}
+
+size_t base32_decode(const unsigned char *coded, unsigned char *plain)
+{
+       size_t written = 0;
+       for (size_t i = 0, j = 0; ; i += 8, j += 5) {
+               int n = decode_sequence(&coded[i], &plain[j]);
+               written += n;
+               if (n < 5)
+                       return written;
+       }
+}
diff --git a/legacy/lib/base32/base32.h b/legacy/lib/base32/base32.h
new file mode 100644 (file)
index 0000000..abf2d03
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * base32 (de)coder implementation as specified by RFC4648.
+ *
+ * Copyright (c) 2010 Adrien Kunysz
+ *
+ * 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 __BASE32_H_
+#define __BASE32_H_
+
+#include <stddef.h>   // size_t
+
+/**
+ * Returns the length of the output buffer required to encode len bytes of
+ * data into base32. This is a macro to allow users to define buffer size at
+ * compilation time.
+ */
+#define BASE32_LEN(len)  (((len)/5)*8 + ((len) % 5 ? 8 : 0))
+
+/**
+ * Returns the length of the output buffer required to decode a base32 string
+ * of len characters. Please note that len must be a multiple of 8 as per
+ * definition of a base32 string. This is a macro to allow users to define
+ * buffer size at compilation time.
+ */
+#define UNBASE32_LEN(len)  (((len)/8)*5)
+
+/**
+ * Encode the data pointed to by plain into base32 and store the
+ * result at the address pointed to by coded. The "coded" argument
+ * must point to a location that has enough available space
+ * to store the whole coded string. The resulting string will only
+ * contain characters from the [A-Z2-7=] set. The "len" arguments
+ * define how many bytes will be read from the "plain" buffer.
+ **/
+void base32_encode(const unsigned char *plain, size_t len, unsigned char *coded);
+
+/**
+ * Decode the null terminated string pointed to by coded and write
+ * the decoded data into the location pointed to by plain. The
+ * "plain" argument must point to a location that has enough available
+ * space to store the whole decoded string.
+ * Returns the length of the decoded string. This may be less than
+ * expected due to padding. If an invalid base32 character is found
+ * in the coded string, decoding will stop at that point.
+ **/
+size_t base32_decode(const unsigned char *coded, unsigned char *plain);
+
+#endif
diff --git a/legacy/lib/morsecalc/calc.c b/legacy/lib/morsecalc/calc.c
new file mode 100644 (file)
index 0000000..ed7eb25
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "calc.h"
+#include "calc_fns.h"
+
+#define CALC_NAN (0.0/0.0)
+
+/* calc_init 
+ * Initialize calculator
+ */
+int calc_init(calc_state_t *cs) {    
+    memset(cs->stack, CALC_NAN, N_STACK*sizeof(cs->stack[0]));
+    cs->s = 0; 
+    cs->mem = 0.0;
+    return 0;
+}
+
+/* calc_input_function
+ * Try to execute the token as a calculator function
+ */
+int calc_input_function(calc_state_t *cs, char *token) {
+    for(uint8_t idx=0; idx<sizeof(calc_dict)/sizeof(calc_dict[0]); idx++) {
+        for(uint8_t idxn=0; idxn<calc_dict[idx].n_names; idxn++) {
+            if(0 == strcmp(calc_dict[idx].names[idxn], token)) { // Found a match
+                return (*calc_dict[idx].fn)(cs); // Run calculator function
+            }
+        }
+    }
+    return -1; // Unrecognized function name
+}
+
+/* calc_input_float
+ * Read the token as a float.
+ * For convenience, numerals can be written in binary:
+ * 0     1    2    3    4    5    6    7    8    9
+ * .     -    -.   --   -..  -.-  --.  ---  -... -..-
+ * e     t    n    m    d    k    g    o    b    x
+ * 
+ * Exponent signs must be entered as "p".
+ * Decimal place "." can be entered as "h" (code ....)
+ * Sign "-" can be entered as "Ch digraph" (code ----)
+ * 
+ * e.g. "4.2e-3" can be entered directly or as "4h2pC3"
+ * similarly, "0.0042" can be "eheedn"
+ */ 
+#define REPCHAR(X,Y) for(idx=0; idx<strlen(token); idx++) \
+    if(X==token[idx]) token[idx] = Y
+int calc_input_float(calc_state_t *cs, char *token) {
+    uint8_t idx;
+    REPCHAR('e', '0');
+    REPCHAR('t', '1');
+    REPCHAR('n', '2');
+    REPCHAR('m', '3');
+    REPCHAR('d', '4');
+    REPCHAR('k', '5');
+    REPCHAR('g', '6');
+    REPCHAR('o', '7');
+    REPCHAR('b', '8');
+    REPCHAR('x', '9');
+    REPCHAR('h', '.');
+    REPCHAR('C', '-');
+    REPCHAR('p', 'E');
+    
+    char *endptr;
+    double d = calc_strtof(token, &endptr);
+    if(!endptr || (uint8_t)(endptr-token)<strlen(token)) return -1; // Bad format
+    if(cs->s >= N_STACK) return -2; // Stack full
+    cs->stack[cs->s++] = d;
+    return 0;
+}
+
+/* calc_input
+ * Manipulate the stack using the entered token.
+ * If the token isn't a calculator function, try to convert it to a number and
+ * add it to the stack.
+ *
+ * Return values: 
+ *  0 if function completed successfully.
+ * -1 if token isn't a calculator function and couldn't convert to float.
+ * -2 if stack is too full or too empty
+ * -3 for something else
+ */
+int calc_input(calc_state_t *cs, char *token) {
+    int retval = calc_input_function(cs, token);
+    if(-1 == retval) retval = calc_input_float(cs, token);
+    return retval;
+}
diff --git a/legacy/lib/morsecalc/calc.h b/legacy/lib/morsecalc/calc.h
new file mode 100644 (file)
index 0000000..1fc4749
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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 CALC_H_INCLUDED
+#define CALC_H_INCLUDED 
+
+#include <stdint.h>
+
+#define N_STACK 10 
+
+typedef struct {
+    double stack[N_STACK];
+    double mem;
+    uint8_t s; // # of items in stack 
+} calc_state_t;
+int calc_init(calc_state_t *cs);
+int calc_input(calc_state_t *cs, char *token);
+int calc_input_function(calc_state_t *cs, char *token);
+int calc_input_float(calc_state_t *cs, char *token);
+double calc_strtof(const char *str, char **endptr);
+
+#endif
diff --git a/legacy/lib/morsecalc/calc_fns.c b/legacy/lib/morsecalc/calc_fns.c
new file mode 100644 (file)
index 0000000..9b40a6f
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <math.h>
+
+#include "calc_fns.h" 
+
+#define STACK_CHECK_0_IN_1_OUT if(cs->s >= N_STACK) return -2
+#define STACK_CHECK_1_IN_0_OUT if(cs->s < 1) return -2
+#define STACK_CHECK_1_IN_1_OUT if(cs->s < 1) return -2
+#define STACK_CHECK_2_IN_1_OUT if(cs->s < 2) return -2
+#define STACK_CHECK_2_IN_2_OUT if(cs->s < 2) return -2
+
+static const double to_rad = M_PI/180;
+static const double to_deg = 180/M_PI;
+
+// Stack and memory control
+int calc_delete(calc_state_t *cs) {
+    if(cs->s < 1) return -2; // Check stack
+    cs->s--; 
+    return 0;
+}
+int calc_clear_stack(calc_state_t *cs) {
+    memset(cs->stack, (0.0/0.0), N_STACK*sizeof(cs->stack[0]));
+    cs->s = 0; 
+    return 0;
+}
+int calc_flip(calc_state_t *cs) {
+    STACK_CHECK_2_IN_2_OUT;
+    double buff = cs->stack[cs->s-2];
+    cs->stack[cs->s-2] = cs->stack[cs->s-1];
+    cs->stack[cs->s-1] = buff;
+    return 0;
+}
+int calc_mem_clear(calc_state_t *cs) {
+    cs->mem = 0.0;
+    return 0;
+}
+int calc_mem_recall(calc_state_t *cs) { 
+    STACK_CHECK_0_IN_1_OUT;
+    cs->stack[cs->s++] = cs->mem;
+    return 0;
+}
+int calc_mem_add(calc_state_t *cs) {
+    STACK_CHECK_1_IN_0_OUT;
+    cs->mem += cs->stack[cs->s-1];
+    cs->s--;
+    return 0;
+}
+int calc_mem_subtract(calc_state_t *cs) {
+    STACK_CHECK_1_IN_0_OUT;
+    cs->mem -= cs->stack[cs->s-1];
+    cs->s--;
+    return 0;
+}
+
+// Basic operations
+int calc_add(calc_state_t *cs) {
+    STACK_CHECK_2_IN_1_OUT; 
+    cs->stack[cs->s-2] += cs->stack[cs->s-1];
+    cs->s--;
+    return 0;
+}
+int calc_subtract(calc_state_t *cs) {
+    STACK_CHECK_2_IN_1_OUT; 
+    cs->stack[cs->s-2] -= cs->stack[cs->s-1];
+    cs->s--;
+    return 0;
+}
+int calc_negate(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = -cs->stack[cs->s-1];
+    return 0;
+}
+int calc_multiply(calc_state_t *cs) {
+    STACK_CHECK_2_IN_1_OUT; 
+    cs->stack[cs->s-2] *= cs->stack[cs->s-1];
+    cs->s--;
+    return 0;
+}
+int calc_divide(calc_state_t *cs) {
+    STACK_CHECK_2_IN_1_OUT;
+    cs->stack[cs->s-2] /= cs->stack[cs->s-1];
+    cs->s--;
+    return 0;
+}
+
+int calc_invert(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = 1.0/cs->stack[cs->s-1];
+    return 0;
+}
+
+// Constants
+int calc_e(calc_state_t *cs) {
+    STACK_CHECK_0_IN_1_OUT;
+    cs->stack[cs->s++] = M_E;
+    return 0;
+}
+int calc_pi(calc_state_t *cs) {
+    STACK_CHECK_0_IN_1_OUT;
+    cs->stack[cs->s++] = M_PI;
+    return 0;
+}
+
+// Exponential/logarithmic
+int calc_exp(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = exp(cs->stack[cs->s-1]);
+    return 0;
+}
+int calc_pow(calc_state_t *cs) {
+    STACK_CHECK_2_IN_1_OUT;
+    cs->stack[cs->s-2] = pow(cs->stack[cs->s-2], cs->stack[cs->s-1]);
+    cs->s--;
+    return 0;
+}
+int calc_ln(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = log(cs->stack[cs->s-1]);
+    return 0;
+}
+int calc_log(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = log10(cs->stack[cs->s-1]);
+    return 0;
+}
+int calc_sqrt(calc_state_t *cs)  {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = sqrt(cs->stack[cs->s-1]);
+    return 0;
+}
+
+// Trigonometric
+int calc_sin(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = sin(cs->stack[cs->s-1]);
+    return 0;
+}
+int calc_cos(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = cos(cs->stack[cs->s-1]);
+    return 0;
+}
+int calc_tan(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = tan(cs->stack[cs->s-1]);
+    return 0;
+} 
+int calc_asin(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = asin(cs->stack[cs->s-1]);
+    return 0;
+}
+int calc_acos(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = acos(cs->stack[cs->s-1]);
+    return 0;
+}
+int calc_atan(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = atan(cs->stack[cs->s-1]);
+    return 0;
+}
+int calc_atan2(calc_state_t *cs) {
+    STACK_CHECK_2_IN_1_OUT;
+    cs->stack[cs->s-2] = atan2(cs->stack[cs->s-2], cs->stack[cs->s-1]);
+    cs->s--;
+    return 0;
+}
+
+int calc_sind(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = sin(cs->stack[cs->s-1]*to_rad);
+    return 0;
+}
+int calc_cosd(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = cos(cs->stack[cs->s-1]*to_rad);
+    return 0;
+}
+int calc_tand(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = tan(cs->stack[cs->s-1]*to_rad);
+    return 0;
+}
+int calc_asind(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = asin(cs->stack[cs->s-1])*to_deg;
+    return 0;
+}
+int calc_acosd(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = acos(cs->stack[cs->s-1])*to_deg;
+    return 0;
+}
+int calc_atand(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT; 
+    cs->stack[cs->s-1] = atan(cs->stack[cs->s-1])*to_deg;
+    return 0;
+}
+int calc_atan2d(calc_state_t *cs) {
+    STACK_CHECK_2_IN_1_OUT;
+    cs->stack[cs->s-2] = atan2(cs->stack[cs->s-2], cs->stack[cs->s-1])*to_deg;
+    cs->s--;
+    return 0;
+}
+int calc_torad(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT;
+    cs->stack[cs->s-1] = cs->stack[cs->s-1]*to_rad;
+    return 0;
+}
+int calc_todeg(calc_state_t *cs) {
+    STACK_CHECK_1_IN_1_OUT;
+    cs->stack[cs->s-1] = cs->stack[cs->s-1]*to_deg;
+    return 0;
+}
+
diff --git a/legacy/lib/morsecalc/calc_fns.h b/legacy/lib/morsecalc/calc_fns.h
new file mode 100644 (file)
index 0000000..15f56b0
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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.
+ */
+
+#include "calc.h"
+
+// Stack and register control
+int calc_delete(calc_state_t *cs);
+int calc_clear_stack(calc_state_t *cs);
+int calc_flip(calc_state_t *cs);
+int calc_mem_clear(calc_state_t *cs);
+int calc_mem_recall(calc_state_t *cs);
+int calc_mem_add(calc_state_t *cs);
+int calc_mem_subtract(calc_state_t *cs);
+
+// Basic operations
+int calc_add(calc_state_t *cs);
+int calc_subtract(calc_state_t *cs);
+int calc_negate(calc_state_t *cs);
+int calc_multiply(calc_state_t *cs);
+int calc_divide(calc_state_t *cs);
+int calc_invert(calc_state_t *cs);
+
+// Constants
+int calc_e(calc_state_t *cs);
+int calc_pi(calc_state_t *cs);
+
+// Exponential/logarithmic
+int calc_exp(calc_state_t *cs);
+int calc_pow(calc_state_t *cs);
+int calc_ln(calc_state_t *cs);
+int calc_log(calc_state_t *cs);
+int calc_sqrt(calc_state_t *cs);
+
+// Trigonometric
+int calc_sin(calc_state_t *cs);
+int calc_cos(calc_state_t *cs);
+int calc_tan(calc_state_t *cs);
+int calc_asin(calc_state_t *cs);
+int calc_acos(calc_state_t *cs);
+int calc_atan(calc_state_t *cs);
+int calc_atan2(calc_state_t *cs);
+int calc_sind(calc_state_t *cs);
+int calc_cosd(calc_state_t *cs);
+int calc_tand(calc_state_t *cs);
+int calc_asind(calc_state_t *cs);
+int calc_acosd(calc_state_t *cs);
+int calc_atand(calc_state_t *cs);
+int calc_atan2d(calc_state_t *cs);
+int calc_torad(calc_state_t *cs);
+int calc_todeg(calc_state_t *cs);
+
+// Dictionary definition
+typedef int (*calc_fn_t)(calc_state_t *cs);
+typedef struct {
+    uint8_t n_names; // Number of aliases
+    const char ** names; // Token to use to run this function
+    calc_fn_t fn; // Pointer to function 
+} calc_dict_entry_t;
+
+static const calc_dict_entry_t calc_dict[] = {
+    // Stack and register control
+    {1, (const char*[]){"x"}, &calc_delete},
+    {1, (const char*[]){"xx"}, &calc_clear_stack},
+    {1, (const char*[]){"xxx"}, &calc_init},
+    {1, (const char*[]){"f"}, &calc_flip},
+    {1, (const char*[]){"mc"}, &calc_mem_clear},
+    {1, (const char*[]){"mr"}, &calc_mem_recall},
+    {1, (const char*[]){"ma"}, &calc_mem_add},
+    {1, (const char*[]){"ms"}, &calc_mem_subtract},
+
+    // Basic operations
+    {1, (const char*[]){"a"}, &calc_add}, 
+    {1, (const char*[]){"s"}, &calc_subtract},
+    {1, (const char*[]){"n"}, &calc_negate},
+    {1, (const char*[]){"m"}, &calc_multiply},
+    {1, (const char*[]){"d"}, &calc_divide},
+    {1, (const char*[]){"i"}, &calc_invert},
+    
+    // Constants
+    {1, (const char*[]){"e"}, &calc_e}, 
+    {1, (const char*[]){"pi"}, &calc_pi}, 
+    
+    // Exponential/logarithmic
+    {1, (const char*[]){"exp"}, &calc_exp},
+    {1, (const char*[]){"pow"}, &calc_pow}, 
+    {1, (const char*[]){"ln"}, &calc_ln}, 
+    {1, (const char*[]){"log"}, &calc_log},
+    {1, (const char*[]){"sqrt"}, &calc_sqrt},
+    
+    // Trigonometric 
+    {2, (const char*[]){"sin", "sn"}, &calc_sin},
+    {1, (const char*[]){"cos"}, &calc_cos},
+    {1, (const char*[]){"tan"}, &calc_tan},
+    {1, (const char*[]){"asin"}, &calc_asin},
+    {1, (const char*[]){"acos"}, &calc_acos},
+    {1, (const char*[]){"atan"}, &calc_atan}, 
+    {1, (const char*[]){"atan2"}, &calc_atan2},
+    {1, (const char*[]){"sind"}, &calc_sind},
+    {1, (const char*[]){"cosd"}, &calc_cosd},
+    {1, (const char*[]){"tand"}, &calc_tand},
+    {1, (const char*[]){"asind"}, &calc_asind},
+    {1, (const char*[]){"acosd"}, &calc_acosd},
+    {1, (const char*[]){"atand"}, &calc_atand}, 
+    {1, (const char*[]){"atan2d"}, &calc_atan2d}, 
+    {1, (const char*[]){"tor"}, &calc_torad}, 
+    {1, (const char*[]){"tod"}, &calc_todeg}, 
+}; 
diff --git a/legacy/lib/morsecalc/calc_strtof.c b/legacy/lib/morsecalc/calc_strtof.c
new file mode 100644 (file)
index 0000000..35d39fa
--- /dev/null
@@ -0,0 +1,144 @@
+//
+// strtod.c
+//
+// Convert string to double 
+//
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 
+// 1. Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer.  
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.  
+// 3. Neither the name of the project nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission. 
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+// SUCH DAMAGE.
+// 
+
+#include <ctype.h>
+#include <math.h>
+#include <float.h>
+#include <stdlib.h>
+
+#include "calc.h"
+
+double calc_strtof(const char *str, char **endptr) {
+  double number;
+  int exponent;
+  int negative;
+  char *p = (char *) str;
+  double p10;
+  int n;
+  int num_digits;
+  int num_decimals;
+
+  // Skip leading whitespace
+  while (isspace((int) *p)) p++;
+
+  // Handle optional sign
+  negative = 0;
+  switch (*p) {
+    case '-': negative = 1; // Fall through to increment position
+    __attribute__ ((fallthrough));
+    case '+': p++;
+  }
+
+  number = 0.;
+  exponent = 0;
+  num_digits = 0;
+  num_decimals = 0;
+
+  // Process string of digits
+  while (isdigit((int) *p)) {
+    number = number * 10. + (*p - '0');
+    p++;
+    num_digits++;
+  }
+
+  // Process decimal part
+  if (*p == '.') {
+    p++;
+
+    while (isdigit((int) *p)) {
+      number = number * 10. + (*p - '0');
+      p++;
+      num_digits++;
+      num_decimals++;
+    }
+
+    exponent -= num_decimals;
+  }
+
+  if (num_digits == 0) {
+    if (endptr) *endptr = p;
+    return 0.0;
+  }
+
+  // Correct for sign
+  if (negative) number = -number;
+
+  // Process an exponent string
+  if (*p == 'e' || *p == 'E') {
+    // Handle optional sign
+    negative = 0;
+    switch (*++p) {
+      case '-': negative = 1;   // Fall through to increment pos
+      __attribute__ ((fallthrough));
+      case '+': p++;
+    }
+
+    // Process string of digits
+    n = 0;
+    while (isdigit((int) *p)) {
+      n = n * 10 + (*p - '0');
+      p++;
+    }
+
+    if (negative) {
+      exponent -= n;
+    } else {
+      exponent += n;
+    }
+  }
+
+  if (exponent < DBL_MIN_EXP  || exponent > DBL_MAX_EXP) {
+    return HUGE_VAL;
+  }
+
+  // Scale the result
+  p10 = 10.;
+  n = exponent;
+  if (n < 0) n = -n;
+  while (n) {
+    if (n & 1) {
+      if (exponent < 0) {
+        number /= p10;
+      } else {
+        number *= p10;
+      }
+    }
+    n >>= 1;
+    p10 *= p10;
+  }
+
+  if (endptr) *endptr = p;
+
+  return number;
+}
+
diff --git a/legacy/lib/morsecalc/morsecalc_display.c b/legacy/lib/morsecalc/morsecalc_display.c
new file mode 100644 (file)
index 0000000..68f06fb
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <math.h>
+
+#include "watch_private_display.h"
+#include "morsecalc_display.h"
+
+// Display float on screen
+void morsecalc_display_float(double d) { 
+    // Special cases 
+    if(d == 0) {
+        watch_display_string("     0", 4); 
+        return;
+    }
+    else if(isnan(d)) {
+        watch_display_string("   nan", 4);
+        return;
+    }
+    else if(d == (1.0)/(0.0)) {
+        watch_display_string("   inf", 4);
+        return;
+    }
+    else if(d == (-1.0)/(0.0)) {
+        watch_display_character('X', 1);
+        watch_display_string("   inf", 4);
+        return;
+    }
+
+    // Record number properties
+    // Sign
+    int is_negative = d<0;
+    if(is_negative) d = -d; 
+
+    // Order of magnitude
+    int om = (int) floor(log(d)/log(10));
+    int om_is_negative = (om<0);
+
+    // Get the first 4 significant figures
+    int digits;
+    digits = round(d*pow(10.0, 3-om));
+    if(digits>9999) {
+        digits = 1000;
+        om++;
+    }
+
+    // Print signs
+    if(is_negative) {
+               // Xi; see https://joeycastillo.github.io/Sensor-Watch-Documentation/segmap
+               watch_set_pixel(0,11);
+               watch_set_pixel(2,12);
+               watch_set_pixel(2,11); 
+       }
+    else watch_display_character(' ', 1); 
+    if(om_is_negative) watch_set_pixel(1,9); 
+    else watch_display_character(' ', 2); 
+
+    // Print first 4 significant figures
+    watch_display_character('0'+(digits/1000)%10, 4);
+    watch_display_character('0'+(digits/100 )%10, 5);
+    watch_display_character('0'+(digits/10  )%10, 6);
+    watch_display_character('0'+(digits/1   )%10, 7);
+
+    // Prinat exponent
+    if(om_is_negative) om = -om; // Make exponent positive for display
+    if(om<=99) {
+        watch_display_character('0'+(om/10  )%10, 8);
+        watch_display_character('0'+(om/1   )%10, 9);
+    } else { // Over/underflow
+        if(om_is_negative) watch_display_string("    uf", 4);
+        else watch_display_string("    of", 4);
+        if(om<9999) { // Use main display to show order of magnitude
+            // (Should always succeed; max double is <2e308)
+            watch_display_character('0'+(om/1000)%10, 4);
+            watch_display_character('0'+(om/100 )%10, 5);
+            watch_display_character('0'+(om/10  )%10, 6);
+            watch_display_character('0'+(om/1   )%10, 7);
+        }
+    }
+    return;
+}
+
+// Print current input token
+void morsecalc_display_token(morsecalc_state_t *mcs) {
+    watch_display_string("          ", 0); // Clear display
+
+    // Print morse code buffer
+    char c = MORSECODE_TREE[mcs->mc]; // Decode the morse code buffer's current contents
+    if('\0' == c) c = ' '; // Needed for watch_display_character
+    watch_display_character(c, 0); // Display current morse code char in mode position
+    
+    unsigned int v = mcs->mc+1;
+    char bidx = 0; while (v >>= 1) bidx++; 
+    watch_display_character('0'+bidx, 3); // Display buffer position in top right
+
+    // Print last 6 chars of current input line 
+    uint8_t nlen = strlen(mcs->token); // number of characters in token
+    uint8_t nprint = min(nlen,6); // number of characters to print
+    watch_display_string(mcs->token+nlen-nprint, 10-nprint); // print right-aligned
+    return;
+}
+
+// Print stack or memory register contents. 
+void morsecalc_display_stack(morsecalc_state_t * mcs) {
+    watch_display_string("          ", 0); // Clear display
+
+    char c = MORSECODE_TREE[mcs->mc]; 
+    if('m' == c) { // Display memory 
+        morsecalc_display_float(mcs->cs->mem);
+        watch_display_character(c, 0);
+    } 
+    else {
+        // If the morse code buffer has a numeral in it, print that stack item 
+        // Otherwise print top of stack
+        uint8_t idx = 0;
+        if(c >= '0' && c <= '9') idx = c - '0';
+        if(idx >= mcs->cs->s) watch_display_string(" empty", 4); // Stack empty
+        else morsecalc_display_float(mcs->cs->stack[mcs->cs->s-1-idx]); // Print stack item
+
+        watch_display_character('0'+idx, 0); // Print which stack item this is top center
+    }
+    watch_display_character('0'+(mcs->cs->s), 3); // Print the # of stack items top right 
+    return;
+}
+
diff --git a/legacy/lib/morsecalc/morsecalc_display.h b/legacy/lib/morsecalc/morsecalc_display.h
new file mode 100644 (file)
index 0000000..74f8b0c
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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.
+ */
+
+#include "morsecalc_face.h"
+
+// Display float on screen
+void morsecalc_display_float(double d);
+
+// Print current input token
+void morsecalc_display_token(morsecalc_state_t *mcs);
+
+// Print stack or memory register contents. 
+void morsecalc_display_stack(morsecalc_state_t *mcs);
+
diff --git a/legacy/lib/morsecalc/test_morsecalc.c b/legacy/lib/morsecalc/test_morsecalc.c
new file mode 100644 (file)
index 0000000..fc640ba
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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.
+ */
+
+// Computer console interface to calc and morsecode for testing without involving watch stuff.
+// cc calc_strtof.c calc.c calc_fns.c test_morsecalc.c -lm
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "calc.h"
+#include "calc_fns.h"
+
+int main(void) {
+    calc_state_t cs;
+    calc_init(&cs);
+    
+    char * word = malloc(0);
+    unsigned int nword = 0;
+    char c;
+    int retval = 0;
+    for(unsigned int ii = 0; ii < 100; ii++) {
+        c = getchar();
+        word = realloc(word, (++nword)*sizeof(char));
+        word[nword-1] = c;    
+        if((nword > 0) && isspace(c)) { // Word is finished
+            word[nword-1] = '\0';
+            retval = calc_input(&cs, word); // Submit word
+            word = realloc(word, 0); nword = 0; // Clear word
+            
+            switch(retval) {
+                case 0: printf("Success.\n"); break; 
+                case -1: printf("Bad command.\n"); break;
+                case -2: printf("Stack over/underflow.\n"); break;
+                case -3: printf("Error.\n"); break;
+            }
+            if(cs.s > 0) printf("[%i]: %.4f\n", cs.s, cs.stack[cs.s-1]);
+            else printf("[%i]\n", cs.s);
+        }
+    }
+    
+    free(word);
+    return 0;
+}
diff --git a/legacy/lib/smallchesslib/smallchesslib.h b/legacy/lib/smallchesslib/smallchesslib.h
new file mode 100644 (file)
index 0000000..7ac8542
--- /dev/null
@@ -0,0 +1,3704 @@
+#ifndef SMALLCHESSLIB_H
+#define SMALLCHESSLIB_H
+
+/**
+  @file smallchesslib.h
+
+  Small and simple single header C99 public domain chess library and engine.
+
+  author: Miloslav Ciz (drummyfish)
+  license: CC0 1.0 (public domain)
+           found at https://creativecommons.org/publicdomain/zero/1.0/
+           + additional waiver of all IP
+  version: 0.8d
+
+  Default notation format for this library is a coordinate one, i.e.
+  
+    squarefrom squareto [promotedpiece]
+
+  e.g.: e2e4 or A2A1q
+
+  This work's goal is to never be encumbered by any exclusive intellectual
+  property rights. The work is therefore provided under CC0 1.0 + additional
+  WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
+  intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
+  INTELLECTUAL PROPERTY RGHTS is as follows:
+
+  Each contributor to this work agrees that they waive any exclusive rights,
+  including but not limited to copyright, patents, trademark, trade dress,
+  industrial design, plant varieties and trade secrets, to any and all ideas,
+  concepts, processes, discoveries, improvements and inventions conceived,
+  discovered, made, designed, researched or developed by the contributor either
+  solely or jointly with others, which relate to this work or result from this
+  work. Should any waiver of such right be judged legally invalid or
+  ineffective under applicable law, the contributor hereby grants to each
+  affected person a royalty-free, non transferable, non sublicensable, non
+  exclusive, irrevocable and unconditional license to this right.
+*/
+
+#include <stdint.h>
+
+#ifndef SCL_DEBUG_AI
+/** AI will print out a Newick-like tree of searched moves. */
+  #define SCL_DEBUG_AI 0
+#endif
+
+/**
+  Maximum number of moves a chess piece can have (a queen in the middle of the
+  board).
+*/
+#define SCL_CHESS_PIECE_MAX_MOVES 25
+#define SCL_BOARD_SQUARES 64
+
+typedef uint8_t (*SCL_RandomFunction)(void);
+
+#if SCL_COUNT_EVALUATED_POSITIONS
+  uint32_t SCL_positionsEvaluated = 0; /**< If enabled by 
+                                            SCL_COUNT_EVALUATED_POSITIONS, this
+                                            will increment with every
+                                            dynamically evaluated position (e.g.
+                                            when AI computes its move). */
+#endif
+
+#ifndef SCL_CALL_WDT_RESET
+  #define SCL_CALL_WDT_RESET 0 /**< Option that should be enabled on some
+                                    Arduinos. If 1, call to watchdog timer
+                                    reset will be performed during dynamic
+                                    evaluation (without it if AI takes long the
+                                    program will reset). */
+#endif
+
+/**
+  Returns a pseudorandom byte. This function has a period 256 and returns each
+  possible byte value exactly once in the period.
+*/
+uint8_t SCL_randomSimple(void);
+void SCL_randomSimpleSeed(uint8_t seed);
+
+/**
+  Like SCL_randomSimple, but internally uses a 16 bit value, so the period is
+  65536.
+*/
+uint8_t SCL_randomBetter(void);
+void SCL_randomBetterSeed(uint16_t seed);
+
+#ifndef SCL_EVALUATION_FUNCTION
+  /**
+    If defined, AI will always use the static evaluation function with this
+    name. This helps avoid pointers to functions and can be faster but the
+    function can't be changed at runtime.
+  */
+  #define SCL_EVALUATION_FUNCTION
+  #undef SCL_EVALUATION_FUNCTION
+#endif
+
+#ifndef SCL_960_CASTLING
+  /**
+    If set, chess 960 (Fisher random) castling will be considered by the library
+    rather than normal castling. 960 castling is slightly different (e.g.
+    requires the inital rook positions to be stored in board state). The
+    castling move is performed as "capturing own rook".
+  */
+  #define SCL_960_CASTLING 0
+#endif
+
+#ifndef SCL_ALPHA_BETA
+  /**
+    Turns alpha-beta pruning (AI optimization) on or off. This can gain
+    performance and should normally be turned on. AI behavior should not
+    change at all.
+  */
+  #define SCL_ALPHA_BETA 1
+#endif
+
+/**
+  A set of game squares as a bit array, each bit representing one game square.
+  Useful for representing e.g. possible moves. To easily iterate over the set
+  use provided macros (SCL_SQUARE_SET_ITERATE, ...).
+*/
+typedef uint8_t SCL_SquareSet[8];
+
+#define SCL_SQUARE_SET_EMPTY {0, 0, 0, 0, 0, 0, 0, 0}
+
+void SCL_squareSetClear(SCL_SquareSet squareSet);
+void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square);
+uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square);
+uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet);
+uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet);
+
+/**
+  Returns a random square from a square set.
+*/
+uint8_t SCL_squareSetGetRandom(const SCL_SquareSet squareSet,
+  SCL_RandomFunction randFunc);
+
+#define SCL_SQUARE_SET_ITERATE_BEGIN(squareSet) \
+  { uint8_t iteratedSquare = 0;\
+    uint8_t iterationEnd = 0;\
+    for (int8_t _i = 0; _i < 8 && !iterationEnd; ++_i) {\
+      uint8_t _row = squareSet[_i];\
+      if (_row == 0) { iteratedSquare += 8; continue; }\
+      \
+      for (uint8_t _j = 0; _j < 8 && !iterationEnd; ++_j) {\
+        if (_row & 0x01) {
+/*
+  Between SCL_SQUARE_SET_ITERATE_BEGIN and _END iteratedSquare variable
+  represents the next square contained in the set. To break out of the
+  iteration set iterationEnd to 1.
+*/
+
+#define SCL_SQUARE_SET_ITERATE_END }\
+  _row >>= 1;\
+  iteratedSquare++;}\
+  } /*for*/ }
+
+#define SCL_SQUARE_SET_ITERATE(squareSet,command)\
+  SCL_SQUARE_SET_ITERATE_BEGIN(squareSet)\
+  { command }\
+  SCL_SQUARE_SET_ITERATE_END
+#define SCL_BOARD_STATE_SIZE 69
+
+/**
+  Represents chess board state as a string in this format:
+  - First 64 characters represent the chess board (A1, B1, ... H8), each field
+    can be either a piece (PRNBKQprnbkq) or empty ('.'). I.e. the state looks
+    like this:
+
+      0 (A1) RNBQKBNR 
+             PPPPPPPP
+             ........
+             ........
+             ........
+             ........
+             pppppppp
+             rnbqkbnr 63 (H8)
+
+  - After this more bytes follow to represent global state, these are:
+    - 64: bits holding en-passant and castling related information:
+      - bits 0-3 (lsb): Column of the pawn that can, in current turn, be
+        taken by en-passant (0xF means no pawn can be taken this way).
+      - bit 4: Whether white is not prevented from short castling by previous
+        king or rook movement.
+      - bit 5: Same as 4, but for long castling.
+      - bit 6: Same as 4, but for black.
+      - bit 7: Same as 4, but for black and long castling.
+    - 65: Number saying the number of ply (half-moves) that have already been
+      played, also determining whose turn it currently is.
+    - 66: Move counter used in the 50 move rule, says the number of ply since
+      the last pawn move or capture.
+    - 67: Extra byte, left for storing additional info in variants. For normal
+      chess this byte should always be 0.
+    - 68: The last byte is always 0 to properly terminate the string in case
+      someone tries to print it.
+  - The state is designed so as to be simple and also print-friendly, i.e. you
+    can simply print it with line break after 8 characters to get a human
+    readable representation of the board.
+
+  NOTE: there is a much more compact representation which however trades some
+  access speed which would affect the AI performance and isn't print friendly,
+  so we don't use it. In it each square takes 4 bits, using 15 out of 16
+  possible values (empty square and W and B pieces including 2 types of pawns,
+  one "en-passant takeable"). Then only one extra byte needed is for castling
+  info (4 bits) and ply count (4 bits).
+*/
+typedef char SCL_Board[SCL_BOARD_STATE_SIZE];
+
+#define SCL_BOARD_ENPASSANT_CASTLE_BYTE 64
+#define SCL_BOARD_PLY_BYTE 65
+#define SCL_BOARD_MOVE_COUNT_BYTE 66
+#define SCL_BOARD_EXTRA_BYTE 67
+
+#if SCL_960_CASTLING
+  #define _SCL_EXTRA_BYTE_VALUE (0 | (7 << 3)) // rooks on classic positions
+#else
+  #define _SCL_EXTRA_BYTE_VALUE 0
+#endif
+
+#define SCL_BOARD_START_STATE \
+  {82, 78, 66, 81, 75, 66, 78, 82,\
+   80, 80, 80, 80, 80, 80, 80, 80,\
+   46, 46, 46, 46, 46, 46, 46, 46,\
+   46, 46, 46, 46, 46, 46, 46, 46,\
+   46, 46, 46, 46, 46, 46, 46, 46,\
+   46, 46, 46, 46, 46, 46, 46, 46,\
+   112,112,112,112,112,112,112,112,\
+   114,110,98, 113,107,98, 110,114,\
+   (char) 0xff,0,0,_SCL_EXTRA_BYTE_VALUE,0}
+
+#define SCL_FEN_START \
+  "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
+
+#define SCL_FEN_HORDE \
+  "ppp2ppp/pppppppp/pppppppp/pppppppp/3pp3/8/PPPPPPPP/RNBQKBNR w KQ - 0 1"
+
+#define SCL_FEN_UPSIDE_DOWN \
+  "RNBKQBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbkqbnr w - - 0 1"
+
+#define SCL_FEN_PEASANT_REVOLT \
+  "1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
+
+#define SCL_FEN_ENDGAME \
+  "4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
+
+#define SCL_FEN_KNIGHTS \
+  "N6n/1N4n1/2N2n2/3Nn3/k2nN2K/2n2N2/1n4N1/n6N w - - 0 1"
+
+/**
+  Holds an info required to undo a single move.
+*/
+typedef struct
+{
+  uint8_t squareFrom;      ///< start square
+  uint8_t squareTo;        ///< target square
+  char enPassantCastle;    ///< previous en passant/castle byte
+  char moveCount;          ///< previous values of the move counter byte
+  uint8_t other;           /**< lowest 7 bits: previous value of target square,
+                                highest bit: if 1 then the move was promotion or
+                                en passant */
+} SCL_MoveUndo;
+
+#define SCL_GAME_STATE_PLAYING         0x00
+#define SCL_GAME_STATE_WHITE_WIN       0x01
+#define SCL_GAME_STATE_BLACK_WIN       0x02
+#define SCL_GAME_STATE_DRAW            0x10 ///< further unspecified draw
+#define SCL_GAME_STATE_DRAW_STALEMATE  0x11 ///< draw by stalemate
+#define SCL_GAME_STATE_DRAW_REPETITION 0x12 ///< draw by repetition
+#define SCL_GAME_STATE_DRAW_50         0x13 ///< draw by 50 move rule
+#define SCL_GAME_STATE_DRAW_DEAD       0x14 ///< draw by dead position
+#define SCL_GAME_STATE_END             0xff ///< end without known result
+
+/**
+  Converts square in common notation (e.g. 'c' 8) to square number. Only accepts
+  lowercase column.
+*/
+#define SCL_SQUARE(colChar,rowInt) (((rowInt) - 1) * 8 + ((colChar) - 'a'))
+#define SCL_S(c,r) SCL_SQUARE(c,r)
+
+void SCL_boardInit(SCL_Board board);
+void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo);
+
+/**
+  Initializes given chess 960 (Fisher random) position. If SCL_960_CASTLING
+  is not set, castling will be disabled by this function.
+*/
+void SCL_boardInit960(SCL_Board board, uint16_t positionNumber);
+
+void SCL_boardDisableCastling(SCL_Board board);
+
+uint32_t SCL_boardHash32(const SCL_Board board);
+
+#define SCL_PHASE_OPENING 0
+#define SCL_PHASE_MIDGAME 1
+#define SCL_PHASE_ENDGAME 2
+
+/**
+  Estimates the game phase: opening, midgame or endgame.
+*/
+uint8_t SCL_boardEstimatePhase(SCL_Board board);
+
+/**
+  Sets the board position. The input string should be 64 characters long zero
+  terminated C string representing the board as squares A1, A2, ..., H8 with
+  each char being either a piece (RKBKQPrkbkqp) or an empty square ('.').
+*/
+void SCL_boardSetPosition(SCL_Board board, const char *pieces,
+  uint8_t castlingEnPassant, uint8_t moveCount, uint8_t ply);
+
+uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2);
+
+/**
+  Function that prints out a single character. This is passed to printing
+  functions.
+*/
+typedef void (*SCL_PutCharFunction)(char);
+
+/**
+  Gets a random move on given board for the player whose move it is.
+*/
+void SCL_boardRandomMove(SCL_Board board, SCL_RandomFunction randFunc,
+  uint8_t *squareFrom, uint8_t *squareTo, char *resultProm);
+
+#define SCL_FEN_MAX_LENGTH 90
+
+/**
+  Converts a position to FEN (Forsyth–Edwards Notation) string. The string has
+  to have at least SCL_FEN_MAX_LENGTH bytes allocated to guarantee the
+  function won't write to unallocated memory. The string will be terminated by
+  0 (this is included in SCL_FEN_MAX_LENGTH). The number of bytes written
+  (including the terminating 0) is returned.
+*/
+uint8_t SCL_boardToFEN(SCL_Board board, char *string);
+
+/**
+  Loads a board from FEN (Forsyth–Edwards Notation) string. Returns 1 on
+  success, 0 otherwise. XFEN isn't supported fully but a start position in
+  chess960 can be loaded with this function. 
+*/
+uint8_t SCL_boardFromFEN(SCL_Board board, const char *string);
+
+/**
+  Returns an approximate/heuristic board rating as a number, 0 meaning equal
+  chances for both players, positive favoring white, negative favoring black.
+*/
+typedef int16_t (*SCL_StaticEvaluationFunction)(SCL_Board);
+
+/* 
+  NOTE: int8_t as a return value was tried for evaluation function, which would
+  be simpler, but it fails to capture important non-material position
+  differences counted in fractions of pawn values, hence we have to use int16_t.
+*/
+
+/**
+  Basic static evaluation function. WARNING: this function supposes a standard
+  chess game, for non-standard positions it may either not work well or even
+  crash the program. You should use a different function for non-standard games.
+*/
+int16_t SCL_boardEvaluateStatic(SCL_Board board);
+
+/**
+  Dynamic evaluation function (search), i.e. unlike SCL_boardEvaluateStatic,
+  this one performs a recursive search for deeper positions to get a more
+  accurate score. Of course, this is much slower and hugely dependent on
+  baseDepth (you mostly want to keep this under 5).
+*/
+int16_t SCL_boardEvaluateDynamic(SCL_Board board, uint8_t baseDepth,
+  uint8_t extensionExtraDepth, SCL_StaticEvaluationFunction evalFunction);
+
+#define SCL_EVALUATION_MAX_SCORE 32600 // don't increase this, we need a margin
+
+/**
+  Checks if the board position is dead, i.e. mate is impossible (e.g. due to
+  insufficient material), which by the rules results in a draw. WARNING: This
+  function may fail to detect some dead positions as this is a non-trivial task.
+*/
+uint8_t SCL_boardDead(SCL_Board board);
+
+/**
+  Tests whether given player is in check.
+*/
+uint8_t SCL_boardCheck(SCL_Board board, uint8_t white);
+
+/**
+  Checks whether given move resets the move counter (used in the 50 move rule).
+*/
+uint8_t SCL_boardMoveResetsCount(SCL_Board board,
+  uint8_t squareFrom, uint8_t squareTo);
+
+uint8_t SCL_boardMate(SCL_Board board);
+
+/**
+  Performs a move on a board WITHOUT checking if the move is legal. Returns an
+  info with which the move can be undone.
+*/
+SCL_MoveUndo SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo,
+  char promotePiece);
+
+void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo);
+
+/**
+  Checks if the game is over, i.e. the current player to move has no legal
+  moves, the game is in dead position etc.
+*/
+uint8_t SCL_boardGameOver(SCL_Board board);
+
+/**
+  Checks if given move is legal.
+*/
+uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom,
+  uint8_t squareTo);
+
+/**
+  Checks if the player to move has at least one legal move.
+*/
+uint8_t SCL_boardMovePossible(SCL_Board board);
+
+#define SCL_POSITION_NORMAL    0x00
+#define SCL_POSITION_CHECK     0x01
+#define SCL_POSITION_MATE      0x02
+#define SCL_POSITION_STALEMATE 0x03
+#define SCL_POSITION_DEAD      0x04
+
+uint8_t SCL_boardGetPosition(SCL_Board board);
+
+/**
+  Returns 1 if the square is attacked by player of given color. This is used to
+  examine checks, so for performance reasons the functions only checks whether
+  or not the square is attacked (not the number of attackers).
+*/
+uint8_t SCL_boardSquareAttacked(SCL_Board board, uint8_t square,
+  uint8_t byWhite);
+
+/**
+  Gets pseudo moves of a piece: all possible moves WITHOUT eliminating moves
+  that lead to own check. To get only legal moves use SCL_boardGetMoves.
+*/
+void SCL_boardGetPseudoMoves(
+  SCL_Board board,
+  uint8_t pieceSquare,
+  uint8_t checkCastling,
+  SCL_SquareSet result);
+
+/**
+  Gets all legal moves of given piece.
+*/
+void SCL_boardGetMoves(
+  SCL_Board board,
+  uint8_t pieceSquare,
+  SCL_SquareSet result);
+
+void _SCL_board960RememberRookPositions(SCL_Board board);
+void _SCL_boardPlaceOnNthAvailable(SCL_Board board, uint8_t pos, char piece);
+void _SCL_handleRookActivity(SCL_Board board, uint8_t rookSquare);
+void SCL_printSquareSet(SCL_SquareSet set, SCL_PutCharFunction putCharFunc);
+int16_t _SCL_rateKingEndgamePosition(uint8_t position);
+int16_t _SCL_boardEvaluateDynamic(SCL_Board board, int8_t depth, int16_t alphaBeta, int8_t takenSquare);
+
+static inline uint8_t SCL_boardWhitesTurn(SCL_Board board);
+
+static inline uint8_t SCL_pieceIsWhite(char piece); 
+static inline uint8_t SCL_squareIsWhite(uint8_t square);
+char SCL_pieceToColor(uint8_t piece, uint8_t toWhite);
+
+/**
+  Converts square coordinates to square number. Each coordinate must be a number
+  <1,8>. Validity of the coordinates is NOT checked.
+*/
+static inline uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column);
+
+#ifndef SCL_VALUE_PAWN
+  #define SCL_VALUE_PAWN 256
+#endif
+
+#ifndef SCL_VALUE_KNIGHT
+  #define SCL_VALUE_KNIGHT 768
+#endif
+
+#ifndef SCL_VALUE_BISHOP
+  #define SCL_VALUE_BISHOP 800
+#endif
+
+#ifndef SCL_VALUE_ROOK
+  #define SCL_VALUE_ROOK 1280
+#endif
+
+#ifndef SCL_VALUE_QUEEN
+  #define SCL_VALUE_QUEEN 2304
+#endif
+
+#ifndef SCL_VALUE_KING
+  #define SCL_VALUE_KING 0
+#endif
+
+#define SCL_ENDGAME_MATERIAL_LIMIT \
+  (2 * (SCL_VALUE_PAWN * 4 + SCL_VALUE_QUEEN + \
+  SCL_VALUE_KING + SCL_VALUE_ROOK + SCL_VALUE_KNIGHT))
+
+#define SCL_START_MATERIAL \
+  (16 * SCL_VALUE_PAWN + 4 * SCL_VALUE_ROOK + 4 * SCL_VALUE_KNIGHT + \
+    4 * SCL_VALUE_BISHOP + 2 * SCL_VALUE_QUEEN + 2 * SCL_VALUE_KING)
+
+#ifndef SCL_RECORD_MAX_LENGTH
+  #define SCL_RECORD_MAX_LENGTH 256
+#endif
+
+#define SCL_RECORD_MAX_SIZE (SCL_RECORD_MAX_LENGTH * 2)
+
+/**
+  Records a single chess game. The format is following:
+
+  Each record item consists of 2 bytes which record a single move (ply):
+
+  abxxxxxx cdyyyyyy
+
+    xxxxxx  Start square of the move, counted as A0, A1, ...
+    yyyyyy  End square of the move in the same format as the start square.
+    ab      00 means this move isn't the last move of the game, other possible
+            values are 01: white wins, 10: black wins, 11: draw or end for
+            other reasons.
+    cd      In case of pawn promotion move this encodes the promoted piece as
+            00: queen, 01: rook, 10: bishop, 11: knight (pawn isn't allowed by
+            chess rules).
+
+  Every record should be ended by an ending move (ab != 00), empty record should
+  have one move where xxxxxx == yyyyyy == 0 and ab == 11.
+*/
+typedef uint8_t SCL_Record[SCL_RECORD_MAX_SIZE];
+
+#define SCL_RECORD_CONT 0x00
+#define SCL_RECORD_W_WIN 0x40
+#define SCL_RECORD_B_WIN 0x80
+#define SCL_RECORD_END 0xc0
+
+#define SCL_RECORD_PROM_Q 0x00
+#define SCL_RECORD_PROM_R 0x40
+#define SCL_RECORD_PROM_B 0x80
+#define SCL_RECORD_PROM_N 0xc0
+
+#define SCL_RECORD_ITEM(s0,s1,p,e) ((e) | (s0)),((p) | (s1))
+
+void SCL_recordInit(SCL_Record r);
+
+void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo);
+
+/**
+  Represents a complete game of chess (or a variant with different staring
+  position). This struct along with associated functions allows to easily
+  implement a chess game that allows undoing moves, detecting draws, recording
+  the moves etc. On platforms with extremely little RAM one can reduce
+  SCL_RECORD_MAX_LENGTH to reduce the size of this struct (which will however
+  possibly limit how many moves can be undone).
+*/
+typedef struct
+{
+  SCL_Board board;
+  SCL_Record record;          /**< Holds the game record. This record is here 
+                              firstly because games are usually recorded and
+                              secondly this allows undoing moves up to the 
+                              beginning of the game. This infinite undoing will
+                              only work as long as the record is able to hold
+                              the whole game; if the record is full, undoing is
+                              no longet possible. */
+  uint16_t state;
+  uint16_t ply;               ///< ply count (board ply counter is only 8 bit)
+
+  uint32_t prevMoves[14];     ///< stores last moves, for repetition detection
+
+  const char *startState;     /**< Optional pointer to the starting board state.
+                              If this is null, standard chess start position is
+                              assumed. This is needed for undoing moves with
+                              game record. */
+} SCL_Game;
+
+/**
+  Initializes a new chess game. The startState parameter is optional and allows
+  for setting up chess variants that differ by starting positions, setting this
+  to 0 will assume traditional starting position. WARNING: if startState is
+  provided, the pointed to board mustn't be deallocated afterwards, the string
+  is not internally copied (for memory saving reasons).
+*/
+void SCL_gameInit(SCL_Game *game, const SCL_Board startState);
+
+void SCL_gameMakeMove(SCL_Game *game, uint8_t squareFrom, uint8_t squareTo,
+  char promoteTo);
+
+uint8_t SCL_gameUndoMove(SCL_Game *game);
+
+/**
+  Gets a move which if played now would cause a draw by repetition. Returns 1
+  if such move exists, 0 otherwise. The results parameters can be set to 0 in
+  which case they will be ignored and only the existence of a draw move will be
+  tested.
+*/
+uint8_t SCL_gameGetRepetiotionMove(SCL_Game *game,
+  uint8_t *squareFrom, uint8_t *squareTo);
+
+/**
+  Leads a game record from PGN string. The function will probably not strictly
+  adhere to the PGN input format, but should accept most sanely written PGN
+  strings.
+*/
+void SCL_recordFromPGN(SCL_Record r, const char *pgn);
+
+uint16_t SCL_recordLength(const SCL_Record r);
+
+/**
+  Gets the move out of a game record, returns the end state of the move
+  (SCL_RECORD_CONT, SCL_RECORD_END etc.)
+*/
+uint8_t SCL_recordGetMove(const SCL_Record r,  uint16_t index,
+  uint8_t *squareFrom, uint8_t *squareTo, char *promotedPiece);
+
+/**
+  Adds another move to the game record. Terminating the record is handled so
+  that the last move is always marked with end flag, endState is here to only
+  indicate possible game result (otherwise pass SCL_RECORD_CONT). Returns 1 if
+  the item was added, otherwise 0 (replay was already of maximum size).
+*/
+uint8_t SCL_recordAdd(SCL_Record r, uint8_t squareFrom,
+  uint8_t squareTo, char promotePiece, uint8_t endState);
+
+/**
+  Removes the last move from the record, returns 1 if the replay is non-empty
+  after the removal, otherwise 0.
+*/
+uint8_t SCL_recordRemoveLast(SCL_Record r);
+
+/**
+  Applies given number of half-moves (ply) to a given board (the board is
+  automatically initialized at the beginning).
+*/
+void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves);
+
+int16_t SCL_pieceValue(char piece);
+int16_t SCL_pieceValuePositive(char piece);
+
+#define SCL_PRINT_FORMAT_NONE 0
+#define SCL_PRINT_FORMAT_NORMAL 1
+#define SCL_PRINT_FORMAT_COMPACT 2
+#define SCL_PRINT_FORMAT_UTF8 3
+#define SCL_PRINT_FORMAT_COMPACT_UTF8 4
+
+/**
+  Gets the best move for the currently moving player as computed by AI. The
+  return value is the value of the move (with the same semantics as the value
+  of an evaluation function). baseDepth is depth in plys to which all moves will
+  be checked. If baseDepth 0 is passed, the function makes a random move and
+  returns the evaluation of the board. extensionExtraDepth is extra depth for
+  checking specific situations like exchanges and checks. endgameExtraDepth is
+  extra depth which is added to baseDepth in the endgame. If the randomness
+  function is 0, AI will always make the first best move it finds, if it is
+  not 0 and randomness is 0, AI will randomly pick between the equally best
+  moves, if it is not 0 and randomness is positive, AI will randomly choose
+  between best moves with some bias (may not pick the best rated move).
+*/
+int16_t SCL_getAIMove(
+  SCL_Board board,
+  uint8_t baseDepth,
+  uint8_t extensionExtraDepth,
+  uint8_t endgameExtraDepth,
+  SCL_StaticEvaluationFunction evalFunc,
+  SCL_RandomFunction randFunc,
+  uint8_t randomness,
+  uint8_t repetitionMoveFrom,
+  uint8_t repetitionMoveTo,
+  uint8_t *resultFrom,
+  uint8_t *resultTo,
+  char *resultProm);
+
+/**
+  Prints given chessboard using given format and an abstract printing function.
+*/
+void SCL_printBoard(
+  SCL_Board board,
+  SCL_PutCharFunction putCharFunc,
+  SCL_SquareSet highlightSquares,
+  uint8_t selectSquare,
+  uint8_t format,
+  uint8_t offset,
+  uint8_t labels,
+  uint8_t blackDown);
+
+void SCL_printBoardSimple(
+  SCL_Board board,
+  SCL_PutCharFunction putCharFunc,
+  uint8_t selectSquare,
+  uint8_t format);
+
+void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc);
+void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc,
+  SCL_Board initialState);
+
+/**
+  Reads a move from string (the notation format is described at the top of this
+  file). The function is safe as long as the string is 0 terminated. Returns 1
+  on success or 0 on fail (invalid move string).
+*/
+uint8_t SCL_stringToMove(const char *moveString, uint8_t *resultFrom, 
+  uint8_t *resultTo, char *resultPromotion);
+
+char *SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1,
+  char promotion, char *string);
+
+/**
+  Function used in drawing, it is called to draw the next pixel. The first
+  parameter is the pixel color, the second one if the sequential number of the
+  pixel.
+*/
+typedef void (*SCL_PutPixelFunction)(uint8_t, uint16_t);
+
+#define SCL_BOARD_PICTURE_WIDTH 64
+
+/**
+  Draws a simple 1bit 64x64 pixels board using a provided abstract function for
+  drawing pixels. The function renders from top left to bottom right, i.e. no
+  frame buffer is required.
+*/
+void SCL_drawBoard(
+  SCL_Board board,
+  SCL_PutPixelFunction putPixel,
+  uint8_t selectedSquare,
+  SCL_SquareSet highlightSquares,
+  uint8_t blackDown);
+
+/**
+  Converts square number to string representation (e.g. "d2"). This function
+  will modify exactly the first two bytes of the provided string.
+*/
+static inline char *SCL_squareToString(uint8_t square, char *string);
+
+/**
+  Converts a string, such as "A1" or "b4", to square number. The string must
+  start with a letter (lower or upper case) and be followed by a number <1,8>.
+  Validity of the string is NOT checked.
+*/
+uint8_t SCL_stringToSquare(const char *square);
+
+//=============================================================================
+// privates:
+
+#define SCL_UNUSED(v) (void)(v)
+
+uint8_t SCL_currentRandom8 = 0;
+
+uint16_t SCL_currentRandom16 = 0;
+
+void SCL_randomSimpleSeed(uint8_t seed)
+{
+  SCL_currentRandom8 = seed;
+}
+
+uint8_t SCL_randomSimple(void)
+{
+  SCL_currentRandom8 *= 13;
+  SCL_currentRandom8 += 7;
+  return SCL_currentRandom8;
+}
+
+uint8_t SCL_randomBetter(void)
+{
+  SCL_currentRandom16 *= 13;
+  SCL_currentRandom16 += 7;
+  return (SCL_currentRandom16 % 256) ^ (SCL_currentRandom16 / 256);
+}
+
+void SCL_randomBetterSeed(uint16_t seed)
+{
+  SCL_currentRandom16 = seed;
+}
+
+void SCL_squareSetClear(SCL_SquareSet squareSet)
+{
+  for (uint8_t i = 0; i < 8; ++i)
+    squareSet[i] = 0;
+}
+
+uint8_t SCL_stringToSquare(const char *square)
+{
+  return (square[1] - '1') * 8 +
+    (square[0] - ((square[0] >= 'A' && square[0] <= 'Z') ? 'A' : 'a'));
+}
+
+char *SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1,
+  char promotion, char *string)
+{
+  char *result = string;
+
+  SCL_squareToString(s0,string);
+  string += 2;
+  string = SCL_squareToString(s1,string);
+  string += 2;
+
+  char c = board[s0];
+
+  if (c == 'p' || c == 'P')
+  {
+    uint8_t rank = s1 / 8;
+
+    if (rank == 0 || rank == 7)
+    {
+      *string = promotion;
+      string++;
+    }
+  }
+
+  *string = 0;
+
+  return result;
+}
+
+uint8_t SCL_boardWhitesTurn(SCL_Board board)
+{
+  return (board[SCL_BOARD_PLY_BYTE] % 2) == 0;
+}
+
+uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column)
+{
+  return row * 8 + column; 
+}
+
+uint8_t SCL_pieceIsWhite(char piece)
+{
+  return piece < 'a';
+}
+
+char *SCL_squareToString(uint8_t square, char *string)
+{
+  string[0] = 'a' + square % 8;
+  string[1] = '1' + square / 8;
+
+  return string;
+}
+  
+uint8_t SCL_squareIsWhite(uint8_t square)
+{
+  return (square % 2) != ((square / 8) % 2);
+}
+
+char SCL_pieceToColor(uint8_t piece, uint8_t toWhite)
+{
+  return (SCL_pieceIsWhite(piece) == toWhite) ?
+    piece : (piece + (toWhite ? -32 : 32));
+}
+
+/**
+  Records the rook starting positions in the board state. This is required in
+  chess 960 in order to be able to correctly perform castling (castling rights
+  knowledge isn't enough as one rook might have moved to the other side and we
+  wouldn't know which one can castle and which not).
+*/
+void _SCL_board960RememberRookPositions(SCL_Board board)
+{
+  uint8_t pos = 0;
+  uint8_t rooks = 2;
+
+  while (pos < 8 && rooks != 0)
+  {
+    if (board[pos] == 'R')
+    {
+      board[SCL_BOARD_EXTRA_BYTE] = rooks == 2 ? pos :
+        (board[SCL_BOARD_EXTRA_BYTE] | (pos << 3));
+
+      rooks--;
+    }
+
+    pos++;
+  }
+}
+
+void SCL_boardInit(SCL_Board board)
+{
+  /*
+    We might use SCL_BOARD_START_STATE and copy it to the board, but that might
+    waste RAM on Arduino, so we init the board by code.
+  */
+
+  char *b = board;
+
+  *b = 'R'; b++; *b = 'N'; b++;
+  *b = 'B'; b++; *b = 'Q'; b++;
+  *b = 'K'; b++; *b = 'B'; b++;
+  *b = 'N'; b++; *b = 'R'; b++;
+
+  char *b2 = board + 48;
+
+  for (uint8_t i = 0; i < 8; ++i, b++, b2++)
+  {
+    *b = 'P';
+    *b2 = 'p';
+  }
+
+  for (uint8_t i = 0; i < 32; ++i, b++)
+    *b = '.';
+
+  b += 8;
+
+  *b = 'r'; b++; *b = 'n'; b++;
+  *b = 'b'; b++; *b = 'q'; b++;
+  *b = 'k'; b++; *b = 'b'; b++;
+  *b = 'n'; b++; *b = 'r'; b++;
+
+  for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE - SCL_BOARD_SQUARES; ++i, ++b)
+    *b = 0;
+
+  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = (char) 0xff;
+
+#if SCL_960_CASTLING
+  _SCL_board960RememberRookPositions(board);
+#endif
+}
+
+void _SCL_boardPlaceOnNthAvailable(SCL_Board board, uint8_t pos, char piece)
+{
+  char *c = board;
+
+  while (1)
+  {
+    if (*c == '.')
+    {
+      if (pos == 0)
+        break;
+
+      pos--;
+    }
+
+    c++;
+  }
+
+  *c = piece;
+}
+
+void SCL_boardInit960(SCL_Board board, uint16_t positionNumber)
+{
+  SCL_Board b;
+
+  SCL_boardInit(b);
+
+  for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
+    board[i] = ((i >= 8 && i < 56) || i >= 64) ? b[i] : '.';
+
+  uint8_t helper = positionNumber % 16;
+
+  board[(helper / 4) * 2] = 'B';
+  board[1 + (helper % 4) * 2] = 'B';
+
+  helper = positionNumber / 16;
+
+  // maybe there's a simpler way :)
+
+  _SCL_boardPlaceOnNthAvailable(board,helper % 6,'Q');
+  _SCL_boardPlaceOnNthAvailable(board,0,helper <= 23 ? 'N' : 'R');
+
+  _SCL_boardPlaceOnNthAvailable(board,0,
+    (helper >= 7 && helper <= 23) ? 'R' :
+      (helper > 41 ? 'K' : 'N' ));
+
+  _SCL_boardPlaceOnNthAvailable(board,0,
+    (helper <= 5 || helper >= 54) ? 'R' : 
+      (((helper >= 12 && helper <= 23) ||
+      (helper >= 30 && helper <= 41)) ? 'K' : 'N'));
+
+  _SCL_boardPlaceOnNthAvailable(board,0,
+    (helper <= 11 || (helper <= 29 && helper >= 24)) ? 'K' :
+      (
+        (
+          (helper >= 18 && helper <= 23) ||
+          (helper >= 36 && helper <= 41) ||
+          (helper >= 48 && helper <= 53)
+        ) ? 'R' : 'N'
+      )
+    );
+
+  uint8_t rooks = 0;
+
+  for (uint8_t i = 0; i < 8; ++i)
+    if (board[i] == 'R')
+      rooks++;
+
+  _SCL_boardPlaceOnNthAvailable(board,0,rooks == 2 ? 'N' : 'R');
+  
+  for (uint8_t i = 0; i < 8; ++i)
+    board[56 + i] = SCL_pieceToColor(board[i],0);
+
+#if SCL_960_CASTLING
+  _SCL_board960RememberRookPositions(board);
+#else
+  SCL_boardDisableCastling(board);
+#endif
+}
+
+uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2)
+{
+  const char *p1 = b1, *p2 = b2;
+
+  while (p1 < b1 + SCL_BOARD_STATE_SIZE)
+  {
+    if (*p1 != *p2)
+      return 1;
+
+    p1++;
+    p2++;
+  }
+
+  return 0;
+}
+
+void SCL_recordInit(SCL_Record r)
+{
+  r[0] = 0 | SCL_RECORD_END;
+  r[1] = 0;
+}
+
+void SCL_recordFromPGN(SCL_Record r, const char *pgn)
+{
+  SCL_Board board;
+
+  SCL_boardInit(board);
+
+  SCL_recordInit(r);
+
+  uint8_t state = 0;
+  uint8_t evenMove = 0;
+
+  while (*pgn != 0)
+  {
+    switch (state)
+    {
+      case 0: // skipping tags and spaces, outside []
+        if (*pgn == '1')
+          state = 2;
+        else if (*pgn == '[')
+          state = 1;
+
+        break;
+
+      case 1: // skipping tags and spaces, inside []
+        if (*pgn == ']')
+          state = 0;
+
+        break;
+
+      case 2: // reading move number
+        if (*pgn == '{')
+          state = 3;
+        else if ((*pgn >= 'a' && *pgn <= 'h') || (*pgn >= 'A' && *pgn <= 'Z'))
+        {
+          state = 4;
+          pgn--;
+        }
+
+        break;
+
+      case 3: // initial comment
+        if (*pgn == '}')
+          state = 2;
+
+        break;
+
+      case 4: // reading move
+      {
+        char piece = 'p';
+        char promoteTo = 'q';
+        uint8_t castle = 0;
+        uint8_t promotion = 0;
+
+        int8_t coords[4];
+
+        uint8_t ranks = 0, files = 0;
+
+        for (uint8_t i = 0; i < 4; ++i)
+          coords[i] = -1;
+
+        while (*pgn != ' ' && *pgn != '\n' && 
+          *pgn != '\t' && *pgn != '{' && *pgn != 0)
+        {
+          if (*pgn == '=')
+            promotion = 1;
+          if (*pgn == 'O' || *pgn == '0')
+            castle++;
+          if (*pgn >= 'A' && *pgn <= 'Z')
+          {
+            if (promotion)
+              promoteTo = *pgn;
+            else
+              piece = *pgn;
+          }
+          else if (*pgn >= 'a' && *pgn <= 'h')
+          {
+            coords[files * 2] = *pgn - 'a';
+            files++;
+          }
+          else if (*pgn >= '1' && *pgn <= '8')
+          {
+            coords[1 + ranks * 2] = *pgn - '1';
+            ranks++;
+          }
+
+          pgn++;
+        }
+
+        if (castle)
+        {
+          piece = 'K';
+
+          coords[0] = 4;
+          coords[1] = 0;
+          coords[2] = castle < 3 ? 6 : 2;
+          coords[3] = 0;
+
+          if (evenMove)
+          {
+            coords[1] = 7;
+            coords[3] = 7;
+          }
+        }
+
+        piece = SCL_pieceToColor(piece,evenMove == 0);
+
+        if (coords[2] < 0)
+        {
+          coords[2] = coords[0];
+          coords[0] = -1;
+        }
+
+        if (coords[3] < 0)
+        {
+          coords[3] = coords[1];
+          coords[1] = -1;
+        }
+          
+        uint8_t squareTo = coords[3] * 8 + coords[2];
+
+        if (coords[0] < 0 || coords[1] < 0)
+        {
+          // without complete starting coords we have to find the piece
+
+          for (int i = 0; i < SCL_BOARD_SQUARES; ++i)
+            if (board[i] == piece)
+            {
+              SCL_SquareSet s;
+
+              SCL_squareSetClear(s);
+
+              SCL_boardGetMoves(board,i,s);
+
+              if (SCL_squareSetContains(s,squareTo) &&
+                (coords[0] < 0 || coords[0] == i % 8) &&
+                (coords[1] < 0 || coords[1] == i / 8))
+              {
+                coords[0] = i % 8;
+                coords[1] = i / 8;
+                break;
+              }
+            }
+        }
+
+        uint8_t squareFrom = coords[1] * 8 + coords[0];
+
+        SCL_boardMakeMove(board,squareFrom,squareTo,promoteTo);
+
+// for some reason tcc bugs here, the above line sets squareFrom to 0 lol
+// can be fixed with doing "squareFrom = coords[1] * 8 + coords[0];" again
+
+        SCL_recordAdd(r,squareFrom,squareTo,promoteTo,SCL_RECORD_CONT);
+
+        while (*pgn == ' ' || *pgn == '\n' || *pgn == '\t' || *pgn == '{')
+        {
+          if (*pgn == '{')
+            while (*pgn != '}')
+              pgn++;
+
+          pgn++;
+        }
+
+        if (*pgn == 0)
+          return;
+
+        pgn--;
+
+        if (evenMove)
+          state = 2;
+
+        evenMove = !evenMove;
+
+        break;
+      }
+
+      default: break;
+    }
+
+    pgn++;
+  }
+}
+
+uint16_t SCL_recordLength(const SCL_Record r)
+{
+  if ((r[0] & 0x3f) == (r[1] & 0x3f)) // empty record that's only terminator
+    return 0;
+
+  uint16_t result = 0;
+
+  while ((r[result] & 0xc0) == 0)
+    result += 2;
+
+  return (result / 2) + 1;
+}
+
+uint8_t SCL_recordGetMove(const SCL_Record r,  uint16_t index,
+  uint8_t *squareFrom, uint8_t *squareTo, char *promotedPiece)
+{ 
+  index *= 2;
+  uint8_t b = r[index];
+
+  *squareFrom = b & 0x3f;
+  uint8_t result = b & 0xc0;
+
+  index++;
+
+  b = r[index];
+  *squareTo = b & 0x3f;
+
+  b &= 0xc0;
+
+  switch (b)
+  {
+    case SCL_RECORD_PROM_Q: *promotedPiece = 'q'; break;
+    case SCL_RECORD_PROM_R: *promotedPiece = 'r'; break;
+    case SCL_RECORD_PROM_B: *promotedPiece = 'b'; break;
+    case SCL_RECORD_PROM_N: 
+    default:            *promotedPiece = 'n'; break;
+  }
+
+  return result;
+}
+
+uint8_t SCL_recordAdd(SCL_Record r, uint8_t squareFrom,
+  uint8_t squareTo, char promotePiece, uint8_t endState)
+{
+  uint16_t l = SCL_recordLength(r);
+
+  if (l >= SCL_RECORD_MAX_LENGTH)
+    return 0;
+
+  l *= 2;
+
+  if (l != 0)
+    r[l - 2] &= 0x3f; // remove the end flag from previous item
+
+  if (endState == SCL_RECORD_CONT)
+    endState = SCL_RECORD_END;
+
+  r[l] = squareFrom | endState;
+
+  uint8_t p;
+
+  switch (promotePiece)
+  {
+    case 'n': case 'N': p = SCL_RECORD_PROM_N; break;
+    case 'b': case 'B': p = SCL_RECORD_PROM_B; break;
+    case 'r': case 'R': p = SCL_RECORD_PROM_R; break;
+    case 'q': case 'Q': 
+    default:            p = SCL_RECORD_PROM_Q; break;
+  }
+
+  l++;
+
+  r[l] = squareTo | p;
+
+  return 1;
+}
+
+uint8_t SCL_recordRemoveLast(SCL_Record r)
+{
+  uint16_t l = SCL_recordLength(r);
+  
+  if (l == 0)
+    return 0;
+
+  if (l == 1)
+    SCL_recordInit(r);
+  else
+  {
+    l = (l - 2) * 2;
+
+    r[l] = (r[l] & 0x3f) | SCL_RECORD_END;
+  }
+  
+  return 1;
+}
+
+void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves)
+{
+  SCL_boardInit(b);
+
+  uint16_t l = SCL_recordLength(r);
+
+  if (moves > l)
+    moves = l;
+
+  for (uint16_t i = 0; i < moves; ++i)
+  {
+    uint8_t s0, s1;
+    char p;
+
+     SCL_recordGetMove(r,i,&s0,&s1,&p);
+     SCL_boardMakeMove(b,s0,s1,p);
+  }
+}
+
+void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo)
+{
+#if SCL_960_CASTLING
+  char squareToNow = board[moveUndo.squareTo];
+#endif
+
+  board[moveUndo.squareFrom] = board[moveUndo.squareTo];
+  board[moveUndo.squareTo] = moveUndo.other & 0x7f;
+  board[SCL_BOARD_PLY_BYTE]--;
+  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = moveUndo.enPassantCastle;
+  board[SCL_BOARD_MOVE_COUNT_BYTE] = moveUndo.moveCount;
+
+  if (moveUndo.other & 0x80)
+  {
+    moveUndo.squareTo /= 8;
+
+    if (moveUndo.squareTo == 0 || moveUndo.squareTo == 7)
+      board[moveUndo.squareFrom] = SCL_pieceIsWhite(board[moveUndo.squareFrom]) 
+        ? 'P' : 'p';
+      // ^ was promotion
+    else
+      board[(moveUndo.squareFrom / 8) * 8 + (moveUndo.enPassantCastle & 0x0f)] =
+        (board[moveUndo.squareFrom] == 'P') ? 'p' : 'P'; // was en passant
+  }
+#if !SCL_960_CASTLING
+  else if (board[moveUndo.squareFrom] == 'k' && // black castling
+    moveUndo.squareFrom == 60)
+  {
+    if (moveUndo.squareTo == 58)
+    {
+      board[59] = '.';
+      board[56] = 'r';
+    } 
+    else if (moveUndo.squareTo == 62)
+    {
+      board[61] = '.';
+      board[63] = 'r';
+    } 
+  }
+  else if (board[moveUndo.squareFrom] == 'K' && // white castling
+    moveUndo.squareFrom == 4)
+  {
+    if (moveUndo.squareTo == 2)
+    {
+      board[3] = '.';
+      board[0] = 'R';
+    } 
+    else if (moveUndo.squareTo == 6)
+    {
+      board[5] = '.';
+      board[7] = 'R';
+    } 
+  }
+#else // 960 castling
+  else if (((moveUndo.other & 0x7f) == 'r') && // black castling
+    (squareToNow == '.' || !SCL_pieceIsWhite(squareToNow)))
+  {
+    board[moveUndo.squareTo < moveUndo.squareFrom ? 59 : 61] = '.';
+    board[moveUndo.squareTo < moveUndo.squareFrom ? 58 : 62] = '.';
+
+    board[moveUndo.squareFrom] = 'k';
+    board[moveUndo.squareTo] = 'r';
+  }
+  else if (((moveUndo.other & 0x7f) == 'R') && // white castling
+    (squareToNow == '.' || SCL_pieceIsWhite(squareToNow)))
+  {
+    board[moveUndo.squareTo < moveUndo.squareFrom ? 3 : 5] = '.';
+    board[moveUndo.squareTo < moveUndo.squareFrom ? 2 : 6] = '.';
+
+    board[moveUndo.squareFrom] = 'K';
+    board[moveUndo.squareTo] = 'R';
+  }
+#endif
+}
+
+/**
+  Potentially disables castling rights according to whether something moved from
+  or to a square with a rook.
+*/
+void _SCL_handleRookActivity(SCL_Board board, uint8_t rookSquare)
+{
+#if !SCL_960_CASTLING
+  switch (rookSquare)
+  {
+    case 0:  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x20; break;
+    case 7:  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x10; break;
+    case 56: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x80; break;
+    case 63: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x40; break;
+    default: break;
+  }
+#else // 960 castling
+  if (rookSquare == (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x20;
+  else if (rookSquare == (board[SCL_BOARD_EXTRA_BYTE] >> 3))
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x10;
+  else if (rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x80;
+  else if (rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] >> 3))
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x40;
+#endif
+}
+
+SCL_MoveUndo SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo, 
+  char promotePiece)
+{
+  char s = board[squareFrom];
+
+  SCL_MoveUndo moveUndo;
+
+  moveUndo.squareFrom = squareFrom;
+  moveUndo.squareTo = squareTo; 
+  moveUndo.moveCount = board[SCL_BOARD_MOVE_COUNT_BYTE];
+  moveUndo.enPassantCastle = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE];
+  moveUndo.other = board[squareTo];
+
+  // reset the en-passant state
+  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] |= 0x0f;
+
+  if (SCL_boardMoveResetsCount(board,squareFrom,squareTo))
+    board[SCL_BOARD_MOVE_COUNT_BYTE] = 0;
+  else
+    board[SCL_BOARD_MOVE_COUNT_BYTE]++;
+
+#if SCL_960_CASTLING
+  uint8_t castled = 0;
+#endif
+
+  if ((s == 'k') || (s == 'K'))
+  {
+#if !SCL_960_CASTLING
+    if ((squareFrom == 4) || (squareFrom == 60)) // check castling
+    {
+      int8_t difference = squareTo - squareFrom;
+
+      char rook = SCL_pieceToColor('r',SCL_pieceIsWhite(s));
+
+      if (difference == 2) // short
+      {
+        board[squareTo - 1] = rook;
+        board[squareTo + 1] = '.';
+      }
+      else if (difference == -2) // long
+      {
+        board[squareTo - 2] = '.';
+        board[squareTo + 1] = rook;
+      }
+    }
+#else // 960 castling
+    uint8_t isWhite = SCL_pieceIsWhite(s);
+    char rook = SCL_pieceToColor('r',isWhite);
+
+    if (board[squareTo] == rook)
+    {
+      castled = 1;
+        
+      board[squareFrom] = '.';
+      board[squareTo] = '.';
+
+      if (squareTo > squareFrom) // short
+      {
+        board[isWhite ? 6 : (56 + 6)] = s;
+        board[isWhite ? 5 : (56 + 5)] = rook;
+      }
+      else // long
+      {
+        board[isWhite ? 2 : (56 + 2)] = s;
+        board[isWhite ? 3 : (56 + 3)] = rook;
+      }
+    }
+#endif
+
+    // after king move disable castling
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= ~(0x03 << ((s == 'K') ? 4 : 6));
+  }
+  else if ((s == 'p') || (s == 'P'))
+  {
+    uint8_t row = squareTo / 8;
+
+    int8_t rowDiff = squareFrom / 8 - row;
+
+    if (rowDiff == 2 || rowDiff == -2) // record en passant column
+    {
+      board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = 
+        (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0) | (squareFrom % 8);
+    }
+
+    if (row == 0 || row == 7)
+    {
+      // promotion
+      s = SCL_pieceToColor(promotePiece,SCL_pieceIsWhite(s));
+
+      moveUndo.other |= 0x80;
+    }
+    else
+    { 
+      // check en passant move
+
+      int8_t columnDiff = (squareTo % 8) - (squareFrom % 8);
+
+      if ((columnDiff != 0) && (board[squareTo] == '.'))
+      {
+        board[squareFrom + columnDiff] = '.'; 
+        moveUndo.other |= 0x80;
+      }
+    }
+  }
+  else if ((s == 'r') || (s == 'R'))
+    _SCL_handleRookActivity(board,squareFrom);
+
+  char taken = board[squareTo];
+
+  // taking a rook may also disable castling:
+
+  if (taken == 'R' || taken == 'r')
+    _SCL_handleRookActivity(board,squareTo);
+
+#if SCL_960_CASTLING
+  if (!castled)
+#endif
+  {
+    board[squareTo] = s;
+    board[squareFrom] = '.';
+  }
+
+  board[SCL_BOARD_PLY_BYTE]++; // increase ply count
+
+  return moveUndo;
+}
+
+void SCL_boardSetPosition(SCL_Board board, const char *pieces,
+  uint8_t castlingEnPassant, uint8_t moveCount, uint8_t ply)
+{
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, pieces++)
+    if (*pieces != 0)
+      board[i] = *pieces;
+    else
+      break;
+
+  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castlingEnPassant;
+  board[SCL_BOARD_PLY_BYTE] = ply;
+  board[SCL_BOARD_MOVE_COUNT_BYTE] = moveCount;
+  board[SCL_BOARD_STATE_SIZE - 1] = 0;
+}
+
+void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square)
+{
+  squareSet[square / 8] |= 0x01 << (square % 8);
+}
+
+uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square)
+{
+  return squareSet[square / 8] & (0x01 << (square % 8));
+}
+
+uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet)
+{
+  uint8_t result = 0;
+
+  for (uint8_t i = 0; i < 8; ++i)
+  {
+    uint8_t byte = squareSet[i];
+
+    for (uint8_t j = 0; j < 8; ++j)
+    {
+      result += byte & 0x01;
+      byte >>= 1;
+    }
+  }
+
+  return result;
+}
+
+uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet)
+{
+  for (uint8_t i = 0; i < 8; ++i)
+    if (squareSet[i] != 0)
+      return 0;  
+
+  return 1;
+}
+
+uint8_t SCL_squareSetGetRandom(
+  const SCL_SquareSet squareSet, SCL_RandomFunction randFunc)
+{
+  uint8_t size = SCL_squareSetSize(squareSet);
+
+  if (size == 0)
+    return 0;
+
+  uint8_t n = (randFunc() % size) + 1;
+  uint8_t i = 0;
+
+  while (i < SCL_BOARD_SQUARES)
+  {
+    if (SCL_squareSetContains(squareSet,i))
+    {
+      n--;
+
+      if (n == 0)
+        break;
+    }
+
+    ++i;
+  }
+
+  return i;
+}
+
+void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo)
+{
+  for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
+    boardTo[i] = boardFrom[i]; 
+}
+
+uint8_t SCL_boardSquareAttacked(
+  SCL_Board board,
+  uint8_t square,
+  uint8_t byWhite)
+{
+  const char *currentSquare = board;
+
+  /* We need to place a temporary piece on the tested square in order to test if
+     the square is attacked (consider testing if attacked by a pawn). */
+
+  char previous = board[square];
+
+  board[square] = SCL_pieceToColor('r',!byWhite);
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++currentSquare)
+  {
+    char s = *currentSquare;
+    if ((s == '.') || (SCL_pieceIsWhite(s) != byWhite))
+      continue;
+
+    SCL_SquareSet moves;
+    SCL_boardGetPseudoMoves(board,i,0,moves);
+
+    if (SCL_squareSetContains(moves,square))
+    {
+      board[square] = previous;
+      return 1;
+    }
+  }
+      
+  board[square] = previous;    
+  return 0;
+}
+
+uint8_t SCL_boardCheck(SCL_Board board,uint8_t white)
+{
+  const char *square = board;
+  char kingChar = white ? 'K' : 'k';
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++square)
+    if ((*square == kingChar && 
+      SCL_boardSquareAttacked(board,i,!white)))
+        return 1;
+
+  return 0;
+}
+
+uint8_t SCL_boardGameOver(SCL_Board board)
+{
+  uint8_t position = SCL_boardGetPosition(board);
+
+  return (position == SCL_POSITION_MATE) ||
+         (position == SCL_POSITION_STALEMATE) ||
+         (position == SCL_POSITION_DEAD);
+}
+
+uint8_t SCL_boardMovePossible(SCL_Board board)
+{
+  uint8_t white = SCL_boardWhitesTurn(board);
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
+  {
+    char s = board[i];
+
+    if ((s != '.') && (SCL_pieceIsWhite(s) == white))
+    {
+      SCL_SquareSet moves;
+
+      SCL_boardGetMoves(board,i,moves);
+
+      if (SCL_squareSetSize(moves) != 0)
+        return 1;
+    }
+  }
+
+  return 0;
+}
+
+uint8_t SCL_boardMate(SCL_Board board)
+{
+  return SCL_boardGetPosition(board) == SCL_POSITION_MATE;
+}
+
+void SCL_boardGetPseudoMoves(
+  SCL_Board board,
+  uint8_t pieceSquare,
+  uint8_t checkCastling,
+  SCL_SquareSet result)
+{
+  char piece = board[pieceSquare];
+
+  SCL_squareSetClear(result);
+
+  uint8_t isWhite = SCL_pieceIsWhite(piece);
+  int8_t horizontalPosition = pieceSquare % 8;
+  int8_t pawnOffset = -8;
+
+  switch (piece)
+  {
+    case 'P':
+      pawnOffset = 8;
+      // intentional fallthrough
+    case 'p':
+    {
+      uint8_t square = pieceSquare + pawnOffset;
+      uint8_t verticalPosition = pieceSquare / 8;
+
+      if (board[square] == '.') // forward move
+      {
+        SCL_squareSetAdd(result,square);
+
+        if (verticalPosition == (1 + (piece == 'p') * 5)) // start position?
+        {
+          uint8_t square2 = square + pawnOffset;
+
+          if (board[square2] == '.')
+            SCL_squareSetAdd(result,square2);
+        }
+      }
+
+      #define checkDiagonal(hor,add) \
+        if (horizontalPosition != hor) \
+        { \
+          uint8_t square2 = square + add; \
+          char c = board[square2]; \
+          if (c != '.' && SCL_pieceIsWhite(c) != isWhite) \
+            SCL_squareSetAdd(result,square2); \
+        }
+
+      // diagonal moves
+      checkDiagonal(0,-1)
+      checkDiagonal(7,1)
+
+      uint8_t enPassantRow = 4;
+      uint8_t enemyPawn = 'p';
+
+      if (piece == 'p')
+      {
+        enPassantRow = 3;
+        enemyPawn = 'P';
+      }
+
+      // en-passant moves
+      if (verticalPosition == enPassantRow)
+      {
+        uint8_t enPassantColumn = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
+        uint8_t column = pieceSquare % 8;
+
+        for (int8_t offset = -1; offset < 2; offset += 2)
+          if ((enPassantColumn == column + offset) &&
+              (board[pieceSquare + offset] == enemyPawn))
+          {
+            SCL_squareSetAdd(result,pieceSquare + pawnOffset + offset);
+            break;
+          }
+      } 
+
+      #undef checkDiagonal
+    }
+      break;
+
+    case 'r': // rook
+    case 'R':
+    case 'b': // bishop
+    case 'B':
+    case 'q': // queen
+    case 'Q':
+    {
+      const int8_t offsets[8] =    {-8, 1, 8, -1, -7, 9, -9, 7};
+      const int8_t columnDirs[8] = { 0, 1, 0, -1,  1, 1, -1,-1};
+
+      uint8_t from = (piece == 'b' || piece == 'B') * 4;
+      uint8_t to = 4 + (piece != 'r' && piece != 'R') * 4;
+
+      for (uint8_t i = from; i < to; ++i)
+      {
+        int8_t offset = offsets[i];
+        int8_t columnDir = columnDirs[i];
+        int8_t square = pieceSquare;
+        int8_t col = horizontalPosition;
+
+        while (1)
+        {
+          square += offset;
+          col += columnDir;
+
+          if (square < 0 || square > 63 || col < 0 || col > 7)
+            break;
+
+          char squareC = board[square];
+
+          if (squareC == '.')
+            SCL_squareSetAdd(result,square);
+          else
+          {
+            if (SCL_pieceIsWhite(squareC) != isWhite)
+              SCL_squareSetAdd(result,square);
+
+            break;
+          }
+        }
+      }
+    }
+      break;
+
+    case 'n': // knight
+    case 'N':
+    {
+      const int8_t offsets[4] = {6, 10, 15, 17};
+      const int8_t columnsMinus[4] = {2,-2,1,-1};
+      const int8_t columnsPlus[4] = {-2,2,-1,1};
+      const int8_t *off, *col;
+
+      #define checkOffsets(op,comp,limit,dir)\
+        off = offsets;\
+        col = columns ## dir;\
+        for (uint8_t i = 0; i < 4; ++i, ++off, ++col)\
+        {\
+          int8_t square = pieceSquare op (*off);\
+          if (square comp limit) /* out of board? */\
+            break;\
+          int8_t horizontalCheck = horizontalPosition + (*col);\
+          if (horizontalCheck < 0 || horizontalCheck >= 8)\
+            continue;\
+          char squareC = board[square];\
+          if ((squareC == '.') || (SCL_pieceIsWhite(squareC) != isWhite))\
+            SCL_squareSetAdd(result,square);\
+        }
+
+      checkOffsets(-,<,0,Minus)
+      checkOffsets(+,>=,SCL_BOARD_SQUARES,Plus)
+
+      #undef checkOffsets
+    } 
+      break; 
+
+    case 'k': // king
+    case 'K':
+    {
+      uint8_t verticalPosition = pieceSquare / 8;
+
+      uint8_t
+        u = verticalPosition != 0,
+        d = verticalPosition != 7,
+        l = horizontalPosition != 0,
+        r = horizontalPosition != 7;
+
+      uint8_t square2 = pieceSquare - 9;
+
+      #define checkSquare(cond,add) \
+        if (cond && ((board[square2] == '.') || \
+            (SCL_pieceIsWhite(board[square2])) != isWhite))\
+          SCL_squareSetAdd(result,square2);\
+        square2 += add;
+
+      checkSquare(l && u,1)
+      checkSquare(u,1)
+      checkSquare(r && u,6)
+      checkSquare(l,2)
+      checkSquare(r,6)
+      checkSquare(l && d,1)
+      checkSquare(d,1)
+      checkSquare(r && d,0)
+
+      #undef checkSquare
+
+      // castling:
+
+      if (checkCastling)
+      {
+        uint8_t bitShift = 4 + 2 * (!isWhite);
+
+        if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x03 << bitShift)) &&
+          !SCL_boardSquareAttacked(board,pieceSquare,!isWhite)) // no check?
+        { 
+#if !SCL_960_CASTLING
+          // short castle:
+          pieceSquare++;
+            
+          if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x01 << bitShift)) &&
+              (board[pieceSquare] == '.') &&
+              (board[pieceSquare + 1] == '.') &&
+              (board[pieceSquare + 2] == SCL_pieceToColor('r',isWhite)) &&
+              !SCL_boardSquareAttacked(board,pieceSquare,!isWhite))
+            SCL_squareSetAdd(result,pieceSquare + 1);
+
+          /* note: don't check the final square for check, it will potentially
+             be removed later (can't end up in check) */
+
+          // long castle:
+          pieceSquare -= 2;
+
+          if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x02 << bitShift)) &&
+              (board[pieceSquare] == '.') &&
+              (board[pieceSquare - 1] == '.') &&
+              (board[pieceSquare - 2] == '.') &&
+              (board[pieceSquare - 3] == SCL_pieceToColor('r',isWhite)) &&
+              !SCL_boardSquareAttacked(board,pieceSquare,!isWhite))
+            SCL_squareSetAdd(result,pieceSquare - 1);
+#else // 960 castling
+          for (int i = 0; i < 2; ++i) // short and long
+            if (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & ((i + 1) << bitShift))
+            {
+              uint8_t 
+                rookPos = board[SCL_BOARD_EXTRA_BYTE] >> 3,
+                targetPos = 5;
+
+              if (i == 1)
+              {
+                rookPos = board[SCL_BOARD_EXTRA_BYTE] & 0x07,
+                targetPos = 3;
+              }
+
+              if (!isWhite)
+              {
+                rookPos += 56;
+                targetPos += 56;
+              }
+
+              uint8_t ok = board[rookPos] == SCL_pieceToColor('r',isWhite);
+
+              if (!ok)
+                continue;
+
+              int8_t inc = 1 - 2 * (targetPos > rookPos);
+
+              while (targetPos != rookPos) // check vacant squares for the rook
+              {
+                if (board[targetPos] != '.' && 
+                    targetPos != pieceSquare)
+                {
+                  ok = 0;
+                  break;
+                }
+
+                targetPos += inc;
+              }
+
+              if (!ok) 
+                continue;
+
+              targetPos = i == 0 ? 6 : 2;
+
+              if (!isWhite)
+                targetPos += 56;
+
+              inc = 1 - 2 * (targetPos > pieceSquare);
+
+              while (targetPos != pieceSquare) // check squares for the king
+              {
+                if ((board[targetPos] != '.' && 
+                     targetPos != rookPos) ||
+                     SCL_boardSquareAttacked(board,targetPos,!isWhite))
+                {
+                  ok = 0;
+                  break;
+                }
+
+                targetPos += inc;
+              }
+
+              if (ok)
+                SCL_squareSetAdd(result,rookPos);
+            }
+#endif
+        }
+      }
+    }
+      break;
+
+    default:
+      break;
+  }
+}
+
+void SCL_printSquareSet(SCL_SquareSet set, SCL_PutCharFunction putCharFunc)
+{
+  uint8_t first = 1;
+
+  putCharFunc('(');
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
+  {
+    if (!SCL_squareSetContains(set, i))
+      continue;
+
+    if (!first)
+      putCharFunc(',');
+    else
+      first = 0;
+
+    putCharFunc('A' + i % 8);
+    putCharFunc('1' + i / 8);
+  }
+
+  putCharFunc(')');
+}
+
+void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc)
+{
+  uint32_t val = 0;
+
+  switch (square)
+  {
+    case 'r': val = 0x9c99e200; break;
+    case 'n': val = 0x9e99e200; break;
+    case 'b': val = 0x9d99e200; break;
+    case 'q': val = 0x9b99e200; break;
+    case 'k': val = 0x9a99e200; break;
+    case 'p': val = 0x9f99e200; break;
+    case 'R': val = 0x9699e200; break;
+    case 'N': val = 0x9899e200; break;
+    case 'B': val = 0x9799e200; break;
+    case 'Q': val = 0x9599e200; break;
+    case 'K': val = 0x9499e200; break;
+    case 'P': val = 0x9999e200; break;
+    case '.': val = 0x9296e200; break;
+    case ',': val = 0x9196e200; break;
+    default:  putCharFunc(square); return; break;
+  }
+
+  uint8_t count = 4;
+
+  while ((val % 256 == 0) && (count > 0))
+  {
+    val /= 256;
+    count--;
+  }
+
+  while (count > 0)
+  {
+    putCharFunc(val % 256);
+    val /= 256;
+    count--;
+  }
+}
+
+void SCL_boardGetMoves(
+  SCL_Board board,
+  uint8_t pieceSquare,
+  SCL_SquareSet result)
+{
+  SCL_SquareSet allMoves;
+
+  SCL_squareSetClear(allMoves);
+
+  for (uint8_t i = 0; i < 8; ++i)
+    result[i] = 0;
+
+  SCL_boardGetPseudoMoves(board,pieceSquare,1,allMoves);
+
+  // Now only keep moves that don't lead to one's check:
+
+  SCL_SQUARE_SET_ITERATE_BEGIN(allMoves)
+
+    SCL_MoveUndo undo = SCL_boardMakeMove(board,pieceSquare,iteratedSquare,'q');
+
+    if (!SCL_boardCheck(board,!SCL_boardWhitesTurn(board)))
+      SCL_squareSetAdd(result,iteratedSquare);
+
+    SCL_boardUndoMove(board,undo);
+
+  SCL_SQUARE_SET_ITERATE_END
+}
+
+uint8_t SCL_boardDead(SCL_Board board)
+{
+  /*
+    This byte represents material by bits:
+
+    MSB _ _ _ _ _ _ _ _ LSB
+          | | |   | | \_ white knight
+          | | |   |  \__ white bishop on white
+          | | |    \____ white bishop on black
+          | |  \________ black knight
+          |  \__________ black bishop on white
+           \____________ black bishop on black
+  */
+  uint8_t material = 0;
+
+  const char *p = board;
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
+  {
+    char c = *p;
+
+    switch (c)
+    {
+      case 'n': material |= 0x01; break;
+      case 'N': material |= 0x10; break;
+      case 'b': material |= (0x02 << (!SCL_squareIsWhite(i))); break;
+      case 'B': material |= (0x20 << (!SCL_squareIsWhite(i))); break;
+      case 'p':
+      case 'P': 
+      case 'r':
+      case 'R': 
+      case 'q':
+      case 'Q': 
+        return 0; // REMOVE later if more complex check are performed
+        break;
+
+      default: break;
+    }
+
+    p++;
+  }
+
+  // TODO: add other checks than only insufficient material
+
+  // possible combinations of insufficient material:
+
+  return
+    (material == 0x00) || // king vs king
+    (material == 0x01) || // king and knight vs king 
+    (material == 0x10) || // king and knight vs king
+    (material == 0x02) || // king and bishop vs king
+    (material == 0x20) || // king and bishop vs king
+    (material == 0x04) || // king and bishop vs king
+    (material == 0x40) || // king and bishop vs king
+    (material == 0x22) || // king and bishop vs king and bishop (same color)
+    (material == 0x44);   // king and bishop vs king and bishop (same color)
+}
+
+uint8_t SCL_boardGetPosition(SCL_Board board)
+{
+  uint8_t check = SCL_boardCheck(board,SCL_boardWhitesTurn(board)); 
+  uint8_t moves = SCL_boardMovePossible(board);
+
+  if (check)
+    return moves ? SCL_POSITION_CHECK : SCL_POSITION_MATE;
+  else if (!moves)
+    return SCL_POSITION_STALEMATE;
+
+  if (SCL_boardDead(board))
+    return SCL_POSITION_DEAD;
+
+  return SCL_POSITION_NORMAL;
+}
+
+uint8_t SCL_stringToMove(const char *moveString, uint8_t *resultFrom, 
+  uint8_t *resultTo, char *resultPromotion)
+{
+  char c;
+
+  uint8_t *dst = resultFrom;
+
+  for (uint8_t i = 0; i < 2; ++i)
+  {
+    c = *moveString;
+  
+    *dst = (c >= 'a') ? (c - 'a') : (c - 'A');
+
+    if (*dst > 7)
+      return 0;
+
+    moveString++;    
+    c = *moveString;
+  
+    *dst += 8 * (c - '1');
+  
+    if (*dst > 63)
+      return 0;
+  
+    moveString++;    
+
+    dst = resultTo;
+  }
+
+  c = *moveString;
+
+  if (c < 'A')
+    c = c - 'A' + 'a';
+
+  switch (c)
+  {
+    case 'N': case 'n': *resultPromotion = 'n'; break;
+    case 'B': case 'b': *resultPromotion = 'b'; break;
+    case 'R': case 'r': *resultPromotion = 'r'; break;
+    case 'Q': case 'q':
+    default: *resultPromotion = 'q'; break;
+  }
+  
+  return 1;
+}
+
+void SCL_printBoard(
+  SCL_Board board,
+  SCL_PutCharFunction putCharFunc,
+  SCL_SquareSet highlightSquares,
+  uint8_t selectSquare,
+  uint8_t format,
+  uint8_t offset,
+  uint8_t labels,
+  uint8_t blackDown)
+{
+  if (labels)
+  {
+    for (uint8_t i = 0; i < offset + 2; ++i)
+      putCharFunc(' ');
+
+    for (uint8_t i = 0; i < 8; ++i)
+    {
+      if ((format != SCL_PRINT_FORMAT_COMPACT) &&
+          (format != SCL_PRINT_FORMAT_COMPACT_UTF8))
+        putCharFunc(' ');
+
+      putCharFunc(blackDown ? ('H' - i) : ('A' + i));
+    }     
+
+    putCharFunc('\n');
+  }   
+  int8_t i = 7;
+  int8_t add = 1;
+
+  if (!blackDown)
+  {
+    i = 56;
+    add = -1;
+  }
+
+  for (int8_t row = 0; row < 8; ++row)
+  {
+    for (uint8_t j = 0; j < offset; ++j)
+      putCharFunc(' ');
+
+    if (labels)
+    {
+      putCharFunc(!blackDown ? ('8' - row) : ('1' + row));
+      putCharFunc(' ');
+    }
+
+    const char *square = board + i;
+
+    for (int8_t col = 0; col < 8; ++col)
+    {
+      switch (format)
+      {
+        case SCL_PRINT_FORMAT_COMPACT:
+          putCharFunc(
+            (*square == '.') ? (
+            ((i != selectSquare) ?
+              (!SCL_squareSetContains(highlightSquares,i) ? *square : '*')
+              : '#')) : *square);
+          break;
+
+        case SCL_PRINT_FORMAT_UTF8:
+        {
+          char squareChar = SCL_squareIsWhite(i) ? '.' : ',';
+          char pieceChar = (*square == '.') ? squareChar : *square;
+
+          if (i == selectSquare)
+          {
+            putCharFunc('(');
+
+            if (*square == '.')
+              putCharFunc(')');
+            else
+              SCL_printSquareUTF8(pieceChar,putCharFunc);
+          }
+          else if (!SCL_squareSetContains(highlightSquares,i))
+          {
+            SCL_printSquareUTF8(squareChar,putCharFunc);
+            SCL_printSquareUTF8(pieceChar,putCharFunc);
+          }
+          else
+          {
+            putCharFunc('[');
+
+            if (*square == '.')
+              putCharFunc(']');
+            else
+              SCL_printSquareUTF8(*square,putCharFunc);
+          }
+
+          break;
+        }
+
+        case SCL_PRINT_FORMAT_COMPACT_UTF8:
+          SCL_printSquareUTF8(
+            (*square == '.') ? (
+              SCL_squareSetContains(highlightSquares,i) ? '*' :
+              (i == selectSquare ? '#' : ((SCL_squareIsWhite(i) ? '.' : ',')))
+            ) : *square,putCharFunc);
+          break;
+
+        case SCL_PRINT_FORMAT_NORMAL:
+        default:
+        {
+          uint8_t c = *square;
+
+          char squareColor = SCL_squareIsWhite(i) ? ' ' : ':';
+
+          putCharFunc((i != selectSquare) ?
+            (!SCL_squareSetContains(highlightSquares,i) ?
+            squareColor : '#') : '@');
+
+          putCharFunc(c == '.' ? squareColor : *square);
+          break;
+        }
+      }
+
+      i -= add;
+      square -= add;
+    }
+        
+    putCharFunc('\n');
+
+    i += add * 16;  
+  } // for rows
+}
+
+int16_t SCL_pieceValuePositive(char piece)
+{
+  switch (piece)
+  {
+    case 'p':
+    case 'P': return SCL_VALUE_PAWN; break;
+    case 'n':
+    case 'N': return SCL_VALUE_KNIGHT; break;
+    case 'b':
+    case 'B': return SCL_VALUE_BISHOP; break;
+    case 'r':
+    case 'R': return SCL_VALUE_ROOK; break;
+    case 'q':
+    case 'Q': return SCL_VALUE_QUEEN; break;
+    case 'k':
+    case 'K': return SCL_VALUE_KING; break;
+    default: break;
+  }
+
+  return 0;
+}
+
+int16_t SCL_pieceValue(char piece)
+{
+  switch (piece)
+  {
+    case 'P': return SCL_VALUE_PAWN; break;
+    case 'N': return SCL_VALUE_KNIGHT; break;
+    case 'B': return SCL_VALUE_BISHOP; break;
+    case 'R': return SCL_VALUE_ROOK; break;
+    case 'Q': return SCL_VALUE_QUEEN; break;
+    case 'K': return SCL_VALUE_KING; break;
+    case 'p': return -1 * SCL_VALUE_PAWN; break;
+    case 'n': return -1 * SCL_VALUE_KNIGHT; break;
+    case 'b': return -1 * SCL_VALUE_BISHOP; break;
+    case 'r': return -1 * SCL_VALUE_ROOK; break;
+    case 'q': return -1 * SCL_VALUE_QUEEN; break;
+    case 'k': return -1 * SCL_VALUE_KING; break;
+    default: break;
+  }
+
+  return 0;
+}
+
+#define ATTACK_BONUS 3
+#define MOBILITY_BONUS 10
+#define CENTER_BONUS 7
+#define CHECK_BONUS 5
+#define KING_CASTLED_BONUS 30
+#define KING_BACK_BONUS 15
+#define KING_NOT_CENTER_BONUS 15
+#define PAWN_NON_DOUBLE_BONUS 3
+#define PAWN_PAIR_BONUS 3
+#define KING_CENTERNESS 10
+
+int16_t _SCL_rateKingEndgamePosition(uint8_t position)
+{
+  int16_t result = 0;
+  uint8_t rank = position / 8;
+  position %= 8;
+
+  if (position > 1 && position < 6)
+    result += KING_CENTERNESS;
+
+  if (rank > 1 && rank < 6)
+    result += KING_CENTERNESS;
+
+  return result;
+}
+
+int16_t SCL_boardEvaluateStatic(SCL_Board board)
+{
+  uint8_t position = SCL_boardGetPosition(board);
+  
+  int16_t total = 0;                        
+
+  switch (position)
+  {
+    case SCL_POSITION_MATE:
+      return SCL_boardWhitesTurn(board) ? 
+        -1 * SCL_EVALUATION_MAX_SCORE : SCL_EVALUATION_MAX_SCORE;
+      break;
+
+    case SCL_POSITION_STALEMATE:
+    case SCL_POSITION_DEAD:
+      return 0;
+      break;
+
+    /*
+      main points are assigned as follows:
+      - points for material as a sum of all material on board
+      - for playing side: if a piece attacks piece of greater value, a fraction
+        of the value difference is gained (we suppose exchange), this is only
+        gained once per every attacking piece (maximum gain is taken), we only
+        take fraction so that actually taking the piece is favored
+      - ATTACK_BONUS points for any attacked piece
+
+      other points are assigned as follows (in total these shouldn't be more
+      than the value of one pawn)
+      - mobility: MOBILITY_BONUS points for each piece with at least 4 possible
+        moves
+      - center control: CENTER_BONUS points for a piece on a center square
+      - CHECK_BONUS points for check
+      - king:
+        - safety (non endgame): KING_BACK_BONUS points for king on staring rank,
+          additional KING_CASTLED_BONUS if the kind if on castled square or
+          closer to the edge, additional KING_NOT_CENTER_BONUS for king not on
+          its start neighbouring center square
+        - center closeness (endgame): up to 2 * KING_CENTERNESS points for
+          being closer to center
+      - non-doubled pawns: PAWN_NON_DOUBLE_BONUS points for each pawn without
+        same color pawn directly in front of it 
+      - pawn structure: PAWN_PAIR_BONUS points for each pawn guarding own pawn
+      - advancing pawns: 1 point for each pawn's rank in its move
+        direction
+    */
+
+    case SCL_POSITION_CHECK:
+      total += SCL_boardWhitesTurn(board) ? -1 * CHECK_BONUS : CHECK_BONUS;
+      // intentional fallthrough
+    case SCL_POSITION_NORMAL:
+    default:
+    {
+      SCL_SquareSet moves;
+
+      const char *p = board;
+
+      int16_t positiveMaterial = 0;
+      uint8_t endgame = 0;
+
+      // first count material to see if this is endgame or not
+      for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p)
+      {
+        char s = *p;
+
+        if (s != '.')
+        {
+          positiveMaterial += SCL_pieceValuePositive(s);
+          total += SCL_pieceValue(s);
+        }
+      }
+
+      endgame = positiveMaterial <= SCL_ENDGAME_MATERIAL_LIMIT;
+
+      p = board;
+
+      for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p)
+      {
+        char s = *p;
+
+        if (s != '.')
+        {
+          uint8_t white = SCL_pieceIsWhite(s);
+
+          switch (s)
+          {
+            case 'k': // king safety
+              if (endgame)
+                total -= _SCL_rateKingEndgamePosition(i);
+              else if (i >= 56)
+              {
+                total -= KING_BACK_BONUS;
+
+                if (i != 59)
+                {
+                  total -= KING_NOT_CENTER_BONUS;
+
+                  if (i >= 62 || i <= 58)
+                    total -= KING_CASTLED_BONUS;
+                }
+              }
+            break;
+
+            case 'K':
+              if (endgame)
+                total += _SCL_rateKingEndgamePosition(i);
+              else if (i <= 7)
+              {
+                total += KING_BACK_BONUS;
+
+                if (i != 3)
+                {
+                  total += KING_NOT_CENTER_BONUS;
+
+                  if (i <= 2 || i >= 6)
+                    total += KING_CASTLED_BONUS;
+                }
+              }
+            break;
+
+            case 'P': // pawns
+            case 'p':
+            {
+              int8_t rank = i / 8;
+
+              if (rank != 0 && rank != 7)
+              {
+                if (s == 'P')
+                {
+                  total += rank;
+
+                  char *tmp = board + i + 8;
+
+                  if (*tmp != 'P')
+                    total += PAWN_NON_DOUBLE_BONUS;
+
+                  if (i % 8 != 7)
+                  {
+                    tmp++;
+
+                    if (*tmp == 'P')
+                      total += PAWN_PAIR_BONUS;
+
+                    if (*(tmp - 16) == 'P')
+                      total += PAWN_PAIR_BONUS;
+                  }
+                }
+                else
+                {
+                  total -= 7 - rank;
+
+                  char *tmp = board + i - 8;
+
+                  if (*tmp != 'p')
+                    total -= PAWN_NON_DOUBLE_BONUS;
+
+                  if (i % 8 != 7)
+                  {
+                    tmp += 17;
+
+                    if (*tmp == 'p')
+                      total -= PAWN_PAIR_BONUS;
+
+                    if (*(tmp - 16) == 'p')
+                      total -= PAWN_PAIR_BONUS;
+                  }
+                }
+              }
+           
+              break;
+            }
+
+            default: break;
+          }
+
+          if (i >= 27 && i <= 36 && (i >= 35 || i <= 28)) // center control
+            total += white ? CENTER_BONUS : (-1 * CENTER_BONUS);
+
+          // for performance we only take pseudo moves
+          SCL_boardGetPseudoMoves(board,i,0,moves);
+
+          if (SCL_squareSetSize(moves) >= 4) // mobility
+            total += white ? 
+              MOBILITY_BONUS : (-1 * MOBILITY_BONUS);
+
+          int16_t exchangeBonus = 0;
+
+          SCL_SQUARE_SET_ITERATE_BEGIN(moves)
+
+            if (board[iteratedSquare] != '.')
+            {
+              total += white ? 
+                ATTACK_BONUS : (- 1 * ATTACK_BONUS);
+
+              if (SCL_boardWhitesTurn(board) == white)
+              {
+                int16_t valueDiff = 
+                  SCL_pieceValuePositive(board[iteratedSquare]) - 
+                  SCL_pieceValuePositive(s);
+
+                valueDiff /= 4; // only take a fraction to favor taking
+
+                if (valueDiff > exchangeBonus)
+                  exchangeBonus = valueDiff;
+              }
+            }
+
+          SCL_SQUARE_SET_ITERATE_END
+
+          if (exchangeBonus != 0)
+            total += white ? exchangeBonus : -1 * exchangeBonus;
+        }
+      } // for each square
+
+      return total;
+
+      break;
+
+    } // normal position
+  } // switch
+
+  return 0;
+}
+
+#undef ATTACK_BONUS
+#undef MOBILITY_BONUS
+#undef CENTER_BONUS
+#undef CHECK_BONUS
+#undef KING_CASTLED_BONUS
+#undef KING_BACK_BONUS
+#undef PAWN_NON_DOUBLE_BONUS
+#undef PAWN_PAIR_BONUS
+#undef KING_CENTERNESS
+
+SCL_StaticEvaluationFunction _SCL_staticEvaluationFunction;
+int16_t _SCL_currentEval;
+int8_t _SCL_depthHardLimit;
+
+/**
+  Inner recursive function for SCL_boardEvaluateDynamic. It is passed a square
+  (or -1) at which last capture happened, to implement capture extension.
+*/
+int16_t _SCL_boardEvaluateDynamic(SCL_Board board, int8_t depth,
+  int16_t alphaBeta, int8_t takenSquare)
+{
+#if SCL_COUNT_EVALUATED_POSITIONS
+  SCL_positionsEvaluated++;
+#endif
+
+#if SCL_CALL_WDT_RESET
+  wdt_reset();
+#endif
+
+  uint8_t whitesTurn = SCL_boardWhitesTurn(board);
+  int8_t valueMultiply = whitesTurn ? 1 : -1;
+  int16_t bestMoveValue = -1 * SCL_EVALUATION_MAX_SCORE;
+  uint8_t shouldCompute = depth > 0;
+  uint8_t extended = 0;
+  uint8_t positionType = SCL_boardGetPosition(board);
+
+  if (!shouldCompute)
+  {
+    /* here we do two extensions (deeper search): taking on a same square 
+      (exchanges) and checks (good for mating and preventing mates): */
+    extended =
+      (depth > _SCL_depthHardLimit) &&
+      (takenSquare >= 0 ||
+      (SCL_boardGetPosition(board) == SCL_POSITION_CHECK));
+
+    shouldCompute = extended;
+  }
+
+#if SCL_DEBUG_AI
+  char moveStr[8];
+  uint8_t debugFirst = 1;
+#endif
+
+  if (shouldCompute &&
+    (positionType == SCL_POSITION_NORMAL || positionType == SCL_POSITION_CHECK))
+  {
+#if SCL_DEBUG_AI
+    putchar('(');
+#endif
+
+    alphaBeta *= valueMultiply;
+    uint8_t end = 0;
+    const char *b = board;
+
+    depth--;
+
+    for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b)
+    {
+      char s = *b;
+
+      if (s != '.' && SCL_pieceIsWhite(s) == whitesTurn)
+      {
+        SCL_SquareSet moves;
+
+        SCL_squareSetClear(moves);
+
+        SCL_boardGetMoves(board,i,moves);
+
+        if (!SCL_squareSetEmpty(moves))
+        {
+          SCL_SQUARE_SET_ITERATE_BEGIN(moves)
+
+            int8_t captureExtension = -1;
+            
+            if (board[iteratedSquare] != '.' &&   // takes a piece
+              (takenSquare == -1 ||               // extend on first taken sq. 
+              (extended && takenSquare != -1) ||  // ignore check extension
+              (iteratedSquare == takenSquare)))   // extend on same sq. taken
+              captureExtension = iteratedSquare;
+
+            SCL_MoveUndo undo = SCL_boardMakeMove(board,i,iteratedSquare,'q');
+
+            uint8_t s0Dummy, s1Dummy;
+            char pDummy;
+
+            SCL_UNUSED(s0Dummy);
+            SCL_UNUSED(s1Dummy);
+            SCL_UNUSED(pDummy);
+
+#if SCL_DEBUG_AI
+            if (debugFirst)
+              debugFirst = 0;
+            else
+              putchar(',');
+
+            if (extended)
+              putchar('*');
+
+            printf("%s ",SCL_moveToString(board,i,iteratedSquare,'q',moveStr));
+#endif
+
+            int16_t value = _SCL_boardEvaluateDynamic(
+              board,
+              depth, // this is depth - 1, we decremented it
+#if SCL_ALPHA_BETA
+              valueMultiply * bestMoveValue,
+#else
+              0,
+#endif      
+              captureExtension
+              ) * valueMultiply;
+
+            SCL_boardUndoMove(board,undo);
+
+            if (value > bestMoveValue) 
+            {
+              bestMoveValue = value;
+
+#if SCL_ALPHA_BETA
+              // alpha-beta pruning:
+
+              if (value > alphaBeta) // no, >= can't be here
+              {
+                end = 1;
+                iterationEnd = 1;
+              }
+#endif
+            }
+
+          SCL_SQUARE_SET_ITERATE_END
+        } // !squre set empty?
+      } // valid piece?
+
+      if (end)
+        break;
+
+    } // for each square
+
+#if SCL_DEBUG_AI
+  putchar(')');
+#endif
+  }
+  else // don't dive recursively, evaluate statically
+  {
+    bestMoveValue = valueMultiply *
+  #ifndef SCL_EVALUATION_FUNCTION
+      _SCL_staticEvaluationFunction(board);
+  #else
+      SCL_EVALUATION_FUNCTION(board);
+  #endif
+
+    /* For stalemate return the opposite value of the board, i.e. if the
+       position is good for white, then stalemate is good for black and vice
+       versa. */
+    if (positionType == SCL_POSITION_STALEMATE)
+      bestMoveValue *= -1;
+  }
+
+  /* Here we either improve (if the move worsens the situation) or devalve (if
+     it improves the situation) the result: this needs to be done so that good
+     moves far away are seen as worse compared to equally good moves achieved
+     in fewer moves. Without this an AI in winning situation may just repeat
+     random moves and draw by repetition even if it has mate in 1 (it sees all
+     moves as leading to mate). */
+  bestMoveValue += bestMoveValue > _SCL_currentEval * valueMultiply ? -1 : 1;
+
+#if SCL_DEBUG_AI
+  printf("%d",bestMoveValue * valueMultiply);
+#endif
+
+  return bestMoveValue * valueMultiply;
+}
+
+int16_t SCL_boardEvaluateDynamic(SCL_Board board, uint8_t baseDepth,
+  uint8_t extensionExtraDepth, SCL_StaticEvaluationFunction evalFunction)
+{
+  _SCL_staticEvaluationFunction = evalFunction;
+  _SCL_currentEval = evalFunction(board);
+  _SCL_depthHardLimit = 0;
+  _SCL_depthHardLimit -= extensionExtraDepth;
+
+  return _SCL_boardEvaluateDynamic(
+    board,
+    baseDepth,
+    SCL_boardWhitesTurn(board) ? 
+      SCL_EVALUATION_MAX_SCORE : (-1 * SCL_EVALUATION_MAX_SCORE),-1); 
+}
+
+void SCL_boardRandomMove(SCL_Board board, SCL_RandomFunction randFunc,
+  uint8_t *squareFrom, uint8_t *squareTo, char *resultProm)
+{
+  *resultProm = (randFunc() < 128) ? 
+    ((randFunc() < 128) ? 'r' : 'n') : 
+    ((randFunc() < 128) ? 'b' : 'q');
+
+  SCL_SquareSet set;
+  uint8_t white = SCL_boardWhitesTurn(board);
+  const char *s = board;
+
+  SCL_squareSetClear(set);
+
+  // find squares with pieces that have legal moves
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++s)
+  {
+    char c = *s;
+
+    if (c != '.' && SCL_pieceIsWhite(c) == white)
+    {
+      SCL_SquareSet moves;
+
+      SCL_boardGetMoves(board,i,moves);
+
+      if (SCL_squareSetSize(moves) != 0)
+        SCL_squareSetAdd(set,i);
+    }
+  }
+  *squareFrom = SCL_squareSetGetRandom(set,randFunc);
+
+  SCL_boardGetMoves(board,*squareFrom,set);
+
+  *squareTo = SCL_squareSetGetRandom(set,randFunc);
+}
+
+void SCL_printBoardSimple(
+  SCL_Board board,
+  SCL_PutCharFunction putCharFunc,
+  uint8_t selectSquare,
+  uint8_t format)
+{
+  SCL_SquareSet s;
+
+  SCL_squareSetClear(s);
+
+  SCL_printBoard(board,putCharFunc,s,selectSquare,format,1,1,0);
+}
+
+int16_t SCL_getAIMove(
+  SCL_Board board,
+  uint8_t baseDepth,
+  uint8_t extensionExtraDepth,
+  uint8_t endgameExtraDepth,
+  SCL_StaticEvaluationFunction evalFunc,
+  SCL_RandomFunction randFunc,
+  uint8_t randomness,
+  uint8_t repetitionMoveFrom,
+  uint8_t repetitionMoveTo,
+  uint8_t *resultFrom,
+  uint8_t *resultTo,
+  char *resultProm)
+{
+#if SCL_DEBUG_AI
+  puts("===== AI debug =====");
+  putchar('(');
+  unsigned char debugFirst = 1;
+  char moveStr[8];
+#endif 
+
+  if (baseDepth == 0)
+  {
+    SCL_boardRandomMove(board,randFunc,resultFrom,resultTo,resultProm);
+#ifndef SCL_EVALUATION_FUNCTION
+    return evalFunc(board);
+#else
+    return SCL_EVALUATION_FUNCTION(board);
+#endif
+  }
+
+  if (SCL_boardEstimatePhase(board) == SCL_PHASE_ENDGAME)
+    baseDepth += endgameExtraDepth;
+
+  *resultFrom = 0;
+  *resultTo = 0;
+  *resultProm = 'q';
+
+  int16_t bestScore =
+    SCL_boardWhitesTurn(board) ?
+    -1 * SCL_EVALUATION_MAX_SCORE - 1 : (SCL_EVALUATION_MAX_SCORE + 1);
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
+    if (board[i] != '.' && 
+      SCL_boardWhitesTurn(board) == SCL_pieceIsWhite(board[i]))
+    {
+      SCL_SquareSet moves;
+
+      SCL_squareSetClear(moves);
+
+      SCL_boardGetMoves(board,i,moves);
+
+      SCL_SQUARE_SET_ITERATE_BEGIN(moves)
+
+        int16_t score = 0;
+
+#if SCL_DEBUG_AI
+      if (debugFirst)
+        debugFirst = 0;
+      else
+        putchar(',');
+
+printf("%s ",SCL_moveToString(
+board,i,iteratedSquare,'q',moveStr));
+
+#endif
+
+        if (i != repetitionMoveFrom || iteratedSquare != repetitionMoveTo)
+        {
+          SCL_MoveUndo undo = SCL_boardMakeMove(board,i,iteratedSquare,'q');
+
+          score = SCL_boardEvaluateDynamic(board,baseDepth - 1,
+            extensionExtraDepth,evalFunc);
+
+          SCL_boardUndoMove(board,undo);
+        }
+
+        if (randFunc != 0 &&
+          randomness > 1 &&
+          score < 16000 && 
+          score > -16000)
+        {
+          /*^ We limit randomizing by about half the max score for two reasons:
+            to prevent over/under flows and secondly we don't want to alter
+            the highest values for checkmate -- these are modified by tiny
+            values depending on their depth so as to prevent endless loops in
+            which most moves are winning, biasing such values would completely
+            kill that algorithm */
+
+          int16_t bias = randFunc();
+          bias = (bias - 128) / 2;
+          bias *= randomness - 1;
+          score += bias;
+        }
+
+        uint8_t comparison =
+          score == bestScore;
+
+        if ((comparison != 1) &&
+          (
+            (SCL_boardWhitesTurn(board) && score > bestScore) ||
+            (!SCL_boardWhitesTurn(board) && score < bestScore)
+          ))
+          comparison = 2;
+
+        uint8_t replace = 0;
+
+        if (randFunc == 0)
+          replace = comparison == 2;
+        else
+          replace = (comparison == 2) ||
+          ((comparison == 1) && (randFunc() < 160)); // not uniform distr. but simple
+
+        if (replace)
+        {
+          *resultFrom = i;
+          *resultTo = iteratedSquare;
+          bestScore = score;
+        }
+
+      SCL_SQUARE_SET_ITERATE_END
+    }
+
+#if SCL_DEBUG_AI
+  printf(")%d %s\n",bestScore,SCL_moveToString(board,*resultFrom,*resultTo,'q',moveStr));
+  puts("===== AI debug end ===== ");
+#endif 
+
+  return bestScore;
+}
+
+uint8_t SCL_boardToFEN(SCL_Board board, char *string)
+{
+  uint8_t square = 56;
+  uint8_t spaces = 0;
+  uint8_t result = 0;
+  #define put(c) { *string = (c); string++; result++; }
+
+  while (1) // pieces
+  {
+    char s = board[square];
+
+    if (s == '.')
+    {
+      spaces++;
+    }
+    else
+    {
+      if (spaces != 0)
+      {
+        put('0' + spaces)
+        spaces = 0; 
+      }
+
+      put(s)
+    }
+
+    square++;
+
+    if (square % 8 == 0)
+    {
+      if (spaces != 0)
+      {
+        put('0' + spaces)
+        spaces = 0; 
+      }
+
+      if (square == 8)
+        break;
+   
+      put('/'); 
+
+      square -= 16;
+    }
+  }
+
+  put(' ');
+  put(SCL_boardWhitesTurn(board) ? 'w' : 'b');
+  put(' ');
+
+  uint8_t b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0;
+
+  if (b != 0) // castling
+  {
+    if (b & 0x10) put('K');
+    if (b & 0x20) put('Q');
+    if (b & 0x40) put('k');
+    if (b & 0x80) put('q');
+  }
+  else
+    put('-');
+
+  put(' ');
+
+  b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
+
+  if (b < 8)
+  {
+    put('a' + b);
+    put(SCL_boardWhitesTurn(board) ? '6' : '3');
+  }
+  else
+    put('-');
+
+  for (uint8_t i = 0; i < 2; ++i)
+  {
+    put(' ');
+
+    uint8_t moves = i == 0 ?
+      ((uint8_t) board[SCL_BOARD_MOVE_COUNT_BYTE]) :
+      (((uint8_t) board[SCL_BOARD_PLY_BYTE]) / 2 + 1);
+
+    uint8_t hundreds = moves / 100;
+    uint8_t tens = (moves % 100) / 10;
+
+    if (hundreds != 0)
+    {
+      put('0' + hundreds);
+      put('0' + tens);
+    }
+    else if (tens != 0)
+      put('0' + tens);
+
+    put('0' + moves % 10);
+    
+  }
+
+  *string = 0; // terminate the string
+
+  return result + 1;
+
+  #undef put
+}
+
+uint8_t SCL_boardFromFEN(SCL_Board board, const char *string)
+{
+  uint8_t square = 56;
+
+  while (1)
+  {
+    char c = *string;
+
+    if (c == 0)
+      return 0;
+
+    if (c != '/' && c != ' ') // ignore line separators
+    {
+      if (c < '9') // empty square sequence
+      {
+        while (c > '0')
+        {
+          board[square] = '.';
+          square++;
+          c--;
+        }
+      }
+      else // piece
+      {
+        board[square] = c;
+        square++;
+      }
+    }
+    else
+    {
+      if (square == 8)
+        break;
+
+      square -= 16;
+    }
+
+    string++;
+  }
+
+#define nextChar string++; if (*string == 0) return 0;
+
+  nextChar // space
+
+  board[SCL_BOARD_PLY_BYTE] = *string == 'b';
+  nextChar
+
+  nextChar // space
+
+  uint8_t castleEnPassant = 0x0;
+
+  while (*string != ' ')
+  {
+    switch (*string)
+    {
+      case 'K': castleEnPassant |= 0x10; break;
+      case 'Q': castleEnPassant |= 0x20; break;
+      case 'k': castleEnPassant |= 0x40; break;
+      case 'q': castleEnPassant |= 0x80; break;
+      default: castleEnPassant |= 0xf0; break;  // for partial XFEN compat.
+    }
+
+    nextChar
+  }
+
+  nextChar // space
+
+  if (*string != '-')
+  {
+    castleEnPassant |= *string - 'a';
+    nextChar
+  }
+  else
+    castleEnPassant |= 0x0f;
+
+  nextChar
+
+  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castleEnPassant;
+
+  for (uint8_t i = 0; i < 2; ++i)
+  {
+    nextChar // space
+
+    uint8_t ply = 0;
+
+    while (1)
+    {
+      char c = *string;
+
+      if (c < '0' || c > '9')
+        break;
+
+      ply = ply * 10 + (c - '0'); 
+
+      string++;
+    }
+
+    if (i == 0 && *string == 0)
+      return 0;
+
+    if (i == 0)
+      board[SCL_BOARD_MOVE_COUNT_BYTE] = ply;
+    else
+      board[SCL_BOARD_PLY_BYTE] += (ply - 1) * 2;
+  }
+
+#if SCL_960_CASTLING
+  _SCL_board960RememberRookPositions(board);
+#endif
+
+  return 1;
+#undef nextChar
+}
+
+uint8_t SCL_boardEstimatePhase(SCL_Board board)
+{
+  uint16_t totalMaterial = 0;
+
+  uint8_t ply = board[SCL_BOARD_PLY_BYTE];
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
+  {
+    char s = *board;
+
+    if (s != '.')
+    {
+      int16_t v = SCL_pieceValue(s);
+
+      if (!SCL_pieceIsWhite(s))
+        v *= -1;
+
+      totalMaterial += v;
+    }
+
+    board++;
+  }
+
+  if (totalMaterial < SCL_ENDGAME_MATERIAL_LIMIT)
+    return SCL_PHASE_ENDGAME;
+
+  if (ply <= 10 && (totalMaterial >= SCL_START_MATERIAL - 3 * SCL_VALUE_PAWN))
+    return SCL_PHASE_OPENING;    
+
+  return SCL_PHASE_MIDGAME;
+}
+
+#define SCL_IMAGE_COUNT 12
+
+static const uint8_t SCL_images[8 * SCL_IMAGE_COUNT] =
+{
+  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+  0xff,0x81,0xff,0xff,0xff,0xff,0xff,0x81,0xff,0xff,0xff,0xff,
+  0xff,0x81,0xe7,0xf7,0xf7,0xaa,0xff,0xbd,0xe7,0xf7,0xf7,0xaa,
+  0xff,0xc3,0xc3,0xe3,0xc1,0x80,0xff,0x99,0xdb,0xeb,0xc9,0x94,
+  0xe7,0xc3,0x81,0xc1,0x94,0x80,0xe7,0xdb,0xbd,0xdd,0xbe,0xbe,
+  0xc3,0xc3,0x91,0xe3,0x80,0x80,0xdb,0x99,0x8d,0xeb,0xaa,0xbe,
+  0xc3,0x81,0xe1,0xc1,0xc1,0xc1,0xdb,0xbd,0xdd,0xe3,0xdd,0xdd,
+  0x81,0x81,0xc1,0x9c,0xc1,0xc1,0x81,0x81,0xc1,0x9c,0xc1,0xc1
+};
+
+void SCL_drawBoard(
+  SCL_Board board,
+  SCL_PutPixelFunction putPixel,
+  uint8_t selectedSquare,
+  SCL_SquareSet highlightSquares,
+  uint8_t blackDown)
+{
+  uint8_t row = 0;
+  uint8_t col = 0;
+  uint8_t x = 0;
+  uint8_t y = 0;
+  uint16_t n = 0;
+  uint8_t s = 0;
+
+  uint8_t pictureLine = 0;
+  uint8_t loadLine = 1;
+
+  while (row < 8)
+  {
+    if (loadLine)
+    {
+      s = blackDown ? (row * 8 + (7 - col)) : ((7 - row) * 8 + col);
+
+      char piece = board[s];
+
+      if (piece == '.')
+        pictureLine = (y == 4) ? 0xef : 0xff;
+      else
+      {
+        uint8_t offset = SCL_pieceIsWhite(piece) ? 6 : 0;
+        piece = SCL_pieceToColor(piece,1);
+
+        switch (piece)
+        {
+          case 'R': offset += 1; break;
+          case 'N': offset += 2; break;
+          case 'B': offset += 3; break;
+          case 'K': offset += 4; break;
+          case 'Q': offset += 5; break;
+          default: break;
+        }
+      
+        pictureLine = SCL_images[y * SCL_IMAGE_COUNT + offset];
+      }
+
+      if (SCL_squareSetContains(highlightSquares,s))
+        pictureLine &= (y % 2) ? 0xaa : 0x55;
+      
+      if (s == selectedSquare)
+        pictureLine &= (y == 0 || y == 7) ? 0x00 : ~0x81;
+
+      loadLine = 0;
+    }
+
+    putPixel(pictureLine & 0x80,n);
+    pictureLine <<= 1;
+
+    n++;
+    x++;
+
+    if (x == 8)
+    {
+      col++;
+      loadLine = 1;
+      x = 0;
+    }
+
+    if (col == 8)
+    {
+      y++;
+      col = 0;
+      x = 0;
+    }
+
+    if (y == 8)
+    {
+      row++;
+      y = 0;
+    }
+  }
+}
+
+uint32_t SCL_boardHash32(const SCL_Board board)
+{
+  uint32_t result = (board[SCL_BOARD_PLY_BYTE] & 0x01) +
+    (((uint32_t) ((uint8_t) board[SCL_BOARD_ENPASSANT_CASTLE_BYTE])) << 24) +
+    board[SCL_BOARD_MOVE_COUNT_BYTE];
+
+  const char *b = board;
+
+  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b)
+  {
+    switch (*b)
+    {
+#define C(p,n) case p: result ^= (i + 1) * n; break;
+      // the below number are primes
+      C('P',4003)
+      C('R',84673)
+      C('N',93911)
+      C('B',999331)
+      C('Q',909091)
+      C('K',2796203)
+      C('p',4793)
+      C('r',19391)
+      C('n',391939)
+      C('b',108301)
+      C('q',174763)
+      C('k',2474431)
+#undef C
+      default: break;
+    }
+  }
+
+  // for extra spread of values we swap the low/high parts:
+  result = (result >> 16) | (result << 16);
+
+  return result;
+}
+
+void SCL_boardDisableCastling(SCL_Board board)
+{
+  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= 0x0f;
+}
+
+uint8_t SCL_boardMoveResetsCount(SCL_Board board,
+  uint8_t squareFrom, uint8_t squareTo)
+{
+  return board[squareFrom] == 'P' || board[squareFrom] == 'p' ||
+    board[squareTo] != '.';
+}
+
+void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc,
+  SCL_Board initialState)
+{
+  if (SCL_recordLength(r) == 0)
+    return;
+
+  uint16_t pos = 0;
+
+  SCL_Board board;
+
+  if (initialState != 0)
+    for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
+      board[i] = initialState[i];
+  else
+    SCL_boardInit(board);
+
+  while (1)
+  {
+    uint8_t s0, s1;
+    char p;
+
+    uint8_t state = SCL_recordGetMove(r,pos,&s0,&s1,&p);
+
+    pos++;
+
+    if (pos % 2)
+    {
+      uint8_t move = pos / 2 + 1;
+
+      if (move / 100 != 0)
+        putCharFunc('0' + move / 100);
+
+      if (move / 10 != 0 || move / 100 != 0)
+        putCharFunc('0' + (move % 100) / 10);
+
+      putCharFunc('0' + move % 10);
+
+      putCharFunc('.');
+      putCharFunc(' ');
+    }
+
+#if !SCL_960_CASTLING
+    if ((board[s0] == 'K' && s0 == 4 && (s1 == 2 || s1 == 6)) ||
+      (board[s0] == 'k' && s0 == 60 && (s1 == 62 || s1 == 58)))
+#else
+    if ((board[s0] == 'K' && board[s1] == 'R') ||
+        (board[s0] == 'k' && board[s1] == 'r'))
+#endif
+    {
+      putCharFunc('O');
+      putCharFunc('-');
+      putCharFunc('O');
+
+#if !SCL_960_CASTLING
+      if (s1 == 58 || s1 == 2)
+#else
+      if ((s1 == (board[SCL_BOARD_EXTRA_BYTE] & 0x07)) ||
+          (s1 == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07)))
+#endif
+      {
+        putCharFunc('-');
+        putCharFunc('O');
+      }
+    }
+    else
+    {
+      uint8_t pawn = board[s0] == 'P' || board[s0] == 'p';
+
+      if (!pawn)
+      {
+        putCharFunc(SCL_pieceToColor(board[s0],1));
+
+        // disambiguation:
+
+        uint8_t specify = 0;
+
+        for (int i = 0; i < SCL_BOARD_SQUARES; ++i)
+          if (i != s0 && board[i] == board[s0])
+          {
+            SCL_SquareSet s;
+            
+            SCL_squareSetClear(s);
+
+            SCL_boardGetMoves(board,i,s);
+
+            if (SCL_squareSetContains(s,s1))
+              specify |= (s0 % 8 != s1 % 8) ? 1 : 2;
+          }
+
+        if (specify & 0x01)
+          putCharFunc('a' + s0 % 8);
+
+        if (specify & 0x02)
+          putCharFunc('1' + s0 / 8);
+      }
+
+      if (board[s1] != '.' ||
+       (pawn && s0 % 8 != s1 % 8 && board[s1] == '.')) // capture?
+      {
+        if (pawn)
+          putCharFunc('a' + s0 % 8);
+          
+        putCharFunc('x');
+      }
+      
+      putCharFunc('a' + s1 % 8);
+      putCharFunc('1' + s1 / 8);
+
+      if (pawn && (s1 >= 56 || s1 <= 7)) // promotion?
+      {
+        putCharFunc('=');
+        putCharFunc(SCL_pieceToColor(p,1));
+      }
+    }
+
+    SCL_boardMakeMove(board,s0,s1,p);
+
+    uint8_t position = SCL_boardGetPosition(board);
+    if (position == SCL_POSITION_CHECK)
+      putCharFunc('+');
+
+    if (position == SCL_POSITION_MATE)
+    {
+      putCharFunc('#');
+      break;
+    }
+    else if (state != SCL_RECORD_CONT)
+    {
+      putCharFunc('*');
+      break;
+    }
+      
+    putCharFunc(' ');
+  }
+}
+
+void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo)
+{
+  for (uint16_t i = 0; i < SCL_RECORD_MAX_SIZE; ++i)
+    recordTo[i] = recordFrom[i];
+}
+
+void SCL_gameInit(SCL_Game *game, const SCL_Board startState)
+{
+  game->startState = startState;
+
+  if (startState != 0)
+    SCL_boardCopy(startState,game->board);
+  else
+    SCL_boardInit(game->board);
+
+  SCL_recordInit(game->record);
+
+  for (uint8_t i = 0; i < 14; ++i)
+    game->prevMoves[i] = 0;
+  game->state = SCL_GAME_STATE_PLAYING;
+  game->ply = 0;
+
+  SCL_recordInit(game->record);
+}
+
+uint8_t SCL_gameGetRepetiotionMove(SCL_Game *game,
+  uint8_t *squareFrom, uint8_t *squareTo)
+{
+  if (squareFrom != 0 && squareTo != 0)
+  {
+      *squareFrom = 0;
+      *squareTo = 0;
+  }
+
+  /*  pos. 1st         2nd         3rd
+            |           |           |
+            v           v           v       
+             01 23 45 67 89 AB CD EF
+     move    ab cd ba dc ab cd ba dc */
+
+  if (game->ply >= 7 &&
+    game->prevMoves[0] == game->prevMoves[5] &&
+    game->prevMoves[0] == game->prevMoves[8] &&
+    game->prevMoves[0] == game->prevMoves[13] &&
+
+    game->prevMoves[1] == game->prevMoves[4] &&
+    game->prevMoves[1] == game->prevMoves[9] &&
+    game->prevMoves[1] == game->prevMoves[12] &&
+
+    game->prevMoves[2] == game->prevMoves[7] &&
+    game->prevMoves[2] == game->prevMoves[10] &&
+
+    game->prevMoves[3] == game->prevMoves[6] &&
+    game->prevMoves[3] == game->prevMoves[11]
+    )
+  {
+    if (squareFrom != 0 && squareTo != 0)
+    {
+      *squareFrom = game->prevMoves[3];
+      *squareTo = game->prevMoves[2];
+    }
+
+    return 1;
+  } 
+
+  return 0;
+}
+
+void SCL_gameMakeMove(SCL_Game *game, uint8_t squareFrom, uint8_t squareTo,
+  char promoteTo)
+{
+  uint8_t repetitionS0, repetitionS1;
+
+  SCL_gameGetRepetiotionMove(game,&repetitionS0,&repetitionS1);
+  SCL_boardMakeMove(game->board,squareFrom,squareTo,promoteTo);
+  SCL_recordAdd(game->record,squareFrom,squareTo,promoteTo,SCL_RECORD_CONT);
+  // ^ TODO: SCL_RECORD_CONT
+
+  game->ply++;
+
+  for (uint8_t i = 0; i < 14 - 2; ++i)
+    game->prevMoves[i] = game->prevMoves[i + 2];
+
+  game->prevMoves[12] = squareFrom;
+  game->prevMoves[13] = squareTo;
+
+  if (squareFrom == repetitionS0 && squareTo == repetitionS1)
+    game->state = SCL_GAME_STATE_DRAW_REPETITION;
+  else if (game->board[SCL_BOARD_MOVE_COUNT_BYTE] >= 50)
+    game->state = SCL_GAME_STATE_DRAW_50;
+  else
+  {
+    uint8_t position = SCL_boardGetPosition(game->board);
+
+    switch (position)
+    {
+      case SCL_POSITION_MATE:
+        game->state = SCL_boardWhitesTurn(game->board) ?
+          SCL_GAME_STATE_BLACK_WIN : SCL_GAME_STATE_WHITE_WIN;
+        break;
+
+      case SCL_POSITION_STALEMATE:
+        game->state = SCL_GAME_STATE_DRAW_STALEMATE;
+        break;
+
+      case SCL_POSITION_DEAD:
+        game->state = SCL_GAME_STATE_DRAW_DEAD;
+        break;
+
+      default: break;
+    }
+  }
+}
+
+uint8_t SCL_gameUndoMove(SCL_Game *game)
+{
+  if (game->ply == 0)
+    return 0;
+
+  if ((game->ply - 1) > SCL_recordLength(game->record))
+    return 0; // can't undo, lacking record
+
+  SCL_Record r;
+
+  SCL_recordCopy(game->record,r);
+
+  uint16_t applyMoves = game->ply - 1;
+
+  SCL_gameInit(game,game->startState);
+
+  for (uint16_t i = 0; i < applyMoves; ++i)
+  {
+    uint8_t s0, s1;
+    char p;
+
+    SCL_recordGetMove(r,i,&s0,&s1,&p);
+    SCL_gameMakeMove(game,s0,s1,p);
+  }
+    
+  return 1;
+}
+
+uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom,
+  uint8_t squareTo)
+{
+  if (squareFrom >= SCL_BOARD_SQUARES || squareTo >= SCL_BOARD_SQUARES)
+    return 0;
+
+  char piece = board[squareFrom];
+
+  if ((piece == '.') ||
+    (SCL_boardWhitesTurn(board) != SCL_pieceIsWhite(piece)))
+    return 0;
+
+  SCL_SquareSet moves;
+
+  SCL_boardGetMoves(board,squareFrom,moves);
+
+  return SCL_squareSetContains(moves,squareTo);
+}
+
+#endif // guard
diff --git a/legacy/lib/vsop87/LICENSE.txt b/legacy/lib/vsop87/LICENSE.txt
new file mode 100755 (executable)
index 0000000..cc8be27
--- /dev/null
@@ -0,0 +1,5 @@
+Author: Greg A Miller (gmiller@gregmiller.net)\r
+I release all of my work in this project to the public domain.\r
+\r
+Note, though, not everything here can be considered my work.  The VSOP87 theory is the work of\r
+Bureau des Longitudes.
\ No newline at end of file
diff --git a/legacy/lib/vsop87/README.md b/legacy/lib/vsop87/README.md
new file mode 100755 (executable)
index 0000000..6ec7cbb
--- /dev/null
@@ -0,0 +1,143 @@
+# Example\r
+\r
+An example in JavaScript showing computations is available at http://www.celestialprogramming.com/vsop87-multilang/\r
+\r
+### Current Stauts\r
+Versions for many different languages and environments have been created.  These have passed all test cases provided by the\r
+VSOP87 authors, a validation program is also included for each language.  They are ready for use. See the "Languages" directory for the status\r
+of each language.\r
+\r
+Still to come: more languages, documentation, and examples.\r
+\r
+# About this Project\r
+The purpose of this project is to create versions of VSOP87 in many different languages.  The plan is to generate a initial version in C# truncated to different\r
+precisions, then convert the resulting files into other languages.  This project was inspired by the [Neoprogrammics Source Code Generator Tool](http://www.neoprogrammics.com/vsop87/source_code_generator_tool/).\r
+The goal of this project is to provide easier to use (readymade source files), include all data (e.g. the Moon and velocities), are truncated to different accuracy levels,\r
+, for more languages, and have a more permissive license (public domain).  It will provide tests against the original VSOP87 test data for each programming language to validate correctness, as well as examples to show common useage.\r
+\r
+# Language Status\r
+\r
+Below is a list of the planned languages and their current status.\r
+\r
+\r
+Language     |Inline|JSON|CSV|CSV Low Mem|Validation Tests|Alt Az Reduction Example\r
+-------------|------|----|---|-----------|----------------|--------------\r
+Java         |Yes   |    |Yes|           |Pass            |\r
+C            |Yes   |    |   |Yes        |Pass            |\r
+C#           |Yes   |    |Yes|           |Pass            |\r
+Python       |Yes   |Yes |Yes|           |Pass            |\r
+C++          |Yes   |    |   |           |Pass            |\r
+VB.Net       |Yes   |    |   |           |Pass            |\r
+JavaScript   |Yes   |Yes |   |           |Pass            |Yes\r
+PHP          |Yes   |    |   |           |Pass            |\r
+Ruby         |Yes   |    |   |           |Pass            |\r
+Swift        |Yes   |    |   |           |Pass            |\r
+Matlab/Octave|Yes   |    |   |           |Pass            |\r
+Groovy       |      |    |Yes|           |Pass            |\r
+Go           |Yes   |    |   |           |Pass            |\r
+Pascal       |Yes   |    |   |           |0.000009au      |\r
+Perl         |Yes   |    |   |Yes        |Pass            |\r
+R            |Fail! |    |   |           |Fail!           |\r
+Cobol        |      |    |   |           |                |\r
+Fortran      |      |    |   |           |                |\r
+Rust         |Yes   |    |   |           |Pass            |\r
+Arduino      |Yes   |    |   |           |0.000009au      |\r
+\r
+# What is VSOP87? Why use it?\r
+VSOP87 is one of many solutions available for predicting the positions of the planets (plus the Sun and the Moon) in our solar system.  The actual name is\r
+Variations S�culaires des Orbites Plan�taires, and it was published in 1987 by the Bureau des Longitudes.  Even though there have been many other methods\r
+developed before and after VSOP87, it remains one of the most popular methods among amatuers.  It provides better accuracy than most amatuers require (.1 arcseconds)\r
+over a period of a few thousand years before and after the year 2000.\r
+\r
+# Implementation Types\r
+There are a few different types of implementations: Inline, JSON, CSV, and CSV Low Memory.  The inline versions are generally the easiest to use as they will have no\r
+external requirements, they are also the easiest to generate, so they're available for more languages.  The JSON versions require a JSON file (located in the \r
+languages/JSON folder) which is loaded into memory.  The advantages of the JSON versions are you can compute the velocities with the same data the positions\r
+are generated from, and you can load and dispose of the memory used by the data when you need it.  The CSV implementations are similar to the JSON implementations, \r
+but, obviously, read from a CSV file (located in the languages/CSV folder). And the Low Memory CSV implementations read the same CSV files, but the\r
+data is not retained in memory.  The JSON versions are located in the Languages/JSON directory, as well as the required JSON files, and the CSV implementations\r
+are in the languages/CSV folder.\r
+\r
+# Which Version Should I Use?\r
+For the overwhelming majority of users, the VSOP87A_full version will be all that you need.  This is the full version, but should still be fast enough and small enough\r
+for most use cases.  Using the full version eliminates any questions of whether it will be accurate enough.  If, after trying the full version, the computation isn't\r
+fast enough, from there you should experiment with truncated versions.  The VSOP87A versions are the only versions which include both the Earth and Moon.  VSOP87A doesn't include the moon directly, but does include the Earth and the Earth-Moon Barrycenter, and all provided code for the VSOP87A versions include a function to compute the Moon's position from the Earth and EMB.  Using the versions that provide the velocities is necessary if you want to account for relativistic effects do to the motion of the observer.\r
+\r
+There are several versions of the main theory.  The first is just called VSOP87, the remainder of them are appended with the letters A, B, C, D, E.  Each version\r
+provides the data in a slightly different form.\r
+\r
+Version|Mercury|Venus|Earth|EMB|Mars|Jupiter|Saturn|Uranus|Neptune|Sun|Coordinates\r
+-------|-------|-----|-----|---|----|-------|------|------|-------|---|-----------\r
+VSOP87|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|Yes|No|Keperian Orbital Elements\r
+VSOP87A|Yes|Yes|Yes|Yes|Yes|Yes|Yes|Yes|Yes|No|Heliocentric J2000 Ecliptic Rectangular XYZ\r
+VSOP87B|Yes|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|No|Heliocentric J2000 Ecliptic Spherical LBR\r
+VSOP87C|Yes|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|No|Heliocentric Ecliptic of date Rectangular XYZ\r
+VSOP87D|Yes|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|No|Heliocentric Ecliptic of date Spherical LBR\r
+VSOP87E|Yes|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|Yes|Barycentric J2000 Ecliptic Rectangular XYZ\r
+\r
+# Truncated versions\r
+Since the full VSOP87 provides more accuracy than most amateurs require, the algorithm can be shortened by eliminating terms.  This speeds up the computations, and\r
+reduces the overall size of the code at the cost of accuracy.  For each programming language, this project supplies VSOP87 truncated at ten different levels.  The\r
+effects of accuracy are detailed in the graphs below.  Each level of truncation eliminates any terms with a coefficient 1/10 the previous truncation level.\r
+\r
+Trunaction Level|Total Terms|Skipped Terms|Percent Skipped\r
+----------------|-----------|-------------|---------------\r
+full    |269949|0     |0 %\r
+xx large |269949|20998 |7.7 %\r
+x large  |269949|67848 |25.1 %\r
+large   |269949|145031|53.7 %\r
+small   |269949|218559|80.9 %\r
+x small  |269949|250204|92.6 %\r
+milli   |269949|262369|97.1 %\r
+micro   |269949|266975|98.8 %\r
+nano    |269949|268686|99.5 %\r
+pico    |269949|269464|99.8 %\r
+\r
+# Accuracy\r
+\r
+Accuracy graphs are below.  They show the error in degrees of each body as viewed from Earth.  Each graph shows the error for one body for all truncated versions of\r
+VSOP87.  The error is vs. the full version of VSOP87, so the inherent error in VSOP87 also has to be added.  Some bodies appear twice, to zoom in on the lower portion\r
+of the graph, as the error of the pico version makes it difficult to see errors amongst the larger versions.  The Python script and data to reproduce the graphs is\r
+in the Accuracy folder, by regenerating them you can use the Matplotlib interface to explore the graphs further.\r
+\r
+Since the error is computed from the geocenter, the Earth does not appear in the graphs below, nor does the Sun.  Graphs are also not present for the Moon, but graphs\r
+are available for the Earth-Moon Barrycenter (EMB), the error for the Moon will be a linear function of the EMB error.\r
+\r
+The full VSOP87 accuracy is .1 arcseconds for Saturn, and better for all others.  For more details on accuracy of the full theory consult\r
+[Planetary theories in rectangular and spherical variables - VSOP 87 solutions](http://articles.adsabs.harvard.edu/full/1988A%26A...202..309B).\r
+\r
+### Mercury\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/mercury.png)\r
+\r
+### Venus\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/venus.png)\r
+\r
+### Earth-Moon Barrycenter\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/emb.png)\r
+\r
+### Earth-Moon Barrycenter (zoomed)\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/emb2.png)\r
+\r
+### Mars\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/mars.png)\r
+\r
+### Jupiter\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/jupiter.png)\r
+\r
+### Saturn\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/saturn.png)\r
+\r
+### Saturn (zoomed)\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/saturn2.png)\r
+\r
+### Uranus\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/uranus.png)\r
+\r
+### Uranus (zoomed)\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/uranus2.png)\r
+\r
+### Neptune\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/neptune.png)\r
+\r
+### Neptune (zoomed)\r
+![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/neptune2.png)\r
diff --git a/legacy/lib/vsop87/vsop87a_micro.c b/legacy/lib/vsop87/vsop87a_micro.c
new file mode 100755 (executable)
index 0000000..7e69fea
--- /dev/null
@@ -0,0 +1,1019 @@
+//VSOP87-Multilang http://www.astrogreg.com/vsop87-multilang/index.html\r
+//Greg Miller (gmiller@gregmiller.net) 2019.  Released as Public Domain\r
+\r
+#include <math.h>\r
+#include "vsop87a_micro.h"\r
+double vsop87a_micro_earth_x(double t);\r
+double vsop87a_micro_earth_y(double t);\r
+double vsop87a_micro_earth_z(double t);\r
+double vsop87a_micro_emb_x(double t);\r
+double vsop87a_micro_emb_y(double t);\r
+double vsop87a_micro_emb_z(double t);\r
+double vsop87a_micro_jupiter_x(double t);\r
+double vsop87a_micro_jupiter_y(double t);\r
+double vsop87a_micro_jupiter_z(double t);\r
+double vsop87a_micro_mars_x(double t);\r
+double vsop87a_micro_mars_y(double t);\r
+double vsop87a_micro_mars_z(double t);\r
+double vsop87a_micro_mercury_x(double t);\r
+double vsop87a_micro_mercury_y(double t);\r
+double vsop87a_micro_mercury_z(double t);\r
+double vsop87a_micro_neptune_x(double t);\r
+double vsop87a_micro_neptune_y(double t);\r
+double vsop87a_micro_neptune_z(double t);\r
+double vsop87a_micro_saturn_x(double t);\r
+double vsop87a_micro_saturn_y(double t);\r
+double vsop87a_micro_saturn_z(double t);\r
+double vsop87a_micro_uranus_x(double t);\r
+double vsop87a_micro_uranus_y(double t);\r
+double vsop87a_micro_uranus_z(double t);\r
+double vsop87a_micro_venus_x(double t);\r
+double vsop87a_micro_venus_y(double t);\r
+double vsop87a_micro_venus_z(double t);\r
+\r
+\r
+void vsop87a_micro_getEarth(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_earth_x(t);\r
+   temp[1]=vsop87a_micro_earth_y(t);\r
+   temp[2]=vsop87a_micro_earth_z(t);\r
+}\r
+\r
+void vsop87a_micro_getEmb(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_emb_x(t);\r
+   temp[1]=vsop87a_micro_emb_y(t);\r
+   temp[2]=vsop87a_micro_emb_z(t);\r
+}\r
+\r
+void vsop87a_micro_getJupiter(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_jupiter_x(t);\r
+   temp[1]=vsop87a_micro_jupiter_y(t);\r
+   temp[2]=vsop87a_micro_jupiter_z(t);\r
+}\r
+\r
+void vsop87a_micro_getMars(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_mars_x(t);\r
+   temp[1]=vsop87a_micro_mars_y(t);\r
+   temp[2]=vsop87a_micro_mars_z(t);\r
+}\r
+\r
+void vsop87a_micro_getMercury(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_mercury_x(t);\r
+   temp[1]=vsop87a_micro_mercury_y(t);\r
+   temp[2]=vsop87a_micro_mercury_z(t);\r
+}\r
+\r
+void vsop87a_micro_getNeptune(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_neptune_x(t);\r
+   temp[1]=vsop87a_micro_neptune_y(t);\r
+   temp[2]=vsop87a_micro_neptune_z(t);\r
+}\r
+\r
+void vsop87a_micro_getSaturn(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_saturn_x(t);\r
+   temp[1]=vsop87a_micro_saturn_y(t);\r
+   temp[2]=vsop87a_micro_saturn_z(t);\r
+}\r
+\r
+void vsop87a_micro_getUranus(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_uranus_x(t);\r
+   temp[1]=vsop87a_micro_uranus_y(t);\r
+   temp[2]=vsop87a_micro_uranus_z(t);\r
+}\r
+\r
+void vsop87a_micro_getVenus(double t,double temp[]){\r
+   temp[0]=vsop87a_micro_venus_x(t);\r
+   temp[1]=vsop87a_micro_venus_y(t);\r
+   temp[2]=vsop87a_micro_venus_z(t);\r
+}\r
+\r
+void vsop87a_micro_getMoon(double earth[], double emb[],double temp[]){\r
+   temp[0]=(emb[0]-earth[0])*(1 + 1 / 0.01230073677);\r
+   temp[1]=(emb[1]-earth[1])*(1 + 1 / 0.01230073677);\r
+   temp[2]=(emb[2]-earth[2])*(1 + 1 / 0.01230073677);\r
+   temp[0]=temp[0]+earth[0];\r
+   temp[1]=temp[1]+earth[1];\r
+   temp[2]=temp[2]+earth[2];\r
+}\r
+\r
+double vsop87a_micro_earth_x(double t){\r
+   double earth_x_0=0.0;\r
+   earth_x_0+=     0.99982928844 * cos( 1.75348568475 +     6283.07584999140*t);\r
+   earth_x_0+=     0.00835257300 * cos( 1.71034539450 +    12566.15169998280*t);\r
+   earth_x_0+=     0.00561144206 * cos( 0.00000000000 +        0.00000000000*t);\r
+   earth_x_0+=     0.00010466628 * cos( 1.66722645223 +    18849.22754997420*t);\r
+\r
+   double earth_x_1=0.0;\r
+   earth_x_1+=     0.00123403056 * cos( 0.00000000000 +        0.00000000000*t);\r
+   earth_x_1+=     0.00051500156 * cos( 6.00266267204 +    12566.15169998280*t);\r
+   earth_x_1=earth_x_1 * t;\r
+\r
+   return earth_x_0+earth_x_1;\r
+}\r
+\r
+double vsop87a_micro_earth_y(double t){\r
+   double earth_y_0=0.0;\r
+   earth_y_0+=     0.99989211030 * cos( 0.18265890456 +     6283.07584999140*t);\r
+   earth_y_0+=     0.02442699036 * cos( 3.14159265359 +        0.00000000000*t);\r
+   earth_y_0+=     0.00835292314 * cos( 0.13952878991 +    12566.15169998280*t);\r
+   earth_y_0+=     0.00010466965 * cos( 0.09641690558 +    18849.22754997420*t);\r
+\r
+   double earth_y_1=0.0;\r
+   earth_y_1+=     0.00093046324 * cos( 0.00000000000 +        0.00000000000*t);\r
+   earth_y_1+=     0.00051506609 * cos( 4.43180499286 +    12566.15169998280*t);\r
+   earth_y_1=earth_y_1 * t;\r
+\r
+   return earth_y_0+earth_y_1;\r
+}\r
+\r
+double vsop87a_micro_earth_z(double t){\r
+   double earth_z_1=0.0;\r
+   earth_z_1+=     0.00227822442 * cos( 3.41372504278 +     6283.07584999140*t);\r
+   earth_z_1=earth_z_1 * t;\r
+\r
+   return earth_z_1;\r
+}\r
+\r
+double vsop87a_micro_emb_x(double t){\r
+   double emb_x_0=0.0;\r
+   emb_x_0+=     0.99982927460 * cos( 1.75348568475 +     6283.07584999140*t);\r
+   emb_x_0+=     0.00835257300 * cos( 1.71034539450 +    12566.15169998280*t);\r
+   emb_x_0+=     0.00561144161 * cos( 0.00000000000 +        0.00000000000*t);\r
+   emb_x_0+=     0.00010466628 * cos( 1.66722645223 +    18849.22754997420*t);\r
+\r
+   double emb_x_1=0.0;\r
+   emb_x_1+=     0.00123403046 * cos( 0.00000000000 +        0.00000000000*t);\r
+   emb_x_1+=     0.00051500156 * cos( 6.00266267204 +    12566.15169998280*t);\r
+   emb_x_1=emb_x_1 * t;\r
+\r
+   return emb_x_0+emb_x_1;\r
+}\r
+\r
+double vsop87a_micro_emb_y(double t){\r
+   double emb_y_0=0.0;\r
+   emb_y_0+=     0.99989209645 * cos( 0.18265890456 +     6283.07584999140*t);\r
+   emb_y_0+=     0.02442698841 * cos( 3.14159265359 +        0.00000000000*t);\r
+   emb_y_0+=     0.00835292314 * cos( 0.13952878991 +    12566.15169998280*t);\r
+   emb_y_0+=     0.00010466965 * cos( 0.09641690558 +    18849.22754997420*t);\r
+\r
+   double emb_y_1=0.0;\r
+   emb_y_1+=     0.00093046317 * cos( 0.00000000000 +        0.00000000000*t);\r
+   emb_y_1+=     0.00051506609 * cos( 4.43180499286 +    12566.15169998280*t);\r
+   emb_y_1=emb_y_1 * t;\r
+\r
+   return emb_y_0+emb_y_1;\r
+}\r
+\r
+double vsop87a_micro_emb_z(double t){\r
+   double emb_z_1=0.0;\r
+   emb_z_1+=     0.00227822442 * cos( 3.41372504278 +     6283.07584999140*t);\r
+   emb_z_1=emb_z_1 * t;\r
+\r
+   return emb_z_1;\r
+}\r
+\r
+double vsop87a_micro_jupiter_x(double t){\r
+   double jupiter_x_0=0.0;\r
+   jupiter_x_0+=     5.19663470114 * cos( 0.59945082355 +      529.69096509460*t);\r
+   jupiter_x_0+=     0.36662642320 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_x_0+=     0.12593937922 * cos( 0.94911583701 +     1059.38193018920*t);\r
+   jupiter_x_0+=     0.01500672056 * cos( 0.73175134610 +      522.57741809380*t);\r
+   jupiter_x_0+=     0.01476224578 * cos( 3.61736921122 +      536.80451209540*t);\r
+   jupiter_x_0+=     0.00457752736 * cos( 1.29883700755 +     1589.07289528380*t);\r
+   jupiter_x_0+=     0.00301689798 * cos( 5.17372551148 +        7.11354700080*t);\r
+   jupiter_x_0+=     0.00385975375 * cos( 2.01229910687 +      103.09277421860*t);\r
+   jupiter_x_0+=     0.00194025405 * cos( 5.02580363996 +      426.59819087600*t);\r
+   jupiter_x_0+=     0.00150678793 * cos( 6.12003027739 +      110.20632121940*t);\r
+   jupiter_x_0+=     0.00144867641 * cos( 5.55980577080 +      632.78373931320*t);\r
+   jupiter_x_0+=     0.00134226996 * cos( 0.87648567011 +      213.29909543800*t);\r
+   jupiter_x_0+=     0.00103494641 * cos( 6.19324769120 +     1052.26838318840*t);\r
+   jupiter_x_0+=     0.00114201562 * cos( 0.01567084269 +     1162.47470440780*t);\r
+   jupiter_x_0+=     0.00072095575 * cos( 3.96117430643 +     1066.49547719000*t);\r
+   jupiter_x_0+=     0.00059486083 * cos( 4.45769374358 +      949.17560896980*t);\r
+   jupiter_x_0+=     0.00068284021 * cos( 3.44051122631 +      846.08283475120*t);\r
+   jupiter_x_0+=     0.00047092251 * cos( 1.44612636451 +      419.48464387520*t);\r
+   jupiter_x_0+=     0.00030623417 * cos( 2.99132321427 +      206.18554843720*t);\r
+   jupiter_x_0+=     0.00026613459 * cos( 4.85169906494 +      323.50541665740*t);\r
+   jupiter_x_0+=     0.00019727457 * cos( 1.64891626213 +     2118.76386037840*t);\r
+   jupiter_x_0+=     0.00016481594 * cos( 1.95150056568 +      316.39186965660*t);\r
+   jupiter_x_0+=     0.00016101974 * cos( 0.87973155980 +      515.46387109300*t);\r
+   jupiter_x_0+=     0.00014209487 * cos( 2.07769621413 +      742.99006053260*t);\r
+   jupiter_x_0+=     0.00015192516 * cos( 6.25820127906 +      735.87651353180*t);\r
+   jupiter_x_0+=     0.00011423199 * cos( 3.48146108929 +      543.91805909620*t);\r
+   jupiter_x_0+=     0.00012155285 * cos( 3.75229924999 +      525.75881183150*t);\r
+   jupiter_x_0+=     0.00011996271 * cos( 0.58568573729 +      533.62311835770*t);\r
+\r
+   double jupiter_x_1=0.0;\r
+   jupiter_x_1+=     0.00882389251 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_x_1+=     0.00635297172 * cos( 0.10662156868 +     1059.38193018920*t);\r
+   jupiter_x_1+=     0.00599720482 * cos( 2.42996678275 +      522.57741809380*t);\r
+   jupiter_x_1+=     0.00589157060 * cos( 1.91556314637 +      536.80451209540*t);\r
+   jupiter_x_1+=     0.00081697204 * cos( 3.46668108797 +        7.11354700080*t);\r
+   jupiter_x_1+=     0.00046201898 * cos( 0.45714214032 +     1589.07289528380*t);\r
+   jupiter_x_1+=     0.00032508590 * cos( 1.74648849928 +     1052.26838318840*t);\r
+   jupiter_x_1+=     0.00033891193 * cos( 4.10113482752 +      529.69096509460*t);\r
+   jupiter_x_1+=     0.00031234303 * cos( 2.34698051502 +     1066.49547719000*t);\r
+   jupiter_x_1+=     0.00021244363 * cos( 4.36576178953 +      110.20632121940*t);\r
+   jupiter_x_1+=     0.00018156701 * cos( 4.00572238779 +      426.59819087600*t);\r
+   jupiter_x_1+=     0.00013577576 * cos( 0.30008010246 +      632.78373931320*t);\r
+   jupiter_x_1+=     0.00012889505 * cos( 2.57489294062 +      515.46387109300*t);\r
+   jupiter_x_1=jupiter_x_1 * t;\r
+\r
+   double jupiter_x_2=0.0;\r
+   jupiter_x_2+=     0.00123864644 * cos( 4.13563277513 +      522.57741809380*t);\r
+   jupiter_x_2+=     0.00121521296 * cos( 0.21155109275 +      536.80451209540*t);\r
+   jupiter_x_2+=     0.00085355503 * cos( 0.00000000000 +        0.00000000000*t);\r
+   jupiter_x_2+=     0.00077685547 * cos( 5.29776154458 +      529.69096509460*t);\r
+   jupiter_x_2+=     0.00041410887 * cos( 5.12291589939 +     1059.38193018920*t);\r
+   jupiter_x_2+=     0.00011423070 * cos( 1.72917878238 +        7.11354700080*t);\r
+   jupiter_x_2=jupiter_x_2 * t * t;\r
+\r
+   double jupiter_x_3=0.0;\r
+   jupiter_x_3+=     0.00017071323 * cos( 5.86133022278 +      522.57741809380*t);\r
+   jupiter_x_3+=     0.00016713548 * cos( 4.77458794485 +      536.80451209540*t);\r
+   jupiter_x_3=jupiter_x_3 * t * t * t;\r
+\r
+   return jupiter_x_0+jupiter_x_1+jupiter_x_2+jupiter_x_3;\r
+}\r
+\r
+double vsop87a_micro_jupiter_y(double t){\r
+   double jupiter_y_0=0.0;\r
+   jupiter_y_0+=     5.19520046589 * cos( 5.31203162731 +      529.69096509460*t);\r
+   jupiter_y_0+=     0.12592862602 * cos( 5.66160227728 +     1059.38193018920*t);\r
+   jupiter_y_0+=     0.09363670616 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_y_0+=     0.01508275299 * cos( 5.43934968102 +      522.57741809380*t);\r
+   jupiter_y_0+=     0.01475809370 * cos( 2.04679566495 +      536.80451209540*t);\r
+   jupiter_y_0+=     0.00457750806 * cos( 6.01129093501 +     1589.07289528380*t);\r
+   jupiter_y_0+=     0.00300686679 * cos( 3.60948050740 +        7.11354700080*t);\r
+   jupiter_y_0+=     0.00378285578 * cos( 3.53006782383 +      103.09277421860*t);\r
+   jupiter_y_0+=     0.00192333128 * cos( 3.45690564771 +      426.59819087600*t);\r
+   jupiter_y_0+=     0.00146104656 * cos( 4.62267224431 +      110.20632121940*t);\r
+   jupiter_y_0+=     0.00139480058 * cos( 4.00075307706 +      632.78373931320*t);\r
+   jupiter_y_0+=     0.00132696764 * cos( 5.62184581859 +      213.29909543800*t);\r
+   jupiter_y_0+=     0.00101999807 * cos( 4.57594598884 +     1052.26838318840*t);\r
+   jupiter_y_0+=     0.00114043110 * cos( 4.72982262969 +     1162.47470440780*t);\r
+   jupiter_y_0+=     0.00072091178 * cos( 2.39048659148 +     1066.49547719000*t);\r
+   jupiter_y_0+=     0.00059051769 * cos( 2.89529070968 +      949.17560896980*t);\r
+   jupiter_y_0+=     0.00068374489 * cos( 1.86537074374 +      846.08283475120*t);\r
+   jupiter_y_0+=     0.00029807369 * cos( 4.52105772740 +      206.18554843720*t);\r
+   jupiter_y_0+=     0.00026933579 * cos( 3.86233956827 +      419.48464387520*t);\r
+   jupiter_y_0+=     0.00026619714 * cos( 3.28203174951 +      323.50541665740*t);\r
+   jupiter_y_0+=     0.00020873780 * cos( 3.79369881757 +      735.87651353180*t);\r
+   jupiter_y_0+=     0.00019727397 * cos( 0.07818534532 +     2118.76386037840*t);\r
+   jupiter_y_0+=     0.00018639846 * cos( 0.38751972138 +      316.39186965660*t);\r
+   jupiter_y_0+=     0.00016355726 * cos( 5.56997881604 +      515.46387109300*t);\r
+   jupiter_y_0+=     0.00014606858 * cos( 0.47759399145 +      742.99006053260*t);\r
+   jupiter_y_0+=     0.00011419853 * cos( 1.91089341468 +      543.91805909620*t);\r
+   jupiter_y_0+=     0.00012153427 * cos( 2.18151972499 +      525.75881183150*t);\r
+   jupiter_y_0+=     0.00011988875 * cos( 5.29687602089 +      533.62311835770*t);\r
+\r
+   double jupiter_y_1=0.0;\r
+   jupiter_y_1+=     0.01694798253 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_y_1+=     0.00634859798 * cos( 4.81903199650 +     1059.38193018920*t);\r
+   jupiter_y_1+=     0.00601160431 * cos( 0.85811249940 +      522.57741809380*t);\r
+   jupiter_y_1+=     0.00588928504 * cos( 0.34491576890 +      536.80451209540*t);\r
+   jupiter_y_1+=     0.00081187145 * cos( 1.90914316532 +        7.11354700080*t);\r
+   jupiter_y_1+=     0.00046888090 * cos( 1.91294535618 +      529.69096509460*t);\r
+   jupiter_y_1+=     0.00046194129 * cos( 5.16955994561 +     1589.07289528380*t);\r
+   jupiter_y_1+=     0.00032503453 * cos( 0.17640743623 +     1052.26838318840*t);\r
+   jupiter_y_1+=     0.00031231694 * cos( 0.77623645597 +     1066.49547719000*t);\r
+   jupiter_y_1+=     0.00019462096 * cos( 3.00957119470 +      110.20632121940*t);\r
+   jupiter_y_1+=     0.00017738615 * cos( 2.46531787101 +      426.59819087600*t);\r
+   jupiter_y_1+=     0.00013701692 * cos( 5.02070197804 +      632.78373931320*t);\r
+   jupiter_y_1+=     0.00013034616 * cos( 0.98979834442 +      515.46387109300*t);\r
+   jupiter_y_1=jupiter_y_1 * t;\r
+\r
+   double jupiter_y_2=0.0;\r
+   jupiter_y_2+=     0.00124032509 * cos( 2.56495576833 +      522.57741809380*t);\r
+   jupiter_y_2+=     0.00121455991 * cos( 4.92398766380 +      536.80451209540*t);\r
+   jupiter_y_2+=     0.00076523263 * cos( 3.75913371793 +      529.69096509460*t);\r
+   jupiter_y_2+=     0.00076943042 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_y_2+=     0.00041357600 * cos( 3.55228440457 +     1059.38193018920*t);\r
+   jupiter_y_2+=     0.00011277667 * cos( 0.18559902389 +        7.11354700080*t);\r
+   jupiter_y_2=jupiter_y_2 * t * t;\r
+\r
+   double jupiter_y_3=0.0;\r
+   jupiter_y_3+=     0.00017085516 * cos( 4.29096904063 +      522.57741809380*t);\r
+   jupiter_y_3+=     0.00016701353 * cos( 3.20365737109 +      536.80451209540*t);\r
+   jupiter_y_3=jupiter_y_3 * t * t * t;\r
+\r
+   return jupiter_y_0+jupiter_y_1+jupiter_y_2+jupiter_y_3;\r
+}\r
+\r
+double vsop87a_micro_jupiter_z(double t){\r
+   double jupiter_z_0=0.0;\r
+   jupiter_z_0+=     0.11823100489 * cos( 3.55844646343 +      529.69096509460*t);\r
+   jupiter_z_0+=     0.00859031952 * cos( 0.00000000000 +        0.00000000000*t);\r
+   jupiter_z_0+=     0.00286562094 * cos( 3.90812238338 +     1059.38193018920*t);\r
+   jupiter_z_0+=     0.00042388592 * cos( 3.60144191032 +      522.57741809380*t);\r
+   jupiter_z_0+=     0.00033295491 * cos( 0.30297050585 +      536.80451209540*t);\r
+   jupiter_z_0+=     0.00010416160 * cos( 4.25764593061 +     1589.07289528380*t);\r
+\r
+   double jupiter_z_1=0.0;\r
+   jupiter_z_1+=     0.00407072175 * cos( 1.52699353482 +      529.69096509460*t);\r
+   jupiter_z_1+=     0.00020307341 * cos( 2.59878269248 +     1059.38193018920*t);\r
+   jupiter_z_1+=     0.00014424953 * cos( 4.85400155025 +      536.80451209540*t);\r
+   jupiter_z_1+=     0.00015474611 * cos( 0.00000000000 +        0.00000000000*t);\r
+   jupiter_z_1+=     0.00012730364 * cos( 5.45536715732 +      522.57741809380*t);\r
+   jupiter_z_1=jupiter_z_1 * t;\r
+\r
+   double jupiter_z_2=0.0;\r
+   jupiter_z_2+=     0.00028635326 * cos( 3.01374166973 +      529.69096509460*t);\r
+   jupiter_z_2=jupiter_z_2 * t * t;\r
+\r
+   return jupiter_z_0+jupiter_z_1+jupiter_z_2;\r
+}\r
+\r
+double vsop87a_micro_mars_x(double t){\r
+   double mars_x_0=0.0;\r
+   mars_x_0+=     1.51769936383 * cos( 6.20403346548 +     3340.61242669980*t);\r
+   mars_x_0+=     0.19502945246 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_x_0+=     0.07070919655 * cos( 0.25870338558 +     6681.22485339960*t);\r
+   mars_x_0+=     0.00494196914 * cos( 0.59669127768 +    10021.83728009940*t);\r
+   mars_x_0+=     0.00040938237 * cos( 0.93473307419 +    13362.44970679920*t);\r
+   mars_x_0+=     0.00021067199 * cos( 1.80435656154 +     3337.08930835080*t);\r
+   mars_x_0+=     0.00021041626 * cos( 1.17895619474 +     3344.13554504880*t);\r
+   mars_x_0+=     0.00011370375 * cos( 4.83265211109 +     1059.38193018920*t);\r
+   mars_x_0+=     0.00013527976 * cos( 0.63010765169 +      529.69096509460*t);\r
+\r
+   double mars_x_1=0.0;\r
+   mars_x_1+=     0.00861441374 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_x_1+=     0.00552437949 * cos( 5.09565872891 +     6681.22485339960*t);\r
+   mars_x_1+=     0.00077184977 * cos( 5.43315636209 +    10021.83728009940*t);\r
+   mars_x_1+=     0.00020467294 * cos( 5.57051812369 +     3340.61242669980*t);\r
+   mars_x_1=mars_x_1 * t;\r
+\r
+   double mars_x_2=0.0;\r
+   mars_x_2+=     0.00056323939 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mars_x_2+=     0.00022122528 * cos( 3.54372113272 +     6681.22485339960*t);\r
+   mars_x_2=mars_x_2 * t * t;\r
+\r
+   return mars_x_0+mars_x_1+mars_x_2;\r
+}\r
+\r
+double vsop87a_micro_mars_y(double t){\r
+   double mars_y_0=0.0;\r
+   mars_y_0+=     1.51558976277 * cos( 4.63212206588 +     3340.61242669980*t);\r
+   mars_y_0+=     0.07064550239 * cos( 4.97051892902 +     6681.22485339960*t);\r
+   mars_y_0+=     0.08655481102 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mars_y_0+=     0.00493872848 * cos( 5.30877806694 +    10021.83728009940*t);\r
+   mars_y_0+=     0.00040917422 * cos( 5.64698263703 +    13362.44970679920*t);\r
+   mars_y_0+=     0.00021036784 * cos( 0.23240270955 +     3337.08930835080*t);\r
+   mars_y_0+=     0.00021012921 * cos( 5.89022773653 +     3344.13554504880*t);\r
+   mars_y_0+=     0.00011370034 * cos( 3.26131408801 +     1059.38193018920*t);\r
+   mars_y_0+=     0.00013324177 * cos( 5.34259389724 +      529.69096509460*t);\r
+\r
+   double mars_y_1=0.0;\r
+   mars_y_1+=     0.01427324210 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_y_1+=     0.00551063753 * cos( 3.52128320402 +     6681.22485339960*t);\r
+   mars_y_1+=     0.00077091913 * cos( 3.86082685753 +    10021.83728009940*t);\r
+   mars_y_1+=     0.00037310491 * cos( 1.16016958445 +     3340.61242669980*t);\r
+   mars_y_1=mars_y_1 * t;\r
+\r
+   double mars_y_2=0.0;\r
+   mars_y_2+=     0.00035396765 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_y_2+=     0.00021950759 * cos( 1.96291594946 +     6681.22485339960*t);\r
+   mars_y_2=mars_y_2 * t * t;\r
+\r
+   return mars_y_0+mars_y_1+mars_y_2;\r
+}\r
+\r
+double vsop87a_micro_mars_z(double t){\r
+   double mars_z_0=0.0;\r
+   mars_z_0+=     0.04901207220 * cos( 3.76712324286 +     3340.61242669980*t);\r
+   mars_z_0+=     0.00660669541 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mars_z_0+=     0.00228333904 * cos( 4.10544022266 +     6681.22485339960*t);\r
+   mars_z_0+=     0.00015958402 * cos( 4.44367058261 +    10021.83728009940*t);\r
+\r
+   double mars_z_1=0.0;\r
+   mars_z_1+=     0.00331842958 * cos( 6.05027773492 +     3340.61242669980*t);\r
+   mars_z_1+=     0.00047930411 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_z_1=mars_z_1 * t;\r
+\r
+   double mars_z_2=0.0;\r
+   mars_z_2+=     0.00013705360 * cos( 1.04212852598 +     3340.61242669980*t);\r
+   mars_z_2=mars_z_2 * t * t;\r
+\r
+   return mars_z_0+mars_z_1+mars_z_2;\r
+}\r
+\r
+double vsop87a_micro_mercury_x(double t){\r
+   double mercury_x_0=0.0;\r
+   mercury_x_0+=     0.37546291728 * cos( 4.39651506942 +    26087.90314157420*t);\r
+   mercury_x_0+=     0.03825746672 * cos( 1.16485604339 +    52175.80628314840*t);\r
+   mercury_x_0+=     0.02625615963 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_x_0+=     0.00584261333 * cos( 4.21599394757 +    78263.70942472259*t);\r
+   mercury_x_0+=     0.00105716695 * cos( 0.98379033182 +   104351.61256629678*t);\r
+   mercury_x_0+=     0.00021011730 * cos( 4.03469353923 +   130439.51570787099*t);\r
+\r
+   double mercury_x_1=0.0;\r
+   mercury_x_1+=     0.00318848034 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mercury_x_1+=     0.00105289019 * cos( 5.91600475006 +    52175.80628314840*t);\r
+   mercury_x_1+=     0.00032316001 * cos( 2.68247273347 +    78263.70942472259*t);\r
+   mercury_x_1+=     0.00011992889 * cos( 5.81575112963 +    26087.90314157420*t);\r
+   mercury_x_1=mercury_x_1 * t;\r
+\r
+   return mercury_x_0+mercury_x_1;\r
+}\r
+\r
+double vsop87a_micro_mercury_y(double t){\r
+   double mercury_y_0=0.0;\r
+   mercury_y_0+=     0.37953642888 * cos( 2.83780617820 +    26087.90314157420*t);\r
+   mercury_y_0+=     0.11626131831 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_y_0+=     0.03854668215 * cos( 5.88780608966 +    52175.80628314840*t);\r
+   mercury_y_0+=     0.00587711268 * cos( 2.65498896201 +    78263.70942472259*t);\r
+   mercury_y_0+=     0.00106235493 * cos( 5.70550616735 +   104351.61256629678*t);\r
+   mercury_y_0+=     0.00021100828 * cos( 2.47291315849 +   130439.51570787099*t);\r
+\r
+   double mercury_y_1=0.0;\r
+   mercury_y_1+=     0.00107803852 * cos( 4.34964793883 +    52175.80628314840*t);\r
+   mercury_y_1+=     0.00080651544 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_y_1+=     0.00032715354 * cos( 1.11763734425 +    78263.70942472259*t);\r
+   mercury_y_1+=     0.00011914709 * cos( 1.22139986340 +    26087.90314157420*t);\r
+   mercury_y_1=mercury_y_1 * t;\r
+\r
+   return mercury_y_0+mercury_y_1;\r
+}\r
+\r
+double vsop87a_micro_mercury_z(double t){\r
+   double mercury_z_0=0.0;\r
+   mercury_z_0+=     0.04607665326 * cos( 1.99295081967 +    26087.90314157420*t);\r
+   mercury_z_0+=     0.00708734365 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_z_0+=     0.00469171617 * cos( 5.04215742764 +    52175.80628314840*t);\r
+   mercury_z_0+=     0.00071626395 * cos( 1.80894256071 +    78263.70942472259*t);\r
+   mercury_z_0+=     0.00012957446 * cos( 4.85922032010 +   104351.61256629678*t);\r
+\r
+   double mercury_z_1=0.0;\r
+   mercury_z_1+=     0.00108722177 * cos( 3.91134750825 +    26087.90314157420*t);\r
+   mercury_z_1+=     0.00057826621 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_z_1=mercury_z_1 * t;\r
+\r
+   return mercury_z_0+mercury_z_1;\r
+}\r
+\r
+double vsop87a_micro_neptune_x(double t){\r
+   double neptune_x_0=0.0;\r
+   neptune_x_0+=    30.05890004476 * cos( 5.31211340029 +       38.13303563780*t);\r
+   neptune_x_0+=     0.27080164222 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_x_0+=     0.13505661755 * cos( 3.50078975634 +       76.26607127560*t);\r
+   neptune_x_0+=     0.15726094556 * cos( 0.11319072675 +       36.64856292950*t);\r
+   neptune_x_0+=     0.14935120126 * cos( 1.08499403018 +       39.61750834610*t);\r
+   neptune_x_0+=     0.02597313814 * cos( 1.99590301412 +        1.48447270830*t);\r
+   neptune_x_0+=     0.01074040708 * cos( 5.38502938672 +       74.78159856730*t);\r
+   neptune_x_0+=     0.00823793287 * cos( 1.43221581862 +       35.16409022120*t);\r
+   neptune_x_0+=     0.00817588813 * cos( 0.78180174031 +        2.96894541660*t);\r
+   neptune_x_0+=     0.00565534918 * cos( 5.98964907613 +       41.10198105440*t);\r
+   neptune_x_0+=     0.00495719107 * cos( 0.59948143567 +      529.69096509460*t);\r
+   neptune_x_0+=     0.00307525907 * cos( 0.40023311011 +       73.29712585900*t);\r
+   neptune_x_0+=     0.00272253551 * cos( 0.87443494387 +      213.29909543800*t);\r
+   neptune_x_0+=     0.00135887219 * cos( 5.54676577816 +       77.75054398390*t);\r
+   neptune_x_0+=     0.00090965704 * cos( 1.68910246115 +      114.39910691340*t);\r
+   neptune_x_0+=     0.00069040539 * cos( 5.83469123520 +        4.45341812490*t);\r
+   neptune_x_0+=     0.00060813556 * cos( 2.62589958380 +       33.67961751290*t);\r
+   neptune_x_0+=     0.00054690827 * cos( 1.55799996661 +       71.81265315070*t);\r
+   neptune_x_0+=     0.00028889260 * cos( 4.78966826027 +       42.58645376270*t);\r
+   neptune_x_0+=     0.00012614732 * cos( 3.57002516434 +      112.91463420510*t);\r
+   neptune_x_0+=     0.00012749153 * cos( 2.73719269645 +      111.43016149680*t);\r
+   neptune_x_0+=     0.00012013994 * cos( 0.94912933496 +     1059.38193018920*t);\r
+\r
+   double neptune_x_1=0.0;\r
+   neptune_x_1+=     0.00255840261 * cos( 2.01935686795 +       36.64856292950*t);\r
+   neptune_x_1+=     0.00243125299 * cos( 5.46214902873 +       39.61750834610*t);\r
+   neptune_x_1+=     0.00118398168 * cos( 2.88251845061 +       76.26607127560*t);\r
+   neptune_x_1+=     0.00037965449 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_x_1+=     0.00021924705 * cos( 3.20156164152 +       35.16409022120*t);\r
+   neptune_x_1+=     0.00017459808 * cos( 4.26349398817 +       41.10198105440*t);\r
+   neptune_x_1+=     0.00013130617 * cos( 5.36424961848 +        2.96894541660*t);\r
+   neptune_x_1=neptune_x_1 * t;\r
+\r
+   return neptune_x_0+neptune_x_1;\r
+}\r
+\r
+double vsop87a_micro_neptune_y(double t){\r
+   double neptune_y_0=0.0;\r
+   neptune_y_0+=    30.06056351665 * cos( 3.74086294714 +       38.13303563780*t);\r
+   neptune_y_0+=     0.30205857683 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_y_0+=     0.13506391797 * cos( 1.92953034883 +       76.26607127560*t);\r
+   neptune_y_0+=     0.15706589373 * cos( 4.82539970129 +       36.64856292950*t);\r
+   neptune_y_0+=     0.14936165806 * cos( 5.79694900665 +       39.61750834610*t);\r
+   neptune_y_0+=     0.02584250749 * cos( 0.42549700754 +        1.48447270830*t);\r
+   neptune_y_0+=     0.01073739772 * cos( 3.81371728533 +       74.78159856730*t);\r
+   neptune_y_0+=     0.00815187583 * cos( 5.49429775826 +        2.96894541660*t);\r
+   neptune_y_0+=     0.00582199295 * cos( 6.19633718936 +       35.16409022120*t);\r
+   neptune_y_0+=     0.00565576412 * cos( 4.41843009015 +       41.10198105440*t);\r
+   neptune_y_0+=     0.00495581047 * cos( 5.31205825784 +      529.69096509460*t);\r
+   neptune_y_0+=     0.00304525203 * cos( 5.11048113661 +       73.29712585900*t);\r
+   neptune_y_0+=     0.00272640298 * cos( 5.58603690785 +      213.29909543800*t);\r
+   neptune_y_0+=     0.00135897385 * cos( 3.97553750964 +       77.75054398390*t);\r
+   neptune_y_0+=     0.00090970871 * cos( 0.11783619888 +      114.39910691340*t);\r
+   neptune_y_0+=     0.00068790261 * cos( 4.26391997151 +        4.45341812490*t);\r
+   neptune_y_0+=     0.00028893355 * cos( 3.21848975032 +       42.58645376270*t);\r
+   neptune_y_0+=     0.00020081559 * cos( 1.19787916085 +       33.67961751290*t);\r
+   neptune_y_0+=     0.00012613583 * cos( 1.99777332934 +      112.91463420510*t);\r
+   neptune_y_0+=     0.00012828708 * cos( 1.16740053443 +      111.43016149680*t);\r
+   neptune_y_0+=     0.00012012961 * cos( 5.66157563804 +     1059.38193018920*t);\r
+\r
+   double neptune_y_1=0.0;\r
+   neptune_y_1+=     0.00352947493 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_y_1+=     0.00256125493 * cos( 0.44757496817 +       36.64856292950*t);\r
+   neptune_y_1+=     0.00243147725 * cos( 3.89099798696 +       39.61750834610*t);\r
+   neptune_y_1+=     0.00118427205 * cos( 1.31128027037 +       76.26607127560*t);\r
+   neptune_y_1+=     0.00021936702 * cos( 1.63124087591 +       35.16409022120*t);\r
+   neptune_y_1+=     0.00017462332 * cos( 2.69229902966 +       41.10198105440*t);\r
+   neptune_y_1+=     0.00012992380 * cos( 3.79578633002 +        2.96894541660*t);\r
+   neptune_y_1=neptune_y_1 * t;\r
+\r
+   return neptune_y_0+neptune_y_1;\r
+}\r
+\r
+double vsop87a_micro_neptune_z(double t){\r
+   double neptune_z_0=0.0;\r
+   neptune_z_0+=     0.92866054405 * cos( 1.44103930278 +       38.13303563780*t);\r
+   neptune_z_0+=     0.01245978462 * cos( 0.00000000000 +        0.00000000000*t);\r
+   neptune_z_0+=     0.00474333567 * cos( 2.52218774238 +       36.64856292950*t);\r
+   neptune_z_0+=     0.00451987936 * cos( 3.50949720541 +       39.61750834610*t);\r
+   neptune_z_0+=     0.00417558068 * cos( 5.91310695421 +       76.26607127560*t);\r
+   neptune_z_0+=     0.00084104329 * cos( 4.38928900096 +        1.48447270830*t);\r
+   neptune_z_0+=     0.00032704958 * cos( 1.52048692001 +       74.78159856730*t);\r
+   neptune_z_0+=     0.00030873335 * cos( 3.29017611456 +       35.16409022120*t);\r
+   neptune_z_0+=     0.00025812584 * cos( 3.19303128782 +        2.96894541660*t);\r
+   neptune_z_0+=     0.00016865319 * cos( 2.13251104425 +       41.10198105440*t);\r
+   neptune_z_0+=     0.00011789909 * cos( 3.60001877675 +      213.29909543800*t);\r
+   neptune_z_0+=     0.00011279680 * cos( 3.55816676334 +      529.69096509460*t);\r
+\r
+   double neptune_z_1=0.0;\r
+   neptune_z_1+=     0.00154885971 * cos( 2.14239039664 +       38.13303563780*t);\r
+   neptune_z_1=neptune_z_1 * t;\r
+\r
+   return neptune_z_0+neptune_z_1;\r
+}\r
+\r
+double vsop87a_micro_saturn_x(double t){\r
+   double saturn_x_0=0.0;\r
+   saturn_x_0+=     9.51638335797 * cos( 0.87441380794 +      213.29909543800*t);\r
+   saturn_x_0+=     0.26412374238 * cos( 0.12390892620 +      426.59819087600*t);\r
+   saturn_x_0+=     0.06760430339 * cos( 4.16767145778 +      206.18554843720*t);\r
+   saturn_x_0+=     0.06624260115 * cos( 0.75094737780 +      220.41264243880*t);\r
+   saturn_x_0+=     0.04244797817 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_x_0+=     0.02336340488 * cos( 2.02227784673 +        7.11354700080*t);\r
+   saturn_x_0+=     0.01255372247 * cos( 2.17338917731 +      110.20632121940*t);\r
+   saturn_x_0+=     0.01115684467 * cos( 3.15686878377 +      419.48464387520*t);\r
+   saturn_x_0+=     0.01097683232 * cos( 5.65753337256 +      639.89728631400*t);\r
+   saturn_x_0+=     0.00716328481 * cos( 2.71149993708 +      316.39186965660*t);\r
+   saturn_x_0+=     0.00509313365 * cos( 4.95865624780 +      103.09277421860*t);\r
+   saturn_x_0+=     0.00433994439 * cos( 0.72012820974 +      529.69096509460*t);\r
+   saturn_x_0+=     0.00372894461 * cos( 0.00137195497 +      433.71173787680*t);\r
+   saturn_x_0+=     0.00097843523 * cos( 1.01485750417 +      323.50541665740*t);\r
+   saturn_x_0+=     0.00080600536 * cos( 5.62103979796 +       11.04570026390*t);\r
+   saturn_x_0+=     0.00083782316 * cos( 0.62038893702 +      227.52618943960*t);\r
+   saturn_x_0+=     0.00074150224 * cos( 2.38206066655 +      632.78373931320*t);\r
+   saturn_x_0+=     0.00070219382 * cos( 0.88789752415 +      209.36694217490*t);\r
+   saturn_x_0+=     0.00068855792 * cos( 4.01788097627 +      217.23124870110*t);\r
+   saturn_x_0+=     0.00065620467 * cos( 2.69728593339 +      202.25339517410*t);\r
+   saturn_x_0+=     0.00058297911 * cos( 2.16155251399 +      224.34479570190*t);\r
+   saturn_x_0+=     0.00054022837 * cos( 4.90928184374 +      853.19638175200*t);\r
+   saturn_x_0+=     0.00045550446 * cos( 1.88235037830 +       14.22709400160*t);\r
+   saturn_x_0+=     0.00038345667 * cos( 4.39815501478 +      199.07200143640*t);\r
+   saturn_x_0+=     0.00044551703 * cos( 5.60763553535 +       63.73589830340*t);\r
+   saturn_x_0+=     0.00025165185 * cos( 0.37800582257 +      216.48048917570*t);\r
+   saturn_x_0+=     0.00024554499 * cos( 4.53150598095 +      210.11770170030*t);\r
+   saturn_x_0+=     0.00024673219 * cos( 5.90891573850 +      522.57741809380*t);\r
+   saturn_x_0+=     0.00024677050 * cos( 5.60389382420 +      415.55249061210*t);\r
+   saturn_x_0+=     0.00025491374 * cos( 1.63922423181 +      117.31986822020*t);\r
+   saturn_x_0+=     0.00031253049 * cos( 4.62976601833 +      735.87651353180*t);\r
+   saturn_x_0+=     0.00023372467 * cos( 5.53491987276 +      647.01083331480*t);\r
+   saturn_x_0+=     0.00023355468 * cos( 0.18791490124 +      149.56319713460*t);\r
+   saturn_x_0+=     0.00024805815 * cos( 5.50327676733 +       74.78159856730*t);\r
+   saturn_x_0+=     0.00014731703 * cos( 4.67981909838 +      277.03499374140*t);\r
+   saturn_x_0+=     0.00012427525 * cos( 1.02995545746 +     1059.38193018920*t);\r
+   saturn_x_0+=     0.00012393514 * cos( 4.19747622821 +      490.33408917940*t);\r
+   saturn_x_0+=     0.00012026472 * cos( 5.66372282839 +      351.81659230870*t);\r
+\r
+   double saturn_x_1=0.0;\r
+   saturn_x_1+=     0.07575103962 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_x_1+=     0.03085041716 * cos( 4.27565749128 +      426.59819087600*t);\r
+   saturn_x_1+=     0.02714918399 * cos( 5.85229412397 +      206.18554843720*t);\r
+   saturn_x_1+=     0.02643100909 * cos( 5.33291950584 +      220.41264243880*t);\r
+   saturn_x_1+=     0.00627104520 * cos( 0.32898307969 +        7.11354700080*t);\r
+   saturn_x_1+=     0.00256560953 * cos( 3.52478934343 +      639.89728631400*t);\r
+   saturn_x_1+=     0.00312356512 * cos( 4.83001724941 +      419.48464387520*t);\r
+   saturn_x_1+=     0.00189196274 * cos( 4.48642453552 +      433.71173787680*t);\r
+   saturn_x_1+=     0.00203646570 * cos( 1.10998681782 +      213.29909543800*t);\r
+   saturn_x_1+=     0.00119531145 * cos( 1.14735096078 +      110.20632121940*t);\r
+   saturn_x_1+=     0.00066764238 * cos( 3.72346596928 +      316.39186965660*t);\r
+   saturn_x_1+=     0.00066901225 * cos( 5.20257500380 +      227.52618943960*t);\r
+   saturn_x_1+=     0.00031000840 * cos( 6.06067919437 +      199.07200143640*t);\r
+   saturn_x_1+=     0.00030418100 * cos( 0.18746903351 +       14.22709400160*t);\r
+   saturn_x_1+=     0.00022275210 * cos( 6.19530878014 +      103.09277421860*t);\r
+   saturn_x_1+=     0.00018939377 * cos( 2.77618306725 +      853.19638175200*t);\r
+   saturn_x_1+=     0.00018093009 * cos( 5.09162723865 +      209.36694217490*t);\r
+   saturn_x_1+=     0.00017777854 * cos( 6.10381593351 +      217.23124870110*t);\r
+   saturn_x_1+=     0.00016296201 * cos( 4.86945681437 +      216.48048917570*t);\r
+   saturn_x_1+=     0.00017120250 * cos( 4.59611664188 +      632.78373931320*t);\r
+   saturn_x_1+=     0.00015894491 * cos( 0.03653502304 +      210.11770170030*t);\r
+   saturn_x_1+=     0.00016192653 * cos( 5.60798014450 +      323.50541665740*t);\r
+   saturn_x_1+=     0.00014466010 * cos( 3.67449380090 +      647.01083331480*t);\r
+   saturn_x_1+=     0.00011061528 * cos( 0.03163071461 +      117.31986822020*t);\r
+   saturn_x_1=saturn_x_1 * t;\r
+\r
+   double saturn_x_2=0.0;\r
+   saturn_x_2+=     0.00560746334 * cos( 1.26401632282 +      206.18554843720*t);\r
+   saturn_x_2+=     0.00545834518 * cos( 3.62343709657 +      220.41264243880*t);\r
+   saturn_x_2+=     0.00443342186 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_x_2+=     0.00336109713 * cos( 2.42547432460 +      213.29909543800*t);\r
+   saturn_x_2+=     0.00224302269 * cos( 2.49151203519 +      426.59819087600*t);\r
+   saturn_x_2+=     0.00087170924 * cos( 4.89048951691 +        7.11354700080*t);\r
+   saturn_x_2+=     0.00050028094 * cos( 2.70119046081 +      433.71173787680*t);\r
+   saturn_x_2+=     0.00045122590 * cos( 0.36735068943 +      419.48464387520*t);\r
+   saturn_x_2+=     0.00032847824 * cos( 1.59210153669 +      639.89728631400*t);\r
+   saturn_x_2+=     0.00027153555 * cos( 3.49804002218 +      227.52618943960*t);\r
+   saturn_x_2+=     0.00012676167 * cos( 1.45465729530 +      199.07200143640*t);\r
+   saturn_x_2+=     0.00010330738 * cos( 4.76949531290 +       14.22709400160*t);\r
+   saturn_x_2=saturn_x_2 * t * t;\r
+\r
+   double saturn_x_3=0.0;\r
+   saturn_x_3+=     0.00077115952 * cos( 2.97714385362 +      206.18554843720*t);\r
+   saturn_x_3+=     0.00075340436 * cos( 1.89208005248 +      220.41264243880*t);\r
+   saturn_x_3+=     0.00018450895 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_x_3+=     0.00010527244 * cos( 0.66368256891 +      426.59819087600*t);\r
+   saturn_x_3=saturn_x_3 * t * t * t;\r
+\r
+   return saturn_x_0+saturn_x_1+saturn_x_2+saturn_x_3;\r
+}\r
+\r
+double vsop87a_micro_saturn_y(double t){\r
+   double saturn_y_0=0.0;\r
+   saturn_y_0+=     9.52986882699 * cos( 5.58600556665 +      213.29909543800*t);\r
+   saturn_y_0+=     0.79387988806 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_y_0+=     0.26441781302 * cos( 4.83528061849 +      426.59819087600*t);\r
+   saturn_y_0+=     0.06916653915 * cos( 2.55279408706 +      206.18554843720*t);\r
+   saturn_y_0+=     0.06633570703 * cos( 5.46258848288 +      220.41264243880*t);\r
+   saturn_y_0+=     0.02345609742 * cos( 0.44652132519 +        7.11354700080*t);\r
+   saturn_y_0+=     0.01183874652 * cos( 1.34638298371 +      419.48464387520*t);\r
+   saturn_y_0+=     0.01245790434 * cos( 0.60367177975 +      110.20632121940*t);\r
+   saturn_y_0+=     0.01098751131 * cos( 4.08608782813 +      639.89728631400*t);\r
+   saturn_y_0+=     0.00700849336 * cos( 1.13611298025 +      316.39186965660*t);\r
+   saturn_y_0+=     0.00434466176 * cos( 5.42474696262 +      529.69096509460*t);\r
+   saturn_y_0+=     0.00373327342 * cos( 4.71308726958 +      433.71173787680*t);\r
+   saturn_y_0+=     0.00335162363 * cos( 0.66422253983 +      103.09277421860*t);\r
+   saturn_y_0+=     0.00097837745 * cos( 5.72844290173 +      323.50541665740*t);\r
+   saturn_y_0+=     0.00080571808 * cos( 4.05295449910 +       11.04570026390*t);\r
+   saturn_y_0+=     0.00083899691 * cos( 5.33204070267 +      227.52618943960*t);\r
+   saturn_y_0+=     0.00070158491 * cos( 5.59777963629 +      209.36694217490*t);\r
+   saturn_y_0+=     0.00065937657 * cos( 1.25969608208 +      202.25339517410*t);\r
+   saturn_y_0+=     0.00070957225 * cos( 0.88888207567 +      632.78373931320*t);\r
+   saturn_y_0+=     0.00068985859 * cos( 2.44460312617 +      217.23124870110*t);\r
+   saturn_y_0+=     0.00058382264 * cos( 0.58978766922 +      224.34479570190*t);\r
+   saturn_y_0+=     0.00054049836 * cos( 3.33757904879 +      853.19638175200*t);\r
+   saturn_y_0+=     0.00045790930 * cos( 0.30331527632 +       14.22709400160*t);\r
+   saturn_y_0+=     0.00041976402 * cos( 2.62591355948 +      199.07200143640*t);\r
+   saturn_y_0+=     0.00044697175 * cos( 0.90661238256 +       63.73589830340*t);\r
+   saturn_y_0+=     0.00025199575 * cos( 5.08963506006 +      216.48048917570*t);\r
+   saturn_y_0+=     0.00024640836 * cos( 2.95445247282 +      210.11770170030*t);\r
+   saturn_y_0+=     0.00024835151 * cos( 4.02630190571 +      415.55249061210*t);\r
+   saturn_y_0+=     0.00025545907 * cos( 0.06626229252 +      117.31986822020*t);\r
+   saturn_y_0+=     0.00029666833 * cos( 6.09910638345 +      735.87651353180*t);\r
+   saturn_y_0+=     0.00023396742 * cos( 3.96337393635 +      647.01083331480*t);\r
+   saturn_y_0+=     0.00023380691 * cos( 4.90051072276 +      149.56319713460*t);\r
+   saturn_y_0+=     0.00020272215 * cos( 2.34319548198 +      309.27832265580*t);\r
+   saturn_y_0+=     0.00020099552 * cos( 0.98365186365 +      522.57741809380*t);\r
+   saturn_y_0+=     0.00024827950 * cos( 3.92681428900 +       74.78159856730*t);\r
+   saturn_y_0+=     0.00015383927 * cos( 3.10227822627 +      277.03499374140*t);\r
+   saturn_y_0+=     0.00011629210 * cos( 5.74108283772 +     1059.38193018920*t);\r
+   saturn_y_0+=     0.00012422966 * cos( 2.62557865743 +      490.33408917940*t);\r
+   saturn_y_0+=     0.00012048048 * cos( 4.09265980116 +      351.81659230870*t);\r
+\r
+   double saturn_y_1=0.0;\r
+   saturn_y_1+=     0.05373889135 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_y_1+=     0.03090575152 * cos( 2.70346890906 +      426.59819087600*t);\r
+   saturn_y_1+=     0.02741594312 * cos( 4.26667636015 +      206.18554843720*t);\r
+   saturn_y_1+=     0.02647489677 * cos( 3.76132298889 +      220.41264243880*t);\r
+   saturn_y_1+=     0.00631520527 * cos( 5.03245505280 +        7.11354700080*t);\r
+   saturn_y_1+=     0.00256799701 * cos( 1.95351819758 +      639.89728631400*t);\r
+   saturn_y_1+=     0.00312271930 * cos( 3.25850205023 +      419.48464387520*t);\r
+   saturn_y_1+=     0.00189433319 * cos( 2.91501840819 +      433.71173787680*t);\r
+   saturn_y_1+=     0.00164133553 * cos( 5.29239290066 +      213.29909543800*t);\r
+   saturn_y_1+=     0.00116791227 * cos( 5.89146675760 +      110.20632121940*t);\r
+   saturn_y_1+=     0.00067210919 * cos( 2.17042636344 +      316.39186965660*t);\r
+   saturn_y_1+=     0.00067003292 * cos( 3.63101075514 +      227.52618943960*t);\r
+   saturn_y_1+=     0.00033002406 * cos( 4.35527405801 +      199.07200143640*t);\r
+   saturn_y_1+=     0.00030628998 * cos( 4.88861760772 +       14.22709400160*t);\r
+   saturn_y_1+=     0.00022234714 * cos( 4.62212779231 +      103.09277421860*t);\r
+   saturn_y_1+=     0.00018945004 * cos( 1.20412493845 +      853.19638175200*t);\r
+   saturn_y_1+=     0.00018079959 * cos( 3.51566153251 +      209.36694217490*t);\r
+   saturn_y_1+=     0.00017791543 * cos( 4.53214140649 +      217.23124870110*t);\r
+   saturn_y_1+=     0.00016320701 * cos( 3.29784030970 +      216.48048917570*t);\r
+   saturn_y_1+=     0.00015944258 * cos( 4.74503265169 +      210.11770170030*t);\r
+   saturn_y_1+=     0.00016717122 * cos( 3.00270792752 +      632.78373931320*t);\r
+   saturn_y_1+=     0.00016149947 * cos( 4.04186432517 +      323.50541665740*t);\r
+   saturn_y_1+=     0.00014481431 * cos( 2.10298298650 +      647.01083331480*t);\r
+   saturn_y_1+=     0.00011084040 * cos( 4.74073871754 +      117.31986822020*t);\r
+   saturn_y_1=saturn_y_1 * t;\r
+\r
+   double saturn_y_2=0.0;\r
+   saturn_y_2+=     0.00563706537 * cos( 5.97115878242 +      206.18554843720*t);\r
+   saturn_y_2+=     0.00547012116 * cos( 2.05154973426 +      220.41264243880*t);\r
+   saturn_y_2+=     0.00458518613 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_y_2+=     0.00362294249 * cos( 0.89540100509 +      213.29909543800*t);\r
+   saturn_y_2+=     0.00225521642 * cos( 0.91699821445 +      426.59819087600*t);\r
+   saturn_y_2+=     0.00088390611 * cos( 3.30289449917 +        7.11354700080*t);\r
+   saturn_y_2+=     0.00050101314 * cos( 1.12976163835 +      433.71173787680*t);\r
+   saturn_y_2+=     0.00045516403 * cos( 5.07669466539 +      419.48464387520*t);\r
+   saturn_y_2+=     0.00032896745 * cos( 0.02089057938 +      639.89728631400*t);\r
+   saturn_y_2+=     0.00027199743 * cos( 1.92638417640 +      227.52618943960*t);\r
+   saturn_y_2+=     0.00013251505 * cos( 6.07693099404 +      199.07200143640*t);\r
+   saturn_y_2+=     0.00010425984 * cos( 3.18246869028 +       14.22709400160*t);\r
+   saturn_y_2=saturn_y_2 * t * t;\r
+\r
+   double saturn_y_3=0.0;\r
+   saturn_y_3+=     0.00077376615 * cos( 1.40391048961 +      206.18554843720*t);\r
+   saturn_y_3+=     0.00075564351 * cos( 0.31962896379 +      220.41264243880*t);\r
+   saturn_y_3+=     0.00022843837 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_y_3+=     0.00010672263 * cos( 5.36495663820 +      426.59819087600*t);\r
+   saturn_y_3=saturn_y_3 * t * t * t;\r
+\r
+   return saturn_y_0+saturn_y_1+saturn_y_2+saturn_y_3;\r
+}\r
+\r
+double vsop87a_micro_saturn_z(double t){\r
+   double saturn_z_0=0.0;\r
+   saturn_z_0+=     0.41356950940 * cos( 3.60234142982 +      213.29909543800*t);\r
+   saturn_z_0+=     0.01148283576 * cos( 2.85128367469 +      426.59819087600*t);\r
+   saturn_z_0+=     0.01214249867 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_z_0+=     0.00329280791 * cos( 0.57121407104 +      206.18554843720*t);\r
+   saturn_z_0+=     0.00286934048 * cos( 3.48073526693 +      220.41264243880*t);\r
+   saturn_z_0+=     0.00099076584 * cos( 4.73369511264 +        7.11354700080*t);\r
+   saturn_z_0+=     0.00057361820 * cos( 4.92611225093 +      110.20632121940*t);\r
+   saturn_z_0+=     0.00047738127 * cos( 2.10039779728 +      639.89728631400*t);\r
+   saturn_z_0+=     0.00043458803 * cos( 5.84904978051 +      419.48464387520*t);\r
+   saturn_z_0+=     0.00034565673 * cos( 5.42614229590 +      316.39186965660*t);\r
+   saturn_z_0+=     0.00016185391 * cos( 2.72987173675 +      433.71173787680*t);\r
+   saturn_z_0+=     0.00011433574 * cos( 3.71662021072 +      529.69096509460*t);\r
+\r
+   double saturn_z_1=0.0;\r
+   saturn_z_1+=     0.01906503283 * cos( 4.94544746116 +      213.29909543800*t);\r
+   saturn_z_1+=     0.00528301265 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_z_1+=     0.00130262284 * cos( 2.26140980879 +      206.18554843720*t);\r
+   saturn_z_1+=     0.00101466332 * cos( 1.79095829545 +      220.41264243880*t);\r
+   saturn_z_1+=     0.00085947578 * cos( 0.51612788497 +      426.59819087600*t);\r
+   saturn_z_1+=     0.00022257446 * cos( 3.07684015656 +        7.11354700080*t);\r
+   saturn_z_1+=     0.00016179946 * cos( 1.19987517506 +      419.48464387520*t);\r
+   saturn_z_1=saturn_z_1 * t;\r
+\r
+   double saturn_z_2=0.0;\r
+   saturn_z_2+=     0.00131275155 * cos( 0.08868998101 +      213.29909543800*t);\r
+   saturn_z_2+=     0.00030147649 * cos( 3.91396203887 +      206.18554843720*t);\r
+   saturn_z_2+=     0.00019322173 * cos( 0.09228748624 +      220.41264243880*t);\r
+   saturn_z_2=saturn_z_2 * t * t;\r
+\r
+   return saturn_z_0+saturn_z_1+saturn_z_2;\r
+}\r
+\r
+double vsop87a_micro_uranus_x(double t){\r
+   double uranus_x_0=0.0;\r
+   uranus_x_0+=    19.17370730359 * cos( 5.48133416489 +       74.78159856730*t);\r
+   uranus_x_0+=     1.32272523872 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_x_0+=     0.44402496796 * cos( 1.65967519586 +      149.56319713460*t);\r
+   uranus_x_0+=     0.14668209481 * cos( 3.42395862804 +       73.29712585900*t);\r
+   uranus_x_0+=     0.14130269479 * cos( 4.39572927934 +       76.26607127560*t);\r
+   uranus_x_0+=     0.06201106178 * cos( 5.14043574125 +        1.48447270830*t);\r
+   uranus_x_0+=     0.01542951343 * cos( 4.12121838072 +      224.34479570190*t);\r
+   uranus_x_0+=     0.01444216660 * cos( 2.65117115201 +      148.07872442630*t);\r
+   uranus_x_0+=     0.00944995563 * cos( 1.65869338757 +       11.04570026390*t);\r
+   uranus_x_0+=     0.00657524815 * cos( 0.57595170636 +      151.04766984290*t);\r
+   uranus_x_0+=     0.00621624676 * cos( 3.05882246638 +       77.75054398390*t);\r
+   uranus_x_0+=     0.00585182542 * cos( 4.79934779678 +       71.81265315070*t);\r
+   uranus_x_0+=     0.00634000270 * cos( 4.09556589724 +       63.73589830340*t);\r
+   uranus_x_0+=     0.00547699056 * cos( 3.63127725056 +       85.82729883120*t);\r
+   uranus_x_0+=     0.00458219984 * cos( 3.90788284112 +        2.96894541660*t);\r
+   uranus_x_0+=     0.00496087649 * cos( 0.59947400861 +      529.69096509460*t);\r
+   uranus_x_0+=     0.00383625535 * cos( 6.18762010576 +      138.51749687070*t);\r
+   uranus_x_0+=     0.00267938156 * cos( 0.96885660137 +      213.29909543800*t);\r
+   uranus_x_0+=     0.00215368005 * cos( 5.30877641428 +       38.13303563780*t);\r
+   uranus_x_0+=     0.00145505389 * cos( 2.31759757085 +       70.84944530420*t);\r
+   uranus_x_0+=     0.00135340032 * cos( 5.51062460816 +       78.71375183040*t);\r
+   uranus_x_0+=     0.00119593859 * cos( 4.10138544267 +       39.61750834610*t);\r
+   uranus_x_0+=     0.00125105686 * cos( 2.51455273063 +      111.43016149680*t);\r
+   uranus_x_0+=     0.00111260244 * cos( 5.12252784325 +      222.86032299360*t);\r
+   uranus_x_0+=     0.00104619827 * cos( 3.90538916334 +      146.59425171800*t);\r
+   uranus_x_0+=     0.00110125387 * cos( 4.45473528724 +       35.16409022120*t);\r
+   uranus_x_0+=     0.00063584588 * cos( 0.29966233158 +      299.12639426920*t);\r
+   uranus_x_0+=     0.00053904041 * cos( 3.92590422507 +        3.93215326310*t);\r
+   uranus_x_0+=     0.00065066905 * cos( 3.73008452906 +      109.94568878850*t);\r
+   uranus_x_0+=     0.00039181662 * cos( 2.68841280769 +        4.45341812490*t);\r
+   uranus_x_0+=     0.00034341683 * cos( 3.03781661928 +      225.82926841020*t);\r
+   uranus_x_0+=     0.00033134636 * cos( 2.54201591218 +       65.22037101170*t);\r
+   uranus_x_0+=     0.00034555652 * cos( 1.84699329257 +       79.23501669220*t);\r
+   uranus_x_0+=     0.00033867050 * cos( 5.98418436103 +       70.32818044240*t);\r
+   uranus_x_0+=     0.00028371614 * cos( 2.58026657123 +      127.47179660680*t);\r
+   uranus_x_0+=     0.00035943348 * cos( 4.08754543016 +      202.25339517410*t);\r
+   uranus_x_0+=     0.00025208833 * cos( 5.30272144657 +        9.56122755560*t);\r
+   uranus_x_0+=     0.00023467802 * cos( 4.09729860322 +      145.63104387150*t);\r
+   uranus_x_0+=     0.00022963939 * cos( 5.51475073655 +       84.34282612290*t);\r
+   uranus_x_0+=     0.00031823951 * cos( 5.53948583244 +      152.53214255120*t);\r
+   uranus_x_0+=     0.00028384953 * cos( 6.01785430306 +      184.72728735580*t);\r
+   uranus_x_0+=     0.00026657176 * cos( 6.11027939727 +      160.60889739850*t);\r
+   uranus_x_0+=     0.00019676762 * cos( 5.53431398332 +       74.66972398270*t);\r
+   uranus_x_0+=     0.00019653873 * cos( 2.28660913421 +       74.89347315190*t);\r
+   uranus_x_0+=     0.00019954280 * cos( 0.57450958037 +       12.53017297220*t);\r
+   uranus_x_0+=     0.00018565067 * cos( 0.62225019017 +       52.69019803950*t);\r
+   uranus_x_0+=     0.00020084756 * cos( 4.47297488471 +       22.09140052780*t);\r
+   uranus_x_0+=     0.00019926329 * cos( 1.39878194708 +      112.91463420510*t);\r
+   uranus_x_0+=     0.00018575632 * cos( 5.70217475790 +       33.67961751290*t);\r
+   uranus_x_0+=     0.00016587870 * cos( 4.86920309163 +      108.46121608020*t);\r
+   uranus_x_0+=     0.00015171194 * cos( 2.88415453399 +       41.10198105440*t);\r
+   uranus_x_0+=     0.00011245800 * cos( 6.11597016146 +       71.60020482960*t);\r
+   uranus_x_0+=     0.00013948521 * cos( 6.27545694160 +      221.37585028530*t);\r
+   uranus_x_0+=     0.00010798350 * cos( 1.70031857078 +       77.96299230500*t);\r
+   uranus_x_0+=     0.00013593955 * cos( 2.55407820633 +       87.31177153950*t);\r
+   uranus_x_0+=     0.00011997848 * cos( 0.94875212305 +     1059.38193018920*t);\r
+   uranus_x_0+=     0.00012884351 * cos( 5.08737999470 +      145.10977900970*t);\r
+   uranus_x_0+=     0.00012394786 * cos( 6.21892878850 +       72.33391801250*t);\r
+   uranus_x_0+=     0.00012253318 * cos( 0.19452856525 +       36.64856292950*t);\r
+   uranus_x_0+=     0.00011538642 * cos( 1.77241794539 +       77.22927912210*t);\r
+\r
+   double uranus_x_1=0.0;\r
+   uranus_x_1+=     0.00739730021 * cos( 6.01067825116 +      149.56319713460*t);\r
+   uranus_x_1+=     0.00526878306 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_x_1+=     0.00239840801 * cos( 5.33657762707 +       73.29712585900*t);\r
+   uranus_x_1+=     0.00229676787 * cos( 2.48204455775 +       76.26607127560*t);\r
+   uranus_x_1+=     0.00111045158 * cos( 5.57157235960 +       11.04570026390*t);\r
+   uranus_x_1+=     0.00096352822 * cos( 0.35070389084 +       63.73589830340*t);\r
+   uranus_x_1+=     0.00081511870 * cos( 1.21058618039 +       85.82729883120*t);\r
+   uranus_x_1+=     0.00045687564 * cos( 2.29216583843 +      138.51749687070*t);\r
+   uranus_x_1+=     0.00051382501 * cos( 2.18935125260 +      224.34479570190*t);\r
+   uranus_x_1+=     0.00038844330 * cos( 0.30724575951 +       70.84944530420*t);\r
+   uranus_x_1+=     0.00036158493 * cos( 1.23634798757 +       78.71375183040*t);\r
+   uranus_x_1+=     0.00032333094 * cos( 5.06666556704 +       74.78159856730*t);\r
+   uranus_x_1+=     0.00021685656 * cos( 4.93710968392 +      151.04766984290*t);\r
+   uranus_x_1+=     0.00019441970 * cos( 1.30617490304 +       77.75054398390*t);\r
+   uranus_x_1+=     0.00017376241 * cos( 0.24607221230 +       71.81265315070*t);\r
+   uranus_x_1+=     0.00015211071 * cos( 5.53141633140 +        3.93215326310*t);\r
+   uranus_x_1=uranus_x_1 * t;\r
+\r
+   double uranus_x_2=0.0;\r
+   uranus_x_2+=     0.00016015732 * cos( 3.83700026619 +       74.78159856730*t);\r
+   uranus_x_2+=     0.00010915299 * cos( 3.02987776270 +      149.56319713460*t);\r
+   uranus_x_2=uranus_x_2 * t * t;\r
+\r
+   return uranus_x_0+uranus_x_1+uranus_x_2;\r
+}\r
+\r
+double vsop87a_micro_uranus_y(double t){\r
+   double uranus_y_0=0.0;\r
+   uranus_y_0+=    19.16518231584 * cos( 3.91045677002 +       74.78159856730*t);\r
+   uranus_y_0+=     0.44390465203 * cos( 0.08884111329 +      149.56319713460*t);\r
+   uranus_y_0+=     0.16256125476 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_y_0+=     0.14755940186 * cos( 1.85423280679 +       73.29712585900*t);\r
+   uranus_y_0+=     0.14123958128 * cos( 2.82486076549 +       76.26607127560*t);\r
+   uranus_y_0+=     0.06250078231 * cos( 3.56960243857 +        1.48447270830*t);\r
+   uranus_y_0+=     0.01542668264 * cos( 2.55040539213 +      224.34479570190*t);\r
+   uranus_y_0+=     0.01442356575 * cos( 1.08004542712 +      148.07872442630*t);\r
+   uranus_y_0+=     0.00938975501 * cos( 0.09275714761 +       11.04570026390*t);\r
+   uranus_y_0+=     0.00650331846 * cos( 2.76142680222 +       63.73589830340*t);\r
+   uranus_y_0+=     0.00657343120 * cos( 5.28830704469 +      151.04766984290*t);\r
+   uranus_y_0+=     0.00621326770 * cos( 1.48795811387 +       77.75054398390*t);\r
+   uranus_y_0+=     0.00541961958 * cos( 3.24476486661 +       71.81265315070*t);\r
+   uranus_y_0+=     0.00547472694 * cos( 2.06037924573 +       85.82729883120*t);\r
+   uranus_y_0+=     0.00459589120 * cos( 2.33745536070 +        2.96894541660*t);\r
+   uranus_y_0+=     0.00495936105 * cos( 5.31205753740 +      529.69096509460*t);\r
+   uranus_y_0+=     0.00387922853 * cos( 4.62026923885 +      138.51749687070*t);\r
+   uranus_y_0+=     0.00268363417 * cos( 5.68085299020 +      213.29909543800*t);\r
+   uranus_y_0+=     0.00216239629 * cos( 3.73800767580 +       38.13303563780*t);\r
+   uranus_y_0+=     0.00144032475 * cos( 0.75015700920 +       70.84944530420*t);\r
+   uranus_y_0+=     0.00135290820 * cos( 3.93970260616 +       78.71375183040*t);\r
+   uranus_y_0+=     0.00119670613 * cos( 2.53058783780 +       39.61750834610*t);\r
+   uranus_y_0+=     0.00124868545 * cos( 0.94315917319 +      111.43016149680*t);\r
+   uranus_y_0+=     0.00111204860 * cos( 3.55163219419 +      222.86032299360*t);\r
+   uranus_y_0+=     0.00104507929 * cos( 2.33345675603 +      146.59425171800*t);\r
+   uranus_y_0+=     0.00108584454 * cos( 6.02234848388 +       35.16409022120*t);\r
+   uranus_y_0+=     0.00063573747 * cos( 5.01204967920 +      299.12639426920*t);\r
+   uranus_y_0+=     0.00053289771 * cos( 2.38437587876 +        3.93215326310*t);\r
+   uranus_y_0+=     0.00063774261 * cos( 2.15607602904 +      109.94568878850*t);\r
+   uranus_y_0+=     0.00039218598 * cos( 1.11841109252 +        4.45341812490*t);\r
+   uranus_y_0+=     0.00034205426 * cos( 0.92405922576 +       65.22037101170*t);\r
+   uranus_y_0+=     0.00034334377 * cos( 1.46696169843 +      225.82926841020*t);\r
+   uranus_y_0+=     0.00034538316 * cos( 0.27613780697 +       79.23501669220*t);\r
+   uranus_y_0+=     0.00039256771 * cos( 5.75956853703 +      202.25339517410*t);\r
+   uranus_y_0+=     0.00026157754 * cos( 3.74097610798 +        9.56122755560*t);\r
+   uranus_y_0+=     0.00023427328 * cos( 2.52740125551 +      145.63104387150*t);\r
+   uranus_y_0+=     0.00022933138 * cos( 3.94455540350 +       84.34282612290*t);\r
+   uranus_y_0+=     0.00031816303 * cos( 3.96860170484 +      152.53214255120*t);\r
+   uranus_y_0+=     0.00025237176 * cos( 4.45141413666 +       70.32818044240*t);\r
+   uranus_y_0+=     0.00028372491 * cos( 4.44714627097 +      184.72728735580*t);\r
+   uranus_y_0+=     0.00026652859 * cos( 4.53944395347 +      160.60889739850*t);\r
+   uranus_y_0+=     0.00019666208 * cos( 3.96350065335 +       74.66972398270*t);\r
+   uranus_y_0+=     0.00019643845 * cos( 0.71577796385 +       74.89347315190*t);\r
+   uranus_y_0+=     0.00019838981 * cos( 5.29113397354 +       12.53017297220*t);\r
+   uranus_y_0+=     0.00021523908 * cos( 4.93565132068 +       36.64856292950*t);\r
+   uranus_y_0+=     0.00015537967 * cos( 1.87863275460 +       52.69019803950*t);\r
+   uranus_y_0+=     0.00020115100 * cos( 3.45473780762 +      127.47179660680*t);\r
+   uranus_y_0+=     0.00020051641 * cos( 2.90386352937 +       22.09140052780*t);\r
+   uranus_y_0+=     0.00019901477 * cos( 6.11075402434 +      112.91463420510*t);\r
+   uranus_y_0+=     0.00018126776 * cos( 0.98478853787 +       33.67961751290*t);\r
+   uranus_y_0+=     0.00015174962 * cos( 1.31314034959 +       41.10198105440*t);\r
+   uranus_y_0+=     0.00011239020 * cos( 4.54508334011 +       71.60020482960*t);\r
+   uranus_y_0+=     0.00013948849 * cos( 4.70474945682 +      221.37585028530*t);\r
+   uranus_y_0+=     0.00010819728 * cos( 0.12807029856 +       77.96299230500*t);\r
+   uranus_y_0+=     0.00013589665 * cos( 0.98313719930 +       87.31177153950*t);\r
+   uranus_y_0+=     0.00011996772 * cos( 5.66129275335 +     1059.38193018920*t);\r
+   uranus_y_0+=     0.00012407787 * cos( 4.64945783340 +       72.33391801250*t);\r
+   uranus_y_0+=     0.00011531140 * cos( 0.20190074645 +       77.22927912210*t);\r
+\r
+   double uranus_y_1=0.0;\r
+   uranus_y_1+=     0.02157896385 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_y_1+=     0.00739227349 * cos( 4.43963890935 +      149.56319713460*t);\r
+   uranus_y_1+=     0.00238545685 * cos( 3.76882493145 +       73.29712585900*t);\r
+   uranus_y_1+=     0.00229396424 * cos( 0.91090183978 +       76.26607127560*t);\r
+   uranus_y_1+=     0.00110137111 * cos( 4.00844441616 +       11.04570026390*t);\r
+   uranus_y_1+=     0.00094979054 * cos( 5.07141537066 +       63.73589830340*t);\r
+   uranus_y_1+=     0.00081474163 * cos( 5.92275367106 +       85.82729883120*t);\r
+   uranus_y_1+=     0.00045457174 * cos( 0.73292241207 +      138.51749687070*t);\r
+   uranus_y_1+=     0.00051366974 * cos( 0.61844114994 +      224.34479570190*t);\r
+   uranus_y_1+=     0.00038296005 * cos( 5.01873578671 +       70.84944530420*t);\r
+   uranus_y_1+=     0.00036146116 * cos( 5.94859452787 +       78.71375183040*t);\r
+   uranus_y_1+=     0.00032420558 * cos( 4.32617271732 +       74.78159856730*t);\r
+   uranus_y_1+=     0.00021673269 * cos( 3.36607263522 +      151.04766984290*t);\r
+   uranus_y_1+=     0.00019425087 * cos( 6.01842187783 +       77.75054398390*t);\r
+   uranus_y_1+=     0.00017393206 * cos( 4.96098895488 +       71.81265315070*t);\r
+   uranus_y_1+=     0.00014991169 * cos( 3.97176856758 +        3.93215326310*t);\r
+   uranus_y_1=uranus_y_1 * t;\r
+\r
+   double uranus_y_2=0.0;\r
+   uranus_y_2+=     0.00034812647 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_y_2+=     0.00016589194 * cos( 2.29556740620 +       74.78159856730*t);\r
+   uranus_y_2+=     0.00010905147 * cos( 1.45737963668 +      149.56319713460*t);\r
+   uranus_y_2=uranus_y_2 * t * t;\r
+\r
+   return uranus_y_0+uranus_y_1+uranus_y_2;\r
+}\r
+\r
+double vsop87a_micro_uranus_z(double t){\r
+   double uranus_z_0=0.0;\r
+   uranus_z_0+=     0.25878127698 * cos( 2.61861272578 +       74.78159856730*t);\r
+   uranus_z_0+=     0.01774318778 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_z_0+=     0.00599316131 * cos( 5.08119500585 +      149.56319713460*t);\r
+   uranus_z_0+=     0.00190281890 * cos( 1.61643841193 +       76.26607127560*t);\r
+   uranus_z_0+=     0.00190881685 * cos( 0.57869575952 +       73.29712585900*t);\r
+   uranus_z_0+=     0.00084626761 * cos( 2.26030150166 +        1.48447270830*t);\r
+   uranus_z_0+=     0.00030734257 * cos( 0.23571721555 +       63.73589830340*t);\r
+   uranus_z_0+=     0.00020842052 * cos( 1.26054208091 +      224.34479570190*t);\r
+   uranus_z_0+=     0.00019734273 * cos( 6.04314677688 +      148.07872442630*t);\r
+   uranus_z_0+=     0.00012537530 * cos( 5.17169051466 +       11.04570026390*t);\r
+   uranus_z_0+=     0.00014582864 * cos( 6.14852037212 +       71.81265315070*t);\r
+   uranus_z_0+=     0.00010407529 * cos( 3.65320417038 +      213.29909543800*t);\r
+   uranus_z_0+=     0.00011261541 * cos( 3.55973769686 +      529.69096509460*t);\r
+\r
+   double uranus_z_1=0.0;\r
+   uranus_z_1+=     0.00655916626 * cos( 0.01271947660 +       74.78159856730*t);\r
+   uranus_z_1+=     0.00049648951 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_z_1+=     0.00023874178 * cos( 2.73870491220 +      149.56319713460*t);\r
+   uranus_z_1=uranus_z_1 * t;\r
+\r
+   double uranus_z_2=0.0;\r
+   uranus_z_2+=     0.00014697858 * cos( 1.75149165003 +       74.78159856730*t);\r
+   uranus_z_2=uranus_z_2 * t * t;\r
+\r
+   return uranus_z_0+uranus_z_1+uranus_z_2;\r
+}\r
+\r
+double vsop87a_micro_venus_x(double t){\r
+   double venus_x_0=0.0;\r
+   venus_x_0+=     0.72211281391 * cos( 3.17575836361 +    10213.28554621100*t);\r
+   venus_x_0+=     0.00486448018 * cos( 0.00000000000 +        0.00000000000*t);\r
+   venus_x_0+=     0.00244500474 * cos( 4.05566613861 +    20426.57109242200*t);\r
+\r
+   double venus_x_1=0.0;\r
+   venus_x_1+=     0.00033862636 * cos( 3.14159265359 +        0.00000000000*t);\r
+   venus_x_1+=     0.00017234992 * cos( 0.92721124604 +    20426.57109242200*t);\r
+   venus_x_1=venus_x_1 * t;\r
+\r
+   return venus_x_0+venus_x_1;\r
+}\r
+\r
+double vsop87a_micro_venus_y(double t){\r
+   double venus_y_0=0.0;\r
+   venus_y_0+=     0.72324820731 * cos( 1.60573808356 +    10213.28554621100*t);\r
+   venus_y_0+=     0.00549506273 * cos( 3.14159265359 +        0.00000000000*t);\r
+   venus_y_0+=     0.00244884790 * cos( 2.48564954004 +    20426.57109242200*t);\r
+\r
+   double venus_y_1=0.0;\r
+   venus_y_1+=     0.00039231430 * cos( 0.00000000000 +        0.00000000000*t);\r
+   venus_y_1+=     0.00017282326 * cos( 5.63824735900 +    20426.57109242200*t);\r
+   venus_y_1=venus_y_1 * t;\r
+\r
+   return venus_y_0+venus_y_1;\r
+}\r
+\r
+double vsop87a_micro_venus_z(double t){\r
+   double venus_z_0=0.0;\r
+   venus_z_0+=     0.04282990302 * cos( 0.26703856476 +    10213.28554621100*t);\r
+   venus_z_0+=     0.00035588343 * cos( 3.14159265359 +        0.00000000000*t);\r
+   venus_z_0+=     0.00014501879 * cos( 1.14696911390 +    20426.57109242200*t);\r
+\r
+   double venus_z_1=0.0;\r
+   venus_z_1+=     0.00208096402 * cos( 1.88967278742 +    10213.28554621100*t);\r
+   venus_z_1=venus_z_1 * t;\r
+\r
+   return venus_z_0+venus_z_1;\r
+}\r
+\r
diff --git a/legacy/lib/vsop87/vsop87a_micro.h b/legacy/lib/vsop87/vsop87a_micro.h
new file mode 100755 (executable)
index 0000000..87515f5
--- /dev/null
@@ -0,0 +1,17 @@
+//VSOP87-Multilang http://www.astrogreg.com/vsop87-multilang/index.html\r
+//Greg Miller (gmiller@gregmiller.net) 2019.  Released as Public Domain\r
+\r
+#ifndef VSOP87A_MICRO\r
+#define VSOP87A_MICRO\r
+\r
+   void vsop87a_micro_getEarth(double t,double temp[]);\r
+   void vsop87a_micro_getEmb(double t,double temp[]);\r
+   void vsop87a_micro_getJupiter(double t,double temp[]);\r
+   void vsop87a_micro_getMars(double t,double temp[]);\r
+   void vsop87a_micro_getMercury(double t,double temp[]);\r
+   void vsop87a_micro_getNeptune(double t,double temp[]);\r
+   void vsop87a_micro_getSaturn(double t,double temp[]);\r
+   void vsop87a_micro_getUranus(double t,double temp[]);\r
+   void vsop87a_micro_getVenus(double t,double temp[]);\r
+   void vsop87a_micro_getMoon(double earth[], double emb[],double temp[]);\r
+#endif\r
diff --git a/legacy/lib/vsop87/vsop87a_milli.c b/legacy/lib/vsop87/vsop87a_milli.c
new file mode 100755 (executable)
index 0000000..b187572
--- /dev/null
@@ -0,0 +1,2048 @@
+//VSOP87-Multilang http://www.astrogreg.com/vsop87-multilang/index.html\r
+//Greg Miller (gmiller@gregmiller.net) 2019.  Released as Public Domain\r
+\r
+#include <math.h>\r
+#include "vsop87a_milli.h"\r
+double vsop87a_milli_earth_x(double t);\r
+double vsop87a_milli_earth_y(double t);\r
+double vsop87a_milli_earth_z(double t);\r
+double vsop87a_milli_emb_x(double t);\r
+double vsop87a_milli_emb_y(double t);\r
+double vsop87a_milli_emb_z(double t);\r
+double vsop87a_milli_jupiter_x(double t);\r
+double vsop87a_milli_jupiter_y(double t);\r
+double vsop87a_milli_jupiter_z(double t);\r
+double vsop87a_milli_mars_x(double t);\r
+double vsop87a_milli_mars_y(double t);\r
+double vsop87a_milli_mars_z(double t);\r
+double vsop87a_milli_mercury_x(double t);\r
+double vsop87a_milli_mercury_y(double t);\r
+double vsop87a_milli_mercury_z(double t);\r
+double vsop87a_milli_neptune_x(double t);\r
+double vsop87a_milli_neptune_y(double t);\r
+double vsop87a_milli_neptune_z(double t);\r
+double vsop87a_milli_saturn_x(double t);\r
+double vsop87a_milli_saturn_y(double t);\r
+double vsop87a_milli_saturn_z(double t);\r
+double vsop87a_milli_uranus_x(double t);\r
+double vsop87a_milli_uranus_y(double t);\r
+double vsop87a_milli_uranus_z(double t);\r
+double vsop87a_milli_venus_x(double t);\r
+double vsop87a_milli_venus_y(double t);\r
+double vsop87a_milli_venus_z(double t);\r
+\r
+\r
+void vsop87a_milli_getEarth(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_earth_x(t);\r
+   temp[1]=vsop87a_milli_earth_y(t);\r
+   temp[2]=vsop87a_milli_earth_z(t);\r
+}\r
+\r
+void vsop87a_milli_getEmb(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_emb_x(t);\r
+   temp[1]=vsop87a_milli_emb_y(t);\r
+   temp[2]=vsop87a_milli_emb_z(t);\r
+}\r
+\r
+void vsop87a_milli_getJupiter(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_jupiter_x(t);\r
+   temp[1]=vsop87a_milli_jupiter_y(t);\r
+   temp[2]=vsop87a_milli_jupiter_z(t);\r
+}\r
+\r
+void vsop87a_milli_getMars(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_mars_x(t);\r
+   temp[1]=vsop87a_milli_mars_y(t);\r
+   temp[2]=vsop87a_milli_mars_z(t);\r
+}\r
+\r
+void vsop87a_milli_getMercury(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_mercury_x(t);\r
+   temp[1]=vsop87a_milli_mercury_y(t);\r
+   temp[2]=vsop87a_milli_mercury_z(t);\r
+}\r
+\r
+void vsop87a_milli_getNeptune(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_neptune_x(t);\r
+   temp[1]=vsop87a_milli_neptune_y(t);\r
+   temp[2]=vsop87a_milli_neptune_z(t);\r
+}\r
+\r
+void vsop87a_milli_getSaturn(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_saturn_x(t);\r
+   temp[1]=vsop87a_milli_saturn_y(t);\r
+   temp[2]=vsop87a_milli_saturn_z(t);\r
+}\r
+\r
+void vsop87a_milli_getUranus(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_uranus_x(t);\r
+   temp[1]=vsop87a_milli_uranus_y(t);\r
+   temp[2]=vsop87a_milli_uranus_z(t);\r
+}\r
+\r
+void vsop87a_milli_getVenus(double t,double temp[]){\r
+   temp[0]=vsop87a_milli_venus_x(t);\r
+   temp[1]=vsop87a_milli_venus_y(t);\r
+   temp[2]=vsop87a_milli_venus_z(t);\r
+}\r
+\r
+void vsop87a_milli_getMoon(double earth[], double emb[],double temp[]){\r
+   temp[0]=(emb[0]-earth[0])*(1 + 1 / 0.01230073677);\r
+   temp[1]=(emb[1]-earth[1])*(1 + 1 / 0.01230073677);\r
+   temp[2]=(emb[2]-earth[2])*(1 + 1 / 0.01230073677);\r
+   temp[0]=temp[0]+earth[0];\r
+   temp[1]=temp[1]+earth[1];\r
+   temp[2]=temp[2]+earth[2];\r
+}\r
+\r
+double vsop87a_milli_earth_x(double t){\r
+   double earth_x_0=0.0;\r
+   earth_x_0+=     0.99982928844 * cos( 1.75348568475 +     6283.07584999140*t);\r
+   earth_x_0+=     0.00835257300 * cos( 1.71034539450 +    12566.15169998280*t);\r
+   earth_x_0+=     0.00561144206 * cos( 0.00000000000 +        0.00000000000*t);\r
+   earth_x_0+=     0.00010466628 * cos( 1.66722645223 +    18849.22754997420*t);\r
+   earth_x_0+=     0.00003110838 * cos( 0.66875185215 +    83996.84731811189*t);\r
+   earth_x_0+=     0.00002552498 * cos( 0.58310207301 +      529.69096509460*t);\r
+   earth_x_0+=     0.00002137256 * cos( 1.09235189672 +     1577.34354244780*t);\r
+   earth_x_0+=     0.00001709103 * cos( 0.49540223397 +     6279.55273164240*t);\r
+   earth_x_0+=     0.00001707882 * cos( 6.15315547484 +     6286.59896834040*t);\r
+   earth_x_0+=     0.00001445242 * cos( 3.47272783760 +     2352.86615377180*t);\r
+   earth_x_0+=     0.00001091006 * cos( 3.68984782465 +     5223.69391980220*t);\r
+\r
+   double earth_x_1=0.0;\r
+   earth_x_1+=     0.00123403056 * cos( 0.00000000000 +        0.00000000000*t);\r
+   earth_x_1+=     0.00051500156 * cos( 6.00266267204 +    12566.15169998280*t);\r
+   earth_x_1+=     0.00001290726 * cos( 5.95943124583 +    18849.22754997420*t);\r
+   earth_x_1+=     0.00001068627 * cos( 2.01554176551 +     6283.07584999140*t);\r
+   earth_x_1=earth_x_1 * t;\r
+\r
+   double earth_x_2=0.0;\r
+   earth_x_2+=     0.00004143217 * cos( 3.14159265359 +        0.00000000000*t);\r
+   earth_x_2+=     0.00002175695 * cos( 4.39999849572 +    12566.15169998280*t);\r
+   earth_x_2=earth_x_2 * t * t;\r
+\r
+   return earth_x_0+earth_x_1+earth_x_2;\r
+}\r
+\r
+double vsop87a_milli_earth_y(double t){\r
+   double earth_y_0=0.0;\r
+   earth_y_0+=     0.99989211030 * cos( 0.18265890456 +     6283.07584999140*t);\r
+   earth_y_0+=     0.02442699036 * cos( 3.14159265359 +        0.00000000000*t);\r
+   earth_y_0+=     0.00835292314 * cos( 0.13952878991 +    12566.15169998280*t);\r
+   earth_y_0+=     0.00010466965 * cos( 0.09641690558 +    18849.22754997420*t);\r
+   earth_y_0+=     0.00003110838 * cos( 5.38114091484 +    83996.84731811189*t);\r
+   earth_y_0+=     0.00002570338 * cos( 5.30103973360 +      529.69096509460*t);\r
+   earth_y_0+=     0.00002147473 * cos( 2.66253538905 +     1577.34354244780*t);\r
+   earth_y_0+=     0.00001709219 * cos( 5.20780401071 +     6279.55273164240*t);\r
+   earth_y_0+=     0.00001707987 * cos( 4.58232858766 +     6286.59896834040*t);\r
+   earth_y_0+=     0.00001440265 * cos( 1.90068164664 +     2352.86615377180*t);\r
+   earth_y_0+=     0.00001135092 * cos( 5.27313415220 +     5223.69391980220*t);\r
+\r
+   double earth_y_1=0.0;\r
+   earth_y_1+=     0.00093046324 * cos( 0.00000000000 +        0.00000000000*t);\r
+   earth_y_1+=     0.00051506609 * cos( 4.43180499286 +    12566.15169998280*t);\r
+   earth_y_1+=     0.00001290800 * cos( 4.38860548540 +    18849.22754997420*t);\r
+   earth_y_1=earth_y_1 * t;\r
+\r
+   double earth_y_2=0.0;\r
+   earth_y_2+=     0.00005080208 * cos( 0.00000000000 +        0.00000000000*t);\r
+   earth_y_2+=     0.00002178016 * cos( 2.82957544235 +    12566.15169998280*t);\r
+   earth_y_2+=     0.00001020487 * cos( 4.63746718598 +     6283.07584999140*t);\r
+   earth_y_2=earth_y_2 * t * t;\r
+\r
+   return earth_y_0+earth_y_1+earth_y_2;\r
+}\r
+\r
+double vsop87a_milli_earth_z(double t){\r
+   double earth_z_1=0.0;\r
+   earth_z_1+=     0.00227822442 * cos( 3.41372504278 +     6283.07584999140*t);\r
+   earth_z_1+=     0.00005429282 * cos( 0.00000000000 +        0.00000000000*t);\r
+   earth_z_1+=     0.00001903183 * cos( 3.37061270964 +    12566.15169998280*t);\r
+   earth_z_1=earth_z_1 * t;\r
+\r
+   double earth_z_2=0.0;\r
+   earth_z_2+=     0.00009721989 * cos( 5.15233725915 +     6283.07584999140*t);\r
+   earth_z_2=earth_z_2 * t * t;\r
+\r
+   return earth_z_1+earth_z_2;\r
+}\r
+\r
+double vsop87a_milli_emb_x(double t){\r
+   double emb_x_0=0.0;\r
+   emb_x_0+=     0.99982927460 * cos( 1.75348568475 +     6283.07584999140*t);\r
+   emb_x_0+=     0.00835257300 * cos( 1.71034539450 +    12566.15169998280*t);\r
+   emb_x_0+=     0.00561144161 * cos( 0.00000000000 +        0.00000000000*t);\r
+   emb_x_0+=     0.00010466628 * cos( 1.66722645223 +    18849.22754997420*t);\r
+   emb_x_0+=     0.00002552498 * cos( 0.58310207301 +      529.69096509460*t);\r
+   emb_x_0+=     0.00002137256 * cos( 1.09235189672 +     1577.34354244780*t);\r
+   emb_x_0+=     0.00001709103 * cos( 0.49540223397 +     6279.55273164240*t);\r
+   emb_x_0+=     0.00001707882 * cos( 6.15315547484 +     6286.59896834040*t);\r
+   emb_x_0+=     0.00001445242 * cos( 3.47272783760 +     2352.86615377180*t);\r
+   emb_x_0+=     0.00001091006 * cos( 3.68984782465 +     5223.69391980220*t);\r
+\r
+   double emb_x_1=0.0;\r
+   emb_x_1+=     0.00123403046 * cos( 0.00000000000 +        0.00000000000*t);\r
+   emb_x_1+=     0.00051500156 * cos( 6.00266267204 +    12566.15169998280*t);\r
+   emb_x_1+=     0.00001290726 * cos( 5.95943124583 +    18849.22754997420*t);\r
+   emb_x_1+=     0.00001068627 * cos( 2.01554176551 +     6283.07584999140*t);\r
+   emb_x_1=emb_x_1 * t;\r
+\r
+   double emb_x_2=0.0;\r
+   emb_x_2+=     0.00004143217 * cos( 3.14159265359 +        0.00000000000*t);\r
+   emb_x_2+=     0.00002175695 * cos( 4.39999849572 +    12566.15169998280*t);\r
+   emb_x_2=emb_x_2 * t * t;\r
+\r
+   return emb_x_0+emb_x_1+emb_x_2;\r
+}\r
+\r
+double vsop87a_milli_emb_y(double t){\r
+   double emb_y_0=0.0;\r
+   emb_y_0+=     0.99989209645 * cos( 0.18265890456 +     6283.07584999140*t);\r
+   emb_y_0+=     0.02442698841 * cos( 3.14159265359 +        0.00000000000*t);\r
+   emb_y_0+=     0.00835292314 * cos( 0.13952878991 +    12566.15169998280*t);\r
+   emb_y_0+=     0.00010466965 * cos( 0.09641690558 +    18849.22754997420*t);\r
+   emb_y_0+=     0.00002570338 * cos( 5.30103973360 +      529.69096509460*t);\r
+   emb_y_0+=     0.00002147473 * cos( 2.66253538905 +     1577.34354244780*t);\r
+   emb_y_0+=     0.00001709219 * cos( 5.20780401071 +     6279.55273164240*t);\r
+   emb_y_0+=     0.00001707987 * cos( 4.58232858766 +     6286.59896834040*t);\r
+   emb_y_0+=     0.00001440265 * cos( 1.90068164664 +     2352.86615377180*t);\r
+   emb_y_0+=     0.00001135092 * cos( 5.27313415220 +     5223.69391980220*t);\r
+\r
+   double emb_y_1=0.0;\r
+   emb_y_1+=     0.00093046317 * cos( 0.00000000000 +        0.00000000000*t);\r
+   emb_y_1+=     0.00051506609 * cos( 4.43180499286 +    12566.15169998280*t);\r
+   emb_y_1+=     0.00001290800 * cos( 4.38860548540 +    18849.22754997420*t);\r
+   emb_y_1=emb_y_1 * t;\r
+\r
+   double emb_y_2=0.0;\r
+   emb_y_2+=     0.00005080208 * cos( 0.00000000000 +        0.00000000000*t);\r
+   emb_y_2+=     0.00002178016 * cos( 2.82957544235 +    12566.15169998280*t);\r
+   emb_y_2+=     0.00001020487 * cos( 4.63746718598 +     6283.07584999140*t);\r
+   emb_y_2=emb_y_2 * t * t;\r
+\r
+   return emb_y_0+emb_y_1+emb_y_2;\r
+}\r
+\r
+double vsop87a_milli_emb_z(double t){\r
+   double emb_z_1=0.0;\r
+   emb_z_1+=     0.00227822442 * cos( 3.41372504278 +     6283.07584999140*t);\r
+   emb_z_1+=     0.00005429282 * cos( 0.00000000000 +        0.00000000000*t);\r
+   emb_z_1+=     0.00001903183 * cos( 3.37061270964 +    12566.15169998280*t);\r
+   emb_z_1=emb_z_1 * t;\r
+\r
+   double emb_z_2=0.0;\r
+   emb_z_2+=     0.00009721989 * cos( 5.15233725915 +     6283.07584999140*t);\r
+   emb_z_2=emb_z_2 * t * t;\r
+\r
+   return emb_z_1+emb_z_2;\r
+}\r
+\r
+double vsop87a_milli_jupiter_x(double t){\r
+   double jupiter_x_0=0.0;\r
+   jupiter_x_0+=     5.19663470114 * cos( 0.59945082355 +      529.69096509460*t);\r
+   jupiter_x_0+=     0.36662642320 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_x_0+=     0.12593937922 * cos( 0.94911583701 +     1059.38193018920*t);\r
+   jupiter_x_0+=     0.01500672056 * cos( 0.73175134610 +      522.57741809380*t);\r
+   jupiter_x_0+=     0.01476224578 * cos( 3.61736921122 +      536.80451209540*t);\r
+   jupiter_x_0+=     0.00457752736 * cos( 1.29883700755 +     1589.07289528380*t);\r
+   jupiter_x_0+=     0.00301689798 * cos( 5.17372551148 +        7.11354700080*t);\r
+   jupiter_x_0+=     0.00385975375 * cos( 2.01229910687 +      103.09277421860*t);\r
+   jupiter_x_0+=     0.00194025405 * cos( 5.02580363996 +      426.59819087600*t);\r
+   jupiter_x_0+=     0.00150678793 * cos( 6.12003027739 +      110.20632121940*t);\r
+   jupiter_x_0+=     0.00144867641 * cos( 5.55980577080 +      632.78373931320*t);\r
+   jupiter_x_0+=     0.00134226996 * cos( 0.87648567011 +      213.29909543800*t);\r
+   jupiter_x_0+=     0.00103494641 * cos( 6.19324769120 +     1052.26838318840*t);\r
+   jupiter_x_0+=     0.00114201562 * cos( 0.01567084269 +     1162.47470440780*t);\r
+   jupiter_x_0+=     0.00072095575 * cos( 3.96117430643 +     1066.49547719000*t);\r
+   jupiter_x_0+=     0.00059486083 * cos( 4.45769374358 +      949.17560896980*t);\r
+   jupiter_x_0+=     0.00068284021 * cos( 3.44051122631 +      846.08283475120*t);\r
+   jupiter_x_0+=     0.00047092251 * cos( 1.44612636451 +      419.48464387520*t);\r
+   jupiter_x_0+=     0.00030623417 * cos( 2.99132321427 +      206.18554843720*t);\r
+   jupiter_x_0+=     0.00026613459 * cos( 4.85169906494 +      323.50541665740*t);\r
+   jupiter_x_0+=     0.00019727457 * cos( 1.64891626213 +     2118.76386037840*t);\r
+   jupiter_x_0+=     0.00016481594 * cos( 1.95150056568 +      316.39186965660*t);\r
+   jupiter_x_0+=     0.00016101974 * cos( 0.87973155980 +      515.46387109300*t);\r
+   jupiter_x_0+=     0.00014209487 * cos( 2.07769621413 +      742.99006053260*t);\r
+   jupiter_x_0+=     0.00015192516 * cos( 6.25820127906 +      735.87651353180*t);\r
+   jupiter_x_0+=     0.00011423199 * cos( 3.48146108929 +      543.91805909620*t);\r
+   jupiter_x_0+=     0.00012155285 * cos( 3.75229924999 +      525.75881183150*t);\r
+   jupiter_x_0+=     0.00011996271 * cos( 0.58568573729 +      533.62311835770*t);\r
+   jupiter_x_0+=     0.00008468556 * cos( 3.47248751739 +      639.89728631400*t);\r
+   jupiter_x_0+=     0.00008223302 * cos( 5.56680447143 +     1478.86657406440*t);\r
+   jupiter_x_0+=     0.00008694124 * cos( 0.38262009411 +     1692.16566950240*t);\r
+   jupiter_x_0+=     0.00007427517 * cos( 5.98380751196 +      956.28915597060*t);\r
+   jupiter_x_0+=     0.00007516470 * cos( 0.92896448412 +     1265.56747862640*t);\r
+   jupiter_x_0+=     0.00007655867 * cos( 0.14178789086 +     1581.95934828300*t);\r
+   jupiter_x_0+=     0.00005318791 * cos( 1.10494016349 +      526.50957135690*t);\r
+   jupiter_x_0+=     0.00005218492 * cos( 3.23235129224 +      532.87235883230*t);\r
+   jupiter_x_0+=     0.00005777311 * cos( 5.03726165628 +       14.22709400160*t);\r
+   jupiter_x_0+=     0.00004622685 * cos( 3.75817086099 +     1375.77379984580*t);\r
+   jupiter_x_0+=     0.00003939864 * cos( 4.30892687511 +     1596.18644228460*t);\r
+   jupiter_x_0+=     0.00004569444 * cos( 2.15087281710 +       95.97922721780*t);\r
+   jupiter_x_0+=     0.00002952712 * cos( 3.85988483947 +      309.27832265580*t);\r
+   jupiter_x_0+=     0.00002857935 * cos( 6.01118473739 +      117.31986822020*t);\r
+   jupiter_x_0+=     0.00002440094 * cos( 4.23995765702 +      433.71173787680*t);\r
+   jupiter_x_0+=     0.00002438257 * cos( 3.88808463822 +      220.41264243880*t);\r
+   jupiter_x_0+=     0.00002675112 * cos( 3.18723449094 +     1169.58825140860*t);\r
+   jupiter_x_0+=     0.00002386425 * cos( 5.96354994324 +     1045.15483618760*t);\r
+   jupiter_x_0+=     0.00001870097 * cos( 0.52019313301 +     1155.36115740700*t);\r
+   jupiter_x_0+=     0.00001939060 * cos( 5.91883412864 +      625.67019231240*t);\r
+   jupiter_x_0+=     0.00001631500 * cos( 4.41910383466 +      942.06206196900*t);\r
+   jupiter_x_0+=     0.00001451667 * cos( 5.76112706040 +      853.19638175200*t);\r
+   jupiter_x_0+=     0.00001361286 * cos( 1.34792748837 +     1368.66025284500*t);\r
+   jupiter_x_0+=     0.00001663331 * cos( 1.94010629194 +      838.96928775040*t);\r
+   jupiter_x_0+=     0.00001611229 * cos( 5.49324974845 +       74.78159856730*t);\r
+   jupiter_x_0+=     0.00001033570 * cos( 0.08907208789 +     1795.25844372100*t);\r
+   jupiter_x_0+=     0.00001088284 * cos( 1.13406104190 +      527.24328453980*t);\r
+   jupiter_x_0+=     0.00001080643 * cos( 3.20528362573 +      532.13864564940*t);\r
+\r
+   double jupiter_x_1=0.0;\r
+   jupiter_x_1+=     0.00882389251 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_x_1+=     0.00635297172 * cos( 0.10662156868 +     1059.38193018920*t);\r
+   jupiter_x_1+=     0.00599720482 * cos( 2.42996678275 +      522.57741809380*t);\r
+   jupiter_x_1+=     0.00589157060 * cos( 1.91556314637 +      536.80451209540*t);\r
+   jupiter_x_1+=     0.00081697204 * cos( 3.46668108797 +        7.11354700080*t);\r
+   jupiter_x_1+=     0.00046201898 * cos( 0.45714214032 +     1589.07289528380*t);\r
+   jupiter_x_1+=     0.00032508590 * cos( 1.74648849928 +     1052.26838318840*t);\r
+   jupiter_x_1+=     0.00033891193 * cos( 4.10113482752 +      529.69096509460*t);\r
+   jupiter_x_1+=     0.00031234303 * cos( 2.34698051502 +     1066.49547719000*t);\r
+   jupiter_x_1+=     0.00021244363 * cos( 4.36576178953 +      110.20632121940*t);\r
+   jupiter_x_1+=     0.00018156701 * cos( 4.00572238779 +      426.59819087600*t);\r
+   jupiter_x_1+=     0.00013577576 * cos( 0.30008010246 +      632.78373931320*t);\r
+   jupiter_x_1+=     0.00012889505 * cos( 2.57489294062 +      515.46387109300*t);\r
+   jupiter_x_1+=     0.00009125875 * cos( 1.78082469962 +      543.91805909620*t);\r
+   jupiter_x_1+=     0.00008085991 * cos( 6.16136518902 +      949.17560896980*t);\r
+   jupiter_x_1+=     0.00007142547 * cos( 3.17267801203 +      323.50541665740*t);\r
+   jupiter_x_1+=     0.00004292240 * cos( 4.74970626655 +      206.18554843720*t);\r
+   jupiter_x_1+=     0.00004393977 * cos( 1.14770788063 +      735.87651353180*t);\r
+   jupiter_x_1+=     0.00003399164 * cos( 2.90091450747 +      526.50957135690*t);\r
+   jupiter_x_1+=     0.00003333344 * cos( 1.43691652967 +      532.87235883230*t);\r
+   jupiter_x_1+=     0.00003873467 * cos( 3.33648870101 +       14.22709400160*t);\r
+   jupiter_x_1+=     0.00003044408 * cos( 1.65428048669 +      525.75881183150*t);\r
+   jupiter_x_1+=     0.00003001874 * cos( 2.68376982746 +      533.62311835770*t);\r
+   jupiter_x_1+=     0.00002933359 * cos( 2.61899855005 +      419.48464387520*t);\r
+   jupiter_x_1+=     0.00002438199 * cos( 3.60655644537 +      316.39186965660*t);\r
+   jupiter_x_1+=     0.00002804218 * cos( 4.89742591320 +      103.09277421860*t);\r
+   jupiter_x_1+=     0.00002990245 * cos( 0.80692155639 +     2118.76386037840*t);\r
+   jupiter_x_1+=     0.00001977572 * cos( 5.08915489088 +      956.28915597060*t);\r
+   jupiter_x_1+=     0.00001853679 * cos( 2.76941001747 +     1596.18644228460*t);\r
+   jupiter_x_1+=     0.00001772800 * cos( 0.72631739446 +      742.99006053260*t);\r
+   jupiter_x_1+=     0.00001812965 * cos( 3.84602148747 +       95.97922721780*t);\r
+   jupiter_x_1+=     0.00001532945 * cos( 4.31556714501 +      117.31986822020*t);\r
+   jupiter_x_1+=     0.00001904067 * cos( 1.85937873703 +     1581.95934828300*t);\r
+   jupiter_x_1+=     0.00001539212 * cos( 1.47899172821 +      639.89728631400*t);\r
+   jupiter_x_1+=     0.00001632362 * cos( 1.41504212408 +     1045.15483618760*t);\r
+   jupiter_x_1+=     0.00001023812 * cos( 2.57182697715 +      433.71173787680*t);\r
+   jupiter_x_1+=     0.00001055422 * cos( 2.50844222977 +     1265.56747862640*t);\r
+   jupiter_x_1=jupiter_x_1 * t;\r
+\r
+   double jupiter_x_2=0.0;\r
+   jupiter_x_2+=     0.00123864644 * cos( 4.13563277513 +      522.57741809380*t);\r
+   jupiter_x_2+=     0.00121521296 * cos( 0.21155109275 +      536.80451209540*t);\r
+   jupiter_x_2+=     0.00085355503 * cos( 0.00000000000 +        0.00000000000*t);\r
+   jupiter_x_2+=     0.00077685547 * cos( 5.29776154458 +      529.69096509460*t);\r
+   jupiter_x_2+=     0.00041410887 * cos( 5.12291589939 +     1059.38193018920*t);\r
+   jupiter_x_2+=     0.00011423070 * cos( 1.72917878238 +        7.11354700080*t);\r
+   jupiter_x_2+=     0.00007051587 * cos( 0.74163703419 +     1066.49547719000*t);\r
+   jupiter_x_2+=     0.00005711029 * cos( 3.63172846494 +     1052.26838318840*t);\r
+   jupiter_x_2+=     0.00005242644 * cos( 4.27482379441 +      515.46387109300*t);\r
+   jupiter_x_2+=     0.00004039540 * cos( 5.58417732117 +     1589.07289528380*t);\r
+   jupiter_x_2+=     0.00003706457 * cos( 0.07769981349 +      543.91805909620*t);\r
+   jupiter_x_2+=     0.00001698817 * cos( 2.44284418066 +      110.20632121940*t);\r
+   jupiter_x_2+=     0.00001134598 * cos( 2.35807061809 +      426.59819087600*t);\r
+   jupiter_x_2+=     0.00001322673 * cos( 1.63142549980 +       14.22709400160*t);\r
+   jupiter_x_2=jupiter_x_2 * t * t;\r
+\r
+   double jupiter_x_3=0.0;\r
+   jupiter_x_3+=     0.00017071323 * cos( 5.86133022278 +      522.57741809380*t);\r
+   jupiter_x_3+=     0.00016713548 * cos( 4.77458794485 +      536.80451209540*t);\r
+   jupiter_x_3+=     0.00003348610 * cos( 0.00000000000 +        0.00000000000*t);\r
+   jupiter_x_3+=     0.00001787838 * cos( 3.56550298031 +     1059.38193018920*t);\r
+   jupiter_x_3+=     0.00001435449 * cos( 5.98502036587 +      515.46387109300*t);\r
+   jupiter_x_3+=     0.00001080194 * cos( 5.42530305914 +     1066.49547719000*t);\r
+   jupiter_x_3+=     0.00001014206 * cos( 4.64773902077 +      543.91805909620*t);\r
+   jupiter_x_3+=     0.00001073175 * cos( 6.22314467964 +        7.11354700080*t);\r
+   jupiter_x_3=jupiter_x_3 * t * t * t;\r
+\r
+   double jupiter_x_4=0.0;\r
+   jupiter_x_4+=     0.00001762402 * cos( 1.32863039757 +      522.57741809380*t);\r
+   jupiter_x_4+=     0.00001717846 * cos( 3.03331531843 +      536.80451209540*t);\r
+   jupiter_x_4=jupiter_x_4 * t * t * t * t;\r
+\r
+   return jupiter_x_0+jupiter_x_1+jupiter_x_2+jupiter_x_3+jupiter_x_4;\r
+}\r
+\r
+double vsop87a_milli_jupiter_y(double t){\r
+   double jupiter_y_0=0.0;\r
+   jupiter_y_0+=     5.19520046589 * cos( 5.31203162731 +      529.69096509460*t);\r
+   jupiter_y_0+=     0.12592862602 * cos( 5.66160227728 +     1059.38193018920*t);\r
+   jupiter_y_0+=     0.09363670616 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_y_0+=     0.01508275299 * cos( 5.43934968102 +      522.57741809380*t);\r
+   jupiter_y_0+=     0.01475809370 * cos( 2.04679566495 +      536.80451209540*t);\r
+   jupiter_y_0+=     0.00457750806 * cos( 6.01129093501 +     1589.07289528380*t);\r
+   jupiter_y_0+=     0.00300686679 * cos( 3.60948050740 +        7.11354700080*t);\r
+   jupiter_y_0+=     0.00378285578 * cos( 3.53006782383 +      103.09277421860*t);\r
+   jupiter_y_0+=     0.00192333128 * cos( 3.45690564771 +      426.59819087600*t);\r
+   jupiter_y_0+=     0.00146104656 * cos( 4.62267224431 +      110.20632121940*t);\r
+   jupiter_y_0+=     0.00139480058 * cos( 4.00075307706 +      632.78373931320*t);\r
+   jupiter_y_0+=     0.00132696764 * cos( 5.62184581859 +      213.29909543800*t);\r
+   jupiter_y_0+=     0.00101999807 * cos( 4.57594598884 +     1052.26838318840*t);\r
+   jupiter_y_0+=     0.00114043110 * cos( 4.72982262969 +     1162.47470440780*t);\r
+   jupiter_y_0+=     0.00072091178 * cos( 2.39048659148 +     1066.49547719000*t);\r
+   jupiter_y_0+=     0.00059051769 * cos( 2.89529070968 +      949.17560896980*t);\r
+   jupiter_y_0+=     0.00068374489 * cos( 1.86537074374 +      846.08283475120*t);\r
+   jupiter_y_0+=     0.00029807369 * cos( 4.52105772740 +      206.18554843720*t);\r
+   jupiter_y_0+=     0.00026933579 * cos( 3.86233956827 +      419.48464387520*t);\r
+   jupiter_y_0+=     0.00026619714 * cos( 3.28203174951 +      323.50541665740*t);\r
+   jupiter_y_0+=     0.00020873780 * cos( 3.79369881757 +      735.87651353180*t);\r
+   jupiter_y_0+=     0.00019727397 * cos( 0.07818534532 +     2118.76386037840*t);\r
+   jupiter_y_0+=     0.00018639846 * cos( 0.38751972138 +      316.39186965660*t);\r
+   jupiter_y_0+=     0.00016355726 * cos( 5.56997881604 +      515.46387109300*t);\r
+   jupiter_y_0+=     0.00014606858 * cos( 0.47759399145 +      742.99006053260*t);\r
+   jupiter_y_0+=     0.00011419853 * cos( 1.91089341468 +      543.91805909620*t);\r
+   jupiter_y_0+=     0.00012153427 * cos( 2.18151972499 +      525.75881183150*t);\r
+   jupiter_y_0+=     0.00011988875 * cos( 5.29687602089 +      533.62311835770*t);\r
+   jupiter_y_0+=     0.00008443107 * cos( 1.91435801697 +      639.89728631400*t);\r
+   jupiter_y_0+=     0.00008163163 * cos( 4.00303742375 +     1478.86657406440*t);\r
+   jupiter_y_0+=     0.00008732789 * cos( 5.09607066097 +     1692.16566950240*t);\r
+   jupiter_y_0+=     0.00007414115 * cos( 4.41141990461 +      956.28915597060*t);\r
+   jupiter_y_0+=     0.00007619486 * cos( 5.59554151997 +     1265.56747862640*t);\r
+   jupiter_y_0+=     0.00007779184 * cos( 4.83346300662 +     1581.95934828300*t);\r
+   jupiter_y_0+=     0.00005322882 * cos( 5.81740472645 +      526.50957135690*t);\r
+   jupiter_y_0+=     0.00005217025 * cos( 1.66178643542 +      532.87235883230*t);\r
+   jupiter_y_0+=     0.00005772132 * cos( 3.46915716927 +       14.22709400160*t);\r
+   jupiter_y_0+=     0.00004528355 * cos( 2.18377558038 +     1375.77379984580*t);\r
+   jupiter_y_0+=     0.00003939875 * cos( 2.73830531054 +     1596.18644228460*t);\r
+   jupiter_y_0+=     0.00004567181 * cos( 3.71300776935 +       95.97922721780*t);\r
+   jupiter_y_0+=     0.00003235419 * cos( 4.76600347062 +      625.67019231240*t);\r
+   jupiter_y_0+=     0.00003140740 * cos( 5.59566796922 +      309.27832265580*t);\r
+   jupiter_y_0+=     0.00002855423 * cos( 4.44478286006 +      117.31986822020*t);\r
+   jupiter_y_0+=     0.00002445625 * cos( 2.67036952230 +      433.71173787680*t);\r
+   jupiter_y_0+=     0.00002253545 * cos( 4.28462825722 +      838.96928775040*t);\r
+   jupiter_y_0+=     0.00002672262 * cos( 1.61857897069 +     1169.58825140860*t);\r
+   jupiter_y_0+=     0.00002423639 * cos( 2.32942339839 +      220.41264243880*t);\r
+   jupiter_y_0+=     0.00002362662 * cos( 4.60417580207 +     1155.36115740700*t);\r
+   jupiter_y_0+=     0.00002409581 * cos( 4.33196301609 +     1045.15483618760*t);\r
+   jupiter_y_0+=     0.00001458169 * cos( 4.18761881277 +      853.19638175200*t);\r
+   jupiter_y_0+=     0.00001432195 * cos( 3.24824554500 +      942.06206196900*t);\r
+   jupiter_y_0+=     0.00001646568 * cos( 3.91965876562 +       74.78159856730*t);\r
+   jupiter_y_0+=     0.00001050270 * cos( 4.83706014327 +     1795.25844372100*t);\r
+   jupiter_y_0+=     0.00001002355 * cos( 1.50931939870 +     1272.68102562720*t);\r
+   jupiter_y_0+=     0.00001087727 * cos( 5.84673086939 +      527.24328453980*t);\r
+   jupiter_y_0+=     0.00001079512 * cos( 1.63448507346 +      532.13864564940*t);\r
+\r
+   double jupiter_y_1=0.0;\r
+   jupiter_y_1+=     0.01694798253 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_y_1+=     0.00634859798 * cos( 4.81903199650 +     1059.38193018920*t);\r
+   jupiter_y_1+=     0.00601160431 * cos( 0.85811249940 +      522.57741809380*t);\r
+   jupiter_y_1+=     0.00588928504 * cos( 0.34491576890 +      536.80451209540*t);\r
+   jupiter_y_1+=     0.00081187145 * cos( 1.90914316532 +        7.11354700080*t);\r
+   jupiter_y_1+=     0.00046888090 * cos( 1.91294535618 +      529.69096509460*t);\r
+   jupiter_y_1+=     0.00046194129 * cos( 5.16955994561 +     1589.07289528380*t);\r
+   jupiter_y_1+=     0.00032503453 * cos( 0.17640743623 +     1052.26838318840*t);\r
+   jupiter_y_1+=     0.00031231694 * cos( 0.77623645597 +     1066.49547719000*t);\r
+   jupiter_y_1+=     0.00019462096 * cos( 3.00957119470 +      110.20632121940*t);\r
+   jupiter_y_1+=     0.00017738615 * cos( 2.46531787101 +      426.59819087600*t);\r
+   jupiter_y_1+=     0.00013701692 * cos( 5.02070197804 +      632.78373931320*t);\r
+   jupiter_y_1+=     0.00013034616 * cos( 0.98979834442 +      515.46387109300*t);\r
+   jupiter_y_1+=     0.00009122660 * cos( 0.21022587969 +      543.91805909620*t);\r
+   jupiter_y_1+=     0.00008109050 * cos( 4.58123811601 +      949.17560896980*t);\r
+   jupiter_y_1+=     0.00007145229 * cos( 1.60381236094 +      323.50541665740*t);\r
+   jupiter_y_1+=     0.00003957592 * cos( 6.18550697817 +      206.18554843720*t);\r
+   jupiter_y_1+=     0.00004347346 * cos( 5.85522835488 +      735.87651353180*t);\r
+   jupiter_y_1+=     0.00003401735 * cos( 1.33033225252 +      526.50957135690*t);\r
+   jupiter_y_1+=     0.00003331887 * cos( 6.14951835712 +      532.87235883230*t);\r
+   jupiter_y_1+=     0.00003866147 * cos( 1.76877582038 +       14.22709400160*t);\r
+   jupiter_y_1+=     0.00003094257 * cos( 1.00670454701 +      419.48464387520*t);\r
+   jupiter_y_1+=     0.00003044205 * cos( 0.08329779827 +      525.75881183150*t);\r
+   jupiter_y_1+=     0.00003001484 * cos( 1.11280606283 +      533.62311835770*t);\r
+   jupiter_y_1+=     0.00002977284 * cos( 3.35507028507 +      103.09277421860*t);\r
+   jupiter_y_1+=     0.00002347100 * cos( 2.06781775390 +      316.39186965660*t);\r
+   jupiter_y_1+=     0.00002990192 * cos( 5.51944830506 +     2118.76386037840*t);\r
+   jupiter_y_1+=     0.00001875464 * cos( 5.32657356489 +      742.99006053260*t);\r
+   jupiter_y_1+=     0.00001854067 * cos( 1.19908734197 +     1596.18644228460*t);\r
+   jupiter_y_1+=     0.00001968401 * cos( 3.51896739844 +      956.28915597060*t);\r
+   jupiter_y_1+=     0.00001808627 * cos( 5.40287543026 +       95.97922721780*t);\r
+   jupiter_y_1+=     0.00001530472 * cos( 2.75094722237 +      117.31986822020*t);\r
+   jupiter_y_1+=     0.00001885393 * cos( 0.29905973710 +     1581.95934828300*t);\r
+   jupiter_y_1+=     0.00001516541 * cos( 6.21684203571 +      639.89728631400*t);\r
+   jupiter_y_1+=     0.00001636913 * cos( 6.09270756447 +     1045.15483618760*t);\r
+   jupiter_y_1+=     0.00001260123 * cos( 0.07143173954 +      625.67019231240*t);\r
+   jupiter_y_1+=     0.00001028165 * cos( 1.00301485824 +      433.71173787680*t);\r
+   jupiter_y_1+=     0.00001035933 * cos( 0.98273794152 +     1265.56747862640*t);\r
+   jupiter_y_1=jupiter_y_1 * t;\r
+\r
+   double jupiter_y_2=0.0;\r
+   jupiter_y_2+=     0.00124032509 * cos( 2.56495576833 +      522.57741809380*t);\r
+   jupiter_y_2+=     0.00121455991 * cos( 4.92398766380 +      536.80451209540*t);\r
+   jupiter_y_2+=     0.00076523263 * cos( 3.75913371793 +      529.69096509460*t);\r
+   jupiter_y_2+=     0.00076943042 * cos( 3.14159265359 +        0.00000000000*t);\r
+   jupiter_y_2+=     0.00041357600 * cos( 3.55228440457 +     1059.38193018920*t);\r
+   jupiter_y_2+=     0.00011277667 * cos( 0.18559902389 +        7.11354700080*t);\r
+   jupiter_y_2+=     0.00007051103 * cos( 5.45404368570 +     1066.49547719000*t);\r
+   jupiter_y_2+=     0.00005719440 * cos( 2.05970000230 +     1052.26838318840*t);\r
+   jupiter_y_2+=     0.00005286157 * cos( 2.69490465064 +      515.46387109300*t);\r
+   jupiter_y_2+=     0.00004039038 * cos( 4.01341034637 +     1589.07289528380*t);\r
+   jupiter_y_2+=     0.00003704528 * cos( 4.79029292271 +      543.91805909620*t);\r
+   jupiter_y_2+=     0.00001280283 * cos( 1.47574006861 +      110.20632121940*t);\r
+   jupiter_y_2+=     0.00001059783 * cos( 0.89610748176 +      426.59819087600*t);\r
+   jupiter_y_2+=     0.00001320627 * cos( 0.05786048417 +       14.22709400160*t);\r
+   jupiter_y_2=jupiter_y_2 * t * t;\r
+\r
+   double jupiter_y_3=0.0;\r
+   jupiter_y_3+=     0.00017085516 * cos( 4.29096904063 +      522.57741809380*t);\r
+   jupiter_y_3+=     0.00016701353 * cos( 3.20365737109 +      536.80451209540*t);\r
+   jupiter_y_3+=     0.00004006038 * cos( 0.00000000000 +        0.00000000000*t);\r
+   jupiter_y_3+=     0.00001782451 * cos( 1.99283071153 +     1059.38193018920*t);\r
+   jupiter_y_3+=     0.00001443816 * cos( 4.40866555269 +      515.46387109300*t);\r
+   jupiter_y_3+=     0.00001079405 * cos( 3.85450799252 +     1066.49547719000*t);\r
+   jupiter_y_3+=     0.00001013157 * cos( 3.07729621279 +      543.91805909620*t);\r
+   jupiter_y_3+=     0.00001055565 * cos( 4.70184773789 +        7.11354700080*t);\r
+   jupiter_y_3=jupiter_y_3 * t * t * t;\r
+\r
+   double jupiter_y_4=0.0;\r
+   jupiter_y_4+=     0.00001762645 * cos( 6.04159386554 +      522.57741809380*t);\r
+   jupiter_y_4+=     0.00001716045 * cos( 1.46206285710 +      536.80451209540*t);\r
+   jupiter_y_4=jupiter_y_4 * t * t * t * t;\r
+\r
+   return jupiter_y_0+jupiter_y_1+jupiter_y_2+jupiter_y_3+jupiter_y_4;\r
+}\r
+\r
+double vsop87a_milli_jupiter_z(double t){\r
+   double jupiter_z_0=0.0;\r
+   jupiter_z_0+=     0.11823100489 * cos( 3.55844646343 +      529.69096509460*t);\r
+   jupiter_z_0+=     0.00859031952 * cos( 0.00000000000 +        0.00000000000*t);\r
+   jupiter_z_0+=     0.00286562094 * cos( 3.90812238338 +     1059.38193018920*t);\r
+   jupiter_z_0+=     0.00042388592 * cos( 3.60144191032 +      522.57741809380*t);\r
+   jupiter_z_0+=     0.00033295491 * cos( 0.30297050585 +      536.80451209540*t);\r
+   jupiter_z_0+=     0.00010416160 * cos( 4.25764593061 +     1589.07289528380*t);\r
+   jupiter_z_0+=     0.00007449294 * cos( 5.24213104150 +      103.09277421860*t);\r
+   jupiter_z_0+=     0.00006910102 * cos( 1.75032945752 +        7.11354700080*t);\r
+   jupiter_z_0+=     0.00005292012 * cos( 1.68231447192 +      426.59819087600*t);\r
+   jupiter_z_0+=     0.00004313598 * cos( 3.70673689841 +      213.29909543800*t);\r
+   jupiter_z_0+=     0.00003784265 * cos( 2.71522544491 +      110.20632121940*t);\r
+   jupiter_z_0+=     0.00003798016 * cos( 2.16715743175 +      632.78373931320*t);\r
+   jupiter_z_0+=     0.00002455385 * cos( 2.96904135659 +     1052.26838318840*t);\r
+   jupiter_z_0+=     0.00002461547 * cos( 2.99889460411 +     1162.47470440780*t);\r
+   jupiter_z_0+=     0.00002001451 * cos( 2.68535838309 +      419.48464387520*t);\r
+   jupiter_z_0+=     0.00002163471 * cos( 6.26718259854 +      846.08283475120*t);\r
+   jupiter_z_0+=     0.00001633653 * cos( 0.64194743493 +     1066.49547719000*t);\r
+   jupiter_z_0+=     0.00001450672 * cos( 1.17108416193 +      949.17560896980*t);\r
+\r
+   double jupiter_z_1=0.0;\r
+   jupiter_z_1+=     0.00407072175 * cos( 1.52699353482 +      529.69096509460*t);\r
+   jupiter_z_1+=     0.00020307341 * cos( 2.59878269248 +     1059.38193018920*t);\r
+   jupiter_z_1+=     0.00014424953 * cos( 4.85400155025 +      536.80451209540*t);\r
+   jupiter_z_1+=     0.00015474611 * cos( 0.00000000000 +        0.00000000000*t);\r
+   jupiter_z_1+=     0.00012730364 * cos( 5.45536715732 +      522.57741809380*t);\r
+   jupiter_z_1+=     0.00002100882 * cos( 0.09538864287 +        7.11354700080*t);\r
+   jupiter_z_1+=     0.00001230425 * cos( 3.14222500244 +     1589.07289528380*t);\r
+   jupiter_z_1=jupiter_z_1 * t;\r
+\r
+   double jupiter_z_2=0.0;\r
+   jupiter_z_2+=     0.00028635326 * cos( 3.01374166973 +      529.69096509460*t);\r
+   jupiter_z_2+=     0.00003114752 * cos( 3.13228646176 +      536.80451209540*t);\r
+   jupiter_z_2+=     0.00002379765 * cos( 0.95574345340 +      522.57741809380*t);\r
+   jupiter_z_2+=     0.00001310111 * cos( 2.05263704913 +     1059.38193018920*t);\r
+   jupiter_z_2=jupiter_z_2 * t * t;\r
+\r
+   return jupiter_z_0+jupiter_z_1+jupiter_z_2;\r
+}\r
+\r
+double vsop87a_milli_mars_x(double t){\r
+   double mars_x_0=0.0;\r
+   mars_x_0+=     1.51769936383 * cos( 6.20403346548 +     3340.61242669980*t);\r
+   mars_x_0+=     0.19502945246 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_x_0+=     0.07070919655 * cos( 0.25870338558 +     6681.22485339960*t);\r
+   mars_x_0+=     0.00494196914 * cos( 0.59669127768 +    10021.83728009940*t);\r
+   mars_x_0+=     0.00040938237 * cos( 0.93473307419 +    13362.44970679920*t);\r
+   mars_x_0+=     0.00021067199 * cos( 1.80435656154 +     3337.08930835080*t);\r
+   mars_x_0+=     0.00021041626 * cos( 1.17895619474 +     3344.13554504880*t);\r
+   mars_x_0+=     0.00011370375 * cos( 4.83265211109 +     1059.38193018920*t);\r
+   mars_x_0+=     0.00013527976 * cos( 0.63010765169 +      529.69096509460*t);\r
+   mars_x_0+=     0.00006774107 * cos( 3.61785048282 +     3340.59517304760*t);\r
+   mars_x_0+=     0.00006774060 * cos( 5.64862211431 +     3340.62968035200*t);\r
+   mars_x_0+=     0.00008226069 * cos( 1.86843519535 +     2281.23049651060*t);\r
+   mars_x_0+=     0.00005469046 * cos( 1.13324429003 +     2942.46342329160*t);\r
+   mars_x_0+=     0.00004817134 * cos( 1.85091045536 +     3738.76143010800*t);\r
+   mars_x_0+=     0.00004937579 * cos( 4.43241440654 +     5621.84292321040*t);\r
+   mars_x_0+=     0.00005276260 * cos( 2.33148083116 +     6151.53388830500*t);\r
+   mars_x_0+=     0.00003636667 * cos( 6.11397592106 +      796.29800681640*t);\r
+   mars_x_0+=     0.00003725823 * cos( 1.27280182943 +    16703.06213349900*t);\r
+   mars_x_0+=     0.00003729746 * cos( 1.21398323637 +      398.14900340820*t);\r
+   mars_x_0+=     0.00002368513 * cos( 2.96841895360 +     2544.31441988340*t);\r
+   mars_x_0+=     0.00002397865 * cos( 0.63553674054 +     3149.16416058820*t);\r
+   mars_x_0+=     0.00002274646 * cos( 2.35708328853 +     3532.06069281140*t);\r
+   mars_x_0+=     0.00001977579 * cos( 2.14087826110 +     6677.70173505060*t);\r
+   mars_x_0+=     0.00002229176 * cos( 1.69588962513 +     3340.54511639700*t);\r
+   mars_x_0+=     0.00002229117 * cos( 1.28739323821 +     3340.67973700260*t);\r
+   mars_x_0+=     0.00002182206 * cos( 1.69655112969 +     6283.07584999140*t);\r
+   mars_x_0+=     0.00002241010 * cos( 4.82218655311 +     8962.45534991020*t);\r
+   mars_x_0+=     0.00001677693 * cos( 3.14442612046 +     5884.92684658320*t);\r
+   mars_x_0+=     0.00001630482 * cos( 0.24117974845 +     4136.91043351620*t);\r
+   mars_x_0+=     0.00001958162 * cos( 1.51914544555 +     6684.74797174860*t);\r
+   mars_x_0+=     0.00001378470 * cos( 2.18011900021 +     1751.53953141600*t);\r
+   mars_x_0+=     0.00001289804 * cos( 4.70970778621 +     1194.44701022460*t);\r
+   mars_x_0+=     0.00001468124 * cos( 1.87869730543 +     3870.30339179440*t);\r
+   mars_x_0+=     0.00001290170 * cos( 0.43596325296 +     2810.92146160520*t);\r
+   mars_x_0+=     0.00001572540 * cos( 4.84809921789 +     1589.07289528380*t);\r
+\r
+   double mars_x_1=0.0;\r
+   mars_x_1+=     0.00861441374 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_x_1+=     0.00552437949 * cos( 5.09565872891 +     6681.22485339960*t);\r
+   mars_x_1+=     0.00077184977 * cos( 5.43315636209 +    10021.83728009940*t);\r
+   mars_x_1+=     0.00020467294 * cos( 5.57051812369 +     3340.61242669980*t);\r
+   mars_x_1+=     0.00009589581 * cos( 5.77107234791 +    13362.44970679920*t);\r
+   mars_x_1+=     0.00002620610 * cos( 6.22441295122 +     3344.13554504880*t);\r
+   mars_x_1+=     0.00002620537 * cos( 3.04172154436 +     3337.08930835080*t);\r
+   mars_x_1+=     0.00001163612 * cos( 6.10909257097 +    16703.06213349900*t);\r
+   mars_x_1=mars_x_1 * t;\r
+\r
+   double mars_x_2=0.0;\r
+   mars_x_2+=     0.00056323939 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mars_x_2+=     0.00022122528 * cos( 3.54372113272 +     6681.22485339960*t);\r
+   mars_x_2+=     0.00006091409 * cos( 3.93272649649 +    10021.83728009940*t);\r
+   mars_x_2+=     0.00001451998 * cos( 3.64655666460 +     3340.61242669980*t);\r
+   mars_x_2+=     0.00001130613 * cos( 4.28827023222 +    13362.44970679920*t);\r
+   mars_x_2=mars_x_2 * t * t;\r
+\r
+   return mars_x_0+mars_x_1+mars_x_2;\r
+}\r
+\r
+double vsop87a_milli_mars_y(double t){\r
+   double mars_y_0=0.0;\r
+   mars_y_0+=     1.51558976277 * cos( 4.63212206588 +     3340.61242669980*t);\r
+   mars_y_0+=     0.07064550239 * cos( 4.97051892902 +     6681.22485339960*t);\r
+   mars_y_0+=     0.08655481102 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mars_y_0+=     0.00493872848 * cos( 5.30877806694 +    10021.83728009940*t);\r
+   mars_y_0+=     0.00040917422 * cos( 5.64698263703 +    13362.44970679920*t);\r
+   mars_y_0+=     0.00021036784 * cos( 0.23240270955 +     3337.08930835080*t);\r
+   mars_y_0+=     0.00021012921 * cos( 5.89022773653 +     3344.13554504880*t);\r
+   mars_y_0+=     0.00011370034 * cos( 3.26131408801 +     1059.38193018920*t);\r
+   mars_y_0+=     0.00013324177 * cos( 5.34259389724 +      529.69096509460*t);\r
+   mars_y_0+=     0.00006764653 * cos( 4.07671230062 +     3340.62968035200*t);\r
+   mars_y_0+=     0.00006764700 * cos( 2.04594066912 +     3340.59517304760*t);\r
+   mars_y_0+=     0.00008346220 * cos( 3.42464704002 +     2281.23049651060*t);\r
+   mars_y_0+=     0.00005400042 * cos( 5.81507793194 +     2942.46342329160*t);\r
+   mars_y_0+=     0.00004809528 * cos( 0.27875310553 +     3738.76143010800*t);\r
+   mars_y_0+=     0.00004849523 * cos( 2.85190987550 +     5621.84292321040*t);\r
+   mars_y_0+=     0.00005263268 * cos( 0.75811089992 +     6151.53388830500*t);\r
+   mars_y_0+=     0.00003609527 * cos( 4.53244488294 +      796.29800681640*t);\r
+   mars_y_0+=     0.00003724293 * cos( 5.98516013322 +    16703.06213349900*t);\r
+   mars_y_0+=     0.00003805073 * cos( 5.94234296399 +      398.14900340820*t);\r
+   mars_y_0+=     0.00002394490 * cos( 5.34678816191 +     3149.16416058820*t);\r
+   mars_y_0+=     0.00002251027 * cos( 0.76938193892 +     3532.06069281140*t);\r
+   mars_y_0+=     0.00001975769 * cos( 0.56949816579 +     6677.70173505060*t);\r
+   mars_y_0+=     0.00002226030 * cos( 5.99867316288 +     3340.67973700260*t);\r
+   mars_y_0+=     0.00002226089 * cos( 0.12398424247 +     3340.54511639700*t);\r
+   mars_y_0+=     0.00002177591 * cos( 0.12334436516 +     6283.07584999140*t);\r
+   mars_y_0+=     0.00001690439 * cos( 1.58331163985 +     5884.92684658320*t);\r
+   mars_y_0+=     0.00002234121 * cos( 3.24909113765 +     8962.45534991020*t);\r
+   mars_y_0+=     0.00001628395 * cos( 4.95250906888 +     4136.91043351620*t);\r
+   mars_y_0+=     0.00001956411 * cos( 6.23095843554 +     6684.74797174860*t);\r
+   mars_y_0+=     0.00001697214 * cos( 0.81869636263 +     2544.31441988340*t);\r
+   mars_y_0+=     0.00001385946 * cos( 3.73437191158 +     1751.53953141600*t);\r
+   mars_y_0+=     0.00001439619 * cos( 5.19505958438 +     2810.92146160520*t);\r
+   mars_y_0+=     0.00001281890 * cos( 3.13035275682 +     1194.44701022460*t);\r
+   mars_y_0+=     0.00001469783 * cos( 0.30415060688 +     3870.30339179440*t);\r
+   mars_y_0+=     0.00001571880 * cos( 3.27679498650 +     1589.07289528380*t);\r
+   mars_y_0+=     0.00001575854 * cos( 2.78266835243 +     5092.15195811580*t);\r
+\r
+   double mars_y_1=0.0;\r
+   mars_y_1+=     0.01427324210 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_y_1+=     0.00551063753 * cos( 3.52128320402 +     6681.22485339960*t);\r
+   mars_y_1+=     0.00077091913 * cos( 3.86082685753 +    10021.83728009940*t);\r
+   mars_y_1+=     0.00037310491 * cos( 1.16016958445 +     3340.61242669980*t);\r
+   mars_y_1+=     0.00009582122 * cos( 4.19942354479 +    13362.44970679920*t);\r
+   mars_y_1+=     0.00002617695 * cos( 1.47284555520 +     3337.08930835080*t);\r
+   mars_y_1+=     0.00002611572 * cos( 4.65030772498 +     3344.13554504880*t);\r
+   mars_y_1+=     0.00001162955 * cos( 4.53778503576 +    16703.06213349900*t);\r
+   mars_y_1=mars_y_1 * t;\r
+\r
+   double mars_y_2=0.0;\r
+   mars_y_2+=     0.00035396765 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_y_2+=     0.00021950759 * cos( 1.96291594946 +     6681.22485339960*t);\r
+   mars_y_2+=     0.00006075990 * cos( 2.35864321001 +    10021.83728009940*t);\r
+   mars_y_2+=     0.00002571425 * cos( 5.64795745327 +     3340.61242669980*t);\r
+   mars_y_2+=     0.00001129099 * cos( 2.71576248963 +    13362.44970679920*t);\r
+   mars_y_2=mars_y_2 * t * t;\r
+\r
+   double mars_y_3=0.0;\r
+   mars_y_3+=     0.00001448778 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mars_y_3=mars_y_3 * t * t * t;\r
+\r
+   return mars_y_0+mars_y_1+mars_y_2+mars_y_3;\r
+}\r
+\r
+double vsop87a_milli_mars_z(double t){\r
+   double mars_z_0=0.0;\r
+   mars_z_0+=     0.04901207220 * cos( 3.76712324286 +     3340.61242669980*t);\r
+   mars_z_0+=     0.00660669541 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mars_z_0+=     0.00228333904 * cos( 4.10544022266 +     6681.22485339960*t);\r
+   mars_z_0+=     0.00015958402 * cos( 4.44367058261 +    10021.83728009940*t);\r
+   mars_z_0+=     0.00001321976 * cos( 4.78186604114 +    13362.44970679920*t);\r
+\r
+   double mars_z_1=0.0;\r
+   mars_z_1+=     0.00331842958 * cos( 6.05027773492 +     3340.61242669980*t);\r
+   mars_z_1+=     0.00047930411 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_z_1+=     0.00009896501 * cos( 1.61155844715 +     6681.22485339960*t);\r
+   mars_z_1+=     0.00001700147 * cos( 2.63703242065 +    10021.83728009940*t);\r
+   mars_z_1=mars_z_1 * t;\r
+\r
+   double mars_z_2=0.0;\r
+   mars_z_2+=     0.00013705360 * cos( 1.04212852598 +     3340.61242669980*t);\r
+   mars_z_2+=     0.00005931596 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mars_z_2=mars_z_2 * t * t;\r
+\r
+   return mars_z_0+mars_z_1+mars_z_2;\r
+}\r
+\r
+double vsop87a_milli_mercury_x(double t){\r
+   double mercury_x_0=0.0;\r
+   mercury_x_0+=     0.37546291728 * cos( 4.39651506942 +    26087.90314157420*t);\r
+   mercury_x_0+=     0.03825746672 * cos( 1.16485604339 +    52175.80628314840*t);\r
+   mercury_x_0+=     0.02625615963 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_x_0+=     0.00584261333 * cos( 4.21599394757 +    78263.70942472259*t);\r
+   mercury_x_0+=     0.00105716695 * cos( 0.98379033182 +   104351.61256629678*t);\r
+   mercury_x_0+=     0.00021011730 * cos( 4.03469353923 +   130439.51570787099*t);\r
+   mercury_x_0+=     0.00004433373 * cos( 0.80236674527 +   156527.41884944518*t);\r
+\r
+   double mercury_x_1=0.0;\r
+   mercury_x_1+=     0.00318848034 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mercury_x_1+=     0.00105289019 * cos( 5.91600475006 +    52175.80628314840*t);\r
+   mercury_x_1+=     0.00032316001 * cos( 2.68247273347 +    78263.70942472259*t);\r
+   mercury_x_1+=     0.00011992889 * cos( 5.81575112963 +    26087.90314157420*t);\r
+   mercury_x_1+=     0.00008783200 * cos( 5.73285747425 +   104351.61256629678*t);\r
+   mercury_x_1+=     0.00002329042 * cos( 2.50023793407 +   130439.51570787099*t);\r
+   mercury_x_1=mercury_x_1 * t;\r
+\r
+   double mercury_x_2=0.0;\r
+   mercury_x_2+=     0.00001484185 * cos( 4.35401210269 +    52175.80628314840*t);\r
+   mercury_x_2+=     0.00001214995 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mercury_x_2=mercury_x_2 * t * t;\r
+\r
+   return mercury_x_0+mercury_x_1+mercury_x_2;\r
+}\r
+\r
+double vsop87a_milli_mercury_y(double t){\r
+   double mercury_y_0=0.0;\r
+   mercury_y_0+=     0.37953642888 * cos( 2.83780617820 +    26087.90314157420*t);\r
+   mercury_y_0+=     0.11626131831 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_y_0+=     0.03854668215 * cos( 5.88780608966 +    52175.80628314840*t);\r
+   mercury_y_0+=     0.00587711268 * cos( 2.65498896201 +    78263.70942472259*t);\r
+   mercury_y_0+=     0.00106235493 * cos( 5.70550616735 +   104351.61256629678*t);\r
+   mercury_y_0+=     0.00021100828 * cos( 2.47291315849 +   130439.51570787099*t);\r
+   mercury_y_0+=     0.00004450056 * cos( 5.52354907071 +   156527.41884944518*t);\r
+\r
+   double mercury_y_1=0.0;\r
+   mercury_y_1+=     0.00107803852 * cos( 4.34964793883 +    52175.80628314840*t);\r
+   mercury_y_1+=     0.00080651544 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_y_1+=     0.00032715354 * cos( 1.11763734425 +    78263.70942472259*t);\r
+   mercury_y_1+=     0.00008858158 * cos( 4.16852401867 +   104351.61256629678*t);\r
+   mercury_y_1+=     0.00011914709 * cos( 1.22139986340 +    26087.90314157420*t);\r
+   mercury_y_1+=     0.00002344469 * cos( 0.93615372641 +   130439.51570787099*t);\r
+   mercury_y_1=mercury_y_1 * t;\r
+\r
+   double mercury_y_2=0.0;\r
+   mercury_y_2+=     0.00004612157 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mercury_y_2+=     0.00001575670 * cos( 2.81172733349 +    52175.80628314840*t);\r
+   mercury_y_2=mercury_y_2 * t * t;\r
+\r
+   return mercury_y_0+mercury_y_1+mercury_y_2;\r
+}\r
+\r
+double vsop87a_milli_mercury_z(double t){\r
+   double mercury_z_0=0.0;\r
+   mercury_z_0+=     0.04607665326 * cos( 1.99295081967 +    26087.90314157420*t);\r
+   mercury_z_0+=     0.00708734365 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_z_0+=     0.00469171617 * cos( 5.04215742764 +    52175.80628314840*t);\r
+   mercury_z_0+=     0.00071626395 * cos( 1.80894256071 +    78263.70942472259*t);\r
+   mercury_z_0+=     0.00012957446 * cos( 4.85922032010 +   104351.61256629678*t);\r
+   mercury_z_0+=     0.00002575002 * cos( 1.62646731545 +   130439.51570787099*t);\r
+\r
+   double mercury_z_1=0.0;\r
+   mercury_z_1+=     0.00108722177 * cos( 3.91134750825 +    26087.90314157420*t);\r
+   mercury_z_1+=     0.00057826621 * cos( 3.14159265359 +        0.00000000000*t);\r
+   mercury_z_1+=     0.00004297352 * cos( 2.56373047177 +    52175.80628314840*t);\r
+   mercury_z_1+=     0.00002435833 * cos( 0.05112640506 +    78263.70942472259*t);\r
+   mercury_z_1=mercury_z_1 * t;\r
+\r
+   double mercury_z_2=0.0;\r
+   mercury_z_2+=     0.00001053118 * cos( 5.37979214357 +    26087.90314157420*t);\r
+   mercury_z_2+=     0.00001185024 * cos( 0.00000000000 +        0.00000000000*t);\r
+   mercury_z_2=mercury_z_2 * t * t;\r
+\r
+   return mercury_z_0+mercury_z_1+mercury_z_2;\r
+}\r
+\r
+double vsop87a_milli_neptune_x(double t){\r
+   double neptune_x_0=0.0;\r
+   neptune_x_0+=    30.05890004476 * cos( 5.31211340029 +       38.13303563780*t);\r
+   neptune_x_0+=     0.27080164222 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_x_0+=     0.13505661755 * cos( 3.50078975634 +       76.26607127560*t);\r
+   neptune_x_0+=     0.15726094556 * cos( 0.11319072675 +       36.64856292950*t);\r
+   neptune_x_0+=     0.14935120126 * cos( 1.08499403018 +       39.61750834610*t);\r
+   neptune_x_0+=     0.02597313814 * cos( 1.99590301412 +        1.48447270830*t);\r
+   neptune_x_0+=     0.01074040708 * cos( 5.38502938672 +       74.78159856730*t);\r
+   neptune_x_0+=     0.00823793287 * cos( 1.43221581862 +       35.16409022120*t);\r
+   neptune_x_0+=     0.00817588813 * cos( 0.78180174031 +        2.96894541660*t);\r
+   neptune_x_0+=     0.00565534918 * cos( 5.98964907613 +       41.10198105440*t);\r
+   neptune_x_0+=     0.00495719107 * cos( 0.59948143567 +      529.69096509460*t);\r
+   neptune_x_0+=     0.00307525907 * cos( 0.40023311011 +       73.29712585900*t);\r
+   neptune_x_0+=     0.00272253551 * cos( 0.87443494387 +      213.29909543800*t);\r
+   neptune_x_0+=     0.00135887219 * cos( 5.54676577816 +       77.75054398390*t);\r
+   neptune_x_0+=     0.00090965704 * cos( 1.68910246115 +      114.39910691340*t);\r
+   neptune_x_0+=     0.00069040539 * cos( 5.83469123520 +        4.45341812490*t);\r
+   neptune_x_0+=     0.00060813556 * cos( 2.62589958380 +       33.67961751290*t);\r
+   neptune_x_0+=     0.00054690827 * cos( 1.55799996661 +       71.81265315070*t);\r
+   neptune_x_0+=     0.00028889260 * cos( 4.78966826027 +       42.58645376270*t);\r
+   neptune_x_0+=     0.00012614732 * cos( 3.57002516434 +      112.91463420510*t);\r
+   neptune_x_0+=     0.00012749153 * cos( 2.73719269645 +      111.43016149680*t);\r
+   neptune_x_0+=     0.00012013994 * cos( 0.94912933496 +     1059.38193018920*t);\r
+   neptune_x_0+=     0.00007540650 * cos( 2.77783477855 +       70.32818044240*t);\r
+   neptune_x_0+=     0.00007573383 * cos( 0.10011329853 +      426.59819087600*t);\r
+   neptune_x_0+=     0.00008004318 * cos( 1.63965626260 +      108.46121608020*t);\r
+   neptune_x_0+=     0.00006464842 * cos( 4.62580066013 +        5.93789083320*t);\r
+   neptune_x_0+=     0.00005565860 * cos( 3.82502185953 +       32.19514480460*t);\r
+   neptune_x_0+=     0.00004654361 * cos( 0.10385887980 +       37.61177077600*t);\r
+   neptune_x_0+=     0.00004732434 * cos( 4.09723977191 +       79.23501669220*t);\r
+   neptune_x_0+=     0.00004557247 * cos( 1.09712669317 +       38.65430049960*t);\r
+   neptune_x_0+=     0.00004322550 * cos( 2.37744780188 +       38.08485152800*t);\r
+   neptune_x_0+=     0.00004315539 * cos( 5.10473142056 +       38.18121974760*t);\r
+   neptune_x_0+=     0.00004089036 * cos( 1.99429048244 +       37.16982779130*t);\r
+   neptune_x_0+=     0.00004249674 * cos( 5.63324475823 +       28.57180808220*t);\r
+   neptune_x_0+=     0.00003920412 * cos( 5.49263784865 +       39.09624348430*t);\r
+   neptune_x_0+=     0.00003951848 * cos( 2.29996934110 +       98.89998852460*t);\r
+   neptune_x_0+=     0.00003322735 * cos( 4.68798591938 +        4.19278569400*t);\r
+   neptune_x_0+=     0.00003108292 * cos( 1.84434543409 +       47.69426319340*t);\r
+   neptune_x_0+=     0.00003260095 * cos( 1.81839652878 +      145.10977900970*t);\r
+   neptune_x_0+=     0.00002723442 * cos( 3.82296285941 +      109.94568878850*t);\r
+   neptune_x_0+=     0.00002522938 * cos( 4.66296126912 +      312.19908396260*t);\r
+   neptune_x_0+=     0.00001887430 * cos( 3.20485417792 +       35.68535508300*t);\r
+   neptune_x_0+=     0.00001648985 * cos( 4.06990666591 +       30.05628079050*t);\r
+   neptune_x_0+=     0.00001826700 * cos( 3.58024318649 +       44.07092647100*t);\r
+   neptune_x_0+=     0.00001945462 * cos( 4.15326825288 +      206.18554843720*t);\r
+   neptune_x_0+=     0.00001681255 * cos( 4.27560304282 +       40.58071619260*t);\r
+   neptune_x_0+=     0.00001533383 * cos( 1.17732211665 +       38.02116105320*t);\r
+   neptune_x_0+=     0.00001891892 * cos( 0.74998855536 +      220.41264243880*t);\r
+   neptune_x_0+=     0.00001527526 * cos( 0.02173640246 +       38.24491022240*t);\r
+   neptune_x_0+=     0.00002084477 * cos( 1.56821671369 +      149.56319713460*t);\r
+   neptune_x_0+=     0.00002083682 * cos( 2.83676961811 +      137.03302416240*t);\r
+   neptune_x_0+=     0.00001615063 * cos( 2.91063835010 +      106.97674337190*t);\r
+   neptune_x_0+=     0.00001265797 * cos( 3.42037275447 +       46.20979048510*t);\r
+   neptune_x_0+=     0.00001560429 * cos( 0.55865739143 +       37.87240320690*t);\r
+   neptune_x_0+=     0.00001545705 * cos( 0.64028780672 +       38.39366806870*t);\r
+   neptune_x_0+=     0.00001434798 * cos( 0.72658718863 +      522.57741809380*t);\r
+   neptune_x_0+=     0.00001271543 * cos( 2.74412981229 +       33.94024994380*t);\r
+   neptune_x_0+=     0.00001407422 * cos( 3.61743288666 +      536.80451209540*t);\r
+   neptune_x_0+=     0.00001387922 * cos( 3.71814330952 +      115.88357962170*t);\r
+   neptune_x_0+=     0.00001228939 * cos( 2.78878211792 +       72.07328558160*t);\r
+   neptune_x_0+=     0.00001448439 * cos( 1.98814317259 +      181.75834193920*t);\r
+   neptune_x_0+=     0.00001170078 * cos( 3.98594689041 +        8.07675484730*t);\r
+   neptune_x_0+=     0.00001080795 * cos( 4.75485636019 +       42.32582133180*t);\r
+   neptune_x_0+=     0.00001220341 * cos( 2.64791449584 +      148.07872442630*t);\r
+\r
+   double neptune_x_1=0.0;\r
+   neptune_x_1+=     0.00255840261 * cos( 2.01935686795 +       36.64856292950*t);\r
+   neptune_x_1+=     0.00243125299 * cos( 5.46214902873 +       39.61750834610*t);\r
+   neptune_x_1+=     0.00118398168 * cos( 2.88251845061 +       76.26607127560*t);\r
+   neptune_x_1+=     0.00037965449 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_x_1+=     0.00021924705 * cos( 3.20156164152 +       35.16409022120*t);\r
+   neptune_x_1+=     0.00017459808 * cos( 4.26349398817 +       41.10198105440*t);\r
+   neptune_x_1+=     0.00013130617 * cos( 5.36424961848 +        2.96894541660*t);\r
+   neptune_x_1+=     0.00005086527 * cos( 1.92377354729 +       38.13303563780*t);\r
+   neptune_x_1+=     0.00004899718 * cos( 2.09349497813 +       73.29712585900*t);\r
+   neptune_x_1+=     0.00002745267 * cos( 4.06252818667 +       77.75054398390*t);\r
+   neptune_x_1+=     0.00002204414 * cos( 4.38855639521 +       33.67961751290*t);\r
+   neptune_x_1+=     0.00002168719 * cos( 4.11768012563 +        4.45341812490*t);\r
+   neptune_x_1+=     0.00001572202 * cos( 1.07606611589 +      114.39910691340*t);\r
+   neptune_x_1+=     0.00001344022 * cos( 3.03802059051 +       42.58645376270*t);\r
+   neptune_x_1+=     0.00001285542 * cos( 6.02367554997 +       74.78159856730*t);\r
+   neptune_x_1=neptune_x_1 * t;\r
+\r
+   double neptune_x_2=0.0;\r
+   neptune_x_2+=     0.00005371138 * cos( 0.00000000000 +        0.00000000000*t);\r
+   neptune_x_2+=     0.00004536283 * cos( 5.02700751836 +       36.64856292950*t);\r
+   neptune_x_2+=     0.00004350766 * cos( 2.45420254304 +       39.61750834610*t);\r
+   neptune_x_2+=     0.00003092965 * cos( 0.62250463031 +       38.13303563780*t);\r
+   neptune_x_2+=     0.00002163703 * cos( 1.79218168368 +       76.26607127560*t);\r
+   neptune_x_2=neptune_x_2 * t * t;\r
+\r
+   return neptune_x_0+neptune_x_1+neptune_x_2;\r
+}\r
+\r
+double vsop87a_milli_neptune_y(double t){\r
+   double neptune_y_0=0.0;\r
+   neptune_y_0+=    30.06056351665 * cos( 3.74086294714 +       38.13303563780*t);\r
+   neptune_y_0+=     0.30205857683 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_y_0+=     0.13506391797 * cos( 1.92953034883 +       76.26607127560*t);\r
+   neptune_y_0+=     0.15706589373 * cos( 4.82539970129 +       36.64856292950*t);\r
+   neptune_y_0+=     0.14936165806 * cos( 5.79694900665 +       39.61750834610*t);\r
+   neptune_y_0+=     0.02584250749 * cos( 0.42549700754 +        1.48447270830*t);\r
+   neptune_y_0+=     0.01073739772 * cos( 3.81371728533 +       74.78159856730*t);\r
+   neptune_y_0+=     0.00815187583 * cos( 5.49429775826 +        2.96894541660*t);\r
+   neptune_y_0+=     0.00582199295 * cos( 6.19633718936 +       35.16409022120*t);\r
+   neptune_y_0+=     0.00565576412 * cos( 4.41843009015 +       41.10198105440*t);\r
+   neptune_y_0+=     0.00495581047 * cos( 5.31205825784 +      529.69096509460*t);\r
+   neptune_y_0+=     0.00304525203 * cos( 5.11048113661 +       73.29712585900*t);\r
+   neptune_y_0+=     0.00272640298 * cos( 5.58603690785 +      213.29909543800*t);\r
+   neptune_y_0+=     0.00135897385 * cos( 3.97553750964 +       77.75054398390*t);\r
+   neptune_y_0+=     0.00090970871 * cos( 0.11783619888 +      114.39910691340*t);\r
+   neptune_y_0+=     0.00068790261 * cos( 4.26391997151 +        4.45341812490*t);\r
+   neptune_y_0+=     0.00028893355 * cos( 3.21848975032 +       42.58645376270*t);\r
+   neptune_y_0+=     0.00020081559 * cos( 1.19787916085 +       33.67961751290*t);\r
+   neptune_y_0+=     0.00012613583 * cos( 1.99777332934 +      112.91463420510*t);\r
+   neptune_y_0+=     0.00012828708 * cos( 1.16740053443 +      111.43016149680*t);\r
+   neptune_y_0+=     0.00012012961 * cos( 5.66157563804 +     1059.38193018920*t);\r
+   neptune_y_0+=     0.00008768580 * cos( 3.23487156950 +      108.46121608020*t);\r
+   neptune_y_0+=     0.00007581788 * cos( 4.81169168396 +      426.59819087600*t);\r
+   neptune_y_0+=     0.00006439265 * cos( 3.05453259951 +        5.93789083320*t);\r
+   neptune_y_0+=     0.00005297978 * cos( 0.79002313990 +       71.81265315070*t);\r
+   neptune_y_0+=     0.00004650708 * cos( 4.81540983294 +       37.61177077600*t);\r
+   neptune_y_0+=     0.00004733483 * cos( 2.52620194642 +       79.23501669220*t);\r
+   neptune_y_0+=     0.00004557247 * cos( 5.80951552318 +       38.65430049960*t);\r
+   neptune_y_0+=     0.00004322550 * cos( 0.80665145881 +       38.08485152800*t);\r
+   neptune_y_0+=     0.00004315539 * cos( 3.53393506841 +       38.18121974760*t);\r
+   neptune_y_0+=     0.00004089036 * cos( 0.42349446479 +       37.16982779130*t);\r
+   neptune_y_0+=     0.00004247643 * cos( 4.06355336504 +       28.57180808220*t);\r
+   neptune_y_0+=     0.00003932515 * cos( 3.91607592815 +       39.09624348430*t);\r
+   neptune_y_0+=     0.00003930135 * cos( 3.86614178174 +       98.89998852460*t);\r
+   neptune_y_0+=     0.00003323991 * cos( 3.11674274385 +        4.19278569400*t);\r
+   neptune_y_0+=     0.00003112636 * cos( 0.27319642944 +       47.69426319340*t);\r
+   neptune_y_0+=     0.00003373281 * cos( 3.39616255650 +      145.10977900970*t);\r
+   neptune_y_0+=     0.00002670944 * cos( 2.31235275416 +      109.94568878850*t);\r
+   neptune_y_0+=     0.00002523042 * cos( 6.23400745185 +      312.19908396260*t);\r
+   neptune_y_0+=     0.00001888827 * cos( 1.63364331324 +       35.68535508300*t);\r
+   neptune_y_0+=     0.00001647474 * cos( 2.50010254963 +       30.05628079050*t);\r
+   neptune_y_0+=     0.00001826390 * cos( 2.00938305966 +       44.07092647100*t);\r
+   neptune_y_0+=     0.00001967147 * cos( 2.56634772532 +      206.18554843720*t);\r
+   neptune_y_0+=     0.00001681258 * cos( 2.70480318579 +       40.58071619260*t);\r
+   neptune_y_0+=     0.00001533383 * cos( 5.88971113590 +       38.02116105320*t);\r
+   neptune_y_0+=     0.00001894261 * cos( 5.46274825258 +      220.41264243880*t);\r
+   neptune_y_0+=     0.00001527526 * cos( 4.73412534395 +       38.24491022240*t);\r
+   neptune_y_0+=     0.00002086907 * cos( 6.28313624461 +      149.56319713460*t);\r
+   neptune_y_0+=     0.00002057794 * cos( 4.38552505781 +      137.03302416240*t);\r
+   neptune_y_0+=     0.00001720954 * cos( 4.49400805134 +      106.97674337190*t);\r
+   neptune_y_0+=     0.00001314116 * cos( 1.80386443362 +       46.20979048510*t);\r
+   neptune_y_0+=     0.00001732739 * cos( 4.14518500834 +       70.32818044240*t);\r
+   neptune_y_0+=     0.00001559193 * cos( 5.27114846878 +       37.87240320690*t);\r
+   neptune_y_0+=     0.00001545705 * cos( 5.35267669439 +       38.39366806870*t);\r
+   neptune_y_0+=     0.00001435274 * cos( 5.44292013172 +      522.57741809380*t);\r
+   neptune_y_0+=     0.00001404991 * cos( 2.04611088339 +      536.80451209540*t);\r
+   neptune_y_0+=     0.00001242929 * cos( 1.10242173566 +       33.94024994380*t);\r
+   neptune_y_0+=     0.00001388024 * cos( 2.14792830412 +      115.88357962170*t);\r
+   neptune_y_0+=     0.00001467042 * cos( 3.56226463770 +      181.75834193920*t);\r
+   neptune_y_0+=     0.00001227926 * cos( 1.21334651843 +       72.07328558160*t);\r
+   neptune_y_0+=     0.00001080807 * cos( 3.18401661435 +       42.32582133180*t);\r
+   neptune_y_0+=     0.00001111708 * cos( 5.51669920239 +        8.07675484730*t);\r
+   neptune_y_0+=     0.00001237027 * cos( 1.08622199668 +      148.07872442630*t);\r
+\r
+   double neptune_y_1=0.0;\r
+   neptune_y_1+=     0.00352947493 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_y_1+=     0.00256125493 * cos( 0.44757496817 +       36.64856292950*t);\r
+   neptune_y_1+=     0.00243147725 * cos( 3.89099798696 +       39.61750834610*t);\r
+   neptune_y_1+=     0.00118427205 * cos( 1.31128027037 +       76.26607127560*t);\r
+   neptune_y_1+=     0.00021936702 * cos( 1.63124087591 +       35.16409022120*t);\r
+   neptune_y_1+=     0.00017462332 * cos( 2.69229902966 +       41.10198105440*t);\r
+   neptune_y_1+=     0.00012992380 * cos( 3.79578633002 +        2.96894541660*t);\r
+   neptune_y_1+=     0.00004945117 * cos( 0.51727080684 +       73.29712585900*t);\r
+   neptune_y_1+=     0.00002745921 * cos( 2.49178311082 +       77.75054398390*t);\r
+   neptune_y_1+=     0.00002145481 * cos( 2.54768447291 +        4.45341812490*t);\r
+   neptune_y_1+=     0.00001572289 * cos( 5.78853350711 +      114.39910691340*t);\r
+   neptune_y_1+=     0.00001565725 * cos( 2.89846266272 +       33.67961751290*t);\r
+   neptune_y_1+=     0.00001458269 * cos( 1.61835542699 +       38.13303563780*t);\r
+   neptune_y_1+=     0.00001293459 * cos( 4.45868061082 +       74.78159856730*t);\r
+   neptune_y_1+=     0.00001343731 * cos( 1.46712622109 +       42.58645376270*t);\r
+   neptune_y_1=neptune_y_1 * t;\r
+\r
+   double neptune_y_2=0.0;\r
+   neptune_y_2+=     0.00004539421 * cos( 3.45613207922 +       36.64856292950*t);\r
+   neptune_y_2+=     0.00004347956 * cos( 0.88317230351 +       39.61750834610*t);\r
+   neptune_y_2+=     0.00003595394 * cos( 3.14159265359 +        0.00000000000*t);\r
+   neptune_y_2+=     0.00003058647 * cos( 5.31956613665 +       38.13303563780*t);\r
+   neptune_y_2+=     0.00002163809 * cos( 0.22086532214 +       76.26607127560*t);\r
+   neptune_y_2=neptune_y_2 * t * t;\r
+\r
+   return neptune_y_0+neptune_y_1+neptune_y_2;\r
+}\r
+\r
+double vsop87a_milli_neptune_z(double t){\r
+   double neptune_z_0=0.0;\r
+   neptune_z_0+=     0.92866054405 * cos( 1.44103930278 +       38.13303563780*t);\r
+   neptune_z_0+=     0.01245978462 * cos( 0.00000000000 +        0.00000000000*t);\r
+   neptune_z_0+=     0.00474333567 * cos( 2.52218774238 +       36.64856292950*t);\r
+   neptune_z_0+=     0.00451987936 * cos( 3.50949720541 +       39.61750834610*t);\r
+   neptune_z_0+=     0.00417558068 * cos( 5.91310695421 +       76.26607127560*t);\r
+   neptune_z_0+=     0.00084104329 * cos( 4.38928900096 +        1.48447270830*t);\r
+   neptune_z_0+=     0.00032704958 * cos( 1.52048692001 +       74.78159856730*t);\r
+   neptune_z_0+=     0.00030873335 * cos( 3.29017611456 +       35.16409022120*t);\r
+   neptune_z_0+=     0.00025812584 * cos( 3.19303128782 +        2.96894541660*t);\r
+   neptune_z_0+=     0.00016865319 * cos( 2.13251104425 +       41.10198105440*t);\r
+   neptune_z_0+=     0.00011789909 * cos( 3.60001877675 +      213.29909543800*t);\r
+   neptune_z_0+=     0.00009770125 * cos( 2.80133971586 +       73.29712585900*t);\r
+   neptune_z_0+=     0.00011279680 * cos( 3.55816676334 +      529.69096509460*t);\r
+   neptune_z_0+=     0.00004119873 * cos( 1.67934316836 +       77.75054398390*t);\r
+   neptune_z_0+=     0.00002818034 * cos( 4.10661077794 +      114.39910691340*t);\r
+   neptune_z_0+=     0.00002868677 * cos( 4.27011526203 +       33.67961751290*t);\r
+   neptune_z_0+=     0.00002213464 * cos( 1.96045135168 +        4.45341812490*t);\r
+   neptune_z_0+=     0.00001865650 * cos( 5.05540709577 +       71.81265315070*t);\r
+\r
+   double neptune_z_1=0.0;\r
+   neptune_z_1+=     0.00154885971 * cos( 2.14239039664 +       38.13303563780*t);\r
+   neptune_z_1+=     0.00007783708 * cos( 4.40146905905 +       36.64856292950*t);\r
+   neptune_z_1+=     0.00006862414 * cos( 1.65930160610 +       39.61750834610*t);\r
+   neptune_z_1+=     0.00009464276 * cos( 0.00000000000 +        0.00000000000*t);\r
+   neptune_z_1+=     0.00003891873 * cos( 5.46761139427 +       76.26607127560*t);\r
+   neptune_z_1=neptune_z_1 * t;\r
+\r
+   double neptune_z_2=0.0;\r
+   neptune_z_2+=     0.00001264840 * cos( 1.91401498992 +       38.13303563780*t);\r
+   neptune_z_2=neptune_z_2 * t * t;\r
+\r
+   return neptune_z_0+neptune_z_1+neptune_z_2;\r
+}\r
+\r
+double vsop87a_milli_saturn_x(double t){\r
+   double saturn_x_0=0.0;\r
+   saturn_x_0+=     9.51638335797 * cos( 0.87441380794 +      213.29909543800*t);\r
+   saturn_x_0+=     0.26412374238 * cos( 0.12390892620 +      426.59819087600*t);\r
+   saturn_x_0+=     0.06760430339 * cos( 4.16767145778 +      206.18554843720*t);\r
+   saturn_x_0+=     0.06624260115 * cos( 0.75094737780 +      220.41264243880*t);\r
+   saturn_x_0+=     0.04244797817 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_x_0+=     0.02336340488 * cos( 2.02227784673 +        7.11354700080*t);\r
+   saturn_x_0+=     0.01255372247 * cos( 2.17338917731 +      110.20632121940*t);\r
+   saturn_x_0+=     0.01115684467 * cos( 3.15686878377 +      419.48464387520*t);\r
+   saturn_x_0+=     0.01097683232 * cos( 5.65753337256 +      639.89728631400*t);\r
+   saturn_x_0+=     0.00716328481 * cos( 2.71149993708 +      316.39186965660*t);\r
+   saturn_x_0+=     0.00509313365 * cos( 4.95865624780 +      103.09277421860*t);\r
+   saturn_x_0+=     0.00433994439 * cos( 0.72012820974 +      529.69096509460*t);\r
+   saturn_x_0+=     0.00372894461 * cos( 0.00137195497 +      433.71173787680*t);\r
+   saturn_x_0+=     0.00097843523 * cos( 1.01485750417 +      323.50541665740*t);\r
+   saturn_x_0+=     0.00080600536 * cos( 5.62103979796 +       11.04570026390*t);\r
+   saturn_x_0+=     0.00083782316 * cos( 0.62038893702 +      227.52618943960*t);\r
+   saturn_x_0+=     0.00074150224 * cos( 2.38206066655 +      632.78373931320*t);\r
+   saturn_x_0+=     0.00070219382 * cos( 0.88789752415 +      209.36694217490*t);\r
+   saturn_x_0+=     0.00068855792 * cos( 4.01788097627 +      217.23124870110*t);\r
+   saturn_x_0+=     0.00065620467 * cos( 2.69728593339 +      202.25339517410*t);\r
+   saturn_x_0+=     0.00058297911 * cos( 2.16155251399 +      224.34479570190*t);\r
+   saturn_x_0+=     0.00054022837 * cos( 4.90928184374 +      853.19638175200*t);\r
+   saturn_x_0+=     0.00045550446 * cos( 1.88235037830 +       14.22709400160*t);\r
+   saturn_x_0+=     0.00038345667 * cos( 4.39815501478 +      199.07200143640*t);\r
+   saturn_x_0+=     0.00044551703 * cos( 5.60763553535 +       63.73589830340*t);\r
+   saturn_x_0+=     0.00025165185 * cos( 0.37800582257 +      216.48048917570*t);\r
+   saturn_x_0+=     0.00024554499 * cos( 4.53150598095 +      210.11770170030*t);\r
+   saturn_x_0+=     0.00024673219 * cos( 5.90891573850 +      522.57741809380*t);\r
+   saturn_x_0+=     0.00024677050 * cos( 5.60389382420 +      415.55249061210*t);\r
+   saturn_x_0+=     0.00025491374 * cos( 1.63922423181 +      117.31986822020*t);\r
+   saturn_x_0+=     0.00031253049 * cos( 4.62976601833 +      735.87651353180*t);\r
+   saturn_x_0+=     0.00023372467 * cos( 5.53491987276 +      647.01083331480*t);\r
+   saturn_x_0+=     0.00023355468 * cos( 0.18791490124 +      149.56319713460*t);\r
+   saturn_x_0+=     0.00024805815 * cos( 5.50327676733 +       74.78159856730*t);\r
+   saturn_x_0+=     0.00014731703 * cos( 4.67981909838 +      277.03499374140*t);\r
+   saturn_x_0+=     0.00012427525 * cos( 1.02995545746 +     1059.38193018920*t);\r
+   saturn_x_0+=     0.00009943329 * cos( 0.84628387596 +        3.93215326310*t);\r
+   saturn_x_0+=     0.00012393514 * cos( 4.19747622821 +      490.33408917940*t);\r
+   saturn_x_0+=     0.00012026472 * cos( 5.66372282839 +      351.81659230870*t);\r
+   saturn_x_0+=     0.00008222014 * cos( 2.47875301104 +      742.99006053260*t);\r
+   saturn_x_0+=     0.00009087093 * cos( 4.33505326762 +     1052.26838318840*t);\r
+   saturn_x_0+=     0.00006717741 * cos( 5.51897460997 +      838.96928775040*t);\r
+   saturn_x_0+=     0.00006232999 * cos( 2.45837758015 +      846.08283475120*t);\r
+   saturn_x_0+=     0.00007161671 * cos( 2.18152751738 +       95.97922721780*t);\r
+   saturn_x_0+=     0.00006321101 * cos( 0.83915408770 +      309.27832265580*t);\r
+   saturn_x_0+=     0.00006074958 * cos( 6.15905897331 +      440.82528487760*t);\r
+   saturn_x_0+=     0.00005343894 * cos( 3.60046273598 +      412.37109687440*t);\r
+   saturn_x_0+=     0.00004860582 * cos( 0.26461045175 +      536.80451209540*t);\r
+   saturn_x_0+=     0.00005775802 * cos( 5.30717695229 +       38.13303563780*t);\r
+   saturn_x_0+=     0.00005194178 * cos( 4.54584467686 +      210.85141488320*t);\r
+   saturn_x_0+=     0.00005152474 * cos( 0.34669517150 +      215.74677599280*t);\r
+   saturn_x_0+=     0.00003792540 * cos( 5.99766568983 +      422.66603761290*t);\r
+   saturn_x_0+=     0.00003762834 * cos( 3.72112920226 +      212.33588759150*t);\r
+   saturn_x_0+=     0.00003747433 * cos( 1.16965137714 +      214.26230328450*t);\r
+   saturn_x_0+=     0.00003114576 * cos( 0.84631897292 +      213.25091132820*t);\r
+   saturn_x_0+=     0.00003113641 * cos( 4.04410367190 +      213.34727954780*t);\r
+   saturn_x_0+=     0.00002990421 * cos( 0.04148806852 +      625.67019231240*t);\r
+   saturn_x_0+=     0.00004111695 * cos( 5.96153153046 +      137.03302416240*t);\r
+   saturn_x_0+=     0.00002966450 * cos( 5.39568820046 +      138.51749687070*t);\r
+   saturn_x_0+=     0.00002827527 * cos( 0.73252555642 +      330.61896365820*t);\r
+   saturn_x_0+=     0.00003363323 * cos( 1.42089586686 +      437.64389113990*t);\r
+   saturn_x_0+=     0.00002886599 * cos( 1.14057922619 +       85.82729883120*t);\r
+   saturn_x_0+=     0.00002634075 * cos( 5.40645201521 +      288.08069400530*t);\r
+   saturn_x_0+=     0.00002713354 * cos( 0.96812639712 +      203.73786788240*t);\r
+   saturn_x_0+=     0.00003169390 * cos( 5.76640408988 +       76.26607127560*t);\r
+   saturn_x_0+=     0.00002618634 * cos( 5.49334837098 +      127.47179660680*t);\r
+   saturn_x_0+=     0.00002527746 * cos( 5.09752068381 +      628.85158605010*t);\r
+   saturn_x_0+=     0.00002989778 * cos( 4.15673836604 +     1066.49547719000*t);\r
+   saturn_x_0+=     0.00002507415 * cos( 1.49447138038 +        9.56122755560*t);\r
+   saturn_x_0+=     0.00002470181 * cos( 5.27435870056 +     1155.36115740700*t);\r
+   saturn_x_0+=     0.00002427626 * cos( 3.97311214231 +      222.86032299360*t);\r
+   saturn_x_0+=     0.00003128325 * cos( 4.05483976553 +     1368.66025284500*t);\r
+   saturn_x_0+=     0.00002309076 * cos( 3.67821438247 +      430.53034413910*t);\r
+   saturn_x_0+=     0.00002162629 * cos( 3.26951119901 +      340.77089204480*t);\r
+   saturn_x_0+=     0.00002912676 * cos( 1.76893577106 +        3.18139373770*t);\r
+   saturn_x_0+=     0.00002095366 * cos( 3.55759089756 +      423.41679713830*t);\r
+   saturn_x_0+=     0.00002335270 * cos( 5.86791072516 +      388.46515523820*t);\r
+   saturn_x_0+=     0.00001634262 * cos( 4.54357767539 +       12.53017297220*t);\r
+   saturn_x_0+=     0.00001635975 * cos( 2.19968869780 +      212.77783057620*t);\r
+   saturn_x_0+=     0.00001632759 * cos( 2.69164822165 +      213.82036029980*t);\r
+   saturn_x_0+=     0.00001498689 * cos( 3.60168057129 +       52.69019803950*t);\r
+   saturn_x_0+=     0.00001461217 * cos( 5.92456743836 +      429.77958461370*t);\r
+   saturn_x_0+=     0.00001963947 * cos( 2.05086487180 +        1.48447270830*t);\r
+   saturn_x_0+=     0.00001538425 * cos( 4.78544077085 +      860.30992875280*t);\r
+   saturn_x_0+=     0.00001485856 * cos( 5.65501463408 +      949.17560896980*t);\r
+   saturn_x_0+=     0.00001418116 * cos( 5.41419993599 +      350.33211960040*t);\r
+   saturn_x_0+=     0.00001147607 * cos( 0.19147238521 +      942.06206196900*t);\r
+   saturn_x_0+=     0.00001111703 * cos( 0.47907488492 +      234.63973644040*t);\r
+   saturn_x_0+=     0.00001019566 * cos( 5.00707811029 +     1471.75302706360*t);\r
+   saturn_x_0+=     0.00001009190 * cos( 1.34289487761 +      265.98929347750*t);\r
+   saturn_x_0+=     0.00001089450 * cos( 5.82690672710 +      362.86229257260*t);\r
+   saturn_x_0+=     0.00001092244 * cos( 1.13561107749 +      173.94221952280*t);\r
+   saturn_x_0+=     0.00001173456 * cos( 3.79591687208 +     1685.05212250160*t);\r
+   saturn_x_0+=     0.00001118369 * cos( 3.46624149583 +      703.63318461740*t);\r
+   saturn_x_0+=     0.00001150595 * cos( 3.74707160019 +      200.76892246580*t);\r
+   saturn_x_0+=     0.00001007761 * cos( 1.08964371328 +      225.82926841020*t);\r
+\r
+   double saturn_x_1=0.0;\r
+   saturn_x_1+=     0.07575103962 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_x_1+=     0.03085041716 * cos( 4.27565749128 +      426.59819087600*t);\r
+   saturn_x_1+=     0.02714918399 * cos( 5.85229412397 +      206.18554843720*t);\r
+   saturn_x_1+=     0.02643100909 * cos( 5.33291950584 +      220.41264243880*t);\r
+   saturn_x_1+=     0.00627104520 * cos( 0.32898307969 +        7.11354700080*t);\r
+   saturn_x_1+=     0.00256560953 * cos( 3.52478934343 +      639.89728631400*t);\r
+   saturn_x_1+=     0.00312356512 * cos( 4.83001724941 +      419.48464387520*t);\r
+   saturn_x_1+=     0.00189196274 * cos( 4.48642453552 +      433.71173787680*t);\r
+   saturn_x_1+=     0.00203646570 * cos( 1.10998681782 +      213.29909543800*t);\r
+   saturn_x_1+=     0.00119531145 * cos( 1.14735096078 +      110.20632121940*t);\r
+   saturn_x_1+=     0.00066764238 * cos( 3.72346596928 +      316.39186965660*t);\r
+   saturn_x_1+=     0.00066901225 * cos( 5.20257500380 +      227.52618943960*t);\r
+   saturn_x_1+=     0.00031000840 * cos( 6.06067919437 +      199.07200143640*t);\r
+   saturn_x_1+=     0.00030418100 * cos( 0.18746903351 +       14.22709400160*t);\r
+   saturn_x_1+=     0.00022275210 * cos( 6.19530878014 +      103.09277421860*t);\r
+   saturn_x_1+=     0.00018939377 * cos( 2.77618306725 +      853.19638175200*t);\r
+   saturn_x_1+=     0.00018093009 * cos( 5.09162723865 +      209.36694217490*t);\r
+   saturn_x_1+=     0.00017777854 * cos( 6.10381593351 +      217.23124870110*t);\r
+   saturn_x_1+=     0.00016296201 * cos( 4.86945681437 +      216.48048917570*t);\r
+   saturn_x_1+=     0.00017120250 * cos( 4.59611664188 +      632.78373931320*t);\r
+   saturn_x_1+=     0.00015894491 * cos( 0.03653502304 +      210.11770170030*t);\r
+   saturn_x_1+=     0.00016192653 * cos( 5.60798014450 +      323.50541665740*t);\r
+   saturn_x_1+=     0.00014466010 * cos( 3.67449380090 +      647.01083331480*t);\r
+   saturn_x_1+=     0.00011061528 * cos( 0.03163071461 +      117.31986822020*t);\r
+   saturn_x_1+=     0.00009873183 * cos( 5.20065307357 +      202.25339517410*t);\r
+   saturn_x_1+=     0.00008707608 * cos( 6.03511731637 +      224.34479570190*t);\r
+   saturn_x_1+=     0.00005499109 * cos( 4.40350603415 +      440.82528487760*t);\r
+   saturn_x_1+=     0.00005512222 * cos( 2.60556642348 +       11.04570026390*t);\r
+   saturn_x_1+=     0.00004008257 * cos( 1.48942966807 +      522.57741809380*t);\r
+   saturn_x_1+=     0.00003571196 * cos( 5.10821908379 +      412.37109687440*t);\r
+   saturn_x_1+=     0.00002731381 * cos( 4.10892223660 +      149.56319713460*t);\r
+   saturn_x_1+=     0.00002763786 * cos( 3.96253590209 +       95.97922721780*t);\r
+   saturn_x_1+=     0.00001875862 * cos( 2.52384080586 +        3.93215326310*t);\r
+   saturn_x_1+=     0.00001765816 * cos( 0.75684544353 +      277.03499374140*t);\r
+   saturn_x_1+=     0.00001688957 * cos( 3.98270950731 +      422.66603761290*t);\r
+   saturn_x_1+=     0.00001544787 * cos( 5.30283923836 +      330.61896365820*t);\r
+   saturn_x_1+=     0.00001449143 * cos( 6.26507179861 +      529.69096509460*t);\r
+   saturn_x_1+=     0.00001395551 * cos( 2.03533642541 +     1066.49547719000*t);\r
+   saturn_x_1+=     0.00001330413 * cos( 5.06312203212 +      234.63973644040*t);\r
+   saturn_x_1+=     0.00001511648 * cos( 6.23274598777 +        3.18139373770*t);\r
+   saturn_x_1+=     0.00001271050 * cos( 2.40338468675 +      415.55249061210*t);\r
+   saturn_x_1+=     0.00001171680 * cos( 4.59341412127 +      536.80451209540*t);\r
+   saturn_x_1+=     0.00001129056 * cos( 5.45794529295 +      423.41679713830*t);\r
+   saturn_x_1+=     0.00001126400 * cos( 2.88173213734 +      860.30992875280*t);\r
+   saturn_x_1+=     0.00001101245 * cos( 4.07698108824 +      429.77958461370*t);\r
+   saturn_x_1+=     0.00001273315 * cos( 0.09572429396 +      742.99006053260*t);\r
+   saturn_x_1+=     0.00001342277 * cos( 2.98929557875 +      210.85141488320*t);\r
+   saturn_x_1+=     0.00001331045 * cos( 1.90899526877 +      215.74677599280*t);\r
+   saturn_x_1=saturn_x_1 * t;\r
+\r
+   double saturn_x_2=0.0;\r
+   saturn_x_2+=     0.00560746334 * cos( 1.26401632282 +      206.18554843720*t);\r
+   saturn_x_2+=     0.00545834518 * cos( 3.62343709657 +      220.41264243880*t);\r
+   saturn_x_2+=     0.00443342186 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_x_2+=     0.00336109713 * cos( 2.42547432460 +      213.29909543800*t);\r
+   saturn_x_2+=     0.00224302269 * cos( 2.49151203519 +      426.59819087600*t);\r
+   saturn_x_2+=     0.00087170924 * cos( 4.89048951691 +        7.11354700080*t);\r
+   saturn_x_2+=     0.00050028094 * cos( 2.70119046081 +      433.71173787680*t);\r
+   saturn_x_2+=     0.00045122590 * cos( 0.36735068943 +      419.48464387520*t);\r
+   saturn_x_2+=     0.00032847824 * cos( 1.59210153669 +      639.89728631400*t);\r
+   saturn_x_2+=     0.00027153555 * cos( 3.49804002218 +      227.52618943960*t);\r
+   saturn_x_2+=     0.00012676167 * cos( 1.45465729530 +      199.07200143640*t);\r
+   saturn_x_2+=     0.00010330738 * cos( 4.76949531290 +       14.22709400160*t);\r
+   saturn_x_2+=     0.00007249149 * cos( 5.70264553247 +      110.20632121940*t);\r
+   saturn_x_2+=     0.00004653214 * cos( 1.83710048213 +      647.01083331480*t);\r
+   saturn_x_2+=     0.00004923585 * cos( 3.08463039042 +      216.48048917570*t);\r
+   saturn_x_2+=     0.00004777358 * cos( 1.81695155349 +      210.11770170030*t);\r
+   saturn_x_2+=     0.00004166633 * cos( 5.32887874226 +      316.39186965660*t);\r
+   saturn_x_2+=     0.00003508385 * cos( 0.78251653369 +      853.19638175200*t);\r
+   saturn_x_2+=     0.00002660470 * cos( 3.16731393212 +      209.36694217490*t);\r
+   saturn_x_2+=     0.00002538027 * cos( 2.65097612407 +      440.82528487760*t);\r
+   saturn_x_2+=     0.00002568114 * cos( 1.74024228572 +      217.23124870110*t);\r
+   saturn_x_2+=     0.00002503277 * cos( 4.69450368911 +      117.31986822020*t);\r
+   saturn_x_2+=     0.00002506986 * cos( 1.74781817701 +      103.09277421860*t);\r
+   saturn_x_2+=     0.00002129256 * cos( 0.28453141367 +      632.78373931320*t);\r
+   saturn_x_2+=     0.00001841989 * cos( 3.99269872894 +      323.50541665740*t);\r
+   saturn_x_2+=     0.00001228511 * cos( 0.42906039519 +      412.37109687440*t);\r
+   saturn_x_2=saturn_x_2 * t * t;\r
+\r
+   double saturn_x_3=0.0;\r
+   saturn_x_3+=     0.00077115952 * cos( 2.97714385362 +      206.18554843720*t);\r
+   saturn_x_3+=     0.00075340436 * cos( 1.89208005248 +      220.41264243880*t);\r
+   saturn_x_3+=     0.00018450895 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_x_3+=     0.00010527244 * cos( 0.66368256891 +      426.59819087600*t);\r
+   saturn_x_3+=     0.00008994946 * cos( 0.91696559755 +      433.71173787680*t);\r
+   saturn_x_3+=     0.00007403594 * cos( 1.78627385870 +      227.52618943960*t);\r
+   saturn_x_3+=     0.00008045160 * cos( 3.12864412887 +        7.11354700080*t);\r
+   saturn_x_3+=     0.00004505149 * cos( 2.24531319187 +      419.48464387520*t);\r
+   saturn_x_3+=     0.00003468010 * cos( 3.14590544446 +      199.07200143640*t);\r
+   saturn_x_3+=     0.00002974601 * cos( 6.00030641555 +      639.89728631400*t);\r
+   saturn_x_3+=     0.00002342089 * cos( 3.06091771643 +       14.22709400160*t);\r
+   saturn_x_3+=     0.00001230715 * cos( 4.38196130069 +      213.29909543800*t);\r
+   saturn_x_3+=     0.00001023888 * cos( 0.01138655869 +      647.01083331480*t);\r
+   saturn_x_3=saturn_x_3 * t * t * t;\r
+\r
+   double saturn_x_4=0.0;\r
+   saturn_x_4+=     0.00007959921 * cos( 4.70523623364 +      206.18554843720*t);\r
+   saturn_x_4+=     0.00007836652 * cos( 0.13981693631 +      220.41264243880*t);\r
+   saturn_x_4+=     0.00001511196 * cos( 0.06561560462 +      227.52618943960*t);\r
+   saturn_x_4+=     0.00001223066 * cos( 5.41618485361 +      433.71173787680*t);\r
+   saturn_x_4=saturn_x_4 * t * t * t * t;\r
+\r
+   return saturn_x_0+saturn_x_1+saturn_x_2+saturn_x_3+saturn_x_4;\r
+}\r
+\r
+double vsop87a_milli_saturn_y(double t){\r
+   double saturn_y_0=0.0;\r
+   saturn_y_0+=     9.52986882699 * cos( 5.58600556665 +      213.29909543800*t);\r
+   saturn_y_0+=     0.79387988806 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_y_0+=     0.26441781302 * cos( 4.83528061849 +      426.59819087600*t);\r
+   saturn_y_0+=     0.06916653915 * cos( 2.55279408706 +      206.18554843720*t);\r
+   saturn_y_0+=     0.06633570703 * cos( 5.46258848288 +      220.41264243880*t);\r
+   saturn_y_0+=     0.02345609742 * cos( 0.44652132519 +        7.11354700080*t);\r
+   saturn_y_0+=     0.01183874652 * cos( 1.34638298371 +      419.48464387520*t);\r
+   saturn_y_0+=     0.01245790434 * cos( 0.60367177975 +      110.20632121940*t);\r
+   saturn_y_0+=     0.01098751131 * cos( 4.08608782813 +      639.89728631400*t);\r
+   saturn_y_0+=     0.00700849336 * cos( 1.13611298025 +      316.39186965660*t);\r
+   saturn_y_0+=     0.00434466176 * cos( 5.42474696262 +      529.69096509460*t);\r
+   saturn_y_0+=     0.00373327342 * cos( 4.71308726958 +      433.71173787680*t);\r
+   saturn_y_0+=     0.00335162363 * cos( 0.66422253983 +      103.09277421860*t);\r
+   saturn_y_0+=     0.00097837745 * cos( 5.72844290173 +      323.50541665740*t);\r
+   saturn_y_0+=     0.00080571808 * cos( 4.05295449910 +       11.04570026390*t);\r
+   saturn_y_0+=     0.00083899691 * cos( 5.33204070267 +      227.52618943960*t);\r
+   saturn_y_0+=     0.00070158491 * cos( 5.59777963629 +      209.36694217490*t);\r
+   saturn_y_0+=     0.00065937657 * cos( 1.25969608208 +      202.25339517410*t);\r
+   saturn_y_0+=     0.00070957225 * cos( 0.88888207567 +      632.78373931320*t);\r
+   saturn_y_0+=     0.00068985859 * cos( 2.44460312617 +      217.23124870110*t);\r
+   saturn_y_0+=     0.00058382264 * cos( 0.58978766922 +      224.34479570190*t);\r
+   saturn_y_0+=     0.00054049836 * cos( 3.33757904879 +      853.19638175200*t);\r
+   saturn_y_0+=     0.00045790930 * cos( 0.30331527632 +       14.22709400160*t);\r
+   saturn_y_0+=     0.00041976402 * cos( 2.62591355948 +      199.07200143640*t);\r
+   saturn_y_0+=     0.00044697175 * cos( 0.90661238256 +       63.73589830340*t);\r
+   saturn_y_0+=     0.00025199575 * cos( 5.08963506006 +      216.48048917570*t);\r
+   saturn_y_0+=     0.00024640836 * cos( 2.95445247282 +      210.11770170030*t);\r
+   saturn_y_0+=     0.00024835151 * cos( 4.02630190571 +      415.55249061210*t);\r
+   saturn_y_0+=     0.00025545907 * cos( 0.06626229252 +      117.31986822020*t);\r
+   saturn_y_0+=     0.00029666833 * cos( 6.09910638345 +      735.87651353180*t);\r
+   saturn_y_0+=     0.00023396742 * cos( 3.96337393635 +      647.01083331480*t);\r
+   saturn_y_0+=     0.00023380691 * cos( 4.90051072276 +      149.56319713460*t);\r
+   saturn_y_0+=     0.00020272215 * cos( 2.34319548198 +      309.27832265580*t);\r
+   saturn_y_0+=     0.00020099552 * cos( 0.98365186365 +      522.57741809380*t);\r
+   saturn_y_0+=     0.00024827950 * cos( 3.92681428900 +       74.78159856730*t);\r
+   saturn_y_0+=     0.00015383927 * cos( 3.10227822627 +      277.03499374140*t);\r
+   saturn_y_0+=     0.00011629210 * cos( 5.74108283772 +     1059.38193018920*t);\r
+   saturn_y_0+=     0.00012422966 * cos( 2.62557865743 +      490.33408917940*t);\r
+   saturn_y_0+=     0.00012048048 * cos( 4.09265980116 +      351.81659230870*t);\r
+   saturn_y_0+=     0.00009551796 * cos( 3.48788042094 +       95.97922721780*t);\r
+   saturn_y_0+=     0.00007670379 * cos( 1.16594276164 +      742.99006053260*t);\r
+   saturn_y_0+=     0.00006919946 * cos( 1.17090063883 +      412.37109687440*t);\r
+   saturn_y_0+=     0.00009034877 * cos( 5.86816144198 +     1052.26838318840*t);\r
+   saturn_y_0+=     0.00006536751 * cos( 0.84246459392 +      838.96928775040*t);\r
+   saturn_y_0+=     0.00006082097 * cos( 4.58758280729 +      440.82528487760*t);\r
+   saturn_y_0+=     0.00005027211 * cos( 0.93213690546 +      846.08283475120*t);\r
+   saturn_y_0+=     0.00004838146 * cos( 4.98563812475 +      536.80451209540*t);\r
+   saturn_y_0+=     0.00005768897 * cos( 3.73776690402 +       38.13303563780*t);\r
+   saturn_y_0+=     0.00005201849 * cos( 2.97482802430 +      210.85141488320*t);\r
+   saturn_y_0+=     0.00005156578 * cos( 5.05796998564 +      215.74677599280*t);\r
+   saturn_y_0+=     0.00003792348 * cos( 4.41806046981 +      422.66603761290*t);\r
+   saturn_y_0+=     0.00003881104 * cos( 3.38026646963 +        3.93215326310*t);\r
+   saturn_y_0+=     0.00003768751 * cos( 2.14954247360 +      212.33588759150*t);\r
+   saturn_y_0+=     0.00003752010 * cos( 5.88125434018 +      214.26230328450*t);\r
+   saturn_y_0+=     0.00003118938 * cos( 5.55799397159 +      213.25091132820*t);\r
+   saturn_y_0+=     0.00003118056 * cos( 2.47259780102 +      213.34727954780*t);\r
+   saturn_y_0+=     0.00003815691 * cos( 2.44166851155 +      625.67019231240*t);\r
+   saturn_y_0+=     0.00004042463 * cos( 1.24471211016 +      137.03302416240*t);\r
+   saturn_y_0+=     0.00002829944 * cos( 5.44434225998 +      330.61896365820*t);\r
+   saturn_y_0+=     0.00003367270 * cos( 6.13298847057 +      437.64389113990*t);\r
+   saturn_y_0+=     0.00002891352 * cos( 5.85313497106 +       85.82729883120*t);\r
+   saturn_y_0+=     0.00002774595 * cos( 5.61594351302 +      203.73786788240*t);\r
+   saturn_y_0+=     0.00002638715 * cos( 3.83678156812 +      288.08069400530*t);\r
+   saturn_y_0+=     0.00003171680 * cos( 4.19553075395 +       76.26607127560*t);\r
+   saturn_y_0+=     0.00002532374 * cos( 3.52629372341 +      628.85158605010*t);\r
+   saturn_y_0+=     0.00002533632 * cos( 3.89788590926 +      138.51749687070*t);\r
+   saturn_y_0+=     0.00002982174 * cos( 2.58535107213 +     1066.49547719000*t);\r
+   saturn_y_0+=     0.00002620642 * cos( 0.69751279148 +      127.47179660680*t);\r
+   saturn_y_0+=     0.00002501775 * cos( 6.19929274396 +        9.56122755560*t);\r
+   saturn_y_0+=     0.00002448467 * cos( 0.54179432209 +     1155.36115740700*t);\r
+   saturn_y_0+=     0.00002431496 * cos( 2.40122451395 +      222.86032299360*t);\r
+   saturn_y_0+=     0.00003138628 * cos( 5.63058455924 +     1368.66025284500*t);\r
+   saturn_y_0+=     0.00002269226 * cos( 2.12401905105 +      430.53034413910*t);\r
+   saturn_y_0+=     0.00002078049 * cos( 1.95682348964 +      423.41679713830*t);\r
+   saturn_y_0+=     0.00002670750 * cos( 0.18165311734 +        3.18139373770*t);\r
+   saturn_y_0+=     0.00002339764 * cos( 4.29619053852 +      388.46515523820*t);\r
+   saturn_y_0+=     0.00001636580 * cos( 2.97440139727 +       12.53017297220*t);\r
+   saturn_y_0+=     0.00001639181 * cos( 0.62823227849 +      212.77783057620*t);\r
+   saturn_y_0+=     0.00001634235 * cos( 1.12043073218 +      213.82036029980*t);\r
+   saturn_y_0+=     0.00001499665 * cos( 5.16865990579 +       52.69019803950*t);\r
+   saturn_y_0+=     0.00001462908 * cos( 4.35285690993 +      429.77958461370*t);\r
+   saturn_y_0+=     0.00001678952 * cos( 2.07211719214 +      949.17560896980*t);\r
+   saturn_y_0+=     0.00001989253 * cos( 0.42496478369 +        1.48447270830*t);\r
+   saturn_y_0+=     0.00001540242 * cos( 3.21449770483 +      860.30992875280*t);\r
+   saturn_y_0+=     0.00001437063 * cos( 3.84293543293 +      350.33211960040*t);\r
+   saturn_y_0+=     0.00001276377 * cos( 2.98728987770 +      340.77089204480*t);\r
+   saturn_y_0+=     0.00001152164 * cos( 1.78736848302 +      942.06206196900*t);\r
+   saturn_y_0+=     0.00001112617 * cos( 5.19114183145 +      234.63973644040*t);\r
+   saturn_y_0+=     0.00001011023 * cos( 0.27242160432 +     1471.75302706360*t);\r
+   saturn_y_0+=     0.00001162807 * cos( 2.35040840317 +      200.76892246580*t);\r
+   saturn_y_0+=     0.00001091025 * cos( 4.25638370205 +      362.86229257260*t);\r
+   saturn_y_0+=     0.00001090678 * cos( 5.85086226218 +      173.94221952280*t);\r
+   saturn_y_0+=     0.00001186647 * cos( 5.38323620554 +     1685.05212250160*t);\r
+   saturn_y_0+=     0.00001120819 * cos( 1.89478696683 +      703.63318461740*t);\r
+   saturn_y_0+=     0.00001008898 * cos( 5.80110302450 +      225.82926841020*t);\r
+\r
+   double saturn_y_1=0.0;\r
+   saturn_y_1+=     0.05373889135 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_y_1+=     0.03090575152 * cos( 2.70346890906 +      426.59819087600*t);\r
+   saturn_y_1+=     0.02741594312 * cos( 4.26667636015 +      206.18554843720*t);\r
+   saturn_y_1+=     0.02647489677 * cos( 3.76132298889 +      220.41264243880*t);\r
+   saturn_y_1+=     0.00631520527 * cos( 5.03245505280 +        7.11354700080*t);\r
+   saturn_y_1+=     0.00256799701 * cos( 1.95351819758 +      639.89728631400*t);\r
+   saturn_y_1+=     0.00312271930 * cos( 3.25850205023 +      419.48464387520*t);\r
+   saturn_y_1+=     0.00189433319 * cos( 2.91501840819 +      433.71173787680*t);\r
+   saturn_y_1+=     0.00164133553 * cos( 5.29239290066 +      213.29909543800*t);\r
+   saturn_y_1+=     0.00116791227 * cos( 5.89146675760 +      110.20632121940*t);\r
+   saturn_y_1+=     0.00067210919 * cos( 2.17042636344 +      316.39186965660*t);\r
+   saturn_y_1+=     0.00067003292 * cos( 3.63101075514 +      227.52618943960*t);\r
+   saturn_y_1+=     0.00033002406 * cos( 4.35527405801 +      199.07200143640*t);\r
+   saturn_y_1+=     0.00030628998 * cos( 4.88861760772 +       14.22709400160*t);\r
+   saturn_y_1+=     0.00022234714 * cos( 4.62212779231 +      103.09277421860*t);\r
+   saturn_y_1+=     0.00018945004 * cos( 1.20412493845 +      853.19638175200*t);\r
+   saturn_y_1+=     0.00018079959 * cos( 3.51566153251 +      209.36694217490*t);\r
+   saturn_y_1+=     0.00017791543 * cos( 4.53214140649 +      217.23124870110*t);\r
+   saturn_y_1+=     0.00016320701 * cos( 3.29784030970 +      216.48048917570*t);\r
+   saturn_y_1+=     0.00015944258 * cos( 4.74503265169 +      210.11770170030*t);\r
+   saturn_y_1+=     0.00016717122 * cos( 3.00270792752 +      632.78373931320*t);\r
+   saturn_y_1+=     0.00016149947 * cos( 4.04186432517 +      323.50541665740*t);\r
+   saturn_y_1+=     0.00014481431 * cos( 2.10298298650 +      647.01083331480*t);\r
+   saturn_y_1+=     0.00011084040 * cos( 4.74073871754 +      117.31986822020*t);\r
+   saturn_y_1+=     0.00009905491 * cos( 3.60258599375 +      202.25339517410*t);\r
+   saturn_y_1+=     0.00008726051 * cos( 4.46341342877 +      224.34479570190*t);\r
+   saturn_y_1+=     0.00006585597 * cos( 4.07326320487 +      309.27832265580*t);\r
+   saturn_y_1+=     0.00005505978 * cos( 2.83207390240 +      440.82528487760*t);\r
+   saturn_y_1+=     0.00005424041 * cos( 1.03197684410 +       11.04570026390*t);\r
+   saturn_y_1+=     0.00004178266 * cos( 3.01038512076 +      412.37109687440*t);\r
+   saturn_y_1+=     0.00004049905 * cos( 5.17488767645 +       95.97922721780*t);\r
+   saturn_y_1+=     0.00002735256 * cos( 2.53975850409 +      149.56319713460*t);\r
+   saturn_y_1+=     0.00002369024 * cos( 2.40497927917 +      522.57741809380*t);\r
+   saturn_y_1+=     0.00001745258 * cos( 5.50576015456 +      277.03499374140*t);\r
+   saturn_y_1+=     0.00001692790 * cos( 2.39926502529 +      422.66603761290*t);\r
+   saturn_y_1+=     0.00001546006 * cos( 3.73156925599 +      330.61896365820*t);\r
+   saturn_y_1+=     0.00001389354 * cos( 0.46207025895 +     1066.49547719000*t);\r
+   saturn_y_1+=     0.00001332553 * cos( 3.49199812296 +      234.63973644040*t);\r
+   saturn_y_1+=     0.00001300934 * cos( 0.83727681906 +      415.55249061210*t);\r
+   saturn_y_1+=     0.00001393622 * cos( 4.62214277175 +        3.18139373770*t);\r
+   saturn_y_1+=     0.00001174319 * cos( 2.72609984335 +      846.08283475120*t);\r
+   saturn_y_1+=     0.00001148010 * cos( 3.04374738882 +      536.80451209540*t);\r
+   saturn_y_1+=     0.00001314125 * cos( 4.38891656600 +      625.67019231240*t);\r
+   saturn_y_1+=     0.00001127952 * cos( 3.87309692307 +      423.41679713830*t);\r
+   saturn_y_1+=     0.00001127646 * cos( 1.31088906213 +      860.30992875280*t);\r
+   saturn_y_1+=     0.00001519732 * cos( 6.12880664637 +        3.93215326310*t);\r
+   saturn_y_1+=     0.00001102361 * cos( 2.50535306014 +      429.77958461370*t);\r
+   saturn_y_1+=     0.00001344891 * cos( 1.41793593685 +      210.85141488320*t);\r
+   saturn_y_1+=     0.00001331786 * cos( 0.33834520814 +      215.74677599280*t);\r
+   saturn_y_1=saturn_y_1 * t;\r
+\r
+   double saturn_y_2=0.0;\r
+   saturn_y_2+=     0.00563706537 * cos( 5.97115878242 +      206.18554843720*t);\r
+   saturn_y_2+=     0.00547012116 * cos( 2.05154973426 +      220.41264243880*t);\r
+   saturn_y_2+=     0.00458518613 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_y_2+=     0.00362294249 * cos( 0.89540100509 +      213.29909543800*t);\r
+   saturn_y_2+=     0.00225521642 * cos( 0.91699821445 +      426.59819087600*t);\r
+   saturn_y_2+=     0.00088390611 * cos( 3.30289449917 +        7.11354700080*t);\r
+   saturn_y_2+=     0.00050101314 * cos( 1.12976163835 +      433.71173787680*t);\r
+   saturn_y_2+=     0.00045516403 * cos( 5.07669466539 +      419.48464387520*t);\r
+   saturn_y_2+=     0.00032896745 * cos( 0.02089057938 +      639.89728631400*t);\r
+   saturn_y_2+=     0.00027199743 * cos( 1.92638417640 +      227.52618943960*t);\r
+   saturn_y_2+=     0.00013251505 * cos( 6.07693099404 +      199.07200143640*t);\r
+   saturn_y_2+=     0.00010425984 * cos( 3.18246869028 +       14.22709400160*t);\r
+   saturn_y_2+=     0.00006673556 * cos( 4.24747633887 +      110.20632121940*t);\r
+   saturn_y_2+=     0.00004658591 * cos( 0.26557833758 +      647.01083331480*t);\r
+   saturn_y_2+=     0.00004934094 * cos( 1.51301179516 +      216.48048917570*t);\r
+   saturn_y_2+=     0.00004789554 * cos( 0.24337901916 +      210.11770170030*t);\r
+   saturn_y_2+=     0.00004167268 * cos( 3.73203671391 +      316.39186965660*t);\r
+   saturn_y_2+=     0.00003509537 * cos( 5.49281440568 +      853.19638175200*t);\r
+   saturn_y_2+=     0.00002743470 * cos( 6.21939083886 +      103.09277421860*t);\r
+   saturn_y_2+=     0.00002661172 * cos( 1.58795412736 +      209.36694217490*t);\r
+   saturn_y_2+=     0.00002541191 * cos( 1.07964653574 +      440.82528487760*t);\r
+   saturn_y_2+=     0.00002568018 * cos( 0.16811216098 +      217.23124870110*t);\r
+   saturn_y_2+=     0.00002507738 * cos( 3.11882746290 +      117.31986822020*t);\r
+   saturn_y_2+=     0.00002159089 * cos( 4.99912567024 +      632.78373931320*t);\r
+   saturn_y_2+=     0.00001828412 * cos( 2.43368650590 +      323.50541665740*t);\r
+   saturn_y_2+=     0.00001351629 * cos( 4.81673889364 +      412.37109687440*t);\r
+   saturn_y_2+=     0.00001177305 * cos( 5.84484412189 +      309.27832265580*t);\r
+   saturn_y_2=saturn_y_2 * t * t;\r
+\r
+   double saturn_y_3=0.0;\r
+   saturn_y_3+=     0.00077376615 * cos( 1.40391048961 +      206.18554843720*t);\r
+   saturn_y_3+=     0.00075564351 * cos( 0.31962896379 +      220.41264243880*t);\r
+   saturn_y_3+=     0.00022843837 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_y_3+=     0.00010672263 * cos( 5.36495663820 +      426.59819087600*t);\r
+   saturn_y_3+=     0.00009010175 * cos( 5.62865146645 +      433.71173787680*t);\r
+   saturn_y_3+=     0.00007418018 * cos( 0.21442310101 +      227.52618943960*t);\r
+   saturn_y_3+=     0.00008298723 * cos( 1.52262563519 +        7.11354700080*t);\r
+   saturn_y_3+=     0.00004507061 * cos( 0.67248969480 +      419.48464387520*t);\r
+   saturn_y_3+=     0.00003581682 * cos( 1.51466786030 +      199.07200143640*t);\r
+   saturn_y_3+=     0.00002981969 * cos( 4.42868951627 +      639.89728631400*t);\r
+   saturn_y_3+=     0.00002376221 * cos( 1.46232779180 +       14.22709400160*t);\r
+   saturn_y_3+=     0.00001024263 * cos( 4.72337917196 +      647.01083331480*t);\r
+   saturn_y_3=saturn_y_3 * t * t * t;\r
+\r
+   double saturn_y_4=0.0;\r
+   saturn_y_4+=     0.00007978886 * cos( 3.13229268011 +      206.18554843720*t);\r
+   saturn_y_4+=     0.00007868379 * cos( 4.84940260021 +      220.41264243880*t);\r
+   saturn_y_4+=     0.00001514835 * cos( 4.77675733867 +      227.52618943960*t);\r
+   saturn_y_4+=     0.00001225569 * cos( 3.84500138574 +      433.71173787680*t);\r
+   saturn_y_4=saturn_y_4 * t * t * t * t;\r
+\r
+   return saturn_y_0+saturn_y_1+saturn_y_2+saturn_y_3+saturn_y_4;\r
+}\r
+\r
+double vsop87a_milli_saturn_z(double t){\r
+   double saturn_z_0=0.0;\r
+   saturn_z_0+=     0.41356950940 * cos( 3.60234142982 +      213.29909543800*t);\r
+   saturn_z_0+=     0.01148283576 * cos( 2.85128367469 +      426.59819087600*t);\r
+   saturn_z_0+=     0.01214249867 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_z_0+=     0.00329280791 * cos( 0.57121407104 +      206.18554843720*t);\r
+   saturn_z_0+=     0.00286934048 * cos( 3.48073526693 +      220.41264243880*t);\r
+   saturn_z_0+=     0.00099076584 * cos( 4.73369511264 +        7.11354700080*t);\r
+   saturn_z_0+=     0.00057361820 * cos( 4.92611225093 +      110.20632121940*t);\r
+   saturn_z_0+=     0.00047738127 * cos( 2.10039779728 +      639.89728631400*t);\r
+   saturn_z_0+=     0.00043458803 * cos( 5.84904978051 +      419.48464387520*t);\r
+   saturn_z_0+=     0.00034565673 * cos( 5.42614229590 +      316.39186965660*t);\r
+   saturn_z_0+=     0.00016185391 * cos( 2.72987173675 +      433.71173787680*t);\r
+   saturn_z_0+=     0.00009001270 * cos( 1.38140102737 +      103.09277421860*t);\r
+   saturn_z_0+=     0.00011433574 * cos( 3.71662021072 +      529.69096509460*t);\r
+   saturn_z_0+=     0.00005398708 * cos( 5.13204892363 +      202.25339517410*t);\r
+   saturn_z_0+=     0.00003902467 * cos( 3.71499738796 +      323.50541665740*t);\r
+   saturn_z_0+=     0.00003709212 * cos( 5.05549348785 +      632.78373931320*t);\r
+   saturn_z_0+=     0.00003614100 * cos( 3.35210451276 +      227.52618943960*t);\r
+   saturn_z_0+=     0.00003379953 * cos( 2.13868919206 +       11.04570026390*t);\r
+   saturn_z_0+=     0.00003089874 * cos( 3.62572857085 +      209.36694217490*t);\r
+   saturn_z_0+=     0.00002683064 * cos( 4.87689555581 +      224.34479570190*t);\r
+   saturn_z_0+=     0.00002963493 * cos( 0.46490184985 +      217.23124870110*t);\r
+   saturn_z_0+=     0.00002343367 * cos( 1.34558278340 +      853.19638175200*t);\r
+   saturn_z_0+=     0.00002423663 * cos( 2.92907094760 +       63.73589830340*t);\r
+   saturn_z_0+=     0.00001701916 * cos( 1.89892525654 +      735.87651353180*t);\r
+   saturn_z_0+=     0.00001941205 * cos( 4.59421314662 +       14.22709400160*t);\r
+   saturn_z_0+=     0.00001990145 * cos( 0.73166053611 +      199.07200143640*t);\r
+   saturn_z_0+=     0.00001460265 * cos( 3.12851339724 +      522.57741809380*t);\r
+   saturn_z_0+=     0.00001148341 * cos( 4.41139213915 +      117.31986822020*t);\r
+   saturn_z_0+=     0.00001092809 * cos( 3.10679381209 +      216.48048917570*t);\r
+   saturn_z_0+=     0.00001015179 * cos( 1.97897195994 +      647.01083331480*t);\r
+   saturn_z_0+=     0.00001098254 * cos( 0.96097709156 +      210.11770170030*t);\r
+   saturn_z_0+=     0.00001028743 * cos( 2.11933059243 +      415.55249061210*t);\r
+\r
+   double saturn_z_1=0.0;\r
+   saturn_z_1+=     0.01906503283 * cos( 4.94544746116 +      213.29909543800*t);\r
+   saturn_z_1+=     0.00528301265 * cos( 3.14159265359 +        0.00000000000*t);\r
+   saturn_z_1+=     0.00130262284 * cos( 2.26140980879 +      206.18554843720*t);\r
+   saturn_z_1+=     0.00101466332 * cos( 1.79095829545 +      220.41264243880*t);\r
+   saturn_z_1+=     0.00085947578 * cos( 0.51612788497 +      426.59819087600*t);\r
+   saturn_z_1+=     0.00022257446 * cos( 3.07684015656 +        7.11354700080*t);\r
+   saturn_z_1+=     0.00016179946 * cos( 1.19987517506 +      419.48464387520*t);\r
+   saturn_z_1+=     0.00009117402 * cos( 6.17205626814 +      639.89728631400*t);\r
+   saturn_z_1+=     0.00007470703 * cos( 0.93135621171 +      433.71173787680*t);\r
+   saturn_z_1+=     0.00004966668 * cos( 0.19044864213 +      316.39186965660*t);\r
+   saturn_z_1+=     0.00003816564 * cos( 4.38284565245 +      110.20632121940*t);\r
+   saturn_z_1+=     0.00002724120 * cos( 1.65580138665 +      227.52618943960*t);\r
+   saturn_z_1+=     0.00001734540 * cos( 3.51628075636 +      103.09277421860*t);\r
+   saturn_z_1+=     0.00001541995 * cos( 2.42323572812 +      199.07200143640*t);\r
+   saturn_z_1+=     0.00001209302 * cos( 2.91140089093 +       14.22709400160*t);\r
+   saturn_z_1=saturn_z_1 * t;\r
+\r
+   double saturn_z_2=0.0;\r
+   saturn_z_2+=     0.00131275155 * cos( 0.08868998101 +      213.29909543800*t);\r
+   saturn_z_2+=     0.00030147649 * cos( 3.91396203887 +      206.18554843720*t);\r
+   saturn_z_2+=     0.00019322173 * cos( 0.09228748624 +      220.41264243880*t);\r
+   saturn_z_2+=     0.00006868926 * cos( 5.48420255395 +      426.59819087600*t);\r
+   saturn_z_2+=     0.00002826107 * cos( 1.36583318555 +        7.11354700080*t);\r
+   saturn_z_2+=     0.00002646332 * cos( 2.94607395955 +      419.48464387520*t);\r
+   saturn_z_2+=     0.00003138233 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_z_2+=     0.00001844798 * cos( 5.43612062856 +      433.71173787680*t);\r
+   saturn_z_2+=     0.00001055383 * cos( 6.23890785179 +      227.52618943960*t);\r
+   saturn_z_2+=     0.00001036435 * cos( 4.33916308552 +      639.89728631400*t);\r
+   saturn_z_2=saturn_z_2 * t * t;\r
+\r
+   double saturn_z_3=0.0;\r
+   saturn_z_3+=     0.00004559419 * cos( 1.70646871501 +      213.29909543800*t);\r
+   saturn_z_3+=     0.00004779074 * cos( 5.57723756330 +      206.18554843720*t);\r
+   saturn_z_3+=     0.00003965402 * cos( 0.00000000000 +        0.00000000000*t);\r
+   saturn_z_3+=     0.00002508242 * cos( 4.64959056313 +      220.41264243880*t);\r
+   saturn_z_3=saturn_z_3 * t * t * t;\r
+\r
+   return saturn_z_0+saturn_z_1+saturn_z_2+saturn_z_3;\r
+}\r
+\r
+double vsop87a_milli_uranus_x(double t){\r
+   double uranus_x_0=0.0;\r
+   uranus_x_0+=    19.17370730359 * cos( 5.48133416489 +       74.78159856730*t);\r
+   uranus_x_0+=     1.32272523872 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_x_0+=     0.44402496796 * cos( 1.65967519586 +      149.56319713460*t);\r
+   uranus_x_0+=     0.14668209481 * cos( 3.42395862804 +       73.29712585900*t);\r
+   uranus_x_0+=     0.14130269479 * cos( 4.39572927934 +       76.26607127560*t);\r
+   uranus_x_0+=     0.06201106178 * cos( 5.14043574125 +        1.48447270830*t);\r
+   uranus_x_0+=     0.01542951343 * cos( 4.12121838072 +      224.34479570190*t);\r
+   uranus_x_0+=     0.01444216660 * cos( 2.65117115201 +      148.07872442630*t);\r
+   uranus_x_0+=     0.00944995563 * cos( 1.65869338757 +       11.04570026390*t);\r
+   uranus_x_0+=     0.00657524815 * cos( 0.57595170636 +      151.04766984290*t);\r
+   uranus_x_0+=     0.00621624676 * cos( 3.05882246638 +       77.75054398390*t);\r
+   uranus_x_0+=     0.00585182542 * cos( 4.79934779678 +       71.81265315070*t);\r
+   uranus_x_0+=     0.00634000270 * cos( 4.09556589724 +       63.73589830340*t);\r
+   uranus_x_0+=     0.00547699056 * cos( 3.63127725056 +       85.82729883120*t);\r
+   uranus_x_0+=     0.00458219984 * cos( 3.90788284112 +        2.96894541660*t);\r
+   uranus_x_0+=     0.00496087649 * cos( 0.59947400861 +      529.69096509460*t);\r
+   uranus_x_0+=     0.00383625535 * cos( 6.18762010576 +      138.51749687070*t);\r
+   uranus_x_0+=     0.00267938156 * cos( 0.96885660137 +      213.29909543800*t);\r
+   uranus_x_0+=     0.00215368005 * cos( 5.30877641428 +       38.13303563780*t);\r
+   uranus_x_0+=     0.00145505389 * cos( 2.31759757085 +       70.84944530420*t);\r
+   uranus_x_0+=     0.00135340032 * cos( 5.51062460816 +       78.71375183040*t);\r
+   uranus_x_0+=     0.00119593859 * cos( 4.10138544267 +       39.61750834610*t);\r
+   uranus_x_0+=     0.00125105686 * cos( 2.51455273063 +      111.43016149680*t);\r
+   uranus_x_0+=     0.00111260244 * cos( 5.12252784325 +      222.86032299360*t);\r
+   uranus_x_0+=     0.00104619827 * cos( 3.90538916334 +      146.59425171800*t);\r
+   uranus_x_0+=     0.00110125387 * cos( 4.45473528724 +       35.16409022120*t);\r
+   uranus_x_0+=     0.00063584588 * cos( 0.29966233158 +      299.12639426920*t);\r
+   uranus_x_0+=     0.00053904041 * cos( 3.92590422507 +        3.93215326310*t);\r
+   uranus_x_0+=     0.00065066905 * cos( 3.73008452906 +      109.94568878850*t);\r
+   uranus_x_0+=     0.00039181662 * cos( 2.68841280769 +        4.45341812490*t);\r
+   uranus_x_0+=     0.00034341683 * cos( 3.03781661928 +      225.82926841020*t);\r
+   uranus_x_0+=     0.00033134636 * cos( 2.54201591218 +       65.22037101170*t);\r
+   uranus_x_0+=     0.00034555652 * cos( 1.84699329257 +       79.23501669220*t);\r
+   uranus_x_0+=     0.00033867050 * cos( 5.98418436103 +       70.32818044240*t);\r
+   uranus_x_0+=     0.00028371614 * cos( 2.58026657123 +      127.47179660680*t);\r
+   uranus_x_0+=     0.00035943348 * cos( 4.08754543016 +      202.25339517410*t);\r
+   uranus_x_0+=     0.00025208833 * cos( 5.30272144657 +        9.56122755560*t);\r
+   uranus_x_0+=     0.00023467802 * cos( 4.09729860322 +      145.63104387150*t);\r
+   uranus_x_0+=     0.00022963939 * cos( 5.51475073655 +       84.34282612290*t);\r
+   uranus_x_0+=     0.00031823951 * cos( 5.53948583244 +      152.53214255120*t);\r
+   uranus_x_0+=     0.00028384953 * cos( 6.01785430306 +      184.72728735580*t);\r
+   uranus_x_0+=     0.00026657176 * cos( 6.11027939727 +      160.60889739850*t);\r
+   uranus_x_0+=     0.00019676762 * cos( 5.53431398332 +       74.66972398270*t);\r
+   uranus_x_0+=     0.00019653873 * cos( 2.28660913421 +       74.89347315190*t);\r
+   uranus_x_0+=     0.00019954280 * cos( 0.57450958037 +       12.53017297220*t);\r
+   uranus_x_0+=     0.00018565067 * cos( 0.62225019017 +       52.69019803950*t);\r
+   uranus_x_0+=     0.00020084756 * cos( 4.47297488471 +       22.09140052780*t);\r
+   uranus_x_0+=     0.00019926329 * cos( 1.39878194708 +      112.91463420510*t);\r
+   uranus_x_0+=     0.00018575632 * cos( 5.70217475790 +       33.67961751290*t);\r
+   uranus_x_0+=     0.00016587870 * cos( 4.86920309163 +      108.46121608020*t);\r
+   uranus_x_0+=     0.00015171194 * cos( 2.88415453399 +       41.10198105440*t);\r
+   uranus_x_0+=     0.00011245800 * cos( 6.11597016146 +       71.60020482960*t);\r
+   uranus_x_0+=     0.00013948521 * cos( 6.27545694160 +      221.37585028530*t);\r
+   uranus_x_0+=     0.00010798350 * cos( 1.70031857078 +       77.96299230500*t);\r
+   uranus_x_0+=     0.00013593955 * cos( 2.55407820633 +       87.31177153950*t);\r
+   uranus_x_0+=     0.00011997848 * cos( 0.94875212305 +     1059.38193018920*t);\r
+   uranus_x_0+=     0.00012884351 * cos( 5.08737999470 +      145.10977900970*t);\r
+   uranus_x_0+=     0.00012394786 * cos( 6.21892878850 +       72.33391801250*t);\r
+   uranus_x_0+=     0.00012253318 * cos( 0.19452856525 +       36.64856292950*t);\r
+   uranus_x_0+=     0.00011538642 * cos( 1.77241794539 +       77.22927912210*t);\r
+   uranus_x_0+=     0.00008738409 * cos( 4.96956808452 +      186.21176006410*t);\r
+   uranus_x_0+=     0.00007095608 * cos( 1.30384750044 +      297.64192156090*t);\r
+   uranus_x_0+=     0.00006262602 * cos( 1.71385983783 +      153.49535039770*t);\r
+   uranus_x_0+=     0.00007487302 * cos( 0.11408470667 +      426.59819087600*t);\r
+   uranus_x_0+=     0.00007798974 * cos( 5.82410372587 +      340.77089204480*t);\r
+   uranus_x_0+=     0.00006669249 * cos( 5.08626589612 +       62.25142559510*t);\r
+   uranus_x_0+=     0.00005505358 * cos( 3.31282108025 +      140.00196957900*t);\r
+   uranus_x_0+=     0.00005372927 * cos( 4.12498282863 +       75.30286342910*t);\r
+   uranus_x_0+=     0.00005354242 * cos( 3.69263973447 +       74.26033370550*t);\r
+   uranus_x_0+=     0.00004478123 * cos( 1.11838191479 +       66.70484372000*t);\r
+   uranus_x_0+=     0.00004233075 * cos( 3.94913608184 +      265.98929347750*t);\r
+   uranus_x_0+=     0.00005038353 * cos( 4.68664376918 +       18.15924726470*t);\r
+   uranus_x_0+=     0.00004570470 * cos( 0.97536665751 +      183.24281464750*t);\r
+   uranus_x_0+=     0.00004751325 * cos( 4.95762395337 +       73.81839072080*t);\r
+   uranus_x_0+=     0.00004448651 * cos( 0.29436142982 +      114.39910691340*t);\r
+   uranus_x_0+=     0.00003312340 * cos( 0.52418923788 +       82.85835341460*t);\r
+   uranus_x_0+=     0.00004515952 * cos( 2.88576303120 +       75.74480641380*t);\r
+   uranus_x_0+=     0.00003559276 * cos( 1.47627607503 +        5.93789083320*t);\r
+   uranus_x_0+=     0.00003268117 * cos( 0.51827231333 +      220.41264243880*t);\r
+   uranus_x_0+=     0.00003578235 * cos( 1.11528903208 +      137.03302416240*t);\r
+   uranus_x_0+=     0.00003004737 * cos( 5.12122132051 +        7.11354700080*t);\r
+   uranus_x_0+=     0.00002882392 * cos( 2.76136583899 +      373.90799283650*t);\r
+   uranus_x_0+=     0.00002579454 * cos( 3.84784330333 +      277.03499374140*t);\r
+   uranus_x_0+=     0.00002597765 * cos( 0.22409539936 +       96.87299909510*t);\r
+   uranus_x_0+=     0.00002560744 * cos( 4.44236223450 +       80.19822453870*t);\r
+   uranus_x_0+=     0.00002722745 * cos( 6.09456175016 +      106.97674337190*t);\r
+   uranus_x_0+=     0.00002528025 * cos( 0.89508396542 +       68.84370773410*t);\r
+   uranus_x_0+=     0.00002631138 * cos( 0.04831552531 +      305.34616939270*t);\r
+   uranus_x_0+=     0.00002541716 * cos( 0.64495056482 +       32.19514480460*t);\r
+   uranus_x_0+=     0.00002241129 * cos( 5.22377697501 +        3.18139373770*t);\r
+   uranus_x_0+=     0.00001965145 * cos( 0.09207526632 +       20.60692781950*t);\r
+   uranus_x_0+=     0.00002232022 * cos( 0.63571664756 +       80.71948940050*t);\r
+   uranus_x_0+=     0.00001933814 * cos( 5.75490033864 +       74.73341445750*t);\r
+   uranus_x_0+=     0.00001933817 * cos( 2.06557585395 +       74.82978267710*t);\r
+   uranus_x_0+=     0.00002138391 * cos( 4.20897429922 +       74.52096613640*t);\r
+   uranus_x_0+=     0.00002126427 * cos( 3.61171465436 +       75.04223099820*t);\r
+   uranus_x_0+=     0.00002215516 * cos( 2.18613112875 +      259.50888592310*t);\r
+   uranus_x_0+=     0.00001891213 * cos( 5.49941424248 +      300.61086697750*t);\r
+   uranus_x_0+=     0.00001927679 * cos( 1.29228021932 +      159.12442469020*t);\r
+   uranus_x_0+=     0.00001796558 * cos( 5.73271543335 +       74.62153987290*t);\r
+   uranus_x_0+=     0.00001792522 * cos( 2.08789166984 +       74.94165726170*t);\r
+   uranus_x_0+=     0.00001873542 * cos( 4.23391867169 +      206.18554843720*t);\r
+   uranus_x_0+=     0.00002182901 * cos( 1.23755478345 +      479.28838891550*t);\r
+   uranus_x_0+=     0.00001860591 * cos( 1.67536711716 +       42.58645376270*t);\r
+   uranus_x_0+=     0.00002075591 * cos( 3.15586933464 +      131.40394986990*t);\r
+   uranus_x_0+=     0.00001912582 * cos( 5.83091918696 +       14.97785352700*t);\r
+   uranus_x_0+=     0.00001529180 * cos( 2.05204104820 +      191.20769491020*t);\r
+   uranus_x_0+=     0.00002064173 * cos( 3.60208606410 +      835.03713448730*t);\r
+   uranus_x_0+=     0.00001768763 * cos( 1.19254481620 +      219.89137757700*t);\r
+   uranus_x_0+=     0.00001892359 * cos( 4.32128621847 +      154.01661525950*t);\r
+   uranus_x_0+=     0.00001797047 * cos( 1.73417465594 +      227.31374111850*t);\r
+   uranus_x_0+=     0.00001558489 * cos( 6.16891070489 +       59.80374504030*t);\r
+   uranus_x_0+=     0.00001672893 * cos( 0.01232646186 +      143.62530630140*t);\r
+   uranus_x_0+=     0.00001467268 * cos( 2.10975578758 +        2.44768055480*t);\r
+   uranus_x_0+=     0.00001677659 * cos( 0.42525121334 +        8.07675484730*t);\r
+   uranus_x_0+=     0.00001347303 * cos( 5.46763140224 +      288.08069400530*t);\r
+   uranus_x_0+=     0.00001744555 * cos( 0.82022450313 +       56.62235130260*t);\r
+   uranus_x_0+=     0.00001427180 * cos( 0.38786175669 +       92.94084583200*t);\r
+   uranus_x_0+=     0.00001263032 * cos( 5.63689596853 +      404.50679034820*t);\r
+   uranus_x_0+=     0.00001221506 * cos( 5.20012455894 +       54.17467074780*t);\r
+   uranus_x_0+=     0.00001574905 * cos( 5.72297800263 +       39.35687591520*t);\r
+   uranus_x_0+=     0.00001269686 * cos( 2.66330104031 +      142.44965013380*t);\r
+   uranus_x_0+=     0.00001438869 * cos( 0.72633739717 +      522.57741809380*t);\r
+   uranus_x_0+=     0.00001408997 * cos( 3.61751904356 +      536.80451209540*t);\r
+   uranus_x_0+=     0.00001418300 * cos( 2.29718712012 +      235.39049596580*t);\r
+   uranus_x_0+=     0.00001040948 * cos( 2.74644165501 +        5.41662597140*t);\r
+   uranus_x_0+=     0.00001256867 * cos( 5.61684736425 +       67.66805156650*t);\r
+   uranus_x_0+=     0.00001164218 * cos( 2.08302637541 +       81.89514556810*t);\r
+   uranus_x_0+=     0.00001009252 * cos( 2.02320517037 +       74.03083904190*t);\r
+   uranus_x_0+=     0.00001077810 * cos( 1.05685900920 +      128.95626931510*t);\r
+   uranus_x_0+=     0.00001212025 * cos( 3.41577832660 +      211.81462272970*t);\r
+   uranus_x_0+=     0.00001223648 * cos( 3.84373514640 +      187.69623277240*t);\r
+   uranus_x_0+=     0.00001334371 * cos( 2.17621743689 +      380.12776796000*t);\r
+   uranus_x_0+=     0.00001144565 * cos( 2.42148845239 +      296.15744885260*t);\r
+   uranus_x_0+=     0.00001241589 * cos( 1.81282962357 +      134.58534360760*t);\r
+   uranus_x_0+=     0.00001192274 * cos( 5.58661990233 +       50.40257617910*t);\r
+   uranus_x_0+=     0.00001093678 * cos( 3.94451812233 +      230.56457082540*t);\r
+   uranus_x_0+=     0.00001077916 * cos( 5.01417740021 +      181.75834193920*t);\r
+   uranus_x_0+=     0.00001166898 * cos( 2.02955848543 +      110.20632121940*t);\r
+   uranus_x_0+=     0.00001056848 * cos( 5.53440854164 +       14.01464568050*t);\r
+\r
+   double uranus_x_1=0.0;\r
+   uranus_x_1+=     0.00739730021 * cos( 6.01067825116 +      149.56319713460*t);\r
+   uranus_x_1+=     0.00526878306 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_x_1+=     0.00239840801 * cos( 5.33657762707 +       73.29712585900*t);\r
+   uranus_x_1+=     0.00229676787 * cos( 2.48204455775 +       76.26607127560*t);\r
+   uranus_x_1+=     0.00111045158 * cos( 5.57157235960 +       11.04570026390*t);\r
+   uranus_x_1+=     0.00096352822 * cos( 0.35070389084 +       63.73589830340*t);\r
+   uranus_x_1+=     0.00081511870 * cos( 1.21058618039 +       85.82729883120*t);\r
+   uranus_x_1+=     0.00045687564 * cos( 2.29216583843 +      138.51749687070*t);\r
+   uranus_x_1+=     0.00051382501 * cos( 2.18935125260 +      224.34479570190*t);\r
+   uranus_x_1+=     0.00038844330 * cos( 0.30724575951 +       70.84944530420*t);\r
+   uranus_x_1+=     0.00036158493 * cos( 1.23634798757 +       78.71375183040*t);\r
+   uranus_x_1+=     0.00032333094 * cos( 5.06666556704 +       74.78159856730*t);\r
+   uranus_x_1+=     0.00021685656 * cos( 4.93710968392 +      151.04766984290*t);\r
+   uranus_x_1+=     0.00019441970 * cos( 1.30617490304 +       77.75054398390*t);\r
+   uranus_x_1+=     0.00017376241 * cos( 0.24607221230 +       71.81265315070*t);\r
+   uranus_x_1+=     0.00015211071 * cos( 5.53141633140 +        3.93215326310*t);\r
+   uranus_x_1+=     0.00007735984 * cos( 1.61349552789 +       71.60020482960*t);\r
+   uranus_x_1+=     0.00007425078 * cos( 6.20357977116 +       77.96299230500*t);\r
+   uranus_x_1+=     0.00006995857 * cos( 2.40633283814 +      145.63104387150*t);\r
+   uranus_x_1+=     0.00007291691 * cos( 2.23597571444 +        2.96894541660*t);\r
+   uranus_x_1+=     0.00007585264 * cos( 2.76074218330 +      148.07872442630*t);\r
+   uranus_x_1+=     0.00004378335 * cos( 3.74296322240 +      160.60889739850*t);\r
+   uranus_x_1+=     0.00004127713 * cos( 1.48475181305 +       22.09140052780*t);\r
+   uranus_x_1+=     0.00003933541 * cos( 4.73864204208 +       65.22037101170*t);\r
+   uranus_x_1+=     0.00002910312 * cos( 5.91941333050 +      127.47179660680*t);\r
+   uranus_x_1+=     0.00002788434 * cos( 4.90117297196 +      213.29909543800*t);\r
+   uranus_x_1+=     0.00002802392 * cos( 3.76505436434 +       52.69019803950*t);\r
+   uranus_x_1+=     0.00002545264 * cos( 3.36768337628 +        9.56122755560*t);\r
+   uranus_x_1+=     0.00002647073 * cos( 4.53813176345 +       12.53017297220*t);\r
+   uranus_x_1+=     0.00003177614 * cos( 4.65226634926 +      299.12639426920*t);\r
+   uranus_x_1+=     0.00002228396 * cos( 0.18087986338 +       87.31177153950*t);\r
+   uranus_x_1+=     0.00002824668 * cos( 3.40143685673 +       84.34282612290*t);\r
+   uranus_x_1+=     0.00002525203 * cos( 2.83821144961 +       18.15924726470*t);\r
+   uranus_x_1+=     0.00002216987 * cos( 4.78338909951 +       72.33391801250*t);\r
+   uranus_x_1+=     0.00001624493 * cos( 3.75817281127 +      153.49535039770*t);\r
+   uranus_x_1+=     0.00001928881 * cos( 2.39940180311 +       39.61750834610*t);\r
+   uranus_x_1+=     0.00001555444 * cos( 4.13741667297 +       73.81839072080*t);\r
+   uranus_x_1+=     0.00001600865 * cos( 0.08376247543 +       79.23501669220*t);\r
+   uranus_x_1+=     0.00001476317 * cos( 3.67283851029 +       75.74480641380*t);\r
+   uranus_x_1+=     0.00001427088 * cos( 1.45690759014 +       70.32818044240*t);\r
+   uranus_x_1+=     0.00001533469 * cos( 3.71776498048 +      152.53214255120*t);\r
+   uranus_x_1+=     0.00001747266 * cos( 3.24870046809 +       77.22927912210*t);\r
+   uranus_x_1+=     0.00001716831 * cos( 3.39415662657 +      222.86032299360*t);\r
+   uranus_x_1+=     0.00001707504 * cos( 1.11296012106 +      225.82926841020*t);\r
+   uranus_x_1+=     0.00001734228 * cos( 5.39619902298 +      146.59425171800*t);\r
+   uranus_x_1+=     0.00001476617 * cos( 3.53047075439 +        3.18139373770*t);\r
+   uranus_x_1+=     0.00001179645 * cos( 5.13953276367 +      220.41264243880*t);\r
+   uranus_x_1+=     0.00001239315 * cos( 0.98221206501 +        4.45341812490*t);\r
+   uranus_x_1+=     0.00001099691 * cos( 1.35138854505 +       62.25142559510*t);\r
+   uranus_x_1+=     0.00001061312 * cos( 5.00125105380 +      131.40394986990*t);\r
+   uranus_x_1+=     0.00001029261 * cos( 5.42434597865 +      109.94568878850*t);\r
+   uranus_x_1=uranus_x_1 * t;\r
+\r
+   double uranus_x_2=0.0;\r
+   uranus_x_2+=     0.00016015732 * cos( 3.83700026619 +       74.78159856730*t);\r
+   uranus_x_2+=     0.00010915299 * cos( 3.02987776270 +      149.56319713460*t);\r
+   uranus_x_2+=     0.00007497619 * cos( 3.83429136661 +       11.04570026390*t);\r
+   uranus_x_2+=     0.00008053623 * cos( 2.54646146122 +       63.73589830340*t);\r
+   uranus_x_2+=     0.00005408033 * cos( 4.78033642303 +       70.84944530420*t);\r
+   uranus_x_2+=     0.00005021971 * cos( 3.04632772928 +       78.71375183040*t);\r
+   uranus_x_2+=     0.00006717313 * cos( 5.31264214501 +       85.82729883120*t);\r
+   uranus_x_2+=     0.00005284684 * cos( 2.11901942097 +       73.29712585900*t);\r
+   uranus_x_2+=     0.00004874936 * cos( 5.68616132176 +       76.26607127560*t);\r
+   uranus_x_2+=     0.00003002124 * cos( 4.07944398452 +      138.51749687070*t);\r
+   uranus_x_2+=     0.00002521797 * cos( 3.36028253173 +       71.60020482960*t);\r
+   uranus_x_2+=     0.00002413832 * cos( 4.45865225690 +       77.96299230500*t);\r
+   uranus_x_2+=     0.00002221373 * cos( 0.87427485235 +        3.93215326310*t);\r
+   uranus_x_2+=     0.00002291767 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_x_2+=     0.00001040250 * cos( 0.73133408837 +      145.63104387150*t);\r
+   uranus_x_2+=     0.00001046100 * cos( 5.85311910228 +      224.34479570190*t);\r
+   uranus_x_2=uranus_x_2 * t * t;\r
+\r
+   double uranus_x_3=0.0;\r
+   uranus_x_3+=     0.00001307049 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_x_3=uranus_x_3 * t * t * t;\r
+\r
+   return uranus_x_0+uranus_x_1+uranus_x_2+uranus_x_3;\r
+}\r
+\r
+double vsop87a_milli_uranus_y(double t){\r
+   double uranus_y_0=0.0;\r
+   uranus_y_0+=    19.16518231584 * cos( 3.91045677002 +       74.78159856730*t);\r
+   uranus_y_0+=     0.44390465203 * cos( 0.08884111329 +      149.56319713460*t);\r
+   uranus_y_0+=     0.16256125476 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_y_0+=     0.14755940186 * cos( 1.85423280679 +       73.29712585900*t);\r
+   uranus_y_0+=     0.14123958128 * cos( 2.82486076549 +       76.26607127560*t);\r
+   uranus_y_0+=     0.06250078231 * cos( 3.56960243857 +        1.48447270830*t);\r
+   uranus_y_0+=     0.01542668264 * cos( 2.55040539213 +      224.34479570190*t);\r
+   uranus_y_0+=     0.01442356575 * cos( 1.08004542712 +      148.07872442630*t);\r
+   uranus_y_0+=     0.00938975501 * cos( 0.09275714761 +       11.04570026390*t);\r
+   uranus_y_0+=     0.00650331846 * cos( 2.76142680222 +       63.73589830340*t);\r
+   uranus_y_0+=     0.00657343120 * cos( 5.28830704469 +      151.04766984290*t);\r
+   uranus_y_0+=     0.00621326770 * cos( 1.48795811387 +       77.75054398390*t);\r
+   uranus_y_0+=     0.00541961958 * cos( 3.24476486661 +       71.81265315070*t);\r
+   uranus_y_0+=     0.00547472694 * cos( 2.06037924573 +       85.82729883120*t);\r
+   uranus_y_0+=     0.00459589120 * cos( 2.33745536070 +        2.96894541660*t);\r
+   uranus_y_0+=     0.00495936105 * cos( 5.31205753740 +      529.69096509460*t);\r
+   uranus_y_0+=     0.00387922853 * cos( 4.62026923885 +      138.51749687070*t);\r
+   uranus_y_0+=     0.00268363417 * cos( 5.68085299020 +      213.29909543800*t);\r
+   uranus_y_0+=     0.00216239629 * cos( 3.73800767580 +       38.13303563780*t);\r
+   uranus_y_0+=     0.00144032475 * cos( 0.75015700920 +       70.84944530420*t);\r
+   uranus_y_0+=     0.00135290820 * cos( 3.93970260616 +       78.71375183040*t);\r
+   uranus_y_0+=     0.00119670613 * cos( 2.53058783780 +       39.61750834610*t);\r
+   uranus_y_0+=     0.00124868545 * cos( 0.94315917319 +      111.43016149680*t);\r
+   uranus_y_0+=     0.00111204860 * cos( 3.55163219419 +      222.86032299360*t);\r
+   uranus_y_0+=     0.00104507929 * cos( 2.33345675603 +      146.59425171800*t);\r
+   uranus_y_0+=     0.00108584454 * cos( 6.02234848388 +       35.16409022120*t);\r
+   uranus_y_0+=     0.00063573747 * cos( 5.01204967920 +      299.12639426920*t);\r
+   uranus_y_0+=     0.00053289771 * cos( 2.38437587876 +        3.93215326310*t);\r
+   uranus_y_0+=     0.00063774261 * cos( 2.15607602904 +      109.94568878850*t);\r
+   uranus_y_0+=     0.00039218598 * cos( 1.11841109252 +        4.45341812490*t);\r
+   uranus_y_0+=     0.00034205426 * cos( 0.92405922576 +       65.22037101170*t);\r
+   uranus_y_0+=     0.00034334377 * cos( 1.46696169843 +      225.82926841020*t);\r
+   uranus_y_0+=     0.00034538316 * cos( 0.27613780697 +       79.23501669220*t);\r
+   uranus_y_0+=     0.00039256771 * cos( 5.75956853703 +      202.25339517410*t);\r
+   uranus_y_0+=     0.00026157754 * cos( 3.74097610798 +        9.56122755560*t);\r
+   uranus_y_0+=     0.00023427328 * cos( 2.52740125551 +      145.63104387150*t);\r
+   uranus_y_0+=     0.00022933138 * cos( 3.94455540350 +       84.34282612290*t);\r
+   uranus_y_0+=     0.00031816303 * cos( 3.96860170484 +      152.53214255120*t);\r
+   uranus_y_0+=     0.00025237176 * cos( 4.45141413666 +       70.32818044240*t);\r
+   uranus_y_0+=     0.00028372491 * cos( 4.44714627097 +      184.72728735580*t);\r
+   uranus_y_0+=     0.00026652859 * cos( 4.53944395347 +      160.60889739850*t);\r
+   uranus_y_0+=     0.00019666208 * cos( 3.96350065335 +       74.66972398270*t);\r
+   uranus_y_0+=     0.00019643845 * cos( 0.71577796385 +       74.89347315190*t);\r
+   uranus_y_0+=     0.00019838981 * cos( 5.29113397354 +       12.53017297220*t);\r
+   uranus_y_0+=     0.00021523908 * cos( 4.93565132068 +       36.64856292950*t);\r
+   uranus_y_0+=     0.00015537967 * cos( 1.87863275460 +       52.69019803950*t);\r
+   uranus_y_0+=     0.00020115100 * cos( 3.45473780762 +      127.47179660680*t);\r
+   uranus_y_0+=     0.00020051641 * cos( 2.90386352937 +       22.09140052780*t);\r
+   uranus_y_0+=     0.00019901477 * cos( 6.11075402434 +      112.91463420510*t);\r
+   uranus_y_0+=     0.00018126776 * cos( 0.98478853787 +       33.67961751290*t);\r
+   uranus_y_0+=     0.00015174962 * cos( 1.31314034959 +       41.10198105440*t);\r
+   uranus_y_0+=     0.00011239020 * cos( 4.54508334011 +       71.60020482960*t);\r
+   uranus_y_0+=     0.00013948849 * cos( 4.70474945682 +      221.37585028530*t);\r
+   uranus_y_0+=     0.00010819728 * cos( 0.12807029856 +       77.96299230500*t);\r
+   uranus_y_0+=     0.00013589665 * cos( 0.98313719930 +       87.31177153950*t);\r
+   uranus_y_0+=     0.00011996772 * cos( 5.66129275335 +     1059.38193018920*t);\r
+   uranus_y_0+=     0.00012407787 * cos( 4.64945783340 +       72.33391801250*t);\r
+   uranus_y_0+=     0.00011531140 * cos( 0.20190074645 +       77.22927912210*t);\r
+   uranus_y_0+=     0.00008736150 * cos( 3.39874828293 +      186.21176006410*t);\r
+   uranus_y_0+=     0.00007093587 * cos( 6.01613487245 +      297.64192156090*t);\r
+   uranus_y_0+=     0.00006408245 * cos( 3.93246367895 +       62.25142559510*t);\r
+   uranus_y_0+=     0.00006261153 * cos( 0.14258542752 +      153.49535039770*t);\r
+   uranus_y_0+=     0.00007494000 * cos( 4.82565771386 +      426.59819087600*t);\r
+   uranus_y_0+=     0.00007856014 * cos( 1.12354254831 +      340.77089204480*t);\r
+   uranus_y_0+=     0.00005516018 * cos( 1.73758326119 +      140.00196957900*t);\r
+   uranus_y_0+=     0.00005556643 * cos( 3.68095215063 +      145.10977900970*t);\r
+   uranus_y_0+=     0.00005368405 * cos( 2.55422957958 +       75.30286342910*t);\r
+   uranus_y_0+=     0.00005350948 * cos( 2.12171493922 +       74.26033370550*t);\r
+   uranus_y_0+=     0.00004508794 * cos( 5.82224064821 +       66.70484372000*t);\r
+   uranus_y_0+=     0.00004290374 * cos( 5.54490766551 +      265.98929347750*t);\r
+   uranus_y_0+=     0.00005013871 * cos( 3.11907749268 +       18.15924726470*t);\r
+   uranus_y_0+=     0.00004326138 * cos( 5.70135056853 +      183.24281464750*t);\r
+   uranus_y_0+=     0.00004750018 * cos( 3.38678300054 +       73.81839072080*t);\r
+   uranus_y_0+=     0.00004445347 * cos( 5.00638490308 +      114.39910691340*t);\r
+   uranus_y_0+=     0.00003314154 * cos( 5.23054574329 +       82.85835341460*t);\r
+   uranus_y_0+=     0.00004509054 * cos( 1.31254342829 +       75.74480641380*t);\r
+   uranus_y_0+=     0.00003553107 * cos( 6.18906516846 +        5.93789083320*t);\r
+   uranus_y_0+=     0.00003265634 * cos( 5.23063560176 +      220.41264243880*t);\r
+   uranus_y_0+=     0.00003575435 * cos( 5.83994849224 +      137.03302416240*t);\r
+   uranus_y_0+=     0.00002880734 * cos( 1.19038424330 +      373.90799283650*t);\r
+   uranus_y_0+=     0.00002885443 * cos( 3.50279993038 +        7.11354700080*t);\r
+   uranus_y_0+=     0.00002594155 * cos( 4.93691413537 +       96.87299909510*t);\r
+   uranus_y_0+=     0.00002559357 * cos( 2.87184237678 +       80.19822453870*t);\r
+   uranus_y_0+=     0.00002676616 * cos( 1.61805362044 +      305.34616939270*t);\r
+   uranus_y_0+=     0.00002246530 * cos( 5.80081898763 +      108.46121608020*t);\r
+   uranus_y_0+=     0.00002474483 * cos( 2.21173751117 +       32.19514480460*t);\r
+   uranus_y_0+=     0.00002060991 * cos( 6.24178596384 +       56.62235130260*t);\r
+   uranus_y_0+=     0.00001958711 * cos( 4.80807045815 +       20.60692781950*t);\r
+   uranus_y_0+=     0.00002227451 * cos( 5.34765264557 +       80.71948940050*t);\r
+   uranus_y_0+=     0.00001937874 * cos( 0.49529839431 +       74.82978267710*t);\r
+   uranus_y_0+=     0.00001937871 * cos( 4.18462288684 +       74.73341445750*t);\r
+   uranus_y_0+=     0.00002164382 * cos( 0.47581392325 +        3.18139373770*t);\r
+   uranus_y_0+=     0.00002138407 * cos( 2.63817804331 +       74.52096613640*t);\r
+   uranus_y_0+=     0.00002130909 * cos( 2.04143912495 +       75.04223099820*t);\r
+   uranus_y_0+=     0.00001787737 * cos( 0.32096699926 +        2.44768055480*t);\r
+   uranus_y_0+=     0.00002212861 * cos( 0.61491281306 +      259.50888592310*t);\r
+   uranus_y_0+=     0.00001889369 * cos( 3.92852240171 +      300.61086697750*t);\r
+   uranus_y_0+=     0.00002275258 * cos( 1.55666401505 +      131.40394986990*t);\r
+   uranus_y_0+=     0.00001925946 * cos( 6.00527473515 +      159.12442469020*t);\r
+   uranus_y_0+=     0.00001802494 * cos( 4.16218259902 +       74.62153987290*t);\r
+   uranus_y_0+=     0.00001796292 * cos( 0.51761494342 +       74.94165726170*t);\r
+   uranus_y_0+=     0.00002240648 * cos( 0.47739127862 +      181.75834193920*t);\r
+   uranus_y_0+=     0.00001924499 * cos( 2.64284880495 +      206.18554843720*t);\r
+   uranus_y_0+=     0.00001626134 * cos( 3.70023731184 +      191.20769491020*t);\r
+   uranus_y_0+=     0.00001860824 * cos( 0.10445996392 +       42.58645376270*t);\r
+   uranus_y_0+=     0.00002177437 * cos( 2.80437422101 +      479.28838891550*t);\r
+   uranus_y_0+=     0.00001896184 * cos( 4.26975898003 +       14.97785352700*t);\r
+   uranus_y_0+=     0.00002045249 * cos( 5.17400788104 +      835.03713448730*t);\r
+   uranus_y_0+=     0.00001887812 * cos( 2.75000237791 +      154.01661525950*t);\r
+   uranus_y_0+=     0.00001794754 * cos( 0.16290844853 +      227.31374111850*t);\r
+   uranus_y_0+=     0.00001347410 * cos( 3.89237011696 +      288.08069400530*t);\r
+   uranus_y_0+=     0.00001572826 * cos( 5.93367812903 +      219.89137757700*t);\r
+   uranus_y_0+=     0.00001424804 * cos( 5.10004758033 +       92.94084583200*t);\r
+   uranus_y_0+=     0.00001267766 * cos( 0.92771324396 +      404.50679034820*t);\r
+   uranus_y_0+=     0.00001588897 * cos( 4.15115668974 +       39.35687591520*t);\r
+   uranus_y_0+=     0.00001269786 * cos( 1.09685727529 +      142.44965013380*t);\r
+   uranus_y_0+=     0.00001291065 * cos( 5.67425699047 +       68.84370773410*t);\r
+   uranus_y_0+=     0.00001436850 * cos( 5.44312198350 +      522.57741809380*t);\r
+   uranus_y_0+=     0.00001405564 * cos( 2.04677392527 +      536.80451209540*t);\r
+   uranus_y_0+=     0.00001416917 * cos( 0.72597245494 +      235.39049596580*t);\r
+   uranus_y_0+=     0.00001165315 * cos( 0.51071041452 +       81.89514556810*t);\r
+   uranus_y_0+=     0.00001035262 * cos( 1.20639876458 +        5.41662597140*t);\r
+   uranus_y_0+=     0.00001009454 * cos( 0.45375065997 +       74.03083904190*t);\r
+   uranus_y_0+=     0.00001220696 * cos( 1.84988185963 +      211.81462272970*t);\r
+   uranus_y_0+=     0.00001222886 * cos( 2.27306099902 +      187.69623277240*t);\r
+   uranus_y_0+=     0.00001336792 * cos( 3.74888989756 +      380.12776796000*t);\r
+   uranus_y_0+=     0.00001151803 * cos( 0.46579056125 +      128.95626931510*t);\r
+   uranus_y_0+=     0.00001149114 * cos( 0.85101218281 +      296.15744885260*t);\r
+   uranus_y_0+=     0.00001163762 * cos( 5.51157783762 +      230.56457082540*t);\r
+   uranus_y_0+=     0.00001192292 * cos( 0.68084398426 +       99.16062095550*t);\r
+   uranus_y_0+=     0.00001151286 * cos( 4.01147735438 +       67.66805156650*t);\r
+   uranus_y_0+=     0.00001189801 * cos( 4.01778306134 +       50.40257617910*t);\r
+   uranus_y_0+=     0.00001015998 * cos( 1.00290501307 +       35.42472265210*t);\r
+   uranus_y_0+=     0.00001174953 * cos( 0.45683512473 +      110.20632121940*t);\r
+   uranus_y_0+=     0.00001051606 * cos( 3.96907647535 +       14.01464568050*t);\r
+\r
+   double uranus_y_1=0.0;\r
+   uranus_y_1+=     0.02157896385 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_y_1+=     0.00739227349 * cos( 4.43963890935 +      149.56319713460*t);\r
+   uranus_y_1+=     0.00238545685 * cos( 3.76882493145 +       73.29712585900*t);\r
+   uranus_y_1+=     0.00229396424 * cos( 0.91090183978 +       76.26607127560*t);\r
+   uranus_y_1+=     0.00110137111 * cos( 4.00844441616 +       11.04570026390*t);\r
+   uranus_y_1+=     0.00094979054 * cos( 5.07141537066 +       63.73589830340*t);\r
+   uranus_y_1+=     0.00081474163 * cos( 5.92275367106 +       85.82729883120*t);\r
+   uranus_y_1+=     0.00045457174 * cos( 0.73292241207 +      138.51749687070*t);\r
+   uranus_y_1+=     0.00051366974 * cos( 0.61844114994 +      224.34479570190*t);\r
+   uranus_y_1+=     0.00038296005 * cos( 5.01873578671 +       70.84944530420*t);\r
+   uranus_y_1+=     0.00036146116 * cos( 5.94859452787 +       78.71375183040*t);\r
+   uranus_y_1+=     0.00032420558 * cos( 4.32617271732 +       74.78159856730*t);\r
+   uranus_y_1+=     0.00021673269 * cos( 3.36607263522 +      151.04766984290*t);\r
+   uranus_y_1+=     0.00019425087 * cos( 6.01842187783 +       77.75054398390*t);\r
+   uranus_y_1+=     0.00017393206 * cos( 4.96098895488 +       71.81265315070*t);\r
+   uranus_y_1+=     0.00014991169 * cos( 3.97176856758 +        3.93215326310*t);\r
+   uranus_y_1+=     0.00007732367 * cos( 0.04256630122 +       71.60020482960*t);\r
+   uranus_y_1+=     0.00007438492 * cos( 4.63165436478 +       77.96299230500*t);\r
+   uranus_y_1+=     0.00006979238 * cos( 0.83723520791 +      145.63104387150*t);\r
+   uranus_y_1+=     0.00007321559 * cos( 0.66348425538 +        2.96894541660*t);\r
+   uranus_y_1+=     0.00007595636 * cos( 1.19807643487 +      148.07872442630*t);\r
+   uranus_y_1+=     0.00004376824 * cos( 2.17182724016 +      160.60889739850*t);\r
+   uranus_y_1+=     0.00003962568 * cos( 3.18042711824 +       65.22037101170*t);\r
+   uranus_y_1+=     0.00004117202 * cos( 6.19931612790 +       22.09140052780*t);\r
+   uranus_y_1+=     0.00002830313 * cos( 3.34365222278 +      213.29909543800*t);\r
+   uranus_y_1+=     0.00002690065 * cos( 1.78946471198 +        9.56122755560*t);\r
+   uranus_y_1+=     0.00002628159 * cos( 2.97459067399 +       12.53017297220*t);\r
+   uranus_y_1+=     0.00003174617 * cos( 3.08131638838 +      299.12639426920*t);\r
+   uranus_y_1+=     0.00002227510 * cos( 4.89407437055 +       87.31177153950*t);\r
+   uranus_y_1+=     0.00002821206 * cos( 1.83130010947 +       84.34282612290*t);\r
+   uranus_y_1+=     0.00002510693 * cos( 1.27166561854 +       18.15924726470*t);\r
+   uranus_y_1+=     0.00002224655 * cos( 3.21541108798 +       72.33391801250*t);\r
+   uranus_y_1+=     0.00001929662 * cos( 4.68383962079 +       52.69019803950*t);\r
+   uranus_y_1+=     0.00001707606 * cos( 5.60415260609 +      127.47179660680*t);\r
+   uranus_y_1+=     0.00001620819 * cos( 2.18676505386 +      153.49535039770*t);\r
+   uranus_y_1+=     0.00001926926 * cos( 0.82821252139 +       39.61750834610*t);\r
+   uranus_y_1+=     0.00001555611 * cos( 2.56681954823 +       73.81839072080*t);\r
+   uranus_y_1+=     0.00001595703 * cos( 4.79558057860 +       79.23501669220*t);\r
+   uranus_y_1+=     0.00001476550 * cos( 2.10111459539 +       75.74480641380*t);\r
+   uranus_y_1+=     0.00001531355 * cos( 2.14701519407 +      152.53214255120*t);\r
+   uranus_y_1+=     0.00001744564 * cos( 1.67897185084 +       77.22927912210*t);\r
+   uranus_y_1+=     0.00001713945 * cos( 1.82334975258 +      222.86032299360*t);\r
+   uranus_y_1+=     0.00001705184 * cos( 5.82532917611 +      225.82926841020*t);\r
+   uranus_y_1+=     0.00001739019 * cos( 3.82452086292 +      146.59425171800*t);\r
+   uranus_y_1+=     0.00001291753 * cos( 6.19666243545 +       70.32818044240*t);\r
+   uranus_y_1+=     0.00001458526 * cos( 5.10147126404 +        3.18139373770*t);\r
+   uranus_y_1+=     0.00001179966 * cos( 3.56807126055 +      220.41264243880*t);\r
+   uranus_y_1+=     0.00001234914 * cos( 5.69239889831 +        4.45341812490*t);\r
+   uranus_y_1+=     0.00001075108 * cos( 1.76286452034 +       56.62235130260*t);\r
+   uranus_y_1+=     0.00001035661 * cos( 6.12642568708 +       62.25142559510*t);\r
+   uranus_y_1+=     0.00001114523 * cos( 3.41304662369 +      131.40394986990*t);\r
+   uranus_y_1+=     0.00001050191 * cos( 3.84176879347 +      109.94568878850*t);\r
+   uranus_y_1=uranus_y_1 * t;\r
+\r
+   double uranus_y_2=0.0;\r
+   uranus_y_2+=     0.00034812647 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_y_2+=     0.00016589194 * cos( 2.29556740620 +       74.78159856730*t);\r
+   uranus_y_2+=     0.00010905147 * cos( 1.45737963668 +      149.56319713460*t);\r
+   uranus_y_2+=     0.00007484633 * cos( 2.27968076918 +       11.04570026390*t);\r
+   uranus_y_2+=     0.00007964298 * cos( 0.97230247087 +       63.73589830340*t);\r
+   uranus_y_2+=     0.00005307100 * cos( 3.20519221878 +       70.84944530420*t);\r
+   uranus_y_2+=     0.00005018595 * cos( 1.47518527303 +       78.71375183040*t);\r
+   uranus_y_2+=     0.00006713255 * cos( 3.74148881189 +       85.82729883120*t);\r
+   uranus_y_2+=     0.00005265170 * cos( 0.54901216905 +       73.29712585900*t);\r
+   uranus_y_2+=     0.00004864822 * cos( 4.11367426823 +       76.26607127560*t);\r
+   uranus_y_2+=     0.00002995853 * cos( 2.49432193549 +      138.51749687070*t);\r
+   uranus_y_2+=     0.00002519021 * cos( 1.78896824345 +       71.60020482960*t);\r
+   uranus_y_2+=     0.00002418371 * cos( 2.88675006488 +       77.96299230500*t);\r
+   uranus_y_2+=     0.00002185856 * cos( 5.58862614977 +        3.93215326310*t);\r
+   uranus_y_2+=     0.00001035578 * cos( 5.44752448275 +      145.63104387150*t);\r
+   uranus_y_2+=     0.00001044459 * cos( 4.27972239360 +      224.34479570190*t);\r
+   uranus_y_2=uranus_y_2 * t * t;\r
+\r
+   double uranus_y_3=0.0;\r
+   uranus_y_3+=     0.00001211380 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_y_3=uranus_y_3 * t * t * t;\r
+\r
+   return uranus_y_0+uranus_y_1+uranus_y_2+uranus_y_3;\r
+}\r
+\r
+double vsop87a_milli_uranus_z(double t){\r
+   double uranus_z_0=0.0;\r
+   uranus_z_0+=     0.25878127698 * cos( 2.61861272578 +       74.78159856730*t);\r
+   uranus_z_0+=     0.01774318778 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_z_0+=     0.00599316131 * cos( 5.08119500585 +      149.56319713460*t);\r
+   uranus_z_0+=     0.00190281890 * cos( 1.61643841193 +       76.26607127560*t);\r
+   uranus_z_0+=     0.00190881685 * cos( 0.57869575952 +       73.29712585900*t);\r
+   uranus_z_0+=     0.00084626761 * cos( 2.26030150166 +        1.48447270830*t);\r
+   uranus_z_0+=     0.00030734257 * cos( 0.23571721555 +       63.73589830340*t);\r
+   uranus_z_0+=     0.00020842052 * cos( 1.26054208091 +      224.34479570190*t);\r
+   uranus_z_0+=     0.00019734273 * cos( 6.04314677688 +      148.07872442630*t);\r
+   uranus_z_0+=     0.00012537530 * cos( 5.17169051466 +       11.04570026390*t);\r
+   uranus_z_0+=     0.00014582864 * cos( 6.14852037212 +       71.81265315070*t);\r
+   uranus_z_0+=     0.00010407529 * cos( 3.65320417038 +      213.29909543800*t);\r
+   uranus_z_0+=     0.00011261541 * cos( 3.55973769686 +      529.69096509460*t);\r
+   uranus_z_0+=     0.00008855669 * cos( 4.03774505739 +      151.04766984290*t);\r
+   uranus_z_0+=     0.00008239460 * cos( 0.34225652715 +       77.75054398390*t);\r
+   uranus_z_0+=     0.00007950169 * cos( 0.72564903051 +       85.82729883120*t);\r
+   uranus_z_0+=     0.00006867469 * cos( 0.81417174224 +        2.96894541660*t);\r
+   uranus_z_0+=     0.00005648720 * cos( 3.45324719543 +      138.51749687070*t);\r
+   uranus_z_0+=     0.00004581938 * cos( 1.69668682344 +       38.13303563780*t);\r
+   uranus_z_0+=     0.00002578399 * cos( 5.19696447390 +      111.43016149680*t);\r
+   uranus_z_0+=     0.00002964070 * cos( 6.14338802239 +       35.16409022120*t);\r
+   uranus_z_0+=     0.00001884104 * cos( 2.61192472648 +       78.71375183040*t);\r
+   uranus_z_0+=     0.00002330304 * cos( 5.72640226150 +       70.84944530420*t);\r
+   uranus_z_0+=     0.00001985215 * cos( 0.76408839812 +       39.61750834610*t);\r
+   uranus_z_0+=     0.00001743154 * cos( 1.20586281789 +      146.59425171800*t);\r
+   uranus_z_0+=     0.00002037011 * cos( 0.95353587037 +       70.32818044240*t);\r
+   uranus_z_0+=     0.00001508924 * cos( 2.26195448553 +      222.86032299360*t);\r
+   uranus_z_0+=     0.00001082736 * cos( 2.62872874057 +      108.46121608020*t);\r
+   uranus_z_0+=     0.00001051968 * cos( 0.75560753840 +      109.94568878850*t);\r
+\r
+   double uranus_z_1=0.0;\r
+   uranus_z_1+=     0.00655916626 * cos( 0.01271947660 +       74.78159856730*t);\r
+   uranus_z_1+=     0.00049648951 * cos( 0.00000000000 +        0.00000000000*t);\r
+   uranus_z_1+=     0.00023874178 * cos( 2.73870491220 +      149.56319713460*t);\r
+   uranus_z_1+=     0.00007552177 * cos( 5.49304207700 +       76.26607127560*t);\r
+   uranus_z_1+=     0.00005941304 * cos( 3.61254073304 +       73.29712585900*t);\r
+   uranus_z_1+=     0.00002868429 * cos( 4.17954157878 +       63.73589830340*t);\r
+   uranus_z_1+=     0.00002087455 * cos( 5.97858625817 +        1.48447270830*t);\r
+   uranus_z_1+=     0.00001827697 * cos( 2.71810813335 +       11.04570026390*t);\r
+   uranus_z_1+=     0.00001305063 * cos( 4.52337002195 +       85.82729883120*t);\r
+   uranus_z_1+=     0.00001158250 * cos( 5.31913504112 +      224.34479570190*t);\r
+   uranus_z_1=uranus_z_1 * t;\r
+\r
+   double uranus_z_2=0.0;\r
+   uranus_z_2+=     0.00014697858 * cos( 1.75149165003 +       74.78159856730*t);\r
+   uranus_z_2+=     0.00001600044 * cos( 3.14159265359 +        0.00000000000*t);\r
+   uranus_z_2=uranus_z_2 * t * t;\r
+\r
+   return uranus_z_0+uranus_z_1+uranus_z_2;\r
+}\r
+\r
+double vsop87a_milli_venus_x(double t){\r
+   double venus_x_0=0.0;\r
+   venus_x_0+=     0.72211281391 * cos( 3.17575836361 +    10213.28554621100*t);\r
+   venus_x_0+=     0.00486448018 * cos( 0.00000000000 +        0.00000000000*t);\r
+   venus_x_0+=     0.00244500474 * cos( 4.05566613861 +    20426.57109242200*t);\r
+   venus_x_0+=     0.00002800281 * cos( 0.33147492492 +     2352.86615377180*t);\r
+   venus_x_0+=     0.00001949669 * cos( 4.23196016801 +     1577.34354244780*t);\r
+   venus_x_0+=     0.00001241717 * cos( 4.93573787058 +    30639.85663863300*t);\r
+   venus_x_0+=     0.00001162258 * cos( 2.87958246189 +    18073.70493865020*t);\r
+   venus_x_0+=     0.00001046690 * cos( 1.75434920413 +     6283.07584999140*t);\r
+\r
+   double venus_x_1=0.0;\r
+   venus_x_1+=     0.00033862636 * cos( 3.14159265359 +        0.00000000000*t);\r
+   venus_x_1+=     0.00017234992 * cos( 0.92721124604 +    20426.57109242200*t);\r
+   venus_x_1+=     0.00006510416 * cos( 2.19289889733 +    10213.28554621100*t);\r
+   venus_x_1=venus_x_1 * t;\r
+\r
+   return venus_x_0+venus_x_1;\r
+}\r
+\r
+double vsop87a_milli_venus_y(double t){\r
+   double venus_y_0=0.0;\r
+   venus_y_0+=     0.72324820731 * cos( 1.60573808356 +    10213.28554621100*t);\r
+   venus_y_0+=     0.00549506273 * cos( 3.14159265359 +        0.00000000000*t);\r
+   venus_y_0+=     0.00244884790 * cos( 2.48564954004 +    20426.57109242200*t);\r
+   venus_y_0+=     0.00002789807 * cos( 5.04214523606 +     2352.86615377180*t);\r
+   venus_y_0+=     0.00001933868 * cos( 5.80597990261 +     1577.34354244780*t);\r
+   venus_y_0+=     0.00001243658 * cos( 3.36573697344 +    30639.85663863300*t);\r
+   venus_y_0+=     0.00001164480 * cos( 1.30970620277 +    18073.70493865020*t);\r
+   venus_y_0+=     0.00001041872 * cos( 0.18129136925 +     6283.07584999140*t);\r
+\r
+   double venus_y_1=0.0;\r
+   venus_y_1+=     0.00039231430 * cos( 0.00000000000 +        0.00000000000*t);\r
+   venus_y_1+=     0.00017282326 * cos( 5.63824735900 +    20426.57109242200*t);\r
+   venus_y_1+=     0.00005968075 * cos( 3.60854944086 +    10213.28554621100*t);\r
+   venus_y_1=venus_y_1 * t;\r
+\r
+   double venus_y_2=0.0;\r
+   venus_y_2+=     0.00002007155 * cos( 3.14159265359 +        0.00000000000*t);\r
+   venus_y_2=venus_y_2 * t * t;\r
+\r
+   return venus_y_0+venus_y_1+venus_y_2;\r
+}\r
+\r
+double vsop87a_milli_venus_z(double t){\r
+   double venus_z_0=0.0;\r
+   venus_z_0+=     0.04282990302 * cos( 0.26703856476 +    10213.28554621100*t);\r
+   venus_z_0+=     0.00035588343 * cos( 3.14159265359 +        0.00000000000*t);\r
+   venus_z_0+=     0.00014501879 * cos( 1.14696911390 +    20426.57109242200*t);\r
+\r
+   double venus_z_1=0.0;\r
+   venus_z_1+=     0.00208096402 * cos( 1.88967278742 +    10213.28554621100*t);\r
+   venus_z_1+=     0.00001264989 * cos( 3.71037501321 +    20426.57109242200*t);\r
+   venus_z_1+=     0.00001364144 * cos( 0.00000000000 +        0.00000000000*t);\r
+   venus_z_1=venus_z_1 * t;\r
+\r
+   double venus_z_2=0.0;\r
+   venus_z_2+=     0.00009148044 * cos( 3.34791005272 +    10213.28554621100*t);\r
+   venus_z_2=venus_z_2 * t * t;\r
+\r
+   return venus_z_0+venus_z_1+venus_z_2;\r
+}\r
+\r
diff --git a/legacy/lib/vsop87/vsop87a_milli.h b/legacy/lib/vsop87/vsop87a_milli.h
new file mode 100755 (executable)
index 0000000..4c820e8
--- /dev/null
@@ -0,0 +1,17 @@
+//VSOP87-Multilang http://www.astrogreg.com/vsop87-multilang/index.html\r
+//Greg Miller (gmiller@gregmiller.net) 2019.  Released as Public Domain\r
+\r
+#ifndef VSOP87A_MILLI\r
+#define VSOP87A_MILLI\r
+\r
+   void vsop87a_milli_getEarth(double t,double temp[]);\r
+   void vsop87a_milli_getEmb(double t,double temp[]);\r
+   void vsop87a_milli_getJupiter(double t,double temp[]);\r
+   void vsop87a_milli_getMars(double t,double temp[]);\r
+   void vsop87a_milli_getMercury(double t,double temp[]);\r
+   void vsop87a_milli_getNeptune(double t,double temp[]);\r
+   void vsop87a_milli_getSaturn(double t,double temp[]);\r
+   void vsop87a_milli_getUranus(double t,double temp[]);\r
+   void vsop87a_milli_getVenus(double t,double temp[]);\r
+   void vsop87a_milli_getMoon(double earth[], double emb[],double temp[]);\r
+#endif\r
diff --git a/legacy/movement_faces.h b/legacy/movement_faces.h
new file mode 100644 (file)
index 0000000..b273204
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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 MOVEMENT_FACES_H_
+#define MOVEMENT_FACES_H_
+
+#include "simple_clock_face.h"
+#include "close_enough_clock_face.h"
+#include "clock_face.h"
+#include "world_clock_face.h"
+#include "preferences_face.h"
+#include "set_time_face.h"
+#include "set_time_hackwatch_face.h"
+#include "pulsometer_face.h"
+#include "temperature_display_face.h"
+#include "temperature_logging_face.h"
+#include "thermistor_testing_face.h"
+#include "character_set_face.h"
+#include "beats_face.h"
+#include "day_one_face.h"
+#include "voltage_face.h"
+#include "stopwatch_face.h"
+#include "totp_face.h"
+#include "totp_face_lfs.h"
+#include "lis2dw_logging_face.h"
+#include "demo_face.h"
+#include "hello_there_face.h"
+#include "sunrise_sunset_face.h"
+#include "countdown_face.h"
+#include "sailing_face.h"
+#include "counter_face.h"
+#include "blinky_face.h"
+#include "moon_phase_face.h"
+#include "accelerometer_data_acquisition_face.h"
+#include "mars_time_face.h"
+#include "orrery_face.h"
+#include "astronomy_face.h"
+#include "tomato_face.h"
+#include "probability_face.h"
+#include "wake_face.h"
+#include "frequency_correction_face.h"
+#include "alarm_face.h"
+#include "ratemeter_face.h"
+#include "rpn_calculator_alt_face.h"
+#include "weeknumber_clock_face.h"
+#include "fast_stopwatch_face.h"
+#include "tachymeter_face.h"
+#include "nanosec_face.h"
+#include "finetune_face.h"
+#include "databank_face.h"
+#include "tempchart_face.h"
+#include "tally_face.h"
+#include "tarot_face.h"
+#include "interval_face.h"
+#include "morsecalc_face.h"
+#include "rpn_calculator_face.h"
+#include "activity_face.h"
+#include "chirpy_demo_face.h"
+#include "ships_bell_face.h"
+#include "lightmeter_face.h"
+#include "discgolf_face.h"
+#include "habit_face.h"
+#include "planetary_time_face.h"
+#include "planetary_hours_face.h"
+#include "breathing_face.h"
+#include "repetition_minute_face.h"
+#include "timer_face.h"
+#include "invaders_face.h"
+#include "world_clock2_face.h"
+#include "time_left_face.h"
+#include "randonaut_face.h"
+#include "toss_up_face.h"
+#include "geomancy_face.h"
+#include "dual_timer_face.h"
+#include "simple_clock_bin_led_face.h"
+#include "menstrual_cycle_face.h"
+#include "flashlight_face.h"
+#include "decimal_time_face.h"
+#include "wyoscan_face.h"
+#include "save_load_face.h"
+#include "day_night_percentage_face.h"
+#include "simple_coin_flip_face.h"
+#include "solstice_face.h"
+#include "couch_to_5k_face.h"
+#include "minute_repeater_decimal_face.h"
+#include "tuning_tones_face.h"
+#include "minmax_face.h"
+#include "kitchen_conversions_face.h"
+#include "butterfly_game_face.h"
+#include "wareki_face.h"
+#include "wordle_face.h"
+#include "endless_runner_face.h"
+#include "periodic_face.h"
+#include "deadline_face.h"
+#include "higher_lower_game_face.h"
+#include "french_revolutionary_face.h"
+#include "minimal_clock_face.h"
+#include "simon_face.h"
+#include "simple_calculator_face.h"
+#include "alarm_thermometer_face.h"
+#include "beeps_face.h"
+#include "accel_interrupt_count_face.h"
+#include "metronome_face.h"
+#include "smallchess_face.h"
+// New includes go above this line.
+
+#endif // MOVEMENT_FACES_H_
diff --git a/legacy/watch_faces/clock/close_enough_clock_face.c b/legacy/watch_faces/clock/close_enough_clock_face.c
new file mode 100644 (file)
index 0000000..98a8e70
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Ruben Nic
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "close_enough_clock_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+const char *words[12] = {
+    "  ",
+    " 5",
+    "10",
+    "15",
+    "20",
+    "25",
+    "30",
+    "35",
+    "40",
+    "45",
+    "50",
+    "55",
+};
+
+static const char *past_word = " P";
+static const char *to_word = " 2";
+static const char *oclock_word = "OC";
+
+// sets when in the five minute period we switch
+// from "X past HH" to  "X to HH+1"
+static const int hour_switch_index = 8;
+
+static void _update_alarm_indicator(bool settings_alarm_enabled, close_enough_clock_state_t *state) {
+    state->alarm_enabled = settings_alarm_enabled;
+    if (state->alarm_enabled) {
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    } else {
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+    };
+}
+
+void close_enough_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(close_enough_clock_state_t));
+    }
+}
+
+void close_enough_clock_face_activate(void *context) {
+    close_enough_clock_state_t *state = (close_enough_clock_state_t *)context;
+
+    if (watch_sleep_animation_is_running()) {
+        watch_stop_sleep_animation();
+    }
+
+    if (movement_clock_mode_24h()) {
+        watch_set_indicator(WATCH_INDICATOR_24H);
+    }
+
+    // show alarm indicator if there is an active alarm
+    _update_alarm_indicator(movement_alarm_enabled(), state);
+
+    // this ensures that none of the five_minute_periods will match, so we always rerender when the face activates
+    state->prev_five_minute_period = -1;
+    state->prev_min_checked = -1;
+}
+
+bool close_enough_clock_face_loop(movement_event_t event, void *context) {
+    close_enough_clock_state_t *state = (close_enough_clock_state_t *)context;
+
+    char buf[11];
+    watch_date_time_t date_time;
+    bool show_next_hour = false;
+    int prev_five_minute_period;
+    int prev_min_checked;
+    int close_enough_hour;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+        case EVENT_LOW_ENERGY_UPDATE:
+            date_time = watch_rtc_get_date_time();
+            prev_five_minute_period = state->prev_five_minute_period;
+            prev_min_checked = state->prev_min_checked;
+
+            // check the battery voltage once a day...
+            if (date_time.unit.day != state->last_battery_check) {
+                state->last_battery_check = date_time.unit.day;
+                watch_enable_adc();
+                uint16_t voltage = watch_get_vcc_voltage();
+                watch_disable_adc();
+                // 2.2 volts will happen when the battery has maybe 5-10% remaining?
+                // we can refine this later.
+                state->battery_low = (voltage < 2200);
+            }
+
+            // ...and set the LAP indicator if low.
+            if (state->battery_low) {
+                watch_set_indicator(WATCH_INDICATOR_LAP);
+            }
+
+            // same minute, skip update
+            if (date_time.unit.minute == prev_min_checked) {
+                break;
+            } else {
+                state->prev_min_checked = date_time.unit.minute;
+            }
+
+            int five_minute_period = (date_time.unit.minute / 5) % 12;
+
+            // If we are 60% to the next 5 interval, move up to the next period
+            if (fmodf(date_time.unit.minute / 5.0f, 1.0f) > 0.5f) {
+                // If we are on the last 5 interval and moving to the next period we need to display the next hour because we are wrapping around
+                if (five_minute_period == 11) {
+                    show_next_hour = true;
+                }
+
+                five_minute_period = (five_minute_period + 1) % 12;
+            }
+
+            // same five_minute_period, skip update
+            if (five_minute_period == prev_five_minute_period) {
+                break;
+            }
+
+            // we don't want to modify date_time.unit.hour just in case other watch faces use it
+            close_enough_hour = date_time.unit.hour;
+
+            // move from "MM(mins) P HH" to "MM(mins) 2 HH+1"
+            if (five_minute_period >= hour_switch_index || show_next_hour) {
+                close_enough_hour = (close_enough_hour + 1) % 24;
+            }
+
+            if (!movement_clock_mode_24h()) {
+                // if we are in 12 hour mode, do some cleanup.
+                if (close_enough_hour < 12) {
+                    watch_clear_indicator(WATCH_INDICATOR_PM);
+                } else {
+                    watch_set_indicator(WATCH_INDICATOR_PM);
+                }
+
+                close_enough_hour %= 12;
+                if (close_enough_hour == 0) {
+                    close_enough_hour = 12;
+                }
+
+                date_time.unit.hour %= 12;
+                if (date_time.unit.hour == 0) {
+                    date_time.unit.hour = 12;
+                }
+            }
+
+            char first_word[3];
+            char second_word[3];
+            char third_word[3];
+            if (five_minute_period == 0) { // "HH  OC",
+                sprintf(first_word, "%2d", close_enough_hour);
+                strncpy(second_word, words[five_minute_period], 3);
+                strncpy(third_word, oclock_word, 3);
+            } else {
+                int words_length = sizeof(words) / sizeof(words[0]);
+
+                strncpy(
+                    first_word,
+                    five_minute_period >= hour_switch_index ?
+                        words[words_length - five_minute_period] :
+                        words[five_minute_period],
+                    3
+                );
+                strncpy(
+                    second_word,
+                    five_minute_period >= hour_switch_index ?
+                        to_word : past_word,
+                    3
+                );
+                sprintf(third_word, "%2d", close_enough_hour);
+            }
+
+            sprintf(
+                buf,
+                "%s%2d%s%s%s",
+                watch_utility_get_weekday(date_time),
+                date_time.unit.day,
+                first_word,
+                second_word,
+                third_word
+            );
+
+            watch_display_string(buf, 0);
+            state->prev_five_minute_period = five_minute_period;
+
+            // handle alarm indicator
+            if (state->alarm_enabled != movement_alarm_enabled()) {
+                _update_alarm_indicator(movement_alarm_enabled(), state);
+            }
+
+            break;
+
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void close_enough_clock_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/clock/close_enough_clock_face.h b/legacy/watch_faces/clock/close_enough_clock_face.h
new file mode 100644 (file)
index 0000000..e693043
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Ruben Nic
+ *
+ * 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 CLOSE_ENOUGH_CLOCK_FACE_H_
+#define CLOSE_ENOUGH_CLOCK_FACE_H_
+
+/*
+ * CLOSE ENOUGH CLOCK FACE
+ *
+ * Displays the current time; but only in periods of 5.
+ * Just in the in the formats of:
+ * - "10 past 5"
+ * - "15 to 7"
+ * - "6 o'clock"
+ *
+ */
+
+#include "movement.h"
+
+typedef struct {
+    int prev_five_minute_period;
+    int prev_min_checked;
+    uint8_t last_battery_check;
+    bool battery_low;
+    bool alarm_enabled;
+} close_enough_clock_state_t;
+
+void close_enough_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void close_enough_clock_face_activate(void *context);
+bool close_enough_clock_face_loop(movement_event_t event, void *context);
+void close_enough_clock_face_resign(void *context);
+
+#define close_enough_clock_face ((const watch_face_t){ \
+    close_enough_clock_face_setup, \
+    close_enough_clock_face_activate, \
+    close_enough_clock_face_loop, \
+    close_enough_clock_face_resign, \
+    NULL, \
+})
+
+#endif // CLOSE_ENOUGH_CLOCK_FACE_H_
diff --git a/legacy/watch_faces/clock/day_night_percentage_face.c b/legacy/watch_faces/clock/day_night_percentage_face.c
new file mode 100644 (file)
index 0000000..fbd3d45
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Wesley Aptekar-Cassels
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "day_night_percentage_face.h"
+#include "watch_utility.h"
+#include "sunriset.h"
+
+// fmod but handle negatives right
+static double better_fmod(double x, double y) {
+    return fmod(fmod(x, y) + y, y);
+}
+
+static void recalculate(watch_date_time_t utc_now, day_night_percentage_state_t *state) {
+    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+
+    if (movement_location.reg == 0) {
+        state->result = -2;
+        return;
+    }
+
+    // Weird quirky unsigned things were happening when I tried to cast these directly to doubles below.
+    // it looks redundant, but extracting them to local int16's seemed to fix it.
+    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
+    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
+
+    double lat = (double)lat_centi / 100.0;
+    double lon = (double)lon_centi / 100.0;
+
+    state->daylen = day_length(utc_now.unit.year + WATCH_RTC_REFERENCE_YEAR, utc_now.unit.month, utc_now.unit.day, lon, lat);
+
+    state->result = sun_rise_set(utc_now.unit.year + WATCH_RTC_REFERENCE_YEAR, utc_now.unit.month, utc_now.unit.day, lon, lat, &state->rise, &state->set);
+}
+
+void day_night_percentage_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(day_night_percentage_state_t));
+        day_night_percentage_state_t *state = (day_night_percentage_state_t *)*context_ptr;
+        watch_date_time_t utc_now = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_get_current_timezone_offset(), 0);
+        recalculate(utc_now, state);
+    }
+}
+
+void day_night_percentage_face_activate(void *context) {
+    (void) context;
+}
+
+bool day_night_percentage_face_loop(movement_event_t event, void *context) {
+    day_night_percentage_state_t *state = (day_night_percentage_state_t *)context;
+
+    char buf[12];
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0);
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+        case EVENT_LOW_ENERGY_UPDATE:
+            if ((utc_now.unit.hour == 0 && utc_now.unit.minute == 0 && utc_now.unit.second == 0) || state->result == -2) {
+                recalculate(utc_now, state);
+            }
+
+            if (state->result == -2) {
+                watch_display_string("    no Loc", 0);
+                break;
+            }
+
+            const char* weekday = watch_utility_get_weekday(date_time);
+            if (state->result != 0) {
+                if (state->result == 1) {
+                    watch_clear_indicator(WATCH_INDICATOR_PM);
+                } else {
+                    watch_set_indicator(WATCH_INDICATOR_PM);
+                }
+                sprintf(buf, "%s%2dEtrnal", weekday, date_time.unit.day);
+                watch_display_string(buf, 0);
+            } else {
+                double day_hours_decimal = utc_now.unit.hour + (utc_now.unit.minute + (utc_now.unit.second / 60.0)) / 60.0;
+
+                double day_percentage = (24.0 - better_fmod(state->rise - day_hours_decimal, 24.0)) / state->daylen;
+                double night_percentage = (24.0 - better_fmod(state->set - day_hours_decimal, 24.0)) / (24 - state->daylen);
+
+                uint16_t percentage;
+                if (day_percentage > 0.0 && day_percentage < 1.0) {
+                    percentage = day_percentage * 10000;
+                    watch_clear_indicator(WATCH_INDICATOR_PM);
+                } else {
+                    percentage = night_percentage * 10000;
+                    watch_set_indicator(WATCH_INDICATOR_PM);
+                }
+                if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+                    if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
+                    sprintf(buf, "%s%2d  %02d  ", weekday, date_time.unit.day, percentage / 100);
+                } else {
+                    sprintf(buf, "%s%2d  %04d", weekday, date_time.unit.day, percentage);
+                }
+                watch_display_string(buf, 0);
+            }
+
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void day_night_percentage_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/clock/day_night_percentage_face.h b/legacy/watch_faces/clock/day_night_percentage_face.h
new file mode 100644 (file)
index 0000000..01c94de
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Wesley Aptekar-Cassels
+ *
+ * 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 DAY_NIGHT_PERCENTAGE_FACE_H_
+#define DAY_NIGHT_PERCENTAGE_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Day/night percentage face
+ *
+ * Shows the percentage of the way through the day/night the current time is.
+ *
+ * The time digits show the percentage of the way through the day/night it is,
+ * with decimals in the smaller seconds digits. If the day or night will last
+ * for a full 24 hours, the text "Etrnal" is displayed instead of a percentage.
+ * The "PM" indicator is set when it is currently nighttime. The weekday and
+ * day digits display the weekday and day, as one would expect.
+ *
+ * This face does not currently offer any configuration. You must set the
+ * location register with some other face.
+ */
+
+typedef struct {
+    int result; // -1, 0, 1: result from sun_rise_set, -2: no location set
+    double rise;
+    double set;
+    double daylen;
+} day_night_percentage_state_t;
+
+void day_night_percentage_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void day_night_percentage_face_activate(void *context);
+bool day_night_percentage_face_loop(movement_event_t event, void *context);
+void day_night_percentage_face_resign(void *context);
+
+#define day_night_percentage_face ((const watch_face_t){ \
+    day_night_percentage_face_setup, \
+    day_night_percentage_face_activate, \
+    day_night_percentage_face_loop, \
+    day_night_percentage_face_resign, \
+    NULL, \
+})
+
+#endif // DAY_NIGHT_PERCENTAGE_FACE_H_
+
diff --git a/legacy/watch_faces/clock/decimal_time_face.c b/legacy/watch_faces/clock/decimal_time_face.c
new file mode 100644 (file)
index 0000000..5056302
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Curtis J. Brown <mrbrown8@juno.com>
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "decimal_time_face.h"
+#include "watch.h"
+
+
+
+void decimal_time_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    // These next two lines just silence the compiler warnings associated with unused parameters.
+    // We have no use for the settings or the watch_face_index, so we make that explicit here.
+    (void) watch_face_index;
+    (void) context_ptr;
+    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
+    if (*context_ptr == NULL) {
+        // in this case, we allocate an area of memory sufficient to store the stuff we need to track.
+        *context_ptr = malloc(sizeof(decimal_time_face_state_t));
+        decimal_time_face_state_t *state = (decimal_time_face_state_t *)*context_ptr;
+        state->chime_enabled = false;
+        state->features_to_show = 0 ;
+    }
+
+
+}
+
+
+void decimal_time_face_activate(void *context) {
+
+    // same as above: silence the warning, we don't need to check the settings.
+
+    // we do however need to set some things in our context. Here we cast it to the correct type...
+    decimal_time_face_state_t *state = (decimal_time_face_state_t *)context;
+
+    watch_set_indicator(WATCH_INDICATOR_24H);  // This face is always 24h, so just set the indicators
+    watch_clear_indicator(WATCH_INDICATOR_PM);
+
+    if (state->chime_enabled) {
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    }
+
+
+}
+
+
+
+bool decimal_time_face_loop(movement_event_t event, void *context) {
+
+    decimal_time_face_state_t *state = (decimal_time_face_state_t *)context;
+
+    char buf[16];
+    uint8_t centihours, decimal_seconds;
+    watch_date_time_t date_time;
+    
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            // on activate and tick
+            
+            date_time = watch_rtc_get_date_time();
+            
+            centihours = (( date_time.unit.minute * 60 + date_time.unit.second ) * 100 ) / 3600;  // Integer division, fractions get dropped, no need for abs() (bonus)
+
+            decimal_seconds = ( date_time.unit.minute * 60 + date_time.unit.second ) % 36 ;
+            
+            switch (state->features_to_show) {
+                case 0:
+                    sprintf( buf, "dT  %02d%02d  ",                       date_time.unit.hour, centihours );
+                    break;
+                case 1:
+                    sprintf( buf, "dT  %02d%02d%2d",                      date_time.unit.hour, centihours, decimal_seconds );
+                    break;
+                case 2:
+                    sprintf( buf, "dT%2d%02d%02d  ",  date_time.unit.day, date_time.unit.hour, centihours );
+                    break;
+                case 3:
+                    sprintf( buf, "dT%2d%02d%02d%2d", date_time.unit.day, date_time.unit.hour, centihours, decimal_seconds );
+                    break;
+            }
+
+            watch_display_string(buf, 0);  // display calculated time
+            
+            // at the top of every hour...
+            if (date_time.unit.minute == 0 && date_time.unit.second == 0 && state->chime_enabled) {
+                watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+                watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
+                watch_buzzer_play_note(BUZZER_NOTE_REST, 15);
+                watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
+                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+            }
+            
+            break;
+
+        case EVENT_ALARM_LONG_PRESS:   
+            state->chime_enabled = !state->chime_enabled;   // just like from simple_watch_face
+            if (state->chime_enabled) 
+                watch_set_indicator(WATCH_INDICATOR_BELL);
+            else
+                watch_clear_indicator(WATCH_INDICATOR_BELL);
+            break;
+
+        case EVENT_ALARM_BUTTON_UP:
+            state->features_to_show += 1 ;  // cycle thru what's to be shown
+            break;
+
+
+        case EVENT_LOW_ENERGY_UPDATE:
+            // This low energy mode update occurs once a minute, if the watch face is in the
+            // foreground when Movement enters low energy mode. We have the option of supporting
+            // this mode, but since our watch face animates once a second, the "Hello there" face
+            // isn't very useful in this mode. So we choose not to support it. (continued below)
+            break;
+
+        case EVENT_TIMEOUT:
+            // ... Instead, we respond to the timeout event. This event happens after a configurable
+            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
+            // resign control if we want to, and in this case, we do.
+            // This function will return the watch to the first screen (usually a simple clock),
+            // and it will do it long before the watch enters low energy mode. This ensures we
+            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
+            //movement_move_to_face(0);
+            break;
+
+        default:
+            movement_default_loop_handler(event);
+            break;
+
+    }
+
+    return true;
+}
+
+void decimal_time_face_resign(void *context) {
+    // our watch face, like most watch faces, has nothing special to do when resigning.
+    // there are no peripherals or sensors here to worry about turning off.
+    (void) context;
+
+}
+
+
+// void decimal_time_face_advise() {
+// 
+// }
+
+
+
+
+
+
+
+
+
+
+
diff --git a/legacy/watch_faces/clock/decimal_time_face.h b/legacy/watch_faces/clock/decimal_time_face.h
new file mode 100644 (file)
index 0000000..eb1a41e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Curtis J. Brown <mrbrown8@juno.com>
+ *
+ * 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 DECIMAL_TIME_FACE_H_
+#define DECIMAL_TIME_FACE_H_
+
+/*
+ * DECIMAL TIME face
+ * 
+ * This face presents the current time as hours and hundredths of an hour. Every hundreth of an hour, or "centihour", 
+ * occurs every 36 seconds. Because they range from 0 to 99, centihours, in the seventies range, will be displayed with a lowercase 7.
+ * 
+ * See https://en.wikipedia.org/wiki/Decimal_time#Decimal_hours
+ *  
+ * This method of timekeeping is used by the United States Postal Service.
+ * http://www.branch38nalc.com/sitebuildercontent/sitebuilderfiles/CONVERSION_TABLE_TIME.pdf
+ * https://postalemployeenetwork.com/time-conversion-print.htm
+ * 
+ * This method may be used by other organizations as well
+ * https://www.labor.nc.gov/workplace-rights/employer-responsibilities/time-conversion-chart-minutes-decimal-hours
+ * https://uh.edu/office-of-finance/payroll/time_conversion_chart_minutes_to_decimalhours.pdf
+ * https://www.placer.ca.gov/DocumentCenter/View/3860/Decimals-to-Minutes-Conversion-Table-PDF
+ * https://hr.colostate.edu/minute-to-decimal-conversion-chart/
+ * 
+ * Many thanks go to Joey Castillo for making this project happen.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    bool chime_enabled;            // did the user enable hourly chime for this face? 
+    uint8_t features_to_show : 2 ; // what features are to be displayed?
+} decimal_time_face_state_t;
+
+void decimal_time_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void decimal_time_face_activate(void *context);
+bool decimal_time_face_loop(movement_event_t event, void *context);
+void decimal_time_face_resign(void *context);
+// void decimal_time_face_advise();
+
+
+#define decimal_time_face ((const watch_face_t){ \
+    decimal_time_face_setup, \
+    decimal_time_face_activate, \
+    decimal_time_face_loop, \
+    decimal_time_face_resign, \
+    NULL, \
+})
+
+#endif // DECIMAL_TIME_FACE_H_
diff --git a/legacy/watch_faces/clock/french_revolutionary_face.c b/legacy/watch_faces/clock/french_revolutionary_face.c
new file mode 100644 (file)
index 0000000..83564a3
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 CarpeNoctem
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "french_revolutionary_face.h"
+
+void french_revolutionary_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(french_revolutionary_state_t));
+        memset(*context_ptr, 0, sizeof(french_revolutionary_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+        french_revolutionary_state_t *state = (french_revolutionary_state_t *)*context_ptr;
+        state->use_am_pm = false;
+        state->show_seconds = true;
+        state->display_type = 0;
+        state->colon_set_after_splash = false;
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void french_revolutionary_face_activate(void *context) {
+    french_revolutionary_state_t *state = (french_revolutionary_state_t *)context;
+
+    // Handle any tasks related to your watch face coming on screen.
+    state->colon_set_after_splash = false;
+}
+
+bool french_revolutionary_face_loop(movement_event_t event, void *context) {
+    french_revolutionary_state_t *state = (french_revolutionary_state_t *)context;
+
+    char buf[11];
+    watch_date_time_t date_time;
+    fr_decimal_time decimal_time;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Initial UI - Show a quick "splash screen"
+            watch_clear_display();
+            watch_display_string("FR  dECimL", 0);
+            break;
+        case EVENT_TICK:
+        case EVENT_LOW_ENERGY_UPDATE:
+
+            date_time = watch_rtc_get_date_time();
+
+            decimal_time = get_decimal_time(&date_time);
+
+            set_display_buffer(buf, state, &decimal_time, &date_time);
+
+            // If we're in low-energy mode, don't write out the seconds. Also start the LE tick animation if it's not already going.
+            if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+                buf[8] = ' ';
+                buf[9] = ' ';
+                if (!watch_sleep_animation_is_running()) { watch_start_sleep_animation(500); }
+            }
+
+            // Update the display with our decimal time
+            watch_display_string(buf, 0);
+
+            // Oh, and a one-off to set the colon after the "splash screen"
+            if (!state->colon_set_after_splash) {
+                watch_set_colon();
+                state->colon_set_after_splash = true;
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            state->display_type += 1 ; // cycle through the display types
+            if (state->display_type > 2) { state->display_type = 0; } // but return to 0 after 2
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            // I originally had chiming on the decimal-hour enabled, and this would enable/disable that chime, just like on
+            // the simple clock and decimal time faces. But because decimal seconds don't always line up with normal seconds,
+            // I assume the (decimal-)hourly chime could sometimes be missed. Additionally, I need this button for other purposes,
+            // now that I added seconds on/off toggle and upper normal-time with the ability to toggle that between 12/24hr format.
+            state->show_seconds = !state->show_seconds;
+            if (!state->show_seconds) { watch_display_string("  ", 8); }
+            else { watch_display_string("--", 8); }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            // In case anyone really wants that upper time in 12-hour format. I thought about using the global setting (movement_clock_mode_24h())
+            // for this preference, but thought someone who prefers 12-hour format normally, might prefer 24hr when compared to a 10hr decimal day,
+            // so this is separate for now.
+            state->use_am_pm = !state->use_am_pm;
+            if (state->use_am_pm) {
+                watch_clear_indicator(WATCH_INDICATOR_24H);
+                date_time = watch_rtc_get_date_time();
+                if (date_time.unit.hour < 12) { watch_clear_indicator(WATCH_INDICATOR_PM); }
+                else { watch_set_indicator(WATCH_INDICATOR_PM); }
+            } else {
+                watch_clear_indicator(WATCH_INDICATOR_PM);
+                watch_set_indicator(WATCH_INDICATOR_24H);
+            }
+            break;
+        default:
+            // Movement's default loop handler will step in for any cases you don't handle above:
+            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
+            // You can override any of these behaviors by adding a case for these events to this switch statement.
+            return movement_default_loop_handler(event);
+    }
+
+    // return true if the watch can enter standby mode. Generally speaking, you should always return true.
+    // Exceptions:
+    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
+    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
+    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
+    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
+    return true;
+}
+
+void french_revolutionary_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
+
+// Calculate decimal time from normal (24hr) time
+fr_decimal_time get_decimal_time(watch_date_time_t *date_time) {
+    uint32_t current_24hr_secs, current_decimal_seconds;
+    fr_decimal_time decimal_time;
+    // Current 24-hr time in seconds (There are 86400 of these in a day.)
+    current_24hr_secs = date_time->unit.hour * 3600 + date_time->unit.minute * 60 + date_time->unit.second;
+
+    // Current Decimal Time in seconds. There are 100000 seconds in a 10-hr decimal-time day.
+    // current_decimal_seconds = current_24hr_seconds * 100000 / 86400, or = current_24_seconds * 1000 / 864;
+    // By chopping the extra zeros off the end, we can use uint32 instead of uint64.
+    current_decimal_seconds = current_24hr_secs * 1000 / 864;
+
+    decimal_time.hour = current_decimal_seconds / 10000;
+    // Remove the hours from total seconds and keep the remainder for below.
+    current_decimal_seconds = current_decimal_seconds - decimal_time.hour * 10000;
+
+    decimal_time.minute = current_decimal_seconds / 100;
+    // Remove the minutes from total seconds and keep the remaining seconds
+    // Note: I think I used an extra seconds variable here because sprintf or movement weren't liking a uint32...
+    decimal_time.second = current_decimal_seconds - decimal_time.minute * 100;
+    return decimal_time;
+}
+
+// Fills in the display buffer, depending on the currently-selected display option (and sub-options):
+// - Decimal-time only
+// - Decimal-time with date in top-right
+// - Decimal-time with normal time in the top (minutes first, then hours, due to display limitations)
+// TODO: There is some power-saving stuff that simple clock does here around not redrawing characters that haven't changed, but we're not doing that here.
+//       I'll try to add that optimization could be added in a future commit.
+void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time_t *date_time) {
+    switch (state->display_type) {
+                // Decimal time only
+                case 0:
+                    // Originally I had the day slot set to "FR" (French Revolutionary time), but my brain kept thinking "Friday" whenever I saw it,
+                    // so I changed it to dT (Decimal Time) to avoid that confusion. Apologies to anyone who has the other decimal_time face and this one
+                    // installed concurrently. Maybe the splash screen will help a little.
+                    sprintf( buf, "dT  %2d%02d%02d", decimal_time->hour, decimal_time->minute, decimal_time->second );
+                    watch_clear_indicator(WATCH_INDICATOR_PM);
+                    watch_clear_indicator(WATCH_INDICATOR_24H);
+                    break;
+                // Decimal time and date
+                case 1:
+                    sprintf( buf, "dT%2d%2d%02d%02d",  date_time->unit.day, decimal_time->hour, decimal_time->minute, decimal_time->second );
+                    watch_clear_indicator(WATCH_INDICATOR_PM);
+                    watch_clear_indicator(WATCH_INDICATOR_24H);
+                    break;
+                // Decimal time on bottom, normal time above
+                case 2:
+                    if (state->use_am_pm) {
+                        // if we are in 12 hour mode, do some cleanup.
+                        watch_clear_indicator(WATCH_INDICATOR_24H);
+                        if (date_time->unit.hour < 12) {
+                            watch_clear_indicator(WATCH_INDICATOR_PM);
+                        } else {
+                            watch_set_indicator(WATCH_INDICATOR_PM);
+                        }
+                        date_time->unit.hour %= 12;
+                        if (date_time->unit.hour == 0) date_time->unit.hour = 12;
+                    } else {
+                        watch_clear_indicator(WATCH_INDICATOR_PM);
+                        watch_set_indicator(WATCH_INDICATOR_24H);
+                    }
+                    // Note, the date digits don't display a leading zero well, so we don't use it.
+                    sprintf( buf, "%02d%2d%2d%02d%02d", date_time->unit.minute, date_time->unit.hour, decimal_time->hour, decimal_time->minute, decimal_time->second );
+                    
+                    // Make the second character of the Day area more readable
+                    buf[1] = fix_character_one(buf[1]);
+                    break;
+            }
+            // Finally, if show_seconds is disabled, trim those off.
+            if (!state->show_seconds) {
+                buf[8] = ' ';
+                buf[9] = ' ';
+            }
+}
+
+// Sadly, the second character of the Day field cannot show all numbers, so we make some replacements.
+// See https://www.sensorwatch.net/docs/wig/display/#limitations-of-the-weekday-digits
+char fix_character_one(char digit) {
+    char return_char = digit; // We don't need to update this for 0, 1, 3, 7 and 8.
+    switch(digit) {
+        case '2':
+            // Roman numeral / tally representation of 2
+            return_char = '|'; // Thanks, Joey, for already having this in the character set.
+            break;
+        case '4':
+            // Looks almost like a 4 - just missing the top-left segment.
+            // 0b01000110
+            return_char = '&'; // Slight hack - I want 0b01000110, but 0b01000100 is already in the character set and will do, since B and C segments are linked in this position.
+            break;
+        case '5':
+            return_char = 'F'; // F for Five
+            break;
+        case '6':
+            return_char = 'E'; // Looks almost like a 6 - just missing the bottom-right segment. Not super happy with it, but liked it best of the options I tried.
+            break;
+        case '9':
+            return_char = 'N'; // N for Nine
+            break;
+    }
+    return return_char;
+}
diff --git a/legacy/watch_faces/clock/french_revolutionary_face.h b/legacy/watch_faces/clock/french_revolutionary_face.h
new file mode 100644 (file)
index 0000000..554bcea
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 CarpeNoctem
+ *
+ * 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 FRENCH_REVOLUTIONARY_FACE_H_
+#define FRENCH_REVOLUTIONARY_FACE_H_
+
+#include "movement.h"
+
+/*
+ * French Revolutionary Decimal Time
+ *
+ * Similar to the Decimal Time face, but with the day divided into ten hours instead of twenty four.
+ * Each hour is divided into one hundred minutes, and those minutes are divided into 100 seconds.
+ * I came across this one the Svalbard watch site here: https://svalbard.watch/pages/about_decimal_time.html
+ * More info here as well: https://en.wikipedia.org/wiki/Decimal_time
+ * 
+ * By default, the face just displays the current decimal time. Pressing the alarm button will toggle through other display options:
+ * 1) Just decimal time (with dT indicator at top)
+ * 2) Decimal time, with dT indicator and date above.
+ * 3) Decimal time, with 24-hr time above (where Day and Date would normally be displayed), BUT minutes first then hours.
+ *    Sadly, the first character of the date area only goes up to 3 (see https://www.sensorwatch.net/docs/wig/display/#the-day-digits)
+ *    I was going to begrudgindly leave this display option out when I realized that, but thought it would be better to have this backwards
+ *    representation of the "normal" time than not at all.
+ * 
+ * A long-press of the light button will toggle the upper time between 12-hr AM/PM and 24-hr mode. I thought of reading the main setting for this,
+ * but thought that a person could normally prefer 12hr time, but next to a 10hr day want to see the normal time in the 24hr format.
+ * 
+ * A long-press of the alarm button will toggle the seconds off and on.
+ *
+ */
+
+typedef struct {
+    bool use_am_pm; // Use 12-hr AM/PM for upper display instead of 24-hr? (Default is 24-hr)
+    bool show_seconds;
+    bool colon_set_after_splash;
+    uint8_t display_type : 2;
+} french_revolutionary_state_t;
+
+typedef struct {
+    uint8_t second : 8;    // 0-99
+    uint8_t minute : 8;    // 0-99
+    uint8_t hour : 5;      // 0-10
+} fr_decimal_time;
+
+void french_revolutionary_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void french_revolutionary_face_activate(void *context);
+bool french_revolutionary_face_loop(movement_event_t event, void *context);
+void french_revolutionary_face_resign(void *context);
+char fix_character_one(char digit);
+fr_decimal_time get_decimal_time(watch_date_time_t *date_time);
+void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time_t *date_time);
+
+
+#define french_revolutionary_face ((const watch_face_t){ \
+    french_revolutionary_face_setup, \
+    french_revolutionary_face_activate, \
+    french_revolutionary_face_loop, \
+    french_revolutionary_face_resign, \
+    NULL, \
+})
+
+#endif // FRENCH_REVOLUTIONARY_FACE_H_
+
diff --git a/legacy/watch_faces/clock/mars_time_face.c b/legacy/watch_faces/clock/mars_time_face.c
new file mode 100644 (file)
index 0000000..5c55cd1
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "watch_utility.h"
+#include "mars_time_face.h"
+
+// note: lander coordinates come from Mars24's `marslandmarks.xml` file
+static double site_longitudes[MARS_TIME_NUM_SITES] = {
+    0,                      // Mars Coordinated Time, at the meridian
+    360.0 - 109.9,          // Zhurong lander site
+    360.0 - 77.45088572,    // Perseverance lander site
+    360.0 - 135.623447,     // InSight lander site
+    360.0 - 137.441635,     // Curiosity lander site
+};
+
+static char site_names[MARS_TIME_NUM_SITES][3] = {
+    "MC",
+    "ZH",
+    "PE",
+    "IN",
+    "CU",
+};
+
+static uint16_t landing_sols[MARS_TIME_NUM_SITES] = {
+    0,
+    52387,
+    52304,
+    51511,
+    49269,
+};
+
+typedef struct {
+    uint8_t hour;
+    uint8_t minute;
+    uint8_t second;
+} mars_clock_hms_t;
+
+static void _h_to_hms(mars_clock_hms_t *date_time, double h) {
+       unsigned int seconds = (unsigned int)(h * 3600.0);
+       date_time->hour = seconds / 3600;
+       seconds = seconds % 3600;
+       date_time->minute = floor(seconds / 60);
+       date_time->second = round(seconds % 60);
+}
+
+static void _update(mars_time_state_t *state) {
+    char buf[11];
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
+    // TODO: I'm skipping over some steps here.
+    // https://www.giss.nasa.gov/tools/mars24/help/algorithm.html
+    double jdut = 2440587.5 + ((double)now / 86400.0);
+    double jdtt = jdut + ((37.0 + 32.184) / 86400.0);
+    double jd2k = jdtt - 2451545.0;
+    double msd = ((jd2k - 4.5) / 1.0274912517) + 44796.0 - 0.0009626;
+    double mtc = fmod(24 * msd, 24);
+    double lmt;
+
+    if (state->current_site == 0) {
+        lmt = mtc;
+    } else {
+        double longitude = site_longitudes[state->current_site];
+        double lmst = mtc - ((longitude * 24.0) / 360.0);
+        lmt = fmod(lmst + 24, 24);
+    }
+
+    if (state->displaying_sol) {
+        // TODO: this is not right, mission sol should turn over at midnight local time?
+        uint16_t sol = floor(msd) - landing_sols[state->current_site];
+        if (sol < 1000) sprintf(&buf[0], "%s  Sol%3d", site_names[state->current_site], sol);
+        else sprintf(&buf[0], "%s $%6d", site_names[state->current_site], sol);
+        watch_clear_colon();
+        watch_clear_indicator(WATCH_INDICATOR_24H);
+    } else {
+        mars_clock_hms_t mars_time;
+        _h_to_hms(&mars_time, lmt);
+        sprintf(&buf[0], "%s  %02d%02d%02d", site_names[state->current_site], mars_time.hour, mars_time.minute, mars_time.second);
+        watch_set_colon();
+        watch_set_indicator(WATCH_INDICATOR_24H);
+    }
+
+    watch_display_string(buf, 0);
+}
+
+void mars_time_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(mars_time_state_t));
+        memset(*context_ptr, 0, sizeof(mars_time_state_t));
+    }
+}
+
+void mars_time_face_activate(void *context) {
+    mars_time_state_t *state = (mars_time_state_t *)context;
+    (void) state;
+}
+
+bool mars_time_face_loop(movement_event_t event, void *context) {
+    mars_time_state_t *state = (mars_time_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            _update(state);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            state->displaying_sol = !state->displaying_sol;
+            _update(state);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            movement_illuminate_led();
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            state->current_site = (state->current_site + 1) % MARS_TIME_NUM_SITES;
+            _update(state);
+            break;
+        case EVENT_TIMEOUT:
+            // TODO: make this lower power so we can avoid timeout
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // TODO: low energy update
+            // watch_start_sleep_animation(500);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void mars_time_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/clock/mars_time_face.h b/legacy/watch_faces/clock/mars_time_face.h
new file mode 100644 (file)
index 0000000..ddb43e6
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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 MARS_TIME_FACE_H_
+#define MARS_TIME_FACE_H_
+
+/*
+ * MARS TIME face
+ *
+ * This watch face is dedicated to Martian timekeeping.
+ * It has several modes, and can display either a time or a date.
+ *
+ * Pressing the ALARM button cycles through different time zones on Mars:
+ *   MC - Mars Coordinated Time, the time at Airy-0 Crater on the Martian prime meridian
+ *   ZH - Local mean solar time for the Zhurong rover
+ *   PE - LMST for the Perseverance rover
+ *   IN - LMST for the Insight lander
+ *   CU - LMST for the Curiosity rover
+ *
+ * Press the LIGHT button to toggle between displaying time and date:
+ *   MC S - the Mars Sol Date, Martian days since December 29, 1873
+ *   ZH Sol - Mission sol for the Zhurong rover
+ *   PE Sol - Mission sol for the Perseverance rover
+ *   IN S - Mission sol for the InSight lander
+ *   CU S - Mission sol for the Curiosity rover
+ *
+ * Note that where the mission sol is below 1000, this watch face displays
+ * the word “Sol” on the bottom line. When the mission sol is over 1000, the
+ * word “Sol” will not fit and so it displays a stylized letter S at the top
+ * right.
+ */
+
+#include "movement.h"
+
+typedef enum {
+    MARS_TIME_MERIDIAN,
+    MARS_TIME_ZHURONG_SITE,
+    MARS_TIME_PERSEVERANCE_SITE,
+    MARS_TIME_INSIGHT_SITE,
+    MARS_TIME_CURIOSITY_SITE,
+    MARS_TIME_NUM_SITES,
+} mars_time_site_t;
+
+typedef struct {
+    mars_time_site_t current_site;
+    bool displaying_sol;
+} mars_time_state_t;
+
+void mars_time_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void mars_time_face_activate(void *context);
+bool mars_time_face_loop(movement_event_t event, void *context);
+void mars_time_face_resign(void *context);
+
+#define mars_time_face ((const watch_face_t){ \
+    mars_time_face_setup, \
+    mars_time_face_activate, \
+    mars_time_face_loop, \
+    mars_time_face_resign, \
+    NULL, \
+})
+
+#endif // MARS_TIME_FACE_H_
+
diff --git a/legacy/watch_faces/clock/minimal_clock_face.c b/legacy/watch_faces/clock/minimal_clock_face.c
new file mode 100644 (file)
index 0000000..cb66323
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Dennisman219
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "minimal_clock_face.h"
+
+static void _minimal_clock_face_update_display() {
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    char buffer[11];
+
+    if (!movement_clock_mode_24h()) {
+        date_time.unit.hour %= 12;
+        sprintf(buffer, "%2d%02d  ", date_time.unit.hour, date_time.unit.minute);
+    } else {
+        sprintf(buffer, "%02d%02d  ", date_time.unit.hour, date_time.unit.minute);
+    }
+
+    watch_display_string(buffer, 4);
+}
+
+void minimal_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(minimal_clock_state_t));
+        memset(*context_ptr, 0, sizeof(minimal_clock_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void minimal_clock_face_activate(void *context) {
+    (void) context;
+    // Handle any tasks related to your watch face coming on screen.
+    watch_set_colon();
+}
+
+bool minimal_clock_face_loop(movement_event_t event, void *context) {
+    (void) context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Show your initial UI here.
+            _minimal_clock_face_update_display();
+            break;
+        case EVENT_TICK:
+            // If needed, update your display here.
+            _minimal_clock_face_update_display();
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            // You can use the Light button for your own purposes. Note that by default, Movement will also
+            // illuminate the LED in response to EVENT_LIGHT_BUTTON_DOWN; to suppress that behavior, add an
+            // empty case for EVENT_LIGHT_BUTTON_DOWN.
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // Just in case you have need for another button.
+            break;
+        case EVENT_TIMEOUT:
+            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
+            // you may uncomment this line to move back to the first watch face in the list:
+            // movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
+            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
+            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
+            // watch_start_sleep_animation(500);
+            _minimal_clock_face_update_display();
+            break;
+        default:
+            // Movement's default loop handler will step in for any cases you don't handle above:
+            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
+            // You can override any of these behaviors by adding a case for these events to this switch statement.
+            return movement_default_loop_handler(event);
+    }
+
+    // return true if the watch can enter standby mode. Generally speaking, you should always return true.
+    // Exceptions:
+    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
+    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
+    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
+    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
+    return true;
+}
+
+void minimal_clock_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/legacy/watch_faces/clock/minimal_clock_face.h b/legacy/watch_faces/clock/minimal_clock_face.h
new file mode 100644 (file)
index 0000000..7c345e3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Dennisman219
+ *
+ * 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 MINIMAL_CLOCK_FACE_H_
+#define MINIMAL_CLOCK_FACE_H_
+
+#include "movement.h"
+
+/*
+ * MINIMAL CLOCK FACE
+ *
+ * A minimal clock face that just shows hours and minutes.
+ * There is nothing to configure. The face follows the 12h/24h setting
+ *
+ */
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint8_t unused;
+} minimal_clock_state_t;
+
+void minimal_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void minimal_clock_face_activate(void *context);
+bool minimal_clock_face_loop(movement_event_t event, void *context);
+void minimal_clock_face_resign(void *context);
+
+#define minimal_clock_face ((const watch_face_t){ \
+    minimal_clock_face_setup, \
+    minimal_clock_face_activate, \
+    minimal_clock_face_loop, \
+    minimal_clock_face_resign, \
+    NULL, \
+})
+
+#endif // MINIMAL_CLOCK_FACE_H_
+
diff --git a/legacy/watch_faces/clock/minute_repeater_decimal_face.c b/legacy/watch_faces/clock/minute_repeater_decimal_face.c
new file mode 100644 (file)
index 0000000..9f5eb43
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jonas Termeau - original repetition_minute_face
+ * Copyright (c) 2023 Brian Blakley - modified minute_repeater_decimal_face
+ *
+ * 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.
+ */
+
+/*
+ * This face, minute_repeater_decimal_face, is a modification of the original 
+ * repetition_minute_face by Jonas Termeau.
+ * 
+ * This version was created by BrianBinFL to use a decimal minute repeater pattern 
+ * (hours, tens, and minutes) instead of the traditional pattern (hours, quarters, 
+ * minutes).
+ *
+ * Also 500ms delays were added after the hours segment and after the tens segment
+ * to make it easier for the user to realize that the counting for the current 
+ * segment has ended.
+ *
+ */
+#include <stdlib.h>
+#include "minute_repeater_decimal_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_private_display.h"
+
+void mrd_play_hour_chime(void) {
+        watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
+}
+
+void mrd_play_tens_chime(void) {
+        watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 150);
+        watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 750);
+}
+
+void mrd_play_minute_chime(void) {
+        watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
+}
+
+static void _update_alarm_indicator(bool settings_alarm_enabled, minute_repeater_decimal_state_t *state) {
+    state->alarm_enabled = settings_alarm_enabled;
+    if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+}
+
+void minute_repeater_decimal_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(minute_repeater_decimal_state_t));
+        minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)*context_ptr;
+        state->signal_enabled = false;
+        state->watch_face_index = watch_face_index;
+    }
+}
+
+void minute_repeater_decimal_face_activate(void *context) {
+    minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
+
+    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
+
+    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
+
+    // handle chime indicator
+    if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+    else watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+    // show alarm indicator if there is an active alarm
+    _update_alarm_indicator(movement_alarm_enabled(), state);
+
+    watch_set_colon();
+
+    // this ensures that none of the timestamp fields will match, so we can re-render them all.
+    state->previous_date_time = 0xFFFFFFFF;
+}
+
+bool minute_repeater_decimal_face_loop(movement_event_t event, void *context) {
+    minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
+    char buf[11];
+    uint8_t pos;
+
+    watch_date_time_t date_time;
+    uint32_t previous_date_time;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+        case EVENT_LOW_ENERGY_UPDATE:
+            date_time = watch_rtc_get_date_time();
+            previous_date_time = state->previous_date_time;
+            state->previous_date_time = date_time.reg;
+
+            // check the battery voltage once a day...
+            if (date_time.unit.day != state->last_battery_check) {
+                state->last_battery_check = date_time.unit.day;
+                watch_enable_adc();
+                uint16_t voltage = watch_get_vcc_voltage();
+                watch_disable_adc();
+                // 2.2 volts will happen when the battery has maybe 5-10% remaining?
+                // we can refine this later.
+                state->battery_low = (voltage < 2200);
+            }
+
+            // ...and set the LAP indicator if low.
+            if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
+
+            if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+                // everything before seconds is the same, don't waste cycles setting those segments.
+                watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
+                watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
+                break;
+            } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+                // everything before minutes is the same.
+                pos = 6;
+                sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
+            } else {
+                // other stuff changed; let's do it all.
+                if (!movement_clock_mode_24h()) {
+                    // if we are in 12 hour mode, do some cleanup.
+                    if (date_time.unit.hour < 12) {
+                        watch_clear_indicator(WATCH_INDICATOR_PM);
+                    } else {
+                        watch_set_indicator(WATCH_INDICATOR_PM);
+                    }
+                    date_time.unit.hour %= 12;
+                    if (date_time.unit.hour == 0) date_time.unit.hour = 12;
+                }
+                pos = 0;
+                if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+                    if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
+                    sprintf(buf, "%s%2d%2d%02d  ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
+                } else {
+                    sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
+                }
+            }
+            watch_display_string(buf, pos);
+            // handle alarm indicator
+            if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->signal_enabled = !state->signal_enabled;
+            if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+            else watch_clear_indicator(WATCH_INDICATOR_BELL);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            movement_play_signal();
+            break;
+        case EVENT_LIGHT_LONG_UP:
+            /*
+             * Howdy neighbors, this is the actual complication. Like an actual
+             * (very expensive) watch with a repetition minute complication it's
+             * boring at 00:00 or 1:00 and very quite musical at 23:59 or 12:59.
+             */
+
+            date_time = watch_rtc_get_date_time();
+            
+            
+            int hours = date_time.unit.hour;
+            int tens = date_time.unit.minute / 10;
+            int minutes = date_time.unit.minute % 10;
+
+            // chiming hours
+            if (!movement_clock_mode_24h()) {
+                hours = date_time.unit.hour % 12;                
+                if (hours == 0) hours = 12;
+            }
+            if (hours > 0) {
+                int count = 0;
+                for(count = hours; count > 0; --count) {          
+                    mrd_play_hour_chime();
+                }
+                // do a little pause before proceeding to tens
+                watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
+            }
+
+            // chiming tens (if needed)
+            if (tens > 0) {
+                int count = 0;
+                for(count = tens; count > 0; --count) {          
+                    mrd_play_tens_chime();
+                }
+                // do a little pause before proceeding to minutes
+                watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
+            }
+
+            // chiming minutes (if needed)
+            if (minutes > 0) {
+                int count = 0;
+                for(count = minutes; count > 0; --count) {          
+                    mrd_play_minute_chime();
+                }
+            }
+           
+            break; 
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void minute_repeater_decimal_face_resign(void *context) {
+    (void) context;
+}
+
+movement_watch_face_advisory_t minute_repeater_decimal_face_advise(void *context) {
+    minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
+    movement_watch_face_advisory_t retval = { 0 };
+
+    if (state->signal_enabled) {
+        watch_date_time_t date_time = watch_rtc_get_date_time();
+        retval.wants_background_task = date_time.unit.minute == 0;
+    }
+
+    return retval;
+}
diff --git a/legacy/watch_faces/clock/minute_repeater_decimal_face.h b/legacy/watch_faces/clock/minute_repeater_decimal_face.h
new file mode 100644 (file)
index 0000000..738b34d
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jonas Termeau - original repetition_minute_face
+ * Copyright (c) 2023 Brian Blakley - modified minute_repeater_decimal_face
+ *
+ * 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 MINUTE_REPEATER_DECIMAL_FACE_H_
+#define MINUTE_REPEATER_DECIMAL_FACE_H_
+
+#include "movement.h"
+
+/*
+ * A hopefully useful complication for friendly neighbors in the dark
+ *
+ * Originating from 1676 from reverend and mechanician Edward Barlow, and
+ * perfected in 1820 by neighbor Abraham Breguet, a minute repeater or 
+ * "repetition minute" is a complication in a mechanical watch or clock that
+ * chimes the hours and often minutes at the press of a button. There are many
+ * types of repeater, from the simple repeater which merely strikes the number
+ * of hours, to the minute repeater which chimes the time down to the minute,
+ * using separate tones for hours, decimal hours, and minutes. They originated
+ * before widespread artificial illumination, to allow the time to be determined
+ * in the dark, and were also used by the visually impaired. 
+ *
+ *
+ * How to use it :
+ * 
+ * Long press the light button to get an auditive reading of the time like so :
+ * 0..23 (1..12 if 24-hours format isn't enabled) low beep(s) for the hours
+ * 0..9 low-high couple pitched beeps for the tens of minutes
+ * 0..9 high pitched beep(s) for the remaining minutes (ones of minutes)
+ *
+ * Prerequisite : a watch with a working buzzer
+ * 
+ * ~ Only in the darkness can you see the stars. - Martin Luther King ~
+ *
+ */
+
+typedef struct {
+    uint32_t previous_date_time;
+    uint8_t last_battery_check;
+    uint8_t watch_face_index;
+    bool signal_enabled;
+    bool battery_low;
+    bool alarm_enabled;
+} minute_repeater_decimal_state_t;
+
+void mrd_play_hour_chime(void);
+void mrd_play_tens_chime(void);
+void mrd_play_minute_chime(void);
+void minute_repeater_decimal_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void minute_repeater_decimal_face_activate(void *context);
+bool minute_repeater_decimal_face_loop(movement_event_t event, void *context);
+void minute_repeater_decimal_face_resign(void *context);
+movement_watch_face_advisory_t minute_repeater_decimal_face_advise(void *context);
+
+#define minute_repeater_decimal_face ((const watch_face_t){ \
+    minute_repeater_decimal_face_setup, \
+    minute_repeater_decimal_face_activate, \
+    minute_repeater_decimal_face_loop, \
+    minute_repeater_decimal_face_resign, \
+    minute_repeater_decimal_face_advise, \
+})
+
+#endif // MINUTE_REPEATER_DECIMAL_FACE_H_
diff --git a/legacy/watch_faces/clock/repetition_minute_face.c b/legacy/watch_faces/clock/repetition_minute_face.c
new file mode 100644 (file)
index 0000000..b475b45
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jonas Termeau
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include "repetition_minute_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_private_display.h"
+
+void play_hour_chime(void) {
+        watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
+}
+
+void play_quarter_chime(void) {
+        watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 150);
+        watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 750);
+}
+
+void play_minute_chime(void) {
+        watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
+}
+
+static void _update_alarm_indicator(bool settings_alarm_enabled, repetition_minute_state_t *state) {
+    state->alarm_enabled = settings_alarm_enabled;
+    if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+}
+
+void repetition_minute_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(repetition_minute_state_t));
+        repetition_minute_state_t *state = (repetition_minute_state_t *)*context_ptr;
+        state->signal_enabled = false;
+        state->watch_face_index = watch_face_index;
+    }
+}
+
+void repetition_minute_face_activate(void *context) {
+    repetition_minute_state_t *state = (repetition_minute_state_t *)context;
+
+    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
+
+    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
+
+    // handle chime indicator
+    if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+    else watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+    // show alarm indicator if there is an active alarm
+    _update_alarm_indicator(movement_alarm_enabled(), state);
+
+    watch_set_colon();
+
+    // this ensures that none of the timestamp fields will match, so we can re-render them all.
+    state->previous_date_time = 0xFFFFFFFF;
+}
+
+bool repetition_minute_face_loop(movement_event_t event, void *context) {
+    repetition_minute_state_t *state = (repetition_minute_state_t *)context;
+    char buf[11];
+    uint8_t pos;
+
+    watch_date_time_t date_time;
+    uint32_t previous_date_time;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+        case EVENT_LOW_ENERGY_UPDATE:
+            date_time = watch_rtc_get_date_time();
+            previous_date_time = state->previous_date_time;
+            state->previous_date_time = date_time.reg;
+
+            // check the battery voltage once a day...
+            if (date_time.unit.day != state->last_battery_check) {
+                state->last_battery_check = date_time.unit.day;
+                watch_enable_adc();
+                uint16_t voltage = watch_get_vcc_voltage();
+                watch_disable_adc();
+                // 2.2 volts will happen when the battery has maybe 5-10% remaining?
+                // we can refine this later.
+                state->battery_low = (voltage < 2200);
+            }
+
+            // ...and set the LAP indicator if low.
+            if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
+
+            if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+                // everything before seconds is the same, don't waste cycles setting those segments.
+                watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
+                watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
+                break;
+            } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+                // everything before minutes is the same.
+                pos = 6;
+                sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
+            } else {
+                // other stuff changed; let's do it all.
+                if (!movement_clock_mode_24h()) {
+                    // if we are in 12 hour mode, do some cleanup.
+                    if (date_time.unit.hour < 12) {
+                        watch_clear_indicator(WATCH_INDICATOR_PM);
+                    } else {
+                        watch_set_indicator(WATCH_INDICATOR_PM);
+                    }
+                    date_time.unit.hour %= 12;
+                    if (date_time.unit.hour == 0) date_time.unit.hour = 12;
+                }
+                pos = 0;
+                if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+                    if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
+                    sprintf(buf, "%s%2d%2d%02d  ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
+                } else {
+                    sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
+                }
+            }
+            watch_display_string(buf, pos);
+            // handle alarm indicator
+            if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->signal_enabled = !state->signal_enabled;
+            if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+            else watch_clear_indicator(WATCH_INDICATOR_BELL);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            // uncomment this line to snap back to the clock face when the hour signal sounds:
+            // movement_move_to_face(state->watch_face_index);
+            movement_play_signal();
+            break;
+        case EVENT_LIGHT_LONG_UP:
+            /*
+             * Howdy neighbors, this is the actual complication. Like an actual
+             * (very expensive) watch with a repetition minute complication it's
+             * boring at 00:00 or 1:00 and very quite musical at 23:59 or 12:59.
+             */
+
+            date_time = watch_rtc_get_date_time();
+            
+            
+            int hours = date_time.unit.hour;
+            int quarters = date_time.unit.minute / 15;
+            int minutes = date_time.unit.minute % 15;
+
+            // chiming hours
+            if (!movement_clock_mode_24h()) {
+                hours = date_time.unit.hour % 12;                
+                if (hours == 0) hours = 12;
+            }
+            if (hours > 0) {
+                int count = 0;
+                for(count = hours; count > 0; --count) {          
+                    play_hour_chime();
+                }
+            }
+
+            // chiming quarters (if needed)
+            if (quarters > 0) {
+                int count = 0;
+                for(count = quarters; count > 0; --count) {          
+                    play_quarter_chime();
+                }
+            }
+
+            // chiming minutes (if needed)
+            if (minutes > 0) {
+                int count = 0;
+                for(count = minutes; count > 0; --count) {          
+                    play_minute_chime();
+                }
+            }
+           
+            break; 
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void repetition_minute_face_resign(void *context) {
+    (void) context;
+}
+
+movement_watch_face_advisory_t repetition_minute_face_advise(void *context) {
+    repetition_minute_state_t *state = (repetition_minute_state_t *)context;
+    movement_watch_face_advisory_t retval = { 0 };
+
+    if (state->signal_enabled) {
+        watch_date_time_t date_time = watch_rtc_get_date_time();
+        retval.wants_background_task = date_time.unit.minute == 0;
+    }
+
+    return retval;
+}
diff --git a/legacy/watch_faces/clock/repetition_minute_face.h b/legacy/watch_faces/clock/repetition_minute_face.h
new file mode 100644 (file)
index 0000000..1e5d10a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jonas Termeau
+ *
+ * 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 REPETITION_MINUTE_FACE_H_
+#define REPETITION_MINUTE_FACE_H_
+
+/*
+ * REPETITION MINUTE face
+ *
+ * A hopefully useful complication for friendly neighbors in the dark
+ *
+ * Originating from 1676 from reverend and mechanician Edward Barlow, and
+ * perfected in 1820 by neighbor Abraham Breguet, a minute repeater or 
+ * "repetition minute" is a complication in a mechanical watch or clock that
+ * chimes the hours and often minutes at the press of a button. There are many
+ * types of repeater, from the simple repeater which merely strikes the number
+ * of hours, to the minute repeater which chimes the time down to the minute,
+ * using separate tones for hours, quarter hours, and minutes. They originated
+ * before widespread artificial illumination, to allow the time to be determined
+ * in the dark, and were also used by the visually impaired. 
+ *
+ * How to use it :
+ * 
+ * Long press the light button to get an auditive reading of the time like so :
+ * 0..23 (1..12 if 24-hours format isn't enabled) low beep(s) for the hours
+ * 0..3 low-high couple pitched beeps for the quarters
+ * 0..14 high pitched beep(s) for the remaining minutes
+ *
+ * Prerequisite : a watch with a working buzzer
+ * 
+ * ~ Only in the darkness can you see the stars. - Martin Luther King ~
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint32_t previous_date_time;
+    uint8_t last_battery_check;
+    uint8_t watch_face_index;
+    bool signal_enabled;
+    bool battery_low;
+    bool alarm_enabled;
+} repetition_minute_state_t;
+
+void play_hour_chime(void);
+void play_quarter_chime(void);
+void play_minute_chime(void);
+void repetition_minute_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void repetition_minute_face_activate(void *context);
+bool repetition_minute_face_loop(movement_event_t event, void *context);
+void repetition_minute_face_resign(void *context);
+movement_watch_face_advisory_t repetition_minute_face_advise(void *context);
+
+#define repetition_minute_face ((const watch_face_t){ \
+    repetition_minute_face_setup, \
+    repetition_minute_face_activate, \
+    repetition_minute_face_loop, \
+    repetition_minute_face_resign, \
+    repetition_minute_face_advise, \
+})
+
+#endif // REPETITION_MINUTE_FACE_H_
diff --git a/legacy/watch_faces/clock/simple_clock_bin_led_face.c b/legacy/watch_faces/clock/simple_clock_bin_led_face.c
new file mode 100644 (file)
index 0000000..527b1cd
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Andreas Nebinger, based on Joey Castillo's simple clock face
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "simple_clock_bin_led_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_private_display.h"
+
+static void _update_alarm_indicator(bool settings_alarm_enabled, simple_clock_bin_led_state_t *state) {
+    state->alarm_enabled = settings_alarm_enabled;
+    if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+}
+
+static void _display_left_aligned(uint8_t value) {
+    if (value >= 10) {
+        watch_display_character('0' + value / 10, 4);
+        watch_display_character('0' + value % 10, 5);
+    } else {
+        watch_display_character('0' + value, 4);
+        watch_display_character(' ', 5);
+    }
+}
+
+void simple_clock_bin_led_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(simple_clock_bin_led_state_t));
+        memset(*context_ptr, 0, sizeof(simple_clock_bin_led_state_t));
+        simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)*context_ptr;
+        state->watch_face_index = watch_face_index;
+    }
+}
+
+void simple_clock_bin_led_face_activate(void *context) {
+    simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
+
+    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
+
+    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
+
+    // handle chime indicator
+    if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+    else watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+    // show alarm indicator if there is an active alarm
+    _update_alarm_indicator(movement_alarm_enabled(), state);
+
+    watch_set_colon();
+
+    // this ensures that none of the timestamp fields will match, so we can re-render them all.
+    state->previous_date_time = 0xFFFFFFFF;
+}
+
+bool simple_clock_bin_led_face_loop(movement_event_t event, void *context) {
+    simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
+    char buf[11];
+    uint8_t pos;
+
+    watch_date_time_t date_time;
+    uint32_t previous_date_time;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+        case EVENT_LOW_ENERGY_UPDATE:
+            date_time = watch_rtc_get_date_time();
+            if (state->flashing_state > 0) {
+                if (state->ticks) {
+                    state->ticks--;
+                } else {
+                    
+                    if (state->flashing_state & 64) {
+                        // start led on for current bit
+                        state->flashing_state &= 63;
+                        state->ticks = (state->flashing_value & 1 ? 7 : 1);
+                        movement_illuminate_led();
+                    } else {
+                        // indicate first or switch to next bit
+                        watch_set_led_off();
+                        if ((state->flashing_state & 128) == 0) state->flashing_value = state->flashing_value >> 1;
+                        if (state->flashing_value || (state->flashing_state & 128)) {
+                            state->flashing_state &= 127;
+                            state->flashing_state |= 64;
+                            state->ticks = 6;
+                        } else if (state->flashing_state & 1) {
+                            // transition to minutes
+                            state->flashing_state = 2 + 128;
+                            state->flashing_value = date_time.unit.minute;
+                            _display_left_aligned(state->flashing_value);
+                            state->ticks = 9;
+                        } else {
+                            // end flashing
+                            state->flashing_state = 0;
+                            state->previous_date_time = 0xFFFFFFFF;
+                            movement_request_tick_frequency(1);
+                            watch_set_colon();
+                        }
+                    }
+                }
+            } else {
+                previous_date_time = state->previous_date_time;
+                state->previous_date_time = date_time.reg;
+
+                // check the battery voltage once a day...
+                if (date_time.unit.day != state->last_battery_check) {
+                    state->last_battery_check = date_time.unit.day;
+                    watch_enable_adc();
+                    uint16_t voltage = watch_get_vcc_voltage();
+                    watch_disable_adc();
+                    // 2.2 volts will happen when the battery has maybe 5-10% remaining?
+                    // we can refine this later.
+                    state->battery_low = (voltage < 2200);
+                }
+
+                // ...and set the LAP indicator if low.
+                if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
+
+                if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+                    // everything before seconds is the same, don't waste cycles setting those segments.
+                    watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
+                    watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
+                    break;
+                } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+                    // everything before minutes is the same.
+                    pos = 6;
+                    sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
+                } else {
+                    // other stuff changed; let's do it all.
+                    if (!movement_clock_mode_24h()) {
+                        // if we are in 12 hour mode, do some cleanup.
+                        if (date_time.unit.hour < 12) {
+                            watch_clear_indicator(WATCH_INDICATOR_PM);
+                        } else {
+                            watch_set_indicator(WATCH_INDICATOR_PM);
+                        }
+                        date_time.unit.hour %= 12;
+                        if (date_time.unit.hour == 0) date_time.unit.hour = 12;
+                    }
+                    pos = 0;
+                    if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+                        if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
+                        sprintf(buf, "%s%2d%2d%02d  ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
+                    } else {
+                        sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
+                    }
+                }
+                watch_display_string(buf, pos);
+                // handle alarm indicator
+                if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->signal_enabled = !state->signal_enabled;
+            if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+            else watch_clear_indicator(WATCH_INDICATOR_BELL);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            // uncomment this line to snap back to the clock face when the hour signal sounds:
+            // movement_move_to_face(state->watch_face_index);
+            movement_play_signal();
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (state->flashing_state == 0) {
+                date_time = watch_rtc_get_date_time();
+                state->flashing_state = 1 + 128;
+                state->ticks = 4;
+                if (!movement_clock_mode_24h()) {
+                    date_time.unit.hour %= 12;
+                    if (date_time.unit.hour == 0) date_time.unit.hour = 12;
+                }
+                watch_display_string("      ", 4);
+                _display_left_aligned(date_time.unit.hour);
+                state->flashing_value = date_time.unit.hour > 12 ? date_time.unit.hour - 12 : date_time.unit.hour;
+                watch_set_led_off();
+                watch_clear_colon();
+                movement_request_tick_frequency(8);
+            }
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void simple_clock_bin_led_face_resign(void *context) {
+    (void) context;
+}
+
+movement_watch_face_advisory_t simple_clock_bin_led_face_advise(void *context) {
+    simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
+    movement_watch_face_advisory_t retval = { 0 };
+
+    if (state->signal_enabled) {
+        watch_date_time_t date_time = watch_rtc_get_date_time();
+        retval.wants_background_task = date_time.unit.minute == 0;
+    }
+
+    return retval;
+}
diff --git a/legacy/watch_faces/clock/simple_clock_bin_led_face.h b/legacy/watch_faces/clock/simple_clock_bin_led_face.h
new file mode 100644 (file)
index 0000000..ed9d3ae
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Andreas Nebinger, based on Joey Castillo's simple clock face
+ *
+ * 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 SIIMPLE_CLOCK_BIN_LED_FACE_H_
+#define SIIMPLE_CLOCK_BIN_LED_FACE_H_
+
+/*
+ * BINARY LED CLOCK FACE
+ *
+ * A "fork" of the simple clock face, which provides the functionality of showing 
+ * the current time by flashing the LED using binary representation.
+ *
+ * This feature serves as a practical solution to compensate for the admittedly 
+ * subpar backlight of the F91w watch and is especially useful if your eyesight
+ * is not the best. By pressing and holding the light button long enough, the
+ * watch will illuminate the LED to showcase the current time.
+ *
+ * How to interpret the flashing led:
+ * - Firstly, the hour is presented as a binary number, with the lowest bit displayed
+ *   first. A short flash signifies 0, while a longer flash represents 1. If you use
+ *   the watch in 24h mode, please note that the indicated value may be decreased 
+ *   by 12, to keep things simple and short. For example, 22h would be translated
+ *   to 10h.
+ * - After showing the hour, a lengthier  pause indicates that minutes will be shown 
+ *   next.
+ * - Similar to the hour representation, minutes are displayed in binary format, 
+ *   starting with the lowest bits. A short flash denotes 0, a longer flash 
+ *   represents 1.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint32_t previous_date_time;
+    uint8_t last_battery_check;
+    uint8_t watch_face_index;
+    bool signal_enabled;
+    bool battery_low;
+    bool alarm_enabled;
+    uint8_t flashing_state; // bitmap representing the flashing state. Bit 0 = hours showing, bit 1 = minutes showing,
+                            // bit 6 = short break between flashing bits, bit 7 = long break between hours and minutes
+    uint8_t flashing_value;
+    uint8_t ticks;
+} simple_clock_bin_led_state_t;
+
+void simple_clock_bin_led_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void simple_clock_bin_led_face_activate(void *context);
+bool simple_clock_bin_led_face_loop(movement_event_t event, void *context);
+void simple_clock_bin_led_face_resign(void *context);
+movement_watch_face_advisory_t simple_clock_bin_led_face_advise(void *context);
+
+#define simple_clock_bin_led_face ((const watch_face_t){ \
+    simple_clock_bin_led_face_setup, \
+    simple_clock_bin_led_face_activate, \
+    simple_clock_bin_led_face_loop, \
+    simple_clock_bin_led_face_resign, \
+    simple_clock_bin_led_face_advise, \
+})
+
+#endif // SIIMPLE_CLOCK_BIN_LED_FACE_H_
+
diff --git a/legacy/watch_faces/clock/weeknumber_clock_face.c b/legacy/watch_faces/clock/weeknumber_clock_face.c
new file mode 100644 (file)
index 0000000..3a153e1
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include "weeknumber_clock_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+static void _update_alarm_indicator(bool settings_alarm_enabled, weeknumber_clock_state_t *state) {
+    state->alarm_enabled = settings_alarm_enabled;
+    if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+}
+
+void weeknumber_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(weeknumber_clock_state_t));
+        weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)*context_ptr;
+        state->signal_enabled = false;
+        state->watch_face_index = watch_face_index;
+    }
+}
+
+void weeknumber_clock_face_activate(void *context) {
+    weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
+
+    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
+
+    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
+
+    // handle chime indicator
+    if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+    else watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+    // show alarm indicator if there is an active alarm
+    _update_alarm_indicator(movement_alarm_enabled(), state);
+
+    watch_set_colon();
+
+    // this ensures that none of the timestamp fields will match, so we can re-render them all.
+    state->previous_date_time = 0xFFFFFFFF;
+}
+
+bool weeknumber_clock_face_loop(movement_event_t event, void *context) {
+    weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
+    char buf[11];
+    uint8_t pos;
+
+    watch_date_time_t date_time;
+    uint32_t previous_date_time;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+        case EVENT_LOW_ENERGY_UPDATE:
+            date_time = watch_rtc_get_date_time();
+            previous_date_time = state->previous_date_time;
+            state->previous_date_time = date_time.reg;
+
+            // check the battery voltage once a day...
+            if (date_time.unit.day != state->last_battery_check) {
+                state->last_battery_check = date_time.unit.day;
+                watch_enable_adc();
+                uint16_t voltage = watch_get_vcc_voltage();
+                watch_disable_adc();
+                // 2.2 volts will happen when the battery has maybe 5-10% remaining?
+                // we can refine this later.
+                state->battery_low = (voltage < 2200);
+            }
+
+            // ...and set the LAP indicator if low.
+            if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
+
+            if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+                // everything before minutes is the same.
+                pos = 6;
+                sprintf(buf, "%02d%02d", date_time.unit.minute, watch_utility_get_weeknumber(date_time.unit.year, date_time.unit.month, date_time.unit.day));
+            } else {
+                // other stuff changed; let's do it all.
+                if (!movement_clock_mode_24h()) {
+                    // if we are in 12 hour mode, do some cleanup.
+                    if (date_time.unit.hour < 12) {
+                        watch_clear_indicator(WATCH_INDICATOR_PM);
+                    } else {
+                        watch_set_indicator(WATCH_INDICATOR_PM);
+                    }
+                    date_time.unit.hour %= 12;
+                    if (date_time.unit.hour == 0) date_time.unit.hour = 12;
+                }
+                pos = 0;
+                if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+                    if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
+                    sprintf(buf, "%s%2d%2d%02d  ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
+                } else {
+                    sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, watch_utility_get_weeknumber(date_time.unit.year, date_time.unit.month, date_time.unit.day));
+                }
+            }
+            watch_display_string(buf, pos);
+            // handle alarm indicator
+            if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->signal_enabled = !state->signal_enabled;
+            if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+            else watch_clear_indicator(WATCH_INDICATOR_BELL);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            // uncomment this line to snap back to the clock face when the hour signal sounds:
+            // movement_move_to_face(state->watch_face_index);
+            movement_play_signal();
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void weeknumber_clock_face_resign(void *context) {
+    (void) context;
+}
+
+movement_watch_face_advisory_t weeknumber_clock_face_advise(void *context) {
+    weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
+    movement_watch_face_advisory_t retval = { 0 };
+
+    if (state->signal_enabled) {
+        watch_date_time_t date_time = watch_rtc_get_date_time();
+        retval.wants_background_task = date_time.unit.minute == 0;
+    }
+
+    return retval;
+}
diff --git a/legacy/watch_faces/clock/weeknumber_clock_face.h b/legacy/watch_faces/clock/weeknumber_clock_face.h
new file mode 100644 (file)
index 0000000..9ba1270
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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 WEEKNUMBER_CLOCK_FACE_H_
+#define WEEKNUMBER_CLOCK_FACE_H_
+
+/*
+ * WEEK-NUMBER WATCH FACE
+ *
+ * Same as simple clock, but has iso 8601 week number instead of seconds counter.
+ *
+ * Long-press ALARM to toggle the hourly chime.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint32_t previous_date_time;
+    uint8_t last_battery_check;
+    uint8_t watch_face_index;
+    bool signal_enabled;
+    bool battery_low;
+    bool alarm_enabled;
+} weeknumber_clock_state_t;
+
+void weeknumber_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void weeknumber_clock_face_activate(void *context);
+bool weeknumber_clock_face_loop(movement_event_t event, void *context);
+void weeknumber_clock_face_resign(void *context);
+movement_watch_face_advisory_t weeknumber_clock_face_advise(void *context);
+
+#define weeknumber_clock_face ((const watch_face_t){ \
+    weeknumber_clock_face_setup, \
+    weeknumber_clock_face_activate, \
+    weeknumber_clock_face_loop, \
+    weeknumber_clock_face_resign, \
+    weeknumber_clock_face_advise, \
+})
+
+#endif // SIMPLE_CLOCK_FACE_H_
diff --git a/legacy/watch_faces/clock/world_clock2_face.c b/legacy/watch_faces/clock/world_clock2_face.c
new file mode 100644 (file)
index 0000000..e79f2df
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Konrad Rieck
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "world_clock2_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_utility.h"
+
+static bool refresh_face;
+
+/* Simple macros for navigation */
+#define FORWARD             +1
+#define BACKWARD            -1
+
+/* Activate refresh of time */
+#define REFRESH_TIME        0xffffffff
+
+/* List of all time zone names */
+const char *zone_names[] = {
+    "UTC",     //  0 :   0:00:00 (UTC)
+    "CET",     //  1 :   1:00:00 (Central European Time)
+    "SAST",    //  2 :   2:00:00 (South African Standard Time)
+    "ARST",    //  3 :   3:00:00 (Arabia Standard Time)
+    "IRST",    //  4 :   3:30:00 (Iran Standard Time)
+    "GET",     //  5 :   4:00:00 (Georgia Standard Time)
+    "AFT",     //  6 :   4:30:00 (Afghanistan Time)
+    "PKT",     //  7 :   5:00:00 (Pakistan Standard Time)
+    "IST",     //  8 :   5:30:00 (Indian Standard Time)
+    "NPT",     //  9 :   5:45:00 (Nepal Time)
+    "KGT",     // 10 :   6:00:00 (Kyrgyzstan time)
+    "MYST",    // 11 :   6:30:00 (Myanmar Time)
+    "THA",     // 12 :   7:00:00 (Thailand Standard Time)
+    "CST",     // 13 :   8:00:00 (China Standard Time, Australian Western Standard Time)
+    "ACWS",    // 14 :   8:45:00 (Australian Central Western Standard Time)
+    "JST",     // 15 :   9:00:00 (Japan Standard Time, Korea Standard Time)
+    "ACST",    // 16 :   9:30:00 (Australian Central Standard Time)
+    "AEST",    // 17 :  10:00:00 (Australian Eastern Standard Time)
+    "LHST",    // 18 :  10:30:00 (Lord Howe Standard Time)
+    "SBT",     // 19 :  11:00:00 (Solomon Islands Time)
+    "NZST",    // 20 :  12:00:00 (New Zealand Standard Time)
+    "CHAS",    // 21 :  12:45:00 (Chatham Standard Time)
+    "TOT",     // 22 :  13:00:00 (Tonga Time)
+    "CHAD",    // 23 :  13:45:00 (Chatham Daylight Time)
+    "LINT",    // 24 :  14:00:00 (Line Islands Time)
+    "BIT",     // 25 : -12:00:00 (Baker Island Time)
+    "NUT",     // 26 : -11:00:00 (Niue Time)
+    "HST",     // 27 : -10:00:00 (Hawaii-Aleutian Standard Time)
+    "MART",    // 28 :  -9:30:00 (Marquesas Islands Time)
+    "AKST",    // 29 :  -9:00:00 (Alaska Standard Time)
+    "PST",     // 30 :  -8:00:00 (Pacific Standard Time)
+    "MST",     // 31 :  -7:00:00 (Mountain Standard Time)
+    "CST",     // 32 :  -6:00:00 (Central Standard Time)
+    "EST",     // 33 :  -5:00:00 (Eastern Standard Time)
+    "VET",     // 34 :  -4:30:00 (Venezuelan Standard Time)
+    "AST",     // 35 :  -4:00:00 (Atlantic Standard Time)
+    "NST",     // 36 :  -3:30:00 (Newfoundland Standard Time)
+    "BRT",     // 37 :  -3:00:00 (Brasilia Time)
+    "NDT",     // 38 :  -2:30:00 (Newfoundland Daylight Time)
+    "FNT",     // 39 :  -2:00:00 (Fernando de Noronha Time)
+    "AZOT",    // 40 :  -1:00:00 (Azores Standard Time)
+};
+
+/* Modulo function */
+static inline unsigned int mod(int a, int b)
+{
+    int r = a % b;
+    return r < 0 ? r + b : r;
+}
+
+/* Find the next selected time zone */
+static inline uint8_t find_selected_zone(world_clock2_state_t *state, int direction)
+{
+    uint8_t i = state->current_zone;
+
+    do {
+       i = mod(i + direction, NUM_TIME_ZONES);
+       /* Could not find a selected zone. Return UTC */
+       if (i == state->current_zone) {
+           return 0;
+       }
+    } while (!state->zones[i].selected);
+
+    return i;
+}
+
+/* Beep when zone is enabled. An octave up */
+static void beep_enable() {
+    watch_buzzer_play_note(BUZZER_NOTE_G7, 50);
+    watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
+    watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
+}
+
+/* Beep when zone id disable. An octave down */
+static void beep_disable() {
+    watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+    watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
+    watch_buzzer_play_note(BUZZER_NOTE_G7, 75);
+}
+
+void world_clock2_face_setup(uint8_t watch_face_index, void **context_ptr)
+{
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(world_clock2_state_t));
+        memset(*context_ptr, 0, sizeof(world_clock2_state_t));
+
+        /* Start in settings mode */
+        world_clock2_state_t *state = (world_clock2_state_t *) * context_ptr;
+        state->current_mode = WORLD_CLOCK2_MODE_SETTINGS;
+    }
+}
+
+void world_clock2_face_activate(void *context)
+{
+    world_clock2_state_t *state = (world_clock2_state_t *) context;
+
+    if (watch_sleep_animation_is_running())
+        watch_stop_sleep_animation();
+
+    switch (state->current_mode) {
+        case WORLD_CLOCK2_MODE_DISPLAY:
+            /* Normal tick frequency */
+            movement_request_tick_frequency(1);
+            break;
+        case WORLD_CLOCK2_MODE_SETTINGS:
+            /* Faster frequency for blinking effect */
+            movement_request_tick_frequency(4);
+            break;
+    }
+    refresh_face = true;
+}
+
+static bool mode_display(movement_event_t event, world_clock2_state_t *state)
+{
+    char buf[11];
+    uint8_t pos;
+
+    uint32_t timestamp;
+    uint32_t previous_date_time;
+    watch_date_time_t date_time;
+
+    switch (event.event_type) {
+       case EVENT_ACTIVATE:
+       case EVENT_TICK:
+       case EVENT_LOW_ENERGY_UPDATE:
+           /* Update indicators and colon on refresh */
+           if (refresh_face) {
+               watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+               watch_set_colon();
+                if (movement_clock_mode_24h())
+                    watch_set_indicator(WATCH_INDICATOR_24H);
+
+                state->previous_date_time = REFRESH_TIME;
+                refresh_face = false;
+            }
+
+            /* Determine current time at time zone and store date/time */
+           date_time = watch_rtc_get_date_time();
+           timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
+           date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->current_zone] * 60);
+           previous_date_time = state->previous_date_time;
+           state->previous_date_time = date_time.reg;
+
+           if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+                /* Everything before seconds is the same, don't waste cycles setting those segments. */
+               pos = 8;
+               sprintf(buf, "%02d", date_time.unit.second);
+           } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+               /* Everything before minutes is the same. */
+               pos = 6;
+               sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
+           } else {
+               /* Other stuff changed; Let's do it all. */
+               if (!movement_clock_mode_24h()) {
+                   /* If we are in 12 hour mode, do some cleanup. */
+                   if (date_time.unit.hour < 12) {
+                       watch_clear_indicator(WATCH_INDICATOR_PM);
+                   } else {
+                       watch_set_indicator(WATCH_INDICATOR_PM);
+                   }
+                   date_time.unit.hour %= 12;
+                   if (date_time.unit.hour == 0)
+                       date_time.unit.hour = 12;
+               }
+
+               pos = 0;
+               if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+                   if (!watch_sleep_animation_is_running())
+                       watch_start_sleep_animation(500);
+
+                   sprintf(buf, "%.2s%2d%2d%02d  ",
+                            zone_names[state->current_zone],
+                            date_time.unit.day,
+                            date_time.unit.hour,
+                            date_time.unit.minute);
+               } else {
+                   sprintf(buf, "%.2s%2d%2d%02d%02d",
+                           zone_names[state->current_zone],
+                            date_time.unit.day,
+                            date_time.unit.hour,
+                            date_time.unit.minute,
+                            date_time.unit.second);
+               }
+           }
+           watch_display_string(buf, pos);
+           break;
+       case EVENT_ALARM_BUTTON_UP:
+           state->current_zone = find_selected_zone(state, FORWARD);
+            state->previous_date_time = REFRESH_TIME;
+           break;
+       case EVENT_LIGHT_BUTTON_DOWN:
+           /* Do nothing. */
+           break;
+       case EVENT_LIGHT_BUTTON_UP:
+           state->current_zone = find_selected_zone(state, BACKWARD);
+            state->previous_date_time = REFRESH_TIME;
+           break;
+       case EVENT_LIGHT_LONG_PRESS:
+           movement_illuminate_led();
+           break;
+       case EVENT_ALARM_LONG_PRESS:
+           /* Switch to settings mode */
+           state->current_mode = WORLD_CLOCK2_MODE_SETTINGS;
+           refresh_face = true;
+            movement_request_tick_frequency(1);
+
+            if (movement_button_should_sound())
+                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+           break;
+        case EVENT_MODE_BUTTON_UP:
+            /* Reset frequency and move to next face */
+            movement_request_tick_frequency(1);
+            movement_move_to_next_face();
+            break;
+       default:
+           return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+static bool mode_settings(movement_event_t event, world_clock2_state_t *state)
+{
+    char buf[11];
+    int8_t hours, minutes;
+    uint8_t zone;
+    div_t result;
+
+    switch (event.event_type) {
+       case EVENT_ACTIVATE:
+       case EVENT_TICK:
+       case EVENT_LOW_ENERGY_UPDATE:
+            /* Update indicator and colon on refresh */
+            if (refresh_face) {
+                watch_clear_colon();
+                watch_clear_indicator(WATCH_INDICATOR_24H);
+                watch_clear_indicator(WATCH_INDICATOR_PM);
+                refresh_face = false;
+            }
+           result = div(movement_timezone_offsets[state->current_zone], 60);
+           hours = result.quot;
+           minutes = result.rem;
+
+           /*
+            * Display time zone. The range of the parameters is reduced
+            * to avoid accidentally overflowing the buffer and to suppress
+            * corresponding compiler warnings.
+            */
+           sprintf(buf, "%.2s%2d %c%02d%02d",
+                    zone_names[state->current_zone],
+                    state->current_zone % 100,
+                    hours < 0 ? '-' : '+',
+                    abs(hours) % 24,
+                   abs(minutes) % 60);
+
+            /* Let the zone number blink */
+            if (event.subsecond % 2)
+                buf[2] = buf[3] = ' ';
+
+           if (state->zones[state->current_zone].selected)
+               watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+           else
+               watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+
+           watch_display_string(buf, 0);
+           break;
+       case EVENT_ALARM_BUTTON_UP:
+           state->current_zone = mod(state->current_zone + FORWARD, NUM_TIME_ZONES);
+           break;
+       case EVENT_LIGHT_BUTTON_UP:
+           state->current_zone = mod(state->current_zone + BACKWARD, NUM_TIME_ZONES);
+           break;
+       case EVENT_LIGHT_BUTTON_DOWN:
+           /* Do nothing */
+           break;
+       case EVENT_ALARM_LONG_PRESS:
+           /* Find next selected zone */
+           if (!state->zones[state->current_zone].selected)
+               state->current_zone = find_selected_zone(state, FORWARD);
+
+           /* Switch to display mode */
+           state->current_mode = WORLD_CLOCK2_MODE_DISPLAY;
+           refresh_face = true;
+            movement_request_tick_frequency(1);
+
+            if (movement_button_should_sound())
+                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+           break;
+       case EVENT_LIGHT_LONG_PRESS:
+           /* Toggle selection of current zone */
+           zone = state->current_zone;
+           state->zones[zone].selected = !state->zones[zone].selected;
+
+            if (movement_button_should_sound()) {
+                if (state->zones[zone].selected) {
+                    beep_enable();
+                } else {
+                    beep_disable();
+                }
+            }
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            /* Reset frequency and move to next face */
+            movement_request_tick_frequency(1);
+            movement_move_to_next_face();
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+bool world_clock2_face_loop(movement_event_t event, void *context)
+{
+    world_clock2_state_t *state = (world_clock2_state_t *) context;
+    switch (state->current_mode) {
+       case WORLD_CLOCK2_MODE_DISPLAY:
+           return mode_display(event, state);
+       case WORLD_CLOCK2_MODE_SETTINGS:
+           return mode_settings(event, state);
+    }
+    return false;
+}
+
+void world_clock2_face_resign(void *context)
+{
+    (void) context;
+}
diff --git a/legacy/watch_faces/clock/world_clock2_face.h b/legacy/watch_faces/clock/world_clock2_face.h
new file mode 100644 (file)
index 0000000..bd1ac69
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Konrad Rieck
+ * 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 WORLD_CLOCK2_FACE_H_
+#define WORLD_CLOCK2_FACE_H_
+
+/*
+ * WORLD CLOCK 2
+ *
+ * This is an alternative world clock face that allows the user to cycle
+ * through a list of selected time zones. It extends the original
+ * implementation by Joey Castillo. The face has two modes: display mode
+ * and settings mode.
+ *
+ * Settings mode
+ *
+ * When the clock face is activated for the first time, it enters settings
+ * mode. Here, the user can select the time zones they want to display. The
+ * face shows a summary of the current time zone:
+ *  * The top of the face displays the first two letters of the time zone
+ *    abbreviation, such as "PS" for Pacific Standard Time or CE for
+ *    "Central European Time".
+ *  * The upper-right corner shows the index number of the time zone. This
+ *    helps avoid confusion when multiple time zones have the same two-letter
+ *    abbreviation.
+ *  * The main display shows the offset from UTC, with a "+" indicating a
+ *    positive offset and a "-" indicating a negative offset. For example,
+ *    the offset for Japanese Standard Time is displayed as "+9:00".
+ *
+ * The user can navigate through the time zones and select them using the
+ * following buttons:
+ *  * The ALARM button moves forward to the next time zone, while the LIGHT
+ *    button moves backward to the previous zone. This way, the user can
+ *    cycle through all 41 supported time zones.
+ *  * A long press on the LIGHT button selects the current time zone, and
+ *    the signal indicator appears at the top left. Another long press of
+ *    the LIGHT button deselects the time zone.
+ *  * A long press on the ALARM button exits settings mode and returns to
+ *    display mode.
+ *
+ * Display mode
+ *
+ * In the display mode, the face shows the time of the currently selected
+ * time zone. The face includes the following components:
+ *  * The top of the face displays the first two letters of the time zone
+ *    abbreviation, such as "PS" for Pacific Standard Time or "CE" for
+ *    Central European Time.
+ *  * The upper-right corner shows the current day of the month, which helps
+ *    indicate time zones that cross the international date line with respect
+ *    to the local time.
+ *  * The main display shows the time in the selected time zone in either
+ *    12-hour or 24-hour form. There is no timeout, allowing users to keep
+ *    the chosen time zone displayed for as long as they wish.
+ *
+ * The user can navigate through the selected time zones using the following
+ * buttons:
+ *  * The ALARM button moves to the next selected time zone, while the LIGHT
+ *    button moves to the previous zone. If no time zone is selected, the
+ *    face simply shows UTC.
+ *  * A long press on the ALARM button enters settings mode and enables the
+ *    user to re-configure the selected time zones.
+ *  * A long press on the LIGHT button activates the LED illumination of the
+ *    watch.
+ */
+
+/* Number of zones. See movement_timezone_offsets. */
+#define NUM_TIME_ZONES  41
+
+#include "movement.h"
+
+typedef enum {
+    WORLD_CLOCK2_MODE_DISPLAY,
+    WORLD_CLOCK2_MODE_SETTINGS
+} world_clock2_mode_t;
+
+typedef struct {
+    bool selected;
+} world_clock2_zone_t;
+
+typedef struct {
+    world_clock2_zone_t zones[NUM_TIME_ZONES];
+    world_clock2_mode_t current_mode;
+    uint8_t current_zone;
+    uint32_t previous_date_time;
+} world_clock2_state_t;
+
+void world_clock2_face_setup(uint8_t watch_face_index, void **context_ptr);
+void world_clock2_face_activate(void *context);
+bool world_clock2_face_loop(movement_event_t event, void *context);
+void world_clock2_face_resign(void *context);
+
+#define world_clock2_face ((const watch_face_t){ \
+    world_clock2_face_setup, \
+    world_clock2_face_activate, \
+    world_clock2_face_loop, \
+    world_clock2_face_resign, \
+    NULL, \
+})
+
+#endif /* WORLD_CLOCK2_FACE_H_ */
diff --git a/legacy/watch_faces/clock/wyoscan_face.c b/legacy/watch_faces/clock/wyoscan_face.c
new file mode 100644 (file)
index 0000000..bf7b5e1
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 <#author_name#>
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "wyoscan_face.h"
+#include "watch_private_display.h"
+
+/*
+Slowly render the current time from left to right, 
+scanning across its liquid crystal face, completing 1 cycle every 2 seconds.
+
+Created to mimic the wyoscan watch that was produced by Halmos and designed by Dexter Sinister
+It looks like this https://www.o-r-g.com/apps/wyoscan
+
+You’ll notice that reading this watch requires more attention than usual, 
+as the seven segments of each digit are lit one by one across its display. 
+This speed may be adjusted until it reaches the limits of your perception. 
+You and your watch are now in tune.
+
+This is a relatively generic way of animating a time display.
+If you want to modify the animation, you can change the segment_map
+the A-F are corresponding to the segments on the watch face
+  A  
+F   B
+  G
+E   C
+  D
+the X's are the frames that will be skipped in the animation
+This particular segment_map allocates 8 frames to display each number
+this is to achieve the 2 second cycle time.
+8 frames per number * 6 numbers + the trailing 16 frames = 64 frames
+at 32 frames per second, this is a 2 second cycle time.
+
+I tried to make the animation of each number display similar to if you were 
+to draw the number on the watch face with a pen, pausing with 'X'
+when your pen might turn a corner or when you might cross over 
+a line you've already drawn. It is vaguely top to bottom and counter,
+clockwise when possible.
+*/
+static char *segment_map[] = {
+    "AXFBDEXC",  // 0
+    "BXXXCXXX",  // 1
+    "ABGEXXXD",  // 2
+    "ABGXXXCD",  // 3
+    "FXGBXXXC",  // 4
+    "AXFXGXCD",  // 5
+    "AXFEDCXG",  // 6
+    "AXXBXXCX",  // 7
+    "AFGCDEXB",  // 8
+    "AFGBXXCD"   // 9
+};
+
+/*
+This is the mapping of input to the watch_set_pixel() function
+for each position in hhmmss it defines the 2 dimention input at each of A-F*/
+static const int32_t clock_mapping[6][7][2] = {
+    // hour 1
+    {{1,18}, {2,19}, {0,19}, {1,18}, {0,18}, {2,18}, {1,19}},
+    // hour 2
+    {{2,20}, {2,21}, {1,21}, {0,21}, {0,20}, {1,17}, {1,20}},
+    // minute 1
+    {{0,22}, {2,23}, {0,23}, {0,22}, {1,22}, {2,22}, {1,23}},
+    // minute 2
+    {{2,1}, {2,10}, {0,1}, {0,0}, {1,0}, {2,0}, {1,1}},
+    // second 1
+    {{2,2}, {2,3}, {0,4}, {0,3}, {0,2}, {1,2}, {1,3}},
+    // second 2
+    {{2,4}, {2,5}, {1,6}, {0,6}, {0,5}, {1,4}, {1,5}},
+};
+
+void wyoscan_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(wyoscan_state_t));
+        memset(*context_ptr, 0, sizeof(wyoscan_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void wyoscan_face_activate(void *context) {
+    wyoscan_state_t *state = (wyoscan_state_t *)context;
+    movement_request_tick_frequency(32);
+    state->total_frames = 64;
+}
+
+bool wyoscan_face_loop(movement_event_t event, void *context) {
+    wyoscan_state_t *state = (wyoscan_state_t *)context;
+
+    watch_date_time_t date_time;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            break;
+        case EVENT_TICK:            
+            if (!state->animate) {
+                date_time = watch_rtc_get_date_time();
+                state->start = 0; 
+                state->end = 0;
+                state->animation = 0;
+                state->animate = true;
+                state->time_digits[0] = date_time.unit.hour / 10;
+                state->time_digits[1] = date_time.unit.hour % 10;
+                state->time_digits[2] = date_time.unit.minute / 10;
+                state->time_digits[3] = date_time.unit.minute % 10;
+                state->time_digits[4] = date_time.unit.second / 10;
+                state->time_digits[5] = date_time.unit.second % 10;
+            }
+            if ( state->animate ) {
+                // if we have reached the max number of illuminated segments, we clear the oldest one
+                if ((state->end + 1) % MAX_ILLUMINATED_SEGMENTS == state->start) {
+                    // clear the oldest pixel if it's not 'X'
+                    if (state->illuminated_segments[state->start][0] != 99 && state->illuminated_segments[state->start][1] != 99) {
+                        watch_clear_pixel(state->illuminated_segments[state->start][0], state->illuminated_segments[state->start][1]);
+                    }
+                    // increment the start index to point to the next oldest pixel
+                    state->start = (state->start + 1) % MAX_ILLUMINATED_SEGMENTS;
+                }
+                if (state->animation < state->total_frames - MAX_ILLUMINATED_SEGMENTS) {
+                    if (state->animation % 32 == 0) {
+                        if (state->colon) {
+                            watch_set_colon();
+                        } else {
+                            watch_clear_colon();
+                        }
+                        state->colon = !state->colon;
+                    }
+                    
+                    // calculate the start position for the current frame
+                    state->position = (state->animation / 8) % 6;
+                    // calculate the current segment for the current digit
+                    state->segment = state->animation % strlen(segment_map[state->time_digits[state->position]]);
+                    // get the segments for the current digit
+                    state->segments = segment_map[state->time_digits[state->position]];
+                    
+                    if (state->segments[state->segment] == 'X') {
+                        // if 'X', skip this frame
+                        state->illuminated_segments[state->end][0] = 99;
+                        state->illuminated_segments[state->end][1] = 99;
+                        state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
+                        state->animation = (state->animation + 1);
+                        break;
+                    }
+
+                    // calculate the animation frame
+                    state->x = clock_mapping[state->position][state->segments[state->segment]-'A'][0];
+                    state->y = clock_mapping[state->position][state->segments[state->segment]-'A'][1];
+                    
+                    // set the new pixel
+                    watch_set_pixel(state->x, state->y);
+                    
+                    // store this pixel in the buffer
+                    state->illuminated_segments[state->end][0] = state->x;
+                    state->illuminated_segments[state->end][1] = state->y;
+                    // increment the end index to the next position
+                    state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
+                } 
+                else if (state->animation >= state->total_frames - MAX_ILLUMINATED_SEGMENTS && state->animation < state->total_frames) {
+                    state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
+                }
+                else {
+                    // reset the animation state
+                    state->animate = false;
+                }
+                state->animation = (state->animation + 1);
+            } 
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            break;
+        case EVENT_BACKGROUND_TASK:
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void wyoscan_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/legacy/watch_faces/clock/wyoscan_face.h b/legacy/watch_faces/clock/wyoscan_face.h
new file mode 100644 (file)
index 0000000..ee41200
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 <#author_name#>
+ *
+ * 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 WYOSCAN_FACE_H_
+#define WYOSCAN_FACE_H_
+
+/*
+ * WYOSCAN .5 hz watchface
+ *
+ * This is a recreation of the Wyoscan watch, which was a $175 watch in 2014.
+ * It was an f-91w pcb replacement.
+ * 
+ * Video: https://user-images.githubusercontent.com/1795778/252550124-e07f0ed1-e328-4337-a654-fa1ee65d883f.mp4
+ * Background information: https://artmetropole.com/shop/11460
+ * Demo of what it looks like: https://www.o-r-g.com/apps/wyoscan
+ *
+ * 8 frames per number * 6 numbers + the trailing 16 frames = 64 frames
+ * at 32 frames per second, this is a 2-second cycle time or 0.5 Hz.
+ *
+ * It is giving me a stack overflow after about 2.5 cycles of the time display
+ * in the emulator, but it works fine on the watch.
+ *
+ * I'd like to make something for the low energy mode, but I haven't thought
+ * about how that might work, right now it just freezes in low energy mode
+ * until you press the 12-24HR button.
+ *
+ * There are no controls; it simply animates as long as the page is active.
+ *
+ */
+
+#include "movement.h"
+
+#define MAX_ILLUMINATED_SEGMENTS 16
+
+typedef struct {
+    uint32_t previous_date_time;
+    uint8_t last_battery_check;
+    uint8_t watch_face_index;
+    bool signal_enabled;
+    bool battery_low;
+    bool alarm_enabled;
+    uint8_t animation;
+    bool animate;
+    uint32_t start;
+    uint32_t end;
+    uint32_t total_frames;
+    bool colon;
+    uint8_t position, segment;
+    char *segments;
+    uint8_t x, y;
+    uint32_t time_digits[6];
+    uint32_t illuminated_segments[MAX_ILLUMINATED_SEGMENTS][2]; 
+} wyoscan_state_t;
+
+void wyoscan_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void wyoscan_face_activate(void *context);
+bool wyoscan_face_loop(movement_event_t event, void *context);
+void wyoscan_face_resign(void *context);
+movement_watch_face_advisory_t wyoscan_face_advise(void *context);
+
+#define wyoscan_face ((const watch_face_t){ \
+    wyoscan_face_setup, \
+    wyoscan_face_activate, \
+    wyoscan_face_loop, \
+    wyoscan_face_resign, \
+    NULL, \
+})
+
+#endif // WYOSCAN_FACE_H_
+
diff --git a/legacy/watch_faces/complication/activity_face.c b/legacy/watch_faces/complication/activity_face.c
new file mode 100644 (file)
index 0000000..5810749
--- /dev/null
@@ -0,0 +1,722 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * 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.
+ */
+
+/* ** TODO
+ * ===================
+ * -- Additional power-saving optimizations
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "activity_face.h"
+#include "chirpy_tx.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+// ===========================================================================
+// This part is configurable: you can edit values here to customize you activity face
+// In particular, with num_enabled_activities and enabled_activities you can choose a subset of the
+//   activities that you want to see in your watch.
+// You can also add new items to activity_names, but don't redefine or remove existing ones.
+
+// If a logged activity is shorter than this, then it won't be added to log when it ends.
+// This way scarce log slots are not taken up by aborted events that weren't real activities.
+static const uint16_t activity_min_length_sec = 60;
+
+// Supported activities. ID of activity is index in this buffer
+// W e should never change order or redefine items, only add new items when needed.
+static const char activity_names[][7] = {
+    " bIKE ",
+    "uuaLK ",
+    "  rUn ",
+    "DAnCE ",
+    " yOgA ",
+    "CrOSS ",
+    "Suuinn",
+    "ELLIP ",
+    "  gYnn",
+    "  rOuu",
+    "SOCCEr",
+    " FOOTb",
+    " bALL ",
+    "  SKI ",
+};
+
+// Currently enabled activities. This makes picking on first subface easier: why show activities you personally never do.
+static const uint8_t enabled_activities[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
+
+// Number of currently enabled activities (size of enabled_activities).
+static const uint8_t num_enabled_activities = sizeof(enabled_activities) / sizeof(uint8_t);
+
+// End configurable section
+// ===========================================================================
+
+// One logged activity
+typedef struct __attribute__((__packed__)) {
+    // Activity's start time
+    watch_date_time_t start_time;
+
+    // Total duration of activity, including time spend in paus
+    uint16_t total_sec;
+
+    // Number of seconds the activity was paused
+    uint16_t pause_sec;
+
+    // Type of activity (index in activity_names)
+    uint8_t activity_type;
+
+} activity_item_t;
+
+#define MAX_ACTIVITY_SECONDS 28800 // 8 hours = 28800 sec
+
+// Size of (fixed) buffer to log activites. Takes up x9 bytes in SRAM if face is installed.
+#define ACTIVITY_LOG_SZ 99
+
+// Number of activities in buffer.
+static uint8_t activity_log_count = 0;
+
+// Buffer with all logged activities.
+static activity_item_t activity_log_buffer[ACTIVITY_LOG_SZ];
+
+#define CHIRPY_PREFIX_LEN 2
+// First two bytes chirped out, to identify transmission as from the activity face
+static const uint8_t activity_chirpy_prefix[CHIRPY_PREFIX_LEN] = {0x27, 0x00};
+
+// The face's different UI modes (views).
+typedef enum {
+    ACTM_CHOOSE = 0,
+    ACTM_LOGGING,
+    ACTM_PAUSED,
+    ACTM_DONE,
+    ACTM_LOGSIZE,
+    ACTM_CHIRP,
+    ACTM_CHIRPING,
+    ACTM_CLEAR,
+    ACTM_CLEAR_CONFIRM,
+    ACTM_CLEAR_DONE,
+} activity_mode_t;
+
+// The full state of the activity face
+typedef struct {
+    // Current mode (which secondary face, or ongoing operation like logging)
+    activity_mode_t mode;
+
+    // Index of currently selected activity in enabled_activities
+    uint8_t type_ix;
+
+    // Used for different things depending on mode
+    // In ACTM_DONE: countdown for animation, before returning to start face
+    // In ACTM_LOGGING and ACTM_PAUSED: drives blinking colon and alternating time display
+    // In ACTM_LOGSIZE, ACTM_CLEAR: enables timeout return to choose screen
+    uint16_t counter;
+
+    // Start of currently logged activity, if any
+    watch_date_time_t start_time;
+
+    // Total seconds elapsed since logging started
+    uint16_t curr_total_sec;
+
+    // Total paused seconds in current log
+    uint16_t curr_pause_sec;
+
+    // Helps us handle 1/64 ticks during transmission; including countdown timer
+    chirpy_tick_state_t chirpy_tick_state;
+
+    // Used by chirpy encoder during transmission
+    chirpy_encoder_state_t chirpy_encoder_state;
+
+    // 0: Running normally
+    // 1: In LE mode
+    // 2: Just woke up from LE mode. Will go to 0 after ignoring ALARM_BUTTON_UP.
+    uint8_t le_state;
+
+} activity_state_t;
+
+#define ACTIVITY_BUF_SZ 14
+
+// Temp buffer used for sprintf'ing content for the display.
+char activity_buf[ACTIVITY_BUF_SZ];
+
+// Needed by _activity_get_next_byte to keep track of where we are in transmission
+uint16_t *activity_seq_pos;
+
+static void _activity_clear_buffers() {
+    // Clear activity buffer; 0xcd is good for diagnostics
+    memset(activity_log_buffer, 0xcd, ACTIVITY_LOG_SZ * sizeof(activity_item_t));
+    // Clear display buffer
+    memset(activity_buf, 0, ACTIVITY_BUF_SZ);
+}
+
+static void _activity_display_choice(activity_state_t *state);
+static void _activity_update_logging_screen(activity_state_t *state);
+static uint8_t _activity_get_next_byte(uint8_t *next_byte);
+
+void activity_face_setup(uint8_t watch_face_index, void **context_ptr) {
+    (void)watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(activity_state_t));
+        memset(*context_ptr, 0, sizeof(activity_state_t));
+        // This happens only at boot
+        _activity_clear_buffers();
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void activity_face_activate(void *context) {
+    (void)context;
+
+    // Not using this function. Calling _activity_activate from the event handler.
+    // That is what we get both when the face is shown upon navigation by MODE,
+    // and when waking from low energy state.
+}
+
+// Called from the ACTIVATE event handler in the loop
+static void _activity_activate(activity_state_t *state) {
+    // If waking from low-energy state and currently logging: update seconds values
+    // Those are not up-to-date because ticks have not been coming
+    if (state->le_state != 0 && state->mode == ACTM_LOGGING) {
+        state->le_state = 2;
+        watch_date_time_t now = watch_rtc_get_date_time();
+        uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
+        uint32_t start_timestamp = watch_utility_date_time_to_unix_time(state->start_time, 0);
+        uint32_t total_seconds = now_timestamp - start_timestamp;
+        state->curr_total_sec = total_seconds;
+        _activity_update_logging_screen(state);
+    }
+    // Regular activation: start from defaults
+    else {
+        state->le_state = 0;
+        state->mode = 0;
+        state->type_ix = 0;
+        _activity_display_choice(state);
+    }
+}
+
+static void _activity_display_choice(activity_state_t *state) {
+    watch_display_string("AC", 0);
+    // If buffer is full: We say "FULL"
+    if (activity_log_count >= ACTIVITY_LOG_SZ) {
+        watch_display_string(" FULL ", 4);
+    }
+    // Otherwise, we show currently activity
+    else {
+        uint8_t activity_ix = enabled_activities[state->type_ix];
+        const char *name = activity_names[activity_ix];
+        watch_display_string((char *)name, 4);
+    }
+}
+
+const uint8_t activity_anim_pixels[][2] = {
+    {1, 4},  // TL
+    {0, 5},  // BL
+    {0, 6},  // BOT
+    {1, 6},  // BR
+    {2, 5},  // TR
+    {2, 4},  // TOP
+    // {2, 4}, // MID
+};
+
+static void _activity_update_logging_screen(activity_state_t *state) {
+    watch_duration_t duration;
+
+    watch_display_string("AC  ", 0);
+
+    // If we're in LE state: per-minute update is special
+    if (state->le_state == 1) {
+        watch_date_time_t now = watch_rtc_get_date_time();
+        uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
+        uint32_t start_timestamp = watch_utility_date_time_to_unix_time(state->start_time, 0);
+        uint32_t total_seconds = now_timestamp - start_timestamp;
+        duration = watch_utility_seconds_to_duration(total_seconds);
+        sprintf(activity_buf, " %d%02d  ", duration.hours, duration.minutes);
+        watch_display_string(activity_buf, 4);
+        watch_set_colon();
+        watch_set_indicator(WATCH_INDICATOR_LAP);
+        watch_clear_indicator(WATCH_INDICATOR_PM);
+        watch_clear_indicator(WATCH_INDICATOR_24H);
+        return;
+    }
+
+    // Show elapsed time, or PAUSE
+    if ((state->counter % 5) < 3) {
+        watch_set_indicator(WATCH_INDICATOR_LAP);
+        watch_clear_indicator(WATCH_INDICATOR_PM);
+        watch_clear_indicator(WATCH_INDICATOR_24H);
+        if (state->mode == ACTM_PAUSED) {
+            watch_display_string(" PAUSE", 4);
+            watch_clear_colon();
+        } else {
+            duration = watch_utility_seconds_to_duration(state->curr_total_sec);
+            // Under 10 minutes: M:SS
+            if (state->curr_total_sec < 600) {
+                sprintf(activity_buf, "   %01d%02d", duration.minutes, duration.seconds);
+                watch_display_string(activity_buf, 4);
+                watch_clear_colon();
+            }
+            // Under an hour: MM:SS
+            else if (state->curr_total_sec < 3600) {
+                sprintf(activity_buf, "  %02d%02d", duration.minutes, duration.seconds);
+                watch_display_string(activity_buf, 4);
+                watch_clear_colon();
+            }
+            // Over an hour: H:MM:SS
+            // (We never go to two-digit hours; stop at 8)
+            else {
+                sprintf(activity_buf, " %d%02d%02d", duration.hours, duration.minutes, duration.seconds);
+                watch_display_string(activity_buf, 4);
+                watch_set_colon();
+            }
+        }
+    }
+    // Briefly, show time without seconds
+    else {
+        watch_clear_indicator(WATCH_INDICATOR_LAP);
+        watch_date_time_t now = watch_rtc_get_date_time();
+        uint8_t hour = now.unit.hour;
+        if (!movement_clock_mode_24h()) {
+            watch_clear_indicator(WATCH_INDICATOR_24H);
+            if (hour < 12)
+                watch_clear_indicator(WATCH_INDICATOR_PM);
+            else
+                watch_set_indicator(WATCH_INDICATOR_PM);
+            hour %= 12;
+            if (hour == 0) hour = 12;
+        }
+        else {
+            watch_set_indicator(WATCH_INDICATOR_24H);
+            watch_clear_indicator(WATCH_INDICATOR_PM);
+        }
+        sprintf(activity_buf, "%2d%02d  ", hour, now.unit.minute);
+        watch_set_colon();
+        watch_display_string(activity_buf, 4);
+    }
+}
+
+static void _activity_quit_chirping() {
+    watch_clear_indicator(WATCH_INDICATOR_BELL);
+    watch_set_buzzer_off();
+    movement_request_tick_frequency(1);
+}
+
+static void _activity_chirp_tick_transmit(void *context) {
+    activity_state_t *state = (activity_state_t *)context;
+
+    uint8_t tone = chirpy_get_next_tone(&state->chirpy_encoder_state);
+    // Transmission over?
+    if (tone == 255) {
+        _activity_quit_chirping();
+        state->mode = ACTM_CHIRP;
+        state->counter = 0;
+        watch_display_string("AC  CHIRP ", 0);
+        return;
+    }
+    uint16_t period = chirpy_get_tone_period(tone);
+    watch_set_buzzer_period_and_duty_cycle(period, 25);
+    watch_set_buzzer_on();
+}
+
+static void _activity_chirp_tick_countdown(void *context) {
+    activity_state_t *state = (activity_state_t *)context;
+
+    // Countdown over: start actual broadcast
+    if (state->chirpy_tick_state.seq_pos == 8 * 3) {
+        state->chirpy_tick_state.tick_compare = 3;
+        state->chirpy_tick_state.tick_count = 2;  // tick_compare - 1, so it starts immediately
+        state->chirpy_tick_state.seq_pos = 0;
+        state->chirpy_tick_state.tick_fun = _activity_chirp_tick_transmit;
+        return;
+    }
+    // Sound or turn off buzzer
+    if ((state->chirpy_tick_state.seq_pos % 8) == 0) {
+        watch_set_buzzer_period_and_duty_cycle(NotePeriods[BUZZER_NOTE_A5], 25);
+        watch_set_buzzer_on();
+        if (state->chirpy_tick_state.seq_pos == 0) {
+            watch_display_string(" ---  ", 4);
+        } else if (state->chirpy_tick_state.seq_pos == 8) {
+            watch_display_string(" --", 5);
+        } else if (state->chirpy_tick_state.seq_pos == 16) {
+            watch_display_string("  -", 5);
+        }
+    } else if ((state->chirpy_tick_state.seq_pos % 8) == 1) {
+        watch_set_buzzer_off();
+    }
+    ++state->chirpy_tick_state.seq_pos;
+}
+
+static uint8_t _activity_get_next_byte(uint8_t *next_byte) {
+    uint16_t num_bytes = 2 + activity_log_count * sizeof(activity_item_t);
+    uint16_t pos = *activity_seq_pos;
+
+    // Init counter
+    if (pos == 0) {
+        sprintf(activity_buf, "%3d", activity_log_count);
+        watch_display_string(activity_buf, 5);
+    }
+
+    if (pos == num_bytes) {
+        return 0;
+    }
+    // Two-byte prefix
+    if (pos < 2) {
+        (*next_byte) = activity_chirpy_prefix[pos];
+    }
+    // Data
+    else {
+        pos -= 2;
+        uint16_t ix = pos / sizeof(activity_item_t);
+        const activity_item_t *itm = &activity_log_buffer[ix];
+        uint16_t ofs = pos % sizeof(activity_item_t);
+
+        // Update counter when starting new item
+        if (ofs == 0) {
+            sprintf(activity_buf, "%3d", activity_log_count - ix);
+            watch_display_string(activity_buf, 5);
+        }
+
+        // Do this the hard way, byte by byte, to avoid high/low endedness issues
+        // Higher order bytes first, is our serialization format
+        uint8_t val;
+        // watch_date_time_t start_time;
+        // uint16_t total_sec;
+        // uint16_t pause_sec;
+        // uint8_t activity_type;
+        if (ofs == 0)
+            val = (itm->start_time.reg & 0xff000000) >> 24;
+        else if (ofs == 1)
+            val = (itm->start_time.reg & 0x00ff0000) >> 16;
+        else if (ofs == 2)
+            val = (itm->start_time.reg & 0x0000ff00) >> 8;
+        else if (ofs == 3)
+            val = (itm->start_time.reg & 0x000000ff);
+        else if (ofs == 4)
+            val = (itm->total_sec & 0xff00) >> 8;
+        else if (ofs == 5)
+            val = (itm->total_sec & 0x00ff);
+        else if (ofs == 6)
+            val = (itm->pause_sec & 0xff00) >> 8;
+        else if (ofs == 7)
+            val = (itm->pause_sec & 0x00ff);
+        else
+            val = itm->activity_type;
+        (*next_byte) = val;
+    }
+    ++(*activity_seq_pos);
+    return 1;
+}
+
+static void _activity_finish_logging(activity_state_t *state) {
+    // Save this activity
+    // If shorter than minimum for log: don't save
+    // Sanity check about buffer length. This should never happen, but also we never want to overrun by error
+    if (state->curr_total_sec >= activity_min_length_sec && activity_log_count + 1 < ACTIVITY_LOG_SZ) {
+        activity_item_t *itm = &activity_log_buffer[activity_log_count];
+        itm->start_time = state->start_time;
+        itm->total_sec = state->curr_total_sec;
+        itm->pause_sec = state->curr_pause_sec;
+        itm->activity_type = state->type_ix;
+        ++activity_log_count;
+    }
+
+    // Go to DONE animation
+    // TODO: Not in LE mode
+    state->mode = ACTM_DONE;
+    watch_clear_indicator(WATCH_INDICATOR_LAP);
+    movement_request_tick_frequency(2);
+    state->counter = 6 * 1;
+    watch_clear_display();
+    watch_display_string("AC   dONE ", 0);
+}
+
+static void _activity_handle_tick(activity_state_t *state) {
+    // Display stopwatch-like duration while logging, alternating with time
+    if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
+        ++state->counter;
+        ++state->curr_total_sec;
+        if (state->mode == ACTM_PAUSED)
+            ++state->curr_pause_sec;
+        // If we've reached max activity length: finish logging
+        if (state->curr_total_sec == MAX_ACTIVITY_SECONDS) {
+            _activity_finish_logging(state);
+        }
+        // Still logging: refresh display
+        else {
+            _activity_update_logging_screen(state);
+        }
+    }
+    // Display countown animation, and exit face when down
+    else if (state->mode == ACTM_DONE) {
+        if (state->counter == 0) {
+            movement_move_to_face(0);
+            movement_request_tick_frequency(1);
+        }
+        else {
+            uint8_t cd = state->counter % 6;
+            watch_clear_pixel(activity_anim_pixels[cd][0], activity_anim_pixels[cd][1]);
+            --state->counter;
+            cd = state->counter % 6;
+            watch_set_pixel(activity_anim_pixels[cd][0], activity_anim_pixels[cd][1]);
+        }
+    }
+    // Log size, chirp, clear: return to choose after some time
+    else if (state->mode == ACTM_LOGSIZE || state->mode == ACTM_CHIRP || state->mode == ACTM_CLEAR) {
+        ++state->counter;
+        // Leave Log Size after 20 seconds
+        // Leave Clear after only 10: this is danger zone, we don't like hanging around here
+        // Leave Chirp after 2 minutes: most likely need to the time to fiddle with mic & Chirpy RX on the computer
+        uint16_t timeout = 20;
+        if (state->mode == ACTM_CLEAR) timeout = 10;
+        else if (state->mode == ACTM_CHIRP) timeout = 120;
+        if (state->counter > timeout) {
+            state->mode = ACTM_CHOOSE;
+            _activity_display_choice(state);
+        }
+    }
+    // Chirping
+    else if (state->mode == ACTM_CHIRPING) {
+        ++state->chirpy_tick_state.tick_count;
+        if (state->chirpy_tick_state.tick_count == state->chirpy_tick_state.tick_compare) {
+            state->chirpy_tick_state.tick_count = 0;
+            state->chirpy_tick_state.tick_fun(state);
+        }
+    }
+    // Clear confirm: blink CLEAR
+    else if (state->mode == ACTM_CLEAR_CONFIRM) {
+        ++state->counter;
+        if ((state->counter % 2) == 0)
+            watch_display_string("CLEAR ", 4);
+        else
+            watch_display_string("      ", 4);
+        if (state->counter > 12) {
+            state->mode = ACTM_CHOOSE;
+            _activity_display_choice(state);
+            movement_request_tick_frequency(1);
+        }
+    }
+    // Clear done: fill up zeroes, then return to choose screen
+    else if (state->mode == ACTM_CLEAR_DONE) {
+        ++state->counter;
+        // Animation done
+        if (state->counter == 7) {
+            state->mode = ACTM_CHOOSE;
+            _activity_display_choice(state);
+            movement_request_tick_frequency(1);
+            return;
+        }
+        // Display current state of animation
+        sprintf(activity_buf, "      ");
+        uint8_t nZeros = state->counter + 1;
+        if (nZeros > 6) nZeros = 6;
+        for (uint8_t i = 0; i < nZeros; ++i) {
+            activity_buf[i] = '0';
+        }
+        watch_display_string(activity_buf, 4);
+    }
+}
+
+static void _activity_alarm_long(activity_state_t *state) {
+    // On choose face: start logging activity
+    if (state->mode == ACTM_CHOOSE) {
+        // If buffer is full: Ignore this long press
+        if (activity_log_count >= ACTIVITY_LOG_SZ)
+            return;
+        // OK, we go ahead and start logging
+        state->start_time = watch_rtc_get_date_time();
+        state->curr_total_sec = 0;
+        state->curr_pause_sec = 0;
+        state->counter = -1;
+        state->mode = ACTM_LOGGING;
+        watch_set_indicator(WATCH_INDICATOR_LAP);
+        _activity_update_logging_screen(state);
+    }
+    // If logging or paused: end logging
+    else if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
+        _activity_finish_logging(state);
+    }
+    // If chirp: kick off chirping
+    else if (state->mode == ACTM_CHIRP) {
+        // Set up our tick handling for countdown beeps
+        activity_seq_pos = &state->chirpy_tick_state.seq_pos;
+        state->chirpy_tick_state.tick_compare = 8;
+        state->chirpy_tick_state.tick_count = 7;  // tick_compare - 1, so it starts immediately
+        state->chirpy_tick_state.seq_pos = 0;
+        state->chirpy_tick_state.tick_fun = _activity_chirp_tick_countdown;
+        // Set up chirpy encoder
+        chirpy_init_encoder(&state->chirpy_encoder_state, _activity_get_next_byte);
+        // Show bell; switch to 64/sec ticks
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+        movement_request_tick_frequency(64);
+        state->mode = ACTM_CHIRPING;
+    }
+    // If clear: confirm (unless empty)
+    else if (state->mode == ACTM_CLEAR) {
+        if (activity_log_count == 0)
+            return;
+        state->mode = ACTM_CLEAR_CONFIRM;
+        state->counter = -1;
+        movement_request_tick_frequency(4);
+    }
+    // If clear confirm: do clear.
+    else if (state->mode == ACTM_CLEAR_CONFIRM) {
+        _activity_clear_buffers();
+        activity_log_count = 0;
+        state->mode = ACTM_CLEAR_DONE;
+        state->counter = -1;
+        watch_display_string("0     ", 4);
+        movement_request_tick_frequency(2);
+    }
+}
+
+static void _activity_alarm_short(activity_state_t *state) {
+    // In the choose face, short ALARM cycles through activities
+    if (state->mode == ACTM_CHOOSE) {
+        state->type_ix = (state->type_ix + 1) % num_enabled_activities;
+        _activity_display_choice(state);
+    }
+    // If logging: pause
+    else if (state->mode == ACTM_LOGGING) {
+        state->mode = ACTM_PAUSED;
+        state->counter = 0;
+        _activity_update_logging_screen(state);
+    }
+    // If paused: Update paused seconds count and return to logging
+    else if (state->mode == ACTM_PAUSED) {
+        state->mode = ACTM_LOGGING;
+        state->counter = 0;
+        _activity_update_logging_screen(state);
+    }
+    // If chirping: stoppit
+    else if (state->mode == ACTM_CHIRPING) {
+        _activity_quit_chirping();
+        state->mode = ACTM_CHIRP;
+        state->counter = 0;
+        watch_display_string("AC  CHIRP ", 0);
+    }
+}
+
+static void _activity_light_short(activity_state_t *state) {
+    // If choose face: move to log size
+    if (state->mode == ACTM_CHOOSE) {
+        state->mode = ACTM_LOGSIZE;
+        state->counter = 0;
+        sprintf(activity_buf, "AC  L#g%3d", activity_log_count);
+        watch_display_string(activity_buf, 0);
+    }
+    // If log size face: move to chirp
+    else if (state->mode == ACTM_LOGSIZE) {
+        state->mode = ACTM_CHIRP;
+        state->counter = 0;
+        watch_display_string("AC  CHIRP ", 0);
+    }
+    // If chirp face: move to clear
+    else if (state->mode == ACTM_CHIRP) {
+        state->mode = ACTM_CLEAR;
+        state->counter = 0;
+        watch_display_string("AC  CLEAR ", 0);
+    }
+    // If clear face: return to choose face
+    else if (state->mode == ACTM_CLEAR || state->mode == ACTM_CLEAR_CONFIRM) {
+        state->mode = ACTM_CHOOSE;
+        _activity_display_choice(state);
+        movement_request_tick_frequency(1);
+    }
+    // While logging or paused, light is light
+    else if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
+        movement_illuminate_led();
+    }
+    // Otherwise, we don't do light.
+}
+
+bool activity_face_loop(movement_event_t event, void *context) {
+    activity_state_t *state = (activity_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _activity_activate(state);
+            break;
+        case EVENT_TICK:
+            _activity_handle_tick(state);
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            if (state->mode != ACTM_LOGGING && state->mode != ACTM_PAUSED && state->mode != ACTM_CHIRPING) {
+                movement_request_tick_frequency(1);
+                movement_move_to_next_face();
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            _activity_light_short(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // We also receive ALARM press that woke us up from LE state
+            // Don't want to act on that as if it were a real button press for us
+            if (state->le_state != 2)
+                _activity_alarm_short(state);
+            else
+                state->le_state = 0;
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            _activity_alarm_long(state);
+            break;
+        case EVENT_TIMEOUT:
+            if (state->mode != ACTM_LOGGING && state->mode != ACTM_PAUSED &&
+                state->mode != ACTM_CHIRP && state->mode != ACTM_CHIRPING) {
+                movement_request_tick_frequency(1);
+                movement_move_to_face(0);
+            }
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            state->le_state = 1;
+            // If we're in paused logging mode: let's lose this activity. Pause is not meant for over an hour.
+            if (state->mode == ACTM_PAUSED) {
+                // When waking, face will revert to default screen
+                state->mode = ACTM_CHOOSE;
+                watch_display_string("AC  SLEEP ", 0);
+                watch_clear_colon();
+                watch_clear_indicator(WATCH_INDICATOR_LAP);
+                watch_clear_indicator(WATCH_INDICATOR_PM);
+            }
+            else {
+                _activity_update_logging_screen(state);
+                watch_start_sleep_animation(500);
+            }
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    // Return true if the watch can enter standby mode. False needed when chirping.
+    if (state->mode == ACTM_CHIRPING)
+        return false;
+    else
+        return true;
+}
+
+void activity_face_resign(void *context) {
+    (void)context;
+
+    // Face should only ever temporarily request a higher frequency, so by the time we're resigning,
+    // this should not be needed. But we don't want an error to create a situation that drains the battery.
+    // Rather do this defensively here.
+    movement_request_tick_frequency(1);
+}
diff --git a/legacy/watch_faces/complication/activity_face.h b/legacy/watch_faces/complication/activity_face.h
new file mode 100644 (file)
index 0000000..48f9ee8
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * 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 ACTIVITY_FACE_H_
+#define ACTIVITY_FACE_H_
+
+/*
+ * ACTIVITY watch face
+ *
+ * The Activity face lets you record activities like you would do with a fitness watch.
+ * It supports different activities like running, biking, rowing etc., and for each recorded activity
+ * it stores when it started and how long it was.
+ * 
+ * You can save up to 99 activities this way. Every once in a while you can chirp them out
+ * using the watch's piezo buzzer as a modem, then clear the log in the watch.
+ * To record and decode a chirpy transmission on your computer, you can use the web app here:
+ * https://jealousmarkup.xyz/off/chirpy/rx/
+ * 
+ * Using the face
+ * 
+ * When you activate the face, it starts with the first screen to select the activity you want to log.
+ * ALARM cycles through the list of activities.
+ * LONG ALARM starts logging.
+ * While logging is in progress, the face alternates between the elapsed time and the current time.
+ * You can press ALARM to pause (e.g., while you hop in to the baker's for a croissant during your jog).
+ * Pressing ALARM again resumes the activity.
+ * LONG ALARM stops logging and saves the activity.
+ * 
+ * When you're not loggin, you can press LIGHT to access the secondary faces.
+ * LIGHT #1 => Shows the size of the log (how many activities have been recorded).
+ * LIGHT #2 => The screen to chirp out the data. Press LONG ALARM to start chirping.
+ * LIGHT #3 => The screen to clear the log in the watch. Press LONG ALARM twice to clear data.
+ *
+ * Quirky details
+ * 
+ * The face will discard short activities (less than a minute) when you press LONG ALARM to finish logging.
+ * These were probably logged by mistake, and it's better to save slots and chirping battery power for
+ * stuff that really matters.
+ * 
+ * The face will continue to record an activity past the normal one-hour mark, when the watch
+ * enters low energy mode. However, it will always stop at 8 hours. If an activity takes that long,
+ * you probably just forgot to stop logging.
+ * 
+ * The log is stored in regular memory. It will be lost when you remove the battery, so make
+ * sure you chirp it out before taking the watch apart.
+ * 
+ * See the top of activity_face.c for some customization options. What you most likely want to do
+ * is reduce the list of activities shown on the first screen to the ones you are regularly doing.
+ */
+
+#include "movement.h"
+
+void activity_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void activity_face_activate(void *context);
+bool activity_face_loop(movement_event_t event, void *context);
+void activity_face_resign(void *context);
+
+#define activity_face ((const watch_face_t){ \
+    activity_face_setup, \
+    activity_face_activate, \
+    activity_face_loop, \
+    activity_face_resign, \
+    NULL, \
+})
+
+#endif // ACTIVITY_FACE_H_
+
diff --git a/legacy/watch_faces/complication/astronomy_face.c b/legacy/watch_faces/complication/astronomy_face.c
new file mode 100644 (file)
index 0000000..61df67b
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "astronomy_face.h"
+#include "watch_utility.h"
+
+#if __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+
+#define NUM_AVAILABLE_BODIES 9
+
+static const char astronomy_available_celestial_bodies[NUM_AVAILABLE_BODIES] = {
+    ASTRO_BODY_SUN,
+    ASTRO_BODY_MERCURY,
+    ASTRO_BODY_VENUS,
+    ASTRO_BODY_MOON,
+    ASTRO_BODY_MARS,
+    ASTRO_BODY_JUPITER,
+    ASTRO_BODY_SATURN,
+    ASTRO_BODY_URANUS,
+    ASTRO_BODY_NEPTUNE
+};
+
+static const char astronomy_celestial_body_names[NUM_AVAILABLE_BODIES][3] = {
+    "SO",   // Sol
+    "ME",   // Mercury
+    "VE",   // Venus
+    "LU",   // Moon (Luna)
+    "MA",   // Mars
+    "JU",   // Jupiter
+    "SA",   // Saturn
+    "UR",   // Uranus
+    "NE"    // Neptune
+};
+
+static void _astronomy_face_recalculate(astronomy_state_t *state) {
+#if __EMSCRIPTEN__
+    int16_t browser_lat = EM_ASM_INT({
+        return lat;
+    });
+    int16_t browser_lon = EM_ASM_INT({
+        return lon;
+    });
+    if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
+        movement_location_t browser_loc;
+        browser_loc.bit.latitude = browser_lat;
+        browser_loc.bit.longitude = browser_lon;
+        watch_store_backup_data(browser_loc.reg, 1);
+        double lat = (double)browser_lat / 100.0;
+        double lon = (double)browser_lon / 100.0;
+        state->latitude_radians = astro_degrees_to_radians(lat);
+        state->longitude_radians = astro_degrees_to_radians(lon);
+    }
+#endif
+
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
+    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);
+
+    astro_equatorial_coordinates_t radec_precession = astro_get_ra_dec(jd, astronomy_available_celestial_bodies[state->active_body_index], state->latitude_radians, state->longitude_radians, true);
+    printf("\nParams to convert: %f %f %f %f %f\n",
+            jd,
+            astro_radians_to_degrees(state->latitude_radians),
+            astro_radians_to_degrees(state->longitude_radians),
+            astro_radians_to_degrees(radec_precession.right_ascension),
+            astro_radians_to_degrees(radec_precession.declination));
+
+    astro_horizontal_coordinates_t horiz = astro_ra_dec_to_alt_az(jd, state->latitude_radians, state->longitude_radians, radec_precession.right_ascension, radec_precession.declination);
+    astro_equatorial_coordinates_t radec = astro_get_ra_dec(jd, astronomy_available_celestial_bodies[state->active_body_index], state->latitude_radians, state->longitude_radians, false);
+    state->altitude = astro_radians_to_degrees(horiz.altitude);
+    state->azimuth = astro_radians_to_degrees(horiz.azimuth);
+    state->right_ascension = astro_radians_to_hms(radec.right_ascension);
+    state->declination = astro_radians_to_dms(radec.declination);
+    state->distance = radec.distance;
+
+    printf("Calculated coordinates for %s on %f: \n\tRA  = %f / %2dh %2dm %2ds\n\tDec = %f / %3d° %3d' %3d\"\n\tAzi = %f\n\tAlt = %f\n\tDst = %f AU\n",
+            astronomy_celestial_body_names[state->active_body_index],
+            jd,
+            astro_radians_to_degrees(radec.right_ascension),
+            state->right_ascension.hours,
+            state->right_ascension.minutes,
+            state->right_ascension.seconds,
+            astro_radians_to_degrees(radec.declination),
+            state->declination.degrees,
+            state->declination.minutes,
+            state->declination.seconds,
+            state->altitude,
+            state->azimuth,
+            state->distance);
+}
+
+static void _astronomy_face_update(movement_event_t event, astronomy_state_t *state) {
+    char buf[16];
+    switch (state->mode) {
+        case ASTRONOMY_MODE_SELECTING_BODY:
+            watch_clear_colon();
+            watch_display_string(" Astro", 4);
+            if (event.subsecond % 2) {
+                watch_display_string((char *)astronomy_celestial_body_names[state->active_body_index], 0);
+            } else {
+                watch_display_string("  ", 0);
+            }
+            if (event.subsecond == 0) {
+                watch_display_string("  ", 2);
+                switch (state->animation_state) {
+                    case 0:
+                        watch_set_pixel(0, 7);
+                        watch_set_pixel(2, 6);
+                        break;
+                    case 1:
+                        watch_set_pixel(1, 7);
+                        watch_set_pixel(2, 9);
+                        break;
+                    case 2:
+                        watch_set_pixel(2, 7);
+                        watch_set_pixel(0, 9);
+                        break;
+                }
+                state->animation_state = (state->animation_state + 1) % 3;
+            }
+            break;
+        case ASTRONOMY_MODE_CALCULATING:
+            watch_clear_display();
+             // this takes a moment and locks the UI, flash C for "Calculating"
+            watch_start_character_blink('C', 100);
+            _astronomy_face_recalculate(state);
+            watch_stop_blink();
+            state->mode = ASTRONOMY_MODE_DISPLAYING_ALT;
+            // fall through
+        case ASTRONOMY_MODE_DISPLAYING_ALT:
+            sprintf(buf, "%saL%6d", astronomy_celestial_body_names[state->active_body_index], (int16_t)round(state->altitude * 100));
+            watch_display_string(buf, 0);
+            break;
+        case ASTRONOMY_MODE_DISPLAYING_AZI:
+            sprintf(buf, "%saZ%6d", astronomy_celestial_body_names[state->active_body_index], (int16_t)round(state->azimuth * 100));
+            watch_display_string(buf, 0);
+            break;
+        case ASTRONOMY_MODE_DISPLAYING_RA:
+            watch_set_colon();
+            sprintf(buf, "ra H%02d%02d%02d", state->right_ascension.hours, state->right_ascension.minutes, state->right_ascension.seconds);
+            watch_display_string(buf, 0);
+            break;
+        case ASTRONOMY_MODE_DISPLAYING_DEC:
+            watch_clear_colon();
+            sprintf(buf, "de %3d%2d%2d", state->declination.degrees, state->declination.minutes, state->declination.seconds);
+            watch_display_string(buf, 0);
+            break;
+        case ASTRONOMY_MODE_DISPLAYING_DIST:
+            if (state->distance >= 0.00668456) {
+                // if >= 1,000,000 kilometers (all planets), we display distance in AU.
+                sprintf(buf, "diAU%6d", (uint16_t)round(state->distance * 100));
+            } else {
+                // otherwise distance in kilometers fits in 6 digits. This mode will only happen for Luna.
+                sprintf(buf, "di K%6ld", (uint32_t)round(state->distance * 149597871.0));
+            }
+            watch_display_string(buf, 0);
+            break;
+        case ASTRONOMY_MODE_NUM_MODES:
+            // this case does not happen, but we need it to silence a warning.
+            break;
+    }
+}
+
+void astronomy_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(astronomy_state_t));
+        memset(*context_ptr, 0, sizeof(astronomy_state_t));
+    }
+}
+
+void astronomy_face_activate(void *context) {
+    astronomy_state_t *state = (astronomy_state_t *)context;
+    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
+    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
+    double lat = (double)lat_centi / 100.0;
+    double lon = (double)lon_centi / 100.0;
+    state->latitude_radians = astro_degrees_to_radians(lat);
+    state->longitude_radians = astro_degrees_to_radians(lon);
+
+    movement_request_tick_frequency(4);
+}
+
+bool astronomy_face_loop(movement_event_t event, void *context) {
+    astronomy_state_t *state = (astronomy_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            _astronomy_face_update(event, state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch (state->mode) {
+                case ASTRONOMY_MODE_SELECTING_BODY:
+                    // advance to next celestial body (move to calculations with a long press)
+                    state->active_body_index = (state->active_body_index + 1) % NUM_AVAILABLE_BODIES;
+                    break;
+                case ASTRONOMY_MODE_CALCULATING:
+                    // ignore button press during calculations
+                    break;
+                case ASTRONOMY_MODE_DISPLAYING_DIST:
+                    // at last mode, wrap around
+                    state->mode = ASTRONOMY_MODE_DISPLAYING_ALT;
+                    break;
+                default:
+                    // otherwise, advance to next mode
+                    state->mode++;
+                    break;
+            }
+            _astronomy_face_update(event, state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->mode == ASTRONOMY_MODE_SELECTING_BODY) {
+                // celestial body selected! this triggers a calculation in the update method.
+                state->mode = ASTRONOMY_MODE_CALCULATING;
+                movement_request_tick_frequency(1);
+                _astronomy_face_update(event, state);
+            } else if (state->mode != ASTRONOMY_MODE_CALCULATING) {
+                // in all modes except "doing a calculation", return to the selection screen.
+                state->mode = ASTRONOMY_MODE_SELECTING_BODY;
+                movement_request_tick_frequency(4);
+            _astronomy_face_update(event, state);
+            }
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // TODO?
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void astronomy_face_resign(void *context) {
+    astronomy_state_t *state = (astronomy_state_t *)context;
+    state->mode = ASTRONOMY_MODE_SELECTING_BODY;
+}
diff --git a/legacy/watch_faces/complication/astronomy_face.h b/legacy/watch_faces/complication/astronomy_face.h
new file mode 100644 (file)
index 0000000..e0b33f5
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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 ASTRONOMY_FACE_H_
+#define ASTRONOMY_FACE_H_
+
+/*
+ * ASTRONOMY face
+ *
+ * The Astronomy watch face is among the most complex watch faces in the
+ * Movement collection. It allows you to calculate the locations of celestial
+ * bodies in the sky, as well as distance in astronomical units (or, in the
+ * case of the Moon, distance in kilometers).
+ * 
+ * When you arrive at the Astronomy watch face, you’ll see its name (“Astro”)
+ * and an animation of two objects orbiting each other. You will also see “SO”
+ * (for Sol) flashing in the top left. The flashing letters indicate the
+ * currently selected celestial body. Short press Alarm to advance through
+ * the available celestial bodies:
+ * 
+ *     SO - Sol, the sun
+ *     ME - Mercury
+ *     VE - Venus
+ *     LU - Luna, the Earth’s moon
+ *     MA - Mars
+ *     JU - Jupiter
+ *     SA - Saturn
+ *     UR - Uranus
+ *     NE - Neptune
+ * 
+ * Once you’ve selected the celestial body whose parameters you wish to
+ * calculate, long press the Alarm button and release it. The letter “C” will
+ * flash while the calculation is performed.
+ * 
+ * When the calculation is complete, the screen will display the altitude
+ * (“aL”) of the celestial body. You can cycle through the available parameters
+ * with repeated short presses on the Alarm button:
+ * 
+ *     aL - Altitude (in degrees), the elevation over the horizon. If negative, it is below the horizon.
+ *     aZ - Azimuth (in degrees), the cardinal direction relative to true north.
+ *     rA - Right Ascension (in hours/minutes/seconds)
+ *     dE - Declination (in degrees/minutes/seconds)
+ *     di - Distance (the digits in the top right will display either aU for astronomical units, or K for kilometers)
+ * 
+ * Long press on the Alarm button to select another celestial body.
+ */
+
+#include "movement.h"
+#include "astrolib.h"
+
+typedef enum {
+    ASTRONOMY_MODE_SELECTING_BODY = 0,
+    ASTRONOMY_MODE_CALCULATING,
+    ASTRONOMY_MODE_DISPLAYING_ALT,
+    ASTRONOMY_MODE_DISPLAYING_AZI,
+    ASTRONOMY_MODE_DISPLAYING_RA,
+    ASTRONOMY_MODE_DISPLAYING_DEC,
+    ASTRONOMY_MODE_DISPLAYING_DIST,
+    ASTRONOMY_MODE_NUM_MODES
+} astronomy_mode_t;
+
+typedef struct {
+    astronomy_mode_t mode;
+    uint8_t active_body_index;
+    uint8_t animation_state;
+    double latitude_radians;    // this is the user location
+    double longitude_radians;   // but in radians
+    astro_angle_hms_t right_ascension;
+    astro_angle_dms_t declination;
+    double altitude;    // in decimal degrees
+    double azimuth;     // in decimal degrees
+    double distance;    // in AU
+} astronomy_state_t;
+
+void astronomy_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void astronomy_face_activate(void *context);
+bool astronomy_face_loop(movement_event_t event, void *context);
+void astronomy_face_resign(void *context);
+
+#define astronomy_face ((const watch_face_t){ \
+    astronomy_face_setup, \
+    astronomy_face_activate, \
+    astronomy_face_loop, \
+    astronomy_face_resign, \
+    NULL, \
+})
+
+#endif // ASTRONOMY_FACE_H_
diff --git a/legacy/watch_faces/complication/blinky_face.c b/legacy/watch_faces/complication/blinky_face.c
new file mode 100644 (file)
index 0000000..aa43466
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "blinky_face.h"
+#include "watch.h"
+
+void blinky_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(blinky_face_state_t));
+        memset(*context_ptr, 0, sizeof(blinky_face_state_t));
+    }
+}
+
+void blinky_face_activate(void *context) {
+    blinky_face_state_t *state = (blinky_face_state_t *)context;
+    state->active = false;
+}
+
+static void _blinky_face_update_lcd(blinky_face_state_t *state) {
+    char buf[11];
+    const char colors[][7] = {" red  ", " Green", " Yello"};
+    sprintf(buf, "BL %c%s", state->fast ? 'F' : 'S', colors[state->color]);
+    watch_display_string(buf, 0);
+}
+
+bool blinky_face_loop(movement_event_t event, void *context) {
+    blinky_face_state_t *state = (blinky_face_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _blinky_face_update_lcd(state);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            if (!state->active) {
+                state->color = (state->color + 1) % 3;
+                _blinky_face_update_lcd(state);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if (!state->active) {
+                state->active = true;
+                watch_clear_display();
+                movement_request_tick_frequency(state->fast ? 8 : 2);            
+            } else {
+                state->active = false;
+                watch_set_led_off();
+                _blinky_face_update_lcd(state);
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (!state->active) {
+                state->fast = !state->fast;
+                _blinky_face_update_lcd(state);
+            }
+            break;
+        case EVENT_TICK:
+            if (state->active) {
+                if (event.subsecond % 2 == 0) watch_set_led_off();
+                else if (state->color == 0) watch_set_led_red();
+                else if (state->color == 1) watch_set_led_green();
+                else watch_set_led_yellow();
+            }
+            break;
+        case EVENT_TIMEOUT:
+            if (!state->active) movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void blinky_face_resign(void *context) {
+    (void) context;
+    watch_set_led_off();
+}
diff --git a/legacy/watch_faces/complication/blinky_face.h b/legacy/watch_faces/complication/blinky_face.h
new file mode 100644 (file)
index 0000000..cf6f265
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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 BLINKY_FACE_H_
+#define BLINKY_FACE_H_
+
+/*
+ * BLINKY LIGHT face
+ *
+ * The blinky light watch face was designed as a tutorial for making a watch
+ * face in Movement, but it actually might be useful to have a blinking light
+ * in a pinch.
+ *
+ * The screen displays the name of the watch face (”BL”), as well as an S at
+ * the top right for slow blink or an F for fast blink. The bottom line selects
+ * the color: green, red or yellow. You can change the speed of the blinking
+ * light by pressing the Alarm button, and change the color with the Light
+ * button. A long press on the Alarm button starts the blinking light, and
+ * another long press stops it.
+ *
+ * Note that this will chew through your battery! The green LED uses about
+ * 450µA at full brightness, which is 45 times the normal power consumption of
+ * the watch. The red LED is an order of magnitude less efficient (4500 µA),
+ * and the yellow setting lights both LEDs, which chews through nearly
+ * 5 milliamperes. This means that one hour of yellow blinking is likely to
+ * eat up between 2 and 3 percent of the battery’s usable life!
+ *
+ * Still, if you need to signal your location to someone in a dark forest,
+ * this watch face could come in handy. Just try to use the green LED as much
+ * as you can.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    bool active;
+    bool fast;
+    uint8_t color;
+} blinky_face_state_t;
+
+void blinky_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void blinky_face_activate(void *context);
+bool blinky_face_loop(movement_event_t event, void *context);
+void blinky_face_resign(void *context);
+
+#define blinky_face ((const watch_face_t){ \
+    blinky_face_setup, \
+    blinky_face_activate, \
+    blinky_face_loop, \
+    blinky_face_resign, \
+    NULL, \
+})
+
+#endif // BLINKY_FACE_H_
diff --git a/legacy/watch_faces/complication/breathing_face.c b/legacy/watch_faces/complication/breathing_face.c
new file mode 100644 (file)
index 0000000..c9ea67a
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Bernd Plontsch
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "breathing_face.h"
+#include "watch.h"
+
+typedef struct {
+    uint8_t current_stage;
+    bool sound_on;
+} breathing_state_t;
+
+static void beep_in (void);
+static void beep_in_hold (void);
+static void beep_out (void);
+static void beep_out_hold (void);
+
+void breathing_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    // These next two lines just silence the compiler warnings associated with unused parameters.
+    // We have no use for the settings or the watch_face_index, so we make that explicit here.
+    (void) watch_face_index;
+    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
+    if (*context_ptr == NULL) {
+        // in this case, we allocate an area of memory sufficient to store the stuff we need to track.
+        *context_ptr = malloc(sizeof(breathing_state_t));
+    }
+}
+
+void breathing_face_activate(void *context) {
+    // same as above: silence the warning, we don't need to check the settings.
+    // we do however need to set some things in our context. Here we cast it to the correct type...
+    breathing_state_t *state = (breathing_state_t *)context;
+    // ...and set the initial state of our watch face.
+    state->current_stage = 0;
+    state->sound_on = true;
+}
+
+const int NOTE_LENGTH = 80;
+
+void beep_in (void) {
+        const watch_buzzer_note_t notes[] = {
+            BUZZER_NOTE_C4,
+            BUZZER_NOTE_D4,
+            BUZZER_NOTE_E4,
+        };
+        const uint16_t durations[] = {
+            NOTE_LENGTH,
+            NOTE_LENGTH,
+            NOTE_LENGTH
+        };
+        for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
+            watch_buzzer_play_note(notes[i], durations[i]);
+        }
+}
+
+void beep_in_hold (void) {
+        const watch_buzzer_note_t notes[] = {
+            BUZZER_NOTE_E4,
+            BUZZER_NOTE_REST,
+            BUZZER_NOTE_E4,
+        };
+        const uint16_t durations[] = {
+            NOTE_LENGTH,
+            NOTE_LENGTH * 2,
+            NOTE_LENGTH,
+        };
+        for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
+            watch_buzzer_play_note(notes[i], durations[i]);
+        }
+}
+
+void beep_out (void) {
+        const watch_buzzer_note_t notes[] = {
+            BUZZER_NOTE_E4,
+            BUZZER_NOTE_D4,
+            BUZZER_NOTE_C4,
+        };
+        const uint16_t durations[] = {
+            NOTE_LENGTH,
+            NOTE_LENGTH,
+            NOTE_LENGTH,
+        };
+        for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
+            watch_buzzer_play_note(notes[i], durations[i]);
+        }
+}
+
+void beep_out_hold (void) {
+        const watch_buzzer_note_t notes[] = {
+            BUZZER_NOTE_C4,
+            BUZZER_NOTE_REST * 2,
+            BUZZER_NOTE_C4,
+        };
+        const uint16_t durations[] = {
+            NOTE_LENGTH,
+            NOTE_LENGTH,
+            NOTE_LENGTH,
+        };
+        for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
+            watch_buzzer_play_note(notes[i], durations[i]);
+        }
+}
+
+bool breathing_face_loop(movement_event_t event, void *context) {
+    breathing_state_t *state = (breathing_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+
+            if (state->sound_on == true) {
+                watch_set_indicator(WATCH_INDICATOR_BELL); 
+            } else {
+                watch_clear_indicator(WATCH_INDICATOR_BELL); 
+            }
+
+            switch (state->current_stage)
+            {
+            case 0: { watch_display_string("Breath", 4); if (state->sound_on) beep_in(); } break;
+            case 1: watch_display_string("In   3", 4); break;
+            case 2: watch_display_string("In   2", 4); break;
+            case 3: watch_display_string("In   1", 4); break;
+            
+            case 4: { watch_display_string("Hold 4", 4); if (state->sound_on) beep_in_hold(); } break;
+            case 5: watch_display_string("Hold 3", 4); break;
+            case 6: watch_display_string("Hold 2", 4); break;               
+            case 7:  watch_display_string("Hold 1", 4); break;
+
+            case 8: { watch_display_string("Ou t 4", 4); if (state->sound_on) beep_out(); } break;
+            case 9: watch_display_string("Ou t 3", 4); break;
+            case 10: watch_display_string("Ou t 2", 4); break;
+            case 11: watch_display_string("Ou t 1", 4); break;         
+            
+            case 12: { watch_display_string("Hold 4", 4); if (state->sound_on) beep_out_hold(); } break;
+            case 13: watch_display_string("Hold 3", 4); break;
+            case 14: watch_display_string("Hold 2", 4); break;     
+            case 15: watch_display_string("Hold 1", 4); break;     
+            default:
+                break;
+            }
+
+            // and increment it so that it will update on the next tick.
+            state->current_stage = (state->current_stage + 1) % 16;
+
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            state->sound_on = !state->sound_on;            
+             if (state->sound_on == true) {
+                watch_set_indicator(WATCH_INDICATOR_BELL); 
+            } else {
+                watch_clear_indicator(WATCH_INDICATOR_BELL); 
+            }
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // This low energy mode update occurs once a minute, if the watch face is in the
+            // foreground when Movement enters low energy mode. We have the option of supporting
+            // this mode, but since our watch face animates once a second, the "Hello there" face
+            // isn't very useful in this mode. So we choose not to support it. (continued below)
+            break;
+        case EVENT_TIMEOUT:
+            // ... Instead, we respond to the timeout event. This event happens after a configurable
+            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
+            // resign control if we want to, and in this case, we do.
+            // This function will return the watch to the first screen (usually a simple clock),
+            // and it will do it long before the watch enters low energy mode. This ensures we
+            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
+            
+            // movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void breathing_face_resign(void *context) {
+    // our watch face, like most watch faces, has nothing special to do when resigning.
+    // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/breathing_face.h b/legacy/watch_faces/complication/breathing_face.h
new file mode 100644 (file)
index 0000000..91ef2a6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Bernd Plontsch
+ *
+ * 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 BREATHING_FACE_H_
+#define BREATHING_FACE_H_
+
+/*
+ * BOXED BREATHING face
+ *
+ * Breathing is a complication for guiding boxed breathing sessions.
+ * Boxed breathing is a technique to help you stay calm and improve
+ * concentration in stressful situations.
+ *
+ * Usage: Timed messages will cycle as long as this face is active.
+ * Press ALARM to toggle sound.
+ */
+
+#include "movement.h"
+
+void breathing_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void breathing_face_activate(void *context);
+bool breathing_face_loop(movement_event_t event, void *context);
+void breathing_face_resign(void *context);
+
+#define breathing_face ((const watch_face_t){ \
+    breathing_face_setup, \
+    breathing_face_activate, \
+    breathing_face_loop, \
+    breathing_face_resign, \
+    NULL, \
+})
+
+#endif // BREATHING_FACE_H_
diff --git a/legacy/watch_faces/complication/butterfly_game_face.c b/legacy/watch_faces/complication/butterfly_game_face.c
new file mode 100644 (file)
index 0000000..b8f5434
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Hugo Chargois
+ *
+ * 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.
+ */
+
+// Emulator only: need time() to seed the random number generator
+#if __EMSCRIPTEN__
+#include <time.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "butterfly_game_face.h"
+
+static char butterfly_shapes[][3] = {
+    "[]", "][", "25", "52", "9e", "e9", "6a", "a6", "3E", "E3", "00", "HH", "88"
+};
+
+static int8_t single_beep[] = {BUZZER_NOTE_A7, 4, 0};
+static int8_t round_win_melody[] = {
+    BUZZER_NOTE_C6, 4,
+    BUZZER_NOTE_E6, 4,
+    BUZZER_NOTE_G6, 4,
+    BUZZER_NOTE_C7, 12,
+    0};
+static int8_t round_lose_melody[] = {
+    BUZZER_NOTE_E6, 4,
+    BUZZER_NOTE_F6, 4,
+    BUZZER_NOTE_D6SHARP_E6FLAT, 4,
+    BUZZER_NOTE_C6, 12,
+    0};
+static int8_t game_win_melody[] = {
+    BUZZER_NOTE_G6, 4,
+    BUZZER_NOTE_A6, 4,
+    BUZZER_NOTE_B6, 4,
+    BUZZER_NOTE_C7, 12,
+    BUZZER_NOTE_D7, 4,
+    BUZZER_NOTE_E7, 4,
+    BUZZER_NOTE_D7, 4,
+    BUZZER_NOTE_C7, 12,
+    BUZZER_NOTE_B6, 4,
+    BUZZER_NOTE_C7, 4,
+    BUZZER_NOTE_D7, 4,
+    BUZZER_NOTE_G7, 24,
+    0};
+
+#define NUM_SHAPES (sizeof(butterfly_shapes) / sizeof(butterfly_shapes[0]))
+
+#define POS_LEFT   4
+#define POS_CENTER 6
+#define POS_RIGHT  8
+
+#define TICK_FREQ 8
+#define TICKS_PER_SHAPE 8
+
+#define PLAYER_1 0
+#define PLAYER_2 1
+
+
+// returns a random integer r with 0 <= r < max
+static inline uint8_t _get_rand(uint8_t max) {
+#if __EMSCRIPTEN__
+    return rand() % max;
+#else
+    return arc4random_uniform(max);
+#endif
+}
+
+/*
+ * The game is built with a simple state machine where each state is called a
+ * "screen". Each screen can draw on the display and handles events, including
+ * the "activate" event, which is repurposed and sent whenever we move from one
+ * screen to another via the _transition_to function. Basically it's a mini
+ * movement inside movement.
+ */
+typedef bool (*screen_fn_t)(movement_event_t, butterfly_game_state_t*);
+
+static screen_fn_t cur_screen_fn;
+
+static bool _transition_to(screen_fn_t sf, butterfly_game_state_t *state) {
+    movement_event_t ev = {EVENT_ACTIVATE, 0};
+    cur_screen_fn = sf;
+    return sf(ev, state);
+}
+
+static uint8_t _pick_wrong_shape(butterfly_game_state_t *state, bool skip_wrong_shape) {
+    if (!skip_wrong_shape) {
+        // easy case, we only need to skip over 1 shape: the correct shape
+        uint8_t r = _get_rand(NUM_SHAPES-1);
+        if (r >= state->correct_shape) {
+            r++;
+        }
+        return r;
+    } else {
+        // a bit more complex, we need to skip over 2 shapes: the correct one
+        // and the current wrong one
+        uint8_t r = _get_rand(NUM_SHAPES-2);
+        uint8_t i1, i2; // the 2 indices to skip over, with i1 < i2
+        if (state->correct_shape < state->current_shape) {
+            i1 = state->correct_shape;
+            i2 = state->current_shape;
+        } else {
+            i1 = state->current_shape;
+            i2 = state->correct_shape;
+        }
+
+        if (r >= i1) {
+            r++;
+        }
+        if (r >= i2) {
+            r++;
+        }
+
+        return r;
+    }
+}
+
+static void _display_shape(uint8_t shape, uint8_t pos) {
+    watch_display_string(butterfly_shapes[shape], pos);
+}
+
+static void _display_scores(butterfly_game_state_t *state) {
+    char buf[] = " ";
+    buf[0] = '0' + state->score_p1;
+    watch_display_string(buf, 0);
+    buf[0] = '0' + state->score_p2;
+    watch_display_string(buf, 3);
+}
+
+static void _play_sound(butterfly_game_state_t *state, int8_t *seq) {
+    if (state->sound) watch_buzzer_play_sequence(seq, NULL);
+}
+
+static bool _round_start_screen(movement_event_t event, butterfly_game_state_t *state);
+static bool _reset_screen(movement_event_t event, butterfly_game_state_t *state);
+
+static bool _game_win_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            state->ctr = 4 * TICK_FREQ;
+            watch_clear_display();
+
+            if (state->score_p1 >= state->goal_score) {
+                watch_display_string("pl1  wins", 0);
+            } else {
+                watch_display_string("pl2  wins", 0);
+            }
+            _play_sound(state, game_win_melody);
+
+            break;
+        case EVENT_TICK:
+            state->ctr--;
+            if (state->ctr == 0) {
+                return _transition_to(_reset_screen, state);
+            }
+            break;
+    }
+    return true;
+}
+
+static bool _round_win_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            state->ctr = TICK_FREQ;
+
+            if (state->round_winner == PLAYER_1) {
+                state->score_p1++;
+            } else {
+                state->score_p2++;
+            }
+
+            watch_clear_display();
+            _display_scores(state);
+            _display_shape(state->correct_shape, state->round_winner == PLAYER_1 ? POS_LEFT : POS_RIGHT);
+            _play_sound(state, round_win_melody);
+            break;
+        case EVENT_TICK:
+            state->ctr--;
+            if (state->ctr == 0) {
+                if (state->score_p1 >= state->goal_score || state->score_p2 >= state->goal_score) {
+                    return _transition_to(_game_win_screen, state);
+                }
+                return _transition_to(_round_start_screen, state);
+            }
+            break;
+    }
+    return true;
+}
+
+static bool _round_lose_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            state->ctr = TICK_FREQ;
+
+            if (state->round_winner == PLAYER_1) {
+                if (state->score_p2 > 0) state->score_p2--;
+            } else {
+                if (state->score_p1 > 0) state->score_p1--;
+            }
+
+            _display_shape(state->correct_shape, POS_CENTER);
+            _play_sound(state, round_lose_melody);
+            break;
+        case EVENT_TICK:
+            if (--state->ctr == 0) {
+                return _transition_to(_round_start_screen, state);
+            }
+            _display_shape(state->ctr%2 ? state->correct_shape : state->current_shape, POS_CENTER);
+            break;
+
+    }
+    return true;
+}
+
+static bool _correct_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _display_shape(state->correct_shape, POS_CENTER);
+            _play_sound(state, single_beep);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            state->round_winner = PLAYER_1;
+            return _transition_to(_round_win_screen, state);
+        case EVENT_ALARM_BUTTON_DOWN:
+            state->round_winner = PLAYER_2;
+            return _transition_to(_round_win_screen, state);
+    }
+    return true;
+}
+
+static bool _wrong_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            state->ctr = TICKS_PER_SHAPE;
+            state->current_shape = _pick_wrong_shape(state, true);
+            _display_shape(state->current_shape, POS_CENTER);
+            _play_sound(state, single_beep);
+            break;
+        case EVENT_TICK:
+            if (--state->ctr == 0) {
+                if (--state->show_correct_shape_after == 0) {
+                    return _transition_to(_correct_shape_screen, state);
+                }
+                return _transition_to(_wrong_shape_screen, state);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            state->round_winner = PLAYER_2;
+            return _transition_to(_round_lose_screen, state);
+        case EVENT_ALARM_BUTTON_DOWN:
+            state->round_winner = PLAYER_1;
+            return _transition_to(_round_lose_screen, state);
+    }
+    return true;
+}
+
+static bool _first_wrong_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
+    // the first of the wrong shape screens is a bit different than the next
+    // ones, for 2 reasons:
+    // * we can pick any shape except one (the correct shape); whereas in the
+    //   subsequent wrong shape screens, we also must not pick the same wrong
+    //   shape as the last
+    // * we don't act on the light/alarm button events; they would normally be
+    //   a fail in a wrong shape screen, but in this case it may just be that
+    //   the 2 players acknowledge the picked shape (in the previous screen) in
+    //   quick succession, and we don't want the second player to immediately
+    //   fail.
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            state->ctr = TICKS_PER_SHAPE;
+            state->current_shape = _pick_wrong_shape(state, false);
+            _display_shape(state->current_shape, POS_CENTER);
+            _play_sound(state, single_beep);
+            break;
+        case EVENT_TICK:
+            if (--state->ctr == 0) {
+                return _transition_to(_wrong_shape_screen, state);
+            }
+            break;
+    }
+    return true;
+}
+
+static bool _round_start_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            state->correct_shape = _get_rand(NUM_SHAPES);
+            state->show_correct_shape_after = _get_rand(10) + 1;
+            watch_display_string("    -    -", 0);
+            _display_scores(state);
+            _display_shape(state->correct_shape, POS_CENTER);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+        case EVENT_ALARM_BUTTON_DOWN:
+            watch_display_string("      ", 4);
+            return _transition_to(_first_wrong_shape_screen, state);
+    }
+    return true;
+}
+
+static bool _goal_select_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_clear_display();
+            state->goal_score = 6;
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            return _transition_to(_round_start_screen, state);
+        case EVENT_ALARM_BUTTON_DOWN:
+            state->goal_score += 3;
+            if (state->goal_score > 9) state->goal_score = 3;
+            break;
+    }
+
+    char buf[] = "GOaL  ";
+    buf[5] = '0' + state->goal_score;
+    watch_display_string(buf, 4);
+    return true;
+}
+
+static bool _reset_screen(movement_event_t event, butterfly_game_state_t *state) {
+    (void) event;
+
+    state->score_p1 = 0;
+    state->score_p2 = 0;
+
+    return _transition_to(_goal_select_screen, state);
+}
+
+static bool _continue_select_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_clear_display();
+
+            // no game in progress, start a new game
+            if (state->score_p1 == 0 && state->score_p2 == 0) {
+                return _transition_to(_goal_select_screen, state);
+            }
+
+            state->cont = false;
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            if (state->cont) {
+                return _transition_to(_round_start_screen, state);
+            }
+            return _transition_to(_reset_screen, state);
+        case EVENT_ALARM_BUTTON_DOWN:
+            state->cont = !state->cont;
+            break;
+    }
+
+    if (state->cont) {
+        watch_display_string("Cont y", 4);
+    } else {
+        watch_display_string("Cont n", 4);
+    }
+    return true;
+}
+
+static bool _sound_select_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_clear_display();
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            return _transition_to(_continue_select_screen, state);
+        case EVENT_ALARM_BUTTON_DOWN:
+            state->sound = !state->sound;
+            break;
+    }
+
+    if (state->sound) {
+        watch_display_string("snd y", 5);
+    } else {
+        watch_display_string("snd n", 5);
+    }
+    return true;
+}
+
+static bool _splash_screen(movement_event_t event, butterfly_game_state_t *state) {
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            state->ctr = TICK_FREQ;
+
+            watch_clear_display();
+            watch_display_string("Btrfly", 4);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+        case EVENT_ALARM_BUTTON_DOWN:
+            return _transition_to(_sound_select_screen, state);
+        case EVENT_TICK:
+            if (--state->ctr == 0) {
+                return _transition_to(_sound_select_screen, state);
+            }
+            break;
+    }
+    return true;
+}
+
+void butterfly_game_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(butterfly_game_state_t));
+        memset(*context_ptr, 0, sizeof(butterfly_game_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+#if __EMSCRIPTEN__
+    // simulator only: seed the random number generator
+    time_t t;
+    srand((unsigned) time(&t));
+#endif
+}
+
+void butterfly_game_face_activate(void *context) {
+    (void) context;
+
+    movement_request_tick_frequency(TICK_FREQ);
+}
+
+bool butterfly_game_face_loop(movement_event_t event, void *context) {
+    butterfly_game_state_t *state = (butterfly_game_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            return _transition_to(_splash_screen, state);
+        case EVENT_TICK:
+        case EVENT_LIGHT_BUTTON_DOWN:
+        case EVENT_ALARM_BUTTON_DOWN:
+            return (*cur_screen_fn)(event, state);
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            return true;
+        default:
+            return movement_default_loop_handler(event);
+    }
+}
+
+void butterfly_game_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/legacy/watch_faces/complication/butterfly_game_face.h b/legacy/watch_faces/complication/butterfly_game_face.h
new file mode 100644 (file)
index 0000000..5922247
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Hugo Chargois
+ *
+ * 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 BUTTERFLY_GAME_FACE_H_
+#define BUTTERFLY_GAME_FACE_H_
+
+#include "movement.h"
+
+/*
+ * BUTTERFLY
+ *
+ * A GAME OF SHAPE RECOGNITION AND QUICK REFLEXES FOR 2 PLAYERS
+ *
+ * Setup
+ * =====
+ * 
+ * The game is played by 2 players, each using a distinct button:
+ * - player 1 plays with the LIGHT (upper left) button
+ * - player 2 plays with the ALARM (lower right) button
+ * 
+ * To play, both players need a firm grip on the watch. A suggested method is to
+ * face each other, remove the watch from the wrist, and position it sideways
+ * between you. Hold one side of the strap in your preferred hand (right or
+ * left) and use your thumb to play.
+ * 
+ * Start of the game
+ * =================
+ * 
+ * After the splash screen (BtrFly) is shown, the game proceeds through a couple
+ * configuration screens. Use ALARM to cycle through the possible values, and
+ * LIGHT to validate and move to the next screen.
+ * 
+ * The configuration options are:
+ * 
+ * - snd  y/n     Toggle sound effects on or off
+ * - goal 3/6/9   Choose to play a game of 3, 6 or 9 points
+ * - cont y/n     Decide to continue an unfinished game or start a new one
+ *                (this option appears only if a game is in progress)
+ * 
+ * Rules
+ * =====
+ * 
+ * Prior to each round, a symmetrical shape composed of 2 characters is shown in
+ * the center of the screen. This shape, representing a butterfly's wings, is
+ * randomly chosen from a set of a dozen or so possible shapes. For example:
+ * 
+ *     ][
+ * 
+ * Memorize this shape! Your objective in the round will be to "catch" this 
+ * "butterfly" by pressing your button before your opponent does.
+ * 
+ * Once you believe you've memorized the shape, press your button. The round
+ * officially begins as soon as either player presses their button.
+ * 
+ * Various "butterflies" will then appear on the screen, one after the other.
+ * The fastest player to press their button when the correct butterfly is shown
+ * wins the round. However, if a player presses their button when an incorrect
+ * butterfly is shown, they immediately lose the round.
+ * 
+ * Scoring
+ * =======
+ * 
+ * The scores are displayed at the top of the screen at all times.
+ * 
+ * When a round is won by a player, their score increases by one. When a round
+ * is lost by a player, their score decreases by one; unless they have a score
+ * of 0, in which case it remains unchanged.
+ * 
+ * The game ends when a player reaches the set point goal (3, 6 or 9 points).
+ *
+ */
+
+typedef struct {
+    bool cont : 1; // continue
+    bool sound : 1;
+    uint8_t goal_score : 4;
+    
+    // a generic ctr used by multiple states to display themselves for multiple frames
+    uint8_t ctr : 6;
+
+    uint8_t correct_shape : 5;
+    uint8_t current_shape : 5;
+    uint8_t show_correct_shape_after : 5;
+    uint8_t round_winner : 1;
+
+    uint8_t score_p1 : 5;
+    uint8_t score_p2 : 5;
+} butterfly_game_state_t;
+
+void butterfly_game_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void butterfly_game_face_activate(void *context);
+bool butterfly_game_face_loop(movement_event_t event, void *context);
+void butterfly_game_face_resign(void *context);
+
+#define butterfly_game_face ((const watch_face_t){ \
+    butterfly_game_face_setup, \
+    butterfly_game_face_activate, \
+    butterfly_game_face_loop, \
+    butterfly_game_face_resign, \
+    NULL, \
+})
+
+#endif // BUTTERFLY_GAME_FACE_H_
+
diff --git a/legacy/watch_faces/complication/couch_to_5k_face.c b/legacy/watch_faces/complication/couch_to_5k_face.c
new file mode 100644 (file)
index 0000000..37efa80
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "couch_to_5k_face.h"
+
+// They go: Warmup, Run, Walk, Run, Walk, Run, Walk ... , End (0)
+// Time is defined in seconds
+// Maybe do /10 to reduce memory usage?
+// (i don't want to use floats)
+
+// uint16_t C25K_WEEK_TEST[] = {10, 10, 10, 0};
+uint16_t C25K_WEEK_1[]   = {300, 60, 90, 60, 90, 60, 90, 60, 90, 60, 90, 60,
+    90, 60, 90, 60, 90, 0};
+uint16_t C25K_WEEK_2[]   = {300, 90, 120, 90, 120, 90, 120, 90, 120, 90, 120,
+    90, 120, 0};
+uint16_t C25K_WEEK_3[]   = {300, 90, 90, 180, 180, 90, 90, 180, 180, 0};
+uint16_t C25K_WEEK_4[]   = {300, 180, 90, 300, 150, 180, 90, 300, 0};
+uint16_t C25K_WEEK_5_1[] = {300, 300, 180, 300, 180, 300, 0 };
+uint16_t C25K_WEEK_5_2[] = {300, 480, 300, 480 , 0};
+uint16_t C25K_WEEK_5_3[] = {300, 1200, 0};
+uint16_t C25K_WEEK_6_1[] = {300, 300, 180, 480, 180, 300, 0 };
+uint16_t C25K_WEEK_6_2[] = {300, 600, 180, 600 , 0};
+uint16_t C25K_WEEK_6_3[] = {300, 1500, 0};
+uint16_t C25K_WEEK_7[]   = {300, 1500, 0};
+uint16_t C25K_WEEK_8[]   = {300, 1680, 0};
+uint16_t C25K_WEEK_9[]   = {300, 1800, 0};
+
+
+#define C25K_SESSIONS_LENGTH 3*9
+uint16_t *C25K_SESSIONS[C25K_SESSIONS_LENGTH];
+
+static inline bool _finished(couch_to_5k_state_t *state){
+    return state->exercise_type == C25K_FINISHED;
+}
+static inline bool _cleared(couch_to_5k_state_t *state){
+    return state->timer == C25K_SESSIONS[state->session][0]
+        && state->exercise == 0;
+}
+static inline void _next_session(couch_to_5k_state_t *state){
+    if (++state->session >= C25K_SESSIONS_LENGTH){
+        state->session = 0;
+    }
+}
+
+static inline void _assign_exercise_type(couch_to_5k_state_t *state){
+    if (state->exercise == 0){
+        state->exercise_type = C25K_WARMUP;
+    } else if (state->exercise % 2 == 1){
+        state->exercise_type = C25K_RUN;
+    } else {
+        state->exercise_type = C25K_WALK;
+    }
+}
+
+static void _next_exercise(couch_to_5k_state_t *state){
+    state->exercise++;
+    state->timer = C25K_SESSIONS[state->session][state->exercise];
+    // If the new timer starts in zero, it's finished
+    if (state->timer == 0){
+        movement_play_alarm_beeps(7, BUZZER_NOTE_C8);
+        state->exercise_type = C25K_FINISHED;
+        return;
+    }
+    movement_play_alarm_beeps(4, BUZZER_NOTE_A7);
+    _assign_exercise_type(state);
+}
+
+static void _init_session(couch_to_5k_state_t *state){
+    state->exercise = 0; // Restart exercise counter
+    state->timer = C25K_SESSIONS[state->session][state->exercise];
+    _assign_exercise_type(state);
+}
+
+static char *_exercise_type_to_str(exercise_type_t t){
+    switch (t){
+        case C25K_WARMUP:
+            return "WU";
+        case C25K_RUN:
+            return "RU";
+        case C25K_WALK:
+            return "WA";
+        case C25K_FINISHED:
+            return "--";
+        default:
+            return "  ";
+    }
+}
+static void _display(couch_to_5k_state_t *state, char *buf){
+    // TODO only repaint needed parts
+    uint8_t seconds = state->timer % 60;
+    sprintf(buf, "%s%2d%2d%02d%02d",
+            _exercise_type_to_str(state->exercise_type),
+            (state->session + 1) % 100,
+            ((state->timer - seconds) / 60) % 100,
+            seconds,
+            (state->exercise + 1) % 100);
+    watch_display_string(buf, 0);
+}
+
+
+void couch_to_5k_face_setup(uint8_t
+                          watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(couch_to_5k_state_t));
+        memset(*context_ptr, 0, sizeof(couch_to_5k_state_t));
+        // Do any one-time tasks in here; the inside of this conditional
+        // happens only at boot.
+        // C25K_SESSIONS[0]  = C25K_WEEK_TEST;
+        C25K_SESSIONS[0]  = C25K_WEEK_1;
+        C25K_SESSIONS[1]  = C25K_WEEK_1;
+        C25K_SESSIONS[2]  = C25K_WEEK_1;
+        C25K_SESSIONS[3]  = C25K_WEEK_2;
+        C25K_SESSIONS[4]  = C25K_WEEK_2;
+        C25K_SESSIONS[5]  = C25K_WEEK_2;
+        C25K_SESSIONS[6]  = C25K_WEEK_3;
+        C25K_SESSIONS[7]  = C25K_WEEK_3;
+        C25K_SESSIONS[8]  = C25K_WEEK_3;
+        C25K_SESSIONS[9]  = C25K_WEEK_4;
+        C25K_SESSIONS[10] = C25K_WEEK_4;
+        C25K_SESSIONS[11] = C25K_WEEK_4;
+        C25K_SESSIONS[12] = C25K_WEEK_5_1;
+        C25K_SESSIONS[13] = C25K_WEEK_5_2;
+        C25K_SESSIONS[14] = C25K_WEEK_5_3;
+        C25K_SESSIONS[15] = C25K_WEEK_6_1;
+        C25K_SESSIONS[16] = C25K_WEEK_6_2;
+        C25K_SESSIONS[17] = C25K_WEEK_6_3;
+        C25K_SESSIONS[18] = C25K_WEEK_7;
+        C25K_SESSIONS[19] = C25K_WEEK_7;
+        C25K_SESSIONS[20] = C25K_WEEK_7;
+        C25K_SESSIONS[21] = C25K_WEEK_8;
+        C25K_SESSIONS[22] = C25K_WEEK_8;
+        C25K_SESSIONS[23] = C25K_WEEK_8;
+        C25K_SESSIONS[24] = C25K_WEEK_9;
+        C25K_SESSIONS[25] = C25K_WEEK_9;
+        C25K_SESSIONS[26] = C25K_WEEK_9;
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the
+    // watch wakes from deep sleep.
+}
+
+void couch_to_5k_face_activate(void *context) {
+    (void) context;
+    // Handle any tasks related to your watch face coming on screen.
+    watch_set_colon();
+}
+
+
+bool couch_to_5k_face_loop(movement_event_t event,
+                         void *context) {
+    couch_to_5k_state_t *state = (couch_to_5k_state_t *)context;
+    static char buf[11];
+    static bool paused = true;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Show your initial UI here.
+            movement_request_tick_frequency(1);
+            _init_session(state);
+            paused = true;
+            _display(state, buf);
+            break;
+        case EVENT_TICK:
+            if ( !paused && !_finished(state) ) {
+                if (state->timer == 0){
+                    _next_exercise(state);
+                } else {
+                    state->timer--;
+                }
+            }
+            _display(state, buf);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            // This is the next-exercise / reset button.
+
+            // When finished move to the next session and leave it paused
+            if ( _finished(state) ){
+                _next_session(state);
+                _init_session(state);
+                paused = true;
+                break;
+            }
+            // When paused and cleared move to next, when only paused, clear
+            if ( paused ) {
+                if ( _cleared(state) ){
+                    _next_session(state);
+                }
+                _init_session(state);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if (movement_button_should_sound()) {
+                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+            }
+            paused = !paused;
+            break;
+        case EVENT_TIMEOUT:
+            // Your watch face will receive this event after a period of
+            // inactivity. If it makes sense to resign,
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // If you did not resign in EVENT_TIMEOUT, you can use this event
+            // to update the display once a minute. Avoid displaying
+            // fast-updating values like seconds, since the display won't
+            // update again for 60 seconds. You should also consider starting
+            // the tick animation, to show the wearer that this is sleep mode:
+            // watch_start_sleep_animation(500);
+            break;
+        default:
+            // Movement's default loop handler will step in for any cases you
+            // don't handle above:
+            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or
+            // skips to the secondary watch face, if configured)
+            // You can override any of these behaviors by adding a case for
+            // these events to this switch statement.
+            return movement_default_loop_handler(event);
+    }
+
+    // return true if the watch can enter standby mode. Generally speaking, you
+    // should always return true.
+    // Exceptions:
+    //  * If you are displaying a color using the low-level watch_set_led_color
+    //  function, you should return false.
+    //  * If you are sounding the buzzer using the low-level
+    //  watch_set_buzzer_on function, you should return false.
+    // Note that if you are driving the LED or buzzer using Movement functions
+    // like movement_illuminate_led or movement_play_alarm, you can still
+    // return true. This guidance only applies to the low-level watch_
+    // functions.
+    return true;
+}
+
+void couch_to_5k_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/legacy/watch_faces/complication/couch_to_5k_face.h b/legacy/watch_faces/complication/couch_to_5k_face.h
new file mode 100644 (file)
index 0000000..73d7b8f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
+ *
+ * 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 COUCHTO5K_FACE_H_
+#define COUCHTO5K_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Couch To 5k;
+ *
+ *
+ * The program is designed to train 3 times a week. Each training is a
+ * *session*. Each of the rounds you have in the training is an *exercise*.
+ *
+ * The training goes like this:
+ * 5min warm-up walk -> Run X minutes -> Walk Y minutes -> ... -> Stop
+ *
+ * The watch face shows it like this: The weekday indicator shows if you need
+ * to Warm Up (`WU`), run (`rU`), walk (`WA`) or stop (`--`).
+ *
+ * The month-day indicator shows the session you are in (from 1 to 27).
+ *
+ * The timer shows the time you have left in the exercise and the exercise you
+ * are doing (MM:SS:ee). When an exercise finishes you are notified with an
+ * alarm. When the whole session finishes, a different tone is played for a
+ * longer period.
+ *
+ * Pressing the ALARM button pauses/resumes the clock.
+ *
+ * Pressing the LIGHT button does nothing if the timer is not paused. When it
+ * is paused it clears the current session (it restarts it to the beginning)
+ * and if it was already cleared or the current session was finished moves to
+ * the next session.
+ */
+
+typedef enum {
+    C25K_WARMUP,
+    C25K_RUN,
+    C25K_WALK,
+    C25K_FINISHED
+} exercise_type_t;
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint8_t session;
+    uint8_t exercise;
+    exercise_type_t exercise_type;
+    uint16_t timer;
+} couch_to_5k_state_t;
+
+void couch_to_5k_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void couch_to_5k_face_activate(void *context);
+bool couch_to_5k_face_loop(movement_event_t event, void *context);
+void couch_to_5k_face_resign(void *context);
+
+#define couch_to_5k_face ((const watch_face_t){ \
+    couch_to_5k_face_setup, \
+    couch_to_5k_face_activate, \
+    couch_to_5k_face_loop, \
+    couch_to_5k_face_resign, \
+    NULL, \
+})
+
+#endif // COUCHTO5K_FACE_H_
+
diff --git a/legacy/watch_faces/complication/counter_face.c b/legacy/watch_faces/complication/counter_face.c
new file mode 100644 (file)
index 0000000..3542d78
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Shogo Okamoto
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "counter_face.h"
+#include "watch.h"
+
+void counter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(counter_state_t));
+        memset(*context_ptr, 0, sizeof(counter_state_t));
+        counter_state_t *state = (counter_state_t *)*context_ptr;
+        state->beep_on = true;
+    }
+}
+
+void counter_face_activate(void *context) {
+    counter_state_t *state = (counter_state_t *)context;
+    if (state->beep_on) {
+        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    }
+}
+
+bool counter_face_loop(movement_event_t event, void *context) {
+
+    counter_state_t *state = (counter_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_UP:
+            watch_buzzer_abort_sequence(); //abort running buzzer sequence when counting fast
+            state->counter_idx++; // increment counter index
+            if (state->counter_idx>99) { //0-99
+                state->counter_idx=0;//reset counter index
+            }
+            print_counter(state);
+            if (state->beep_on) {
+                beep_counter(state);
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            watch_buzzer_abort_sequence();
+            state->beep_on = !state->beep_on;
+            if (state->beep_on) {
+                watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+            } else {
+                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->counter_idx=0; // reset counter index
+            print_counter(state);
+            break;
+        case EVENT_ACTIVATE:
+            print_counter(state);
+            break;
+        case EVENT_TIMEOUT:
+            // ignore timeout
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+// beep counter index times
+void beep_counter(counter_state_t *state) {
+    int low_count = state->counter_idx/5;
+    int high_count = state->counter_idx - low_count * 5;
+    static int8_t sound_seq[15];
+    memset(sound_seq, 0, 15);
+    int i = 0;
+    if (low_count > 0) {
+        sound_seq[i] = BUZZER_NOTE_A6;
+        i++;
+        sound_seq[i] = 3;
+        i++;
+        sound_seq[i] = BUZZER_NOTE_REST;
+        i++;
+        sound_seq[i] = 6;
+        i++;
+        if (low_count > 1) {
+            sound_seq[i] = -2;
+            i++;
+            sound_seq[i] = low_count-1;
+            i++;
+        }
+        sound_seq[i] = BUZZER_NOTE_REST;
+        i++;
+        sound_seq[i] = 6;
+        i++;
+    }
+    if (high_count > 0) {
+        sound_seq[i] = BUZZER_NOTE_B6;
+        i++;
+        sound_seq[i] = 3;
+        i++;
+        sound_seq[i] = BUZZER_NOTE_REST;
+        i++;
+        sound_seq[i] = 6;
+        i++;
+    }
+    if (high_count > 1) {
+        sound_seq[i] = -2;
+        i++;
+        sound_seq[i] = high_count-1;
+    }
+    watch_buzzer_play_sequence((int8_t *)sound_seq, NULL);
+}
+
+
+// print counter index at the center of display.
+void print_counter(counter_state_t *state) {
+    char buf[14];
+    sprintf(buf, "CO    %02d", state->counter_idx); // center of LCD display
+    watch_display_string(buf, 0);
+}
+
+void counter_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/counter_face.h b/legacy/watch_faces/complication/counter_face.h
new file mode 100644 (file)
index 0000000..2ec6812
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Shogo Okamoto
+ *
+ * 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 COUNTER_FACE_H_
+#define COUNTER_FACE_H_
+
+/*
+ * COUNTER face
+ *
+ * Counter face is designed to count the number of running laps during exercises.
+ *
+ * Usage:
+ * Short-press ALARM to increment the counter (loops at 99)
+ * Long-press ALARM to reset the counter.
+ * Long-press LIGHT to toggle sound.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint8_t counter_idx;
+    bool beep_on;
+} counter_state_t;
+
+
+void counter_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void counter_face_activate(void *context);
+bool counter_face_loop(movement_event_t event, void *context);
+void counter_face_resign(void *context);
+
+void print_counter(counter_state_t *state);
+void beep_counter(counter_state_t *state);
+
+#define counter_face ((const watch_face_t){ \
+    counter_face_setup, \
+    counter_face_activate, \
+    counter_face_loop, \
+    counter_face_resign, \
+    NULL, \
+})
+
+#endif // COUNTER_FACE_H_
diff --git a/legacy/watch_faces/complication/databank_face.c b/legacy/watch_faces/complication/databank_face.c
new file mode 100644 (file)
index 0000000..34cc4b4
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Mikhail Svarichevsky
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "databank_face.h"
+#include "watch.h"
+#include "watch_private_display.h"
+
+const char *pi_data[] = {
+    "PI", "314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442",
+    "S ", "9192631770",
+    "31", "2147483648",
+    "32", "4294967296",
+    "63", "9223372036854775808",
+    "64", "18446744073709551616",
+};
+//we show 6 characters per screen
+
+const int databank_num_pages = (sizeof(pi_data) / sizeof(char*) / 2);
+
+struct {
+    uint8_t current_word;
+    uint8_t databank_page;
+    bool animating;
+} databank_state;
+
+void databank_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    // These next two lines just silence the compiler warnings associated with unused parameters.
+    // We have no use for the settings or the watch_face_index, so we make that explicit here.
+    (void) context_ptr;
+    (void) watch_face_index;
+    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
+}
+
+void databank_face_activate(void *context) {
+    // same as above: silence the warning, we don't need to check the settings.
+    (void) context;
+    // we do however need to set some things in our context. Here we cast it to the correct type...
+    databank_state.current_word = 0;
+    databank_state.animating = true;
+}
+
+static void display()
+{
+    char buf[14];
+    int page = databank_state.current_word;
+    sprintf(buf, "%s%2d", pi_data[databank_state.databank_page * 2 + 0], page);
+    watch_display_string(buf, 0);
+    bool data_ended = false;
+    for (int i = 0; i < 6; i++) {
+        if (pi_data[databank_state.databank_page * 2 + 1][page * 6 + i] == 0) {
+            data_ended = true;
+        }
+
+        if (!data_ended) {
+            watch_display_character(pi_data[databank_state.databank_page * 2 + 1][page * 6 + i], 4 + i);
+        } else {
+            // only 6 digits per page
+            watch_display_character(' ', 4 + i);
+        }
+    }
+}
+
+bool databank_face_loop(movement_event_t event, void *context) {
+    (void) context;
+    int max_words = (strlen(pi_data[databank_state.databank_page * 2 + 1]) - 1) / 6 + 1;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+             display();
+        case EVENT_TICK:
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            databank_state.current_word = (databank_state.current_word + max_words - 1) % max_words;
+            display();
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            databank_state.databank_page = (databank_state.databank_page + databank_num_pages - 1) % databank_num_pages;
+            databank_state.current_word = 0;
+            display();
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            databank_state.databank_page = (databank_state.databank_page + 1) % databank_num_pages;
+            databank_state.current_word = 0;
+            display();
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            databank_state.current_word = (databank_state.current_word + 1) % max_words;
+            display();
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // This low energy mode update occurs once a minute, if the watch face is in the
+            // foreground when Movement enters low energy mode. We have the option of supporting
+            // this mode, but since our watch face animates once a second, the "Hello there" face
+            // isn't very useful in this mode. So we choose not to support it. (continued below)
+            break;
+        case EVENT_TIMEOUT:
+            // ... Instead, we respond to the timeout event. This event happens after a configurable
+            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
+            // resign control if we want to, and in this case, we do.
+            // This function will return the watch to the first screen (usually a simple clock),
+            // and it will do it long before the watch enters low energy mode. This ensures we
+            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
+            movement_move_to_face(0);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void databank_face_resign(void *context) {
+    // our watch face, like most watch faces, has nothing special to do when resigning.
+    // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/databank_face.h b/legacy/watch_faces/complication/databank_face.h
new file mode 100644 (file)
index 0000000..a9a4a32
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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 DATABANK_FACE_H_
+#define DATABANK_FACE_H_
+
+/*
+ * DATABANK face
+ *
+ * Displays some pre-defined data that you might want to remember
+ * Math constants, birthdays, phone numbers...
+ *
+ * Usage: Edit the global variable `pi_data` in "databank_face.c"
+ * to the define the data that will be displayed. Each "item" contains
+ * a two-letter label (using the day-of-week display), then a longer
+ * string that will be displayed one "word" (six characters) at a time.
+ *
+ * Short-press ALARM to display the next word.
+ * Short-press LIGHT to display the previous word.
+ * Long-press ALARM to display the next item.
+ * Long-press LIGHT to display the previous item.
+ */
+
+#include "movement.h"
+
+void databank_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void databank_face_activate(void *context);
+bool databank_face_loop(movement_event_t event, void *context);
+void databank_face_resign(void *context);
+
+#define databank_face ((const watch_face_t){ \
+    databank_face_setup, \
+    databank_face_activate, \
+    databank_face_loop, \
+    databank_face_resign, \
+    NULL, \
+})
+
+#endif // DATABANK_FACE_H_
diff --git a/legacy/watch_faces/complication/day_one_face.c b/legacy/watch_faces/complication/day_one_face.c
new file mode 100644 (file)
index 0000000..2315233
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "day_one_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+static uint32_t _day_one_face_juliandaynum(uint16_t year, uint16_t month, uint16_t day) {
+    // from here: https://en.wikipedia.org/wiki/Julian_day#Julian_day_number_calculation
+    return (1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100))/4 + day - 32075;
+}
+
+static void _day_one_face_update(day_one_state_t *state) {
+    char buf[15];
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    uint32_t julian_date = _day_one_face_juliandaynum(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day);
+    uint32_t julian_birthdate = _day_one_face_juliandaynum(state->birth_year, state->birth_month, state->birth_day);
+    if (julian_date < julian_birthdate) {
+        sprintf(buf, "DA  %6lu", julian_birthdate - julian_date);
+    } else {
+        sprintf(buf, "DA  %6lu", julian_date - julian_birthdate);
+    }
+    watch_display_string(buf, 0);
+}
+
+static void _day_one_face_abort_quick_cycle(day_one_state_t *state) {
+    if (state->quick_cycle) {
+        state->quick_cycle = false;
+        movement_request_tick_frequency(4);
+    }
+}
+
+static void _day_one_face_increment(day_one_state_t *state) {
+    state->birthday_changed = true;
+    switch (state->current_page) {
+        case PAGE_YEAR:
+            state->birth_year = state->birth_year + 1;
+            if (state->birth_year > 2080) state->birth_year = 1900;
+            break;
+        case PAGE_MONTH:
+            state->birth_month = (state->birth_month % 12) + 1;
+            break;
+        case PAGE_DAY:
+            state->birth_day = (state->birth_day % watch_utility_days_in_month(state->birth_month, state->birth_year)) + 1;
+            break;
+        default:
+            break;
+    }
+}
+
+void day_one_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(day_one_state_t));
+        memset(*context_ptr, 0, sizeof(day_one_state_t));
+        movement_birthdate_t movement_birthdate = (movement_birthdate_t) watch_get_backup_data(2);
+        if (movement_birthdate.reg == 0) {
+            // if birth date is totally blank, set a reasonable starting date. this works well for anyone under 63, but
+            // you can keep pressing to go back to 1900; just pass the year 2080. also picked this date because if you
+            // set it to 1959-01-02, it counts up from the launch of Luna-1, the first spacecraft to leave the well.
+            movement_birthdate.bit.year = 1959;
+            movement_birthdate.bit.month = 1;
+            movement_birthdate.bit.day = 1;
+            watch_store_backup_data(movement_birthdate.reg, 2);
+        }
+    }
+}
+
+void day_one_face_activate(void *context) {
+    day_one_state_t *state = (day_one_state_t *)context;
+
+    state->current_page = PAGE_DISPLAY;
+    state->quick_cycle = false;
+    state->ticks = 0;
+
+    // fetch the user's birth date from the birthday register.
+    movement_birthdate_t movement_birthdate = (movement_birthdate_t) watch_get_backup_data(2);
+    state->birth_year = movement_birthdate.bit.year;
+    state->birth_month = movement_birthdate.bit.month;
+    state->birth_day = movement_birthdate.bit.day;
+}
+
+bool day_one_face_loop(movement_event_t event, void *context) {
+    day_one_state_t *state = (day_one_state_t *)context;
+
+    char buf[9];
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _day_one_face_update(state);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+        case EVENT_TICK:
+            if (state->quick_cycle) {
+                if (HAL_GPIO_BTN_ALARM_read()) {
+                    _day_one_face_increment(state);
+                } else {
+                    _day_one_face_abort_quick_cycle(state);
+                }
+            }
+            switch (state->current_page) {
+                // if in settings mode, update whatever the current page is
+                case PAGE_YEAR:
+                    watch_display_string("YR        ", 0);
+                    if (event.subsecond % 2) {
+                        sprintf(buf, "%4d", state->birth_year);
+                        watch_display_string(buf, 4);
+                    }
+                    break;
+                case PAGE_MONTH:
+                    watch_display_string("MO        ", 0);
+                    if (event.subsecond % 2) {
+                        sprintf(buf, "%2d", state->birth_month);
+                        watch_display_string(buf, 4);
+                    }
+                    break;
+                case PAGE_DAY:
+                    watch_display_string("DA        ", 0);
+                    if (event.subsecond % 2) {
+                        sprintf(buf, "%2d", state->birth_day);
+                        watch_display_string(buf, 6);
+                    }
+                    break;
+                // otherwise, check if we have to update. the display only needs to change at midnight!
+                case PAGE_DISPLAY: {
+                    watch_date_time_t date_time = watch_rtc_get_date_time();
+                    if (date_time.unit.hour == 0 &&  date_time.unit.minute == 0 && date_time.unit.second == 0) {
+                        _day_one_face_update(state);
+                    }
+                    break;}
+                case PAGE_DATE:
+                    if (state->ticks > 0) {
+                        state->ticks--;
+                    } else {
+                        state->current_page = PAGE_DISPLAY;
+                        _day_one_face_update(state);
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // only illuminate if we're in display mode
+            switch (state->current_page) {
+                case PAGE_DISPLAY:
+                    // fall through
+                case PAGE_DATE:
+                    movement_illuminate_led();
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            // otherwise use the light button to advance settings pages.
+            switch (state->current_page) {
+                case PAGE_YEAR:
+                    // fall through
+                case PAGE_MONTH:
+                    // fall through
+                case PAGE_DAY:
+                    // go to next setting page...
+                    state->current_page = (state->current_page + 1) % 4;
+                    if (state->current_page == PAGE_DISPLAY) {
+                        // ...unless we've been pushed back to display mode.
+                        movement_request_tick_frequency(1);
+                        // force display since it normally won't update til midnight.
+                        _day_one_face_update(state);
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // if we are on a settings page, increment whatever value we're setting.
+            switch (state->current_page) {
+                case PAGE_YEAR:
+                    // fall through
+                case PAGE_MONTH:
+                    // fall through
+                case PAGE_DAY:
+                    _day_one_face_abort_quick_cycle(state);
+                    _day_one_face_increment(state);
+                    break;
+                case PAGE_DISPLAY:
+                    state->current_page = PAGE_DATE;
+                    sprintf(buf, "%04d%02d%02d", state->birth_year % 10000, state->birth_month % 100, state->birth_day % 100);
+                    watch_display_string(buf, 2);
+                    state->ticks = 2;
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            // if we aren't already in settings mode, put us there.
+            switch (state->current_page) {
+                case PAGE_DISPLAY:
+                    state->current_page++;
+                    movement_request_tick_frequency(4);
+                    break;
+                case PAGE_YEAR:
+                    // fall through
+                case PAGE_MONTH:
+                    // fall through
+                case PAGE_DAY:
+                    state->quick_cycle = true;
+                    movement_request_tick_frequency(8);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_ALARM_LONG_UP:
+            _day_one_face_abort_quick_cycle(state);
+            break;
+        case EVENT_TIMEOUT:
+            _day_one_face_abort_quick_cycle(state);
+            // return home if we're on a settings page (this saves our changes when we resign).
+            if (state->current_page != PAGE_DISPLAY) {
+                movement_move_to_face(0);
+            }
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void day_one_face_resign(void *context) {
+    day_one_state_t *state = (day_one_state_t *)context;
+
+    // if the user changed their birth date, store it to the birth date register
+    if (state->birthday_changed) {
+        day_one_state_t *state = (day_one_state_t *)context;
+        movement_birthdate_t movement_birthdate = (movement_birthdate_t) watch_get_backup_data(2);
+        movement_birthdate.bit.year = state->birth_year;
+        movement_birthdate.bit.month = state->birth_month;
+        movement_birthdate.bit.day = state->birth_day;
+        watch_store_backup_data(movement_birthdate.reg, 2);
+        state->birthday_changed = false;
+    }
+}
diff --git a/legacy/watch_faces/complication/day_one_face.h b/legacy/watch_faces/complication/day_one_face.h
new file mode 100644 (file)
index 0000000..ab6a3ce
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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 DAY_ONE_FACE_H_
+#define DAY_ONE_FACE_H_
+
+/*
+ * DAY ONE face
+ *
+ * This watch face displays the number of days since or until a given date.
+ * It was originally designed to display the number of days you’ve been alive,
+ * but technically it can count up from any date in the 20th century or the
+ * 21st century, so far.
+ *
+ * Long press on the Alarm button to enter customization mode. The text “YR”
+ * will appear, and will allow you to set the year starting from 1959. Press
+ * Alarm repeatedly to advance the year. If your birthday is before 1959,
+ * advance beyond the current year and it will wrap around to 1900.
+ *
+ * Once you have set the year, press Light to set the month (“MO”) and
+ * day (“DA”), advancing the value by pressing Alarm repeatedly.
+ *
+ * Note that at this time, the Day One face does not display the sleep
+ * indicator in sleep mode, which may make the watch appear to be
+ * unresponsive in sleep mode. You can still press the Alarm button to
+ * wake the watch. This UI quirk will be addressed in a future update.
+ */
+
+#include "movement.h"
+
+typedef enum {
+    PAGE_DISPLAY,
+    PAGE_YEAR,
+    PAGE_MONTH,
+    PAGE_DAY,
+    PAGE_DATE
+} day_one_page_t;
+
+typedef struct {
+    day_one_page_t current_page;
+    uint16_t birth_year;
+    uint8_t birth_month;
+    uint8_t birth_day;
+    bool birthday_changed;
+    bool quick_cycle;
+    uint8_t ticks;
+} day_one_state_t;
+
+void day_one_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void day_one_face_activate(void *context);
+bool day_one_face_loop(movement_event_t event, void *context);
+void day_one_face_resign(void *context);
+
+#define day_one_face ((const watch_face_t){ \
+    day_one_face_setup, \
+    day_one_face_activate, \
+    day_one_face_loop, \
+    day_one_face_resign, \
+    NULL, \
+})
+
+#endif // DAY_ONE_FACE_H_
diff --git a/legacy/watch_faces/complication/deadline_face.c b/legacy/watch_faces/complication/deadline_face.c
new file mode 100644 (file)
index 0000000..66d6378
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023-2024 Konrad Rieck
+ *
+ * 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.
+ */
+
+/*
+ * # Deadline Face
+ * 
+ * This is a watch face for tracking deadlines.  It draws inspiration from
+ * other watch faces of the project but focuses on keeping track of
+ * deadlines.  You can enter and monitor up to four different deadlines by
+ * providing their respective date and time.  The face has two modes:
+ * *running mode* and *settings mode*.
+ *
+ * ## Running Mode
+ * 
+ * When the watch face is activated, it defaults to running mode.  The top
+ * right corner shows the current deadline number, and the main display
+ * presents the time left until the deadline.  The format of the display
+ * varies depending on the remaining time.
+ *
+ * - When less than a day is left, the display shows the remaining hours,
+ *   minutes, and seconds in the form `HH:MM:SS`.
+ * 
+ * - When less than a month is left, the display shows the remaining days
+ *   and hours in the form `DD:HH` with the unit `dy` for days.
+ * 
+ * - When less than a year is left, the display shows the remaining months
+ *   and days in the form `MM:DD` with the unit `mo` for months.
+ * 
+ * - When more than a year is left, the years and months are displayed in
+ *   the form `YY:MM` with the unit `yr` for years.
+ * 
+ * - When a deadline has passed in the last 24 hours, the display shows
+ *   `over` to indicate that the deadline has just recently been reached.
+ * 
+ * - When no deadline is set for a particular slot, or if a deadline has
+ *   already passed by more than 24 hours, `--:--` is displayed.
+ * 
+ * The user can navigate in running mode using the following buttons:
+ * 
+ * - The *alarm button* moves the next deadline.  There are currently four
+ *   slots available for deadlines.  When the last slot has been reached,
+ *   pressing the button moves to the first slot.
+ *
+ * - A *long press* on the *alarm button* activates settings mode and
+ *   enables configuring the currently selected deadline.
+ * 
+ * - A *long press* on the *light button* activates a deadline alarm.  The 
+ *   bell icon is displayed, and the alarm will ring upon reaching any of
+ *   the deadlines set.  It is important to note that the watch will not 
+ *   enter low-energy sleep mode while the alarm is enabled.
+ *
+ *
+ * ## Settings Mode
+ * 
+ * In settings mode, the currently selected slot for a deadline can be
+ * configured by providing the date and the time.  Like running mode, the
+ * top right corner of the display indicates the current deadline number. 
+ * The main display shows the date and, on the next page, the time to be
+ * configured.
+ *
+ * The user can use the following buttons in settings mode.
+ *
+ * - The *light button* navigates through the different date and time
+ *   settings, going from year, month, day, hour, to minute.  The selected
+ *   position is blinking.
+ *
+ * - A *long press* on the light button resets the date and time to the next
+ *   day at midnight.  This is the default deadline.
+ *
+ * - The *alarm button* increments the currently selected position.  A *long
+ *   press* on the *alarm button* changes the value faster.
+ *
+ * - The *mode button* exists setting mode and returns to *running mode*. 
+ *   Here the selected deadline slot can be changed.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "deadline_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+#define SETTINGS_NUM (5)
+const char settings_titles[SETTINGS_NUM][3] = { "YR", "MO", "DA", "HR", "M1" };
+
+/* Local functions */
+static void _running_init(deadline_state_t *state);
+static bool _running_loop(movement_event_t event, void *context);
+static void _running_display(movement_event_t event, deadline_state_t *state);
+static void _setting_init(deadline_state_t *state);
+static bool _setting_loop(movement_event_t event, void *context);
+static void _setting_display(movement_event_t event, deadline_state_t *state, watch_date_time_t date);
+
+/* Utility functions */
+static void _background_alarm_play(deadline_state_t *state);
+static void _background_alarm_schedule(deadline_state_t *state);
+static void _background_alarm_cancel(deadline_state_t *state);
+static void _increment_date(deadline_state_t *state, watch_date_time_t date_time);
+static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state);
+static inline bool _is_leap(int16_t y);
+static inline int _days_in_month(int16_t mpnth, int16_t y);
+static inline unsigned int _mod(int a, int b);
+static inline void _beep_button();
+static inline void _beep_enable();
+static inline void _beep_disable();
+static inline void _reset_deadline(deadline_state_t *state);
+
+/* Check for leap year */
+static inline bool _is_leap(int16_t y)
+{
+    y += 1900;
+    return !(y % 4) && ((y % 100) || !(y % 400));
+}
+
+/* Modulo function */
+static inline unsigned int _mod(int a, int b)
+{
+    int r = a % b;
+    return r < 0 ? r + b : r;
+}
+
+/* Return days in month */
+static inline int _days_in_month(int16_t month, int16_t year)
+{
+    uint8_t days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+    month = _mod(month - 1, 12);
+
+    if (month == 1 && _is_leap(year)) {
+        return days[month] + 1;
+    } else {
+        return days[month];
+    }
+}
+
+/* Beep for a button press*/
+static inline void _beep_button()
+{
+    // Play a beep as confirmation for a button press (if applicable)
+    if (!movement_button_should_sound())
+        return;
+
+    watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+}
+
+/* Beep for entering settings */
+static inline void _beep_enable()
+{
+    if (!movement_button_should_sound())
+        return;
+
+    watch_buzzer_play_note(BUZZER_NOTE_G7, 50);
+    watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
+    watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
+}
+
+/* Beep for leaving settings */
+static inline void _beep_disable()
+{
+    if (!movement_button_should_sound())
+        return;
+
+    watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+    watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
+    watch_buzzer_play_note(BUZZER_NOTE_G7, 75);
+}
+
+/* Change tick frequency */
+static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state)
+{
+    if (state->tick_freq != freq) {
+        movement_request_tick_frequency(freq);
+        state->tick_freq = freq;
+    }
+}
+
+/* Determine index of closest deadline */
+static uint8_t _closest_deadline(deadline_state_t *state)
+{
+    watch_date_time_t now = watch_rtc_get_date_time();
+    uint32_t now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
+    uint32_t min_ts = UINT32_MAX;
+    uint8_t min_index = 0;
+
+    for (uint8_t i = 0; i < DEADLINE_FACE_DATES; i++) {
+        if (state->deadlines[i] < now_ts || state->deadlines[i] > min_ts)
+            continue;
+        min_ts = state->deadlines[i];
+        min_index = i;
+    }
+
+    return min_index;
+}
+
+/* Play background alarm */
+static void _background_alarm_play(deadline_state_t *state)
+{
+
+    /* Use the default alarm from movement and move to foreground */
+    if (state->alarm_enabled) {
+        movement_play_alarm();
+        movement_move_to_face(state->face_idx);
+    }
+}
+
+/* Schedule background alarm */
+static void _background_alarm_schedule(deadline_state_t *state)
+{
+    /* We simply re-use the scheduling in the background task */
+    deadline_face_advise(state);
+}
+
+/* Cancel background alarm */
+static void _background_alarm_cancel(deadline_state_t *state)
+{
+
+    movement_cancel_background_task_for_face(state->face_idx);
+}
+
+/* Reset deadline to tomorrow */
+static inline void _reset_deadline(deadline_state_t *state)
+{
+    /* Get current time and reset hours/minutes/seconds */
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    date_time.unit.second = 0;
+    date_time.unit.minute = 0;
+    date_time.unit.hour = 0;
+
+    /* Add 24 hours to obtain first second of tomorrow */
+    uint32_t ts = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
+    ts += 24 * 60 * 60;
+
+    state->deadlines[state->current_index] = ts;
+}
+
+/* Increment date in settings mode. Function taken from `set_time_face.c` */
+static void _increment_date(deadline_state_t *state, watch_date_time_t date_time)
+{
+    const uint8_t days_in_month[12] = { 31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31 };
+
+    switch (state->current_page) {
+        case 0:
+            /* Only 10 years covered. Fix this sometime next decade */
+            date_time.unit.year = ((date_time.unit.year % 10) + 1);
+            break;
+        case 1:
+            date_time.unit.month = (date_time.unit.month % 12) + 1;
+            break;
+        case 2:
+            date_time.unit.day = (date_time.unit.day % watch_utility_days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR)) + 1;
+            break;
+        case 3:
+            date_time.unit.hour = (date_time.unit.hour + 1) % 24;
+            break;
+        case 4:
+            date_time.unit.minute = (date_time.unit.minute + 1) % 60;
+            break;
+    }
+
+    uint32_t ts = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
+    state->deadlines[state->current_index] = ts;
+}
+
+/* Update display in running mode */
+static void _running_display(movement_event_t event, deadline_state_t *state)
+{
+    (void) event;
+
+    /* Seconds, minutes, hours, days, months, years */
+    int16_t unit[] = { 0, 0, 0, 0, 0, 0 };
+    uint8_t i, range[] = { 60, 60, 24, 30, 12, 0 };
+    char buf[16];
+
+    /* Display indicators */
+    if (state->alarm_enabled)
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    else
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+    watch_date_time_t now = watch_rtc_get_date_time();
+    uint32_t now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
+
+    /* Deadline expired */
+    if (state->deadlines[state->current_index] < now_ts) {
+        if (state->deadlines[state->current_index] + 24 * 60 * 60 > now_ts)
+            sprintf(buf, "DL%2dOVER  ", state->current_index + 1);
+        else
+            sprintf(buf, "DL%2d----  ", state->current_index + 1);
+
+        //watch_clear_indicator(WATCH_INDICATOR_BELL);
+        watch_display_string(buf, 0);
+        return;
+    }
+
+    /* Get date time structs */
+    watch_date_time_t deadline = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], movement_get_current_timezone_offset()
+        );
+
+    /* Calculate naive difference of dates */
+    unit[0] = deadline.unit.second - now.unit.second;
+    unit[1] = deadline.unit.minute - now.unit.minute;
+    unit[2] = deadline.unit.hour - now.unit.hour;
+    unit[3] = deadline.unit.day - now.unit.day;
+    unit[4] = deadline.unit.month - now.unit.month;
+    unit[5] = deadline.unit.year - now.unit.year;
+
+    /* Correct errors of naive difference */
+    for (i = 0; i < 6; i++) {
+        if (unit[i] < 0) {
+            /* Correct remaining units */
+            if (i == 3)
+                unit[i] += _days_in_month(deadline.unit.month - 1, deadline.unit.year);
+            else
+                unit[i] += range[i];
+
+            /* Carry over change to next unit if non-zero */
+            if (i < 5 && unit[i + 1] != 0)
+                unit[i + 1] -= 1;
+        }
+    }
+
+    /* Set range */
+    i = state->current_index + 1;
+    if (unit[5] > 0) {
+        /* years:months */
+        sprintf(buf, "DL%2d%02d%02dYR", i, unit[5] % 100, unit[4] % 12);
+    } else if (unit[4] > 0) {
+        /* months:days */
+        sprintf(buf, "DL%2d%02d%02dMO", i, (unit[5] * 12 + unit[4]) % 100, unit[3] % 32);
+    } else if (unit[3] > 0) {
+        /* days:hours */
+        sprintf(buf, "DL%2d%02d%02ddY", i, unit[3] % 32, unit[2] % 24);
+    } else {
+        /* hours:minutes:seconds */
+        sprintf(buf, "DL%2d%02d%02d%02d", i, unit[2] % 24, unit[1] % 60, unit[0] % 60);
+    }
+    watch_display_string(buf, 0);
+}
+
+/* Init running mode */
+static void _running_init(deadline_state_t *state)
+{
+    (void) state;
+
+    watch_clear_indicator(WATCH_INDICATOR_24H);
+    watch_clear_indicator(WATCH_INDICATOR_PM);
+    watch_set_colon();
+
+    /* Ensure 1Hz updates only */
+    _change_tick_freq(1, state);
+}
+
+/* Loop of running mode */
+static bool _running_loop(movement_event_t event, void *context)
+{
+    deadline_state_t *state = (deadline_state_t *) context;
+
+    if (event.event_type != EVENT_BACKGROUND_TASK)
+        _running_display(event, state);
+
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_UP:
+            _beep_button();
+            state->current_index = (state->current_index + 1) % DEADLINE_FACE_DATES;
+            _running_display(event, state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            _beep_enable();
+            _setting_init(state);
+            state->mode = DEADLINE_FACE_SETTING;
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            movement_move_to_next_face();
+            return false;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            _beep_button();
+            state->alarm_enabled = !state->alarm_enabled;
+            if (state->alarm_enabled) {
+                _background_alarm_schedule(settings, context);
+            } else {
+                _background_alarm_cancel(settings, context);
+            }
+            _running_display(event, state);
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            _background_alarm_play(state);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+/* Update display in settings mode */
+static void _setting_display(movement_event_t event, deadline_state_t *state, watch_date_time_t date_time)
+{
+    char buf[11];
+
+    int i = state->current_index + 1;
+    if (state->current_page > 2) {
+        watch_set_colon();
+        if (movement_clock_mode_24h()) {
+            watch_set_indicator(WATCH_INDICATOR_24H);
+            sprintf(buf, "%s%2d%2d%02d  ", settings_titles[state->current_page], i, date_time.unit.hour, date_time.unit.minute);
+        } else {
+            sprintf(buf, "%s%2d%2d%02d  ", settings_titles[state->current_page], i, (date_time.unit.hour % 12) ? (date_time.unit.hour % 12) : 12,
+                    date_time.unit.minute);
+            if (date_time.unit.hour < 12)
+                watch_clear_indicator(WATCH_INDICATOR_PM);
+            else
+                watch_set_indicator(WATCH_INDICATOR_PM);
+        }
+    } else {
+        watch_clear_colon();
+        watch_clear_indicator(WATCH_INDICATOR_24H);
+        watch_clear_indicator(WATCH_INDICATOR_PM);
+        sprintf(buf, "%s%2d%2d%02d%02d", settings_titles[state->current_page], i, date_time.unit.year + 20, date_time.unit.month, date_time.unit.day);
+    }
+
+    /* Blink up the parameter we are setting */
+    if (event.subsecond % 2) {
+        switch (state->current_page) {
+            case 0:
+            case 3:
+                buf[4] = buf[5] = ' ';
+                break;
+            case 1:
+            case 4:
+                buf[6] = buf[7] = ' ';
+                break;
+            case 2:
+                buf[8] = buf[9] = ' ';
+                break;
+        }
+    }
+
+    watch_display_string(buf, 0);
+}
+
+/* Init setting mode */
+static void _setting_init(deadline_state_t *state)
+{
+    state->current_page = 0;
+
+    /* Init fresh deadline to next day */
+    if (state->deadlines[state->current_index] == 0) {
+        _reset_deadline(state);
+    }
+
+    /* Ensure 1Hz updates only */
+    _change_tick_freq(1, state);
+}
+
+/* Loop of setting mode */
+static bool _setting_loop(movement_event_t event, void *context)
+{
+    deadline_state_t *state = (deadline_state_t *) context;
+    watch_date_time_t date_time;
+    date_time = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], movement_get_current_timezone_offset());
+
+    if (event.event_type != EVENT_BACKGROUND_TASK)
+        _setting_display(event, state, date_time);
+
+    switch (event.event_type) {
+        case EVENT_TICK:
+            if (state->tick_freq == 8) {
+                if (HAL_GPIO_BTN_ALARM_read()) {
+                    _increment_date(state, date_time);
+                    _setting_display(event, state, date_time);
+                } else {
+                    _change_tick_freq(4, state);
+                }
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            _change_tick_freq(8, state);
+            break;
+        case EVENT_ALARM_LONG_UP:
+            _change_tick_freq(4, state);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            _beep_button();
+            _reset_deadline(state);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            state->current_page = (state->current_page + 1) % SETTINGS_NUM;
+            _setting_display(event, state, date_time);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            _change_tick_freq(4, state);
+            _increment_date(state, date_time);
+            _setting_display(event, state, date_time);
+            break;
+        case EVENT_TIMEOUT:
+            _beep_button();
+            _background_alarm_schedule(settings, context);
+            _change_tick_freq(1, state);
+            state->mode = DEADLINE_FACE_RUNNING;
+            movement_move_to_face(0);
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            _beep_disable();
+            _background_alarm_schedule(settings, context);
+            _running_init(state);
+            _running_display(event, state);
+            state->mode = DEADLINE_FACE_RUNNING;
+            break;
+        case EVENT_BACKGROUND_TASK:
+            _background_alarm_play(state);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+/* Setup face */
+void deadline_face_setup(uint8_t watch_face_index, void **context_ptr)
+{
+    (void) watch_face_index;
+    if (*context_ptr != NULL)
+        return; /* Skip setup if context available */
+
+    /* Allocate state */
+    *context_ptr = malloc(sizeof(deadline_state_t));
+    memset(*context_ptr, 0, sizeof(deadline_state_t));
+
+    /* Store face index for background tasks */
+    deadline_state_t *state = (deadline_state_t *) *context_ptr;
+    state->face_idx = watch_face_index;
+}
+
+/* Activate face */
+void deadline_face_activate(void *context)
+{
+    deadline_state_t *state = (deadline_state_t *) context;
+
+    /* Set display options */
+    _running_init(state);
+    state->mode = DEADLINE_FACE_RUNNING;
+    state->current_index = _closest_deadline(state);
+}
+
+/* Loop face */
+bool deadline_face_loop(movement_event_t event, void *context)
+{
+    deadline_state_t *state = (deadline_state_t *) context;
+    switch (state->mode) {
+        case DEADLINE_FACE_SETTING:
+            _setting_loop(event, settings, context);
+            break;
+        default:
+        case DEADLINE_FACE_RUNNING:
+            _running_loop(event, settings, context);
+            break;
+    }
+
+    return true;
+}
+
+/* Resign face */
+void deadline_face_resign(void *context)
+{
+    (void) context;
+}
+
+/* Want background task */
+movement_watch_face_advisory_t deadline_face_advise(void *context)
+{
+    deadline_state_t *state = (deadline_state_t *) context;
+
+    if (!state->alarm_enabled)
+        return false;
+
+    /* Determine closest deadline */
+    watch_date_time_t now = watch_rtc_get_date_time();
+    uint32_t now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
+    uint32_t next_ts = state->deadlines[_closest_deadline(state)];
+
+    /* No active deadline */
+    if (next_ts < now_ts)
+        return false;
+
+    /* No deadline within next 60 seconds */
+    if (next_ts >= now_ts + 60)
+        return false;
+
+    /* Deadline within next minute. Let's set up an alarm */
+    watch_date_time_t next = watch_utility_date_time_from_unix_time(next_ts, movement_get_current_timezone_offset());
+    movement_request_wake();
+    movement_schedule_background_task_for_face(state->face_idx, next);
+    return false;
+}
diff --git a/legacy/watch_faces/complication/deadline_face.h b/legacy/watch_faces/complication/deadline_face.h
new file mode 100644 (file)
index 0000000..3cfca55
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023-2024 Konrad Rieck
+ *
+ * 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 DEADLINE_FACE_H_
+#define DEADLINE_FACE_H_
+
+#include "movement.h"
+
+/* Modes of face */
+typedef enum {
+    DEADLINE_FACE_RUNNING = 0,
+    DEADLINE_FACE_SETTING
+} deadline_mode_t;
+
+/* Number of deadline dates */
+#define DEADLINE_FACE_DATES (4)
+
+/* Deadline configuration */
+typedef struct {
+    deadline_mode_t mode:1;
+    uint8_t current_page:3;
+    uint8_t current_index:2;
+    uint8_t alarm_enabled:1;
+    uint8_t tick_freq;
+    uint8_t face_idx;
+    uint32_t deadlines[DEADLINE_FACE_DATES];
+} deadline_state_t;
+
+void deadline_face_setup(uint8_t watch_face_index, void **context_ptr);
+void deadline_face_activate(void *context);
+bool deadline_face_loop(movement_event_t event, void *context);
+void deadline_face_resign(void *context);
+movement_watch_face_advisory_t deadline_face_advise(void *context);
+
+#define deadline_face ((const watch_face_t){ \
+    deadline_face_setup, \
+    deadline_face_activate, \
+    deadline_face_loop, \
+    deadline_face_resign, \
+    deadline_face_advise \
+})
+
+#endif                          // DEADLINE_FACE_H_
diff --git a/legacy/watch_faces/complication/discgolf_face.c b/legacy/watch_faces/complication/discgolf_face.c
new file mode 100644 (file)
index 0000000..8d49992
--- /dev/null
@@ -0,0 +1,322 @@
+#include <stdlib.h>
+#include <string.h>
+#include "discgolf_face.h"
+#include "watch.h"          // Remember to change number of courses in this file
+#include "watch_utility.h"
+
+/* 
+ * Keep track of scores in discgolf or golf!
+ * The watch face operates in three different modes:
+ *
+ *  - dg_setting: Select a course
+ *      Enter this mode by holding down the light button. The screen will display
+ *      the label for the hole and the lowest score since last boot. 
+ *      Press alarm to loop through the holes. Press the light button to make a 
+ *      selection. This will reset all scores and start a new game in dg_idle mode.
+ *
+ *  -dg_idle: We're playing a hole
+ *      This either shows your current score relative to par, or the score for a
+ *      particular hole. 
+ *      At the start of a game, press alarm to loop through the holes and leave it
+ *      your starting hole. For optimal experience, play the course linearly after that
+ *      If you're viewing the hole you're supposed to be playing, the watch face will
+ *      display your score relative to par.
+ *      Use the alarm button to view other holes than the one you're playing, in which
+ *      case the input score for that hole will be displayed, in case it needs changing.
+ *      Long press the alarm button to snap back to currently playing hole.
+ *      To input scores for a hole in this mode, press the light button.
+ *
+ *  -dg_scoring: Input score for a hole
+ *      In this mode, if the score is 0 (hasn't been entered during this round),
+ *      it will blink, indicating we're in scoring mode. Press the alarm button
+ *      to increment the score up until 15, in which case it loops back to 0. 
+ *      Press the light button to save the score for that hole, advance one hole
+ *      if you're not editing an already input score, and returning to idle mode.
+ *
+ *  When all scores have been entered, the LAP indicator turns on. At that point, if we enter 
+ *  dg_setting to select a course, the score for that round is evaluated against the current 
+ *  lowest score for that course, and saved if it is better.
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Enter course data
+/* Initialize lowest scores with a high number */
+int8_t best[courses];
+
+static const uint8_t pars[][18] = {
+   { 3, 3, 4, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },    // Grafarholt
+   { 3, 4, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3 },    // Gufunes
+   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 },    // Vífilsstaðir
+   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 },    // Dalvegur
+   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 },    // Laugardalur
+   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 },    // Guðmundarlundur
+   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 },    // Víðistaðatún
+   { 3, 3, 3, 4, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 },    // Fossvogur
+   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 },    // Klambratún
+   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 },    // Seljahverfi
+   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }     // Fella- og Hóla
+};
+static const uint8_t holes[] = {                                // Number of holes on each course
+    18,
+    18,
+    10,
+    10,
+    10,
+    10,
+    9,
+    9,
+    9,
+    9,
+    9
+};
+/* Two-letter descriptive labels, second field can only be A, B, C, D, E, F, H, I, J, L, N, O, R, T, U and X. */
+static const char labels[][2] = {
+    { 'G', 'H' },
+    { 'G', 'N' },
+    { 'V', 'I' },
+    { 'D', 'V' },
+    { 'L', 'A' },
+    { 'G', 'L' },
+    { 'V', 'T' },
+    { 'F', 'V' },
+    { 'K', 'T' },
+    { 'S', 'E' },
+    { 'F', 'H' }
+};
+
+// End of course data
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+/* Beep function */
+static inline void beep() {
+    if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+}
+
+/* Prep for a new round */
+static inline void reset(discgolf_state_t *state) {
+    for (int i = 0; i < holes[state->course]; i++) {
+        state->scores[i] = 0;
+    }
+    state->hole = 1;
+    watch_clear_indicator(WATCH_INDICATOR_LAP);
+    return;
+}
+
+/* Total number of throws so far */
+static inline uint8_t score_sum(discgolf_state_t *state) {
+    uint8_t sum = 0;
+    for (int i = 0; i < holes[state->course]; i++) {
+        sum = sum + state->scores[i];
+    }
+    return sum;
+}
+
+/* Count how many holes have been played */
+static inline uint8_t count_played(discgolf_state_t *state) {
+    uint8_t holes_played = 0;
+    for (int i = 0; i < holes[state->course]; i++) {
+        if (state->scores[i] > 0) holes_played++;
+    }
+    return holes_played;
+}
+
+
+/* Calculate the current score relative to par */
+static inline int8_t calculate_score(discgolf_state_t *state) {
+    uint8_t par_sum = 0;
+    uint8_t score_sum = 0;
+    
+    for (int i = 0; i < holes[state->course]; i++) {
+        if (state->scores[i] > 0) {
+            par_sum = par_sum + pars[state->course][i];
+            score_sum = score_sum + state->scores[i];
+        }
+    }
+    return score_sum - par_sum;
+}
+
+/* Store score if it's the best so far */
+static inline void store_best(discgolf_state_t *state) {
+    uint8_t played = count_played(state);
+    if ( played == holes[state->course] ) {
+        int8_t high_score = calculate_score(state);
+        if (high_score < best[state->course] ) {
+            best[state->course] = high_score;
+        }
+    }
+}
+
+/* Configuration at boot, the high score array can be initialized with your high scores if they're known */
+void discgolf_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+       *context_ptr = malloc(sizeof(discgolf_state_t));
+       discgolf_state_t *state = (discgolf_state_t *)*context_ptr;
+       memset(*context_ptr, 0, sizeof(discgolf_state_t));
+       state->hole = 1;
+       state->course = 0;
+       state->playing = holes[state->course] + 1;
+       for (int i = 0; i < courses; i++) best[i] = 99;
+       state->mode = dg_setting;
+    }
+}
+
+void discgolf_face_activate(void *context) {
+    watch_clear_colon();
+    discgolf_state_t *state = (discgolf_state_t *)context;
+
+    /* If we were playing, go to current hole */
+    if (state->playing <= holes[0]) {
+        state->hole = state->playing;
+    }
+    /* Set LAP if round finished */
+    if (count_played(state) == holes[state->course] ) {
+        watch_set_indicator(WATCH_INDICATOR_LAP);
+    }
+    movement_request_tick_frequency(4);
+}
+
+bool discgolf_face_loop(movement_event_t event, void *context) {
+    
+    discgolf_state_t *state = (discgolf_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_TIMEOUT:
+            /* Snap to first screen if we're not playing*/
+            if ( count_played(state) == holes[state->course] || state->mode == dg_setting) {
+                movement_move_to_face(0);
+            }
+            /* Cancel scoring if timed out */
+            if (state->mode == dg_scoring) {
+                state->scores[state->hole] = 0;
+                state->mode = dg_idle;
+            }
+            break;
+        /* Advance if not scoring */
+        case EVENT_MODE_BUTTON_UP:
+            if ( state->mode != dg_scoring ) {
+                               movement_move_to_next_face();
+            }
+            break;
+        /* Go to default face if not scoring */
+        case EVENT_MODE_LONG_PRESS:
+            if ( state->mode != dg_scoring ) {
+                movement_move_to_face(0);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            switch ( state->mode ) {
+                case dg_idle:
+                    /* Check if selected hole is the first one */
+                    if ( score_sum(state) == 0 ) {
+                        state->playing = state->hole;
+                    }
+                    /* Enter scoring mode */
+                    state->mode = dg_scoring;
+                    break;
+                case dg_scoring:
+                    /* Set the LAP indicator if all scores are entered */
+                    if (count_played(state) == holes[state->course] ) {
+                        watch_set_indicator(WATCH_INDICATOR_LAP);
+                    }
+                    /* Advance to next hole if not editing previously set score */
+                    if ( state->hole == state->playing ) {
+                        if (state->hole < holes[state->course]) state->hole++;
+                        else state->hole = 1;
+                        if (state->playing < holes[state->course]) state->playing++;
+                        else state->playing = 1;
+                    }
+                    /* Return to idle */
+                    state->mode = dg_idle;
+                    break;
+                case dg_setting:
+                    /* Return to idle */
+                    state->playing = holes[state->course] + 1;
+                    state->mode = dg_idle;
+                    break;
+            }
+            beep();
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch (state->mode) {
+                /* Setting, loop through courses */
+                case dg_setting:
+                    state->course = (state->course + 1) % courses;
+                    break;
+                /* Scoring, increment score for current hole */
+                case dg_scoring:
+                    state->scores[state->hole - 1] = (state->scores[state->hole - 1] + 1) % 16;   // Loop around at 15
+                    break;
+                /* Idle, loop through holes */
+                case dg_idle:
+                    if (state->hole < holes[state->course]) {
+                        state->hole++;
+                    } else { state->hole = 1; }
+                    break;
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            /* Enter setting mode, reset state */
+            if ( state->mode == dg_idle ) {
+                state->mode = dg_setting;
+                store_best(state);
+                reset(state);
+                beep();
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            /* Snap back to currently playing hole if we've established one*/
+            if ( (state->mode == dg_idle) && (state->hole != state->playing) && (state->playing <= holes[state->course]) ) {
+                state->hole = state->playing;
+                beep();
+            }
+            break;
+        default:
+            break;
+    }
+
+    char buf[21];
+    char prefix;
+    int8_t diff;
+
+    switch (state->mode) {
+        /* Setting mode, display course label and high score */
+        case dg_setting:
+            if ( best[state->course] < 0 ) { 
+                prefix = '-';
+            } else { prefix = ' '; }
+            sprintf(buf, "%c%c   %c%2d  ", labels[state->course][0], labels[state->course][1], prefix, abs(best[state->course]));
+            break;
+        /* Idle, show relative or input score */
+        case dg_idle:
+            if (state->hole == state->playing) {
+                diff = calculate_score(state);
+                if ( diff < 0 ) {
+                    prefix = '-';
+                } else { prefix = ' '; }
+                sprintf(buf, "%c%c%2d %c%2d  ", labels[state->course][0], labels[state->course][1], state->hole, prefix, abs(diff));
+            } else {
+                sprintf(buf, "%c%c%2d  %2d  ", labels[state->course][0], labels[state->course][1], state->hole, state->scores[state->hole - 1]);
+            }
+            break;
+        /* Scoring, show set score */
+        case dg_scoring:
+            sprintf(buf, "%c%c%2d  %2d  ", labels[state->course][0], labels[state->course][1], state->hole, state->scores[state->hole - 1]);
+            break;
+    }
+
+    /* Blink during scoring */
+    if (event.subsecond % 2 && state->mode == dg_scoring) {
+        buf[6] = buf[7] = ' ';
+    }
+
+    /* Draw screen */
+    watch_display_string(buf, 0);
+
+    return true;
+}
+
+void discgolf_face_resign(void *context) {
+    (void) context;
+    watch_clear_indicator(WATCH_INDICATOR_LAP);
+}
diff --git a/legacy/watch_faces/complication/discgolf_face.h b/legacy/watch_faces/complication/discgolf_face.h
new file mode 100644 (file)
index 0000000..bfdd532
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Þorsteinn Jón Gautason
+ *
+ * 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 DISCGOLF_FACE_H_
+#define DISCGOLF_FACE_H_
+
+/*
+ * DISC GOLF face
+ *
+ * Keep track of scores in discgolf or golf!
+ * The watch face operates in three different modes:
+ *
+ *  - dg_setting: Select a course
+ *      Enter this mode by holding down the light button. The screen will display
+ *      the label for the hole and the lowest score since last boot.
+ *      Press alarm to loop through the holes. Press the light button to make a
+ *      selection. This will reset all scores and start a new game in dg_idle mode.
+ *
+ *  -dg_idle: We're playing a hole
+ *      This either shows your current score relative to par, or the score for a
+ *      particular hole.
+ *      At the start of a game, press alarm to loop through the holes and leave it
+ *      your starting hole. For optimal experience, play the course linearly after that
+ *      If you're viewing the hole you're supposed to be playing, the watch face will
+ *      display your score relative to par.
+ *      Use the alarm button to view other holes than the one you're playing, in which
+ *      case the input score for that hole will be displayed, in case it needs changing.
+ *      Long press the alarm button to snap back to currently playing hole.
+ *      To input scores for a hole in this mode, press the light button.
+ *
+ *  -dg_scoring: Input score for a hole
+ *      In this mode, if the score is 0 (hasn't been entered during this round),
+ *      it will blink, indicating we're in scoring mode. Press the alarm button
+ *      to increment the score up until 15, in which case it loops back to 0.
+ *      Press the light button to save the score for that hole, advance one hole
+ *      if you're not editing an already input score, and returning to idle mode.
+ *
+ *  When all scores have been entered, the LAP indicator turns on. At that point, if we enter
+ *  dg_setting to select a course, the score for that round is evaluated against the current
+ *  lowest score for that course, and saved if it is better.
+*/
+
+#include "movement.h"
+#define courses 11
+
+typedef enum {
+  dg_setting,                   // We are selecting a course
+  dg_scoring,                   // We are inputting our score
+  dg_idle,                      // We have input our score and are playing a hole
+} discgolf_mode_t;
+
+typedef struct {
+    uint8_t course;             // Index for course selection, from 0
+    uint8_t hole;               // Index for current hole, from 1
+    uint8_t playing;            // Current hole
+    int scores[18];             // Scores for each played hole
+    discgolf_mode_t mode;       // Watch face mode
+} discgolf_state_t;
+
+void discgolf_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void discgolf_face_activate(void *context);
+bool discgolf_face_loop(movement_event_t event, void *context);
+void discgolf_face_resign(void *context);
+
+#define discgolf_face ((const watch_face_t){ \
+    discgolf_face_setup, \
+    discgolf_face_activate, \
+    discgolf_face_loop, \
+    discgolf_face_resign, \
+    NULL, \
+})
+
+#endif // DISCGOLF_FACE_H_
diff --git a/legacy/watch_faces/complication/dual_timer_face.c b/legacy/watch_faces/complication/dual_timer_face.c
new file mode 100644 (file)
index 0000000..e819d59
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ * Copyright (c) 2022 Andreas Nebinger
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "dual_timer_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_rtc.h"
+
+/*
+ * IMPORTANT: This watch face uses the same TC2 callback counter as the Stock Stopwatch
+ * watch-face. It works through calling a global handler function. The two watch-faces
+ * therefore can't coexist within the same firmware. If you want to compile this watch-face
+ * then you need to remove the line <../watch_faces/complication/fast_stopwatch_face.c \>
+ * from the Makefile.
+ */
+
+// FROM fast_stopwatch_face.c ////////////////////////////////////////////////
+// Copyright (c) 2022 Andreas Nebinger
+
+#if __EMSCRIPTEN__
+#include <emscripten.h>
+#include <emscripten/html5.h>
+#else
+#include "../../../watch-library/hardware/include/saml22j18a.h"
+#include "../../../watch-library/hardware/include/component/tc.h"
+#include "../../../watch-library/hardware/hri/hri_tc_l22.h"
+#endif
+
+static const watch_date_time_t distant_future = {.unit = {0, 0, 0, 1, 1, 63}};
+static bool _is_running;
+static uint32_t _ticks;
+
+#if __EMSCRIPTEN__
+
+static long _em_interval_id = 0;
+
+void em_dual_timer_cb_handler(void *userData) {
+    // interrupt handler for emscripten 128 Hz callbacks
+    (void) userData;
+    _ticks++;
+}
+
+static void _dual_timer_cb_initialize() { }
+
+static inline void _dual_timer_cb_stop() {
+    emscripten_clear_interval(_em_interval_id);
+    _em_interval_id = 0;
+    _is_running = false;
+}
+
+static inline void _dual_timer_cb_start() {
+    // initiate 128 hz callback
+    _em_interval_id = emscripten_set_interval(em_dual_timer_cb_handler, (double)(1000/128), (void *)NULL);
+}
+
+#else
+
+static inline void _dual_timer_cb_start() {
+    // start the TC2 timer
+    hri_tc_set_CTRLA_ENABLE_bit(TC2);
+    _is_running = true;
+}
+
+static inline void _dual_timer_cb_stop() {
+    // stop the TC2 timer
+    hri_tc_clear_CTRLA_ENABLE_bit(TC2);
+    _is_running = false;
+}
+
+static void _dual_timer_cb_initialize() {
+    // setup and initialize TC2 for a 64 Hz interrupt
+    hri_mclk_set_APBCMASK_TC2_bit(MCLK);
+    hri_gclk_write_PCHCTRL_reg(GCLK, TC2_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN);
+    _dual_timer_cb_stop();
+    hri_tc_write_CTRLA_reg(TC2, TC_CTRLA_SWRST);
+    hri_tc_wait_for_sync(TC2, TC_SYNCBUSY_SWRST);
+    hri_tc_write_CTRLA_reg(TC2, TC_CTRLA_PRESCALER_DIV64 | // 32 Khz divided by 64 divided by 4 results in a 128 Hz interrupt
+                           TC_CTRLA_MODE_COUNT8 | 
+                           TC_CTRLA_RUNSTDBY);
+    hri_tccount8_write_PER_reg(TC2, 3);
+    hri_tc_set_INTEN_OVF_bit(TC2);
+    NVIC_ClearPendingIRQ(TC2_IRQn);
+    NVIC_EnableIRQ (TC2_IRQn);
+}
+
+// you need to take fast_stopwatch.c out of the Makefile or this will create a conflict
+// you have to choose between one of the stopwatches
+ void TC2_Handler(void) {
+    // interrupt handler for TC2 (globally!)
+    _ticks++;
+    TC2->COUNT8.INTFLAG.reg |= TC_INTFLAG_OVF;
+}
+
+#endif
+
+// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
+
+/** @brief converts tick counts to duration struct for time display 
+ */
+static dual_timer_duration_t ticks_to_duration(uint32_t ticks) {
+    dual_timer_duration_t duration;
+    uint8_t hours = 0;
+    uint8_t days = 0;
+
+    // count hours and days
+    while (ticks >= (128 * 60 * 60)) {
+        ticks -= (128 * 60 * 60);
+        hours++;
+        if (hours >= 24) {
+            hours -= 24;
+            days++;
+        }
+    }
+
+    // convert minutes, seconds, centiseconds
+    duration.centiseconds = (ticks & 0x7F) * 100 / 128;
+    duration.seconds = (ticks >> 7) % 60;
+    duration.minutes = (ticks >> 7) / 60;
+    duration.hours = hours;
+    duration.days = days;
+
+    return duration;
+}
+
+/** @brief starts one of the dual timers
+ *  @details Starts a dual timer. If no previous timer is running it starts the global
+ *  tick counter. If a previous timer is already running it registers the current tick. 
+ */
+static void start_timer(dual_timer_state_t *state, bool timer) {
+    // if it is not running yet, run it
+    if ( !_is_running ) {
+        _is_running = true;
+        movement_request_tick_frequency(16);
+        state->start_ticks[timer] = 0;
+        state->stop_ticks[timer] = 0;
+        _ticks = 0;
+        _dual_timer_cb_start();
+        movement_schedule_background_task(distant_future);
+    } else {
+        // if another timer is already running save the current tick
+        state->start_ticks[timer] = _ticks;
+        state->stop_ticks[timer] = _ticks;
+    }
+    state->running[timer] = true;
+}
+
+/** @brief stops one of the dual timers
+ *  @details Stops a dual timer. If no other timer is running it stops the global
+ *  tick counter. If another timer is already running it registers the current stop tick. 
+ */
+static void stop_timer(dual_timer_state_t *state, bool timer) {
+    // stop timer and save duration
+    state->stop_ticks[timer] = _ticks;
+    state->duration[timer] = ticks_to_duration(state->stop_ticks[timer] - state->start_ticks[timer]);
+    state->running[timer] = false;
+    // if the other timer is not running, stop callback
+    if ( state->running[!timer] == false ) {
+        _is_running = false;
+        _dual_timer_cb_stop();
+        movement_request_tick_frequency(1);
+        movement_cancel_background_task();
+    }
+}
+
+/** @brief displays the measured time for each of the dual timers
+ *  @details displays the dual timer. Below 1 hour it displays the timed minutes, seconds,
+ *  and centiseconds. Above that it shows the timed hours, minutes, and seconds. If it 
+ *  has run for more than a day it shows the days, hours, and minutes.
+ *  When the timer is running, the colon blinks every half second.
+ *  It also indicates at the top if another counter is running and for how long.
+ */
+static void dual_timer_display(dual_timer_state_t *state) {
+    char buf[11];
+    char oi[3];
+    // get the current time count of the selected counter
+    dual_timer_duration_t timer = state->running[state->show] ? ticks_to_duration(state->stop_ticks[state->show] - state->start_ticks[state->show]) : state->duration[state->show];
+    // get the current time count of the other counter
+    dual_timer_duration_t other = ticks_to_duration(state->stop_ticks[!state->show] - state->start_ticks[!state->show]);
+    
+    if ( timer.days > 0 )
+        sprintf(buf, "%02u%02u%02u", timer.days, timer.hours, timer.minutes);
+    else if ( timer.hours > 0 )
+        sprintf(buf, "%02u%02u%02u", timer.hours, timer.minutes, timer.seconds);
+    else
+        sprintf(buf, "%02u%02u%02u", timer.minutes, timer.seconds, timer.centiseconds);
+    watch_display_string(buf, 4);
+    
+    // which counter is displayed
+    watch_display_string(state->show ? "B" : "A", 0);
+    
+    // indicate whether other counter is running
+    watch_display_string(state->running[!state->show] && (_ticks % 100) < 50 ? "+" : " ", 1);
+    
+    // indicate for how long the other counter has been running
+    sprintf(oi, "%2u", other.days > 0 ? other.days : (other.hours > 0 ? other.hours : (other.minutes > 0 ? other.minutes : (other.seconds > 0 ? other.seconds : other.centiseconds))));
+    watch_display_string( (state->stop_ticks[!state->show] - state->start_ticks[!state->show]) > 0 ? oi : "  ", 2);
+    
+    // blink colon when running
+    if ( timer.centiseconds > 50 || !state->running[state->show] ) watch_set_colon();
+    else watch_clear_colon();
+}
+
+// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
+
+void dual_timer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(dual_timer_state_t));
+        memset(*context_ptr, 0, sizeof(dual_timer_state_t));
+        _ticks = 0;
+    }
+    if (!_is_running) {
+        _dual_timer_cb_initialize();
+    }
+}
+
+void dual_timer_face_activate(void *context) {
+    (void) context;
+    if (_is_running) {
+        movement_schedule_background_task(distant_future);
+    }
+}
+
+bool dual_timer_face_loop(movement_event_t event, void *context) {
+    dual_timer_state_t *state = (dual_timer_state_t *)context;
+
+    // timers stop at 99:23:59:59:99
+    if ( (_ticks - state->start_ticks[0]) >= 1105919999 )
+        stop_timer(state, 0);
+
+    if ( (_ticks - state->start_ticks[1]) >= 1105919999 )
+        stop_timer(state, 1);
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_set_colon();
+            if (_is_running) {
+                movement_request_tick_frequency(16);
+                if ( state->running[0] )
+                    state->show = 0;
+                else state->show = 1;
+            } else {
+                if (state->stop_ticks[0] > 0 || state->stop_ticks[1] > 0)
+                    dual_timer_display(state);
+                else watch_display_string("A   000000", 0);
+            }
+            break;
+        case EVENT_TICK:
+            if ( _is_running ) {
+                // update stop ticks
+                if ( state->running[0] )
+                    state->stop_ticks[0] = _ticks;
+                if ( state->running[1] )
+                    state->stop_ticks[1] = _ticks;
+                dual_timer_display(state);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // start/stop timer B
+            state->running[1] = !state->running[1];
+            if ( state->running[1] ) {
+                start_timer(state, 1);
+            } else {
+                stop_timer(state, 1);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+            // start/stop timer A
+            state->running[0] = !state->running[0];
+            if ( state->running[0] ) {
+                start_timer(state, 0);
+            } else {
+                stop_timer(state, 0);
+            }
+            break;
+        case EVENT_MODE_BUTTON_DOWN:
+            // switch between the timers
+            state->show = !state->show;
+            dual_timer_display(state);
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            // don't switch to next face...
+            break;
+        case EVENT_MODE_LONG_PRESS:
+            // ...but do it on long press MODE!
+            movement_move_to_next_face();
+            break;
+        case EVENT_TIMEOUT:
+            // go back to 
+            if (!_is_running) movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            dual_timer_display(state);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void dual_timer_face_resign(void *context) {
+    (void) context;
+    movement_cancel_background_task();
+    // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/legacy/watch_faces/complication/dual_timer_face.h b/legacy/watch_faces/complication/dual_timer_face.h
new file mode 100644 (file)
index 0000000..ea8d3c4
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ * Copyright (c) 2022 Andreas Nebinger
+ *
+ * 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 DUAL_TIMER_FACE_H_
+#define DUAL_TIMER_FACE_H_
+
+/*
+ * DUAL TIMER
+ * ==========
+ * 
+ * Inspired by special ops and tactical trope targeted watches like the Nixon Regulus
+ * that feature two chronographs for timing two simultaneous events, here is a watch
+ * face that expands upon Andreas Nebinger's Stock Stopwatch Face code to implement this 
+ * functionality.
+ * 
+ * ALARM starts/stops timer A, resets on the next start
+ * LIGHT starts/stops timer B, resets on the next start
+ * 
+ * When a timer is running, tapping MODE toggles between displaying timers A or B, as
+ * indicated at the top of the display.
+ * 
+ * The currently selected timer shows minutes, seconds, and 100ths of seconds until the
+ * timed event passes the hour mark. Then it shows hours, minutes, and seconds. Once
+ * it runs for more than a day it shows days, hours, and minutes. The blinking colon
+ * indicates that the timer is running.
+ * 
+ * The longest time span the timers are able to track as 99 days and 23:59:59:99 hours and
+ * they will simply stop.
+ * 
+ * If the other timer that is not currently selected is also running then a plus sign is
+ * blinking next to the A or B indicator. Its progress is indicated by showing the timer's
+ * currently highest time unit ( 100ths of seconds if less than a second, seconds if less
+ * than a minute, minutes if less than an hour, hours if less than a day, days if more than
+ * a day).
+ * 
+ * Please Note: If at least one timer is running then the default function of the MODE
+ * button to move to the next watch face is disabled to be able to use it to toggle between
+ * the timers. In this case LONG PRESSING MODE will move to the next face instead of moving
+ * back to the default watch face.
+ *
+ * IMPORTANT: This watch face uses the same TC2 callback counter as the Stock Stopwatch
+ * watch-face. It works through calling a global handler function. The two watch-faces
+ * therefore can't coexist within the same firmware. If you want to compile this watch-face
+ * then you need to remove the line <../watch_faces/complication/fast_stopwatch_face.c \>
+ * from the Makefile.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint8_t centiseconds : 7;  // 0-59
+    uint8_t seconds : 6;  // 0-59
+    uint8_t minutes : 6;  // 0-59
+    uint8_t hours : 5;    // 0-23
+    uint8_t days : 7;    // 0-99
+} dual_timer_duration_t;
+
+typedef struct {
+    uint32_t start_ticks[2];
+    uint32_t stop_ticks[2];
+    dual_timer_duration_t duration[2];
+    bool running[2];
+    bool show;
+} dual_timer_state_t;
+
+void dual_timer_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void dual_timer_face_activate(void *context);
+bool dual_timer_face_loop(movement_event_t event, void *context);
+void dual_timer_face_resign(void *context);
+
+#if __EMSCRIPTEN__
+void em_dual_timer_cb_handler(void *userData);
+#else
+void TC2_Handler(void);
+#endif
+
+#define dual_timer_face ((const watch_face_t){ \
+    dual_timer_face_setup, \
+    dual_timer_face_activate, \
+    dual_timer_face_loop, \
+    dual_timer_face_resign, \
+    NULL, \
+})
+
+#endif // DUAL_TIMER_FACE_H_
+
diff --git a/legacy/watch_faces/complication/endless_runner_face.c b/legacy/watch_faces/complication/endless_runner_face.c
new file mode 100644 (file)
index 0000000..1a17329
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 <David Volovskiy>
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "endless_runner_face.h"
+
+typedef enum {
+    JUMPING_FINAL_FRAME = 0,
+    NOT_JUMPING,
+    JUMPING_START,
+} RunnerJumpState;
+
+typedef enum {
+    SCREEN_TITLE = 0,
+    SCREEN_PLAYING,
+    SCREEN_LOSE,
+    SCREEN_TIME,
+    SCREEN_COUNT
+} RunnerCurrScreen;
+
+typedef enum {
+    DIFF_BABY = 0,  // FREQ_SLOW FPS;       MIN_ZEROES 0's min;  Jump is JUMP_FRAMES_EASY frames
+    DIFF_EASY,      //      FREQ FPS;       MIN_ZEROES 0's min;  Jump is JUMP_FRAMES_EASY frames
+    DIFF_NORM,      //      FREQ FPS;       MIN_ZEROES 0's min;  Jump is JUMP_FRAMES frames
+    DIFF_HARD,      //      FREQ FPS;  MIN_ZEROES_HARD 0's min;  jump is JUMP_FRAMES frames
+    DIFF_FUEL,      // Mode where the top-right displays the amoount of fuel that you can be above the ground for, dodging obstacles. When on the ground, your fuel recharges.
+    DIFF_FUEL_1,    // Same as DIFF_FUEL, but if your fuel is 0, then you won't recharge
+    DIFF_COUNT
+} RunnerDifficulty;
+
+#define NUM_GRID 12  // This the length that the obstacle track can be on
+#define FREQ 8  // Frequency request for the game
+#define FREQ_SLOW 4  // Frequency request for baby mode
+#define JUMP_FRAMES 2  // Wait this many frames on difficulties above EASY before coming down from the jump button pressed
+#define JUMP_FRAMES_EASY 3 // Wait this many frames on difficulties at or below EASY before coming down from the jump button pressed
+#define MIN_ZEROES 4  // At minimum, we'll have this many spaces between obstacles
+#define MIN_ZEROES_HARD 3 // At minimum, we'll have this many spaces between obstacles on hard mode
+#define MAX_HI_SCORE 9999  // Max hi score to store and display on the title screen.
+#define MAX_DISP_SCORE 39  // The top-right digits can't properly display above 39
+#define JUMP_FRAMES_FUEL 30  // The max fuel that fuel that the fuel mode game will hold
+#define JUMP_FRAMES_FUEL_RECHARGE 3 // How much fuel each frame on the ground adds
+#define MAX_DISP_SCORE_FUEL 9  // Since the fuel mode displays the score in the weekday slot, two digits will display wonky data
+
+typedef struct {
+    uint32_t obst_pattern;
+    uint16_t obst_indx : 8;
+    uint16_t jump_state : 5;
+    uint16_t sec_before_moves : 3;
+    uint16_t curr_score : 10;
+    uint16_t curr_screen : 4;
+    bool loc_2_on;
+    bool loc_3_on;
+    bool success_jump;
+    bool fuel_mode;
+    uint8_t fuel;
+} game_state_t;
+
+static game_state_t game_state;
+static const uint8_t _num_bits_obst_pattern = sizeof(game_state.obst_pattern) * 8;
+
+static void print_binary(uint32_t value, int bits) {
+#if __EMSCRIPTEN__
+    for (int i = bits - 1; i >= 0; i--) {
+        // Print each bit
+        printf("%lu", (value >> i) & 1);
+        // Optional: add a space every 4 bits for readability
+        if (i % 4 == 0 && i != 0) {
+            printf(" ");
+        }
+    }
+    printf("\r\n");
+#else
+    (void) value;
+    (void) bits;
+#endif
+    return;
+}
+
+static uint32_t get_random(uint32_t max) {
+    #if __EMSCRIPTEN__
+    return rand() % max;
+    #else
+    return arc4random_uniform(max);
+    #endif
+}
+
+static uint32_t get_random_nonzero(uint32_t max) {
+    uint32_t random;
+    do
+    {
+        random = get_random(max);
+    } while (random == 0);
+    return random;
+}
+
+static uint32_t get_random_kinda_nonzero(uint32_t max) {
+    // Returns a number that's between 1 and max, unless max is 0 or 1, then it returns 0 to max.
+    if (max == 0) return 0;
+    else if (max == 1) return get_random(max);
+    return get_random_nonzero(max);
+}
+
+static uint32_t get_random_fuel(uint32_t prev_val) {
+    static uint8_t prev_rand_subset = 0;
+    uint32_t rand;
+    uint8_t max_ones, subset;
+    uint32_t rand_legal = 0;
+    prev_val = prev_val & ~0xFFFF;
+
+    for (int i = 0; i < 2; i++) {
+        subset = 0;
+        max_ones = 8;
+        if (prev_rand_subset > 4)
+            max_ones -= prev_rand_subset;
+        rand = get_random_kinda_nonzero(max_ones);
+        if (rand > 5 && prev_rand_subset) rand = 5;  // The gap of one or two is awkward
+        for (uint32_t j = 0; j < rand; j++) {
+            subset |= (1 << j);
+        }
+        if (prev_rand_subset >= 7)
+            subset = subset << 1;
+        subset &= 0xFF;
+        rand_legal |= subset << (8 * i);
+        prev_rand_subset = rand;
+    }
+
+    rand_legal = prev_val | rand_legal;
+    print_binary(rand_legal, 32);
+    return rand_legal;
+}
+
+static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) {
+/** @brief A legal random number starts with the previous number (which should be the 12 bits on the screen).
+  * @param prev_val The previous value to tack onto. The return will have its first NUM_GRID MSBs be the same as prev_val, and the rest be new
+  * @param difficulty To dictate how spread apart the obsticles must be
+  * @return the new random value, where it's first NUM_GRID MSBs are the same as prev_val
+  */
+    uint8_t min_zeros = (difficulty == DIFF_HARD) ? MIN_ZEROES_HARD : MIN_ZEROES;
+    uint32_t max = (1 << (_num_bits_obst_pattern - NUM_GRID)) - 1;
+    uint32_t rand = get_random_nonzero(max);
+    uint32_t rand_legal = 0;
+    prev_val = prev_val & ~max;
+
+    for (int i = (NUM_GRID + 1); i <= _num_bits_obst_pattern; i++) {
+        uint32_t mask = 1 << (_num_bits_obst_pattern - i);
+        bool msb = (rand & mask) >> (_num_bits_obst_pattern - i);
+        if (msb) {
+            rand_legal = rand_legal << min_zeros;
+            i+=min_zeros;
+        }
+        rand_legal |= msb;
+        rand_legal = rand_legal << 1;
+    }
+
+    rand_legal = rand_legal & max;
+    for (int i = 0; i <= min_zeros; i++) {
+        if (prev_val & (1 << (i + _num_bits_obst_pattern - NUM_GRID))){
+            rand_legal = rand_legal >> (min_zeros - i);
+            break;
+        }
+    }
+    rand_legal = prev_val | rand_legal;
+    print_binary(rand_legal, 32);
+    return rand_legal;
+}
+
+static void display_ball(bool jumping) {
+    if (!jumping) {
+        watch_set_pixel(0, 21);
+        watch_set_pixel(1, 21);
+        watch_set_pixel(0, 20);
+        watch_set_pixel(1, 20);
+        watch_clear_pixel(1, 17);
+        watch_clear_pixel(2, 20);
+        watch_clear_pixel(2, 21);     
+    }
+    else {
+        watch_clear_pixel(0, 21);
+        watch_clear_pixel(1, 21);
+        watch_clear_pixel(0, 20);
+        watch_set_pixel(1, 20);
+        watch_set_pixel(1, 17);
+        watch_set_pixel(2, 20);
+        watch_set_pixel(2, 21);
+    }
+}
+
+static void display_score(uint8_t score) {
+    char buf[3];
+    if (game_state.fuel_mode) {
+        score %= (MAX_DISP_SCORE_FUEL + 1);
+        sprintf(buf, "%1d", score);
+        watch_display_string(buf, 0);
+    }
+    else {
+        score %= (MAX_DISP_SCORE + 1);
+        sprintf(buf, "%2d", score);
+        watch_display_string(buf, 2);
+    }
+}
+
+static void add_to_score(endless_runner_state_t *state) {
+    if (game_state.curr_score <= MAX_HI_SCORE) {
+        game_state.curr_score++;
+        if (game_state.curr_score > state -> hi_score)
+            state -> hi_score = game_state.curr_score;
+    }
+    game_state.success_jump = true;
+    display_score(game_state.curr_score);
+}
+
+static void display_fuel(uint8_t subsecond, uint8_t difficulty) {
+    char buf[4];
+    if (difficulty == DIFF_FUEL_1 && game_state.fuel == 0 && subsecond % (FREQ/2) == 0) {
+        watch_display_string("  ", 2);  // Blink the 0 fuel to show it cannot be refilled.
+        return;
+    }
+    sprintf(buf, "%2d", game_state.fuel);
+    watch_display_string(buf, 2);
+}
+
+static void check_and_reset_hi_score(endless_runner_state_t *state) {
+    // Resets the hi score at the beginning of each month.
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    if ((state -> year_last_hi_score != date_time.unit.year) || 
+        (state -> month_last_hi_score != date_time.unit.month))
+    {
+        // The high score resets itself every new month.
+        state -> hi_score = 0;
+        state -> year_last_hi_score = date_time.unit.year;
+        state -> month_last_hi_score = date_time.unit.month;
+    }
+}
+
+static void display_difficulty(uint16_t difficulty) {
+    switch (difficulty)
+    {
+    case DIFF_BABY:
+        watch_display_string(" b", 2);
+        break;
+    case DIFF_EASY:
+        watch_display_string(" E", 2);
+        break;
+    case DIFF_HARD:
+        watch_display_string(" H", 2);
+        break;
+    case DIFF_FUEL:
+        watch_display_string(" F", 2);
+        break;
+    case DIFF_FUEL_1:
+        watch_display_string("1F", 2);
+        break;
+    case DIFF_NORM:
+    default:
+        watch_display_string(" N", 2);
+        break;
+    }
+    game_state.fuel_mode = difficulty >= DIFF_FUEL && difficulty <= DIFF_FUEL_1;
+}
+
+static void change_difficulty(endless_runner_state_t *state) {
+    state -> difficulty = (state -> difficulty + 1) % DIFF_COUNT;
+    display_difficulty(state -> difficulty);
+    if (state -> soundOn) {
+        if (state -> difficulty == 0) watch_buzzer_play_note(BUZZER_NOTE_B4, 30);
+        else  watch_buzzer_play_note(BUZZER_NOTE_C5, 30);
+    }
+}
+
+static void toggle_sound(endless_runner_state_t *state) {
+    state -> soundOn = !state -> soundOn;
+    if (state -> soundOn){
+        watch_buzzer_play_note(BUZZER_NOTE_C5, 30);
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    }
+    else {
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+    }
+}
+
+static void display_title(endless_runner_state_t *state) {
+    uint16_t hi_score = state -> hi_score;
+    uint8_t difficulty = state -> difficulty;
+    bool sound_on = state -> soundOn;
+    game_state.curr_screen = SCREEN_TITLE;
+    memset(&game_state, 0, sizeof(game_state));
+    game_state.sec_before_moves = 1; // The first obstacles will all be 0s, which is about an extra second of delay.
+    if (sound_on) game_state.sec_before_moves--; // Start chime is about 1 second
+    watch_set_colon();
+    if (hi_score > MAX_HI_SCORE) {
+        watch_display_string("ER  HS  --", 0);
+    }
+    else {
+        char buf[14];
+        sprintf(buf, "ER  HS%4d", hi_score);
+        watch_display_string(buf, 0);
+    }
+    display_difficulty(difficulty);
+}
+
+static void display_time(watch_date_time_t date_time, bool clock_mode_24h) {
+    static watch_date_time_t previous_date_time;
+    char buf[6 + 1];
+
+    // If the hour needs updating or it's the first time displaying the time
+    if ((game_state.curr_screen != SCREEN_TIME) || (date_time.unit.hour != previous_date_time.unit.hour)) {
+        uint8_t hour = date_time.unit.hour;
+        game_state.curr_screen = SCREEN_TIME;
+
+        if (clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
+        else {
+            if (hour >= 12) watch_set_indicator(WATCH_INDICATOR_PM);
+            hour %= 12;
+            if (hour == 0) hour = 12;
+        }
+        watch_set_colon();
+        sprintf( buf, "%2d%02d  ", hour, date_time.unit.minute);
+        watch_display_string(buf, 4);
+    }
+    // If both digits of the minute need updating
+    else if ((date_time.unit.minute / 10) != (previous_date_time.unit.minute / 10)) {
+        sprintf( buf, "%02d  ", date_time.unit.minute);
+        watch_display_string(buf, 6);
+    }
+    // If only the ones-place of the minute needs updating.
+    else if (date_time.unit.minute != previous_date_time.unit.minute) {
+        sprintf( buf, "%d  ", date_time.unit.minute % 10);
+        watch_display_string(buf, 7);
+    }
+    previous_date_time.reg = date_time.reg;
+}
+
+static void begin_playing(endless_runner_state_t *state) {
+    uint8_t difficulty = state -> difficulty;
+    game_state.curr_screen = SCREEN_PLAYING;
+    watch_clear_colon();
+    movement_request_tick_frequency((state -> difficulty == DIFF_BABY) ? FREQ_SLOW : FREQ);
+    if (game_state.fuel_mode) {
+        watch_display_string("           ", 0);
+        game_state.obst_pattern = get_random_fuel(0);
+        if ((16 * JUMP_FRAMES_FUEL_RECHARGE) < JUMP_FRAMES_FUEL) // 16 frames of zeros at the start of a level
+            game_state.fuel = JUMP_FRAMES_FUEL - (16 * JUMP_FRAMES_FUEL_RECHARGE); // Have it below its max to show it counting up when starting.
+        if (game_state.fuel < JUMP_FRAMES_FUEL_RECHARGE) game_state.fuel = JUMP_FRAMES_FUEL_RECHARGE;
+    }
+    else {
+        watch_display_string("         ", 2);
+        game_state.obst_pattern = get_random_legal(0, difficulty);
+    }
+    game_state.jump_state = NOT_JUMPING;
+    display_ball(game_state.jump_state != NOT_JUMPING);
+    display_score( game_state.curr_score);
+    if (state -> soundOn){
+        watch_buzzer_play_note(BUZZER_NOTE_C5, 200);
+        watch_buzzer_play_note(BUZZER_NOTE_E5, 200);
+        watch_buzzer_play_note(BUZZER_NOTE_G5, 200);
+    }
+}
+
+static void display_lose_screen(endless_runner_state_t *state) {
+    game_state.curr_screen = SCREEN_LOSE;
+    game_state.curr_score = 0;
+    watch_display_string("     LOSE ", 0);
+    if (state -> soundOn)
+        watch_buzzer_play_note(BUZZER_NOTE_A1, 600);
+    else
+        delay_ms(600);
+}
+
+static void display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t *state) {
+    static bool prev_obst_pos_two = 0;
+    switch (grid_loc)
+    {
+    case 2:
+        game_state.loc_2_on = obstacle;
+        if (obstacle)
+            watch_set_pixel(0, 20);
+        else if (game_state.jump_state != NOT_JUMPING) {
+            watch_clear_pixel(0, 20);
+            if (game_state.fuel_mode && prev_obst_pos_two)
+                add_to_score(state);
+        }
+        prev_obst_pos_two = obstacle;
+        break;
+    case 3:
+        game_state.loc_3_on = obstacle;
+        if (obstacle)
+            watch_set_pixel(1, 21);
+        else if (game_state.jump_state != NOT_JUMPING)
+            watch_clear_pixel(1, 21);
+        break;
+    
+    case 1:
+        if (!game_state.fuel_mode && obstacle)  // If an obstacle is here, it means the ball cleared it
+            add_to_score(state);
+        //fall through
+    case 0:
+    case 5:
+        if (obstacle)
+            watch_set_pixel(0, 18 + grid_loc);
+        else
+            watch_clear_pixel(0, 18 + grid_loc);
+        break;
+    case 4:
+        if (obstacle)
+            watch_set_pixel(1, 22);
+        else
+            watch_clear_pixel(1, 22);
+        break;
+    case 6:
+        if (obstacle)
+            watch_set_pixel(1, 0);
+        else
+            watch_clear_pixel(1, 0);
+        break;
+    case 7:
+    case 8:
+        if (obstacle)
+            watch_set_pixel(0, grid_loc - 6);
+        else
+            watch_clear_pixel(0, grid_loc - 6);
+        break;
+    case 9:
+    case 10:
+        if (obstacle)
+            watch_set_pixel(0, grid_loc - 5);
+        else
+            watch_clear_pixel(0, grid_loc - 5);
+        break;
+    case 11:
+        if (obstacle)
+            watch_set_pixel(1, 6);
+        else
+            watch_clear_pixel(1, 6);
+        break;
+    default:
+        break;
+    }
+}
+
+static void stop_jumping(endless_runner_state_t *state) {
+    game_state.jump_state = NOT_JUMPING;
+    display_ball(game_state.jump_state != NOT_JUMPING);
+    if (state -> soundOn){
+        if (game_state.success_jump)
+            watch_buzzer_play_note(BUZZER_NOTE_C5, 60);
+        else
+            watch_buzzer_play_note(BUZZER_NOTE_C3, 60);
+    }
+    game_state.success_jump = false;
+}
+
+static void display_obstacles(endless_runner_state_t *state) {
+    for (int i = 0; i < NUM_GRID; i++) {
+        // Use a bitmask to isolate each bit and shift it to the least significant position
+        uint32_t mask = 1 << ((_num_bits_obst_pattern - 1) - i);
+        bool obstacle = (game_state.obst_pattern & mask) >> ((_num_bits_obst_pattern - 1) - i);
+        display_obstacle(obstacle, i, state);
+    }
+    game_state.obst_pattern = game_state.obst_pattern << 1;
+    game_state.obst_indx++;
+    if (game_state.fuel_mode) {
+        if (game_state.obst_indx >= (_num_bits_obst_pattern / 2)) {
+                game_state.obst_indx = 0;
+                game_state.obst_pattern = get_random_fuel(game_state.obst_pattern);
+        }
+    }
+    else if (game_state.obst_indx >= _num_bits_obst_pattern - NUM_GRID) {
+        game_state.obst_indx = 0;
+        game_state.obst_pattern = get_random_legal(game_state.obst_pattern, state -> difficulty);
+    }
+}
+
+static void update_game(endless_runner_state_t *state, uint8_t subsecond) {
+    uint8_t curr_jump_frame = 0;
+    if (game_state.sec_before_moves != 0) {
+        if (subsecond == 0) --game_state.sec_before_moves;
+        return;
+    }
+    display_obstacles(state);
+    switch (game_state.jump_state)
+    {
+    case NOT_JUMPING:
+        if (game_state.fuel_mode) {
+            for (int i = 0; i < JUMP_FRAMES_FUEL_RECHARGE; i++)
+            {
+                if(game_state.fuel >= JUMP_FRAMES_FUEL || (state -> difficulty == DIFF_FUEL_1 && !game_state.fuel))
+                    break;
+                game_state.fuel++;
+            }
+        }
+        break;
+    case JUMPING_FINAL_FRAME:
+        stop_jumping(state);
+        break;
+    default:
+        if (game_state.fuel_mode) {
+            if (!game_state.fuel)
+                game_state.jump_state = JUMPING_FINAL_FRAME;
+            else
+                game_state.fuel--;
+            if (!HAL_GPIO_BTN_ALARM_read() && !HAL_GPIO_BTN_LIGHT_read()) stop_jumping(state);
+        }
+        else {
+            curr_jump_frame = game_state.jump_state - NOT_JUMPING;
+            if (curr_jump_frame >= JUMP_FRAMES_EASY || (state -> difficulty >= DIFF_NORM && curr_jump_frame >= JUMP_FRAMES))
+                game_state.jump_state = JUMPING_FINAL_FRAME;
+            else
+                game_state.jump_state++;
+        }
+        break;
+    }
+    if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) {
+        delay_ms(200);  // To show the player jumping onto the obstacle before displaying the lose screen.
+        display_lose_screen(state);
+    }
+    else if (game_state.fuel_mode)
+        display_fuel(subsecond, state -> difficulty);
+}
+
+void endless_runner_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(endless_runner_state_t));
+        memset(*context_ptr, 0, sizeof(endless_runner_state_t));
+        endless_runner_state_t *state = (endless_runner_state_t *)*context_ptr;
+        state->difficulty = DIFF_NORM;
+    }
+}
+
+void endless_runner_face_activate(void *context) {
+    (void) context;
+}
+
+bool endless_runner_face_loop(movement_event_t event, void *context) {
+    endless_runner_state_t *state = (endless_runner_state_t *)context;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            check_and_reset_hi_score(state);
+            if (state -> soundOn) watch_set_indicator(WATCH_INDICATOR_BELL);
+            display_title(state);
+            break;
+        case EVENT_TICK:
+            switch (game_state.curr_screen)
+            {
+            case SCREEN_TITLE:
+            case SCREEN_LOSE:
+                break;
+            default:
+                update_game(state, event.subsecond);
+                break;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+        case EVENT_ALARM_BUTTON_UP:
+            if (game_state.curr_screen == SCREEN_TITLE)
+                begin_playing(state);
+            else if (game_state.curr_screen == SCREEN_LOSE)
+                display_title(state);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (game_state.curr_screen == SCREEN_TITLE)
+                change_difficulty(state);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+        case EVENT_ALARM_BUTTON_DOWN:
+            if (game_state.curr_screen == SCREEN_PLAYING && game_state.jump_state == NOT_JUMPING){
+                if (game_state.fuel_mode && !game_state.fuel) break;
+                game_state.jump_state = JUMPING_START;
+                display_ball(game_state.jump_state != NOT_JUMPING);
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (game_state.curr_screen != SCREEN_PLAYING)
+                toggle_sound(state);
+            break;
+        case EVENT_TIMEOUT:
+            if (game_state.curr_screen != SCREEN_TITLE)
+                display_title(state);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            display_time(watch_rtc_get_date_time(), movement_clock_mode_24h());
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+    return true;
+}
+
+void endless_runner_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/complication/endless_runner_face.h b/legacy/watch_faces/complication/endless_runner_face.h
new file mode 100644 (file)
index 0000000..3cfa681
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 <David Volovskiy>
+ *
+ * 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 ENDLESS_RUNNER_FACE_H_
+#define ENDLESS_RUNNER_FACE_H_
+
+#include "movement.h"
+
+/*
+    ENDLESS_RUNNER face
+
+    This is a basic endless-runner, like the [Chrome Dino game](https://en.wikipedia.org/wiki/Dinosaur_Game).
+    On the title screen, you can select a difficulty by long-pressing LIGHT or toggle sound by long-pressing ALARM.
+    LED or ALARM are used to jump.
+    High-score is displayed on the top-right on the title screen. During a game, the current score is displayed.
+*/
+
+typedef struct {
+    uint16_t hi_score : 10;
+    uint8_t difficulty : 3;
+    uint8_t month_last_hi_score : 4;
+    uint8_t year_last_hi_score : 6;
+    uint8_t soundOn : 1;
+    /* 24 bits, likely aligned to 32 bits = 4 bytes */
+} endless_runner_state_t;
+
+void endless_runner_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void endless_runner_face_activate(void *context);
+bool endless_runner_face_loop(movement_event_t event, void *context);
+void endless_runner_face_resign(void *context);
+
+#define endless_runner_face ((const watch_face_t){ \
+    endless_runner_face_setup, \
+    endless_runner_face_activate, \
+    endless_runner_face_loop, \
+    endless_runner_face_resign, \
+    NULL, \
+})
+
+#endif // ENDLESS_RUNNER_FACE_H_
+
diff --git a/legacy/watch_faces/complication/flashlight_face.c b/legacy/watch_faces/complication/flashlight_face.c
new file mode 100644 (file)
index 0000000..10794de
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "flashlight_face.h"
+
+void flashlight_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(flashlight_state_t));
+        memset(*context_ptr, 0, sizeof(flashlight_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void flashlight_face_activate(void *context) {
+    (void) context;
+
+    HAL_GPIO_A2_out();
+    HAL_GPIO_A2_clr();
+}
+
+bool flashlight_face_loop(movement_event_t event, void *context) {
+    (void) context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_display_string("FL", 0);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            HAL_GPIO_A2_toggle();
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void flashlight_face_resign(void *context) {
+    (void) context;
+
+    HAL_GPIO_A2_clr();
+    HAL_GPIO_A2_off();
+}
diff --git a/legacy/watch_faces/complication/flashlight_face.h b/legacy/watch_faces/complication/flashlight_face.h
new file mode 100644 (file)
index 0000000..6fc7f8e
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 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 FLASHLIGHT_FACE_H_
+#define FLASHLIGHT_FACE_H_
+
+/*
+ * FLASHLIGHT face
+ *
+ * A flashlight for use with the Flashlight sensor board.
+ *
+ * When the watch face appears, the display will show "FL" in the top two positions.
+ * Pressing the Light button will toggle the flashlight on and off.
+ *
+ */
+
+#include "movement.h"
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint8_t unused;
+} flashlight_state_t;
+
+void flashlight_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void flashlight_face_activate(void *context);
+bool flashlight_face_loop(movement_event_t event, void *context);
+void flashlight_face_resign(void *context);
+
+#define flashlight_face ((const watch_face_t){ \
+    flashlight_face_setup, \
+    flashlight_face_activate, \
+    flashlight_face_loop, \
+    flashlight_face_resign, \
+    NULL, \
+})
+
+#endif // FLASHLIGHT_FACE_H_
+
diff --git a/legacy/watch_faces/complication/geomancy_face.c b/legacy/watch_faces/complication/geomancy_face.c
new file mode 100644 (file)
index 0000000..d507601
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "toss_up_face.h"
+#include "geomancy_face.h"
+
+// CONSTANTS //////////////////////////////////////////////////////////////////
+
+// The Bagua 八卦 Trigrams encoded as 3bit tribbles, represented as binary integer
+static const uint32_t bagua = 0b00000101001110010111011100000000;
+
+// The King Wen Sequence 文王卦序 of the I Ching 易經 Hexagrams 卦 encoded as an array
+// of decimal integers in the order of two combined Trigram tribbles from 0b000000 to 
+// 0b111111 
+static const uint8_t wen_order[] = {
+     1, 22,  7, 19, 15, 34, 44, 11, 
+    14, 51, 38, 52, 61, 55, 30, 32, 
+     6,  3, 28, 58, 39, 63, 46,  5, 
+    45, 17, 47, 56, 31, 49, 27, 43, 
+    23, 26,  2, 41, 50, 20, 16, 24, 
+    35, 21, 62, 36, 54, 29, 48, 12, 
+    18, 40, 59, 60, 53, 37, 57,  9, 
+    10, 25,  4,  8, 33, 13, 42,  0
+};
+
+// The geomantic figures encoded as 4 bit nibbles, represented as hexadecimal integer
+static const uint64_t geomantic = 0x4ABF39D25E76C180;
+
+// Abbreviations of the Names of the Geomantic Figures in the order of the 4 bit nibbles
+// from 0b0000 to 0b1111
+static const char figures[16][2] = {
+    "VI" /* Via */, "Hd" /* Head of the Dragon */, "PA" /* Puella */, "GF" /* Greater Fortune*/, 
+    "PR" /* Puer */, "AQ" /* Acquisitio */, "CA" /* Carcer */, "TR" /* Tristitia */,
+    "Td" /* Tail of the Dragon */, "CO" /* Conjunctio */, "AM" /* Amissio */, "AL" /* Albus */,
+    "LF" /* Lesser Fortune */, "RU" /* Rubeus */, "LA" /* Laetitia */, "PO" /* Populus */
+};
+
+// DECLARATIONS ///////////////////////////////////////////////////////////////
+
+static void geomancy_face_display(geomancy_state_t *state);
+static nibble_t _geomancy_pick_figure(void);
+static tribble_t _iching_pick_trigram(void);
+static uint8_t _iching_form_hexagram(void);
+static void _geomancy_display(nibble_t code);
+static void _display_hexagram(uint8_t hexagram, char* str);
+static void _fix_broken_line(uint8_t hexagram);
+static void _throw_animation(geomancy_state_t *state);
+
+// WATCH FACE FUNCTIONS ///////////////////////////////////////////////////////
+
+void geomancy_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(geomancy_state_t));
+        memset(*context_ptr, 0, sizeof(geomancy_state_t));
+    }
+}
+
+void geomancy_face_activate(void *context) {
+    (void) context;
+}
+
+bool geomancy_face_loop(movement_event_t event, void *context) {
+    geomancy_state_t *state = (geomancy_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            state->animate = false;
+            state->animation = 0;
+            watch_display_string("    IChing", 0);
+            break;
+        case EVENT_TICK:
+            if ( state->animate ) {
+                state->animation = (state->animation + 1) % 39;
+                geomancy_face_display(state);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if ( state->animate ) break;
+            if ( state->mode <= 1 ) state->mode = 2;
+            else if ( state->mode >= 2 ) state->mode = 0;
+            geomancy_face_display(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if ( state->animate ) break;
+            switch ( state->mode ) {
+                case 0:
+                    state->mode++;
+                    // fall through
+                case 1:
+                    state->animate = true;
+                    state->i_ching_hexagram = _iching_form_hexagram();
+                    break;
+                case 2:
+                    state->mode++;
+                    // fall through
+                case 3:
+                    state->animate = true;
+                    state->geomantic_figure = _geomancy_pick_figure().bits;
+                    break;
+                default:
+                    break;
+            }
+            geomancy_face_display(state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if ( state->animate ) break;
+            state->caption = !state->caption;
+            watch_display_string("    ", 0);
+            geomancy_face_display(state);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+    return true;
+}
+
+void geomancy_face_resign(void *context) {
+    (void) context;
+}
+
+// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
+
+/** @brief display handler */
+static void geomancy_face_display(geomancy_state_t *state) {
+    char token[7] = {0};
+    nibble_t figure = *((nibble_t*) &state->geomantic_figure);
+    switch ( state->mode ) {
+        case 0:
+            watch_display_string("    IChing", 0);
+            break;
+        case 1:
+            _throw_animation(state);
+            if ( !state->animate ) {
+                _display_hexagram(state->i_ching_hexagram, token);
+                watch_display_string(token, 4);
+                _fix_broken_line(state->i_ching_hexagram);
+                if (state->caption) {
+                    sprintf(token, "%2d", wen_order[state->i_ching_hexagram] + 1);
+                    watch_display_string(token, 2);
+                }
+            }
+            break;
+        case 2:
+            watch_display_string("    GeomCy", 0);
+            break;
+        case 3:
+            _throw_animation(state);
+            if ( !state->animate ) {
+                if ( state->caption ) {
+                    sprintf(token, "%c%c", figures[state->geomantic_figure][0], figures[state->geomantic_figure][1]);
+                    watch_display_string(token, 0);
+                }
+                _geomancy_display(figure);
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+/** @brief screen clearing animation between castings */
+static void _throw_animation(geomancy_state_t *state) {
+    movement_request_tick_frequency(16);
+    switch ( state->animation ) {
+        case 0:
+            watch_set_pixel(0, 22);
+            break;
+        case 1:
+            watch_set_pixel(2, 22);
+            watch_set_pixel(2, 23);
+            watch_clear_pixel(0, 22);
+            break;
+        case 2:
+            watch_set_pixel(1, 22);
+            watch_set_pixel(0, 23);
+            break;
+        case 3:
+            watch_set_pixel(2, 0);
+            watch_set_pixel(1, 0);
+            watch_set_pixel(2, 21);
+            watch_set_pixel(1, 21);
+            watch_clear_pixel(2, 22);
+            watch_clear_pixel(1, 22);
+            watch_clear_pixel(2, 23);
+            watch_clear_pixel(0, 23);
+            watch_clear_pixel(1, 23);
+            break;
+        case 4:
+            watch_set_pixel(1, 17);
+            watch_set_pixel(0, 20);
+            watch_set_pixel(2, 10);
+            watch_set_pixel(0, 1);
+            break;
+        case 5:
+            watch_clear_pixel(2, 21);
+            watch_clear_pixel(1, 21);
+            watch_clear_pixel(2, 0);
+            watch_clear_pixel(1, 0);
+            watch_clear_pixel(1, 20);
+            watch_clear_pixel(2, 20);
+            watch_clear_pixel(0, 21);
+            watch_clear_pixel(1, 1);
+            watch_clear_pixel(0, 0);
+            watch_clear_pixel(2, 1);
+            watch_set_pixel(2, 19);
+            watch_set_pixel(0, 19);
+            watch_set_pixel(1, 2);
+            watch_set_pixel(0, 2);
+            break;
+        case 6:
+            watch_clear_pixel(1, 17);
+            watch_clear_pixel(0, 20);
+            watch_clear_pixel(2, 10);
+            watch_clear_pixel(0, 1);
+            watch_set_pixel(2, 18);
+            watch_set_pixel(0, 18);
+            watch_set_pixel(2, 3);
+            watch_set_pixel(0, 4);
+            break;
+        case 7:
+            watch_clear_pixel(2, 19);
+            watch_clear_pixel(0, 19);
+            watch_clear_pixel(1, 18);
+            watch_clear_pixel(1, 19);
+            watch_clear_pixel(1, 2);
+            watch_clear_pixel(0, 2);
+            watch_clear_pixel(1, 3);
+            watch_clear_pixel(0, 3);
+            watch_clear_pixel(2, 2);
+            watch_set_pixel(1, 4);
+            watch_set_pixel(0, 5);
+            break;
+        case 8:
+            watch_clear_pixel(2, 18);
+            watch_clear_pixel(0, 18);
+            watch_clear_pixel(2, 3);
+            watch_clear_pixel(0, 4);
+            watch_set_pixel(2, 5);
+            watch_set_pixel(1, 6);
+            break;
+        case 9:
+            watch_clear_pixel(1, 4);
+            watch_clear_pixel(0, 5);
+            watch_clear_pixel(1, 5);
+            watch_clear_pixel(2, 4);
+            watch_clear_pixel(0, 6);
+            break;
+        case 10:
+            watch_clear_pixel(2, 5);
+            watch_clear_pixel(1, 6);
+            break;
+        case 11:
+            state->animate = false;
+            state->animation = 0;
+            movement_request_tick_frequency(1);
+            break;
+    }
+}
+
+// I CHING FUNCTIONS //////////////////////////////////////////////////////////
+
+/** @brief form a trigram from three random bit picks
+ */
+static tribble_t _iching_pick_trigram(void) {
+    uint8_t index = (divine_bit() << 2) | (divine_bit() << 1) | divine_bit();
+    tribble_t trigram = {(bagua >> (3 * index)) & 0b111};
+    return trigram;
+}
+
+/** @brief form a hexagram from two trigrams
+ */
+static uint8_t _iching_form_hexagram(void);
+static uint8_t _iching_form_hexagram(void) {
+    tribble_t inner = _iching_pick_trigram();
+    tribble_t outer = _iching_pick_trigram();
+    uint8_t hexagram = (inner.bits << 3) | outer.bits;
+    return hexagram;
+}
+
+/** @brief display hexagram
+ *  @details | for unbroken lines and Ξ for broken lines, left of display is bottom
+ */
+static void _display_hexagram(uint8_t hexagram, char* str);
+static void _display_hexagram(uint8_t hexagram, char* str) {
+    str[6] = '\0';  // Null-terminate the string
+    for (uint8_t i = 0; i < 6; i++) {
+        if (hexagram & (1 << (5 - i))) {
+            str[i] = '1';
+        } else {
+            str[i] = '=';
+        }
+    }
+}
+
+/** @brief when Ξ digits show as = then manually add a line on top
+ */
+static void _fix_broken_line(uint8_t hexagram) {
+    for (uint8_t i = 0; i < 6; i++) {
+        if (!(hexagram & (1 << (5 - i)))) {
+            if ( i == 1 ) watch_set_pixel(2, 20);
+            if ( i == 3 ) watch_set_pixel(2, 1);
+            if ( i == 4 ) watch_set_pixel(2, 2);
+            if ( i == 5 ) watch_set_pixel(2, 4);
+        }
+    }
+}
+
+// GEOMANCY FUNCTIONS /////////////////////////////////////////////////////////
+
+/** @brief choose a geomantic figure from four random bits
+ *  @details 0 represents · and 1 represents : counting from the bottom
+ */
+static nibble_t _geomancy_pick_figure(void);
+static nibble_t _geomancy_pick_figure(void) {
+    uint8_t index = (divine_bit() << 3) | (divine_bit() << 2) | (divine_bit() << 1) | divine_bit();
+    nibble_t figure = {(geomantic >> (4 * (15 - index))) & 0xF};
+    return figure;
+}
+
+/** @brief display the geomantic figure, left of display is bottom
+ */
+static void _geomancy_display(nibble_t code) {
+    // draw geomantic figures
+    bool row1 = (code.bits >> 3) & 1;
+    bool row2 = (code.bits >> 2) & 1;
+    bool row3 = (code.bits >> 1) & 1;
+    bool row4 = code.bits & 1;
+
+    if ( row1 ) watch_set_pixel(1, 18); else watch_set_pixel(1, 19);
+    if ( row2 ) { watch_set_pixel(2, 20); watch_set_pixel(0, 21);} else watch_set_pixel(1, 20);
+    if ( row3 ) watch_set_pixel(0, 22); else watch_set_pixel(1, 23);
+    if ( row4 ) { watch_set_pixel(2, 1); watch_set_pixel(0, 0);} else watch_set_pixel(1, 1);
+}
\ No newline at end of file
diff --git a/legacy/watch_faces/complication/geomancy_face.h b/legacy/watch_faces/complication/geomancy_face.h
new file mode 100644 (file)
index 0000000..c622bfc
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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 GEOMANCY_FACE_H_
+#define GEOMANCY_FACE_H_
+
+/*
+ * GEOMANCY watch face
+ *
+ * A simple and straightforward watch face for the ancient Eastern geomantic divination system
+ * of I Ching and the western system of "Geomancy". It is an optional addition to the Toss Up
+ * Face.
+ * 
+ * The LIGHT button toggles between the two systems of geomancy.
+ * 
+ * The ALARM button casts an I Ching hexagram or Geomantic figure based on drawing virtual
+ * stalks from the True Random Number Generator in the Sensor Watch.
+ * 
+ * The figures are flipped 90 degrees clockwise, so the left side is the bottom and the
+ * right side the top.
+ * 
+ * LONG PRESSING ALARM toggles the display of the King Wen sequence index for the cast I Ching
+ * Hexagram (https://en.wikipedia.org/wiki/King_Wen_sequence )or the abbreviated name for the
+ * cast Geomantic Figure:
+ * 
+ * GF - Greater Fortune (Fortuna Major)
+ * LF - Lesser Fortune (Fortuna Minor)
+ * PO - Populus
+ * VI - Via
+ * AL - Albus
+ * CO - Conjunctio
+ * PA - Puella
+ * AM - Amissio
+ * PR - Puer
+ * RU - Rubeus
+ * AQ - Acquisitio
+ * LA - Laetitia
+ * TR - Tristitia
+ * CA - Carcer
+ * HD - Head of the Dragon (Caput Draconis)
+ * TD - Tail of the Dragon (Cauda Draconis)
+ * 
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint8_t bits : 4;
+} nibble_t;
+
+typedef struct {
+    uint8_t bits : 3;
+} tribble_t;
+
+typedef struct {
+    uint8_t mode : 3;
+    uint8_t geomantic_figure;
+    uint8_t i_ching_hexagram : 6;
+    bool caption;
+    uint8_t animation;
+    bool animate;
+} geomancy_state_t;
+
+void geomancy_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void geomancy_face_activate(void *context);
+bool geomancy_face_loop(movement_event_t event, void *context);
+void geomancy_face_resign(void *context);
+
+#define geomancy_face ((const watch_face_t){ \
+    geomancy_face_setup, \
+    geomancy_face_activate, \
+    geomancy_face_loop, \
+    geomancy_face_resign, \
+    NULL, \
+})
+
+#endif // GEOMANCY_FACE_H_
+
diff --git a/legacy/watch_faces/complication/habit_face.c b/legacy/watch_faces/complication/habit_face.c
new file mode 100644 (file)
index 0000000..1045032
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 tslil clingman
+ *
+ * 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.
+ */
+
+#include "habit_face.h"
+#include "watch_private_display.h"
+#include "watch_rtc.h"
+#include "watch_slcd.h"
+#include "watch_utility.h"
+#include <stdlib.h>
+#include <string.h>
+
+static inline uint32_t today_unix(const uint32_t utc_offset) {
+  const watch_date_time_t dt = watch_rtc_get_date_time();
+  return watch_utility_convert_to_unix_time(dt.unit.year + 2020, dt.unit.month,
+                                            dt.unit.day, 0, 0, 0, utc_offset);
+}
+
+static inline uint32_t days_since_unix(const uint32_t since,
+                                       const uint32_t until) {
+  return (until - since) / (60 * 60 * 24);
+}
+
+typedef struct {
+  uint16_t total_count;
+  uint8_t lookback;
+  uint32_t last_update;
+  bool display_total;
+} habit_state_t;
+
+void habit_face_setup(uint8_t watch_face_index,
+                      void **context_ptr) {
+  (void)watch_face_index;
+  if (*context_ptr == NULL) {
+    *context_ptr = malloc(sizeof(habit_state_t));
+    memset(*context_ptr, 0, sizeof(habit_state_t));
+    habit_state_t *state = (habit_state_t *)*context_ptr;
+    state->lookback = 0;
+    state->last_update = watch_utility_offset_timestamp(
+        today_unix(movement_get_current_timezone_offset()), -24, 0, 0);
+  }
+}
+
+static inline void display_state(habit_state_t *state) {
+  const bool can_do = (state->lookback & 1) == 0;
+  char buf[16];
+
+  if (can_do) {
+    watch_clear_indicator(WATCH_INDICATOR_LAP);
+  } else {
+    watch_set_indicator(WATCH_INDICATOR_LAP);
+  }
+
+  if (state->display_total) {
+    sprintf(buf, "HA  %03dtot", state->total_count);
+    watch_display_string(buf, 0);
+  } else {
+    sprintf(buf, "HA      d%c", can_do ? 'o' : 'n');
+    uint8_t copy = state->lookback;
+    for (uint8_t c = 0; copy; copy >>= 2, c++) {
+      switch (copy & 3) {
+      case 0:
+        break;
+      case 1:
+        buf[4 + c] = 'I';
+        break;
+      case 2:
+        buf[4 + c] = '1';
+        break;
+      case 3:
+        buf[4 + c] = '|';
+        break;
+      }
+    }
+    watch_display_string(buf, 0);
+    if (can_do) {
+      const uint64_t segmap = Segment_Map[4] >> 48;
+      watch_set_pixel((segmap & 0xFF) >> 6, segmap & 0x3F);
+    }
+  }
+}
+
+void habit_face_activate(void *context) {
+  habit_state_t *state = (habit_state_t *)context;
+  display_state(state);
+}
+
+bool habit_face_loop(movement_event_t event,
+                     void *context) {
+  habit_state_t *state = (habit_state_t *)context;
+
+  const uint32_t today_now_unix = today_unix(movement_get_current_timezone_offset());
+  const bool can_do = (state->lookback & 1) == 0;
+
+  switch (event.event_type) {
+  case EVENT_ACTIVATE:
+  case EVENT_TICK: {
+    display_state(state);
+    if (today_now_unix > state->last_update) {
+      uint8_t num_shifts = days_since_unix(state->last_update, today_now_unix);
+      if (num_shifts > 7)
+        num_shifts = 7;
+      state->lookback <<= num_shifts;
+      state->last_update = today_now_unix;
+    }
+    break;
+  }
+  case EVENT_LIGHT_BUTTON_UP: {
+    state->display_total = !state->display_total;
+    display_state(state);
+    break;
+  }
+  case EVENT_ALARM_BUTTON_UP: {
+    if (can_do) {
+      state->lookback |= 1;
+      state->total_count++;
+      state->last_update = today_now_unix;
+      display_state(state);
+    };
+    break;
+  }
+  case EVENT_TIMEOUT: {
+    movement_move_to_face(0);
+    break;
+  }
+  default:
+    return movement_default_loop_handler(event);
+  }
+  return true;
+}
+
+void habit_face_resign(void *context) {
+  (void)context;
+}
diff --git a/legacy/watch_faces/complication/habit_face.h b/legacy/watch_faces/complication/habit_face.h
new file mode 100644 (file)
index 0000000..6170ee8
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 tslil clingman
+ *
+ * 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 HABIT_FACE_H_
+#define HABIT_FACE_H_
+
+/*
+ * Habit tracking face
+ *
+ * Allows the user to record a single succesful instance of a particular habit
+ * occuring per day, and displays history for eight days prior as well as a
+ * total counter.
+ *
+ */
+
+#include "movement.h"
+
+void habit_face_setup(uint8_t watch_face_index,
+                      void **context_ptr);
+void habit_face_activate(void *context);
+bool habit_face_loop(movement_event_t event, void *context);
+void habit_face_resign(void *context);
+
+#define habit_face ((const watch_face_t){ \
+    habit_face_setup, \
+    habit_face_activate, \
+    habit_face_loop, \
+    habit_face_resign, \
+    NULL, \
+})
+
+#endif // HABIT_FACE_H_
diff --git a/legacy/watch_faces/complication/higher_lower_game_face.c b/legacy/watch_faces/complication/higher_lower_game_face.c
new file mode 100755 (executable)
index 0000000..3491b5b
--- /dev/null
@@ -0,0 +1,393 @@
+/*\r
+ * MIT License\r
+ *\r
+ * Copyright (c) 2023 Chris Ellis\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ * of this software and associated documentation files (the "Software"), to deal\r
+ * in the Software without restriction, including without limitation the rights\r
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ * copies of the Software, and to permit persons to whom the Software is\r
+ * furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ */\r
+\r
+// Emulator only: need time() to seed the random number generator.\r
+#if __EMSCRIPTEN__\r
+#include <time.h>\r
+#endif\r
+\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include "higher_lower_game_face.h"\r
+#include "watch_private_display.h"\r
+\r
+#define TITLE_TEXT "Hi-Lo"\r
+#define GAME_BOARD_SIZE 6\r
+#define MAX_BOARDS 40\r
+#define GUESSES_PER_SCREEN 5\r
+#define WIN_SCORE (MAX_BOARDS * GUESSES_PER_SCREEN)\r
+#define STATUS_DISPLAY_START 0\r
+#define BOARD_SCORE_DISPLAY_START 2\r
+#define BOARD_DISPLAY_START 4\r
+#define BOARD_DISPLAY_END 9\r
+#define MIN_CARD_VALUE 2\r
+#define MAX_CARD_VALUE 14\r
+#define CARD_RANK_COUNT (MAX_CARD_VALUE - MIN_CARD_VALUE + 1)\r
+#define CARD_SUIT_COUNT 4\r
+#define DECK_SIZE (CARD_SUIT_COUNT * CARD_RANK_COUNT)\r
+#define FLIP_BOARD_DIRECTION false\r
+\r
+typedef struct card_t {\r
+    uint8_t value;\r
+    bool revealed;\r
+} card_t;\r
+\r
+typedef enum {\r
+    A, B, C, D, E, F, G\r
+} segment_t;\r
+\r
+typedef enum {\r
+    HL_GUESS_EQUAL,\r
+    HL_GUESS_HIGHER,\r
+    HL_GUESS_LOWER\r
+} guess_t;\r
+\r
+typedef enum {\r
+    HL_GS_TITLE_SCREEN,\r
+    HL_GS_GUESSING,\r
+    HL_GS_WIN,\r
+    HL_GS_LOSE,\r
+    HL_GS_SHOW_SCORE,\r
+} game_state_t;\r
+\r
+static game_state_t game_state = HL_GS_TITLE_SCREEN;\r
+static card_t game_board[GAME_BOARD_SIZE] = {0};\r
+static uint8_t guess_position = 0;\r
+static uint8_t score = 0;\r
+static uint8_t completed_board_count = 0;\r
+static uint8_t deck[DECK_SIZE] = {0};\r
+static uint8_t current_card = 0;\r
+\r
+static uint8_t generate_random_number(uint8_t num_values) {\r
+    // Emulator: use rand. Hardware: use arc4random.\r
+#if __EMSCRIPTEN__\r
+    return rand() % num_values;\r
+#else\r
+    return arc4random_uniform(num_values);\r
+#endif\r
+}\r
+\r
+static void stack_deck(void) {\r
+    for (size_t i = 0; i < CARD_RANK_COUNT; i++) {\r
+        for (size_t j = 0; j < CARD_SUIT_COUNT; j++)\r
+            deck[(i * CARD_SUIT_COUNT) + j] = MIN_CARD_VALUE + i;\r
+    }\r
+}\r
+\r
+static void shuffle_deck(void) {\r
+    // Randomize shuffle with Fisher Yates\r
+    size_t i;\r
+    size_t j;\r
+    uint8_t tmp;\r
+\r
+    for (i = DECK_SIZE - 1; i > 0; i--) {\r
+        j = generate_random_number(0xFF) % (i + 1);\r
+        tmp = deck[j];\r
+        deck[j] = deck[i];\r
+        deck[i] = tmp;\r
+    }\r
+}\r
+\r
+static void reset_deck(void) {\r
+    current_card = 0;\r
+    stack_deck();\r
+    shuffle_deck();\r
+}\r
+\r
+static uint8_t get_next_card(void) {\r
+    if (current_card >= DECK_SIZE)\r
+        reset_deck();\r
+    return deck[current_card++];\r
+}\r
+\r
+static void reset_board(bool first_round) {\r
+    // First card is random on the first board, and carried over from the last position on subsequent boards\r
+    const uint8_t first_card_value = first_round\r
+                                     ? get_next_card()\r
+                                     : game_board[GAME_BOARD_SIZE - 1].value;\r
+\r
+    game_board[0].value = first_card_value;\r
+    game_board[0].revealed = true; // Always reveal first card\r
+\r
+    // Fill remainder of board\r
+    for (size_t i = 1; i < GAME_BOARD_SIZE; ++i) {\r
+        game_board[i] = (card_t) {\r
+                .value = get_next_card(),\r
+                .revealed = false\r
+        };\r
+    }\r
+}\r
+\r
+static void init_game(void) {\r
+    watch_clear_display();\r
+    watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);\r
+    watch_display_string("GA", STATUS_DISPLAY_START);\r
+    reset_deck();\r
+    reset_board(true);\r
+    score = 0;\r
+    completed_board_count = 0;\r
+    guess_position = 1;\r
+}\r
+\r
+static void set_segment_at_position(segment_t segment, uint8_t position) {\r
+    const uint64_t position_segment_data = (Segment_Map[position] >> (8 * (uint8_t) segment)) & 0xFF;\r
+    const uint8_t com_pin = position_segment_data >> 6;\r
+    const uint8_t seg = position_segment_data & 0x3F;\r
+    watch_set_pixel(com_pin, seg);\r
+}\r
+\r
+static void render_board_position(size_t board_position) {\r
+    const size_t display_position = FLIP_BOARD_DIRECTION\r
+                                    ? BOARD_DISPLAY_START + board_position\r
+                                    : BOARD_DISPLAY_END - board_position;\r
+    const bool revealed = game_board[board_position].revealed;\r
+\r
+    //// Current position indicator spot\r
+    //if (board_position == guess_position) {\r
+    //    watch_display_character('-', display_position);\r
+    //    return;\r
+    //}\r
+\r
+    if (!revealed) {\r
+        // Higher or lower indicator (currently just an empty space)\r
+        watch_display_character(' ', display_position);\r
+        //set_segment_at_position(F, display_position);\r
+        return;\r
+    }\r
+\r
+    const uint8_t value = game_board[board_position].value;\r
+    switch (value) {\r
+        case 14: // A (≡)\r
+            watch_display_character(' ', display_position);\r
+            set_segment_at_position(A, display_position);\r
+            set_segment_at_position(D, display_position);\r
+            set_segment_at_position(G, display_position);\r
+            break;\r
+        case 13: // K (=)\r
+            watch_display_character(' ', display_position);\r
+            set_segment_at_position(A, display_position);\r
+            set_segment_at_position(D, display_position);\r
+            break;\r
+        case 12: // Q (-)\r
+            watch_display_character('-', display_position);\r
+            break;\r
+        default: {\r
+            const char display_char = (value - MIN_CARD_VALUE) + '0';\r
+            watch_display_character(display_char, display_position);\r
+        }\r
+    }\r
+}\r
+\r
+static void render_board(void) {\r
+    for (size_t i = 0; i < GAME_BOARD_SIZE; ++i) {\r
+        render_board_position(i);\r
+    }\r
+}\r
+\r
+static void render_board_count(void) {\r
+    // Render completed boards (screens)\r
+    char buf[3] = {0};\r
+    snprintf(buf, sizeof(buf), "%2hhu", completed_board_count);\r
+    watch_display_string(buf, BOARD_SCORE_DISPLAY_START);\r
+}\r
+\r
+static void render_final_score(void) {\r
+    watch_display_string("SC", STATUS_DISPLAY_START);\r
+    char buf[7] = {0};\r
+    const uint8_t complete_boards = score / GUESSES_PER_SCREEN;\r
+    snprintf(buf, sizeof(buf), "%2hu %03hu", complete_boards, score);\r
+    watch_set_colon();\r
+    watch_display_string(buf, BOARD_DISPLAY_START);\r
+}\r
+\r
+static guess_t get_answer(void) {\r
+    if (guess_position < 1 || guess_position > GAME_BOARD_SIZE)\r
+        return HL_GUESS_EQUAL; // Maybe add an error state, shouldn't ever hit this.\r
+\r
+    game_board[guess_position].revealed = true;\r
+    const uint8_t previous_value = game_board[guess_position - 1].value;\r
+    const uint8_t current_value = game_board[guess_position].value;\r
+\r
+    if (current_value > previous_value)\r
+        return HL_GUESS_HIGHER;\r
+    else if (current_value < previous_value)\r
+        return HL_GUESS_LOWER;\r
+    else\r
+        return HL_GUESS_EQUAL;\r
+}\r
+\r
+static void do_game_loop(guess_t user_guess) {\r
+    switch (game_state) {\r
+        case HL_GS_TITLE_SCREEN:\r
+            init_game();\r
+            render_board();\r
+            render_board_count();\r
+            game_state = HL_GS_GUESSING;\r
+            break;\r
+        case HL_GS_GUESSING: {\r
+            const guess_t answer = get_answer();\r
+\r
+            // Render answer indicator\r
+            switch (answer) {\r
+                case HL_GUESS_EQUAL:\r
+                    watch_display_string("==", STATUS_DISPLAY_START);\r
+                    break;\r
+                case HL_GUESS_HIGHER:\r
+                    watch_display_string("HI", STATUS_DISPLAY_START);\r
+                    break;\r
+                case HL_GUESS_LOWER:\r
+                    watch_display_string("LO", STATUS_DISPLAY_START);\r
+                    break;\r
+            }\r
+\r
+            // Scoring\r
+            if (answer == user_guess) {\r
+                score++;\r
+            } else if (answer == HL_GUESS_EQUAL) {\r
+                // No score for two consecutive identical cards\r
+            } else {\r
+                // Incorrect guess, game over\r
+                watch_display_string("GO", STATUS_DISPLAY_START);\r
+                game_board[guess_position].revealed = true;\r
+                render_board_position(guess_position);\r
+                game_state = HL_GS_LOSE;\r
+                return;\r
+            }\r
+\r
+            if (score >= WIN_SCORE) {\r
+                // Win, perhaps some kind of animation sequence?\r
+                watch_display_string("WI", STATUS_DISPLAY_START);\r
+                watch_display_string("  ", BOARD_SCORE_DISPLAY_START);\r
+                watch_display_string("------", BOARD_DISPLAY_START);\r
+                game_state = HL_GS_WIN;\r
+                return;\r
+            }\r
+\r
+            // Next guess position\r
+            const bool final_board_guess = guess_position == GAME_BOARD_SIZE - 1;\r
+            if (final_board_guess) {\r
+                // Seed new board\r
+                completed_board_count++;\r
+                render_board_count();\r
+                guess_position = 1;\r
+                reset_board(false);\r
+                render_board();\r
+            } else {\r
+                guess_position++;\r
+                render_board_position(guess_position - 1);\r
+                render_board_position(guess_position);\r
+            }\r
+            break;\r
+        }\r
+        case HL_GS_WIN:\r
+        case HL_GS_LOSE:\r
+            // Show score screen on button press from either state\r
+            watch_clear_display();\r
+            render_final_score();\r
+            game_state = HL_GS_SHOW_SCORE;\r
+            break;\r
+        case HL_GS_SHOW_SCORE:\r
+            watch_clear_display();\r
+            watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);\r
+            watch_display_string("GA", STATUS_DISPLAY_START);\r
+            game_state = HL_GS_TITLE_SCREEN;\r
+            break;\r
+        default:\r
+            watch_display_string("ERROR", BOARD_DISPLAY_START);\r
+            break;\r
+    }\r
+}\r
+\r
+static void light_button_handler(void) {\r
+    do_game_loop(HL_GUESS_HIGHER);\r
+}\r
+\r
+static void alarm_button_handler(void) {\r
+    do_game_loop(HL_GUESS_LOWER);\r
+}\r
+\r
+void higher_lower_game_face_setup(uint8_t watch_face_index, void **context_ptr) {\r
+    (void) watch_face_index;\r
+\r
+    if (*context_ptr == NULL) {\r
+        *context_ptr = malloc(sizeof(higher_lower_game_face_state_t));\r
+        memset(*context_ptr, 0, sizeof(higher_lower_game_face_state_t));\r
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.\r
+        memset(game_board, 0, sizeof(game_board));\r
+    }\r
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.\r
+}\r
+\r
+void higher_lower_game_face_activate(void *context) {\r
+    higher_lower_game_face_state_t *state = (higher_lower_game_face_state_t *) context;\r
+    (void) state;\r
+    // Handle any tasks related to your watch face coming on screen.\r
+    game_state = HL_GS_TITLE_SCREEN;\r
+}\r
+\r
+bool higher_lower_game_face_loop(movement_event_t event, void *context) {\r
+    higher_lower_game_face_state_t *state = (higher_lower_game_face_state_t *) context;\r
+    (void) state;\r
+\r
+    switch (event.event_type) {\r
+        case EVENT_ACTIVATE:\r
+            // Show your initial UI here.\r
+            watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);\r
+            watch_display_string("GA", STATUS_DISPLAY_START);\r
+            break;\r
+        case EVENT_TICK:\r
+            // If needed, update your display here.\r
+            break;\r
+        case EVENT_LIGHT_BUTTON_UP:\r
+            light_button_handler();\r
+            break;\r
+        case EVENT_LIGHT_BUTTON_DOWN:\r
+            // Don't trigger light\r
+            break;\r
+        case EVENT_ALARM_BUTTON_UP:\r
+            alarm_button_handler();\r
+            break;\r
+        case EVENT_TIMEOUT:\r
+            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,\r
+            // you may uncomment this line to move back to the first watch face in the list:\r
+            // movement_move_to_face(0);\r
+            break;\r
+        default:\r
+            return movement_default_loop_handler(event);\r
+    }\r
+\r
+    // return true if the watch can enter standby mode. Generally speaking, you should always return true.\r
+    // Exceptions:\r
+    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.\r
+    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.\r
+    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or\r
+    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.\r
+    return true;\r
+}\r
+\r
+void higher_lower_game_face_resign(void *context) {\r
+    (void) context;\r
+\r
+    // handle any cleanup before your watch face goes off-screen.\r
+}\r
diff --git a/legacy/watch_faces/complication/higher_lower_game_face.h b/legacy/watch_faces/complication/higher_lower_game_face.h
new file mode 100755 (executable)
index 0000000..51689b5
--- /dev/null
@@ -0,0 +1,106 @@
+/*\r
+ * MIT License\r
+ *\r
+ * Copyright (c) 2023 Chris Ellis\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ * of this software and associated documentation files (the "Software"), to deal\r
+ * in the Software without restriction, including without limitation the rights\r
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ * copies of the Software, and to permit persons to whom the Software is\r
+ * furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ */\r
+\r
+#ifndef HIGHER_LOWER_GAME_FACE_H_\r
+#define HIGHER_LOWER_GAME_FACE_H_\r
+\r
+#include "movement.h"\r
+\r
+/*\r
+ * Higher-Lower game face\r
+ * ======================\r
+ *\r
+ * A game face based on the "higher-lower" card game where the objective is to correctly guess if the next card will\r
+ * be higher or lower than the last revealed cards.\r
+ *\r
+ * Game Flow:\r
+ * - When the face is selected, the "Hi-Lo" "Title" screen will be displayed, and the status indicator will display "GA" for game\r
+ * - Pressing `ALARM` or `LIGHT` will start the game and proceed to the "Guessing" screen\r
+ *   - The first card will be revealed and the player must now make a guess\r
+ *   - A player can guess `Higher` by pressing the `LIGHT` button, and `Lower` by pressing the `ALARM` button\r
+ *   - The status indicator will show the result of the guess: HI (Higher), LO (Lower), or == (Equal)\r
+ *   - There are five guesses to make on each game screen, once the end of the screen is reached, a new screen\r
+ *     will be started, with the last revealed card carried over\r
+ *   - The number of completed screens is displayed in the top right (see Scoring)\r
+ * - If the player has guessed correctly, the score is updated and play continues (see Scoring)\r
+ * - If the player has guessed incorrectly, the status will change to GO (Game Over)\r
+ *   - The current card will be revealed\r
+ *   - Pressing `ALARM` or `LIGHT` will transition to the "Score" screen\r
+ * - If the game is won, the status indicator will display "WI" and the "Win" screen will be displayed\r
+ *   - Pressing `ALARM` or `LIGHT` will transition to the "Score" screen\r
+ * - The status indicator will change to "SC" when the final score is displayed\r
+ *   - The number of completed game screens will be displayed on using the first two digits\r
+ *   - The number of correct guesses will be displayed using the final three digits\r
+ *   - E.g. "13: 063" represents 13 completed screens, with 63 correct guesses\r
+ * - Pressing `ALARM` or `LIGHT` while on the "Score" screen will transition to back to the "Title" screen\r
+ *\r
+ * Scoring:\r
+ * - If the player guesses correctly (HI/LO) a point is gained\r
+ * - If the player guesses incorrectly the game ends\r
+ *   - Unless the revealed card is equal (==) to the last card, in which case play continues, but no point is gained\r
+ * - If the player completes 40 screens full of cards, the game ends and a win screen is displayed\r
+ *\r
+ * Misc:\r
+ * The face tries to remain true to the spirit of using "cards"; to cope with the display limitations I've arrived at\r
+ * the following mapping of card values to screen display, but am open to better suggestions:\r
+ *\r
+ * Thanks to voloved for adding deck shuffling and drawing!\r
+ *\r
+ * | Cards   |                          |\r
+ * |---------|--------------------------|\r
+ * | Value   |2|3|4|5|6|7|8|9|10|J|Q|K|A|\r
+ * | Display |0|1|2|3|4|5|6|7|8 |9|-|=|≡|\r
+ *\r
+ * A previous alternative can be found in the git history:\r
+ * | Cards   |                          |\r
+ * |---------|--------------------------|\r
+ * | Value   |2|3|4|5|6|7|8|9|10|J|Q|K|A|\r
+ * | Display |2|3|4|5|6|7|8|9| 0|-|=|≡|H|\r
+ *\r
+ *\r
+ * Future Ideas:\r
+ * - Add sounds\r
+ * - Save/Display high score\r
+ * - Add a "Win" animation\r
+ * - Consider using lap indicator for larger score limit\r
+ */\r
+\r
+typedef struct {\r
+    // Anything you need to keep track of, put it here!\r
+} higher_lower_game_face_state_t;\r
+\r
+void higher_lower_game_face_setup(uint8_t watch_face_index, void ** context_ptr);\r
+void higher_lower_game_face_activate(void *context);\r
+bool higher_lower_game_face_loop(movement_event_t event, void *context);\r
+void higher_lower_game_face_resign(void *context);\r
+\r
+#define higher_lower_game_face ((const watch_face_t){ \\r
+    higher_lower_game_face_setup, \\r
+    higher_lower_game_face_activate, \\r
+    higher_lower_game_face_loop, \\r
+    higher_lower_game_face_resign, \\r
+    NULL, \\r
+})\r
+\r
+#endif // HIGHER_LOWER_GAME_FACE_H_\r
diff --git a/legacy/watch_faces/complication/interval_face.c b/legacy/watch_faces/complication/interval_face.c
new file mode 100644 (file)
index 0000000..c354e72
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andreas Nebinger
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "interval_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_private_display.h"
+#include "watch_buzzer.h"
+
+typedef enum {
+    interval_setting_0_timer_idx,
+    interval_setting_1_clear_yn,
+    interval_setting_2_warmup_minutes,
+    interval_setting_3_warmup_seconds,
+    interval_setting_4_work_minutes,
+    interval_setting_5_work_seconds,
+    interval_setting_6_work_rounds,
+    interval_setting_7_break_minutes,
+    interval_setting_8_break_seconds,
+    interval_setting_9_full_rounds,
+    interval_setting_10_cooldown_minutes,
+    interval_setting_11_cooldown_seconds,
+    interval_setting_max
+} interval_setting_idx_t;
+
+#define INTERVAL_FACE_STATE_DEFAULT "IT"    // Interval Timer
+#define INTERVAL_FACE_STATE_WARMUP "PR"     // PRepare / warm up
+#define INTERVAL_FACE_STATE_WORK "WO"       // WOrk
+#define INTERVAL_FACE_STATE_BREAK "BR"      // BReak
+#define INTERVAL_FACE_STATE_COOLDOWN "CD"   // CoolDown
+
+// Define some default timer settings. Each timer is described in an array like this: 
+//      1. warm-up seconds,
+//      2. work time (seconds/minutes)
+//      3. break time (seconds/minutes)
+//      4. full rounds (0 = no limit)
+//      5. cooldown seconds
+// Work time and break time: positive number = seconds, negative number = minutes
+static const int8_t _default_timers[6][5] = {{0, 40, 20, 0, 0},
+                                            {0, 45, 15, 0, 0},
+                                            {10, 20, 10, 8, 10},
+                                            {0, 35, 0, 0, 0},
+                                            {0, -25, -5, 0, 0},
+                                            {0, -20, -5, 0, 0}};
+
+static const uint8_t _intro_segdata[4][2] = {{1, 8}, {0, 8}, {0, 7}, {1, 7}};
+static const uint8_t _blink_idx[] = {3, 9, 4, 6, 4, 6, 8, 4, 6, 8, 4, 6};
+static const uint8_t _setting_page_idx[] = {1, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4};
+static const int8_t _sound_seq_warmup[] = {BUZZER_NOTE_F6, 8, BUZZER_NOTE_REST, 1, -2, 3, 0};
+static const int8_t _sound_seq_work[] = {BUZZER_NOTE_F6, 8, BUZZER_NOTE_REST, 1, -2, 2, BUZZER_NOTE_C7, 24, 0};
+static const int8_t _sound_seq_break[] = {BUZZER_NOTE_B6, 15, BUZZER_NOTE_REST, 1, -2, 1, BUZZER_NOTE_B6, 16, 0};
+static const int8_t _sound_seq_cooldown[] = {BUZZER_NOTE_C7, 15, BUZZER_NOTE_REST, 1, -2, 1, BUZZER_NOTE_C7, 24, 0};
+static const int8_t _sound_seq_finish[] = {BUZZER_NOTE_C7, 6, BUZZER_NOTE_E7, 6, BUZZER_NOTE_G7, 6, BUZZER_NOTE_C8, 18, 0};
+
+static interval_setting_idx_t _setting_idx;
+static int8_t _ticks;
+static bool _erase_timer_flag;
+static uint32_t _target_ts;
+static uint32_t _now_ts;
+static uint32_t _paused_ts;
+static uint8_t _timer_work_round;
+static uint8_t _timer_full_round;
+static uint8_t _timer_run_state;
+
+static inline void _inc_uint8(uint8_t *value, uint8_t step, uint8_t max) {
+    *value += step;
+    if (*value >= max) *value = 0;
+}
+
+static uint32_t _get_now_ts() {
+    // returns the current date time as unix timestamp
+    watch_date_time_t now = watch_rtc_get_date_time();
+    return watch_utility_date_time_to_unix_time(now, 0);
+}
+
+static inline void _button_beep() {
+    // play a beep as confirmation for a button press (if applicable)
+    if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+}
+
+static void _timer_write_info(interval_face_state_t *state, char *buf, uint8_t timer_page) {
+    // fill display string with requested timer information
+    switch (timer_page) {
+    case 0:
+        // clear timer?
+        sprintf(buf, "%2s %1dCLEARn", INTERVAL_FACE_STATE_DEFAULT, state->timer_idx + 1);
+        if (_erase_timer_flag) buf[9] = 'y';
+        watch_clear_colon();
+        break;
+    case 1:
+        // warmup time info
+        sprintf(buf, "%2s %1d%02d%02d  ", INTERVAL_FACE_STATE_WARMUP, state->timer_idx + 1, 
+            state->timer[state->timer_idx].warmup_minutes, 
+            state->timer[state->timer_idx].warmup_seconds);
+        break;
+    case 2:
+        // work interval info
+        sprintf(buf, "%2s %1d%02d%02d%2d", INTERVAL_FACE_STATE_WORK, state->timer_idx + 1, 
+            state->timer[state->timer_idx].work_minutes, 
+            state->timer[state->timer_idx].work_seconds,
+            state->timer[state->timer_idx].work_rounds);
+        break;
+    case 3:
+        // break interval info
+        sprintf(buf, "%2s %1d%02d%02d%2d", INTERVAL_FACE_STATE_BREAK, state->timer_idx + 1, 
+            state->timer[state->timer_idx].break_minutes, 
+            state->timer[state->timer_idx].break_seconds,
+            state->timer[state->timer_idx].full_rounds);
+        if (!state->timer[state->timer_idx].full_rounds) buf[9] = '-';
+        break;
+    case 4:
+        // cooldown time info
+        sprintf(buf, "%2s %1d%02d%02d  ", INTERVAL_FACE_STATE_COOLDOWN ,state->timer_idx + 1, 
+            state->timer[state->timer_idx].cooldown_minutes, 
+            state->timer[state->timer_idx].cooldown_seconds);
+        break;
+    default:
+        break;
+    }
+}
+
+static void _face_draw(interval_face_state_t *state, uint8_t subsecond) {
+    // draws current face state
+    if (!state->is_active) return;
+    char buf[14];
+    buf[0] = 0;
+    uint8_t tmp;
+    if (state->face_state == interval_state_waiting && _ticks >= 0) {
+        // play info slideshow for current timer
+        int8_t ticks = _ticks % 12;
+        if (ticks == 0) {
+            if ((state->timer[state->timer_idx].warmup_minutes + state->timer[state->timer_idx].warmup_seconds) == 0) {
+                // skip warmup info if there is none for this timer
+                ticks = 3;
+                _ticks += 3;
+            }
+        }
+        tmp = ticks / 3 + 1;
+        _timer_write_info(state, buf, tmp);
+        // don't show '1 round' when displaying workout time to avoid detail overload
+        if (tmp == 2 && state->timer[state->timer_idx].work_rounds == 1) buf[9] = ' ';
+        // blink colon
+        if (subsecond % 2 == 0 && _ticks < 24) watch_clear_colon();
+        else watch_set_colon();
+    } else if (state->face_state == interval_state_setting) {
+        if (_setting_idx == interval_setting_0_timer_idx) {
+            if ((state->timer[state->timer_idx].warmup_minutes + state->timer[state->timer_idx].warmup_seconds) == 0)
+                tmp = 1;
+            else
+                tmp = 2;
+        } else {
+            tmp = _setting_page_idx[_setting_idx];
+        }
+        _timer_write_info(state, buf, tmp);
+        // blink at cursor position
+        if (subsecond % 2 && _ticks != -2) {
+            buf[_blink_idx[_setting_idx]] = ' ';
+            if (_blink_idx[_setting_idx] % 2 == 0) buf[_blink_idx[_setting_idx] + 1] = ' ';
+        }
+        // show lap indicator only when rounds are set
+        if (_setting_idx == interval_setting_6_work_rounds || _setting_idx == interval_setting_9_full_rounds)
+            watch_set_indicator(WATCH_INDICATOR_LAP);
+        else 
+            watch_clear_indicator(WATCH_INDICATOR_LAP);
+    } else if (state->face_state == interval_state_running || state->face_state == interval_state_pausing) {
+        tmp = _timer_full_round;
+        switch (_timer_run_state) {
+        case 0:
+            sprintf(buf, INTERVAL_FACE_STATE_WARMUP);
+            break;
+        case 1:
+            sprintf(buf, INTERVAL_FACE_STATE_WORK);
+            if (state->timer[state->timer_idx].work_rounds > 1) tmp = _timer_work_round;
+            break;
+        case 2:
+            sprintf(buf, INTERVAL_FACE_STATE_BREAK);
+            break;
+        case 3:
+            sprintf(buf, INTERVAL_FACE_STATE_COOLDOWN);
+            break;
+        default:
+            break;
+        }
+        div_t delta;
+        
+        if (state->face_state == interval_state_pausing) {
+            // pausing
+            delta = div(_target_ts - _paused_ts, 60);
+            // blink the bell icon
+            if (_now_ts % 2) watch_set_indicator(WATCH_INDICATOR_BELL);
+            else watch_clear_indicator(WATCH_INDICATOR_BELL);
+        } else
+            // running
+            delta = div(_target_ts - _now_ts, 60);
+        sprintf(&buf[2], " %1d%02d%02d%2d", state->timer_idx + 1, delta.quot, delta.rem, tmp + 1);
+    }
+    // write out to lcd
+    if (buf[0]) {
+        watch_display_character(buf[0], 0);
+        watch_display_character(buf[1], 1);
+        // set the bar for the i-like symbol on position 2
+        watch_set_pixel(2, 9);
+        // display the rest of the string
+        watch_display_string(&buf[3], 3);
+    }
+}
+
+static void _initiate_setting(interval_face_state_t *state, uint8_t subsecond) {
+    state->face_state = interval_state_setting;
+    _setting_idx = interval_setting_0_timer_idx;
+    _ticks = 0;
+    _erase_timer_flag = false;
+    watch_set_colon();
+    movement_request_tick_frequency(4);
+    _face_draw(state, subsecond);
+}
+
+static void _resume_setting(interval_face_state_t *state, uint8_t subsecond) {
+    state->face_state = interval_state_waiting;
+    _ticks = 0;
+    _face_draw(state, subsecond);
+    movement_request_tick_frequency(2);
+    watch_clear_indicator(WATCH_INDICATOR_LAP);
+}
+
+static void _abort_quick_ticks() {
+    if (_ticks == -2) {
+        _ticks = -1;
+        movement_request_tick_frequency(4);
+    }
+}
+
+static void _handle_alarm_button(interval_face_state_t *state) {
+    // handles the alarm button press and alters the corresponding timer settings
+    switch (_setting_idx) {
+    case interval_setting_0_timer_idx:
+        _inc_uint8(&state->timer_idx, 1, INTERVAL_TIMERS);
+        _erase_timer_flag = false;
+        break;
+    case interval_setting_1_clear_yn:
+        _erase_timer_flag ^= 1;
+        break;
+    case interval_setting_2_warmup_minutes:
+        _inc_uint8(&state->timer[state->timer_idx].warmup_minutes, 1, 60);
+        break;
+    case interval_setting_3_warmup_seconds:
+        _inc_uint8(&state->timer[state->timer_idx].warmup_seconds, 5, 60);
+        break;
+    case interval_setting_4_work_minutes:
+        _inc_uint8(&state->timer[state->timer_idx].work_minutes, 1, 60);
+        if (state->timer[state->timer_idx].work_rounds == 0) state->timer[state->timer_idx].work_rounds = 1;
+        break;
+    case interval_setting_5_work_seconds:
+        _inc_uint8(&state->timer[state->timer_idx].work_seconds, 5, 60);
+        if (state->timer[state->timer_idx].work_rounds == 0) state->timer[state->timer_idx].work_rounds = 1;
+        break;
+    case interval_setting_6_work_rounds:
+        _inc_uint8(&state->timer[state->timer_idx].work_rounds, 1, 100);
+        break;
+    case interval_setting_7_break_minutes:
+        _inc_uint8(&state->timer[state->timer_idx].break_minutes, 1, 60);
+        break;
+    case interval_setting_8_break_seconds:
+        _inc_uint8(&state->timer[state->timer_idx].break_seconds, 5, 60);
+        break;
+    case interval_setting_9_full_rounds:
+        _inc_uint8(&state->timer[state->timer_idx].full_rounds, 1, 100);
+        break;
+    case interval_setting_10_cooldown_minutes:
+        _inc_uint8(&state->timer[state->timer_idx].cooldown_minutes, 1, 60);
+        break;
+    case interval_setting_11_cooldown_seconds:
+        _inc_uint8(&state->timer[state->timer_idx].cooldown_seconds, 5, 60);
+        break;
+    default:
+        break;
+    }
+}
+
+static void _set_next_timestamp(interval_face_state_t *state) {
+    // set next timestamp for the running timer, set background task and pay sound sequence
+    uint16_t delta = 0;
+    int8_t *sound_seq;
+    interval_timer_setting_t timer = state->timer[state->timer_idx];
+    switch (_timer_run_state) {
+    case 0:
+        delta = timer.warmup_minutes * 60 + timer.warmup_seconds;
+        sound_seq = (int8_t *)_sound_seq_warmup;
+        break;
+    case 1:
+        delta = timer.work_minutes * 60 + timer.work_seconds;
+        sound_seq = (int8_t *)_sound_seq_work;
+        break;
+    case 2:
+        delta = timer.break_minutes * 60 + timer.break_seconds;
+        sound_seq = (int8_t *)_sound_seq_break;
+        break;
+    case 3:
+        delta = timer.cooldown_minutes * 60 + timer.cooldown_seconds;
+        sound_seq = (int8_t *)_sound_seq_cooldown;
+        break;
+    default:
+        sound_seq = NULL;
+        break;
+    }
+    // failsafe
+    if (delta <= 0) delta = 1;
+    _target_ts += delta;
+    // schedule next background task
+    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(_target_ts, 0);
+    movement_schedule_background_task_for_face(state->face_idx, target_dt);
+    // play sound
+    watch_buzzer_play_sequence(sound_seq, NULL);
+}
+
+static inline bool _is_timer_empty(interval_timer_setting_t *timer) {
+    // checks if a timer is empty
+    return (timer->warmup_minutes + timer->warmup_seconds 
+        + timer->work_minutes + timer->work_seconds 
+        + timer->break_minutes + timer->break_seconds
+        + timer->cooldown_minutes + timer->cooldown_seconds == 0);
+}
+
+static void _init_timer_info(interval_face_state_t *state) {
+    state->face_state = interval_state_waiting;
+    _ticks = 0;
+    if (state->is_active) movement_request_tick_frequency(2);
+}
+
+static void _abort_running_timer() {
+    _timer_work_round = _timer_full_round = 0;
+    _timer_run_state = 0;
+    movement_cancel_background_task();
+    watch_clear_indicator(WATCH_INDICATOR_BELL);
+    watch_buzzer_play_note(BUZZER_NOTE_C8, 100);
+}
+
+static void _resume_paused_timer(interval_face_state_t *state) {
+    // resume paused timer
+    _now_ts = _get_now_ts();
+    _target_ts += _now_ts - _paused_ts;
+    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(_target_ts, 0);
+    movement_schedule_background_task_for_face(state->face_idx, target_dt);
+    state->face_state = interval_state_running;
+    watch_set_indicator(WATCH_INDICATOR_BELL);
+}
+
+void interval_face_setup(uint8_t watch_face_index, void **context_ptr) {
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(interval_face_state_t));
+        interval_face_state_t *state = (interval_face_state_t *)*context_ptr;
+        memset(*context_ptr, 0, sizeof(interval_face_state_t));
+        state->face_idx = watch_face_index;
+        // somehow the memset above doesn't to the trick. So set the state explicitly
+        state->face_state = interval_state_waiting;
+        for (uint8_t i = 0; i < INTERVAL_TIMERS; i++) state->timer[i].work_rounds = 1;
+        // set up default timers
+        for (uint8_t i = 0; i < 6; i++) {
+            state->timer[i].warmup_seconds = _default_timers[i][0];
+            if (_default_timers[i][1] < 0) state->timer[i].work_minutes = -_default_timers[i][1];
+            else state->timer[i].work_seconds = _default_timers[i][1];
+            state->timer[i].work_rounds = 1;
+            if (_default_timers[i][2] < 0) state->timer[i].break_minutes = -_default_timers[i][2];
+            else state->timer[i].break_seconds = _default_timers[i][2];
+            state->timer[i].full_rounds = _default_timers[i][3];
+            state->timer[i].cooldown_seconds = _default_timers[i][4];
+        }
+    }
+}
+
+void interval_face_activate(void *context) {
+    interval_face_state_t *state = (interval_face_state_t *)context;
+    _erase_timer_flag = false;
+    state->is_active = true;
+    if (state->face_state <= interval_state_waiting) {
+        // initiate the intro loop
+        state->face_state = interval_state_intro;
+        _ticks = 0;
+        movement_request_tick_frequency(8);
+    } else watch_set_colon();
+}
+
+void interval_face_resign(void *context) {
+    interval_face_state_t *state = (interval_face_state_t *)context;
+    if (state->face_state <= interval_state_setting) state->face_state = interval_state_waiting;
+    watch_set_led_off();
+    movement_request_tick_frequency(1);
+    state->is_active = false;
+}
+
+bool interval_face_loop(movement_event_t event, void *context) {
+    interval_face_state_t *state = (interval_face_state_t *)context;
+    interval_timer_setting_t *timer = &state->timer[state->timer_idx];
+
+    switch (event.event_type) {
+    case EVENT_TICK:
+        if (state->face_state == interval_state_intro) {
+            // play intro animation so the wearer knows the face
+            if (_ticks == 4) {
+                // transition to default view of current interval slot
+                watch_set_colon();
+                _init_timer_info(state);
+                _face_draw(state, event.subsecond);
+                break;
+            } 
+            watch_set_pixel(_intro_segdata[_ticks][0], _intro_segdata[_ticks][1]);
+            _ticks++;
+        } else if (state->face_state == interval_state_waiting && _ticks >= 0) {
+            // play information slideshow for current interval timer
+            _ticks++;
+            if ((_ticks % 12 == 9) && (timer->cooldown_minutes + timer->cooldown_seconds == 0)) _ticks += 3;
+            if (_ticks > 24) _ticks = -1;
+            else _face_draw(state, event.subsecond);
+        } else if (state->face_state == interval_state_setting) {
+            if (_ticks == -2) {
+                // fast counting
+                _handle_alarm_button(state);
+            }
+            _face_draw(state, event.subsecond);
+        } else if (state->face_state == interval_state_running || state->face_state == interval_state_pausing) {
+            _now_ts = _get_now_ts();
+            _face_draw(state, event.subsecond);
+        }
+        break;
+    case EVENT_ACTIVATE:
+        watch_display_string(INTERVAL_FACE_STATE_DEFAULT, 0);
+        if (state->face_state) _face_draw(state, event.subsecond);
+        break;
+    case EVENT_LIGHT_BUTTON_UP:
+        if (state->face_state == interval_state_setting) {
+            if (_setting_idx == interval_setting_0_timer_idx) {
+                // skip clear page if timer is empty
+                if (_is_timer_empty(timer)) _setting_idx = interval_setting_1_clear_yn;
+            } else if (_setting_idx == interval_setting_1_clear_yn) {
+                watch_set_colon();
+                if (_erase_timer_flag) {
+                    // clear the current timer
+                    memset((void *)timer, 0, sizeof(interval_timer_setting_t));
+                    // play a short beep as confirmation
+                    watch_buzzer_play_note(BUZZER_NOTE_C8, 70);
+                }
+            } else if (_setting_idx == interval_setting_9_full_rounds && !timer->full_rounds) {
+                // skip cooldown if full rounds are not limited
+                _setting_idx = interval_setting_11_cooldown_seconds;
+            }
+            _setting_idx += 1;
+            if (_setting_idx == interval_setting_max) {
+                // we have done a full settings circle: resume setting
+                _resume_setting(state, event.subsecond);
+            } else 
+                _face_draw(state, event.subsecond);
+        } else {
+            movement_illuminate_led();
+        }
+        break;
+    case EVENT_LIGHT_LONG_PRESS:
+        _button_beep(settings);
+        if (state->face_state == interval_state_setting) {
+            _resume_setting(state, event.subsecond);
+        } else {
+            if (state->face_state >= interval_state_running ) _abort_running_timer();
+            _initiate_setting(state, event.subsecond);
+        }
+        break;
+    case EVENT_ALARM_BUTTON_UP:
+        switch (state->face_state) {
+        case interval_state_waiting:
+            // cycle through timers
+            _inc_uint8(&state->timer_idx, 1, INTERVAL_TIMERS);
+            _ticks = 0;
+            _face_draw(state, event.subsecond);
+            break;
+        case interval_state_setting:
+            // alter timer settings
+            _abort_quick_ticks();
+            _handle_alarm_button(state);
+            break;
+        case interval_state_running:
+            // pause timer
+            _button_beep(settings);
+            _paused_ts = _get_now_ts();
+            state->face_state = interval_state_pausing;
+            movement_cancel_background_task();
+            _face_draw(state, event.subsecond);
+            break;
+        case interval_state_pausing:
+            // resume paused timer
+            _button_beep(settings);
+            _resume_paused_timer(state);
+            _face_draw(state, event.subsecond);
+            break;
+        default:
+            break;
+        }
+        break;
+    case EVENT_ALARM_LONG_PRESS:
+        if (state->face_state == interval_state_setting && _setting_idx != interval_setting_1_clear_yn) {
+            // initiate quick counting
+            _ticks = -2;
+            movement_request_tick_frequency(8);
+            break;
+        } else if (state->face_state <= interval_state_waiting) {
+            if (_is_timer_empty(timer)) {
+                // jump back to timer #1
+                _button_beep(settings);
+                state->timer_idx = 0;
+                _init_timer_info(state);
+            } else {
+                // set initial state and start timer
+                _timer_work_round = _timer_full_round = 0;
+                if (timer->warmup_minutes + timer->warmup_seconds) _timer_run_state = 0;
+                else if (timer->work_minutes + timer->work_seconds) _timer_run_state = 1;
+                else if (timer->break_minutes + timer->break_seconds) _timer_run_state = 2;
+                else if (timer->cooldown_minutes + timer->cooldown_seconds) _timer_run_state = 3;
+                movement_request_tick_frequency(1);
+                _now_ts = _get_now_ts();
+                _target_ts = _now_ts;
+                _set_next_timestamp(state);
+                state->face_state = interval_state_running;
+                watch_set_indicator(WATCH_INDICATOR_BELL);
+                watch_set_colon();
+            }
+        } else if (state->face_state == interval_state_running) {
+            // stop the timer
+            _abort_running_timer();
+            _init_timer_info(state);
+        } else if (state->face_state == interval_state_pausing) {
+            // resume paused timer
+            _button_beep(settings);
+            _resume_paused_timer(state);
+        }
+        _face_draw(state, event.subsecond);
+        break;
+    case EVENT_ALARM_LONG_UP:
+        _abort_quick_ticks();
+        break;
+    case EVENT_BACKGROUND_TASK:
+        // find the next timestamp or end the timer
+        if (_timer_run_state == 0) {
+            // warmup finished
+            if (timer->work_minutes + timer->work_seconds) _timer_run_state = 1;
+            else if (timer->break_minutes + timer->break_seconds) _timer_run_state = 2;
+            else if (timer->cooldown_minutes + timer->cooldown_seconds) _timer_run_state = 3;
+            else _timer_run_state = 4;
+        } else if (_timer_run_state == 1) {
+            // work finished
+            _timer_work_round++;
+            if (_timer_work_round == timer->work_rounds) {
+                _timer_work_round = 0;
+                if (timer->break_minutes + timer->break_seconds && (timer->full_rounds == 0 
+                    || (timer->full_rounds && _timer_full_round + 1 < timer->full_rounds))) _timer_run_state = 2;
+                else {
+                    _timer_full_round++;
+                    if (timer->full_rounds && _timer_full_round == timer->full_rounds) {
+                        if (timer->cooldown_minutes + timer->cooldown_seconds) _timer_run_state = 3;
+                        else _timer_run_state = 4;
+                    } else _timer_run_state = 1;
+                }
+            }
+        } else if (_timer_run_state == 2) {
+            // break finished
+            _timer_full_round++;
+            _timer_work_round = 0;
+            if (timer->full_rounds && _timer_full_round == timer->full_rounds) {
+                if (timer->cooldown_minutes + timer->cooldown_seconds) _timer_run_state = 3;
+                else _timer_run_state = 4;
+                _timer_full_round--;
+            } else {
+                if (timer->work_minutes + timer->work_seconds) _timer_run_state = 1;
+            }
+        } else if (_timer_run_state == 3)
+            // cooldown finished
+            _timer_run_state = 4;
+        // set next timestamp or play final sound sequence
+        if (_timer_run_state < 4) {
+            // transition to next timer phase
+            _set_next_timestamp(state);
+        } else {
+            // timer has finished
+            state->face_state = interval_state_waiting;
+            _init_timer_info(state);
+            _face_draw(state, event.subsecond);
+            watch_buzzer_play_sequence((int8_t *)_sound_seq_finish, NULL);
+        }
+        break;
+    case EVENT_TIMEOUT:
+        if (state->face_state != interval_state_running) movement_move_to_face(0);
+        break;
+    case EVENT_LIGHT_BUTTON_DOWN:
+        // don't light up every time light is hit
+        break;
+    default:
+        movement_default_loop_handler(event);
+        break;
+    }
+    return true;
+}
diff --git a/legacy/watch_faces/complication/interval_face.h b/legacy/watch_faces/complication/interval_face.h
new file mode 100644 (file)
index 0000000..def944b
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andreas Nebinger
+ *
+ * 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 INTERVAL_FACE_H_
+#define INTERVAL_FACE_H_
+
+/*
+ * INTERVAL TIMER face
+ *
+ * This face brings 9 customizable interval timers to the sensor watch,
+ * to be used as hiit training device and/or for time management techniques.
+ *
+ * - There are 9 interval timer slots, you can cycle through these with the
+ *   alarm button (short press). For each timer slot, a short "slideshow"
+ *   displaying the relevant details (like length of each phase - see below)
+ *   is shown.
+ *
+ * - To start an interval timer, press and hold the alarm button.
+ *
+ * - To pause a running timer, press the alarm button (short press).
+ *
+ * - To completely abort a running timer, press and hold the alarm button.
+ *
+ * - Press and hold the light button to enter settings mode for each interval
+ *   timer slot.
+ *
+ * - Each interval timer has 1 to 4 phases of customizable length like so:
+ *   (1) prepare/warum up --> (2) work --> (3) break --> (4) cool down.
+ *   When setting up or running a timer, each of these phases is displayed by
+ *   the letters "PR" (prepare), "WO" (work), "BR" (break), "CD" (cool down).
+ *
+ * - Each of these phases is optional, you can set the corresponding
+ *   minutes and seconds to zero. But at least one phase needs to be set, if
+ *   you want to use the timer.
+ *
+ * - You can define the number of rounds either only for the work
+ *   phase and/or for the combination of work + break phase. Let's say you
+ *   want an interval timer that counts 3 rounds of 30 seconds work, 
+ *   followed by 20 seconds rest:
+ *         work 30s --> work 30s --> work 30s --> break 20s
+ *   You can do this by setting 30s for the "WO"rk phase and setting a 3
+ *   in the lower right hand corner of the work page. The "LAP" indicator
+ *   lights up at this position, to explain that we are setting laps here.
+ *   After that, set the "BR"eak phase to 20s and leave the rest as it is.
+ *
+ * - If you want to set up a certain number of "full rounds", consisting
+ *   of work phase(s) plus breaks, you can do so at the "BR"eak page. The
+ *   number in the lower right hand corner determines the number of full
+ *   rounds to be counted. A "-" means, that there is no limit and the 
+ *   timer keeps alternating between work and break phases.
+ *
+ * - This watch face comes with several pre-defined interval timers,
+ *   suitable for hiit training (timer slots 1 to 4) as well as doing
+ *   work according to the pomodoro principle (timer slots 5 to 6).
+ *   Feel free to adjust the timer slots to your own needs (or completely 
+ *   wipe them ;-)
+ */
+
+#include "movement.h"
+
+#define INTERVAL_TIMERS 9     // no of available customizable timers (be aware: only 4 bits reserved for this value in struct below)
+
+typedef struct {
+    uint8_t warmup_minutes;
+    uint8_t warmup_seconds;
+    uint8_t work_minutes;
+    uint8_t work_seconds;
+    uint8_t break_minutes;
+    uint8_t break_seconds;
+    uint8_t cooldown_minutes;
+    uint8_t cooldown_seconds;
+    uint8_t work_rounds;
+    uint8_t full_rounds;
+} interval_timer_setting_t;
+
+typedef enum {
+    interval_state_intro,
+    interval_state_waiting,
+    interval_state_setting,
+    interval_state_running,
+    interval_state_pausing
+} interval_timer_state_t;
+
+typedef struct {
+    bool is_active;
+    uint8_t face_idx;
+    uint8_t timer_idx;
+    uint8_t timer_running_idx;
+    interval_timer_state_t face_state;
+    interval_timer_setting_t timer[INTERVAL_TIMERS];
+} interval_face_state_t;
+
+void interval_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void interval_face_activate(void *context);
+bool interval_face_loop(movement_event_t event, void *context);
+void interval_face_resign(void *context);
+
+#define interval_face ((const watch_face_t) { \
+    interval_face_setup, \
+    interval_face_activate, \
+    interval_face_loop, \
+    interval_face_resign, \
+    NULL \
+})
+
+#endif // INTERVAL_FACE_H_
diff --git a/legacy/watch_faces/complication/invaders_face.c b/legacy/watch_faces/complication/invaders_face.c
new file mode 100644 (file)
index 0000000..935926a
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Andreas Nebinger
+ *
+ * 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.
+ */
+
+// Emulator only: need time() to seed the random number generator
+#if __EMSCRIPTEN__
+#include <time.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "watch_private_display.h"
+#include "invaders_face.h"
+
+#define INVADERS_FACE_WAVES_PER_STAGE 9 // number of waves per stage (there are two stages)
+#define INVADERS_FACE_WAVE_INVADERS 16  // number of invaders attacking per wave
+
+static const uint8_t _defense_lines_segdata[3][2] = {{2, 12}, {2, 11}, {0, 11}};
+static const uint8_t _bonus_points_segdata[4][2] = {{2, 7}, {2, 8}, {2, 9}, {0, 10}};
+static const uint8_t _bonus_points_helper[] = {1, 5, 9, 11, 15, 19, 21, 25, 29};
+
+static const int8_t _sound_seq_game_start[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 1, BUZZER_NOTE_REST, 10, BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 1, 0};
+static const int8_t _sound_seq_shot_hit[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 2, 0};
+static const int8_t _sound_seq_shot_miss[] = {BUZZER_NOTE_A7, 1, 0};
+static const int8_t _sound_seq_ufo_hit[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 2, -2, 1, 0};
+static const int8_t _sound_seq_def_gone[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 3, BUZZER_NOTE_REST, 40, BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 4, 0};
+static const int8_t _sound_seq_next_wave[] = {BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, BUZZER_NOTE_REST, 8, BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, -2, 1,
+                                              BUZZER_NOTE_REST, 32, 
+                                              BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, BUZZER_NOTE_REST, 8, BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, -2, 1, 0};
+static const int8_t _sound_seq_game_over[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 11, 0};
+
+typedef enum {
+    invaders_state_activated,
+    invaders_state_pre_game,
+    invaders_state_playing,
+    invaders_state_in_wave_break,
+    invaders_state_pre_next_wave,
+    invaders_state_next_wave,
+    invaders_state_game_over
+} invaders_current_state_t;
+
+typedef struct {
+    bool ufo_next : 1;                  // indicates whether next invader is a ufo
+    bool inv_checking : 1;              // flag to indicate whether we are currently moving invaders (to prevent race conditions)
+    bool suspend_buttons : 1;           // used while playing the game over sequence to prevent involuntary immediate restarts
+} invaders_signals_t;
+
+static int8_t _invaders[6];             // array of current invaders values (-1 = empty, 10 = ufo)
+static uint8_t _wave_invaders[INVADERS_FACE_WAVE_INVADERS]; // all invaders for the current wave. (Predefined to save cpu cycles when playing.)
+static invaders_current_state_t _current_state;
+static uint8_t _defense_lines;          // number of defense lines which have been broken in the current wave
+static uint8_t _aim;                    // current "aim" digit
+static uint8_t _invader_idx;            // index of next invader attacking in current wave (0 to 15)
+static uint8_t _wave_position;          // current position of first invader. When > 6 the defense is broken
+static uint8_t _wave_tick_freq;         // number of ticks passing until the next invader is inserted
+static uint8_t _ticks;                  // counts the ticks
+static uint8_t _bonus_countdown;        // ticks countdown until the bonus point indicator is cleared
+static uint8_t _waves;                  // counts the waves (_wave_tick_freq decreases slowly depending on _wave value)
+static uint8_t _shots_in_wave;          // number of shots in current wave. If 30 is reached, the game is over
+static uint8_t _invaders_shot;          // number of sucessfully shot invaders in current wave
+static uint8_t _invaders_shot_sum;      // current sum of invader digits shot (needed to determine if a ufo is coming)
+static invaders_signals_t _signals;     // holds severals flags
+static uint16_t _score;                 // score of the current game
+
+/// @brief return a random number. 0 <= return_value < num_values
+static inline uint8_t _get_rand_num(uint8_t num_values) {
+#if __EMSCRIPTEN__
+    return rand() % num_values;
+#else
+    return arc4random_uniform(num_values);
+#endif
+}
+
+/// @brief callback function to re-enable light and alarm buttons after playing a sound sequence
+static inline void _resume_buttons() {
+    _signals.suspend_buttons = false;
+}
+
+/// @brief play a sound sequence if the game is in beepy mode
+static inline void _play_sequence(invaders_state_t *state, int8_t *sequence) {
+    if (state->sound_on) watch_buzzer_play_sequence((int8_t *)sequence, NULL);
+}
+
+/// @brief draw the remaining defense lines
+static void _display_defense_lines() {
+    watch_display_character(' ', 1);
+    for (uint8_t i = 0; i < 3 - _defense_lines; i++) watch_set_pixel(_defense_lines_segdata[i][0], _defense_lines_segdata[i][1]);
+}
+
+/** @brief draw label followed by the given score value 
+ * @param label string displayed in the upper left corner
+ * @param score score to display
+ */
+static void _display_score(char *label, uint16_t score) {
+    watch_display_character(label[0], 0);
+    watch_display_character(label[1], 1);
+    char buf[10];
+    sprintf(buf, "  %06d", (score * 10));
+    watch_display_string(buf, 2);
+}
+
+/// @brief draw an invader at the given position
+static inline void _display_invader(int8_t invader, uint8_t position) {
+    switch (invader) {
+    case 10:
+        watch_display_character('n', position);
+        break;
+    case -1:
+        watch_display_character(' ', position);
+        break;
+    default:
+        watch_display_character(invader + 48, position);
+        break;
+    }
+}
+
+/// @brief game over: show score and set state
+static void _game_over(invaders_state_t *state) {
+    _display_score("GO", _score);
+    _current_state = invaders_state_game_over;
+    movement_request_tick_frequency(1);
+    _signals.suspend_buttons = true;
+    if (state->sound_on) watch_buzzer_play_sequence((int8_t *)_sound_seq_game_over, _resume_buttons);
+    // save current score to highscore, if applicable
+    if (_score > state->highscore) state->highscore = _score;
+}
+
+/// @brief initialize the current wave
+static void _init_wave() {
+    uint8_t i;
+    if (_current_state == invaders_state_in_wave_break) {
+        _invader_idx = _invaders_shot;
+    } else {
+        _invader_idx = _invaders_shot = _invaders_shot_sum = _defense_lines = _shots_in_wave = 0;
+    }
+    // pre-fill invaders
+    for (i = _invader_idx; i < INVADERS_FACE_WAVE_INVADERS; i++) _wave_invaders[i] = _get_rand_num(10);
+    // init invaders field
+    for (i = 1; i < 6; i++) _invaders[i] = -1;
+    _invaders[0] = _wave_invaders[_invader_idx];
+    _wave_position = _aim = _bonus_countdown = 0;
+    _signals.ufo_next = _signals.inv_checking = _signals.suspend_buttons = false;
+    _current_state = invaders_state_playing;
+    // determine wave speed
+    _wave_tick_freq = 6 - ((_waves % INVADERS_FACE_WAVES_PER_STAGE) + 1) / 2;
+    if (_waves >= INVADERS_FACE_WAVES_PER_STAGE) _wave_tick_freq--;
+    // clear display
+    watch_display_string("        ", 2);
+    watch_display_character('0', 0);
+    _display_defense_lines();
+    // draw first invader
+    watch_display_character(_wave_invaders[_invader_idx] + 48, 9);
+}
+
+/** @brief move invaders and add a new one, if necessary
+ * @returns true, if invaders have reached position 6, false otherwise
+ */
+static bool _move_invaders() {
+    if (_wave_position == 5) return true;
+    _signals.inv_checking = true;
+    if (_invaders[_wave_position] >= 0) _wave_position++;
+    int8_t i;
+    // move invaders
+    for (i = _wave_position; i > 0; i--) _invaders[i] = _invaders[i - 1];
+    if (_invader_idx < INVADERS_FACE_WAVE_INVADERS - 1) {
+        // add invader
+        _invader_idx++;
+        if (_signals.ufo_next) {
+            _invaders[0] = 10;
+            _signals.ufo_next = false;
+        } else {
+            _invaders[0] = _wave_invaders[_invader_idx];
+        }
+    } else {
+        // just add an empty invader slot
+        _invaders[0] = -1;
+    }
+    // update display
+    for (i = 0; i <= _wave_position; i++) {
+        _display_invader(_invaders[i], 9 - i);
+    }
+    _signals.inv_checking = false;
+    return false;
+}
+
+void invaders_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(invaders_state_t));
+        memset(*context_ptr, 0, sizeof(invaders_state_t));
+        invaders_state_t *state = (invaders_state_t *)*context_ptr;
+        // default: sound on
+        state->sound_on = true;
+    }
+#if __EMSCRIPTEN__
+    // simulator only: seed the randon number generator
+    time_t t;
+    srand((unsigned) time(&t));
+#endif
+}
+
+void invaders_face_activate(void *context) {
+    (void) context;
+    _current_state = invaders_state_activated;
+    _signals.suspend_buttons = false;
+}
+
+bool invaders_face_loop(movement_event_t event, void *context) {
+    invaders_state_t *state = (invaders_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // show highscore
+            _display_score("GA", state->highscore);
+            break;
+        case EVENT_TICK:
+            _ticks++;
+            switch (_current_state) {
+                case invaders_state_in_wave_break:
+                case invaders_state_pre_game:
+                case invaders_state_next_wave:
+                    // wait 2 secs to start the first round
+                    if (_ticks >= 2) {
+                        _ticks = 0;
+                        _init_wave();
+                        _current_state = invaders_state_playing;
+                        movement_request_tick_frequency(4);
+                    }
+                    break;
+                case invaders_state_playing:
+                    // game is playing
+                    if (_ticks >= _wave_tick_freq) {
+                        _ticks = 0;
+                        if (_move_invaders()) {
+                            // invaders broke through
+                            if (_defense_lines < 2) {
+                                // start current wave over
+                                _defense_lines++;
+                                _display_defense_lines();
+                                _display_score("GA", _score);
+                                _current_state = invaders_state_in_wave_break;
+                                movement_request_tick_frequency(1);
+                                _play_sequence(state, (int8_t *)_sound_seq_def_gone);
+                            } else {
+                                // game over
+                                _game_over(state);
+                            }
+                        }
+                    }
+                    // handle bonus points indicators
+                    if (_bonus_countdown) {
+                        _bonus_countdown--;
+                        if (!_bonus_countdown) {
+                            watch_display_character(' ', 2);
+                            watch_display_character(' ', 3);
+                        }
+                    }
+                    break;
+                case invaders_state_pre_next_wave:
+                    if (_ticks >= 3) {
+                        // switch to next wave
+                        _ticks = 0;
+                        movement_request_tick_frequency(1);
+                        _display_score("GA", _score);
+                        watch_set_pixel(1, 9);
+                        watch_display_character((_waves % INVADERS_FACE_WAVES_PER_STAGE) + 49, 3);
+                        _current_state = invaders_state_next_wave;
+                        _waves++;
+                        if (_waves == INVADERS_FACE_WAVES_PER_STAGE * 2) _waves = 0;
+                        _play_sequence(state, (int8_t *)_sound_seq_next_wave);
+                    }
+                default:
+                    break;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            if (!_signals.suspend_buttons) {
+                if (_current_state == invaders_state_playing) {
+                    // cycle the aim
+                    _aim = (_aim + 1) % 11;
+                    _display_invader(_aim, 0);
+                } else if (_current_state == invaders_state_activated || _current_state == invaders_state_game_over) {
+                    // just illuminate the LED
+                    movement_illuminate_led();
+                }
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if ((_current_state == invaders_state_activated || _current_state == invaders_state_game_over) && !_signals.suspend_buttons) {
+                // switch between beepy and silent mode
+                state->sound_on = !state->sound_on;
+                watch_buzzer_play_note(BUZZER_NOTE_A7, state->sound_on ? 65 : 25);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+            if (!_signals.suspend_buttons) {
+                switch (_current_state) {
+                    case invaders_state_game_over:
+                    case invaders_state_activated:
+                        // initialize the game
+                        _waves = 0;
+                        _score = 0;
+                        movement_request_tick_frequency(1);
+                        _ticks = 0;
+                        _current_state = invaders_state_pre_game;
+                        _play_sequence(state, (int8_t *)_sound_seq_game_start);
+                        break;
+                    case invaders_state_playing: {
+                        // "shoot"
+                        _shots_in_wave++;
+                        if (_shots_in_wave == 30) {
+                            // max number of shots reached: game over
+                            _game_over(state);
+                        } else {
+                            // wait if we are currently deleting an invader
+                            while (_signals.inv_checking);
+                            // proceed
+                            _signals.inv_checking = true;
+                            bool skip = false;
+                            for (int8_t i = _wave_position; i >= 0 && !skip; i--) {
+                                // if (_invaders[i] == -1) break;
+                                if (_invaders[i] == _aim) {
+                                    // invader is shot
+                                    skip = true;
+                                    _invaders_shot++;
+                                    _play_sequence(state, _aim == 10 ? (int8_t *)_sound_seq_ufo_hit : (int8_t *)_sound_seq_shot_hit);
+                                    if (_invaders_shot == INVADERS_FACE_WAVE_INVADERS) {
+                                        // last invader shot: wave sucessfully completed
+                                        watch_display_character(' ', 9 - _wave_position);
+                                        _ticks = 0;
+                                        _current_state = invaders_state_pre_next_wave;
+                                        _signals.inv_checking = false;
+                                    } else {
+                                        // check for ufo appearance
+                                        if (_aim && _aim < 10) {
+                                            _invaders_shot_sum = (_invaders_shot_sum + _aim) % 10;
+                                            if (_invaders_shot_sum == 0) _signals.ufo_next = true;
+                                        }
+                                        // remove invader
+                                        if (_wave_position == 0 || i == 5) {
+                                            _invaders[i] = -1;
+                                        } else {
+                                            for (uint8_t j = i; j < _wave_position; j++) {
+                                                _invaders[j] = _invaders[j + 1];
+                                                _display_invader(_invaders[j], 9 - j);
+                                            }
+                                        }
+                                        watch_display_character(' ', 9 - _wave_position);
+                                        if (_wave_position) _wave_position--;
+                                        // update score
+                                        if (_aim == 10) {
+                                            // ufo shot. The original game uses a ridiculously complicated scoring system here...
+                                            uint8_t bonus_points = 0;
+                                            uint8_t j;
+                                            for (j = 0; j < sizeof(_bonus_points_helper) && !bonus_points; j++) {
+                                                if (_shots_in_wave == _bonus_points_helper[j]) {
+                                                    bonus_points = 30;
+                                                } else if (_shots_in_wave - 1 == _bonus_points_helper[j]) {
+                                                    bonus_points = 20;
+                                                }
+                                            }
+                                            if (!bonus_points) bonus_points = 10;
+                                            bonus_points += (6 - i);
+                                            if ((_waves >= INVADERS_FACE_WAVES_PER_STAGE) && i) bonus_points += (6 - i);
+                                            _score += bonus_points;
+                                            // represent bonus points by bars
+                                            for (j = 0; j < (bonus_points / 10); j++) watch_set_pixel(_bonus_points_segdata[j][0], _bonus_points_segdata[j][1]);
+                                            _bonus_countdown = 9;
+                                        } else {
+                                            // regular invader
+                                            _score += (6 - _wave_position) * (_waves >= INVADERS_FACE_WAVES_PER_STAGE ? 2 : 1);
+                                        }
+                                    }
+                                }
+                            }
+                            if (!skip) _play_sequence(state, (int8_t *)_sound_seq_shot_miss);
+                            _signals.inv_checking = false;
+                        }
+                        break;
+                    }    
+                    default:
+                        break;
+                }
+            }
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            // Movement's default loop handler will step in for any cases you don't handle above:
+            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
+            // You can override any of these behaviors by adding a case for these events to this switch statement.
+            return movement_default_loop_handler(event);
+    }
+
+    // return true if the watch can enter standby mode. Generally speaking, you should always return true.
+    // Exceptions:
+    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
+    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
+    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
+    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
+    return true;
+}
+
+void invaders_face_resign(void *context) {
+    (void) context;
+    _current_state = invaders_state_game_over;
+}
+
diff --git a/legacy/watch_faces/complication/invaders_face.h b/legacy/watch_faces/complication/invaders_face.h
new file mode 100644 (file)
index 0000000..8886286
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Andreas Nebinger
+ *
+ * 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 INVADERS_FACE_H_
+#define INVADERS_FACE_H_
+
+/*
+ * Remake of the "famous" Casio Number Invaders Game
+ *
+ * This is an authentic remake of the invaders game, found on the Casio
+ * calculator wristwatch CA-85 or CA-851. There were also some calculators
+ * sold with this game, like MG-880.
+ * 
+ * How to play:
+ * 
+ * Press the alarm button to start the game.
+ * "Invaders" (just digits, tbh) will start coming in from the right hand side.
+ * Press the light button to "aim". The digit on the top of the display cycles
+ * from 0 to 9. If your aiming digit is identical to one of the invaders,
+ * press the alarm button to "shoot". The corresponding invader will disappear.
+ * If the invaders reach beneath the very first position, you loose one defense 
+ * line. When all three defense lines are gone, the game is over.
+ * Also: If you shoot more than 29 times per round, you loose the game.
+ * Good to know: There are 16 invaders per wave. There is a short break between
+ * waves.
+ * 
+ * What are the "n" invaders? Ufos!
+ * 
+ * Whenever the sum of all invaders shot is divisible by 10 the next invader 
+ * will be an ufo, represented by the n-symbol. Shooting a ufo gets you extra
+ * points. Example: shoot 2, 5, 3 --> ufo next
+ * 
+ * As for points: the earlier you shoot an invader, the more points you get.
+ *
+ * Anything else? Long pressing the light button toggles sound on or off. (Not
+ * while playing.)
+ * 
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint16_t highscore;
+    bool sound_on;
+} invaders_state_t;
+
+void invaders_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void invaders_face_activate(void *context);
+bool invaders_face_loop(movement_event_t event, void *context);
+void invaders_face_resign(void *context);
+
+#define invaders_face ((const watch_face_t){ \
+    invaders_face_setup, \
+    invaders_face_activate, \
+    invaders_face_loop, \
+    invaders_face_resign, \
+    NULL, \
+})
+
+#endif // INVADERS_FACE_H_
+
diff --git a/legacy/watch_faces/complication/kitchen_conversions_face.c b/legacy/watch_faces/complication/kitchen_conversions_face.c
new file mode 100644 (file)
index 0000000..5c1d1dd
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 PrimmR
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "kitchen_conversions_face.h"
+
+typedef struct
+{
+    char name[6];          // Name to display on selection
+    double conv_factor_uk; // Unit as represented in base units (UK)
+    double conv_factor_us; // Unit as represented in base units (US)
+    int16_t linear_factor; // Addition of constant (For temperatures)
+} unit;
+
+#define TICK_FREQ 4
+
+#define MEASURES_COUNT 3 // Number of different measurement 'types'
+#define WEIGHT 0
+#define TEMP 1
+#define VOL 2
+
+// Names of measurements
+static char measures[MEASURES_COUNT][6] = {"WeIght", " Temp", " VOL"};
+
+// Number of items in each category
+#define WEIGHT_COUNT 4
+#define TEMP_COUNT 3
+#define VOL_COUNT 9
+const uint8_t units_count[4] = {WEIGHT_COUNT, TEMP_COUNT, VOL_COUNT};
+
+static const unit weights[WEIGHT_COUNT] = {
+    {" g", 1., 1., 0}, // BASE
+    {" kg", 1000., 1000, 0},
+    {"Ounce", 28.34952, 28.34952, 0},
+    {" Pound", 453.5924, 453.5924, 0},
+};
+
+static const unit temps[TEMP_COUNT] = {
+    {" # C", 1.8, 1.8, 32},
+    {" # F", 1., 1., 0}, // BASE
+    {"Gas Mk", 25., 25., 250},
+};
+
+static const unit vols[VOL_COUNT] = {
+    {"  n&L", 1., 1., 0}, // BASE (ml)
+    {"   L", 1000., 1000., 0},
+    {" Fl Oz", 28.41306, 29.57353, 0},
+    {" Tbsp", 17.75816, 14.78677, 0},
+    {" Tsp", 5.919388, 4.928922, 0},
+    {"  Cup", 284.1306, 236.5882, 0},
+    {" Pint", 568.2612, 473.1765, 0},
+    {" Quart", 1136.522, 946.353, 0},
+    {"Gallon", 4546.09, 3785.412, 0},
+};
+
+static int8_t calc_success_seq[5] = {BUZZER_NOTE_G6, 10, BUZZER_NOTE_C7, 10, 0};
+static int8_t calc_fail_seq[5] = {BUZZER_NOTE_C7, 10, BUZZER_NOTE_G6, 10, 0};
+
+// Resets all state variables to 0
+static void reset_state(kitchen_conversions_state_t *state)
+{
+    state->pg = measurement;
+    state->measurement_i = 0;
+    state->from_i = 0;
+    state->from_is_us = movement_use_imperial_units(); // If uses imperial, most likely to be US
+    state->to_i = 0;
+    state->to_is_us = movement_use_imperial_units();
+    state->selection_value = 0;
+    state->selection_index = 0;
+    state->light_held = false;
+}
+
+void kitchen_conversions_face_setup(uint8_t watch_face_index, void **context_ptr)
+{
+    (void)watch_face_index;
+    if (*context_ptr == NULL)
+    {
+        *context_ptr = malloc(sizeof(kitchen_conversions_state_t));
+        memset(*context_ptr, 0, sizeof(kitchen_conversions_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void kitchen_conversions_face_activate(void *context)
+{
+    kitchen_conversions_state_t *state = (kitchen_conversions_state_t *)context;
+
+    // Handle any tasks related to your watch face coming on screen.
+    movement_request_tick_frequency(TICK_FREQ);
+
+    reset_state(state, settings);
+}
+
+// Increments index pointer by 1, wrapping
+#define increment_wrapping(index, wrap) ({(index)++; index %= wrap; })
+
+static uint32_t pow_10(uint8_t n)
+{
+    uint32_t result = 1;
+    for (int i = 0; i < n; i++)
+    {
+        result *= 10;
+    }
+    return result;
+}
+
+// Returns correct list of units for the measurement index
+static unit *get_unit_list(uint8_t measurement_i)
+{
+    switch (measurement_i)
+    {
+    case WEIGHT:
+        return (unit *)weights;
+
+    case TEMP:
+        return (unit *)temps;
+
+    case VOL:
+        return (unit *)vols;
+
+    default:
+        return (unit *)weights;
+    }
+}
+
+// Increment digit by 1 in input (wraps)
+static void increment_input(kitchen_conversions_state_t *state)
+{
+    uint8_t digit = state->selection_value / pow_10(DISPLAY_DIGITS - 1 - state->selection_index) % 10;
+    if (digit != 9)
+    {
+        state->selection_value += pow_10(DISPLAY_DIGITS - 1 - state->selection_index);
+    }
+    else
+    {
+        state->selection_value -= 9 * pow_10(DISPLAY_DIGITS - 1 - state->selection_index);
+    }
+}
+
+// Displays the list of units in the selected category
+static void display_units(uint8_t measurement_i, uint8_t list_i)
+{
+    watch_display_string(get_unit_list(measurement_i)[list_i].name, 4);
+}
+
+static void display(kitchen_conversions_state_t *state, uint8_t subsec)
+{
+    watch_clear_display();
+
+    switch (state->pg)
+    {
+    case measurement:
+    {
+        watch_display_string("Un", 0);
+        watch_display_string(measures[state->measurement_i], 4);
+    }
+    break;
+
+    case from:
+        display_units(state->measurement_i, state->from_i);
+
+        // Display Fr if non-locale specific, else display locale and F
+        if (state->measurement_i == VOL)
+        {
+            watch_display_string("F", 3);
+
+            char *locale = state->from_is_us ? "A " : "GB";
+            watch_display_string(locale, 0);
+        }
+        else
+        {
+            watch_display_string("Fr", 0);
+        }
+
+        break;
+
+    case to:
+        display_units(state->measurement_i, state->to_i);
+
+        // Display To if non-locale specific, else display locale and T
+        if (state->measurement_i == VOL)
+        {
+            watch_display_string("T", 3);
+
+            char *locale = state->to_is_us ? "A " : "GB";
+            watch_display_string(locale, 0);
+        }
+        else
+        {
+            watch_display_string("To", 0);
+        }
+
+        break;
+
+    case input:
+    {
+        char buf[7];
+        sprintf(buf, "%06lu", state->selection_value);
+        watch_display_string(buf, 4);
+
+        // Only allow ints for Gas Mk
+        if (state->measurement_i == TEMP && state->from_i == 2)
+        {
+            watch_display_string("  ", 8);
+        }
+
+        // Blink digit (on & off) twice a second
+        if (subsec % 2)
+        {
+            watch_display_string(" ", 4 + state->selection_index);
+        }
+
+        watch_display_string("In", 0);
+    }
+    break;
+
+    case result:
+    {
+        unit froms = get_unit_list(state->measurement_i)[state->from_i];
+        unit tos = get_unit_list(state->measurement_i)[state->to_i];
+        // Chooses correct factor for locale
+        double f_conv_factor = state->from_is_us ? froms.conv_factor_us : froms.conv_factor_uk;
+        double t_conv_factor = state->to_is_us ? tos.conv_factor_us : tos.conv_factor_uk;
+        // Converts
+        double to_base = (state->selection_value * f_conv_factor) + 100 * froms.linear_factor;
+        double conversion = ((to_base - 100 * tos.linear_factor) / t_conv_factor);
+
+        // If number too large or too small
+        uint8_t lower_bound = (state->measurement_i == TEMP && state->to_i == 2) ? 100 : 0;
+        if (conversion >= 1000000 || conversion < lower_bound)
+        {
+            watch_set_indicator(WATCH_INDICATOR_BELL);
+            watch_display_string("Err", 5);
+
+            if (movement_button_should_sound())
+                watch_buzzer_play_sequence(calc_fail_seq, NULL);
+        }
+        else
+        {
+            uint32_t rounded = conversion + .5;
+            char buf[7];
+            sprintf(buf, "%6lu", rounded);
+            watch_display_string(buf, 4);
+
+            // Make sure LSDs always filled
+            if (rounded < 10)
+            {
+                watch_display_string("00", 7);
+            }
+            else if (rounded < 100)
+            {
+                watch_display_string("0", 7);
+            }
+
+            if (movement_button_should_sound())
+                watch_buzzer_play_sequence(calc_success_seq, NULL);
+        }
+        watch_display_string("=", 1);
+    }
+
+    break;
+
+    default:
+        break;
+    }
+}
+
+bool kitchen_conversions_face_loop(movement_event_t event, void *context)
+{
+    kitchen_conversions_state_t *state = (kitchen_conversions_state_t *)context;
+
+    switch (event.event_type)
+    {
+    case EVENT_ACTIVATE:
+        // Initial UI
+        display(state, settings, event.subsecond);
+        break;
+    case EVENT_TICK:
+        // Update for blink animation on input
+        if (state->pg == input)
+        {
+            display(state, settings, event.subsecond);
+
+            // Increments input twice a second when light button held
+            if (state->light_held && event.subsecond % 2)
+                increment_input(state);
+        }
+        break;
+    case EVENT_LIGHT_BUTTON_UP:
+        // Cycles options
+        switch (state->pg)
+        {
+        case measurement:
+            increment_wrapping(state->measurement_i, MEASURES_COUNT);
+            break;
+
+        case from:
+            increment_wrapping(state->from_i, units_count[state->measurement_i]);
+            break;
+
+        case to:
+            increment_wrapping(state->to_i, units_count[state->measurement_i]);
+            break;
+
+        case input:
+            increment_input(state);
+            break;
+
+        default:
+            break;
+        }
+
+        // Light button does nothing on final screen
+        if (state->pg != result)
+            display(state, settings, event.subsecond);
+
+        state->light_held = false;
+
+        break;
+
+    case EVENT_ALARM_BUTTON_UP:
+        // Increments selected digit
+        if (state->pg == input)
+        {
+
+            // Moves between digits in input
+            // Wraps at 6 digits unless gas mark selected
+            if (state->selection_index < (DISPLAY_DIGITS - 1) - 2 * (state->measurement_i == TEMP && state->from_i == 2))
+            {
+                state->selection_index++;
+            }
+            else
+            {
+                state->pg++;
+                display(state, settings, event.subsecond);
+            }
+        }
+        // Moves forward 1 page
+        else
+        {
+            if (state->pg == SCREEN_NUM - 1)
+            {
+                reset_state(state, settings);
+            }
+            else
+            {
+                state->pg++;
+            }
+
+            // Play boop
+            if (movement_button_should_sound())
+                watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+        }
+
+        display(state, settings, event.subsecond);
+
+        state->light_held = false;
+
+        break;
+
+    case EVENT_ALARM_LONG_PRESS:
+        // Moves backwards through pages, resetting certain values
+        if (state->pg != measurement)
+        {
+            switch (state->pg)
+            {
+            case measurement:
+                state->measurement_i = 0;
+                break;
+
+            case from:
+                state->from_i = 0;
+                state->from_is_us = movement_use_imperial_units();
+                break;
+
+            case to:
+                state->to_i = 0;
+                state->to_is_us = movement_use_imperial_units();
+                break;
+
+            case input:
+                state->selection_index = 0;
+                state->selection_value = 0;
+                break;
+
+            case result:
+                state->selection_index = 0;
+                break;
+
+            default:
+                break;
+            }
+
+            state->pg--;
+            display(state, settings, event.subsecond);
+
+            // Play beep
+            if (movement_button_should_sound())
+                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+
+            state->light_held = false;
+        }
+        break;
+
+    case EVENT_LIGHT_LONG_PRESS:
+        // Switch between locales
+        if (state->measurement_i == VOL)
+        {
+            if (state->pg == from)
+            {
+                state->from_is_us = !state->from_is_us;
+            }
+            else if (state->pg == to)
+            {
+                state->to_is_us = !state->to_is_us;
+            }
+
+            if (state->pg == from || state->pg == to)
+            {
+                display(state, settings, event.subsecond);
+
+                // Play bleep
+                if (movement_button_should_sound())
+                    watch_buzzer_play_note(BUZZER_NOTE_E7, 50);
+            }
+        }
+
+        // Sets flag to increment input digit when light held
+        if (state->pg == input)
+            state->light_held = true;
+
+        break;
+
+    case EVENT_LIGHT_LONG_UP:
+        state->light_held = false;
+        break;
+
+    case EVENT_TIMEOUT:
+        movement_move_to_face(0);
+        break;
+
+    default:
+        return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void kitchen_conversions_face_resign(void *context)
+{
+    (void)context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
diff --git a/legacy/watch_faces/complication/kitchen_conversions_face.h b/legacy/watch_faces/complication/kitchen_conversions_face.h
new file mode 100644 (file)
index 0000000..33c6cf4
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 PrimmR
+ *
+ * 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 KITCHEN_CONVERSIONS_FACE_H_
+#define KITCHEN_CONVERSIONS_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Kitchen Conversions
+ * A face that allows the user to convert between common kitchen units of measurement
+ *
+ * How to use
+ * ----------
+ * Short press the alarm button to move forward through menus, and long press to move backwards
+ *
+ * Press the light button to cycle through options in the menus
+ *
+ * When inputting a number, the light button moves forward one place and the alarm button increments a digit by one
+ *
+ * To convert between Imperial (GB) and US (A) measurements of volume, hold the light button
+ *
+ */
+
+#define SCREEN_NUM 5
+
+// Names of each page
+typedef enum
+{
+    measurement,
+    from,
+    to,
+    input,
+    result,
+} page_t;
+
+#define DISPLAY_DIGITS 6
+
+// Settings when app is running
+typedef struct
+{
+    page_t pg;
+    uint8_t measurement_i;
+    uint8_t from_i;
+    bool from_is_us;
+    uint8_t to_i;
+    bool to_is_us;
+    uint32_t selection_value;
+    uint8_t selection_index;
+    bool light_held;
+} kitchen_conversions_state_t;
+
+void kitchen_conversions_face_setup(uint8_t watch_face_index, void **context_ptr);
+void kitchen_conversions_face_activate(void *context);
+bool kitchen_conversions_face_loop(movement_event_t event, void *context);
+void kitchen_conversions_face_resign(void *context);
+
+#define kitchen_conversions_face ((const watch_face_t){ \
+    kitchen_conversions_face_setup,                     \
+    kitchen_conversions_face_activate,                  \
+    kitchen_conversions_face_loop,                      \
+    kitchen_conversions_face_resign,                    \
+    NULL,                                               \
+})
+
+#endif // KITCHEN_CONVERSIONS_FACE_H_
diff --git a/legacy/watch_faces/complication/menstrual_cycle_face.c b/legacy/watch_faces/complication/menstrual_cycle_face.c
new file mode 100644 (file)
index 0000000..ccd678c
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Joseph Borne Komosa | @jokomo24
+ *
+ * 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.
+ *
+ * 
+ * Menstrual Cycle Face
+ * 
+ * Background:
+ * 
+ * I discovered the Casio F-91W through my partner, appreciated the retro aesthetic of the watch,
+ * and got one for myself. Soon afterward I discovered the Sensor Watch project and ordered two boards! 
+ * I introduced the Sensor Watch to my partner who inquired whether she could track her menstrual cycle.
+ * So I decided to implement a menstrual cycle watch face that also calculates the peak fertility window
+ * using The Calendar Method. While this information may be useful when attempting to achieve or avoid 
+ * pregnancy, it is important to understand that these are rough estimates at best.
+ * 
+ * How to use:
+ * 
+ * 1. To begin tracking, go to 'Last Period' page and toggle the alarm button to the number of days since 
+ *    the last, most recent, period and hold the alarm button to enter. This will perform the following actions:
+ *    - Store the corresponding date as the 'first' period in order to calculate the total_days_tracked.
+ *    - Turn on the Signal Indicator to signify that tracking has been activated.
+ *    - Deactivate this page and instead show the ticking animation.
+ *    - Adjust the days left in the 'Period in <num> Days' page accordingly.
+ *    - Activate the 'Period Is Here' page and no longer display 'NA'. To prevent accidental user entry,
+ *      the page will display the ticking animation until ten days have passed since the date of the last 
+ *      period entered.
+ *    - Activate the 'Peak Fertility' page to begin showing the estimated window,
+ *      as well as display the Alarm Indicator, on this page and on the main 'Period in <num> Days' page,
+ *      whenever the current date falls within the Peak Fertility Window.
+ * 
+ * 2. Toggle and enter 'y' in the 'Period Is Here' page on the day of every sequential period afterward. 
+ *    DO NOT FORGET TO DO SO!
+ *    - If forgotten, the data will become inaccurate and tracking will need to be reset! -> (FIXME, allow one to enter a 'missed' period using the 'Last Period' page).
+ *    This will perform the following actions:
+ *    - Calculate this completed cycle's length and reevaluate the shortest and longest cycle variables.
+ *    - Increment total_cycles by one.
+ *    - Recalculate and save the average cycle for 'Average Cycle' page.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "menstrual_cycle_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+#define TYPICAL_AVG_CYC 28
+#define SECONDS_PER_DAY 86400
+
+#define MENSTRUAL_CYCLE_FACE_NUM_PAGES (6)
+enum {
+    period_in_num_days,
+    average_cycle,
+    peak_fertility_window,
+    period_is_here,
+    first_period,
+    reset,
+} page_titles_e;
+const char menstrual_cycle_face_titles[MENSTRUAL_CYCLE_FACE_NUM_PAGES][11] = {
+    "Prin   day",   // Period In <num> Days: Estimated days till the next period occurs
+    "Av  cycle ",   // Average Cycle: The average number of days estimated per cycle
+    "Peak Fert ",   // Peak Fertility Window: The first and last day of month (displayed top & bottom right, respectively, once tracking) for the estimated window of fertility
+    "Prishere  ",   // Period Is Here: Toggle and enter 'y' on the day the actual period occurs to improve Avg and Fert estimations
+    "Last Per  ",   // Last Period: Enter the number of days since the last period to begin tracking from that corresponding date by storing it as the 'first'
+    "    Reset ",   // Reset: Toggle and enter 'y' to reset tracking data
+};
+
+/* Beep function */
+static inline void beep() {
+    if (movement_button_should_sound())
+        watch_buzzer_play_note(BUZZER_NOTE_E8, 75);
+}
+
+// Calculate the total number of days for which menstrual cycle tracking has been active
+static inline uint32_t total_days_tracked(menstrual_cycle_state_t *state) {
+
+    // If tracking has not yet been activated, return 0
+    if (!(state->dates.reg)) 
+        return 0;
+
+    // Otherwise, set the start date to the first day of the first tracked cycle
+    watch_date_time_t date_time_start;
+    date_time_start.unit.second = 0;
+    date_time_start.unit.minute = 0;
+    date_time_start.unit.hour = 0;
+    date_time_start.unit.day = state->dates.bit.first_day;
+    date_time_start.unit.month = state->dates.bit.first_month;
+    date_time_start.unit.year = state->dates.bit.first_year;
+
+    // Get the current date and time
+    watch_date_time_t date_time_now = watch_rtc_get_date_time();
+
+    // Convert the start date and current date to Unix time
+    uint32_t unix_start = watch_utility_date_time_to_unix_time(date_time_start, state->utc_offset);
+    uint32_t unix_now = watch_utility_date_time_to_unix_time(date_time_now, state->utc_offset);
+
+    // Calculate the total number of days and return it
+    return (unix_now - unix_start) / SECONDS_PER_DAY;
+}
+
+// Calculate the number of days until the next menstrual period
+static inline int8_t days_till_period(menstrual_cycle_state_t *state) {
+
+    // Calculate the number of days left until the next period based on the average cycle length and the number of cycles tracked
+    int8_t days_left = (state->cycles.bit.average_cycle * (state->cycles.bit.total_cycles + 1)) - total_days_tracked(state);
+
+    // If the result is negative, return 0 (i.e., the period is expected to start today or has already started)
+    return (days_left < 0) ? 0 : days_left;
+}
+
+static inline void reset_tracking(menstrual_cycle_state_t *state) {
+
+    state->dates.bit.first_day = 0;
+    state->dates.bit.first_month = 0;
+    state->dates.bit.first_year = 0;
+
+    state->dates.bit.prev_day = 0;
+    state->dates.bit.prev_month = 0;
+    state->dates.bit.prev_year = 0;
+
+    state->cycles.bit.shortest_cycle = TYPICAL_AVG_CYC;
+    state->cycles.bit.longest_cycle = TYPICAL_AVG_CYC;
+    state->cycles.bit.average_cycle = TYPICAL_AVG_CYC;
+    state->cycles.bit.total_cycles = 0;
+
+    state->dates.bit.reserved = 0;
+    state->cycles.bit.reserved = 0;
+
+    watch_store_backup_data(state->dates.reg, state->backup_register_dt);
+    watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
+
+    watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+}
+
+/*
+Fertility Window based on "The Calendar Method"
+Source: https://www.womenshealth.gov/pregnancy/you-get-pregnant/trying-conceive
+
+The Calendar Method has several steps:
+
+Step 1: Track the menstrual cycle for 8–12 months. One cycle is from the first day of one 
+        period until the first day of the next period. The average cycle is 28 days, but 
+        it may be as short as 24 days or as long as 38 days.
+Step 2: Subtract 18 from the number of days in the shortest menstrual cycle.
+Step 3: Subtract 11 from the number of days in the longest menstrual cycle.
+Step 4: Using a calendar, mark down the start of the next period (using previous instead). Count ahead by the number
+        of days calculated in step 2. This is when peak fertility begins. Peak fertility ends
+        at the number of days calculated in step 3.
+NOTE: Right now, the fertility window face displays its estimated window as soon as tracking is activated, although
+      it is important to keep in mind that The Calendar Method states that peak accuracy of the window will be 
+      reached only after at least 8 months of tracking the menstrual cycle (can make it so that it only displays
+      after total_days_tracked >= 8 months...but the info is interesting and should already be taken with the understanding that,
+      in general, it is a rough estimation at best).
+*/
+typedef enum Fertile_Window {first_day, last_day} fertile_window;
+// Calculate the predicted starting or ending day of peak fertility
+static inline uint32_t get_day_pk_fert(menstrual_cycle_state_t *state, fertile_window which_day) {
+
+    // Get the date of the previous period
+    watch_date_time_t date_prev_period;
+    date_prev_period.unit.second = 0;
+    date_prev_period.unit.minute = 0;
+    date_prev_period.unit.hour = 0;
+    date_prev_period.unit.day = state->dates.bit.prev_day;
+    date_prev_period.unit.month = state->dates.bit.prev_month;
+    date_prev_period.unit.year = state->dates.bit.prev_year;
+
+    // Convert the previous period date to Unix time
+    uint32_t unix_prev_period = watch_utility_date_time_to_unix_time(date_prev_period, state->utc_offset);
+
+    // Calculate the Unix time of the predicted peak fertility day based on the length of the shortest/longest cycle
+    uint32_t unix_pk_date;
+    switch(which_day) {
+        case first_day:
+            unix_pk_date = unix_prev_period + ((state->cycles.bit.shortest_cycle - 18) * SECONDS_PER_DAY);
+            break;
+        case last_day:
+            unix_pk_date = unix_prev_period + ((state->cycles.bit.longest_cycle - 11) * SECONDS_PER_DAY);
+            break;
+    }
+
+    // Convert the Unix time of the predicted peak fertility day to a date/time and return the day of the month
+    return watch_utility_date_time_from_unix_time(unix_pk_date, state->utc_offset).unit.day;
+}
+
+// Determine if today falls within the predicted peak fertility window
+static inline bool inside_fert_window(menstrual_cycle_state_t *state) {
+
+    // If tracking has not yet been activated, return false
+    if (!(state->dates.reg)) 
+        return false;
+
+    // Get the current date/time
+    watch_date_time_t date_time_now = watch_rtc_get_date_time();
+
+    // Check if the current day falls between the first and last predicted peak fertility days
+    if (get_day_pk_fert(state, first_day) > get_day_pk_fert(state, last_day)) { // We are crossing over the end of the month
+        if (date_time_now.unit.day >= get_day_pk_fert(state, first_day) ||
+            date_time_now.unit.day <= get_day_pk_fert(state, last_day))
+            return true;
+    }
+    else if (date_time_now.unit.day >= get_day_pk_fert(state, first_day) &&
+             date_time_now.unit.day <= get_day_pk_fert(state, last_day))
+             return true;
+    // If the current day does not fall within the predicted peak fertility window, return false
+    return false;
+}
+
+// Update the shortest and longest menstrual cycles based on the previous menstrual cycle
+static inline void update_shortest_longest_cycle(menstrual_cycle_state_t *state) {
+
+    // Get the date of the previous menstrual cycle
+    watch_date_time_t date_prev_period;
+    date_prev_period.unit.second = 0;
+    date_prev_period.unit.minute = 0;
+    date_prev_period.unit.hour = 0;
+    date_prev_period.unit.day = state->dates.bit.prev_day;
+    date_prev_period.unit.month = state->dates.bit.prev_month;
+    date_prev_period.unit.year = state->dates.bit.prev_year;
+
+    // Convert the date of the previous menstrual cycle to UNIX time
+    uint32_t unix_prev_period = watch_utility_date_time_to_unix_time(date_prev_period, state->utc_offset);
+
+    // Calculate the length of the current menstrual cycle
+    uint8_t cycle_length = total_days_tracked(state) - (unix_prev_period / SECONDS_PER_DAY);
+
+    // Update the shortest or longest cycle length if necessary
+    if (cycle_length < state->cycles.bit.shortest_cycle)
+        state->cycles.bit.shortest_cycle = cycle_length;
+    else if (cycle_length > state->cycles.bit.longest_cycle)
+        state->cycles.bit.longest_cycle = cycle_length;
+}
+
+void menstrual_cycle_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(menstrual_cycle_state_t));
+        memset(*context_ptr, 0, sizeof(menstrual_cycle_state_t));
+        menstrual_cycle_state_t *state = ((menstrual_cycle_state_t *)*context_ptr);
+
+        state->dates.bit.first_day = 0;
+        state->dates.bit.first_month = 0;
+        state->dates.bit.first_year = 0;
+
+        state->dates.bit.prev_day = 0;
+        state->dates.bit.prev_month = 0;
+        state->dates.bit.prev_year = 0;
+
+        state->cycles.bit.shortest_cycle = TYPICAL_AVG_CYC;
+        state->cycles.bit.longest_cycle = TYPICAL_AVG_CYC;
+        state->cycles.bit.average_cycle = TYPICAL_AVG_CYC;
+        state->cycles.bit.total_cycles = 0;
+
+        state->dates.bit.reserved = 0;
+        state->cycles.bit.reserved = 0;
+
+        state->backup_register_dt = 0;
+        state->backup_register_cy = 0;
+    }
+
+    menstrual_cycle_state_t *state = ((menstrual_cycle_state_t *)*context_ptr);
+    if (!(state->backup_register_dt && state->backup_register_cy)) {
+        state->backup_register_dt = movement_claim_backup_register();
+        state->backup_register_cy = movement_claim_backup_register();
+
+        if (state->backup_register_dt && state->backup_register_cy) {
+            watch_store_backup_data(state->dates.reg, state->backup_register_dt);
+            watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
+        }
+    }
+    else {
+        state->dates.reg = watch_get_backup_data(state->backup_register_dt);
+        state->cycles.reg = watch_get_backup_data(state->backup_register_cy);
+    }
+}
+
+void menstrual_cycle_face_activate(void *context) {
+    menstrual_cycle_state_t *state = (menstrual_cycle_state_t *)context;
+    state->period_today = 0;
+    state->current_page = 0;
+    state->reset_tracking = 0;
+    state->utc_offset = movement_get_current_timezone_offset();
+    movement_request_tick_frequency(4); // we need to manually blink some pixels
+}
+
+bool menstrual_cycle_face_loop(movement_event_t event, void *context) {
+    menstrual_cycle_state_t *state = (menstrual_cycle_state_t *)context;
+    watch_date_time_t date_period;
+    uint8_t current_page = state->current_page;
+    uint8_t first_day_fert;
+    uint8_t last_day_fert;
+    uint32_t unix_now;
+    uint32_t unix_prev_period;
+    switch (event.event_type) {
+        case EVENT_TICK:
+        case EVENT_ACTIVATE:
+            // Do nothing; handled below.
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            movement_move_to_next_face();
+            return false;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            current_page = (current_page + 1) % MENSTRUAL_CYCLE_FACE_NUM_PAGES;
+            state->current_page = current_page;
+            state->days_prev_period = 0;
+            watch_clear_indicator(WATCH_INDICATOR_BELL);
+            if (watch_sleep_animation_is_running())
+                watch_stop_sleep_animation();
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            switch (current_page) {
+                case period_in_num_days:
+                    break;
+                case average_cycle:
+                    break;
+                case peak_fertility_window:
+                    break;
+                case period_is_here:
+                    if (state->period_today && total_days_tracked(state)) {
+                        // Calculate before updating date of last period
+                        update_shortest_longest_cycle(state);
+                        // Update the date of last period after calulating the, now previous, cycle length
+                        date_period = watch_rtc_get_date_time();
+                        state->dates.bit.prev_day = date_period.unit.day;
+                        state->dates.bit.prev_month = date_period.unit.month;
+                        state->dates.bit.prev_year = date_period.unit.year;
+                        // Calculate new cycle average
+                        state->cycles.bit.total_cycles += 1;
+                        state->cycles.bit.average_cycle = total_days_tracked(state) / state->cycles.bit.total_cycles;
+                        // Store the new data
+                        watch_store_backup_data(state->dates.reg, state->backup_register_dt);
+                        watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
+                        state->period_today = !(state->period_today);
+                        beep(settings);
+                    }
+                    break;
+                case first_period:
+                    // If tracking has not yet been activated
+                    if (!(state->dates.reg)) {
+                        unix_now = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), state->utc_offset);
+                        unix_prev_period = unix_now - (state->days_prev_period * SECONDS_PER_DAY);
+                        date_period = watch_utility_date_time_from_unix_time(unix_prev_period, state->utc_offset);
+                        state->dates.bit.first_day = date_period.unit.day;
+                        state->dates.bit.first_month = date_period.unit.month;
+                        state->dates.bit.first_year = date_period.unit.year;
+                        state->dates.bit.prev_day = date_period.unit.day;
+                        state->dates.bit.prev_month = date_period.unit.month;
+                        state->dates.bit.prev_year = date_period.unit.year;
+                        watch_store_backup_data(state->dates.reg, state->backup_register_dt);
+                        beep(settings);
+                    }
+                    break;
+                case reset:
+                    if (state->reset_tracking) {
+                        reset_tracking(state);
+                        state->reset_tracking = !(state->reset_tracking);
+                        beep(settings);
+                    }
+                    break;
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch (current_page) {
+                case period_in_num_days:
+                    break;
+                case average_cycle:
+                    break;
+                case peak_fertility_window:
+                    break;
+                case period_is_here:
+                    if (total_days_tracked(state))
+                        state->period_today = !(state->period_today);
+                    break;
+                case first_period:
+                    if (!(state->dates.reg)) 
+                        state->days_prev_period = (state->days_prev_period > 99) ? 0 : state->days_prev_period + 1; // Cycle through pages to quickly reset to 0
+                    break;
+                case reset:
+                    state->reset_tracking = !(state->reset_tracking);
+                    break;
+            }
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    watch_display_string((char *)menstrual_cycle_face_titles[current_page], 0);
+    if (state->dates.reg)
+        watch_set_indicator(WATCH_INDICATOR_SIGNAL); // signal that we are now in a tracking state
+
+    char buf[13];
+    switch (current_page) {
+        case period_in_num_days:
+            sprintf(buf, "%2d", days_till_period(state));
+            if (inside_fert_window(state))
+                watch_set_indicator(WATCH_INDICATOR_BELL);
+            watch_display_string(buf, 4);
+            break;
+        case average_cycle:
+            sprintf(buf, "%2d", state->cycles.bit.average_cycle);
+            watch_display_string(buf, 2);
+            break;
+        case peak_fertility_window:
+            if (event.subsecond % 5 && state->dates.reg) { // blink active for 3 quarter-seconds
+                first_day_fert = get_day_pk_fert(state, first_day);
+                last_day_fert = get_day_pk_fert(state, last_day);
+                sprintf(buf, "Fr%2d To %2d", first_day_fert, last_day_fert); // From: first day | To: last day
+                if (inside_fert_window(state))
+                    watch_set_indicator(WATCH_INDICATOR_BELL);
+                watch_display_string(buf, 0);
+            }
+            break;
+        case period_is_here:
+            if (event.subsecond % 5) { // blink active for 3 quarter-seconds
+                if (!(state->dates.reg))
+                    watch_display_string("NA", 8); // Not Applicable: Do not allow period entry until tracking is activated...
+                else if (state->period_today) 
+                    watch_display_string("y", 9);
+                else 
+                    watch_display_string("n", 9);
+            }
+            break;
+        case first_period:
+            if (state->dates.reg) {
+                if (!watch_sleep_animation_is_running())
+                    watch_start_sleep_animation(500); // Tracking activated
+            }
+            else if (event.subsecond % 5) { // blink active for 3 quarter-seconds
+                sprintf(buf, "%2d", state->days_prev_period);
+                watch_display_string(buf, 8);
+            }
+            break;
+        case reset:
+            // blink active for 3 quarter-seconds
+            if (event.subsecond % 5 && state->reset_tracking)
+                watch_display_string("y", 9);
+            else if (event.subsecond % 5)
+                watch_display_string("n", 9);
+            break;
+    }
+    return true;
+}
+
+void menstrual_cycle_face_resign(void *context) {
+    (void) context;
+}
\ No newline at end of file
diff --git a/legacy/watch_faces/complication/menstrual_cycle_face.h b/legacy/watch_faces/complication/menstrual_cycle_face.h
new file mode 100644 (file)
index 0000000..5f0dedc
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Joseph Borne Komosa | @jokomo24
+ *
+ * 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 MENSTRUAL_CYCLE_FACE_H_
+#define MENSTRUAL_CYCLE_FACE_H_
+
+#include "movement.h"
+
+typedef struct {
+    // Store the date of the 'first' and the total cycles since to calulate and store the average menstrual cycle.
+    // Store the date of the previous, most recent, period to calculate the cycle length.
+    // Store the shortest and longest cycle to calculate the fertility window for The Calender Method.
+    // NOTE: Not thrilled about using two registers, but could not find a way to perform The Calender Method
+    //       without requiring both the 'first' and 'prev' dates.
+    union {
+        struct {
+            uint8_t first_day : 5;
+            uint8_t first_month : 4;
+            uint8_t first_year : 6; // 0-63 (representing 2020-2083)
+            uint8_t prev_day : 5;
+            uint8_t prev_month : 4;
+            uint8_t prev_year : 6; // 0-63 (representing 2020-2083)
+            uint8_t reserved : 2; // left over bit space
+        } bit;
+        uint32_t reg; // Tracking's been activated if > 0
+    } dates;
+    union {
+        struct {
+            uint8_t shortest_cycle : 6; // For step 2 of The Calender Method 
+            uint8_t longest_cycle : 6; // For step 3 of The Calender Method 
+            uint8_t average_cycle : 6; // The average menstrual cycle lasts 28 days, but normal cycles can vary from 21 to 35 days
+            uint16_t total_cycles : 11; // The total cycles (periods) entered since the start of tracking
+            uint8_t reserved : 3; // left over bit space
+        } bit; 
+        uint32_t reg;
+    } cycles;
+    uint8_t backup_register_dt;
+    uint8_t backup_register_cy;
+    uint8_t current_page;
+    uint8_t days_prev_period;
+    int32_t utc_offset;
+    bool period_today;
+    bool reset_tracking;
+} menstrual_cycle_state_t;
+
+void menstrual_cycle_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void menstrual_cycle_face_activate(void *context);
+bool menstrual_cycle_face_loop(movement_event_t event, void *context);
+void menstrual_cycle_face_resign(void *context);
+
+#define menstrual_cycle_face ((const watch_face_t){ \
+    menstrual_cycle_face_setup, \
+    menstrual_cycle_face_activate, \
+    menstrual_cycle_face_loop, \
+    menstrual_cycle_face_resign, \
+    NULL, \
+})
+
+#endif // MENSTRUAL_CYCLE_FACE_H_
\ No newline at end of file
diff --git a/legacy/watch_faces/complication/metronome_face.c b/legacy/watch_faces/complication/metronome_face.c
new file mode 100644 (file)
index 0000000..e599362
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Austin Teets
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "metronome_face.h"
+#include "watch.h"
+
+static const int8_t _sound_seq_start[] = {BUZZER_NOTE_C8, 2, 0};
+static const int8_t _sound_seq_beat[] = {BUZZER_NOTE_C6, 2, 0};
+
+void metronome_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(metronome_state_t));
+        memset(*context_ptr, 0, sizeof(metronome_state_t));
+    }
+}
+
+void metronome_face_activate(void *context) {
+    metronome_state_t *state = (metronome_state_t *)context;
+    movement_request_tick_frequency(2);
+    if (state->bpm == 0) {
+        state->count = 4;
+        state->bpm = 120;
+        state->soundOn = true;
+    }
+    state->mode = metWait;
+    state->correction = 0;
+    state->setCur = hundred;
+}
+
+static void _metronome_face_update_lcd(metronome_state_t *state) {
+    char buf[11];
+    if (state->soundOn) {
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    } else {
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+    }
+    sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp");
+    watch_display_string(buf, 0);
+}
+
+static void _metronome_start_stop(metronome_state_t *state) {
+    if (state->mode != metRun) {
+        movement_request_tick_frequency(64);
+        state->mode = metRun;
+        watch_clear_display();
+        double ticks = 3840.0 / (double)state->bpm;
+        state->tick = (int) ticks;
+        state->curTick = (int) ticks;
+        state->halfBeat = (int)(state->tick/2);
+        state->curCorrection = ticks - state->tick;
+        state->correction = ticks - state->tick;
+        state->curBeat = 1;
+    } else {
+        state->mode = metWait;
+        movement_request_tick_frequency(2);
+        _metronome_face_update_lcd(state);
+    }
+}
+
+static void _metronome_tick_beat(metronome_state_t *state) {
+    char buf[11];
+    if (state->soundOn) {
+        if (state->curBeat == 1) {
+            watch_buzzer_play_sequence((int8_t *)_sound_seq_start, NULL);
+        } else {
+            watch_buzzer_play_sequence((int8_t *)_sound_seq_beat, NULL);
+        }
+    }
+    sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp");    
+    watch_display_string(buf, 0);
+}
+
+static void _metronome_event_tick(uint8_t subsecond, metronome_state_t *state) {
+    (void) subsecond;
+
+    if (state->curCorrection >= 1) {
+        state->curCorrection -= 1;
+        state->curTick -= 1;
+    }
+    int diff = state->curTick - state->tick;
+    if(diff == 0) {
+        _metronome_tick_beat(state);
+        state->curTick = 0;
+        state->curCorrection += state->correction;
+        if (state->curBeat < state->count ) {
+            state->curBeat += 1;
+        } else {
+            state->curBeat = 1;
+        }
+    } else {
+        if (state->curTick == state->halfBeat)  {
+            watch_clear_display();
+        }
+        state->curTick += 1;
+    }
+}
+
+static void _metronome_setting_tick(uint8_t subsecond, metronome_state_t *state) {
+    char buf[13];
+    sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp"); 
+    if (subsecond%2 == 0) {
+        switch (state->setCur) {
+            case hundred:
+                buf[5] = ' ';
+                break;
+            case ten:
+                buf[6] = ' ';
+                break;
+            case one:
+                buf[7] = ' ';
+                break;
+            case count:
+                buf[3] = ' ';
+                break;
+            case alarm:
+                break;
+        }
+    }
+    if (state->setCur == alarm) {
+        sprintf(buf, "MN  8eep%s", state->soundOn ? "On" : " -");
+    }
+    if (state->soundOn) {
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    } else {
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+    }
+    watch_display_string(buf, 0);
+}
+
+static void _metronome_update_setting(metronome_state_t *state) {
+    char buf[13];
+    switch (state->setCur) {
+        case hundred:
+            if (state->bpm < 100) {
+                state->bpm += 100;
+            } else {
+                state->bpm -= 100;
+            }
+            break;
+        case ten:
+            if ((state->bpm / 10) % 10 < 9) {
+                state->bpm += 10;
+            } else {
+                state->bpm -= 90;
+            }
+            break;
+        case one:
+            if (state->bpm%10 < 9) {
+                state->bpm += 1;
+            } else {
+                state->bpm -= 9;
+            }
+            break;
+        case count:
+            if (state->count < 9) {
+                state->count += 1;
+            } else {
+                state->count = 2;
+            }
+            break;
+        case alarm:
+            state->soundOn = !state->soundOn;
+            break;
+    }
+    sprintf(buf, "MN %d %03d%s", state->count % 10, state->bpm, "bp"); 
+    if (state->setCur == alarm) {
+        sprintf(buf, "MN  8eep%s", state->soundOn ? "On" : " -");
+    }
+    if (state->soundOn) {
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    } else {
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+    }
+    watch_display_string(buf, 0);
+}
+
+bool metronome_face_loop(movement_event_t event, void *context) {
+    metronome_state_t *state = (metronome_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _metronome_face_update_lcd(state);
+            break;
+        case EVENT_TICK:
+            if (state->mode == metRun){
+                _metronome_event_tick(event.subsecond, state);
+            } else if (state->mode == setMenu) {
+                _metronome_setting_tick(event.subsecond, state);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if (state->mode == setMenu) {
+                _metronome_update_setting(state);
+            } else {
+                _metronome_start_stop(state);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            if (state->mode == setMenu) {
+                if (state->setCur < alarm) {
+                    state->setCur += 1;
+                } else {
+                    state->setCur = hundred;
+                }
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->mode != metRun && state->mode != setMenu) {
+                movement_request_tick_frequency(2);
+                state->mode = setMenu;
+                _metronome_face_update_lcd(state);
+            } else if (state->mode == setMenu) {
+                state->mode = metWait;
+                _metronome_face_update_lcd(state);
+            }
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            movement_move_to_next_face();
+            break;
+        case EVENT_TIMEOUT:
+            if (state->mode != metRun) {
+                movement_move_to_face(0);
+            }
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+    return true;
+}
+
+void metronome_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/complication/metronome_face.h b/legacy/watch_faces/complication/metronome_face.h
new file mode 100644 (file)
index 0000000..32766da
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Austin Teets
+ *
+ * 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 METRONOME_FACE_H_
+#define METRONOME_FACE_H_
+
+#include "movement.h"
+
+/*
+ * A Metronome watch complication
+ * Allows the user to set the BPM, counts per measure, beep sound on/off
+ * Screen flashes on on the beat and off on the half beat (1/8th note)
+ * Beep will sound high for downbeat and low for subsequent beats in measure
+ * USE:
+ *      Press Alarm to start/stop metronome_face
+ *      Hold Alarm to enter settings menu
+ *          Short Light press will move through options
+ *          Short Alarm press will increment/toggle options
+ *          Long alarm press will exit options
+ */
+typedef enum {
+    metWait,
+    metRun,
+    setMenu
+} metronome_mode_t;
+
+typedef enum {
+    hundred,
+    ten,
+    one,
+    count,
+    alarm
+} setting_cursor_t;
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint8_t bpm;
+    double correction;
+    double curCorrection;
+    int count;
+    int tick;
+    int curTick;
+    int curBeat;
+    int halfBeat;
+    metronome_mode_t mode : 3;
+    setting_cursor_t setCur : 4;
+    bool soundOn;
+} metronome_state_t;
+
+void metronome_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void metronome_face_activate(void *context);
+bool metronome_face_loop(movement_event_t event, void *context);
+void metronome_face_resign(void *context);
+
+#define metronome_face ((const watch_face_t){ \
+    metronome_face_setup, \
+    metronome_face_activate, \
+    metronome_face_loop, \
+    metronome_face_resign, \
+    NULL, \
+})
+
+#endif // METRONOME_FACE_H_
+
diff --git a/legacy/watch_faces/complication/moon_phase_face.c b/legacy/watch_faces/complication/moon_phase_face.c
new file mode 100644 (file)
index 0000000..7b28e44
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Joey Castillo
+ *
+ * Based on Phase of Moon App for Tidbyt
+ * https://github.com/tidbyt/community/blob/main/apps/phaseofmoon/phase_of_moon.star
+ * Copyright (c) 2022 Alan Fleming
+ *
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "moon_phase_face.h"
+#include "watch_utility.h"
+
+#define LUNAR_DAYS 29.53058770576
+#define LUNAR_SECONDS (LUNAR_DAYS * (24 * 60 * 60))
+#define FIRST_MOON 947182440 // Saturday, 6 January 2000 18:14:00 in unix epoch time
+#define NUM_PHASES 8
+
+static const float phase_changes[] = {0, 1, 6.38264692644, 8.38264692644, 13.76529385288, 15.76529385288, 21.14794077932, 23.14794077932, 28.53058770576, 29.53058770576};
+
+void moon_phase_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(moon_phase_state_t));
+        memset(*context_ptr, 0, sizeof(moon_phase_state_t));
+    }
+}
+
+void moon_phase_face_activate(void *context) {
+    (void) context;
+}
+
+static void _update(moon_phase_state_t *state, uint32_t offset) {
+    (void)state;
+    char buf[11];
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset()) + offset;
+    date_time = watch_utility_date_time_from_unix_time(now, movement_get_current_timezone_offset());
+    double currentfrac = fmod(now - FIRST_MOON, LUNAR_SECONDS) / LUNAR_SECONDS;
+    double currentday = currentfrac * LUNAR_DAYS;
+    uint8_t phase_index = 0;
+
+    for(phase_index = 0; phase_index <= NUM_PHASES; phase_index++) {
+        if (currentday > phase_changes[phase_index] && currentday <= phase_changes[phase_index + 1]) break;
+    }
+
+    watch_display_string(" ", 0);
+    switch (phase_index) {
+        case 0:
+        case 8:
+            sprintf(buf, "%2d Neu  ", date_time.unit.day);
+            break;
+        case 1:
+            sprintf(buf, "%2dCresnt", date_time.unit.day);
+            watch_set_pixel(2, 13);
+            watch_set_pixel(2, 15);
+            if (currentfrac > 0.125) watch_set_pixel(1, 13);
+            break;
+        case 2:
+            sprintf(buf, "%2d 1st q", date_time.unit.day);
+            watch_set_pixel(2, 13);
+            watch_set_pixel(2, 15);
+            watch_set_pixel(1, 13);
+            watch_set_pixel(1, 14);
+            break;
+        case 3:
+            sprintf(buf, "%2d Gibb ", date_time.unit.day);
+            watch_set_pixel(2, 13);
+            watch_set_pixel(2, 15);
+            watch_set_pixel(1, 14);
+            watch_set_pixel(1, 13);
+            watch_set_pixel(1, 15);
+            break;
+        case 4:
+            sprintf(buf, "%2d FULL ", date_time.unit.day);
+            watch_set_pixel(2, 13);
+            watch_set_pixel(2, 15);
+            watch_set_pixel(1, 14);
+            watch_set_pixel(2, 14);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(0, 14);
+            watch_set_pixel(0, 13);
+            watch_set_pixel(1, 13);
+            break;
+        case 5:
+            sprintf(buf, "%2d Gibb ", date_time.unit.day);
+            watch_set_pixel(1, 14);
+            watch_set_pixel(2, 14);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(0, 14);
+            watch_set_pixel(0, 13);
+            break;
+        case 6:
+            sprintf(buf, "%2d 3rd q", date_time.unit.day);
+            watch_set_pixel(1, 14);
+            watch_set_pixel(2, 14);
+            watch_set_pixel(0, 14);
+            watch_set_pixel(0, 13);
+            break;
+        case 7:
+            sprintf(buf, "%2dCresnt", date_time.unit.day);
+            watch_set_pixel(0, 14);
+            watch_set_pixel(0, 13);
+            if (currentfrac < 0.875) watch_set_pixel(2, 14);
+            break;
+    }
+    watch_display_string(buf, 2);
+}
+
+bool moon_phase_face_loop(movement_event_t event, void *context) {
+    moon_phase_state_t *state = (moon_phase_state_t *)context;
+    watch_date_time_t date_time;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _update(state, state->offset);
+            break;
+        case EVENT_TICK:
+            // only update once an hour
+            date_time = watch_rtc_get_date_time();
+            if ((date_time.unit.minute == 0) && (date_time.unit.second == 0)) _update(state, state->offset);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // update at the top of the hour OR if we're entering sleep mode with an offset.
+            // also, in sleep mode, always show the current moon phase (offset = 0).
+            if (state->offset || (watch_rtc_get_date_time().unit.minute == 0)) _update(state, 0);
+            // and kill the offset so when the wearer wakes up, it matches what's on screen.
+            state->offset = 0;
+            // finally: clear out the last two digits and replace them with the sleep mode indicator
+            watch_display_string("  ", 8);
+            if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(1000);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // Pressing the alarm adds an offset of one day to the displayed value,
+            // so you can see moon phases in the future.
+            state->offset += 86400;
+            _update(state, state->offset);
+            break;
+       case EVENT_ALARM_LONG_PRESS:
+           state->offset = 0;
+            _update(state, state->offset);
+           break;
+        case EVENT_TIMEOUT:
+            // QUESTION: Should timeout reset offset to 0?
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void moon_phase_face_resign(void *context) {
+    moon_phase_state_t *state = (moon_phase_state_t *)context;
+    state->offset = 0;
+}
diff --git a/legacy/watch_faces/complication/moon_phase_face.h b/legacy/watch_faces/complication/moon_phase_face.h
new file mode 100644 (file)
index 0000000..b020982
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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 MOON_PHASE_FACE_H_
+#define MOON_PHASE_FACE_H_
+
+/*
+ * MOON PHASE face
+ *
+ * The Moon Phase face is similar to the Sunrise/Sunset face: it displays the
+ * current phase of the moon, along with the day of the month and a graphical
+ * representation of the moon on the top row.
+ * 
+ * This graphical representation is a bit abstract. The segments that turn on
+ * represent the shape of the moon, waxing from the bottom right and waning at
+ * the top left. A small crescent at the bottom right will grow into a larger
+ * crescent, then add lines in the center for a quarter and half moon. All
+ * segments are on during a full moon. Then gradually the segments at the
+ * bottom right will turn off, until all that remains is a small waning
+ * crescent at the top left.
+ * 
+ * All segments turn off during a new moon.
+ * 
+ * On this screen you may press the Alarm button repeatedly to move forward
+ * in time: the day of the month at the top right will advance by one day for
+ * each button press, and both the text and the graphical representation will
+ * display the moon phase for that day. Try pressing the Alarm button 27 times
+ * now, just to visualize what the moon will look like over the next month.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint32_t offset;
+} moon_phase_state_t;
+
+void moon_phase_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void moon_phase_face_activate(void *context);
+bool moon_phase_face_loop(movement_event_t event, void *context);
+void moon_phase_face_resign(void *context);
+
+#define moon_phase_face ((const watch_face_t){ \
+    moon_phase_face_setup, \
+    moon_phase_face_activate, \
+    moon_phase_face_loop, \
+    moon_phase_face_resign, \
+    NULL, \
+})
+
+#endif // MOON_PHASE_FACE_H_
+
diff --git a/legacy/watch_faces/complication/morsecalc_face.c b/legacy/watch_faces/complication/morsecalc_face.c
new file mode 100644 (file)
index 0000000..0a4ad2c
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_private_display.h"
+
+#include "morsecalc_face.h"
+#include "morsecalc_display.h"
+
+/* mc_input Read an input into a morse code buffer 
+ * Input: mc = index of MORSECODE_TREE[]
+ *        len = max morse code char length
+ *        in = character to read into buffer (0='.', 1='-', ignored otherwise).
+ * If the buffer is full, reset it instead of entering the new character.
+ */
+static void morsecode_input(unsigned int *mc, unsigned int len, char in) {
+    if(*mc >= (unsigned int) ((1<<len)-1)) *mc = 0;
+    else if((in == 0) | (in == 1)) *mc = (*mc)*2+in+1;
+    return;
+}
+
+
+// Clear token buffer
+void morsecalc_reset_token(morsecalc_state_t *mcs) { 
+    memset(mcs->token, '\0', MORSECALC_TOKEN_LEN*sizeof(mcs->token[0]));
+    mcs->idxt = 0;
+    return;
+}
+
+// Write a completed morse code character to the calculator
+void morsecalc_input(morsecalc_state_t * mcs) {
+    int status = 0;
+    char dec = MORSECODE_TREE[mcs->mc]; 
+    mcs->mc = 0;
+    switch(dec) {
+        case '\0': // Invalid character, do nothing
+            morsecalc_display_token(mcs);
+            break;
+            
+        case ' ': // Submit token to calculator
+            if(mcs->idxt > 0) {
+                mcs->token[mcs->idxt] = '\0';
+                status = calc_input(mcs->cs, mcs->token);
+                morsecalc_reset_token(mcs); 
+            } 
+            morsecalc_display_stack(mcs);   
+            break;
+            
+        case '(': // -.--. Erase previous character in token
+            if(mcs->idxt>0) {
+                mcs->idxt--;
+                mcs->token[mcs->idxt] = '\0';
+            }
+            morsecalc_display_token(mcs);
+            break;
+            
+        case 'S': // -.-.- Erase entire token without submitting
+            morsecalc_reset_token(mcs); 
+            morsecalc_display_stack(mcs);   
+            break;
+            
+        default: // Add character to token
+            if(mcs->idxt < MORSECALC_TOKEN_LEN-1) {
+                mcs->token[mcs->idxt] = dec;
+                mcs->idxt = min(mcs->idxt+1, MORSECALC_TOKEN_LEN); 
+                morsecalc_display_token(mcs);
+            }
+            else  watch_display_string("  full", 4); 
+            break;
+    }
+    
+    // Print errors if there are any
+    switch(status) {
+        case  0: break; // Success
+        case -1: watch_display_string("cmderr", 4); break; // Unrecognized command
+        case -2: watch_display_string("stkerr", 4); break; // Bad stack size
+        default: watch_display_string("   err", 4); break; // Other error 
+    }
+    
+    return;    
+}
+
+void morsecalc_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(morsecalc_state_t)); 
+        morsecalc_state_t *mcs = (morsecalc_state_t *)*context_ptr;
+        morsecalc_reset_token(mcs); 
+        
+        mcs->cs = (calc_state_t *) malloc(sizeof(calc_state_t));
+        calc_init(mcs->cs); 
+        mcs->mc = 0;
+        mcs->led_is_on = 0;
+    }
+    return;
+}
+
+void morsecalc_face_activate(void *context) {
+    morsecalc_state_t *mcs = (morsecalc_state_t *) context;
+    mcs->mc = 0;
+    morsecalc_display_stack(mcs);
+    return;
+}
+
+bool morsecalc_face_loop(movement_event_t event, void *context) {
+    morsecalc_state_t *mcs = (morsecalc_state_t *) context;
+    switch(event.event_type) {
+    // input
+    case EVENT_ALARM_BUTTON_UP:
+    // dot
+        morsecode_input(&mcs->mc, MORSECODE_LEN, 0);
+        morsecalc_display_token(mcs);
+        break;
+    case EVENT_LIGHT_BUTTON_UP:
+    // dash
+        morsecode_input(&mcs->mc, MORSECODE_LEN, 1);
+        morsecalc_display_token(mcs);
+        break;
+    case EVENT_MODE_BUTTON_UP:
+    // submit character (or quit)
+        if(mcs->mc || mcs->idxt) morsecalc_input(mcs); 
+        else movement_move_to_next_face();
+        break;
+
+    // show stack
+    case EVENT_ALARM_LONG_PRESS:
+        morsecalc_display_stack(mcs);
+        mcs->mc = 0;
+        break;
+
+    // toggle light
+    case EVENT_LIGHT_LONG_PRESS:
+        mcs->led_is_on = !mcs->led_is_on;
+        if(mcs->led_is_on) {
+            movement_color_t color = movement_backlight_color();
+                       watch_set_led_color(color.red ? (0xF | color.red << 4) : 0,
+                                                               color.green ? (0xF | color.green << 4) : 0);
+            movement_request_tick_frequency(4);
+        }
+        else {
+            watch_set_led_off();
+            movement_request_tick_frequency(1);
+        }
+        break;
+
+    // quit
+    case EVENT_TIMEOUT:
+        movement_move_to_face(0);
+        break;
+    case EVENT_MODE_LONG_PRESS:
+        movement_move_to_next_face();
+        break;
+
+    case EVENT_TICK:
+        if(mcs->led_is_on) { 
+            movement_color_t color = movement_backlight_color();
+                       watch_set_led_color(color.red ? (0xF | color.red << 4) : 0,
+                               color.green ? (0xF | color.green << 4) : 0);
+               }
+        break;
+    case EVENT_LIGHT_BUTTON_DOWN:
+        // don't light up every time light is hit
+        break;
+    default:
+        movement_default_loop_handler(event);
+        break;
+       }
+    
+    return true;
+}
+
+void morsecalc_face_resign(void *context) {
+    morsecalc_state_t *mcs = (morsecalc_state_t *) context;
+    mcs->led_is_on = 0;
+       watch_set_led_off();
+    return;
+}
+
diff --git a/legacy/watch_faces/complication/morsecalc_face.h b/legacy/watch_faces/complication/morsecalc_face.h
new file mode 100644 (file)
index 0000000..1430347
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * 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 MORSECALC_FACE_H_
+#define MORSECALC_FACE_H_
+
+/*
+ * MORSECALC face
+ * Morse-code-based RPN calculator
+ *
+ * The calculator is operated by first composing a **token** in Morse code,
+ * then submitting it to the calculator. A token specifies either a calculator
+ * operation or a float value.
+ *
+ * These two parts of the codebase are totally independent:
+ *  1. The Morse-code reader (`mc.h`, `mc.c`)
+ *  2. The RPN calculator (`calc.h`, `calc.c`, `calc_fn.h`, `calc_fn.c`, `small_strtod.c`)
+ *
+ * The user interface (`morsecalc_face.h`, `morsecalc_face.c`) lets you talk
+ * to the RPN calculator through Morse code.
+ *
+ * ## Controls
+ *  - `light` is dash
+ *  - `alarm` is dot
+ *  - `mode` is "finish character"
+ *  - long-press `mode` or submit a blank token to switch faces
+ *  - long-press `alarm` to show stack
+ *  - long-press `light` to toggle the light
+ *
+ * ## Morse code token entry
+ * As you enter `.`s and `-`s, the morse code char you've entered will
+ * appear in the top center digit. At the top right is the # of morse code
+ * `.`/`-` you've input so far. The character resets at the 6th `.`/`-`.
+ *
+ * Once you have the character you want to enter, push `mode` to enter it.
+ *
+ * The character will be appended to the current token, whose 6 trailing
+ * chars are shown on the main display. Once you've typed in the token you
+ * want, enter a blank Morse code character and then push `mode`.
+ * This submits it to the calculator.
+ *
+ * Special characters:
+ *  - Backspace is `(` (`-.--.`).
+ *  - Clear token input without submitting to calculator is `Start
+ *    transmission` (`-.-.-`).
+ *
+ * ## Writing commands
+ * First the calculator will try to interpret the token as a command/stack operation.
+ * Commands are defined in `calc_dict[]` in `movement/lib/morsecalc/calc_fns.h`.
+ * If the command doesn't appear in the dictionary, the calculator tries to interpret the token as a number.
+ *
+ * ## Writing numbers
+ * Numbers are written like floating point strings.
+ * Entering a number pushes it to the top of the stack if there's room.
+ * This can get long, so for convenience numerals can also be written in binary with .- = 01.
+ *
+ *     0   1    2    3    4    5    6    7    8    9
+ *     .   -    -.   --   -..  -.-  --.  ---  -... -..-
+ *     e   t    n    m    d    k    g    o    b    x
+ *
+ *  - Exponent signs must be entered as "p".
+ *  - Decimal place "." can be entered as "h" (code ....)
+ *  - Sign "-" can be entered as "Ch digraph" (code ----)
+ *
+ * For example: "4.2e-3" can be entered directly, or as "4h2pC3"
+ *   similarly, "0.0042" can also be entered as "eheedn"
+ * Once you submit a number to the watch face, it pushes it to the top of the stack if there's room.
+ *
+ * ## Number display
+ * After a command runs, the top of the stack is displayed in this format:
+ *
+ *   - Main 4 digits = leading 4 digits
+ *   - Last 2 digits = exponent
+ *   - Top middle = [Stack location, Sign of number]
+ *   - Top right = [Stack exponent, Sign of exponent]
+ *
+ * Blank sign digit means positive.
+ * So for example, the watch face might look like this:
+ *
+ *     [   0 -5]
+ *     [4200 03]
+ *
+ * ... representing `+4.200e-3` is in stack location 0 (the top) and it's one of five items in the stack.
+ *
+ * ## Looking at the stack
+ * To show the top of the stack, push and hold `light`/`alarm` or submit a blank token by pushing `mode` a bunch of times.
+ * To show the N-th stack item (0 through 9):
+ *
+ *  - Put in the Morse code for N without pushing the mode button.
+ *  - Push and hold `alarm`.
+ *
+ * To show the memory register, use `m` instead of a number.
+ *
+ * To see all the calculator operations and their token aliases, see the `calc_dict[]` struct in `calc_fns.h`
+ */
+
+#define MORSECALC_TOKEN_LEN 32
+#define MORSECODE_LEN 5
+
+#include "movement.h"
+#include "calc.h"
+
+/*
+ * MC International Morse Code binary tree
+ * Levels of the tree are concatenated.
+ * '.' = 0 and '-' = 1.
+ *
+ * Capitals denote special characters:
+ * C = Ch digraph
+ * V = VERIFY (ITU-R "UNDERSTOOD")
+ * R = REPEAT
+ * W = WAIT
+ * S = START TRANSMISSION
+ * E = END OF WORK
+ */
+static const char MORSECODE_TREE[] = " etianmsurwdkgohvf\0l\0pjbxcyzq\0C\x35\x34V\x33\0R\0\x32W\0+\0\0\0\0\x31\x36=/\0\0S(\0\x37\0\0\0\x38\0\x39\x30\0\0\0\0\0E\0\0\0\0\0\0?_\0\0\0\0\"\0\0.\0\0\0\0@\0\0\0'\0\0-\0\0\0\0\0\0\0\0;!\0)\0\0\0\0\0,\0\0\0\0:\0\0\0\0\0\0";
+
+void morsecalc_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void morsecalc_face_activate(void *context);
+bool morsecalc_face_loop(movement_event_t event, void *context);
+void morsecalc_face_resign(void *context);
+
+typedef struct {
+       calc_state_t *cs;
+       unsigned int mc; // Morse code character
+       char token[MORSECALC_TOKEN_LEN];
+       uint8_t idxt;
+       uint8_t led_is_on;
+} morsecalc_state_t;
+
+void morsecalc_reset_token(morsecalc_state_t *mcs);
+void morsecalc_input(morsecalc_state_t *mcs);
+
+#define morsecalc_face ((const watch_face_t){ \
+    morsecalc_face_setup, \
+    morsecalc_face_activate, \
+    morsecalc_face_loop, \
+    morsecalc_face_resign, \
+    NULL, \
+})
+
+#endif // MORSECALC_FACE_H_
diff --git a/legacy/watch_faces/complication/orrery_face.c b/legacy/watch_faces/complication/orrery_face.c
new file mode 100644 (file)
index 0000000..fba821b
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "orrery_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "vsop87a_micro.h" // smaller size, less accurate
+#include "vsop87a_milli.h"
+#include "astrolib.h"
+
+#define NUM_AVAILABLE_BODIES 9
+
+static const char orrery_celestial_body_names[NUM_AVAILABLE_BODIES][3] = {
+    "ME",   // Mercury
+    "VE",   // Venus
+    "EA",   // Earth
+    "LU",   // Moon (Luna)
+    "MA",   // Mars
+    "JU",   // Jupiter
+    "SA",   // Saturn
+    "UR",   // Uranus
+    "NE"    // Neptune
+};
+
+static void _orrery_face_recalculate(orrery_state_t *state) {
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
+    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 et = astro_convert_jd_to_julian_millenia_since_j2000(jd);
+    double r[3] = {0};
+
+    switch(state->active_body_index) {
+        case 0:
+            vsop87a_milli_getMercury(et, r);
+            break;
+        case 1:
+            vsop87a_milli_getVenus(et, r);
+            break;
+        case 2:
+            vsop87a_milli_getEarth(et, r);
+            break;
+        case 3:
+            {
+                 double earth[3];
+                 double emb[3];
+                 vsop87a_milli_getEarth(et, earth);
+                 vsop87a_milli_getEmb(et, emb);
+                 vsop87a_milli_getMoon(earth, emb, r);
+             }
+            break;
+        case 4:
+            vsop87a_milli_getMars(et, r);
+            break;
+        case 5:
+            vsop87a_milli_getJupiter(et, r);
+            break;
+        case 6:
+            vsop87a_milli_getSaturn(et, r);
+            break;
+        case 7:
+            vsop87a_milli_getUranus(et, r);
+            break;
+        case 8:
+            vsop87a_milli_getNeptune(et, r);
+            break;
+    }
+    state->coords[0] = r[0];
+    state->coords[1] = r[1];
+    state->coords[2] = r[2];
+}
+
+static void _orrery_face_update(movement_event_t event, orrery_state_t *state) {
+    char buf[11];
+    switch (state->mode) {
+        case ORRERY_MODE_SELECTING_BODY:
+            watch_display_string("Orrery", 4);
+            if (event.subsecond % 2) {
+                watch_display_string((char *)orrery_celestial_body_names[state->active_body_index], 0);
+            } else {
+                watch_display_string("  ", 0);
+            }
+            if (event.subsecond == 0) {
+                watch_display_string("  ", 2);
+                switch (state->animation_state) {
+                    case 0:
+                        watch_set_pixel(0, 7);
+                        watch_set_pixel(2, 6);
+                        break;
+                    case 1:
+                        watch_set_pixel(1, 7);
+                        watch_set_pixel(2, 9);
+                        break;
+                    case 2:
+                        watch_set_pixel(2, 7);
+                        watch_set_pixel(0, 9);
+                        break;
+                }
+                state->animation_state = (state->animation_state + 1) % 3;
+            }
+            break;
+        case ORRERY_MODE_CALCULATING:
+            watch_clear_display();
+             // this takes a moment and locks the UI, flash C for "Calculating"
+            watch_start_character_blink('C', 100);
+            _orrery_face_recalculate(state);
+            watch_stop_blink();
+            state->mode = ORRERY_MODE_DISPLAYING_X;
+            // fall through
+        case ORRERY_MODE_DISPLAYING_X:
+            sprintf(buf, "%s X%6d", orrery_celestial_body_names[state->active_body_index], (int16_t)round(state->coords[0] * 100));
+            watch_display_string(buf, 0);
+            break;
+        case ORRERY_MODE_DISPLAYING_Y:
+            sprintf(buf, "%s Y%6d", orrery_celestial_body_names[state->active_body_index], (int16_t)round(state->coords[1] * 100));
+            watch_display_string(buf, 0);
+            break;
+        case ORRERY_MODE_DISPLAYING_Z:
+            sprintf(buf, "%s Z%6d", orrery_celestial_body_names[state->active_body_index], (int16_t)round(state->coords[2] * 100));
+            watch_display_string(buf, 0);
+            break;
+        case ORRERY_MODE_NUM_MODES:
+            // this case does not happen, but we need it to silence a warning.
+            break;
+    }
+}
+
+void orrery_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(orrery_state_t));
+        memset(*context_ptr, 0, sizeof(orrery_state_t));
+    }
+}
+
+void orrery_face_activate(void *context) {
+    (void) context;
+    movement_request_tick_frequency(4);
+}
+
+bool orrery_face_loop(movement_event_t event, void *context) {
+    orrery_state_t *state = (orrery_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            _orrery_face_update(event, state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch (state->mode) {
+                case ORRERY_MODE_SELECTING_BODY:
+                    // advance to next celestial body (move to calculations with a long press)
+                    state->active_body_index = (state->active_body_index + 1) % NUM_AVAILABLE_BODIES;
+                    break;
+                case ORRERY_MODE_CALCULATING:
+                    // ignore button press during calculations
+                    break;
+                case ORRERY_MODE_DISPLAYING_Z:
+                    // at last mode, wrap around
+                    state->mode = ORRERY_MODE_DISPLAYING_X;
+                    break;
+                default:
+                    // otherwise, advance to next mode
+                    state->mode++;
+                    break;
+            }
+            _orrery_face_update(event, state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->mode == ORRERY_MODE_SELECTING_BODY) {
+                // celestial body selected! this triggers a calculation in the update method.
+                state->mode = ORRERY_MODE_CALCULATING;
+                movement_request_tick_frequency(1);
+                _orrery_face_update(event, state);
+            } else if (state->mode != ORRERY_MODE_CALCULATING) {
+                // in all modes except "doing a calculation", return to the selection screen.
+                state->mode = ORRERY_MODE_SELECTING_BODY;
+                movement_request_tick_frequency(4);
+                _orrery_face_update(event, state);
+            }
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void orrery_face_resign(void *context) {
+    orrery_state_t *state = (orrery_state_t *)context;
+    state->mode = ORRERY_MODE_SELECTING_BODY;
+}
diff --git a/legacy/watch_faces/complication/orrery_face.h b/legacy/watch_faces/complication/orrery_face.h
new file mode 100644 (file)
index 0000000..4bf141e
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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 ORRERY_FACE_H_
+#define ORRERY_FACE_H_
+
+/*
+ * ORRERY face
+ *
+ * The Orrery watch face is similar to the Astronomy watch face in that it
+ * calculates properties of the planets, but instead of calculating their
+ * positions in the sky, this watch face calculates their absolute locations
+ * in the solar system. This is only useful if you want to plot the planets
+ * on graph paper, but hey, you never know!
+ * 
+ * The controls are identical to the Astronomy watch face: while the title
+ * screen (“Orrery”) is displayed, you can advance through the available
+ * planets with repeated short presses on the Alarm button. The available
+ * planets:
+ * 
+ *     ME - Mercury
+ *     VE - Venus
+ *     EA - Earth
+ *     LU - Luna, the Earth’s moon
+ *     MA - Mars
+ *     JU - Jupiter
+ *     SA - Saturn
+ *     UR - Uranus
+ *     NE - Neptune
+ * 
+ * Note that the sun is not available in this menu, as the sun is always at
+ * (0,0,0) in this calculation.
+ * 
+ * Long press on the Alarm button to calculate the planet’s location, and
+ * after a flashing “C” (for Calculating), you will be presented with the
+ * planet’s X coordinate in astronomical units. Short press Alarm to cycle
+ * through the X, Y and Z coordinates, and then long press Alarm to return
+ * to planet selection.
+ * 
+ * The large numbers represent the whole number part, and the two smaller
+ * numbers (in the seconds place) represent the decimal portion. So if you
+ * see “SA X 736” and “SA Y -662”, you can read that as an X coordinate of
+ * 7.36 AU and a Y coordinate of -6.62 AU. You can literally draw a dot at
+ * (0, 0) to represent the sun, and a dot at (7.36, -6.62) to represent
+ * Saturn. (The Z coordinates tend to be pretty close to zero, as the
+ * planets largely orbit on a single plane, the ecliptic.)
+ */
+
+#include "movement.h"
+
+typedef enum {
+    ORRERY_MODE_SELECTING_BODY = 0,
+    ORRERY_MODE_CALCULATING,
+    ORRERY_MODE_DISPLAYING_X,
+    ORRERY_MODE_DISPLAYING_Y,
+    ORRERY_MODE_DISPLAYING_Z,
+    ORRERY_MODE_NUM_MODES
+} orrery_mode_t;
+
+typedef struct {
+    orrery_mode_t mode;
+    uint8_t active_body_index;
+    double coords[3];
+    uint8_t animation_state;
+} orrery_state_t;
+
+void orrery_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void orrery_face_activate(void *context);
+bool orrery_face_loop(movement_event_t event, void *context);
+void orrery_face_resign(void *context);
+
+#define orrery_face ((const watch_face_t){ \
+    orrery_face_setup, \
+    orrery_face_activate, \
+    orrery_face_loop, \
+    orrery_face_resign, \
+    NULL, \
+})
+
+#endif // ORRERY_FACE_H_
diff --git a/legacy/watch_faces/complication/periodic_face.c b/legacy/watch_faces/complication/periodic_face.c
new file mode 100644 (file)
index 0000000..f6647e9
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 PrimmR
+ * Copyright (c) 2024 David Volovskiy
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "periodic_face.h"
+
+#define FREQ_FAST 8
+#define FREQ 2
+
+static bool _quick_ticks_running;
+static uint8_t _ts_ticks = 0;
+static int16_t _text_pos;
+static const char* _text_looping;
+static const char title_text[] = "Periodic Table";
+
+void periodic_face_setup(uint8_t watch_face_index, void **context_ptr)
+{
+    (void)watch_face_index;
+    if (*context_ptr == NULL)
+    {
+        *context_ptr = malloc(sizeof(periodic_state_t));
+        memset(*context_ptr, 0, sizeof(periodic_state_t));
+    }
+}
+
+void periodic_face_activate(void *context)
+{
+    periodic_state_t *state = (periodic_state_t *)context;
+
+    state->atomic_num = 0;
+    state->mode = 0;
+    state->selection_index = 0;
+    _quick_ticks_running = false;
+    movement_request_tick_frequency(FREQ);
+}
+
+typedef struct
+{
+    char symbol[3];
+    char name[14];  // Longest is Rutherfordium
+    int16_t year_discovered;  // Negative is BC
+    uint16_t atomic_mass;  // In units of 0.01 AMU
+    uint16_t electronegativity;  // In units of 0.01
+    char group[3];
+} element;
+
+typedef enum {
+    SCREEN_TITLE = 0,
+    SCREEN_ELEMENT,
+    SCREEN_ATOMIC_MASS,
+    SCREEN_DISCOVER_YEAR,
+    SCREEN_ELECTRONEGATIVITY,
+    SCREEN_FULL_NAME,    
+    SCREENS_COUNT
+} PeriodicScreens;
+
+const char screen_name[SCREENS_COUNT][3] = {
+    [SCREEN_ATOMIC_MASS] = "am",
+    [SCREEN_DISCOVER_YEAR] = " y",
+    [SCREEN_ELECTRONEGATIVITY] = "EL",
+    [SCREEN_FULL_NAME] = " n",
+};
+
+// Comments on the table denote symbols that cannot be displayed
+#define MAX_ELEMENT 118
+const element table[MAX_ELEMENT] = {
+    { .symbol = "H", .name = "Hydrogen", .year_discovered = 1671, .atomic_mass = 101, .electronegativity = 220, .group = "  " },
+    { .symbol = "HE", .name = "Helium", .year_discovered = 1868, .atomic_mass = 400, .electronegativity = 0, .group = "0" },
+    { .symbol = "LI", .name = "Lithium", .year_discovered = 1817, .atomic_mass = 694, .electronegativity = 98, .group = "1" },
+    { .symbol = "BE", .name = "Beryllium", .year_discovered = 1798, .atomic_mass = 901, .electronegativity = 157, .group = "2" },
+    { .symbol = "B", .name = "Boron", .year_discovered = 1787, .atomic_mass = 1081, .electronegativity = 204, .group = "3" },
+    { .symbol = "C", .name = "Carbon", .year_discovered = -26000, .atomic_mass = 1201, .electronegativity = 255, .group = "4" },
+    { .symbol = "N", .name = "Nitrogen", .year_discovered = 1772, .atomic_mass = 1401, .electronegativity = 304, .group = "5" },
+    { .symbol = "O", .name = "Oxygen", .year_discovered = 1771, .atomic_mass = 1600, .electronegativity = 344, .group = "6" },
+    { .symbol = "F", .name = "Fluorine", .year_discovered = 1771, .atomic_mass = 1900, .electronegativity = 398, .group = "7" },
+    { .symbol = "NE", .name = "Neon", .year_discovered = 1898, .atomic_mass = 2018, .electronegativity = 0, .group = "0" },
+    { .symbol = "NA", .name = "Sodium", .year_discovered = 1702, .atomic_mass = 2299, .electronegativity = 93, .group = "1" },
+    { .symbol = "MG", .name = "Magnesium", .year_discovered = 1755, .atomic_mass = 2431, .electronegativity = 131, .group = "2" },
+    { .symbol = "AL", .name = "Aluminium", .year_discovered = 1746, .atomic_mass = 2698, .electronegativity = 161, .group = "3" },
+    { .symbol = "SI", .name = "Silicon", .year_discovered = 1739, .atomic_mass = 2809, .electronegativity = 190, .group = "4" },
+    { .symbol = "P", .name = "Phosphorus", .year_discovered = 1669, .atomic_mass = 3097, .electronegativity = 219, .group = "5" },
+    { .symbol = "S", .name = "Sulfur", .year_discovered = -2000, .atomic_mass = 3206, .electronegativity = 258, .group = "6" },
+    { .symbol = "CL", .name = "Chlorine", .year_discovered = 1774, .atomic_mass = 3545., .electronegativity = 316, .group = "7" },
+    { .symbol = "AR", .name = "Argon", .year_discovered = 1894, .atomic_mass = 3995., .electronegativity = 0, .group = "0" },
+    { .symbol = "K", .name = "Potassium", .year_discovered = 1702, .atomic_mass = 3910, .electronegativity = 82, .group = "1" },
+    { .symbol = "CA", .name = "Calcium", .year_discovered = 1739, .atomic_mass = 4008, .electronegativity = 100, .group = "2" },
+    { .symbol = "SC", .name = "Scandium", .year_discovered = 1879, .atomic_mass = 4496, .electronegativity = 136, .group = " T" },
+    { .symbol = "TI", .name = "Titanium", .year_discovered = 1791, .atomic_mass = 4787, .electronegativity = 154, .group = " T" },
+    { .symbol = "W", .name = "Vanadium", .year_discovered = 1801, .atomic_mass = 5094, .electronegativity = 163, .group = " T" },
+    { .symbol = "CR", .name = "Chromium", .year_discovered = 1797, .atomic_mass = 5200, .electronegativity = 166, .group = " T" },
+    { .symbol = "MN", .name = "Manganese", .year_discovered = 1774, .atomic_mass = 5494, .electronegativity = 155, .group = " T" },
+    { .symbol = "FE", .name = "Iron", .year_discovered = -5000, .atomic_mass = 5585, .electronegativity = 183, .group = " T" },
+    { .symbol = "CO", .name = "Cobalt", .year_discovered = 1735, .atomic_mass = 5893, .electronegativity = 188, .group = " T" },
+    { .symbol = "NI", .name = "Nickel", .year_discovered = 1751, .atomic_mass = 5869, .electronegativity = 191, .group = " T" },
+    { .symbol = "CU", .name = "Copper", .year_discovered = -9000, .atomic_mass = 6355, .electronegativity = 190, .group = " T" },
+    { .symbol = "ZN", .name = "Zinc", .year_discovered = -1000, .atomic_mass = 6538, .electronegativity = 165, .group = " T" },
+    { .symbol = "GA", .name = "Gallium", .year_discovered = 1875, .atomic_mass = 6972, .electronegativity = 181, .group = "3" },
+    { .symbol = "GE", .name = "Germanium", .year_discovered = 1886, .atomic_mass = 7263, .electronegativity = 201, .group = "4" },
+    { .symbol = "AS", .name = "Arsenic", .year_discovered = 300, .atomic_mass = 7492, .electronegativity = 218, .group = "5" },
+    { .symbol = "SE", .name = "Selenium", .year_discovered = 1817, .atomic_mass = 7897, .electronegativity = 255, .group = "6" },
+    { .symbol = "BR", .name = "Bromine", .year_discovered = 1825, .atomic_mass = 7990., .electronegativity = 296, .group = "7" },
+    { .symbol = "KR", .name = "Krypton", .year_discovered = 1898, .atomic_mass = 8380, .electronegativity = 300, .group = "0" },
+    { .symbol = "RB", .name = "Rubidium", .year_discovered = 1861, .atomic_mass = 8547, .electronegativity = 82, .group = "1" },
+    { .symbol = "SR", .name = "Strontium", .year_discovered = 1787, .atomic_mass = 8762, .electronegativity = 95, .group = "2" },
+    { .symbol = "Y", .name = "Yttrium", .year_discovered = 1794, .atomic_mass = 8891, .electronegativity = 122, .group = " T" },
+    { .symbol = "ZR", .name = "Zirconium", .year_discovered = 1789, .atomic_mass = 9122, .electronegativity = 133, .group = " T" },
+    { .symbol = "NB", .name = "Niobium", .year_discovered = 1801, .atomic_mass = 9291, .electronegativity = 160, .group = " T" },
+    { .symbol = "MO", .name = "Molybdenum", .year_discovered = 1778, .atomic_mass = 9595, .electronegativity = 216, .group = " T" },
+    { .symbol = "TC", .name = "Technetium", .year_discovered = 1937, .atomic_mass = 9700, .electronegativity = 190, .group = " T" },
+    { .symbol = "RU", .name = "Ruthenium", .year_discovered = 1844, .atomic_mass = 10107, .electronegativity = 220, .group = " T" },
+    { .symbol = "RH", .name = "Rhodium", .year_discovered = 1804, .atomic_mass = 10291, .electronegativity = 228, .group = " T" },
+    { .symbol = "PD", .name = "Palladium", .year_discovered = 1802, .atomic_mass = 10642, .electronegativity = 220, .group = " T" },
+    { .symbol = "AG", .name = "Silver", .year_discovered = -5000, .atomic_mass = 10787, .electronegativity = 193, .group = " T" },
+    { .symbol = "CD", .name = "Cadmium", .year_discovered = 1817, .atomic_mass = 11241, .electronegativity = 169, .group = " T" },
+    { .symbol = "IN", .name = "Indium", .year_discovered = 1863, .atomic_mass = 11482, .electronegativity = 178, .group = "3" },
+    { .symbol = "SN", .name = "Tin", .year_discovered = -3500, .atomic_mass = 11871, .electronegativity = 196, .group = "4" },
+    { .symbol = "SB", .name = "Antimony", .year_discovered = -3000, .atomic_mass = 12176, .electronegativity = 205, .group = "5" },
+    { .symbol = "TE", .name = "Tellurium", .year_discovered = 1782, .atomic_mass = 12760, .electronegativity = 210, .group = "6" },
+    { .symbol = "I", .name = "Iodine", .year_discovered = 1811, .atomic_mass = 12690, .electronegativity = 266, .group = "7" },
+    { .symbol = "XE", .name = "Xenon", .year_discovered = 1898, .atomic_mass = 13129, .electronegativity = 260, .group = "0" },
+    { .symbol = "CS", .name = "Caesium", .year_discovered = 1860, .atomic_mass = 13291, .electronegativity = 79, .group = "1" },
+    { .symbol = "BA", .name = "Barium", .year_discovered = 1772, .atomic_mass = 13733., .electronegativity = 89, .group = "2" },
+    { .symbol = "LA", .name = "Lanthanum", .year_discovered = 1838, .atomic_mass = 13891, .electronegativity = 110, .group = "1a" },
+    { .symbol = "CE", .name = "Cerium", .year_discovered = 1803, .atomic_mass = 14012, .electronegativity = 112, .group = "1a" },
+    { .symbol = "PR", .name = "Praseodymium", .year_discovered = 1885, .atomic_mass = 14091, .electronegativity = 113, .group = "1a" },
+    { .symbol = "ND", .name = "Neodymium", .year_discovered = 1841, .atomic_mass = 14424, .electronegativity = 114, .group = "1a" },
+    { .symbol = "PM", .name = "Promethium", .year_discovered = 1945, .atomic_mass = 14500, .electronegativity = 113, .group = "1a" },
+    { .symbol = "SM", .name = "Samarium", .year_discovered = 1879, .atomic_mass = 15036., .electronegativity = 117, .group = "1a" },
+    { .symbol = "EU", .name = "Europium", .year_discovered = 1896, .atomic_mass = 15196, .electronegativity = 120, .group = "1a" },
+    { .symbol = "GD", .name = "Gadolinium", .year_discovered = 1880, .atomic_mass = 15725, .electronegativity = 120, .group = "1a" },
+    { .symbol = "TB", .name = "Terbium", .year_discovered = 1843, .atomic_mass = 15893, .electronegativity = 120, .group = "1a" },
+    { .symbol = "DY", .name = "Dysprosium", .year_discovered = 1886, .atomic_mass = 16250, .electronegativity = 122, .group = "1a" },
+    { .symbol = "HO", .name = "Holmium", .year_discovered = 1878, .atomic_mass = 16493, .electronegativity = 123, .group = "1a" },
+    { .symbol = "ER", .name = "Erbium", .year_discovered = 1843, .atomic_mass = 16726, .electronegativity = 124, .group = "1a" },
+    { .symbol = "TM", .name = "Thulium", .year_discovered = 1879, .atomic_mass = 16893, .electronegativity = 125, .group = "1a" },
+    { .symbol = "YB", .name = "Ytterbium", .year_discovered = 1878, .atomic_mass = 17305, .electronegativity = 110, .group = "1a" },
+    { .symbol = "LU", .name = "Lutetium", .year_discovered = 1906, .atomic_mass = 17497, .electronegativity = 127, .group = "1a" },
+    { .symbol = "HF", .name = "Hafnium", .year_discovered = 1922, .atomic_mass = 17849, .electronegativity = 130, .group = " T" },
+    { .symbol = "TA", .name = "Tantalum", .year_discovered = 1802, .atomic_mass = 18095, .electronegativity = 150, .group = " T" },
+    { .symbol = "W", .name = "Tungsten", .year_discovered = 1781, .atomic_mass = 18384, .electronegativity = 236, .group = " T" },
+    { .symbol = "RE", .name = "Rhenium", .year_discovered = 1908, .atomic_mass = 18621, .electronegativity = 190, .group = " T" },
+    { .symbol = "OS", .name = "Osmium", .year_discovered = 1803, .atomic_mass = 19023, .electronegativity = 220, .group = " T" },
+    { .symbol = "IR", .name = "Iridium", .year_discovered = 1803, .atomic_mass = 19222, .electronegativity = 220, .group = " T" },
+    { .symbol = "PT", .name = "Platinum", .year_discovered = -600, .atomic_mass = 19508, .electronegativity = 228, .group = " T" },
+    { .symbol = "AU", .name = "Gold", .year_discovered = -6000, .atomic_mass = 19697, .electronegativity = 254, .group = " T" },
+    { .symbol = "HG", .name = "Mercury", .year_discovered = -1500, .atomic_mass = 20059, .electronegativity = 200, .group = " T" },
+    { .symbol = "TL", .name = "Thallium", .year_discovered = 1861, .atomic_mass = 20438, .electronegativity = 162, .group = "3" },
+    { .symbol = "PB", .name = "Lead", .year_discovered = -7000, .atomic_mass = 20720, .electronegativity = 187, .group = "4" },
+    { .symbol = "BI", .name = "Bismuth", .year_discovered = 1500, .atomic_mass = 20898, .electronegativity = 202, .group = "5" },
+    { .symbol = "PO", .name = "Polonium", .year_discovered = 1898, .atomic_mass = 20900, .electronegativity = 200, .group = "6" },
+    { .symbol = "AT", .name = "Astatine", .year_discovered = 1940, .atomic_mass = 21000, .electronegativity = 220, .group = "7" },
+    { .symbol = "RN", .name = "Radon", .year_discovered = 1899, .atomic_mass = 22200, .electronegativity = 220, .group = "0" },
+    { .symbol = "FR", .name = "Francium", .year_discovered = 1939, .atomic_mass = 22300, .electronegativity = 79, .group = "1" },
+    { .symbol = "RA", .name = "Radium", .year_discovered = 1898, .atomic_mass = 22600, .electronegativity = 90, .group = "2" },
+    { .symbol = "AC", .name = "Actinium", .year_discovered = 1902, .atomic_mass = 22700, .electronegativity = 110, .group = "Ac" },
+    { .symbol = "TH", .name = "Thorium", .year_discovered = 1829, .atomic_mass = 23204, .electronegativity = 130, .group = "Ac" },
+    { .symbol = "PA", .name = "Protactinium", .year_discovered = 1913, .atomic_mass = 23104, .electronegativity = 150, .group = "Ac" },
+    { .symbol = "U", .name = "Uranium", .year_discovered = 1789, .atomic_mass = 23803, .electronegativity = 138, .group = "Ac" },
+    { .symbol = "NP", .name = "Neptunium", .year_discovered = 1940, .atomic_mass = 23700, .electronegativity = 136, .group = "Ac" },
+    { .symbol = "PU", .name = "Plutonium", .year_discovered = 1941, .atomic_mass = 24400, .electronegativity = 128, .group = "Ac" },
+    { .symbol = "AM", .name = "Americium", .year_discovered = 1944, .atomic_mass = 24300, .electronegativity = 113, .group = "Ac" },
+    { .symbol = "CM", .name = "Curium", .year_discovered = 1944, .atomic_mass = 24700, .electronegativity = 128, .group = "Ac" },
+    { .symbol = "BK", .name = "Berkelium", .year_discovered = 1949, .atomic_mass = 24700, .electronegativity = 130, .group = "Ac" },
+    { .symbol = "CF", .name = "Californium", .year_discovered = 1950, .atomic_mass = 25100, .electronegativity = 130, .group = "Ac" },
+    { .symbol = "ES", .name = "Einsteinium", .year_discovered = 1952, .atomic_mass = 25200, .electronegativity = 130, .group = "Ac" },
+    { .symbol = "FM", .name = "Fermium", .year_discovered = 1953, .atomic_mass = 25700, .electronegativity = 130, .group = "Ac" },
+    { .symbol = "MD", .name = "Mendelevium", .year_discovered = 1955, .atomic_mass = 25800, .electronegativity = 130, .group = "Ac" },
+    { .symbol = "NO", .name = "Nobelium", .year_discovered = 1965, .atomic_mass = 25900, .electronegativity = 130, .group = "Ac" },
+    { .symbol = "LR", .name = "Lawrencium", .year_discovered = 1961, .atomic_mass = 26600, .electronegativity = 130, .group = "Ac" },
+    { .symbol = "RF", .name = "Rutherfordium", .year_discovered = 1969, .atomic_mass = 26700, .electronegativity = 0, .group = " T" },
+    { .symbol = "DB", .name = "Dubnium", .year_discovered = 1970, .atomic_mass = 26800, .electronegativity = 0, .group = " T" },
+    { .symbol = "SG", .name = "Seaborgium", .year_discovered = 1974, .atomic_mass = 26700, .electronegativity = 0, .group = " T" },
+    { .symbol = "BH", .name = "Bohrium", .year_discovered = 1981, .atomic_mass = 27000, .electronegativity = 0, .group = " T" },
+    { .symbol = "HS", .name = "Hassium", .year_discovered = 1984, .atomic_mass = 27100, .electronegativity = 0, .group = " T" },
+    { .symbol = "MT", .name = "Meitnerium", .year_discovered = 1982, .atomic_mass = 27800, .electronegativity = 0, .group = " T" },
+    { .symbol = "DS", .name = "Darmstadtium", .year_discovered = 1994, .atomic_mass = 28100, .electronegativity = 0, .group = " T" },
+    { .symbol = "RG", .name = "Roentgenium", .year_discovered = 1994, .atomic_mass = 28200, .electronegativity = 0, .group = " T" },
+    { .symbol = "CN", .name = "Copernicium", .year_discovered = 1996, .atomic_mass = 28500, .electronegativity = 0, .group = " T" },
+    { .symbol = "NH", .name = "Nihonium", .year_discovered = 2004, .atomic_mass = 28600, .electronegativity = 0, .group = "3" },
+    { .symbol = "FL", .name = "Flerovium", .year_discovered = 1999, .atomic_mass = 28900, .electronegativity = 0, .group = "4" },
+    { .symbol = "MC", .name = "Moscovium", .year_discovered = 2003, .atomic_mass = 29000, .electronegativity = 0, .group = "5" },
+    { .symbol = "LW", .name = "Livermorium", .year_discovered = 2000, .atomic_mass = 29300, .electronegativity = 0, .group = "6" },
+    { .symbol = "TS", .name = "Tennessine", .year_discovered = 2009, .atomic_mass = 29400, .electronegativity = 0, .group = "7" },
+    { .symbol = "OG", .name = "Oganesson", .year_discovered = 2002, .atomic_mass = 29400, .electronegativity = 0, .group = "0" },
+};
+
+static void _make_upper(char *string) {
+    size_t i = 0;
+    while(string[i] != 0) {
+        if (string[i] >= 'a' && string[i] <= 'z')
+            string[i]-=32;  // 32 = 'a'-'A'
+        i++;
+    }
+}
+
+static void _display_element(periodic_state_t *state)
+{
+    char buf[9];
+    char ele[3];
+    uint8_t atomic_num = state->atomic_num;
+    strcpy(ele, table[atomic_num - 1].symbol);
+    _make_upper(ele);
+    sprintf(buf, "%2s%3d %-2s", table[atomic_num - 1].group, atomic_num, ele);
+    watch_display_string(buf, 2);
+}
+
+static void _display_atomic_mass(periodic_state_t *state)
+{
+    char buf[11];
+    uint16_t mass = table[state->atomic_num - 1].atomic_mass;
+    uint16_t integer = mass / 100;
+    uint16_t decimal = mass % 100;
+    if (decimal == 0)
+        sprintf(buf, "%-2s%2s%4d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer);
+    else
+        sprintf(buf, "%-2s%2s%3d_%.2d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer, decimal);
+    watch_display_string(buf, 0); 
+}
+
+static void _display_year_discovered(periodic_state_t *state)
+{
+    char buf[11];
+    char year_buf[7];
+    int16_t year = table[state->atomic_num - 1].year_discovered;
+    if (abs(year) > 9999)
+        sprintf(year_buf, "----  ");
+    else
+        sprintf(year_buf, "%4d  ", abs(year));
+    if (year < 0) {
+        year_buf[4] = 'b';
+        year_buf[5] = 'c';
+    }
+    sprintf(buf, "%-2s%-2s%s", table[state->atomic_num - 1].symbol, screen_name[state->mode], year_buf);
+    watch_display_string(buf, 0); 
+}
+
+static void _display_name(periodic_state_t *state)
+{
+    char buf[11];
+    _text_looping = table[state->atomic_num - 1].name;
+    _text_pos = 0;
+    sprintf(buf, "%-2s%-2s%s", table[state->atomic_num - 1].symbol, screen_name[state->mode], table[state->atomic_num - 1].name);
+    watch_display_string(buf, 0); 
+}
+
+static void _display_electronegativity(periodic_state_t *state)
+{
+    char buf[11];
+    uint16_t electronegativity = table[state->atomic_num - 1].electronegativity;
+    uint16_t integer = electronegativity / 100;
+    uint16_t decimal = electronegativity % 100;
+    if (decimal == 0)
+        sprintf(buf, "%-2s%2s%4d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer);
+    else
+        sprintf(buf, "%-2s%2s%3d_%.2d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer, decimal);
+    watch_display_string(buf, 0); 
+}
+
+static void start_quick_cyc(void){
+    _quick_ticks_running = true;
+    movement_request_tick_frequency(FREQ_FAST);
+}
+
+static void stop_quick_cyc(void){
+    _quick_ticks_running = false;
+    movement_request_tick_frequency(FREQ);
+}
+
+static int16_t _loop_text(const char* text, int8_t curr_loc, uint8_t char_len){
+    // if curr_loc, then use that many ticks as a delay before looping
+    char buf[15];
+    uint8_t next_pos;
+    uint8_t text_len = strlen(text);
+    uint8_t pos = 10 - char_len;
+    if (curr_loc == -1) curr_loc = 0;  // To avoid double-showing the 0
+    if (char_len >= text_len || curr_loc < 0) {
+        sprintf(buf, "%s", text);
+        watch_display_string(buf, pos);
+        if (curr_loc < 0) return ++curr_loc;
+        return 0;
+    }
+    else if (curr_loc == (text_len + 1))
+        curr_loc = 0;
+    next_pos = curr_loc + 1;
+    sprintf(buf, "%.6s %.6s", text + curr_loc, text);
+    watch_display_string(buf, pos);
+    return next_pos;
+}
+
+static void _display_title(periodic_state_t *state){
+    state->atomic_num = 0;
+    watch_clear_colon();
+    watch_clear_all_indicators();
+    _text_looping = title_text;
+    _text_pos = FREQ * -1;
+    _text_pos = _loop_text(_text_looping, _text_pos, 5);
+}
+
+static void _display_screen(periodic_state_t *state, bool should_sound){
+    watch_clear_display();
+    watch_clear_all_indicators();
+    switch (state->mode)
+    {
+    case SCREEN_TITLE:
+        _display_title(state);
+        break;
+    case SCREEN_ELEMENT:
+    case SCREENS_COUNT:
+        _display_element(state);
+        break;
+    case SCREEN_ATOMIC_MASS:
+        _display_atomic_mass(state);
+        break;
+    case SCREEN_DISCOVER_YEAR:
+        _display_year_discovered(state);
+        break;
+    case SCREEN_ELECTRONEGATIVITY:
+        _display_electronegativity(state);
+        break;
+    case SCREEN_FULL_NAME:
+        _display_name(state);
+        break;
+    }
+    if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+}
+
+static void _handle_forward(periodic_state_t *state, bool should_sound){
+    state->atomic_num = (state->atomic_num % MAX_ELEMENT) + 1; // Wraps back to 1
+    state->mode = SCREEN_ELEMENT;
+    _display_screen(state, false);
+    if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+}
+
+static void _handle_backward(periodic_state_t *state, bool should_sound){
+    if (state->atomic_num <= 1) state->atomic_num = MAX_ELEMENT;
+    else state->atomic_num = state->atomic_num - 1;
+    state->mode = SCREEN_ELEMENT;
+    _display_screen(state, false);
+    if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_A6, 50);
+}
+
+static void _handle_mode_still_pressed(periodic_state_t *state, bool should_sound) {
+    if (_ts_ticks != 0){
+        if (!HAL_GPIO_BTN_MODE_read()) {
+            _ts_ticks = 0;
+            return;
+        }
+        else if (--_ts_ticks == 0){
+            switch (state->mode)
+            {
+            case SCREEN_TITLE:
+                movement_move_to_face(0);
+                return;
+            case SCREEN_ELEMENT:
+                state->mode = SCREEN_TITLE;
+                _display_screen(state, should_sound);
+                break;
+            default:
+                state->mode = SCREEN_ELEMENT;
+                _display_screen(state, should_sound);
+                break;
+            }
+            _ts_ticks = 2;
+        }
+    }
+}
+
+bool periodic_face_loop(movement_event_t event, void *context)
+{
+    periodic_state_t *state = (periodic_state_t *)context;
+    switch (event.event_type)
+    {
+    case EVENT_ACTIVATE:
+        state->mode = SCREEN_TITLE;
+        _display_screen(state, false);
+        break;
+    case EVENT_TICK:
+        if (state->mode == SCREEN_TITLE) _text_pos = _loop_text(_text_looping, _text_pos, 5);
+        else if (state->mode == SCREEN_FULL_NAME) _text_pos = _loop_text(_text_looping, _text_pos, 6);
+        if (_quick_ticks_running) {
+            if (HAL_GPIO_BTN_LIGHT_read()) _handle_backward(state, false);
+            else if (HAL_GPIO_BTN_ALARM_read()) _handle_forward(state, false);
+            else stop_quick_cyc();
+        }
+
+        _handle_mode_still_pressed(state, movement_button_should_sound());
+        break;
+    case EVENT_LIGHT_BUTTON_UP:
+        if (state->mode <= SCREEN_ELEMENT) {
+            _handle_backward(state, movement_button_should_sound());
+        }
+        else {
+            state->mode = SCREEN_ELEMENT;
+            _display_screen(state, movement_button_should_sound());
+        }
+        break;
+    case EVENT_LIGHT_BUTTON_DOWN:
+        break;
+    case EVENT_ALARM_BUTTON_UP:
+        if (state->mode <= SCREEN_ELEMENT) {
+            _handle_forward(state, movement_button_should_sound());
+        }
+        else {
+            state->mode = SCREEN_ELEMENT;
+            _display_screen(state, movement_button_should_sound());
+        }
+        break;
+    case EVENT_ALARM_LONG_PRESS:
+        if (state->mode <= SCREEN_ELEMENT) {
+            start_quick_cyc();
+            _handle_forward(state, movement_button_should_sound());
+        }
+        break;
+    case EVENT_LIGHT_LONG_PRESS:
+        if (state->mode <= SCREEN_ELEMENT) {
+            start_quick_cyc();
+            _handle_backward(state, movement_button_should_sound());
+        }
+        else {
+            movement_illuminate_led();
+        }
+        break;
+    case EVENT_MODE_BUTTON_UP:
+        if (state->mode == SCREEN_TITLE) movement_move_to_next_face();
+        else {
+            state->mode = (state->mode + 1) % SCREENS_COUNT;
+            if (state->mode == SCREEN_TITLE)
+                state->mode = (state->mode + 1) % SCREENS_COUNT;
+            if (state->mode == SCREEN_ELEMENT){
+                _display_screen(state, false);
+                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_A6, 50);
+            }
+            else
+                _display_screen(state, movement_button_should_sound());
+        }
+        break;
+    case EVENT_MODE_LONG_PRESS:
+        switch (state->mode)
+        {
+        case SCREEN_TITLE:
+            movement_move_to_face(0);
+            return true;
+        case SCREEN_ELEMENT:
+            state->mode = SCREEN_TITLE;
+            _display_screen(state, movement_button_should_sound());
+            break;
+        default:
+            state->mode = SCREEN_ELEMENT;
+            _display_screen(state, movement_button_should_sound());
+            break;
+        }
+        _ts_ticks = 2;
+        break;
+    case EVENT_TIMEOUT:
+        // Display title after timeout
+        if (state->mode == SCREEN_TITLE) break;
+        state->mode = SCREEN_TITLE;
+        _display_screen(state, false);
+        break;
+    case EVENT_LOW_ENERGY_UPDATE:
+        // Display static title and tick animation during LE
+        watch_display_string("Pd   Table", 0);
+        watch_start_sleep_animation(500);
+        break;
+    default:
+        return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void periodic_face_resign(void *context)
+{
+    (void)context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
diff --git a/legacy/watch_faces/complication/periodic_face.h b/legacy/watch_faces/complication/periodic_face.h
new file mode 100644 (file)
index 0000000..409b1d8
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 PrimmR
+ * Copyright (c) 2024 David Volovskiy
+ *
+ * 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 PERIODIC_FACE_H_
+#define PERIODIC_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Periodic Table Face
+ * Allows for viewing data of the Periodic Table on your wrist.
+ * When looking at an element, it'll show you the atomic number on the center of the screen,
+ * symbol on the right, and it's group on the top-right.
+ * Pressing the mode button will cycle through the pages.
+ *   Page 1: Atomic Mass
+ *   Page 2: Year Discovered
+ *   Page 3: Electronegativity
+ *   Page 4: Full Name of the Element
+ * 
+ * Controls:
+ *    Mode Press
+ *        On Title: Next Screen
+ *        Else: Cycle through info of an element
+ *    Mode Hold
+ *        On Title: First Screen
+ *        On Element Symbol Screen: Go to Title Screen
+ *        Else: Go to Symbol Screen of current element
+ *    If you are in a subscreen and just keep holding MODE, you will go through all of these menus without needing to depress.
+ * 
+ *    Light Press
+ *        On Title or Element Symbol Screen: Previous Element
+ *                                     Else: Display currenlt-selected element symbol page
+ *    Light Hold
+ *        On Title Screen or Element Symbol: Fast Cycle through Previous Elements
+ *                                     Else: Activate LED backlight
+ *
+ *    Alarm Press
+ *        On Title or Element Symbol Screen: Next Element
+ *                                     Else: Display currenlt-selected element symbol page
+ *    Alarm Hold
+ *        On Title Screen or Element Symbol: Fast Cycle through Next Elements
+ */
+
+#define MODE_VIEW 0
+#define MODE_SELECT 1
+
+typedef struct {
+    uint8_t atomic_num;
+    uint8_t mode;
+    uint8_t selection_index;
+} periodic_state_t;
+
+void periodic_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void periodic_face_activate(void *context);
+bool periodic_face_loop(movement_event_t event, void *context);
+void periodic_face_resign(void *context);
+
+#define periodic_face ((const watch_face_t){ \
+    periodic_face_setup, \
+    periodic_face_activate, \
+    periodic_face_loop, \
+    periodic_face_resign, \
+    NULL, \
+})
+
+#endif // PERIODIC_FACE_H_
+
diff --git a/legacy/watch_faces/complication/planetary_hours_face.c b/legacy/watch_faces/complication/planetary_hours_face.c
new file mode 100644 (file)
index 0000000..f4011ba
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "sunriset.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "planetary_hours_face.h"
+
+#if __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+
+// STATIC FUNCTIONS AND CONSTANTS /////////////////////////////////////////////
+
+/** @brief 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
+ */
+static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin
+static const char planetes[7][3] = {"Ch", "Ze", "Ar", "He", "Af", "Hr", "Se"}; // Greek
+
+/** @brief Ruler of each weekday for easy lookup
+ */
+static const uint8_t plindex[7] = {3, 6, 2, 5, 1, 4, 0}; // day ruler index
+
+/** @brief Astrological symbol for each planet
+ */
+static void _planetary_icon(uint8_t planet) {
+
+    watch_clear_pixel(0, 13);
+    watch_clear_pixel(0, 14);
+    watch_clear_pixel(1, 13);
+    watch_clear_pixel(1, 14);
+    watch_clear_pixel(1, 15);
+    watch_clear_pixel(2, 13);
+    watch_clear_pixel(2, 14);
+    watch_clear_pixel(2, 15);
+
+    switch (planet) {
+        case 0: // Saturn
+            watch_set_pixel(0, 14);
+            watch_set_pixel(2, 14);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(2, 13);
+            break;
+        case 1: // Jupiter
+            watch_set_pixel(0, 14);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(1, 14);
+            break;
+        case 2: // Mars
+            watch_set_pixel(2, 14);
+            watch_set_pixel(2, 15);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(2, 13);
+            watch_set_pixel(1, 13);\
+            break;
+        case 3: // Sol
+            watch_set_pixel(0, 14);
+            watch_set_pixel(2, 14);
+            watch_set_pixel(1, 13);
+            watch_set_pixel(2, 13);
+            watch_set_pixel(0, 13);
+            watch_set_pixel(2, 15);
+            break;
+        case 4: // Venus
+            watch_set_pixel(0, 14);
+            watch_set_pixel(0, 13);
+            watch_set_pixel(1, 13);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(1, 14);
+            break;
+        case 5: // Mercury
+            watch_set_pixel(0, 14);
+            watch_set_pixel(1, 13);
+            watch_set_pixel(1, 14);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(2, 15);
+            break;
+        case 6: // Luna
+            watch_set_pixel(2, 14);
+            watch_set_pixel(2, 15);
+            watch_set_pixel(2, 13);
+            break;
+    }
+}
+
+/** @details A solar phase can be a day phase between sunrise and sunset or an alternating night phase.
+ *  This function calculates the start and end of the current phase based on a given geographic location.
+ *  It also calculates the start of the next following phase.
+ */
+static void _planetary_solar_phases(planetary_hours_state_t *state) {
+    uint8_t phase, h;
+    double sunrise, sunset;
+    double hour_duration, next_hour_duration;
+    uint32_t now_epoch;
+    uint32_t sunrise_epoch_today, sunset_epoch_today, midnight_epoch_today;
+    uint32_t sunset_epoch_yesterday, midnight_epoch_yesterday;
+    uint32_t sunrise_epoch_tomorrow, sunset_epoch_tomorrow, midnight_epoch_tomorrow;
+    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+
+    // check if we have a location. If not, display error
+    if (movement_location.reg == 0) {
+        watch_display_string("    no Loc", 0);
+        state->no_location = true;
+        return;
+    }
+
+    // location detected
+    state->no_location = false;
+
+    watch_date_time_t date_time = watch_rtc_get_date_time(); // the current local date / time
+    watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0); // the current date / time in UTC
+    watch_date_time_t scratch_time; // scratchpad, contains different values at different times
+    watch_date_time_t midnight;
+    scratch_time.reg = midnight.reg = utc_now.reg;
+    midnight.unit.hour = midnight.unit.minute = midnight.unit.second = 0; // start of the day at midnight
+
+    // get location coordinate
+    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
+    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
+    double lat = (double)lat_centi / 100.0;
+    double lon = (double)lon_centi / 100.0;
+
+    // save UTC offset
+    state->utc_offset = ((double)movement_get_current_timezone_offset()) / 3600.0;
+
+    // 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);
+    
+    // calculate sunrise and sunset UNIX timestamps
+    midnight_epoch_today = watch_utility_date_time_to_unix_time(midnight, 0);
+    sunrise_epoch_today = midnight_epoch_today + sunrise * 3600;
+    sunset_epoch_today = midnight_epoch_today + sunset * 3600;
+
+    // go back to yesterday and calculate sunset
+    midnight_epoch_yesterday = midnight_epoch_today - 86400;
+    scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch_yesterday, 0);
+    sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+    sunset_epoch_yesterday = midnight_epoch_yesterday + sunset * 3600;
+
+    // go to tomorrow and calculate sunrise and sunset
+    midnight_epoch_tomorrow = midnight_epoch_today + 86400;
+    scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch_tomorrow, 0);
+    sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+    sunrise_epoch_tomorrow = midnight_epoch_tomorrow + sunrise * 3600;
+    sunset_epoch_tomorrow = midnight_epoch_tomorrow + sunset * 3600;
+
+    // get UNIX epoch time
+    now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
+
+    // by default we assume it is daytime (phase 1) between sunrise and sunset
+    phase = 1;
+    state->phase_start = sunrise_epoch_today;
+    state->phase_end = sunset_epoch_today;
+    state->phase_next = sunrise_epoch_tomorrow;
+    state->start_at_night = false;
+
+    // night time calculations
+    if ( now_epoch < sunrise_epoch_today && now_epoch < sunset_epoch_today ) phase = 0; // morning before dawn
+    if ( now_epoch > sunrise_epoch_today && now_epoch >= sunset_epoch_today ) phase = 2; // evening after dusk
+
+    // phase 0: we are before sunrise
+    if ( phase == 0) {
+        state->phase_start = sunset_epoch_yesterday;
+        state->phase_end = sunrise_epoch_today;
+        state->phase_next = sunset_epoch_today;
+        state->start_at_night = true;
+    }
+
+    // phase 2: we are after sunset
+    if ( phase == 2) {
+        state->phase_start = sunset_epoch_today;
+        state->phase_end = sunrise_epoch_tomorrow;
+        state->phase_next = sunset_epoch_tomorrow;
+        state->start_at_night = true;
+    }
+
+    // calculate the duration of a planetary hour during this and the next solar phase
+    hour_duration = ( state->phase_end - state->phase_start ) / 12.0;
+    next_hour_duration = ( state->phase_next - state->phase_end ) / 12.0;
+
+    // populate list of 24 planetary hour start points in UNIX timestamp format
+    // starting from the beginning of the current phase
+    for ( h = 0; h < 24; h++ ) {
+        if ( h < 12 ) state->planetary_hours[h] = state->phase_start + h * hour_duration; // current phase
+        else state->planetary_hours[h] = state->phase_end + ( h - 12 ) * next_hour_duration; // next phase
+    }
+
+    // initialize
+    state->hour = 0;
+    state->ruler = 0;
+    state->skip_to_current = true;
+
+}
+
+/** @details A planetary hour is one of exactly twelve hours of a solar phase. Its length varies.
+ *  This function calculates the current planetary hour and divides it up into relative minutes and seconds.
+ *  It also calculates the current planetary ruler of the hour and of the day.
+ */
+static void _planetary_hours(planetary_hours_state_t *state) {
+    char buf[14];
+    char ruler[3];
+    uint8_t weekday, planet, planetary_hour;
+    uint32_t current_hour_epoch;
+    watch_date_time_t scratch_time;
+
+    // check if we have a location. If not, display error
+    if ( state->no_location ) {
+        watch_display_string("    no Loc", 0);
+        return;
+    }
+
+    // get current time
+    watch_date_time_t date_time = watch_rtc_get_date_time(); // the current local date / time
+    watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0); // the current date / time in UTC
+    current_hour_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
+    
+    // set the current planetary hour as default screen
+    if ( state->skip_to_current ) {
+        state->hour = ( current_hour_epoch - state->phase_start ) / (( state->phase_end - state->phase_start ) / 12.0);
+        state->skip_to_current = false;
+    }
+    
+
+    // when current phase ends calculate the next phase
+    if ( watch_utility_date_time_to_unix_time(utc_now, 0) >= state->phase_end ) {
+        _planetary_solar_phases(state);
+        return;
+    }
+
+    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
+
+    // roll over hour iterator
+    if ( state->hour < 0 ) state->hour = 23;
+    if ( state->hour > 23 ) state->hour = 0;
+    if ( state->ruler < 0 ) state->hour = 2;
+    if ( state->ruler > 2 ) state->hour = 0;
+
+    // clear indicators
+    watch_clear_indicator(WATCH_INDICATOR_BELL);
+    watch_clear_indicator(WATCH_INDICATOR_LAP);
+
+    // display bell indicator when displaying the current planetary hour
+    if ( state->hour < 24 )
+        if ( current_hour_epoch >= state->planetary_hours[state->hour] && current_hour_epoch < state->planetary_hours[state->hour + 1]) {
+            watch_set_indicator(WATCH_INDICATOR_BELL);
+        }
+
+    // display LAP indicator when the hours of the next phase belong to the next day
+    if ( state->start_at_night == true && state->hour > 11 )
+        watch_set_indicator(WATCH_INDICATOR_LAP);
+
+    // determine weekday from start of current phase
+    scratch_time = watch_utility_date_time_from_unix_time(state->phase_start, 0);
+    scratch_time = watch_utility_date_time_convert_zone(scratch_time, 0, state->utc_offset * 3600);
+    weekday = watch_utility_get_iso8601_weekday_number(scratch_time.unit.year, scratch_time.unit.month, scratch_time.unit.day) - 1;
+    
+    // which planetary hour are we in?
+    planetary_hour = state->hour % 12;
+
+    // accomodate night hour count
+    if ( state->hour < 12 ) {
+        if ( state->start_at_night ) {
+            planetary_hour += 12;
+        }
+    } else {
+        if ( state->start_at_night ) {
+            weekday = ( weekday + 1 ) % 7;
+        } else {
+            planetary_hour += 12;
+        }
+    }
+
+    // make datetime object for selected planetary hour
+    scratch_time = watch_utility_date_time_from_unix_time(state->planetary_hours[state->hour], 0);
+    scratch_time = watch_utility_date_time_convert_zone(scratch_time, 0, state->utc_offset * 3600);
+    
+    // round minutes
+    if (scratch_time.unit.second < 30 && scratch_time.unit.minute > 0 ) scratch_time.unit.minute--;
+    else if ( scratch_time.unit.minute < 59 ) scratch_time.unit.minute++;
+
+    // if we are in 12 hour mode, do some cleanup
+    if (!movement_clock_mode_24h()) {
+        if (scratch_time.unit.hour < 12) {
+            watch_clear_indicator(WATCH_INDICATOR_PM);
+        } else {
+            watch_set_indicator(WATCH_INDICATOR_PM);
+        }
+        scratch_time.unit.hour %= 12;
+        if (scratch_time.unit.hour == 0) scratch_time.unit.hour = 12;
+    }
+    
+    // planetary ruler of the hour
+    planet = ( plindex[weekday] + planetary_hour ) % 7;
+
+    // latin or greek ruler names or astrological symbol
+    if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3);
+    if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3);
+    if ( state->ruler == 2 ) strncpy(ruler, "  ", 3);
+
+    // display planetary time with ruler of the hour or ruler of the day
+    sprintf(buf, "%s%2d%2d%02d  ", ruler, (planetary_hour % 24) + 1, scratch_time.unit.hour, scratch_time.unit.minute);
+
+    watch_set_colon();
+    watch_display_string(buf, 0);
+
+    if ( state->ruler == 2 ) _planetary_icon(planet);
+}
+
+// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
+
+void planetary_hours_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(planetary_hours_state_t));
+        memset(*context_ptr, 0, sizeof(planetary_hours_state_t));
+    }
+}
+
+void planetary_hours_face_activate(void *context) {
+    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
+
+#if __EMSCRIPTEN__
+    int16_t browser_lat = EM_ASM_INT({ return lat; });
+    int16_t browser_lon = EM_ASM_INT({ return lon; });
+    if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
+        movement_location_t browser_loc;
+        browser_loc.bit.latitude = browser_lat;
+        browser_loc.bit.longitude = browser_lon;
+        watch_store_backup_data(browser_loc.reg, 1);
+    }
+#endif
+
+    planetary_hours_state_t *state = (planetary_hours_state_t *)context; 
+    _planetary_solar_phases(state);
+
+}
+
+bool planetary_hours_face_loop(movement_event_t event, void *context) {
+    planetary_hours_state_t *state = (planetary_hours_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Show your initial UI here.
+            watch_clear_indicator(WATCH_INDICATOR_PM);
+            watch_clear_indicator(WATCH_INDICATOR_24H);
+            _planetary_hours(state);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            state->ruler = (state->ruler + 1) % 3;
+            _planetary_hours(state);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            state->skip_to_current = true;
+            _planetary_hours(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            state->hour++;
+            _planetary_hours(state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->hour--;
+            _planetary_hours(state);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+    return true;
+}
+
+void planetary_hours_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/complication/planetary_hours_face.h b/legacy/watch_faces/complication/planetary_hours_face.h
new file mode 100644 (file)
index 0000000..5e94aa0
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ * Copyright (c) 2022 Joey Castillo (sunrise_sunset_face)
+ *
+ * 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 planetary_hours_face_H_
+#define planetary_hours_face_H_
+
+/*
+ * PLANETARY HOURS face
+ * 
+ * Background
+ *
+ * Both the 24 hour day and the order of our weekdays have quite esoteric roots.
+ * The ancient Egyptians divided the day up into 12 hours of sunlight and 12 hours
+ * of night time. Obviously the length of these hours varied throughout the year.
+ * 
+ * The Greeks assigned each hour a ruler of the planetary gods in the ancient
+ * "Chaldean" order from slowest (Chronos for Saturn) to fastest (Selene for Moon).
+ * Because 24 hours cannot be equally divided by seven, the planetary rulers carried
+ * over to the first hour of the next day, effectively ruling over the entire day 
+ * and lending the whole day their name. The seven day week was born.
+ * 
+ * PLANETARY HOUR CHART COMPLICATION
+ * 
+ * This complication watch face displays the start time of the current planetary hour 
+ * according to the given location and day of the year. The number of the current
+ * planetary hour (1 - 24) is indicated at the top right.
+ * 
+ * Short pressing the ALARM button flips through the start times of the following 
+ * planetary hours, long pressing it flips backwards in time. A long press of the 
+ * LIGHT button immediately switches back to the start time of the current hour.
+ * The Bell indicator always marks the current planetary hour in the list.
+ * The LAP indicator shows up when the hours of the next phase are part of the 
+ * upcoming day instead of the current one. This happens when the watch face is 
+ * launched after sunset.
+ * 
+ * The planetary ruler of the current hour and day is displayed at the top in 
+ * Latin or Greek shorthand notation:
+ * 
+ * Saturn (SA) / Chronos (CH) / ♄
+ * Jupiter (JU) / Zeus (ZE) / ♃
+ * Mars (MA) / Ares (AR) / ♂
+ * Sol (SO) / Helios (HE) / ☉
+ * Venus (VE) / Aphrodite (AF) / ♀
+ * Mercury (ME) / Hermes (HR) / ☿
+ * Luna (LU) / Selene (SE) / ☾
+ * 
+ * A short press of the LIGHT button toggles between Latin and Greek ruler shorthand
+ * notation.
+ * 
+ * (IMPORTANT: Make sure the watch's time, timezone and location are set correctly for this
+ * watch face to work properly!)
+ */
+
+#include "movement.h"
+#include "sunrise_sunset_face.h"
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint32_t planetary_hours[24];
+    uint32_t phase_start;
+    uint32_t phase_end;
+    uint32_t phase_next;
+    bool next;
+    double utc_offset;
+    bool no_location;
+    int8_t hour;
+    int8_t ruler;
+    bool start_at_night;
+    bool skip_to_current;
+    sunrise_sunset_state_t sunstate;
+} planetary_hours_state_t;
+
+void planetary_hours_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void planetary_hours_face_activate(void *context);
+bool planetary_hours_face_loop(movement_event_t event, void *context);
+void planetary_hours_face_resign(void *context);
+
+#define planetary_hours_face ((const watch_face_t){ \
+    planetary_hours_face_setup, \
+    planetary_hours_face_activate, \
+    planetary_hours_face_loop, \
+    planetary_hours_face_resign, \
+    NULL, \
+})
+
+#endif // planetary_hours_face_H_
+
diff --git a/legacy/watch_faces/complication/planetary_time_face.c b/legacy/watch_faces/complication/planetary_time_face.c
new file mode 100644 (file)
index 0000000..d19937b
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "sunriset.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "planetary_time_face.h"
+
+#if __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+
+// STATIC FUNCTIONS AND CONSTANTS /////////////////////////////////////////////
+
+/** @brief 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
+ */
+static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin
+static const char planetes[7][3] = {"Ch", "Ze", "Ar", "He", "Af", "Hr", "Se"}; // Greek
+
+/** @brief Ruler of each weekday for easy lookup
+ */
+static const uint8_t plindex[7] = {3, 6, 2, 5, 1, 4, 0}; // day ruler index
+
+/** @brief Astrological symbol for each planet
+ */
+static void _planetary_icon(uint8_t planet) {
+
+    watch_clear_pixel(0, 13);
+    watch_clear_pixel(0, 14);
+    watch_clear_pixel(1, 13);
+    watch_clear_pixel(1, 14);
+    watch_clear_pixel(1, 15);
+    watch_clear_pixel(2, 13);
+    watch_clear_pixel(2, 14);
+    watch_clear_pixel(2, 15);
+
+    switch (planet) {
+        case 0: // Saturn
+            watch_set_pixel(0, 14);
+            watch_set_pixel(2, 14);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(2, 13);
+            break;
+        case 1: // Jupiter
+            watch_set_pixel(0, 14);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(1, 14);
+            break;
+        case 2: // Mars
+            watch_set_pixel(2, 14);
+            watch_set_pixel(2, 15);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(2, 13);
+            watch_set_pixel(1, 13);\
+            break;
+        case 3: // Sol
+            watch_set_pixel(0, 14);
+            watch_set_pixel(2, 14);
+            watch_set_pixel(1, 13);
+            watch_set_pixel(2, 13);
+            watch_set_pixel(0, 13);
+            watch_set_pixel(2, 15);
+            break;
+        case 4: // Venus
+            watch_set_pixel(0, 14);
+            watch_set_pixel(0, 13);
+            watch_set_pixel(1, 13);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(1, 14);
+            break;
+        case 5: // Mercury
+            watch_set_pixel(0, 14);
+            watch_set_pixel(1, 13);
+            watch_set_pixel(1, 14);
+            watch_set_pixel(1, 15);
+            watch_set_pixel(2, 15);
+            break;
+        case 6: // Luna
+            watch_set_pixel(2, 14);
+            watch_set_pixel(2, 15);
+            watch_set_pixel(2, 13);
+            break;
+    }
+}
+
+/** @details solar phase can be a day phase between sunrise and sunset or an alternating night phase.
+ *  This function calculates the start and end of the current phase based on a given geographic location.
+ */
+static void _planetary_solar_phase(planetary_time_state_t *state) {
+    uint8_t phase;
+    double sunrise, sunset;
+    uint32_t now_epoch, sunrise_epoch, sunset_epoch, midnight_epoch;
+    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+
+    // check if we have a location. If not, display error
+    if (movement_location.reg == 0) {
+        watch_display_string("    no Loc", 0);
+        state->no_location = true;
+        return;
+    }
+
+    // location detected
+    state->no_location = false;
+
+    watch_date_time_t date_time = watch_rtc_get_date_time(); // the current local date / time
+    watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0); // the current date / time in UTC
+    watch_date_time_t scratch_time; // scratchpad, contains different values at different times
+    watch_date_time_t midnight;
+    scratch_time.reg = midnight.reg = utc_now.reg;
+    midnight.unit.hour = midnight.unit.minute = midnight.unit.second = 0; // start of the day at midnight
+
+    // get location coordinate
+    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
+    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
+    double lat = (double)lat_centi / 100.0;
+    double lon = (double)lon_centi / 100.0;
+
+    // save UTC offset
+    state->utc_offset = ((double)movement_get_current_timezone_offset()) / 3600.0;
+
+    // get UNIX epoch time
+    now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
+    midnight_epoch = watch_utility_date_time_to_unix_time(midnight, 0);
+
+    // 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);
+    
+    // calculate sunrise and sunset UNIX timestamps
+    sunrise_epoch = midnight_epoch + sunrise * 3600;
+    sunset_epoch = midnight_epoch + sunset * 3600;
+
+    // by default we assume it is daytime (phase 1) between sunrise and sunset
+    phase = 1;
+    state->night = false;
+    state->phase_start = sunrise_epoch;
+    state->phase_end = sunset_epoch;
+
+    // night time calculations
+    if ( now_epoch < sunrise_epoch && now_epoch < sunset_epoch ) phase = 0; // morning before dawn
+    if ( now_epoch > sunrise_epoch && now_epoch >= sunset_epoch ) phase = 2; // evening after dusk
+
+    // phase 0: we are before sunrise
+    if ( phase == 0) {
+        // go back to yesterday and calculate sunset
+        midnight_epoch -= 86400;
+        scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch, 0);
+        sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+        sunset_epoch = midnight_epoch + sunset * 3600;
+        // we are still in yesterday's night hours
+        state->night = true;
+        state->phase_start = sunset_epoch;
+        state->phase_end = sunrise_epoch;
+    }
+
+    // phase 2: we are after sunset
+    if ( phase == 2) {
+        // skip to tomorrow and calculate sunrise
+        midnight_epoch += 86400;
+        scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch, 0);
+        sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+        sunrise_epoch = midnight_epoch + sunrise * 3600;
+        // we are still in yesterday's night hours
+        state->night = true;
+        state->phase_start = sunset_epoch;
+        state->phase_end = sunrise_epoch;
+    }
+
+    // calculate the duration of a planetary second during this solar phase 
+    // and convert to Hertz so we can call a faster tick rate
+    state->freq = (1 / ((double)( state->phase_end - state->phase_start ) / 43200));
+}
+
+/** @details A planetary hour is one of exactly twelve hours of a solar phase. Its length varies.
+ *  This function calculates the current planetary hour and divides it up into relative minutes and seconds.
+ *  It also calculates the current planetary ruler of the hour and of the day.
+ */
+static void _planetary_time(movement_event_t event, planetary_time_state_t *state) {
+    char buf[14];
+    char ruler[3];
+    double night_hour_count = 0.0;
+    uint8_t weekday, planet, planetary_hour;
+    double hour_duration, current_hour, current_minute, current_second;
+
+        watch_set_colon();
+
+    // get current time and convert to UTC
+    state->scratch = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_get_current_timezone_offset(), 0); 
+
+    // when current phase ends calculate the next phase
+    if ( watch_utility_date_time_to_unix_time(state->scratch, 0) >= state->phase_end ) {
+        _planetary_solar_phase(state);
+        return;
+    }
+
+    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
+
+    // PM for night hours, otherwise the night hours are counted from 13
+    if ( state->night ) {
+        if (movement_clock_mode_24h()) night_hour_count = 12;
+        else watch_set_indicator(WATCH_INDICATOR_PM);
+    }
+
+    // calculate the duration of a planetary hour during this solar phase
+    hour_duration = (( state->phase_end - state->phase_start)) / 12.0;
+
+    // which planetary hour are we in?
+
+    // RTC only provides full second precision, so we have to manually add subseconds with each tick
+    current_hour = ((( watch_utility_date_time_to_unix_time(state->scratch, 0) ) + event.subsecond * 0.11111111) - state->phase_start ) / hour_duration;
+    planetary_hour = floor(current_hour) + ( state->night ? 12 : 0 );
+    current_hour  += night_hour_count; //adjust for 24hr display
+    current_minute = modf(current_hour, &current_hour) * 60.0;
+    current_second = modf(current_minute, &current_minute) * 60.0;
+
+    // the day changes after sunrise, so if we are at night it is yesterday's planetary day
+    // hence we take the datetime object of when the last solar phase started as the current day
+    // and then fill in the hours and minutes
+    state->scratch = watch_utility_date_time_from_unix_time(state->phase_start, 0);
+    state->scratch.unit.hour = floor(current_hour);
+    state->scratch.unit.minute = floor(current_minute);
+    state->scratch.unit.second = (uint8_t)floor(current_second) % 60;
+
+    // what weekday is it (0 - 6)
+    weekday = watch_utility_get_iso8601_weekday_number(state->scratch.unit.year, state->scratch.unit.month, state->scratch.unit.day) - 1;
+
+    // planetary ruler of the hour or the day
+    if ( state->day_ruler ) planet = plindex[weekday];
+    else planet = ( plindex[weekday] + planetary_hour ) % 7;
+
+    // latin or greek ruler names or astrological symbol
+    if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3);
+    if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3);
+    if ( state->ruler == 2 ) strncpy(ruler, "  ", 3);
+    
+    // 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);
+    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);
+
+    if ( state->ruler == 2 ) _planetary_icon(planet);
+
+}
+
+// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
+
+void planetary_time_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(planetary_time_state_t));
+        memset(*context_ptr, 0, sizeof(planetary_time_state_t));
+    }
+}
+
+void planetary_time_face_activate(void *context) {
+    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
+
+#if __EMSCRIPTEN__
+    int16_t browser_lat = EM_ASM_INT({ return lat; });
+    int16_t browser_lon = EM_ASM_INT({ return lon; });
+    if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
+        movement_location_t browser_loc;
+        browser_loc.bit.latitude = browser_lat;
+        browser_loc.bit.longitude = browser_lon;
+        watch_store_backup_data(browser_loc.reg, 1);
+    }
+#endif
+
+    planetary_time_state_t *state = (planetary_time_state_t *)context;
+    
+    // calculate phase
+    _planetary_solar_phase(state);
+}
+
+bool planetary_time_face_loop(movement_event_t event, void *context) {
+    planetary_time_state_t *state = (planetary_time_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _planetary_time(event, state);
+            if ( state->freq > 1 )
+                // for hours with shorter seconds let's increase the tick to not skip seconds in the display
+                movement_request_tick_frequency( 8 );
+            break;
+        case EVENT_TICK:
+            _planetary_time(event, state);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            state->ruler = (state->ruler + 1) % 3;
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // Just in case you have need for another button.
+            state->day_ruler = !state->day_ruler;
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            watch_start_sleep_animation(500);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void planetary_time_face_resign(void *context) {
+    (void) context;
+    movement_request_tick_frequency( 1 );
+}
+
diff --git a/legacy/watch_faces/complication/planetary_time_face.h b/legacy/watch_faces/complication/planetary_time_face.h
new file mode 100644 (file)
index 0000000..0dd594e
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ * Copyright (c) 2022 Joey Castillo (sunrise_sunset_face)
+ *
+ * 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 planetary_time_face_H_
+#define planetary_time_face_H_
+
+/*
+ * PLANETARY TIME face
+ *
+ * BACKGROUND
+ *
+ * Both the 24 hour day and the order of our weekdays have quite esoteric roots.
+ * The ancient Egyptians divided the day up into 12 hours of sunlight and 12 hours
+ * of night time. Obviously the length of these hours varied throughout the year.
+ * 
+ * The Greeks assigned each hour a ruler of the planetary gods in the ancient
+ * "Chaldean" order from slowest (Chronos for Saturn) to fastest (Selene for Moon).
+ * Because 24 hours cannot be equally divided by seven, the planetary rulers carried
+ * over to the first hour of the next day, effectively ruling over the entire day 
+ * and lending the whole day their name. The seven day week was born.
+ * 
+ * PLANETARY TIME COMPLICATION
+ * 
+ * The hour digits of this complication watch-face display the current planetary hour 
+ * according to the given location and day of the year (First hour from 12am to 1am,
+ * the second hour from 1am to 2am, and so forth).
+ * 
+ * Like with normal clocks the minutes and seconds help dividing the hour into smaller
+ * units. On this watch-face, all units naturally vary in length because the planetary
+ * hours are not fixed by duration but by the moments of sunrise and sunset which 
+ * obviously vary throughout the year, especially in higher latitudes.
+ * 
+ * On this watch-face the hours indicated as 12am to 12pm (00:00 - 12:00) are used for
+ * the planetary daytime hours between sunrise and sunset and hours indicated as 12pm 
+ * to 12am (12:00 - 00:00) are used for the planetary night hours after sunset and before 
+ * sunrise.
+ * 
+ * The planetary ruler of the current hour and day is displayed at the top in Latin or 
+ * Greek shorthand notation:
+ * 
+ * Saturn (SA) / Chronos (CH) / ♄
+ * Jupiter (JU) / Zeus (ZE) / ♃
+ * Mars (MA) / Ares (AR) / ♂
+ * Sol (SO) / Helios (HE) / ☉
+ * Venus (VE) / Aphrodite (AF) / ♀
+ * Mercury (ME) / Hermes (HR) / ☿
+ * Luna (LU) / Selene (SE) / ☾
+ * 
+ * The ALARM button toggles between displaying the ruler of the hour and the ruler of the day
+ * 
+ * The LIGHT button toggles between Latin and Greek ruler shorthand notation
+ * 
+ * (IMPORTANT: Make sure the watch's time, timezone and location are set correctly for this
+ * watch face to work properly!)
+ */
+
+#include "movement.h"
+#include "sunrise_sunset_face.h"
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint32_t phase_start;
+    uint32_t phase_end;
+    bool night;
+    double utc_offset;
+    double freq;
+    uint8_t ruler;
+    bool day_ruler;
+    bool no_location;
+    sunrise_sunset_state_t sunstate;
+    watch_date_time_t scratch;
+} planetary_time_state_t;
+
+void planetary_time_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void planetary_time_face_activate(void *context);
+bool planetary_time_face_loop(movement_event_t event, void *context);
+void planetary_time_face_resign(void *context);
+
+#define planetary_time_face ((const watch_face_t){ \
+    planetary_time_face_setup, \
+    planetary_time_face_activate, \
+    planetary_time_face_loop, \
+    planetary_time_face_resign, \
+    NULL, \
+})
+
+#endif // planetary_time_face_H_
+
diff --git a/legacy/watch_faces/complication/probability_face.c b/legacy/watch_faces/complication/probability_face.c
new file mode 100644 (file)
index 0000000..5698430
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Spencer Bywater
+ *
+ * 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.
+ */
+
+// Emulator only: need time() to seed the random number generator.
+#if __EMSCRIPTEN__
+#include <time.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "probability_face.h"
+
+#define DEFAULT_DICE_SIDES 2
+#define PROBABILITY_ANIMATION_TICK_FREQUENCY 8
+const uint16_t NUM_DICE_TYPES = 8; // Keep this consistent with # of dice types below
+const uint16_t DICE_TYPES[] = {2, 4, 6, 8, 10, 12, 20, 100};
+
+
+// --------------
+// Custom methods
+// --------------
+
+static void display_dice_roll(probability_state_t *state) {
+    char buf[8];
+    if (state->rolled_value == 0) {
+        if (state->dice_sides == 100) {
+            sprintf(buf, " C    ");
+        } else {
+            sprintf(buf, "%2d    ", state->dice_sides);
+        }
+    } else if (state->dice_sides == 2) {
+        if (state->rolled_value == 1) {
+            sprintf(buf, "%2d   H", state->dice_sides);
+        } else {
+            sprintf(buf, "%2d   T", state->dice_sides);
+        }
+    } else if (state->dice_sides == 100) {
+        sprintf(buf, " C %3d", state->rolled_value);
+    } else {
+        sprintf(buf, "%2d %3d", state->dice_sides, state->rolled_value);
+    }
+    watch_display_string(buf, 4);
+}
+
+static void generate_random_number(probability_state_t *state) {
+    // Emulator: use rand. Hardware: use arc4random.
+    #if __EMSCRIPTEN__
+    state->rolled_value = rand() % state->dice_sides + 1;
+    #else
+    state->rolled_value = arc4random_uniform(state->dice_sides) + 1;
+    #endif
+}
+
+static void display_dice_roll_animation(probability_state_t *state) {
+    if (state->is_rolling) {
+        if (state->animation_frame == 0) {
+            watch_display_string("   ", 7);
+            watch_set_pixel(1, 4);
+            watch_set_pixel(1, 6);
+            state->animation_frame = 1;
+        } else if (state->animation_frame == 1) {
+            watch_clear_pixel(1, 4);
+            watch_clear_pixel(1, 6);
+            watch_set_pixel(2, 4);
+            watch_set_pixel(0, 6);
+            state->animation_frame = 2;
+        } else if (state->animation_frame == 2) {
+            watch_clear_pixel(2, 4);
+            watch_clear_pixel(0, 6);
+            watch_set_pixel(2, 5);
+            watch_set_pixel(0, 5);
+            state->animation_frame = 3;
+        } else if (state->animation_frame == 3) {
+            state->animation_frame = 0;
+            state->is_rolling = false;
+            movement_request_tick_frequency(1);
+            display_dice_roll(state);
+        }
+    }
+}
+
+
+// ---------------------------
+// Standard watch face methods
+// ---------------------------
+void probability_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(probability_state_t));
+        memset(*context_ptr, 0, sizeof(probability_state_t));
+    }
+    // Emulator only: Seed random number generator
+    #if __EMSCRIPTEN__
+    srand(time(NULL));
+    #endif
+}
+
+void probability_face_activate(void *context) {
+    probability_state_t *state = (probability_state_t *)context;
+
+    state->dice_sides = DEFAULT_DICE_SIDES;
+    state->rolled_value = 0;
+    watch_display_string("PR", 0);
+}
+
+bool probability_face_loop(movement_event_t event, void *context) {
+    probability_state_t *state = (probability_state_t *)context;
+
+    if (state->is_rolling && event.event_type != EVENT_TICK) {
+        return true;
+    }
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            display_dice_roll(state);
+            break;
+        case EVENT_TICK:
+            display_dice_roll_animation(state);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // Change how many sides the die has
+            for (int i = 0; i < NUM_DICE_TYPES; i++) {
+                if (DICE_TYPES[i] == state->dice_sides) {
+                    if (i == NUM_DICE_TYPES - 1) {
+                        state->dice_sides = DICE_TYPES[0];
+                    } else {
+                        state->dice_sides = DICE_TYPES[i + 1];
+                    }
+                    break;
+                }
+            }
+            state->rolled_value = 0;
+            display_dice_roll(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // Roll the die
+            generate_random_number(state);
+            state->is_rolling = true;
+            // Dice rolling animation begins on next tick and new roll will be displayed on completion
+            movement_request_tick_frequency(PROBABILITY_ANIMATION_TICK_FREQUENCY);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            watch_display_string("SLEEP ", 4);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void probability_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/probability_face.h b/legacy/watch_faces/complication/probability_face.h
new file mode 100644 (file)
index 0000000..6ce1b1f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Spencer Bywater
+ *
+ * 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 PROBABILITY_FACE_H_
+#define PROBABILITY_FACE_H_
+
+/*
+ * PROBABILITY face
+ *
+ * This face is a dice-rolling random number generator.
+ * Supports dice with 2, 4, 6, 8, 10, 12, 20, or 100 sides.
+ *
+ * Press LIGHT to cycle through die type.
+ * The current die size is indicated on the left ("C" for 100)
+ *
+ * Press ALARM to roll the selected die.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint8_t dice_sides;
+    uint8_t rolled_value;
+    uint8_t animation_frame;
+    bool is_rolling;
+} probability_state_t;
+
+void probability_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void probability_face_activate(void *context);
+bool probability_face_loop(movement_event_t event, void *context);
+void probability_face_resign(void *context);
+
+#define probability_face ((const watch_face_t){ \
+    probability_face_setup, \
+    probability_face_activate, \
+    probability_face_loop, \
+    probability_face_resign, \
+    NULL, \
+})
+
+#endif // PROBABILITY_FACE_H_
+
diff --git a/legacy/watch_faces/complication/pulsometer_face.c b/legacy/watch_faces/complication/pulsometer_face.c
new file mode 100644 (file)
index 0000000..1c29080
--- /dev/null
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * MIT License
+ *
+ * Copyright © 2021-2022 Joey Castillo <joeycastillo@utexas.edu> <jose.castillo@gmail.com>
+ * Copyright © 2023 Jeremy O'Brien <neutral@fastmail.com>
+ * Copyright © 2024 Matheus Afonso Martins Moreira <matheus.a.m.moreira@gmail.com> (https://www.matheusmoreira.com/)
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pulsometer_face.h"
+#include "watch.h"
+
+#ifndef PULSOMETER_FACE_TITLE
+#define PULSOMETER_FACE_TITLE "PL"
+#endif
+
+#ifndef PULSOMETER_FACE_CALIBRATION_DEFAULT
+#define PULSOMETER_FACE_CALIBRATION_DEFAULT (30)
+#endif
+
+#ifndef PULSOMETER_FACE_CALIBRATION_INCREMENT
+#define PULSOMETER_FACE_CALIBRATION_INCREMENT (10)
+#endif
+
+// tick frequency will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.)
+#ifndef PULSOMETER_FACE_FREQUENCY_FACTOR
+#define PULSOMETER_FACE_FREQUENCY_FACTOR (4ul)
+#endif
+
+#define PULSOMETER_FACE_FREQUENCY (1 << PULSOMETER_FACE_FREQUENCY_FACTOR)
+
+typedef struct {
+    bool measuring;
+    int16_t pulses;
+    int16_t ticks;
+    int8_t calibration;
+} pulsometer_state_t;
+
+static void pulsometer_display_title(pulsometer_state_t *pulsometer) {
+    (void) pulsometer;
+    watch_display_string(PULSOMETER_FACE_TITLE, 0);
+}
+
+static void pulsometer_display_calibration(pulsometer_state_t *pulsometer) {
+    char buf[3];
+    snprintf(buf, sizeof(buf), "%2hhd", pulsometer->calibration);
+    watch_display_string(buf, 2);
+}
+
+static void pulsometer_display_measurement(pulsometer_state_t *pulsometer) {
+    char buf[7];
+    snprintf(buf, sizeof(buf), "%-6hd", pulsometer->pulses);
+    watch_display_string(buf, 4);
+}
+
+static void pulsometer_indicate(pulsometer_state_t *pulsometer) {
+    if (pulsometer->measuring) {
+        watch_set_indicator(WATCH_INDICATOR_LAP);
+    } else {
+        watch_clear_indicator(WATCH_INDICATOR_LAP);
+    }
+}
+
+static void pulsometer_start_measurement(pulsometer_state_t *pulsometer) {
+    pulsometer->measuring = true;
+    pulsometer->pulses = INT16_MAX;
+    pulsometer->ticks = 0;
+
+    pulsometer_indicate(pulsometer);
+
+    movement_request_tick_frequency(PULSOMETER_FACE_FREQUENCY);
+}
+
+static void pulsometer_measure(pulsometer_state_t *pulsometer) {
+    if (!pulsometer->measuring) { return; }
+
+    pulsometer->ticks++;
+
+    float ticks_per_minute = 60 << PULSOMETER_FACE_FREQUENCY_FACTOR;
+    float pulses_while_button_held = ticks_per_minute / pulsometer->ticks;
+    float calibrated_pulses = pulses_while_button_held * pulsometer->calibration;
+    calibrated_pulses += 0.5f;
+
+    pulsometer->pulses = (int16_t) calibrated_pulses;
+
+    pulsometer_display_measurement(pulsometer);
+}
+
+static void pulsometer_stop_measurement(pulsometer_state_t *pulsometer) {
+    movement_request_tick_frequency(1);
+
+    pulsometer->measuring = false;
+
+    pulsometer_display_measurement(pulsometer);
+    pulsometer_indicate(pulsometer);
+}
+
+static void pulsometer_cycle_calibration(pulsometer_state_t *pulsometer, int8_t increment) {
+    if (pulsometer->measuring) { return; }
+
+    if (pulsometer->calibration <= 0) {
+        pulsometer->calibration = 1;
+    }
+
+    int8_t last = pulsometer->calibration;
+    pulsometer->calibration += increment;
+
+    if (pulsometer->calibration > 39) {
+        pulsometer->calibration = last == 39? 1 : 39;
+    }
+
+    pulsometer_display_calibration(pulsometer);
+}
+
+void pulsometer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        pulsometer_state_t *pulsometer = malloc(sizeof(pulsometer_state_t));
+
+        pulsometer->calibration = PULSOMETER_FACE_CALIBRATION_DEFAULT;
+        pulsometer->pulses = 0;
+        pulsometer->ticks = 0;
+
+        *context_ptr = pulsometer;
+    }
+}
+
+void pulsometer_face_activate(void *context) {
+
+    pulsometer_state_t *pulsometer = context;
+
+    pulsometer->measuring = false;
+
+    pulsometer_display_title(pulsometer);
+    pulsometer_display_calibration(pulsometer);
+    pulsometer_display_measurement(pulsometer);
+}
+
+bool pulsometer_face_loop(movement_event_t event, void *context) {
+
+    pulsometer_state_t *pulsometer = (pulsometer_state_t *) context;
+
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_DOWN:
+            pulsometer_start_measurement(pulsometer);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+        case EVENT_ALARM_LONG_UP:
+            pulsometer_stop_measurement(pulsometer);
+            break;
+        case EVENT_TICK:
+            pulsometer_measure(pulsometer);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            pulsometer_cycle_calibration(pulsometer, 1);
+            break;
+        case EVENT_LIGHT_LONG_UP:
+            pulsometer_cycle_calibration(pulsometer, PULSOMETER_FACE_CALIBRATION_INCREMENT);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // Inhibit the LED
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void pulsometer_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/pulsometer_face.h b/legacy/watch_faces/complication/pulsometer_face.h
new file mode 100644 (file)
index 0000000..78c906c
--- /dev/null
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * MIT License
+ *
+ * Copyright © 2021-2022 Joey Castillo <joeycastillo@utexas.edu> <jose.castillo@gmail.com>
+ * Copyright © 2022 Alexsander Akers <me@a2.io>
+ * Copyright © 2023 Alex Utter <ooterness@gmail.com>
+ * Copyright © 2024 Matheus Afonso Martins Moreira <matheus.a.m.moreira@gmail.com> (https://www.matheusmoreira.com/)
+ *
+ * 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 PULSOMETER_FACE_H_
+#define PULSOMETER_FACE_H_
+
+/*
+ * PULSOMETER face
+ *
+ * The pulsometer implements a classic mechanical watch complication.
+ * A mechanical pulsometer involves a chronograph with a scale that
+ * allows the user to compute the number of heart beats per minute
+ * in less time. The scale is calibrated, or graduated, for a fixed
+ * number of heart beats, most often 30. The user starts the chronograph
+ * and simultaneously begins counting the heart beats. The movement of
+ * the chronograph's seconds hand over time automatically performs the
+ * computations required. When the calibrated number of heart beats
+ * is reached, the chronograph is stopped and the seconds hand shows
+ * the heart rate.
+ *
+ * The Sensor Watch pulsometer improves this design with user calibration:
+ * it can be graduated to any value between 1 and 39 pulsations per minute.
+ * The default is still 30, mirroring the classic pulsometer calibration.
+ * This feature allows the user to reconfigure the pulsometer to count
+ * many other types of periodic minutely events, making it more versatile.
+ * For example, it can be set to 5 respirations per minute to turn it into
+ * an asthmometer, a nearly identical mechanical watch complication
+ * that doctors might use to quickly measure respiratory rate.
+ *
+ * To use the pulsometer, hold the ALARM button and count the pulses.
+ * When the calibrated number of pulses is reached, release the button.
+ * The display will show the number of pulses per minute.
+ *
+ * In order to measure heart rate, feel for a pulse using the hand with
+ * the watch while holding the button down with the other.
+ * The pulse can be easily felt on the carotid artery of the neck.
+ *
+ * In order to measure breathing rate, simply hold the ALARM button
+ * and count the number of breaths.
+ *
+ * To calibrate the pulsometer, press LIGHT
+ * to cycle to the next integer calibration.
+ * Long press LIGHT to cycle it by 10.
+ */
+
+#include "movement.h"
+
+void pulsometer_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void pulsometer_face_activate(void *context);
+bool pulsometer_face_loop(movement_event_t event, void *context);
+void pulsometer_face_resign(void *context);
+
+#define pulsometer_face ((const watch_face_t){ \
+    pulsometer_face_setup, \
+    pulsometer_face_activate, \
+    pulsometer_face_loop, \
+    pulsometer_face_resign, \
+    NULL, \
+})
+
+#endif // PULSOMETER_FACE_H_
diff --git a/legacy/watch_faces/complication/randonaut_face.c b/legacy/watch_faces/complication/randonaut_face.c
new file mode 100644 (file)
index 0000000..fd9ca5d
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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.
+ */
+
+// Emulator only: need time() to seed the random number generator.
+#if __EMSCRIPTEN__
+#include <time.h>
+#else
+#include "saml22j18a.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "filesystem.h"
+#include "randonaut_face.h"
+
+#define R 6371 // Earth's radius in km
+#define PI 3.14159265358979323846
+
+static void _get_location_from_file(randonaut_state_t *state);
+static void _save_point_to_file(randonaut_state_t *state);
+static void _get_entropy(randonaut_state_t *state);
+static void _generate_blindspot(randonaut_state_t *state);
+static void _randonaut_face_display(randonaut_state_t *state);
+static void _generate_blindspot(randonaut_state_t *state);
+static uint32_t _get_pseudo_entropy(uint32_t max);
+static uint32_t _get_true_entropy(void);
+static void _get_entropy(randonaut_state_t *state);
+
+// MOVEMENT WATCH FACE FUNCTIONS //////////////////////////////////////////////
+
+void randonaut_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(randonaut_state_t));
+        memset(*context_ptr, 0, sizeof(randonaut_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void randonaut_face_activate(void *context) {
+    randonaut_state_t *state = (randonaut_state_t *)context;
+    _get_location_from_file(state);
+    state->face.mode = 0;
+    state->radius = 1000;
+    _get_entropy(state);
+    state->chance = true;
+    // Handle any tasks related to your watch face coming on screen.
+}
+
+bool randonaut_face_loop(movement_event_t event, void *context) {
+    randonaut_state_t *state = (randonaut_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Show your initial UI here.
+            break;
+        case EVENT_TICK:
+            // If needed, update your display here.
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            switch ( state->face.mode ) {
+                case 0: // home
+                    state->face.mode = 2; //point
+                    state->face.location_format = 0; // title
+                    break;
+                case 1: // generate
+                    state->face.mode = 0; //home
+                    break;
+                case 2: // point
+                    state->face.mode = 0; //home
+                    break;
+                case 3: // setup radius
+                    state->face.mode = 4; // toggle to RNG
+                    break;
+                case 4: // setup RNG
+                    state->face.mode = 3; // toggle to Radius
+                    break;
+                case 5: // data processing
+                    break;
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            switch ( state->face.mode ) {
+                case 3: // setup
+                case 4:
+                    state->face.mode = 0; //home
+                    break;
+                default:
+                    state->face.mode = 3; //setup
+                    watch_clear_display();
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch ( state->face.mode ) {
+                case 0: //home
+                    state->face.mode = 1; // generate
+                    break;
+                case 2: // point
+                    state->face.location_format = (( state->face.location_format + 1) % (7));
+                    if ( state->face.location_format == 0 ) 
+                        state->face.location_format++;
+                    break;
+                case 3: //setup radius
+                    state->radius += 500;
+                    if ( state->radius > 10000 )
+                        state->radius = 1000;
+                    break;
+                case 4: //setup RNG
+                    state->face.rng = (state->face.rng + 1) % 3;
+                    switch ( state->face.rng ) {
+                        case 0:
+                            state->chance = true;
+                            break;
+                        case 1:
+                            state->chance = false;
+                            state->quantum = true;
+                            break;
+                        case 2:
+                            state->chance = false;
+                            state->quantum = false;
+                            break;
+                    }
+                    break;
+                case 5: // data processing
+                    _save_point_to_file(state);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if ( state->face.mode == 5 )
+                state->face.mode = 0; // home
+            else
+                state->face.mode = 5; // data processing
+            break;
+        case EVENT_TIMEOUT:
+            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
+            // you may uncomment this line to move back to the first watch face in the list:
+            // movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
+            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
+            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
+            // watch_start_sleep_animation(500);
+            break;
+        default:
+            // Movement's default loop handler will step in for any cases you don't handle above:
+            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
+            // You can override any of these behaviors by adding a case for these events to this switch statement.
+            return movement_default_loop_handler(event);
+    }
+
+    _randonaut_face_display(state);
+
+    // return true if the watch can enter standby mode. Generally speaking, you should always return true.
+    // Exceptions:
+    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
+    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
+    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
+    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
+    return true;
+}
+
+void randonaut_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
+
+// PRIVATE STATIC FUNCTIONS ///////////////////////////////////////////////////
+
+/** @brief display handler
+ */
+static void _randonaut_face_display(randonaut_state_t *state) {
+    char buf[12];
+    watch_clear_colon();
+    switch ( state->face.mode ) {
+        case 0: //home
+            sprintf(buf, "RA   Rando");
+            break;
+        case 1: //generate
+            if ( state->quantum )
+                // All Hail Steve /;[;[/.;]/[.;[/;/;/;/;.;.];.]]--=/ 
+                for ( uint8_t c = 100; c > 0; c--) {
+                    watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(0x33-0x1C));
+                    watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(3432-3409));
+                    watch_set_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(0xE +9));
+                    watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(23));
+                    watch_set_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(12+7+11));
+                    if( c < 70 ) {
+                        watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(12+7+11));
+                    }
+                    if ( c < 60 ) {
+                        watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(0xD68-0xD4A));
+                    }
+                    if ( c < 50 ) { 
+                        watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(14+9));
+                    }
+                    delay_ms(_get_pseudo_entropy(c)+20);
+                    if ( c < 30 ) {
+                        watch_display_string(" ",_get_pseudo_entropy(10));
+                    }
+                    watch_clear_pixel(_get_pseudo_entropy(02),_get_pseudo_entropy(3432-3409));
+                    watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(51-28));
+                    watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(23));
+                    if ( c < 20 ) {
+                        watch_clear_pixel(_get_pseudo_entropy(02),_get_pseudo_entropy(51-28));
+                        watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(14+9));
+                        watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(0xD68-0xD4A));
+                        watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(3432-3409));
+                        watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(12+7+11));
+                        watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(51-28));
+                    }
+                }
+            else
+                for ( uint8_t c = 30; c > 0; c--) {
+                    watch_display_string("1", _get_pseudo_entropy(10));
+                    watch_display_string("0", _get_pseudo_entropy(10));
+                    watch_display_string("11", _get_pseudo_entropy(10));
+                    watch_display_string("00", _get_pseudo_entropy(10));
+                    delay_ms(50);
+                    watch_display_string(" ", _get_pseudo_entropy(10));
+                    watch_display_string(" ", _get_pseudo_entropy(10));
+                    watch_display_string(" ", _get_pseudo_entropy(10));
+                    watch_display_string(" ", _get_pseudo_entropy(10));
+                }
+            _generate_blindspot(state);
+            watch_clear_display();
+            state->face.mode = 2; // point
+            state->face.location_format = 1; // distance
+            watch_display_string("RA   Found", 0);
+            delay_ms(500);
+            sprintf(buf, "RA   Found");
+            break;
+        case 2: //point
+            switch ( state->face.location_format ) {
+                case 0:
+                    sprintf(buf, "RA   Point");
+                    break;
+                case 1: // distance to point
+                    watch_clear_display();
+                    sprintf(buf, "DI m %d", state->point.distance );
+                    break;
+                case 2: // bearing relative to point
+                    watch_clear_display();
+                    sprintf(buf, "BE # %d", state->point.bearing );
+                    break;
+                case 3: // latitude DD._____
+                    sprintf(state->scratchpad, "%07d", abs((int32_t)(state->point.latitude)));
+                    sprintf(buf, "LA #%c %c%c  ", state->point.latitude < 0 ? '-' : '+', state->scratchpad[0], state->scratchpad[1]);
+                    break;
+                case 4: // latitude __.DDDDD
+                    sprintf(buf, "LA , %c%c%c%c%c", state->scratchpad[2], state->scratchpad[3],state->scratchpad[4], state->scratchpad[5],state->scratchpad[6]);
+                    break;
+                case 5: // longitude DD._____
+                    sprintf(state->scratchpad, "%08d", abs((int32_t)(state->point.longitude)));
+                    sprintf(buf, "LO #%c%c%c%c  ", state->point.longitude < 0 ? '-' : '+',state->scratchpad[0], state->scratchpad[1], state->scratchpad[2]);
+                    break;
+                case 6: // longitude __.DDDDD
+                    sprintf(buf, "LO , %c%c%c%c%c", state->scratchpad[3], state->scratchpad[4],state->scratchpad[5], state->scratchpad[6],state->scratchpad[7]);
+                    break;
+            }
+            break;
+        case 3: // setup radius
+            watch_set_colon();
+            if ( state->radius < 10000 )
+                sprintf(buf, "RA m %d ", state->radius);
+            else
+                sprintf(buf, "RA m%d ", state->radius);
+            break;
+        case 4: // setup RNG
+            sprintf(buf, "RN G %s ", state->chance ? "Chnce" : (state->quantum ? "True" : "Psudo"));
+            break;
+        case 5: // data processing
+            sprintf(buf, "WR   File ");
+    }
+    watch_display_string(buf, 0);
+}
+
+/** @brief Official Randonautica Blindspot Algorithm
+ */
+static void _generate_blindspot(randonaut_state_t *state) {
+
+    _get_entropy(state);
+
+    double lat = (double)state->location.latitude / 100000;
+    double lon = (double)state->location.longitude / 100000;
+    uint16_t radius = state->radius;
+
+    const double random_distance  = radius * sqrt( (double)state->entropy / INT32_MAX ) / 1000.0;
+    const double random_bearing = 2.0 * PI * (double)state->entropy / INT32_MAX;
+
+    const double phi = lat * PI / 180;
+    const double lambda = lon * PI / 180;
+    const double alpha = random_distance / R;
+
+    lat = asin( sin(phi) * cos(alpha) + cos(phi) * sin(alpha) * cos(random_bearing) );
+    lon = lambda + atan2( sin(random_bearing) * sin(alpha) * cos(phi), cos(alpha) - sin(phi) * sin( lat ));
+    
+    state->point.latitude  = (int)round(lat * (180 / PI) * 100000);
+    state->point.longitude = (int)round(lon * (180 / PI) * 100000);
+    state->point.distance = random_distance * 1000;
+    state->point.bearing   = (uint16_t)round(random_bearing * (180 / PI) < 0 ? random_bearing * (180 / PI) + 360 : random_bearing * (180 / PI));
+}
+
+
+/** @brief pseudo random number generator
+ */
+static uint32_t _get_pseudo_entropy(uint32_t max) {
+    #if __EMSCRIPTEN__
+    return rand() % max;
+    #else
+    return arc4random_uniform(max);
+    #endif
+}
+
+/** @brief true random number generator
+ */
+static uint32_t _get_true_entropy(void) {
+    #if __EMSCRIPTEN__
+    return rand() % INT32_MAX;
+    #else
+    hri_mclk_set_APBCMASK_TRNG_bit(MCLK);
+    hri_trng_set_CTRLA_ENABLE_bit(TRNG);
+
+    while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready
+
+    watch_disable_TRNG();
+    hri_mclk_clear_APBCMASK_TRNG_bit(MCLK);
+    return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it
+    #endif
+}
+
+/** @brief get location from place.loc
+ */
+static void _get_location_from_file(randonaut_state_t *state) {
+    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+    coordinate_t place;
+    if (filesystem_file_exists("place.loc")) {
+        if (filesystem_read_file("place.loc", (char*)&place, sizeof(place)))
+            state->location = place;
+    } else {
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+        state->location.latitude = movement_location.bit.latitude * 1000;
+        state->location.longitude = movement_location.bit.longitude * 1000;
+    }
+}
+
+/** @brief save generated point to place.loc
+ */
+static void _save_point_to_file(randonaut_state_t *state) {
+    watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    coordinate_t place;
+    place.latitude = state->point.latitude;
+    place.longitude = state->point.longitude;
+    if (filesystem_write_file("place.loc", (char*)&place, sizeof(place))) {
+        delay_ms(100);
+        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+    } else {
+        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+        delay_ms(500);
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+        
+    }
+}
+
+/** @brief get pseudo/quantum entropy and filter modulo bias
+ */
+static void _get_entropy(randonaut_state_t *state) {
+    if ( state->chance ) {
+        state->quantum = (bool)(state->entropy % 2);
+    }   
+    do {
+        if ( !state->quantum ) {
+            state->entropy = _get_pseudo_entropy(INT32_MAX);
+        } else {
+            state->entropy = _get_true_entropy();
+        }
+    } while (state->entropy >= INT32_MAX || state->entropy <= 0);
+    state->entropy %= INT32_MAX;
+}
\ No newline at end of file
diff --git a/legacy/watch_faces/complication/randonaut_face.h b/legacy/watch_faces/complication/randonaut_face.h
new file mode 100644 (file)
index 0000000..72e4726
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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 RANDONAUT_FACE_H_
+#define RANDONAUT_FACE_H_
+
+/*
+ * RANDONAUT face
+ * ==============
+ *
+ * Randonauting is a way to turn the world around you into an adventure and get the user outside 
+ * of their day-to-day routine by using a random number generator to derive a coordinate to journey 
+ * to. In Randonauts lore so-called "Blind Spots" are places you cannot reach methodologically. They
+ * may exist in your own backyard for your whole life and you will never even notice them, because 
+ * you simply have no reason to go to that exact place or look in its direction. Since the very 
+ * limitations of our behavioral algorithms are the reason for the existence of blindspots, they 
+ * can only be found using a randomizer.
+ * 
+ * This watch face generates a random location based on the watch's location and a set radius using
+ * the official Randonautica Blind Spot algorithm.
+ * 
+ * The ALARM button starts the random location generation and then automatically displays the found
+ * Blind Spot.
+ * 
+ * By pressing ALARM again the user can flip through different pieces of information about the Blind
+ * Spot: Distance (DI), Bearing Degree (BE), Latitude degrees and decimal digits (LA), Longitude 
+ * degrees and decimal digits (LO).
+ * 
+ * Pressing LIGHT switches between generating a new blind spot ("Rando") and displaying the info of
+ * the last generated one ("Point").
+ * 
+ * LONG PRESSING LIGHT toggles setup mode. Here pressing LIGHT switches between setting the desired
+ * radius (RA) and setting the random number generator (RNG) for generating the blind spot. 
+ * 
+ * ALARM changes the values respectively:
+ * 
+ * - The radius can be set in 500 meter steps between 1000 and 10,000 meters
+ * 
+ * - The RNG can be set to "true" which utilizes the SAML22J's internal True Random Number Generator
+ * - Setting it to "psudo" will use the pseudorandom number generation algorithm arc4random
+ * - Setting it to "chance" will randomly chose either of the RNGs for each generation (default)
+ *
+ * LONG PRESSING ALARM toggles DATA mode in which the currently generated Blind Spot coordinate can
+ * be written to the <place.loc> file on the watch (press ALARM) and set as active high precision 
+ * location used by other watch faces. It does not overwrite the low precision location information
+ * in the watch register commonly used for astronomical watch faces.
+ * 
+ */
+
+#include "movement.h"
+#include "place_face.h"
+
+typedef struct {
+    uint8_t mode :3;
+    uint8_t location_format :3;
+    uint8_t rng: 2;
+} randonaut_face_mode_t;
+
+typedef struct {
+    int32_t latitude : 26;
+    int32_t longitude : 26;
+    uint16_t distance : 14;
+    uint16_t bearing : 9;
+} randonaut_coordinate_t;
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    coordinate_t location;
+    randonaut_coordinate_t point;
+    uint16_t radius : 14;
+    uint32_t entropy;
+    bool quantum;
+    bool chance;
+    randonaut_face_mode_t face;
+    char scratchpad[10];
+} randonaut_state_t;
+
+void randonaut_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void randonaut_face_activate(void *context);
+bool randonaut_face_loop(movement_event_t event, void *context);
+void randonaut_face_resign(void *context);
+
+#define randonaut_face ((const watch_face_t){ \
+    randonaut_face_setup, \
+    randonaut_face_activate, \
+    randonaut_face_loop, \
+    randonaut_face_resign, \
+    NULL, \
+})
+
+#endif // RANDONAUT_FACE_H_
+
diff --git a/legacy/watch_faces/complication/ratemeter_face.c b/legacy/watch_faces/complication/ratemeter_face.c
new file mode 100644 (file)
index 0000000..746ab39
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 David Singleton
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "ratemeter_face.h"
+#include "watch.h"
+
+#define RATEMETER_FACE_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.)
+#define RATEMETER_FACE_FREQUENCY (1 << RATEMETER_FACE_FREQUENCY_FACTOR)
+
+void ratemeter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) *context_ptr = malloc(sizeof(ratemeter_state_t));
+}
+
+void ratemeter_face_activate(void *context) {
+    memset(context, 0, sizeof(ratemeter_state_t));
+}
+
+bool ratemeter_face_loop(movement_event_t event, void *context) {
+    ratemeter_state_t *ratemeter_state = (ratemeter_state_t *)context;
+    char buf[14];
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_display_string("ra          ", 0);
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+            if (ratemeter_state->ticks != 0) {
+                ratemeter_state->rate = (int16_t)(60.0 / ((float)ratemeter_state->ticks / (float)RATEMETER_FACE_FREQUENCY));
+            }
+            ratemeter_state->ticks = 0;
+            movement_request_tick_frequency(RATEMETER_FACE_FREQUENCY);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            break;
+        case EVENT_TICK:
+            if (ratemeter_state->rate == 0) {
+                watch_display_string("ra          ", 0);
+            } else {
+                if (ratemeter_state->rate > 500) {
+                    watch_display_string("ra      Hi", 0);
+                } else if (ratemeter_state->rate < 1) {
+                    watch_display_string("ra      Lo", 0);
+                } else {
+                    sprintf(buf, "ra  %-3d pn", ratemeter_state->rate);
+                    watch_display_string(buf, 0);
+                }
+            }
+            ratemeter_state->ticks++;
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void ratemeter_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/ratemeter_face.h b/legacy/watch_faces/complication/ratemeter_face.h
new file mode 100644 (file)
index 0000000..85ac255
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 RATEMETER_FACE_H_
+#define RATEMETER_FACE_H_
+
+/*
+ * RATE METER face
+ *
+ * The rate meter shows the rate per minute at which the ALARM button is
+ * being pressed. This is particularly useful in sports where cadence
+ * tracking is useful. For instance, rowing coaches often use a dedicated
+ * rate meter - clicking the rate button each time the crew puts their oars
+ * in the water to see the rate (strokes per minute) on the rate meter.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    int16_t rate;
+    int16_t ticks;
+} ratemeter_state_t;
+
+void ratemeter_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void ratemeter_face_activate(void *context);
+bool ratemeter_face_loop(movement_event_t event, void *context);
+void ratemeter_face_resign(void *context);
+
+#define ratemeter_face ((const watch_face_t){ \
+    ratemeter_face_setup, \
+    ratemeter_face_activate, \
+    ratemeter_face_loop, \
+    ratemeter_face_resign, \
+    NULL, \
+})
+
+#endif // RATEMETER_FACE_H_
diff --git a/legacy/watch_faces/complication/rpn_calculator_alt_face.c b/legacy/watch_faces/complication/rpn_calculator_alt_face.c
new file mode 100644 (file)
index 0000000..03a75ec
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 James Haggerty
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "rpn_calculator_alt_face.h"
+
+static void show_fn(calculator_state_t *state, uint8_t subsecond);
+
+void rpn_calculator_alt_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(calculator_state_t));
+        memset(*context_ptr, 0, sizeof(calculator_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+static void show_number(double num) {
+    char buf[9] = {0};
+    bool negative = num < 0;
+    int max_digits = negative ? 5 : 6;
+
+    // Add back in for debugging...
+    // printf("%f\n", num);
+
+    if (isnan(num)) {
+        watch_clear_colon();
+        watch_display_string("  nan   ", 2);
+        return;
+    }
+
+    if (negative) {
+        num = -num;
+    }
+
+    // Can we reasonably represent this number without a decimal point?
+    if (num == 0 || (num >= 0.5 && fabs(num - (int)num) < 0.0001)) {
+        if (floor(log10(num)) + 1 <= max_digits) {
+            if (negative) {
+                sprintf(buf, "  -%-5d", (int)round(num));
+            } else {
+                sprintf(buf, "  %-6d", (int)round(num));
+            }
+            watch_clear_colon();
+            watch_display_string(buf, 2);
+            return;
+        }
+    }
+
+    // Is this a floating point number where scientific
+    // notation won't get us much? (i.e. between 0.1 and 1)
+    if (num < 1 && num >= 0.0999) {
+        // Display as boring floating point number... (e.g. 0.25)
+        int digits = (int)round(num * 10000);
+        sprintf(buf, "   0%04d", digits);
+        if (negative) {
+            buf[2 ] = '-';
+        }
+        watch_set_colon();
+        watch_display_string(buf, 2);
+        return;
+    }
+
+    // Fall back to scientific notation
+
+    // Calculate exponent
+    int exponent = 0;
+    while (num < 1) {
+        num *= 10;
+        --exponent;
+    }
+
+    while (num >= 10) {
+        num /= 10;
+        ++exponent;
+    }
+
+    if (exponent < -9) {
+        sprintf(buf, "  small ");
+        watch_clear_colon();
+        watch_display_string(buf, 2);
+        return;
+    }
+
+    if (exponent > 39) {
+        sprintf(buf, "   big  ");
+        watch_clear_colon();
+        watch_display_string(buf, 2);
+        return;
+    }
+
+    sprintf(buf, "%2d%c%05d", exponent, negative ? '-' : ' ', (int)round(num * 10000));
+    watch_set_colon();
+    watch_display_string(buf, 2);
+}
+
+#define C (s->stack[s->stack_size - 1])
+#define PUSH(x) (s->stack[++s->stack_size - 1] = x)
+#define POP() (s->stack[s->stack_size-- - 1])
+
+void rpn_calculator_alt_face_activate(void *context) {
+    calculator_state_t *s = (calculator_state_t *)context;
+    s->min = s->max = NAN;
+}
+
+static void change_mode(calculator_state_t *s, enum calculator_mode mode) {
+    s->mode = mode;
+    s->fn_index = 0;
+    show_fn(s, 0);
+    // Faster tick in operation mode so we can blink.
+    movement_request_tick_frequency(mode == CALC_OPERATION ? 4 : 1);
+}
+
+// Binary-ish search to find the right number. direction is +1 if it should be bigger, -1 if it should be smaller.
+static void adjust_number(calculator_state_t *s, int direction) {
+    if (direction > 0) {
+        s->min = C;
+    } else {
+        s->max = C;
+    }
+
+    // If the direction we want to go has no bound (i.e. isnan),
+    // then first get the sign right (moving to 0, then +-10), and
+    // after than go up by *10.
+    if (isnan(direction > 0 ? s->max : s->min)) {
+        if (direction * C < 0) {
+            C = 0;
+        } else if (C == 0) {
+            C = direction * 10;
+        } else {
+            C *= 10;
+        }
+    } else {
+        // We have a higher and lower bound. Split them.
+        C = (s->max + s->min) / 2;
+        // Subtract 0.1 so we don't apply most significant rounding to things that are _exactly_ 1/10/100 apart.
+        double mag = log10(fabs(s->max - s->min)) - 0.1;
+        if (mag > 0.0) {
+            // i.e. the different is >= 2, which means we want to round aggressively
+            // to not show people complicated looking numbers.
+            // e.g. this takes a number like 3.2 to 3, or a number like 464 to 500
+            // (depending on how fine-grained 'mag' tells us to be).
+            float div = pow(10, floor(mag));
+            int sign = C < 0 ? -1 : 1;
+            C = sign * floor(fabs(C) / div) * div;
+        }
+    }
+}
+
+static void fn_number(calculator_state_t *s) {
+    PUSH(10);
+    s->min = s->max = NAN;
+    change_mode(s, CALC_NUMBER);
+}
+
+static void fn_add(calculator_state_t *s) {
+    double a = POP();
+    double b = POP();
+    PUSH(a + b);
+}
+
+static void fn_sub(calculator_state_t *s) {
+    double a = POP();
+    double b = POP();
+    PUSH(b - a);
+}
+
+static void fn_mul(calculator_state_t *s) {
+    double a = POP();
+    double b = POP();
+    PUSH(a * b);
+}
+
+static void fn_div(calculator_state_t *s) {
+    double a = POP();
+    double b = POP();
+    PUSH(b / a);
+}
+
+static void fn_pow(calculator_state_t *s) {
+    double a = POP();
+    double b = POP();
+    PUSH(pow(b, a));
+}
+
+static void fn_sqrt(calculator_state_t *s) {
+    double x = POP();
+    PUSH(sqrt(x));
+}
+
+static void fn_log(calculator_state_t *s) {
+    double x = POP();
+    PUSH(log(x));
+}
+
+static void fn_log10(calculator_state_t *s) {
+    double x = POP();
+    PUSH(log10(x));
+}
+
+static void fn_e(calculator_state_t *s) {
+    PUSH(M_E);
+}
+
+static void fn_sin(calculator_state_t *s) {
+    double x = POP();
+    PUSH(sin(x));
+}
+
+static void fn_cos(calculator_state_t *s) {
+    double x = POP();
+    PUSH(cos(x));
+}
+
+static void fn_tan(calculator_state_t *s) {
+    double x = POP();
+    PUSH(tan(x));
+}
+
+static void fn_pi(calculator_state_t *s) {
+    PUSH(M_PI);
+}
+
+static void fn_pop(calculator_state_t *s) {
+    --s->stack_size;
+}
+
+static void fn_swap(calculator_state_t *s) {
+    double a = POP();
+    double b = POP();
+    PUSH(a);
+    PUSH(b);
+}
+
+static void fn_duplicate(calculator_state_t *s) {
+    double a = POP();
+    PUSH(a);
+    PUSH(a);
+}
+
+static void fn_clear(calculator_state_t *s) {
+    s->stack_size = 0;
+}
+
+static void fn_size(calculator_state_t *s) {
+    double a = s->stack_size;
+    PUSH(a);
+}
+
+struct {
+    char name[2];
+    uint8_t input;
+    uint8_t output;
+    void (*func)(calculator_state_t *);
+} functions[] = {
+    {{'n', 'o'}, 0, 1, fn_number},
+    {{'*', ' '}, 2, 1, fn_add},  // First position * actually looks like a '+'.
+    {{'-', ' '}, 2, 1, fn_sub},
+    {{'H', ' '}, 2, 1, fn_mul},  // For actual *, we throw in the middle vertical segment onto the H.
+    {{'/', ' '}, 2, 1, fn_div},  // There's also some minor hackery on '/'.
+    {{'P', 'o'}, 2, 1, fn_pow},
+    {{'S', 'r'}, 1, 1, fn_sqrt},
+    {{'L', 'n'}, 1, 1, fn_log},
+    {{'L', 'o'}, 1, 1, fn_log10},
+    {{'e', ' '}, 0, 1, fn_e},
+    {{'P', 'i'}, 0, 1, fn_pi},
+    {{'C', 'o'}, 1, 1, fn_cos},
+    {{'S', 'i'}, 1, 1, fn_sin},
+    {{'T', 'a'}, 1, 1, fn_tan},
+    // Stack operations. Accessible via secondary_fn_index (i.e. alarm long press).
+    {{'P', 'O'}, 1, 0, fn_pop},  // This ends up displaying the same as 'POW'. But at least it's in a different place.
+    {{'S', 'W'}, 2, 2, fn_swap},
+    {{'d', 'u'}, 1, 1, fn_duplicate},  // Uppercase 'D' is a bit too 'O' for me.
+    {{'C', 'L'}, 1, 0, fn_clear},  // Operation lie - takes _everything_ off the stack, but a check of 1 is sufficient.
+    {{'L', 'E'}, 1, 0, fn_size},
+};
+
+#define FUNCTIONS_LEN (sizeof(functions) / sizeof(functions[0]))
+#define SECONDARY_FN_INDEX (FUNCTIONS_LEN - 4)
+
+// Show the function name (using day display)
+static void show_fn(calculator_state_t *s, uint8_t subsecond) {
+    if (subsecond % 2) {
+        // blink
+        watch_display_string("  ", 0);
+        return;
+    }
+
+    char *name = functions[s->fn_index].name;
+    char buf[3] = {name[0], name[1], '\0'};
+    watch_display_string(buf, 0);
+    // The first position has a bunch of segments, and I have minor
+    // disagreements with the character set choices in watch_display_string,
+    // so we tweak a little here.
+    switch (buf[0]) {
+        case 'H':
+            // Use the middle segment lines to make our 'H' a '*'-ish thing.
+            watch_set_pixel(1, 14);
+            break;
+        case '/':
+            // Add a middle bar to division.
+            watch_set_pixel(1, 15);
+            break;
+        default:
+            break;
+    }
+}
+
+// Show the top of the stack (using everything except day display).
+static void show_stack_top(calculator_state_t *s) {
+    if (s->stack_size > 0) {
+        show_number(C);
+    } else {
+        watch_display_string("  ------", 2);
+        watch_clear_colon();
+    }
+}
+
+bool rpn_calculator_alt_face_loop(movement_event_t event, void *context) {
+    calculator_state_t *s = (calculator_state_t *)context;
+
+    int proposed_stack_size;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            change_mode(s, CALC_OPERATION);
+            show_stack_top(s);
+            break;
+        case EVENT_TICK:
+            if (s->mode == CALC_OPERATION) {
+                show_fn(s, event.subsecond);
+            }
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            if (s->mode == CALC_NUMBER) {
+                adjust_number(s, -1);
+                show_stack_top(s);
+            } else {
+                movement_move_to_next_face();
+                return false;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            proposed_stack_size = s->stack_size - functions[s->fn_index].input;
+
+            if (s->mode == CALC_NUMBER) {
+                change_mode(s, CALC_OPERATION);
+            } else if (proposed_stack_size < 0 || proposed_stack_size + functions[s->fn_index].output > CALC_MAX_STACK_SIZE) {
+                movement_play_signal();
+                break;
+            } else {
+                functions[s->fn_index].func(s);
+                show_stack_top(s);
+                s->fn_index = 0;
+                show_fn(s, 0);
+            }
+
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if (s->mode == CALC_NUMBER) {
+                adjust_number(s, 1);
+                show_stack_top(s);
+            } else {
+                s->fn_index = (s->fn_index + 1) % FUNCTIONS_LEN;
+                show_fn(s, 0);
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (s->mode == CALC_OPERATION) {
+                if (s->fn_index == 0) {
+                    s->fn_index = SECONDARY_FN_INDEX;
+                } else {
+                    s->fn_index = 0;
+                }
+                show_fn(s, 0);
+            }
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
+    // you should return false since the PWM driver does not operate in standby mode.
+    return true;
+}
+
+void rpn_calculator_alt_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/legacy/watch_faces/complication/rpn_calculator_alt_face.h b/legacy/watch_faces/complication/rpn_calculator_alt_face.h
new file mode 100644 (file)
index 0000000..4e74115
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 <#author_name#>
+ *
+ * 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 CALCULATOR_FACE_H_
+#define CALCULATOR_FACE_H_
+
+/* 
+ * RPN Calculator alternate face.
+ *
+ * Operations appear in the 'day' section; ALARM changes between operations when
+ * operation is flashing. LIGHT executes current operation.
+ *
+ * This is the alternate face because it has a non-traditional number entry system which
+ * I call 'guess a number'. In number entry mode, the watch tries to guess which number you
+ * want, and you respond with 'smaller' (left - MODE) or larger (right - ALARM). This means
+ * that when you _are_ entering a number, MODE will no longer move between faces!
+ *
+ * Example of entering the number 27
+ *  - select the NO operation (probably unnecessary, as this is the default),
+ *    and execute it by hitting LIGHT.
+ *  - you are now in number entry mode; you know this because nothing is flashing.
+ *  - Watch displays 10; you hit ALARM to say you want a larger number.
+ *  - Watch displays 100; you hit MODE to say you want a smaller number.
+ *  - Continuing: 50 -> MODE -> 30 -> MODE -> 20 -> ALARM -> 27
+ *  - Hit LIGHT to add the number to the stack (and now 'NO' is flashing
+ *    again, indicating you're back in operation selection mode).
+ *
+ * One other thing to watch out for is how quickly it will switch into scientific notation
+ * due to the limitations of the display when you have large numbers or non-integer values.
+ * In this mode, the 'colon' serves at the decimal point, and the numbers in the top right
+ * are the exponent.
+ *
+ * As with the main movement firmware, this has the concept of 'secondary' functions which
+ * you can jump to by a long hold of ALARM on NO. These are functions to do with stack
+ * manipulation (pop, swap, dupe, clear, size (le)). If you're _not_ on NO, a long
+ * hold will take you back to it.
+ *
+ * See 'functions' in "rpn_calculator_alt_face.c" for names of all operations.
+ */
+
+#include "movement.h"
+
+#define CALC_MAX_STACK_SIZE 20
+
+enum calculator_mode {
+    CALC_OPERATION = 0,
+    CALC_NUMBER,
+};
+
+typedef struct {
+    double stack[CALC_MAX_STACK_SIZE];
+    uint8_t stack_size;  // this is the current stack top + 1 (so that '0' means nothing on the stack)
+    uint8_t fn_index;
+
+    double min;
+    double max;
+
+    enum calculator_mode mode;
+} calculator_state_t;
+
+void rpn_calculator_alt_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void rpn_calculator_alt_face_activate(void *context);
+bool rpn_calculator_alt_face_loop(movement_event_t event, void *context);
+void rpn_calculator_alt_face_resign(void *context);
+
+#define rpn_calculator_alt_face ((const watch_face_t){ \
+    rpn_calculator_alt_face_setup, \
+    rpn_calculator_alt_face_activate, \
+    rpn_calculator_alt_face_loop, \
+    rpn_calculator_alt_face_resign, \
+    NULL, \
+})
+
+#endif // CALCULATOR_FACE_H_
+
diff --git a/legacy/watch_faces/complication/rpn_calculator_face.c b/legacy/watch_faces/complication/rpn_calculator_face.c
new file mode 100644 (file)
index 0000000..2c5a883
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Niclas Hoyer
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "rpn_calculator_face.h"
+
+static void draw_number(char *buf, float num) {
+    float f = fmodf(num, 1.0) * 100;
+    sprintf(buf, "CA  %4d%02d", ((int)num) % 10000, (int) f);
+}
+
+static void draw_op(char *buf, rpn_calculator_op_t op) {
+    switch (op) {
+        case rpn_calculator_op_add:
+            sprintf(buf, "CA     Add");
+        break;
+        case rpn_calculator_op_sub:
+            sprintf(buf, "CA     sub");
+        break;
+        case rpn_calculator_op_mul:
+            sprintf(buf, "CA    n&ul");
+        break;
+        case rpn_calculator_op_div:
+            sprintf(buf, "CA     div");
+        break;
+        case rpn_calculator_op_pow:
+            sprintf(buf, "CA     pow");
+        break;
+        case rpn_calculator_op_sqrt:
+            sprintf(buf, "CA    sqrt");
+        break;
+        case rpn_calculator_op_pi:
+            sprintf(buf, "CA      pi");
+            break;
+        default:
+            break;
+    }
+}
+
+static void printf_stack(rpn_calculator_state_t *state) {
+    printf("Stack: [%f, %f, %f, %f], top: %d\n",
+        state->stack[0],
+        state->stack[1],
+        state->stack[2],
+        state->stack[3],
+        state->top
+    );
+}
+
+static void next_op(rpn_calculator_state_t *state) {
+    state->op += 1;
+    state->op = state->op % RPN_CALCULATOR_MAX_OPS;
+}
+
+// increase a digit of a floating point number
+// FIXME: this converts the number to string and back, there might
+// be better ways to do this
+static float inc_digit(float num, uint8_t position) {
+    char buf[8];
+    if (position > 5) {
+        return 0.0;
+    }
+    // inverse position (rtl)
+    position = 5 - position;
+    float f = fmodf(num, 1.0) * 100;
+    sprintf(buf, "%04d%02d", ((int)num) % 10000, (int) f);
+    uint8_t digit = buf[position] - '0';
+    digit = (digit + 1) % 10;
+    buf[position] = digit + '0';
+    printf("buf: %s\n", buf);
+    return strtof(buf, NULL) / 100.0;
+}
+
+static void stack_push(rpn_calculator_state_t *state, float f) {
+    printf_stack(state);
+    printf("push: %f\n", f);
+    state->top++;
+    if (state->top >= RPN_CALCULATOR_STACK_SIZE) {
+        // FIXME: implement this using a circular buffer?
+        for (int i=0; i<RPN_CALCULATOR_STACK_SIZE-1; i++) {
+            state->stack[i] = state->stack[i+1];
+        }
+    }
+    state->stack[state->top] = f;
+}
+
+static float stack_peek(rpn_calculator_state_t *state) {
+    if (state->top > -1) {
+        return state->stack[state->top];
+    }
+    return 0;
+}
+
+static float stack_pop(rpn_calculator_state_t *state) {
+    printf_stack(state);
+    float f = stack_peek(state);
+    state->stack[state->top] = 0;
+    printf("pop: %f\n", f);
+    if (state->top > -1) {
+        state->top--;
+    } else {
+        state->top = -1; // empty
+    }
+    return f;
+}
+
+static void run_op(rpn_calculator_state_t *state) {
+    printf_stack(state);
+    bool op_found = false;
+    // ops without parameters
+    switch (state->op)  {
+        case rpn_calculator_op_pi:
+            stack_push(state, M_PI);
+            op_found = true;
+            break;
+        default:
+            break;
+    }
+    if (op_found) {
+        state->mode = rpn_calculator_waiting;
+        return;
+    }
+
+    // ops with one parameter
+    if (state->top < 0) {
+        state->mode = rpn_calculator_err;
+        return;
+    }
+    float right = stack_pop(state);
+    printf("right: %f\n", right);
+    switch (state->op)  {
+        case rpn_calculator_op_sqrt:
+            stack_push(state, sqrt(right));
+            op_found = true;
+            break;
+        default:
+            break;
+    }
+    if (op_found) {
+        state->mode = rpn_calculator_waiting;
+        return;
+    }
+
+    // ops with two parameters
+    if (state->top < 0) {
+        // no parameter left -> error
+        state->mode = rpn_calculator_err;
+        return;
+    }
+    float left = stack_pop(state);
+    printf("left: %f\n", left);
+    switch (state->op)  {
+        case rpn_calculator_op_add:
+            stack_push(state, left + right);
+            op_found = true;
+            break;
+        case rpn_calculator_op_sub:
+            stack_push(state, left - right);
+            op_found = true;
+            break;
+        case rpn_calculator_op_mul:
+            stack_push(state, left * right);
+            op_found = true;
+            break;
+        case rpn_calculator_op_div:
+            stack_push(state, left / right);
+            op_found = true;
+            break;
+        case rpn_calculator_op_pow:
+            stack_push(state, powf(left, right));
+            op_found = true;
+            break;
+        default:
+            break;
+    }
+    if (op_found) {
+        state->mode = rpn_calculator_waiting;
+        return;
+    }
+    state->mode = rpn_calculator_err;
+}
+
+static void draw(rpn_calculator_state_t *state, uint8_t subsecond) {
+    char buf[16];
+    switch (state->mode) {
+        case rpn_calculator_err:
+            sprintf(buf, "CA   err  ");
+            break;
+        case rpn_calculator_number:
+            draw_number(buf, stack_peek(state));
+            uint8_t i = 4 + (5 - state->selection);
+            if (buf[i] == ' ') {
+                buf[i] = '0';
+            }
+            if (subsecond % 2) {
+                buf[i] = ' ';
+            }
+            break;
+        case rpn_calculator_waiting:
+            printf_stack(state);
+            draw_number(buf, stack_peek(state));
+            break;
+        case rpn_calculator_op:
+            draw_op(buf, state->op);
+            break;
+        default:
+            break;
+    }
+    watch_display_string(buf, 0);
+}
+
+void rpn_calculator_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(rpn_calculator_state_t));
+        memset(*context_ptr, 0, sizeof(rpn_calculator_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+        rpn_calculator_state_t *state = *context_ptr;
+        state->top = -1;
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void rpn_calculator_face_activate(void *context) {
+    (void) context;
+
+    // Handle any tasks related to your watch face coming on screen.
+}
+
+bool rpn_calculator_face_loop(movement_event_t event, void *context) {
+
+    rpn_calculator_state_t *state = (rpn_calculator_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            draw(state, event.subsecond);
+            break;
+        case EVENT_TICK:
+            if (state->mode == rpn_calculator_number) {
+                draw(state, event.subsecond);
+            }
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            switch (state->mode) {
+                case rpn_calculator_number:
+                    state->mode = rpn_calculator_waiting;
+                    draw(state, event.subsecond);
+                    movement_request_tick_frequency(1);
+                    break;
+                default:
+                    state->mode = rpn_calculator_waiting;
+                    movement_request_tick_frequency(1);
+                    movement_move_to_next_face();
+                break;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            switch (state->mode) {
+                case rpn_calculator_waiting:
+                    state->mode = rpn_calculator_op;
+                    draw(state, event.subsecond);
+                    break;
+                case rpn_calculator_number:
+                    state->selection = (state->selection + 1) % 6;
+                    draw(state, event.subsecond);
+                    break;
+                case rpn_calculator_op:
+                    next_op(state);
+                    draw(state, event.subsecond);
+                    break;
+                default:
+                    movement_illuminate_led();
+                    break;
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch (state->mode) {
+                case rpn_calculator_waiting:
+                    state->mode = rpn_calculator_number;
+                    state->selection = 2;
+                    stack_push(state, 0);
+                    draw(state, event.subsecond);
+                    movement_request_tick_frequency(4);
+                    break;
+                case rpn_calculator_number:
+                    state->stack[state->top] = inc_digit(state->stack[state->top], state->selection);
+                    printf_stack(state);
+                    draw(state, event.subsecond);
+                    break;
+                case rpn_calculator_err:
+                    state->mode = rpn_calculator_waiting;
+                    draw(state, event.subsecond);
+                    break;
+                case rpn_calculator_op:
+                    run_op(state);
+                    draw(state, event.subsecond);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_TIMEOUT:
+            state->mode = rpn_calculator_waiting;
+            movement_request_tick_frequency(1);
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
+    // you should return false since the PWM driver does not operate in standby mode.
+    return true;
+}
+
+void rpn_calculator_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
diff --git a/legacy/watch_faces/complication/rpn_calculator_face.h b/legacy/watch_faces/complication/rpn_calculator_face.h
new file mode 100644 (file)
index 0000000..a9daad6
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Niclas Hoyer
+ *
+ * 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 RPN_CALCULATOR_FACE_H_
+#define RPN_CALCULATOR_FACE_H_
+
+/*
+ * RPN CALCULATOR face
+ *
+ * A calculator face using reverse polish notation (RPN).
+ *
+ * For usage instructions, please refer to the wiki:
+ * https://www.sensorwatch.net/docs/watchfaces/complication/#rpn-calculator
+ */
+
+#include "movement.h"
+
+#define RPN_CALCULATOR_STACK_SIZE 4
+#define RPN_CALCULATOR_MAX_OPS 7;
+
+typedef enum {
+    rpn_calculator_op_add,
+    rpn_calculator_op_sub,
+    rpn_calculator_op_mul,
+    rpn_calculator_op_div,
+    rpn_calculator_op_pow,
+    rpn_calculator_op_sqrt,
+    rpn_calculator_op_pi
+} rpn_calculator_op_t;
+
+typedef enum {
+    rpn_calculator_waiting,
+    rpn_calculator_op,
+    rpn_calculator_number,
+    rpn_calculator_err,
+} rpn_calculator_mode_t;
+
+typedef struct {
+    rpn_calculator_mode_t mode;
+    rpn_calculator_op_t op;
+    float stack[RPN_CALCULATOR_STACK_SIZE];
+    int8_t top;
+    uint8_t selection;
+} rpn_calculator_state_t;
+
+void rpn_calculator_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void rpn_calculator_face_activate(void *context);
+bool rpn_calculator_face_loop(movement_event_t event, void *context);
+void rpn_calculator_face_resign(void *context);
+
+#define rpn_calculator_face ((const watch_face_t){ \
+    rpn_calculator_face_setup, \
+    rpn_calculator_face_activate, \
+    rpn_calculator_face_loop, \
+    rpn_calculator_face_resign, \
+    NULL, \
+})
+
+#endif // RPN_CALCULATOR_FACE_H_
+
diff --git a/legacy/watch_faces/complication/sailing_face.c b/legacy/watch_faces/complication/sailing_face.c
new file mode 100644 (file)
index 0000000..9f8109b
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jan H. Voigt
+ * Copyright (c) 2022 Wesley Ellis
+ * Copyright (c) 2022 Niclas Hoyer
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "sailing_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+#define sl_SELECTIONS 6
+#define DEFAULT_MINUTES { 5,4,1,0,0,0 }
+
+static inline int32_t get_tz_offset() {
+    return movement_get_current_timezone_offset();
+}
+
+static int lap = 0;
+bool ringflag = false;
+int8_t double_beep[] = {BUZZER_NOTE_C8, 4, BUZZER_NOTE_REST, 5, BUZZER_NOTE_C8, 5, 0};
+int8_t single_beep[] = {BUZZER_NOTE_C8, 4, 0};
+int8_t long_beep[] = {BUZZER_NOTE_C8, 40, 0};
+int beepseconds[] = {600, 540, 480, 420, 360, 300, 240, 180, 120, 60, 30, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; //seconds before start that can trigger the buzzer. Every whole minute, 30s before start & 10s countdown.
+int beepseconds_size = sizeof(beepseconds) / sizeof(int);
+int beepflag = 0;
+int alarmflag = 3;
+
+static void reset(sailing_state_t *state) {
+    state->index = 0;
+    state->mode = sl_waiting;
+    movement_cancel_background_task();
+    watch_clear_indicator(WATCH_INDICATOR_LAP);
+    beepflag = 0;
+    ringflag = false;
+}
+
+static void counting(sailing_state_t *state) {
+    state->mode = sl_counting;  
+    movement_cancel_background_task();
+    watch_set_indicator(WATCH_INDICATOR_LAP);
+}
+
+static void draw(sailing_state_t *state, uint8_t subsecond) {
+
+    char tmp[24];
+    char buf[16];
+
+    uint32_t delta;
+    div_t result;
+    uint8_t hrs, min, sec;
+
+    switch (state->mode) {
+        case sl_running:
+            if (state->now_ts > state->target_ts) {
+                delta = 0;
+                counting(state); //in case buttons are pressed while sound is played (esp. in the last 10s), the timing of ring might be thrown off. Beep stops and the switch to counting doesn't occur. temporary fix/safety net. 
+            } else {
+                delta = state->target_ts - state->now_ts;
+            }
+            result = div(delta, 60);
+            min = result.quot;
+            sec = result.rem;
+
+            if (min > 0) {
+                sprintf(buf, "SA1L  %2d%02d", min, sec);
+            } else {
+                sprintf(buf, "SA1L  %2d  ", sec);
+            }
+            break;
+        case sl_waiting:
+            sprintf(buf, "SA1L  %2d%02d", state->minutes[0], 0);
+            break;
+        case sl_setting:
+            // this sprintf to a larger tmp is to guarantee that no buffer overflows
+            // occur here (and to squelch the corresponding compiler warning)
+            if (state->minutes[0] == 10) { //print 10 vertically.
+                sprintf(tmp, "SA1L %1d%1d%1d%1d%1d",
+                    state->minutes[1],
+                    state->minutes[2],
+                    state->minutes[3],
+                    state->minutes[4],
+                    state->minutes[5]
+                );
+                memcpy(buf, tmp, sizeof(buf));
+                if (subsecond % 2) {
+                    buf[4 + state->selection] = ' ';
+                }
+                watch_display_string(buf, 0);
+                if (!(subsecond % 2) || state->selection != 0) {
+                    watch_set_pixel(0, 18);
+                    watch_set_pixel(0, 19);
+                    watch_set_pixel(1, 18);
+                    watch_set_pixel(1, 19);
+                }
+                return;
+            }
+            sprintf(tmp, "SA1L%1d%1d%1d%1d%1d%1d",
+                state->minutes[0],
+                state->minutes[1],
+                state->minutes[2],
+                state->minutes[3],
+                state->minutes[4],
+                state->minutes[5]
+            );
+            memcpy(buf, tmp, sizeof(buf));
+            if (subsecond % 2) {
+                buf[4 + state->selection] = ' ';
+            }
+            break;
+        case sl_counting:
+            delta = state->now_ts - state->target_ts;
+            if (state->now_ts <= state->target_ts) {
+                sprintf(buf, "SA1L  %2d  ", 0);
+            }
+            else {
+                result = div(delta, 3600);
+                hrs = result.quot;
+                delta -= 60*hrs;
+                result = div(delta, 60);
+                min = result.quot;
+                sec = result.rem;
+                sprintf(buf, "SL%2d%2d%02d%02d", lap, hrs, min, sec);//implement counting
+                if (hrs > 23) {
+                    reset(state);
+                }
+            }
+            break;
+    }
+    watch_display_string(buf, 0);
+}
+
+static void ring(sailing_state_t *state) {    
+// if ring is called in background (while on another face), a button press can interrupt and cancel the execution.
+// To reduce the probability of cancelling all future alarms, the new alarm is set as soon as possible after calling ring.
+    movement_cancel_background_task();
+    if (beepflag + 1 == beepseconds_size) { //equivalent to (beepflag + 1 == sizeof(beepseconds) / sizeof(int)) but without needing to divide here => quicker
+        if (alarmflag != 0){
+            watch_buzzer_play_sequence(long_beep, NULL);
+        }
+        movement_cancel_background_task();
+        counting(state);
+        return;
+    }
+    state->nextbeep_ts = state->target_ts - beepseconds[beepflag+1];
+    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings));
+    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.
+    for (int i = 0; i < 5; i++) {
+        if (beepseconds[beepflag] == 60 * state->minutes[i]) {
+            if (alarmflag > 1) {
+                watch_buzzer_play_sequence((int8_t *)double_beep, NULL);
+            }   
+            ringflag = true;           
+        }
+    }
+    if (!ringflag) {
+        if (alarmflag == 3) {
+            watch_buzzer_play_sequence((int8_t *)single_beep, NULL);
+        }
+    }
+    ringflag = false;
+    beepflag++;
+}
+
+static void start(sailing_state_t *state) {//gets called by starting / switching to next signal
+    while (beepseconds[beepflag] < state->minutes[state->index]*60) {
+        state->index++;
+    }
+    while (beepseconds[beepflag] > state->minutes[state->index]*60) {
+        beepflag++;
+    }
+    if (state->index > 5 || state->minutes[state->index] == 0) {
+        watch_date_time_t now = watch_rtc_get_date_time();
+        state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
+        state->target_ts = state->now_ts;
+        if (alarmflag != 0){
+            watch_buzzer_play_sequence(long_beep, NULL);
+        }
+        counting(state);
+        return;
+    }
+    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;
+    watch_date_time_t now = watch_rtc_get_date_time();
+    state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
+    state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, state->minutes[state->index], 0);
+    ring(state, settings);
+}
+
+static void settings_increment(sailing_state_t *state) {
+    state->minutes[state->selection] += 1;
+    uint8_t max = 11;
+    if (state->selection > 0) {
+        max = state->minutes[state->selection-1];
+    }
+    if (state->minutes[state->selection] >= max) {
+        state->minutes[state->selection] = 0;
+    }
+    // ensure that minutes are decreasing
+    if (state->selection < 5) {
+        for (uint8_t i = 0; i < 5; i++) {
+            if (state->minutes[i+1] >= state->minutes[i]) {
+                if (state->minutes[i] > 0) {
+                    state->minutes[i+1] = state->minutes[i] - 1;
+                } else {
+                    state->minutes[i+1] = 0;
+                }
+            }
+        }
+    }
+    return;
+}
+
+void sailing_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(sailing_state_t));
+        sailing_state_t *state = (sailing_state_t *)*context_ptr;
+        memset(*context_ptr, 0, sizeof(sailing_state_t));
+        static const uint8_t default_minutes[6] = DEFAULT_MINUTES;
+        memcpy(&state->minutes, default_minutes, sizeof(default_minutes));
+        state->watch_face_index = watch_face_index;
+    }
+}
+
+void sailing_face_activate(void *context) {
+    sailing_state_t *state = (sailing_state_t *)context;
+    if(state->mode == sl_running) {
+        watch_date_time_t now = watch_rtc_get_date_time();
+        state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
+    }
+    if(state->mode == sl_counting) {
+        watch_date_time_t now = watch_rtc_get_date_time();
+        state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
+        watch_set_indicator(WATCH_INDICATOR_LAP);
+    }
+    switch (alarmflag) {
+        case 0: //no sound
+            watch_clear_indicator(WATCH_INDICATOR_BELL);
+            watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+            break;
+        case 1: //sound at start only
+            watch_set_indicator(WATCH_INDICATOR_BELL);
+            watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+            break;
+        case 2: //sound at set minutes
+            watch_clear_indicator(WATCH_INDICATOR_BELL);
+            watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+            break;
+        case 3: //sound at every minute, 30s, 10-0s
+            watch_set_indicator(WATCH_INDICATOR_BELL);
+            watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+            break;
+    }
+}
+
+
+bool sailing_face_loop(movement_event_t event, void *context) {
+    sailing_state_t *state = (sailing_state_t *)context;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            draw(state, event.subsecond, settings);
+            break;
+        case EVENT_TICK:
+            if (state->mode == sl_running || state->mode == sl_counting) {
+                state->now_ts++;
+            }
+            draw(state, event.subsecond, settings);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (state->mode == sl_running) {
+                reset(state);
+            }
+            if (state->mode == sl_counting) {
+                reset(state);
+            }
+            if (state->mode == sl_setting) {
+                if (alarmflag == 3) {
+                    alarmflag = 0;
+                }
+                else {
+                    alarmflag++;
+                }
+                switch (alarmflag) {
+                    case 0: //no sound
+                        watch_clear_indicator(WATCH_INDICATOR_BELL);
+                        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+                        break;
+                    case 1: //sound at start only
+                        watch_set_indicator(WATCH_INDICATOR_BELL);
+                        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+                        break;
+                    case 2: //sound at set minutes
+                        watch_clear_indicator(WATCH_INDICATOR_BELL);
+                        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+                        break;
+                    case 3: //sound at every minute, 30s, 10-0s
+                        watch_set_indicator(WATCH_INDICATOR_BELL);
+                        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+                        break;
+                }
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            switch(state->mode) {
+                case sl_running:
+                    movement_illuminate_led();
+                    break;
+                case sl_waiting:
+                    state->mode = sl_setting;
+                    movement_request_tick_frequency(4);
+                    break;
+                case sl_setting:
+                    state->selection++;
+                    if(state->selection >= sl_SELECTIONS) {
+                        state->selection = 0;
+                        state->mode = sl_waiting;
+                        movement_request_tick_frequency(1);
+                    }
+                    break;
+                case sl_counting:
+                    movement_illuminate_led();
+                    break;
+            }
+            draw(state, event.subsecond, settings);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch(state->mode) {
+                case sl_running:
+                    start(state, settings);
+                    break;
+                case sl_waiting:
+                    start(state, settings);
+                    break;
+                case sl_setting:
+                    settings_increment(state);
+                    break;
+                case sl_counting:
+                    //implement lap counting up to 39
+                    if (lap <39){
+                        lap++;
+                    }
+                    break;
+            }
+            draw(state, event.subsecond, settings);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            ring(state, settings);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->mode == sl_setting) {
+                    static const uint8_t default_minutes[6] = DEFAULT_MINUTES;
+                    memcpy(&state->minutes, default_minutes, sizeof(default_minutes));
+                    state->index = 0;
+                    draw(state, event.subsecond, settings);
+                    break;
+            }
+            if (state->mode == sl_counting) {
+                lap = 0;
+            }
+            break;
+        case EVENT_TIMEOUT:
+            if (state->mode != sl_running && state->mode != sl_counting) {
+                movement_move_to_face(0);
+            }
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void sailing_face_resign(void *context) {
+    sailing_state_t *state = (sailing_state_t *)context;
+    if (state->mode == sl_setting) {
+        state->selection = 0;
+        state->mode = sl_waiting;
+    }
+}
diff --git a/legacy/watch_faces/complication/sailing_face.h b/legacy/watch_faces/complication/sailing_face.h
new file mode 100644 (file)
index 0000000..b9a5cfa
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jan H. Voigt
+ * Copyright (c) 2022 Wesley Ellis
+ * Copyright (c) 2022 Niclas Hoyer
+ *
+ * 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 SAILING_FACE_H_
+#define SAILING_FACE_H_
+
+/*
+ * SAILING face
+ * Implements a sailing timer.
+ *
+ * Usage:
+ *
+ * Waiting mode:
+ * LIGHT button enters settings
+ * ALARM button starts the timer (sailing mode).
+ *
+ * Sailing mode:
+ * ALARM button switches to next programmed start signal.
+ * Long press on LIGHT button resets timer and enters waiting mode.
+ * Countdown to zero, then switch to counting mode.
+ *
+ * Counting mode:
+ * After the start signal (0s), the duration of the race is counted (like a stopwatch timer).
+ * ALARM button increases the lap counter, ALARM long press resets lap counter.
+ * Long press on LIGHT button resets timer and enters waiting mode.
+ *
+ * Setting mode:
+ * ALARM button increases active (blinking) signal. Goes to 0 if upper boundary
+ * (11 or whatever the signal left to the active one is set to) is met.
+ * 10 is printed vertically (letter o plus top segment).
+ * ALARM button long press resets to default minutes (5-4-1-0).
+ * LIGHT button cycles through the signals.
+ * Long press on LIGHT button cycles through sound modes:
+ * - Bell indicator: Sound at start (0s) only.
+ * - Signal indicator: Sound at each programmed signal and at start.
+ * - Bell+Signal: Sound at each minute, at 30s and at 10s countdown.
+ * - No indicator: No sound.
+ */
+
+#include "movement.h"
+
+typedef enum {
+    sl_waiting,
+    sl_running,
+    sl_setting,
+    sl_counting
+} sailing_mode_t;
+
+typedef struct {
+    uint8_t watch_face_index;
+    uint32_t target_ts;
+    uint32_t now_ts;
+    uint32_t nextbeep_ts;
+    uint8_t index;
+    uint8_t minutes[6];
+    uint8_t selection;
+    sailing_mode_t mode;
+} sailing_state_t;
+
+
+void sailing_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void sailing_face_activate(void *context);
+bool sailing_face_loop(movement_event_t event, void *context);
+void sailing_face_resign(void *context);
+
+#define sailing_face ((const watch_face_t){ \
+    sailing_face_setup, \
+    sailing_face_activate, \
+    sailing_face_loop, \
+    sailing_face_resign, \
+    NULL, \
+})
+
+#endif // sailing_FACE_H_
diff --git a/legacy/watch_faces/complication/ships_bell_face.c b/legacy/watch_faces/complication/ships_bell_face.c
new file mode 100644 (file)
index 0000000..fc4d51b
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 buckket
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "ships_bell_face.h"
+
+static void ships_bell_ring() {
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+
+    date_time.unit.hour %= 4;
+    date_time.unit.hour = date_time.unit.hour == 0 && date_time.unit.minute < 30 ? 4 : date_time.unit.hour;
+
+    for (uint8_t i = 0; i < date_time.unit.hour; i++) {
+        watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
+        watch_buzzer_play_note(BUZZER_NOTE_C8, 100);
+        watch_buzzer_play_note(BUZZER_NOTE_REST, 250);
+    }
+
+    if (date_time.unit.minute >= 30 ? 1 : 0) {
+        watch_buzzer_play_note(BUZZER_NOTE_C8, 100);
+    }
+}
+
+static void ships_bell_draw(ships_bell_state_t *state) {
+    char buf[8];
+
+    if (state->on_watch) {
+        sprintf(buf, "%d", state->on_watch);
+    } else {
+        sprintf(buf, " ");
+    }
+
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    date_time.unit.hour %= 4;
+
+    sprintf(buf + 1, " %d%02d%02d", date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
+    watch_display_string(buf, 3);
+}
+
+void ships_bell_face_setup(uint8_t watch_face_index, void **context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(ships_bell_state_t));
+        memset(*context_ptr, 0, sizeof(ships_bell_state_t));
+    }
+}
+
+void ships_bell_face_activate(void *context) {
+
+    ships_bell_state_t *state = (ships_bell_state_t *) context;
+    if (state->bell_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+    else watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+    watch_display_string("SB", 0);
+    watch_set_colon();
+}
+
+bool ships_bell_face_loop(movement_event_t event, void *context) {
+
+    ships_bell_state_t *state = (ships_bell_state_t *) context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            ships_bell_draw(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            state->bell_enabled = !state->bell_enabled;
+            if (state->bell_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+            else watch_clear_indicator(WATCH_INDICATOR_BELL);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->on_watch = (state->on_watch + 1) % 4;
+            ships_bell_draw(state);
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        case EVENT_BACKGROUND_TASK:
+            if (watch_is_buzzer_or_led_enabled()) {
+                ships_bell_ring();
+            } else {
+                watch_enable_buzzer();
+                ships_bell_ring();
+                watch_disable_buzzer();
+            }
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void ships_bell_face_resign(void *context) {
+    (void) context;
+}
+
+movement_watch_face_advisory_t ships_bell_face_advise(void *context) {
+    ships_bell_state_t *state = (ships_bell_state_t *) context;
+    movement_watch_face_advisory_t retval = { 0 };
+
+    if (!state->bell_enabled) return retval;
+
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    if (!(date_time.unit.minute == 0 || date_time.unit.minute == 30)) return retval;
+
+    date_time.unit.hour %= 12;
+    // #SecondMovement: This was migrated to the new advisory API but not tested. Needs more testing!
+    switch (state->on_watch) {
+        case 1:
+            if ((date_time.unit.hour >= 4 && date_time.unit.hour < 8) ||
+                (date_time.unit.hour == 8 && date_time.unit.minute == 0))
+                retval.wants_background_task = true;
+                break;
+        case 2:
+            if ((date_time.unit.hour >= 8 && date_time.unit.hour < 12) ||
+                (date_time.unit.hour == 0 && date_time.unit.minute == 0))
+                retval.wants_background_task = true;
+                break;
+        case 3:
+            if ((date_time.unit.hour >= 0 && date_time.unit.hour < 4) ||
+                (date_time.unit.hour == 4 && date_time.unit.minute == 0))
+                retval.wants_background_task = true;
+                break;
+        default:
+            retval.wants_background_task = true;
+    }
+
+    return retval;
+}
diff --git a/legacy/watch_faces/complication/ships_bell_face.h b/legacy/watch_faces/complication/ships_bell_face.h
new file mode 100644 (file)
index 0000000..bda1697
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 buckket
+ *
+ * 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 SHIPS_BELL_FACE_H_
+#define SHIPS_BELL_FACE_H_
+
+/*
+ * SHIP'S BELL face
+ * A ship's bell complication.
+ *
+ * See: https://en.wikipedia.org/wiki/Ship%27s_bell#Simpler_system
+ *
+ * Similar to the default hourly signal of the simple_clock_face this complication will use the buzzer to signal
+ * the time in half-hour intervals according to the scheme mentioned above.
+ *
+ * Additionally, the user can specify one of the three watches
+ * of the standard merchant watch system to only receive signals during this watch.
+ *
+ * If no watch is specified all signals are emitted.
+ *
+ * Usage:
+ *   - short press Alarm button: Turn on/off bell
+ *   - long press Alarm button: Cycle through the watches (All/1/2/3)
+ */
+
+#include "movement.h"
+
+typedef struct {
+    bool bell_enabled;
+    uint8_t on_watch;
+} ships_bell_state_t;
+
+void ships_bell_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void ships_bell_face_activate(void *context);
+bool ships_bell_face_loop(movement_event_t event, void *context);
+void ships_bell_face_resign(void *context);
+movement_watch_face_advisory_t ships_bell_face_advise(void *context);
+
+#define ships_bell_face ((const watch_face_t){ \
+    ships_bell_face_setup, \
+    ships_bell_face_activate, \
+    ships_bell_face_loop, \
+    ships_bell_face_resign, \
+    ships_bell_face_advise, \
+})
+
+#endif // SHIPS_BELL_FACE_H_
diff --git a/legacy/watch_faces/complication/simon_face.c b/legacy/watch_faces/complication/simon_face.c
new file mode 100644 (file)
index 0000000..41bc5c3
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 <#author_name#>
+ *
+ * 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.
+ */
+
+#include "simon_face.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Emulator only: need time() to seed the random number generator
+#if __EMSCRIPTEN__
+#include <time.h>
+#endif
+
+static char _simon_display_buf[12];
+static uint8_t _timer;
+static uint16_t _delay_beep;
+static uint16_t _timeout;
+static uint8_t _secSub;
+
+static inline uint8_t _simon_get_rand_num(uint8_t num_values) {
+#if __EMSCRIPTEN__
+    return rand() % num_values;
+#else
+    return arc4random_uniform(num_values);
+#endif
+}
+
+static void _simon_clear_display(simon_state_t *state) {
+    if (state->playing_state == SIMON_NOT_PLAYING) {
+        watch_display_string("          ", 0);
+    } else {
+        sprintf(_simon_display_buf, "  %2d      ", state->sequence_length);
+        watch_display_string(_simon_display_buf, 0);
+    }
+}
+
+static void _simon_not_playing_display(simon_state_t *state) {
+    _simon_clear_display(state);
+
+    sprintf(_simon_display_buf, "SI  %d", state->best_score);
+    if (!state->soundOff)
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    else
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+    if (!state->lightOff)
+        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    else
+        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+    watch_display_string(_simon_display_buf, 0);
+    switch (state->mode)
+    {
+    case SIMON_MODE_EASY:
+        watch_display_string("E", 9);
+        break;
+    case SIMON_MODE_HARD:
+        watch_display_string("H", 9);
+        break;
+    default:
+        break;
+    }
+}
+
+static void _simon_reset(simon_state_t *state) {
+    state->playing_state = SIMON_NOT_PLAYING;
+    state->listen_index = 0;
+    state->sequence_length = 0;
+    _simon_not_playing_display(state);
+}
+
+
+static void _simon_display_note(SimonNote note, simon_state_t *state) {
+    char *ndtemplate = NULL;
+
+    switch (note) {
+        case SIMON_LED_NOTE:
+            ndtemplate = "LI%2d      ";
+            break;
+        case SIMON_ALARM_NOTE:
+            ndtemplate = "  %2d    AL";
+            break;
+        case SIMON_MODE_NOTE:
+            ndtemplate = "  %2dDE    ";
+            break;
+        case SIMON_WRONG_NOTE:
+            ndtemplate = "OH  NOOOOO";
+    }
+
+    sprintf(_simon_display_buf, ndtemplate, state->sequence_length);
+    watch_display_string(_simon_display_buf, 0);
+}
+
+static void _simon_play_note(SimonNote note, simon_state_t *state, bool skip_rest) {
+    _simon_display_note(note, state);
+    switch (note) {
+        case SIMON_LED_NOTE:
+            if (!state->lightOff) watch_set_led_yellow();
+            if (state->soundOff)
+                delay_ms(_delay_beep);
+            else
+                watch_buzzer_play_note(BUZZER_NOTE_D3, _delay_beep);
+            break;
+        case SIMON_MODE_NOTE:
+            if (!state->lightOff) watch_set_led_red();
+            if (state->soundOff)
+                delay_ms(_delay_beep);
+            else
+               watch_buzzer_play_note(BUZZER_NOTE_E4, _delay_beep);
+            break;
+        case SIMON_ALARM_NOTE:
+            if (!state->lightOff) watch_set_led_green();
+            if (state->soundOff)
+                delay_ms(_delay_beep);
+            else
+               watch_buzzer_play_note(BUZZER_NOTE_C3, _delay_beep);
+            break;
+        case SIMON_WRONG_NOTE:
+            if (state->soundOff)
+                delay_ms(800);
+            else
+               watch_buzzer_play_note(BUZZER_NOTE_A1, 800);
+            break;
+    }
+    watch_set_led_off();
+
+    if (note != SIMON_WRONG_NOTE) {
+        _simon_clear_display(state);
+        if (!skip_rest) {
+            watch_buzzer_play_note(BUZZER_NOTE_REST, (_delay_beep * 2)/3);
+        }
+    }
+}
+
+
+static void _simon_setup_next_note(simon_state_t *state) {
+    if (state->sequence_length > state->best_score) {
+        state->best_score = state->sequence_length;
+    }
+
+    _simon_clear_display(state);
+    state->playing_state = SIMON_TEACHING;
+    state->sequence[state->sequence_length] = _simon_get_rand_num(3) + 1;
+    state->sequence_length = state->sequence_length + 1;
+    state->teaching_index = 0;
+    state->listen_index = 0;
+}
+
+static void _simon_listen(SimonNote note, simon_state_t *state) {
+    if (state->sequence[state->listen_index] == note) {
+        _simon_play_note(note, state, true);
+        state->listen_index++;
+        _timer = 0;
+
+        if (state->listen_index == state->sequence_length) {
+            state->playing_state = SIMON_READY_FOR_NEXT_NOTE;
+        }
+    } else {
+        _simon_play_note(SIMON_WRONG_NOTE, state, true);
+        _simon_reset(state);
+    }
+}
+
+static void _simon_begin_listening(simon_state_t *state) {
+    state->playing_state = SIMON_LISTENING_BACK;
+    state->listen_index = 0;
+}
+
+static void _simon_change_speed(simon_state_t *state){
+      switch (state->mode)
+  {
+  case SIMON_MODE_HARD:
+        _delay_beep = DELAY_FOR_TONE_MS / 2;
+        _secSub = SIMON_FACE_FREQUENCY / 2;
+        _timeout = (TIMER_MAX * SIMON_FACE_FREQUENCY) / 2;
+    break;
+  default:
+        _delay_beep = DELAY_FOR_TONE_MS;
+        _secSub = SIMON_FACE_FREQUENCY;
+        _timeout = TIMER_MAX * SIMON_FACE_FREQUENCY;
+    break;
+  }
+}
+
+void simon_face_setup(uint8_t watch_face_index,
+        void **context_ptr) {
+    (void)watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(simon_state_t));
+        memset(*context_ptr, 0, sizeof(simon_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens
+        // only at boot.
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch
+    // wakes from deep sleep.
+#if __EMSCRIPTEN__
+    // simulator only: seed the randon number generator
+    time_t t;
+    srand((unsigned)time(&t));
+#endif
+}
+
+void simon_face_activate(void *context) {
+  (void) settings;
+  (void) context;
+  simon_state_t *state = (simon_state_t *)context;
+  _simon_change_speed(state);
+  movement_request_tick_frequency(SIMON_FACE_FREQUENCY);
+   _timer = 0;
+}
+
+bool simon_face_loop(movement_event_t event,
+        void *context) {
+    simon_state_t *state = (simon_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Show your initial UI here.
+            _simon_reset(state);
+            break;
+        case EVENT_TICK:
+            if (state->playing_state == SIMON_LISTENING_BACK && state->mode != SIMON_MODE_EASY)
+            {
+                _timer++;
+                if(_timer >= (_timeout)){
+                    _timer = 0;
+                    _simon_play_note(SIMON_WRONG_NOTE, state, true);
+                    _simon_reset(state);
+                }
+            }
+            else if (state->playing_state == SIMON_TEACHING && event.subsecond  == 0) {
+                SimonNote note = state->sequence[state->teaching_index];
+                // if this is the final note in the sequence, don't play the rest to let
+                // the player jump in faster
+                _simon_play_note(note, state, state->teaching_index == (state->sequence_length - 1));
+                state->teaching_index++;
+
+                if (state->teaching_index == state->sequence_length) {
+                    _simon_begin_listening(state);
+                }
+            }
+            else if (state->playing_state == SIMON_READY_FOR_NEXT_NOTE && (event.subsecond % _secSub)  == 0) {
+                _timer = 0;
+                _simon_setup_next_note(state);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (state->playing_state == SIMON_NOT_PLAYING) {
+                state->lightOff = !state->lightOff;
+                _simon_not_playing_display(state);
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->playing_state == SIMON_NOT_PLAYING) {
+                state->soundOff = !state->soundOff;
+                _simon_not_playing_display(state);
+                if (!state->soundOff)
+                    watch_buzzer_play_note(BUZZER_NOTE_D3, _delay_beep);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (state->playing_state == SIMON_NOT_PLAYING) {
+                state->sequence_length = 0;
+                watch_clear_indicator(WATCH_INDICATOR_BELL);
+                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+                _simon_setup_next_note(state);
+            } else if (state->playing_state == SIMON_LISTENING_BACK) {
+                _simon_listen(SIMON_LED_NOTE, state);
+            }
+            break;
+        case EVENT_MODE_LONG_PRESS:
+            if (state->playing_state == SIMON_NOT_PLAYING) {
+                movement_move_to_face(0);
+            } else {
+                state->playing_state = SIMON_NOT_PLAYING;
+                _simon_reset(state);
+            }
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            if (state->playing_state == SIMON_NOT_PLAYING) {
+                movement_move_to_next_face();
+            } else if (state->playing_state == SIMON_LISTENING_BACK) {
+                _simon_listen(SIMON_MODE_NOTE, state);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if (state->playing_state == SIMON_LISTENING_BACK) {
+                _simon_listen(SIMON_ALARM_NOTE, state);
+            }
+            else if (state->playing_state == SIMON_NOT_PLAYING){
+                state->mode = (state->mode + 1) % SIMON_MODE_TOTAL;
+                _simon_change_speed(state);
+                _simon_not_playing_display(state);
+            }
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void simon_face_resign(void *context) {
+    (void)context;
+    watch_set_led_off();
+    watch_set_buzzer_off();
+}
diff --git a/legacy/watch_faces/complication/simon_face.h b/legacy/watch_faces/complication/simon_face.h
new file mode 100644 (file)
index 0000000..63e8389
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 <#author_name#>
+ *
+ * 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 SIMON_FACE_H_
+#define SIMON_FACE_H_
+
+#include "movement.h"
+
+/*
+ * simon_face
+ * -----------
+ * The classic electronic game, Simon, reduced to be played on a Sensor-Watch
+ *
+ * How to play:
+ *
+ * When first arriving at the face, it will show your best score.
+ *
+ * Press the light button to start the game.
+ *
+ * A sequence will be played, starting with length 1. The sequence can be
+ * made up of tones corresponding to any of the three buttons.
+ *
+ * light button: "LI" will display at the top of the screen, the LED will be yellow, and a high D will play
+ * mode button: "DE" will display at the left of the screen, the LED will be red, and a high E will play
+ * alarm button: "AL" will display on the right of the screen, the LED will be green, and a high C will play
+ *
+ * Once the sequence has finished, press the same buttons to recreate the sequence.
+ *
+ * If correct, the sequence will get one tone longer and play again. See how long of a sequence you can get.
+ *
+ * If you recreate the sequence incorrectly, a low note will play with "OH NOOOOO" displayed and the game is over.
+ * Press light to play again.
+ *
+ * Once playing, long press the mode button when it is your turn to exit the game early.
+ */
+
+#define MAX_SEQUENCE 99
+
+typedef enum SimonNote {
+    SIMON_LED_NOTE = 1,
+    SIMON_MODE_NOTE,
+    SIMON_ALARM_NOTE,
+    SIMON_WRONG_NOTE
+} SimonNote;
+
+typedef enum SimonPlayingState {
+    SIMON_NOT_PLAYING = 0,
+    SIMON_TEACHING,
+    SIMON_LISTENING_BACK,
+    SIMON_READY_FOR_NEXT_NOTE
+} SimonPlayingState;
+
+typedef enum SimonMode {
+    SIMON_MODE_NORMAL = 0,  // 5 Second timeout if nothing is input
+    SIMON_MODE_EASY,  // There is no timeout in this mode
+    SIMON_MODE_HARD,  // The speed of the teaching is doubled and th etimeout is halved
+    SIMON_MODE_TOTAL
+} SimonMode;
+
+typedef struct {
+    uint8_t best_score;
+    SimonNote sequence[MAX_SEQUENCE];
+    uint8_t sequence_length;
+    uint8_t teaching_index;
+    uint8_t listen_index;
+    bool soundOff;
+    bool lightOff;
+    uint8_t mode:6;
+    SimonPlayingState playing_state;
+} simon_state_t;
+
+void simon_face_setup(uint8_t watch_face_index, void **context_ptr);
+void simon_face_activate(void *context);
+bool simon_face_loop(movement_event_t event, void *context);
+void simon_face_resign(void *context);
+
+#define simon_face                                                             \
+    ((const watch_face_t){                                                       \
+     simon_face_setup,                                                        \
+     simon_face_activate,                                                     \
+     simon_face_loop,                                                         \
+     simon_face_resign,                                                       \
+     NULL,                                                                    \
+     })
+
+#define TIMER_MAX 5
+#define SIMON_FACE_FREQUENCY 8
+#define DELAY_FOR_TONE_MS 300
+
+#endif // SIMON_FACE_H_
diff --git a/legacy/watch_faces/complication/simple_calculator_face.c b/legacy/watch_faces/complication/simple_calculator_face.c
new file mode 100644 (file)
index 0000000..7c849cd
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Patrick McGuire
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "simple_calculator_face.h"
+
+void simple_calculator_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(simple_calculator_state_t));
+        memset(*context_ptr, 0, sizeof(simple_calculator_state_t));
+    }
+}
+
+static void reset_to_zero(calculator_number_t *number) {
+    number->negative = false;
+    number->hundredths = 0;
+    number->tenths = 0;
+    number->ones = 0;
+    number->tens = 0;
+    number->hundreds = 0;
+    number->thousands = 0;
+}
+
+void simple_calculator_face_activate(void *context) {
+    simple_calculator_state_t *state = (simple_calculator_state_t *)context;
+    state->placeholder = PLACEHOLDER_ONES;
+    state->mode = MODE_ENTERING_FIRST_NUM;
+    reset_to_zero(&state->second_num);
+    reset_to_zero(&state->result);
+    movement_request_tick_frequency(4);
+}
+
+static void increment_placeholder(calculator_number_t *number, calculator_placeholder_t placeholder) {
+    uint8_t *digits[] = {
+        &number->hundredths,
+        &number->tenths,
+        &number->ones,
+        &number->tens,
+        &number->hundreds,
+        &number->thousands
+    };
+    *digits[placeholder] = (*digits[placeholder] + 1) % 10;
+}
+
+static float convert_to_float(calculator_number_t number) {
+    float result = 0.0;
+
+    // Add the whole number portion
+    result += number.thousands * 1000.0f;
+    result += number.hundreds * 100.0f;
+    result += number.tens * 10.0f;
+    result += number.ones * 1.0f;
+
+    // Add the fractional portion
+    result += number.tenths * 0.1f;
+    result += number.hundredths * 0.01f;
+
+    // Round to nearest hundredth
+    result = roundf(result * 100) / 100;
+    
+    // Handle negative numbers
+    if (number.negative) result = -result;
+    //printf("convert_to_float results = %f\n", result); // For debugging
+
+    return result;
+}
+
+static char* update_display_number(calculator_number_t *number, char *display_string, uint8_t which_num) {
+    char sign = ' ';
+    if (number->negative) sign = '-';
+
+    sprintf(display_string, "CA%d%c%d%d%d%d%d%d",
+            which_num,
+            sign,
+            number->thousands,
+            number->hundreds,
+            number->tens,
+            number->ones,
+            number->tenths,
+            number->hundredths);
+
+    return display_string;
+}
+
+static void set_operation(simple_calculator_state_t *state) {
+    switch (state->operation) {
+        case OP_ADD:
+            watch_display_string("       Add", 0);
+            break;
+        case OP_SUB:
+            watch_display_string("       sub", 0);
+            break;
+        case OP_MULT:
+            watch_display_string("      n&ul", 0);
+            break;
+        case OP_DIV:
+            watch_display_string("       div", 0);
+            break;
+        case OP_ROOT:
+            watch_display_string("      root", 0);
+            break;
+        case OP_POWER:
+            watch_display_string("       pow", 0);
+            break;
+    }
+}
+
+static void cycle_operation(simple_calculator_state_t *state) {
+    state->operation = (state->operation + 1) % OPERATIONS_COUNT; // Assuming there are 6 operations
+}
+
+
+static calculator_number_t convert_to_string(float number) {
+    calculator_number_t result;
+
+    // Handle negative numbers
+    if (number < 0) {
+        number = -number;
+        result.negative = true;
+    } else result.negative = false;
+
+    // Get each digit from each placeholder
+    int int_part = (int)number;
+
+    float decimal_part_float = ((number - int_part) * 100); // two decimal places
+    //printf("decimal_part_float = %f\n", decimal_part_float); //For debugging
+
+    int decimal_part = round(decimal_part_float);
+    //printf("decimal_part = %d\n", decimal_part); //For debugging
+
+    result.thousands = int_part / 1000 % 10;
+    result.hundreds = int_part / 100 % 10;
+    result.tens = int_part / 10 % 10;
+    result.ones = int_part % 10;
+
+    result.tenths = decimal_part / 10 % 10;
+    result.hundredths = decimal_part % 10;
+
+    return result;
+}
+
+// This is the main function for setting the first_num and second_num
+// WISH: there must be a way to pass less to this function?
+static void set_number(calculator_number_t *number, calculator_placeholder_t placeholder, char *display_string, char *temp_display_string, movement_event_t event, uint8_t which_num) {
+
+    // Create the display index
+    uint8_t display_index;
+
+    // Update display string with current number and copy into temp string
+    update_display_number(number, display_string, which_num);
+    strcpy(temp_display_string, display_string);
+    
+    // Determine the display index based on the placeholder
+    display_index = 9 - placeholder;
+    
+    // Blink selected placeholder
+    // Check if `event.subsecond` is even
+    if (event.subsecond % 2 == 0) {
+        // Replace the character at the index corresponding to the current placeholder with a space
+        temp_display_string[display_index] = ' ';
+    }
+    
+    // Display the (possibly modified) string
+    watch_display_string(temp_display_string, 0);
+} 
+
+static void view_results(simple_calculator_state_t *state, char *display_string) {
+
+    // Initialize float variables to do the math
+    float first_num_float, second_num_float, result_float = 0.0f;
+
+    // Convert the passed numbers to floats
+    first_num_float = convert_to_float(state->first_num);
+    second_num_float = convert_to_float(state->second_num);
+    
+    // Perform the calculation based on the selected operation
+    switch (state->operation) {
+        case OP_ADD:
+            result_float = first_num_float + second_num_float;
+            break;
+        case OP_SUB:
+            result_float = first_num_float - second_num_float;
+            break;
+        case OP_MULT:
+            result_float = first_num_float * second_num_float;
+            break;
+        case OP_DIV:
+            if (second_num_float != 0) {
+                result_float = first_num_float / second_num_float;
+            } else {
+                state->mode = MODE_ERROR;
+                return;
+            }
+            break;
+        case OP_ROOT:
+            if (first_num_float >= 0) {
+                result_float = sqrtf(first_num_float);
+            } else {
+                state->mode = MODE_ERROR;
+                return;
+            }
+            break;
+        case OP_POWER:
+            result_float = powf(first_num_float, second_num_float);
+            break;
+        default:
+            result_float = 0.0f;
+            break;
+    }
+
+    // Be sure the result can fit on the watch display, else error
+    if (result_float > 9999.99 || result_float < -9999.99) {
+        state->mode = MODE_ERROR;
+        return;
+    }
+    
+    result_float = roundf(result_float * 100.0f) / 100.0f; // Might not be needed
+
+    //printf("result as float = %f\n", result_float); // For debugging
+    
+    // Convert the float result to a string
+    // This isn't strictly necessary, but allows easily reusing the result as
+    // the next calculation's first_num
+    state->result = convert_to_string(result_float);
+    
+    // Update the display with the result
+    update_display_number(&state->result, display_string, 3);
+
+    //printf("display_string = %s\n", display_string); // For debugging
+
+    watch_display_string(display_string, 0);
+}
+
+// Used both when returning from errors and when long pressing MODE
+static void reset_all(simple_calculator_state_t *state) {
+    reset_to_zero(&state->first_num);
+    reset_to_zero(&state->second_num);
+    state->mode = MODE_ENTERING_FIRST_NUM;
+    state->operation = OP_ADD;
+    state->placeholder = PLACEHOLDER_ONES;
+}
+
+bool simple_calculator_face_loop(movement_event_t event, void *context) {
+    simple_calculator_state_t *state = (simple_calculator_state_t *)context;
+    char display_string[10];
+    char temp_display_string[10];  // Temporary buffer for blinking effect
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK: 
+            switch (state->mode) {
+                case MODE_ENTERING_FIRST_NUM:
+                    // See the WISH for this function above
+                    set_number(&state->first_num, 
+                            state->placeholder,
+                            display_string, 
+                            temp_display_string, 
+                            event,
+                            1);
+                    break;                
+
+                case MODE_CHOOSING:
+                    set_operation(state);
+                    break;
+
+                case MODE_ENTERING_SECOND_NUM:
+                    // If doing a square root calculation, skip to results
+                    if (state->operation == OP_ROOT) {
+                        state->mode = MODE_VIEW_RESULTS;
+                    } else {
+                        // See the WISH for this function above
+                        set_number(&state->second_num, 
+                            state->placeholder,
+                            display_string, 
+                            temp_display_string, 
+                            event,
+                            2);
+                    }
+                    break;
+
+                case MODE_VIEW_RESULTS:
+                    view_results(state, display_string);
+                    break;
+
+                case MODE_ERROR:
+                    watch_display_string("CA  Error ", 0);
+                    break;
+            }
+            break;
+
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+
+        case EVENT_LIGHT_BUTTON_UP:
+            switch (state->mode) {
+                case MODE_ENTERING_FIRST_NUM:
+                case MODE_ENTERING_SECOND_NUM:
+                    // Move to the next placeholder when the light button is pressed
+                    state->placeholder = (state->placeholder + 1) % MAX_PLACEHOLDERS; // Loop back to the start after PLACEHOLDER_THOUSANDS
+                    break;
+                case MODE_CHOOSING:
+                    cycle_operation(state);
+                    break;
+                case MODE_ERROR:
+                    reset_all(state);
+                    break;
+                case MODE_VIEW_RESULTS:
+                    break;
+            }
+            break;
+
+        case EVENT_LIGHT_LONG_PRESS:
+            switch (state->mode) {
+                case MODE_ENTERING_FIRST_NUM:
+                    // toggle negative on state->first_num
+                    state->first_num.negative = !state->first_num.negative;
+                    break;
+                case MODE_ENTERING_SECOND_NUM:
+                    // toggle negative on state->second_num
+                    state->second_num.negative = !state->second_num.negative;
+                    break;
+                case MODE_ERROR:
+                    reset_all(state);
+                    break;
+                case MODE_CHOOSING:
+                case MODE_VIEW_RESULTS:
+                    break;
+            }
+            break;
+
+        case EVENT_ALARM_BUTTON_UP:
+            switch (state->mode) {
+                case MODE_ENTERING_FIRST_NUM:
+                    // Increment the digit in the current placeholder
+                    increment_placeholder(&state->first_num, state->placeholder);
+                    update_display_number(&state->first_num, display_string, 1);
+
+                    //printf("display_string = %s\n", display_string); // For debugging
+
+                    break;
+                case MODE_CHOOSING:
+                    // Confirm and select the current operation
+                    state->mode = MODE_ENTERING_SECOND_NUM;                
+                    break;
+                case MODE_ENTERING_SECOND_NUM:
+                    // Increment the digit in the current placeholder
+                    increment_placeholder(&state->second_num, state->placeholder);
+                    update_display_number(&state->second_num, display_string, 2);
+
+                    //printf("display_string = %s\n", display_string); // For debugging
+
+                    break;
+                case MODE_ERROR:
+                    reset_all(state);
+                    break;
+                case MODE_VIEW_RESULTS:
+                    break;
+            }
+            break;
+
+        case EVENT_ALARM_LONG_PRESS:
+            switch (state->mode) {
+                case MODE_ENTERING_FIRST_NUM:
+                    reset_to_zero(&state->first_num);
+                    break;
+                case MODE_ENTERING_SECOND_NUM:
+                    reset_to_zero(&state->second_num);
+                    break;
+                case MODE_ERROR:
+                    reset_all(state);
+                    break;
+                case MODE_CHOOSING:
+                case MODE_VIEW_RESULTS:
+                    break;
+            }
+            break;
+
+        case EVENT_MODE_BUTTON_DOWN:
+            break;
+
+        case EVENT_MODE_BUTTON_UP:
+            if (state->mode == MODE_ERROR) {
+                reset_all(state);
+            } else if (state->mode == MODE_ENTERING_FIRST_NUM &&
+                    state->first_num.hundredths == 0 &&
+                    state->first_num.tenths == 0 &&
+                    state->first_num.ones== 0 &&
+                    state->first_num.tens == 0 &&
+                    state->first_num.hundreds == 0 &&
+                    state->first_num.thousands == 0) { 
+                movement_move_to_next_face();
+            } else {
+                // Reset the placeholder and proceed to the next MODE
+                state->placeholder = PLACEHOLDER_ONES;
+                state->mode = (state->mode + 1) % 4;
+                // When looping back to MODE_ENTERING_FIRST_NUM, reuse the
+                // previous calculation's results as the next calculation's
+                // first_num; also reset other numbers
+                if (state->mode == MODE_ENTERING_FIRST_NUM) {
+                    state->first_num = state->result;
+                    reset_to_zero(&state->second_num);
+                    reset_to_zero(&state->result);
+                }
+            }
+            break;
+
+        case EVENT_MODE_LONG_PRESS:
+            // Move to next face if first number is 0
+            if (state->first_num.hundredths == 0 &&
+                    state->first_num.tenths == 0 &&
+                    state->first_num.ones== 0 &&
+                    state->first_num.tens == 0 &&
+                    state->first_num.hundreds == 0 &&
+                    state->first_num.thousands == 0) { 
+                movement_move_to_face(0);
+            // otherwise, start over
+            } else {
+                reset_all(state);
+            }
+            break;
+
+        case EVENT_TIMEOUT:
+            movement_request_tick_frequency(1);
+            movement_move_to_face(0);
+            break;
+
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void simple_calculator_face_resign(void *context) {
+    (void) context;
+    movement_request_tick_frequency(1);
+}
+
diff --git a/legacy/watch_faces/complication/simple_calculator_face.h b/legacy/watch_faces/complication/simple_calculator_face.h
new file mode 100644 (file)
index 0000000..8a8ea02
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Patrick McGuire
+ *
+ * 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 SIMPLE_CALCULATOR_FACE_H_
+#define SIMPLE_CALCULATOR_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Simple Calculator
+ *
+ * How to use:
+ *
+ * Flow:
+ * Enter first number -> Select operator -> Enter second number -> View Results
+ * 
+ * How to read the display:
+ * - "CA" is displayed at the top to tell you that you're in the CAlculator
+ * - The top-right digit (1, 2, or 3) lets you know whether you're entering the
+ *   first number (1), entering the second number (2), or viewing the results (3).
+ * - To the right of the top-right digit will show the number's sign. If the
+ *   number is negative, a "-" will be displayed, otherwise it is empty.
+ * - The 4 large digits to the left are whole numbers and the 2 smaller digits
+ *   on the right are the tenths and hundredths decimal places.
+ *
+ * Entering the first number:
+ * - Press ALARM to increment the selected (blinking) digit
+ * - Press LIGHT to move to the next placeholder
+ * - LONG PRESS the LIGHT button to toggle the number's sign to make it 
+ *   negative
+ * - LONG PRESS the ALARM button to reset the number to 0
+ * - Press MODE to proceed to selecting the operator
+ *
+ * Selecting the operator:
+ * - Press the LIGHT button to cycle through available operators. They are:
+ *   + Add
+ *   - Subtract
+ *   * Multiply
+ *   / Divide
+ *   sqrtf() Square root
+ *   powf() Power (exponent calculation)
+ * - Press MODE or ALARM to proceed to entering the second number
+ *
+ * Entering the second number:
+ * - Everything is the same as setting the first number except that pressing
+ *   MODE here will proceed to viewing the results
+ *
+ * Viewing the results:
+ * - Pressing MODE will start a new calculation with the result as the first
+ *   number. (LONG PRESS ALARM to reset the value to 0)
+ *
+ * Errors:
+ * - An error will be triggered if the result is not able to be displayed, that
+ * is, if the value is greater than 9,999.99 or less than -9,999.99.
+ * - An error will also be triggered if an impossible operation is selected,
+ *   for instance trying to divide by 0 or get the square root of a negative
+ *   number.
+ * - Exit error mode and start over with any button press.
+ *
+ */
+
+#define OPERATIONS_COUNT 6
+#define MAX_PLACEHOLDERS 6
+
+typedef struct {
+    bool negative;
+    uint8_t hundredths;
+    uint8_t tenths;
+    uint8_t ones;
+    uint8_t tens;
+    uint8_t hundreds;
+    uint8_t thousands;
+} calculator_number_t;
+
+typedef enum {
+    PLACEHOLDER_HUNDREDTHS,
+    PLACEHOLDER_TENTHS,
+    PLACEHOLDER_ONES,
+    PLACEHOLDER_TENS,
+    PLACEHOLDER_HUNDREDS,
+    PLACEHOLDER_THOUSANDS
+} calculator_placeholder_t;
+
+typedef enum {
+    OP_ADD,
+    OP_SUB,
+    OP_MULT,
+    OP_DIV,
+    OP_ROOT,
+    OP_POWER,
+} calculator_operation_t;
+
+typedef enum {
+    MODE_ENTERING_FIRST_NUM,
+    MODE_CHOOSING,
+    MODE_ENTERING_SECOND_NUM,
+    MODE_VIEW_RESULTS,
+    MODE_ERROR
+} calculator_mode_t;
+
+typedef struct {
+    calculator_number_t first_num;
+    calculator_number_t second_num;
+    calculator_number_t result;
+    calculator_operation_t operation;
+    calculator_mode_t mode;
+    calculator_placeholder_t placeholder;
+} simple_calculator_state_t;
+
+void simple_calculator_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void simple_calculator_face_activate(void *context);
+bool simple_calculator_face_loop(movement_event_t event, void *context);
+void simple_calculator_face_resign(void *context);
+
+#define simple_calculator_face ((const watch_face_t){ \
+    simple_calculator_face_setup, \
+    simple_calculator_face_activate, \
+    simple_calculator_face_loop, \
+    simple_calculator_face_resign, \
+    NULL, \
+})
+
+#endif // SIMPLE_CALCULATOR_FACE_H_
+
diff --git a/legacy/watch_faces/complication/simple_coin_flip_face.c b/legacy/watch_faces/complication/simple_coin_flip_face.c
new file mode 100644 (file)
index 0000000..7e0003f
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Wesley Aptekar-Cassels
+ *
+ * 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.
+ */
+
+#if __EMSCRIPTEN__
+#include <time.h>
+#else
+#include "saml22j18a.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "simple_coin_flip_face.h"
+
+#define SIMPLE_COIN_FLIP_REQUIRE_LONG_PRESS_FOR_REFLIP true
+
+void simple_coin_flip_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(simple_coin_flip_state_t));
+        memset(*context_ptr, 0, sizeof(simple_coin_flip_state_t));
+    }
+}
+
+void simple_coin_flip_face_activate(void *context) {
+    (void) context;
+}
+
+static uint32_t get_random(uint32_t max) {
+    #if __EMSCRIPTEN__
+    return rand() % max;
+    #else
+    return arc4random_uniform(max);
+    #endif
+}
+
+static void animation_0() {
+    watch_display_string("  ", 8);
+    watch_set_pixel(0, 3);
+    watch_set_pixel(0, 6);
+}
+
+static void animation_1() {
+    watch_display_string("  ", 8);
+    watch_set_pixel(1, 3);
+    watch_set_pixel(1, 5);
+}
+
+static void animation_2() {
+    watch_display_string("  ", 8);
+    watch_set_pixel(2, 2);
+    watch_set_pixel(2, 4);
+}
+
+bool simple_coin_flip_face_loop(movement_event_t event, void *context) {
+    simple_coin_flip_state_t *state = (simple_coin_flip_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_display_string("flip", 5);
+            state->animation_frame = 0;
+            break;
+        case EVENT_TICK:
+            switch (state->animation_frame) {
+                case 0:
+                case 7:
+                    return true;
+                case 1:
+                    movement_request_tick_frequency(8);
+                    watch_display_string("      ", 4);
+                    // fallthrough
+                case 5:
+                    animation_0();
+                    break;
+                case 2:
+                case 4:
+                    animation_1();
+                    break;
+                case 3:
+                    animation_2();
+                    break;
+                case 6:
+                    movement_request_tick_frequency(1);
+                    if (get_random(2)) {
+                        watch_display_string("Heads ", 4);
+                    } else {
+                        watch_display_string(" Tails", 4);
+                    }
+                    break;
+            }
+            state->animation_frame++;
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+        case EVENT_ALARM_BUTTON_UP:
+            if (!SIMPLE_COIN_FLIP_REQUIRE_LONG_PRESS_FOR_REFLIP || state->animation_frame == 0) {
+                state->animation_frame = 1;
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+        case EVENT_LIGHT_LONG_PRESS:
+            state->animation_frame = 1;
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void simple_coin_flip_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/complication/simple_coin_flip_face.h b/legacy/watch_faces/complication/simple_coin_flip_face.h
new file mode 100644 (file)
index 0000000..710acca
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Wesley Aptekar-Cassels
+ *
+ * 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 SIMPLE_COIN_FLIP_FACE_H_
+#define SIMPLE_COIN_FLIP_FACE_H_
+
+#include "movement.h"
+
+/*
+ * A extremely simple coin flip face.
+ *
+ * Press ALARM or LIGHT to flip a coin, after a short animation it will display
+ * "Heads" or "Tails". Long-press to flip again (you can change a #define to
+ * allow a short-press to reflip as well, if you'd like).
+ *
+ * This is for people who want a simpler UI than probability_face or
+ * randonaut_face. While those have more features, this one is more immediately
+ * obvious - useful, for instance, if you are using a coin flip to agree on
+ * something with someone, and want the operation to be clear to someone who
+ * has not had anything explained to them.
+ */
+
+typedef struct {
+    uint8_t animation_frame;
+} simple_coin_flip_state_t;
+
+void simple_coin_flip_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void simple_coin_flip_face_activate(void *context);
+bool simple_coin_flip_face_loop(movement_event_t event, void *context);
+void simple_coin_flip_face_resign(void *context);
+
+#define simple_coin_flip_face ((const watch_face_t){ \
+    simple_coin_flip_face_setup, \
+    simple_coin_flip_face_activate, \
+    simple_coin_flip_face_loop, \
+    simple_coin_flip_face_resign, \
+    NULL, \
+})
+
+#endif // SIMPLE_COIN_FLIP_FACE_H_
+
diff --git a/legacy/watch_faces/complication/smallchess_face.c b/legacy/watch_faces/complication/smallchess_face.c
new file mode 100644 (file)
index 0000000..6583872
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jeremy O'Brien
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "smallchesslib.h"
+
+#include "smallchess_face.h"
+#include "watch.h"
+
+#define PIECE_LIST_END_MARKER 0xff
+
+int8_t cpu_done_beep[] = {BUZZER_NOTE_C5, 5, BUZZER_NOTE_C6, 5, BUZZER_NOTE_C7, 5, 0};
+
+static void smallchess_init_board(smallchess_face_state_t *state) {
+    SCL_gameInit((SCL_Game *)state->game, 0);
+    memset(state->moveable_pieces, 0xff, sizeof(state->moveable_pieces));
+    memset(state->moveable_dests, 0xff, sizeof(state->moveable_dests));
+}
+
+void smallchess_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(smallchess_face_state_t));
+        memset(*context_ptr, 0, sizeof(smallchess_face_state_t));
+
+        /* now alloc/init the game board */
+        smallchess_face_state_t *state = (smallchess_face_state_t *)*context_ptr;
+        state->game = malloc(sizeof(SCL_Game));
+        smallchess_init_board(*context_ptr);
+    }
+}
+
+void smallchess_face_activate(void *context) {
+    (void) context;
+}
+
+static void _smallchess_calc_moveable_pieces(smallchess_face_state_t *state) {
+    int moveable_pieces_idx = 0;
+    SCL_Game *game = (SCL_Game *)state->game;
+    for (int i = 0; i < SCL_BOARD_SQUARES; ++i) {
+        if (game->board[i] != '.' &&
+                SCL_pieceIsWhite(game->board[i]) == SCL_boardWhitesTurn(game->board)) {
+            SCL_SquareSet moveable_pieces = SCL_SQUARE_SET_EMPTY;
+            SCL_boardGetMoves(game->board, i, moveable_pieces);
+            if (SCL_squareSetSize(moveable_pieces) != 0) {
+                state->moveable_pieces[moveable_pieces_idx] = i;
+                moveable_pieces_idx++;
+            }
+        }
+    }
+    state->moveable_pieces[moveable_pieces_idx] = PIECE_LIST_END_MARKER;
+    state->moveable_pieces_idx = 0;
+}
+
+static void _smallchess_make_ai_move(smallchess_face_state_t *state) {
+    char ai_from_str[3] = {0};
+    char ai_to_str[3] = {0};
+    uint8_t rep_from, rep_to;
+    char ai_prom;
+
+    watch_clear_display();
+    watch_start_character_blink('C', 100);
+    SCL_gameGetRepetiotionMove(state->game, &rep_from, &rep_to);
+
+#ifndef __EMSCRIPTEN__
+    hri_oscctrl_write_OSC16MCTRL_FSEL_bf(OSCCTRL, OSCCTRL_OSC16MCTRL_FSEL_16_Val);
+#endif
+    SCL_getAIMove(state->game, 3, 0, 0, SCL_boardEvaluateStatic, NULL, 0, rep_from, rep_to, &state->ai_from_square, &state->ai_to_square, &ai_prom);
+#ifndef __EMSCRIPTEN__
+    hri_oscctrl_write_OSC16MCTRL_FSEL_bf(OSCCTRL, OSCCTRL_OSC16MCTRL_FSEL_4_Val);
+#endif
+
+    SCL_gameMakeMove(state->game, state->ai_from_square, state->ai_to_square, ai_prom);
+    watch_stop_blink();
+
+    watch_buzzer_play_sequence(cpu_done_beep, NULL);
+
+    /* cache the move as a string for SHOW_CPU_MOVE state */
+    SCL_squareToString(state->ai_from_square, ai_from_str);
+    SCL_squareToString(state->ai_to_square, ai_to_str);
+    snprintf(state->last_move_str, sizeof(state->last_move_str), " %s-%s", ai_from_str, ai_to_str);
+
+    /* now cache the list of legal pieces we can move */
+    _smallchess_calc_moveable_pieces(state);
+}
+
+static char _smallchess_make_lowercase(char c) {
+    if (c < 0x61)
+        return c + 0x20;
+
+    return c;
+}
+
+static void _smallchess_get_endgame_string(smallchess_face_state_t *state, char *buf, uint8_t len) {
+    uint8_t endgame_state = ((SCL_Game *)state->game)->state;
+    uint16_t ply = ((SCL_Game *)state->game)->ply;
+
+    switch (endgame_state) {
+        case SCL_GAME_STATE_WHITE_WIN:
+            snprintf(buf, len, "Wh%2dm&ate ", ply);
+            break;
+        case SCL_GAME_STATE_BLACK_WIN:
+            snprintf(buf, len, "bL%2dm&ate ", ply);
+            break;
+        case SCL_GAME_STATE_DRAW:
+        case SCL_GAME_STATE_DRAW_STALEMATE:
+        case SCL_GAME_STATE_DRAW_REPETITION:
+        case SCL_GAME_STATE_DRAW_50:
+        case SCL_GAME_STATE_DRAW_DEAD:
+            snprintf(buf, len, "  %2d Drauu", ply);
+            break;
+        default:
+            snprintf(buf, len, "  %2d Error", ply);
+            break;
+    }
+}
+
+static void _smallchess_face_update_lcd(smallchess_face_state_t *state) {
+    uint8_t start_square;
+    uint8_t end_square;
+    char start_coord[3] = {0};
+    char end_coord[3] = {0};
+    char buf[14] = {0};
+
+    uint16_t ply = ((SCL_Game *)state->game)->ply;
+
+    switch (state->state) {
+        case SMALLCHESS_MENU_RESUME:
+            snprintf(buf, sizeof(buf), "SC%2dResume", ply);
+            break;
+        case SMALLCHESS_MENU_UNDO:
+            snprintf(buf, sizeof(buf), "SC%2d Undo ", ply);
+            break;
+        case SMALLCHESS_MENU_SHOW_LAST_MOVE:
+            snprintf(buf, sizeof(buf), "SC%2dShLast", ply);
+            break;
+        case SMALLCHESS_MENU_NEW_WHITE:
+            snprintf(buf, sizeof(buf), "Wh%2dStart ", ply);
+            break;
+        case SMALLCHESS_MENU_NEW_BLACK:
+            snprintf(buf, sizeof(buf), "bL%2dStart ", ply);
+            break;
+        case SMALLCHESS_SHOW_CPU_MOVE:
+        case SMALLCHESS_SHOW_LAST_MOVE:
+            snprintf(buf,
+                    sizeof(buf),
+                    "%c %2d%s",
+                    _smallchess_make_lowercase(((SCL_Game *)state->game)->board[state->ai_to_square]),
+                    ply,
+                    state->last_move_str);
+
+            break;
+        case SMALLCHESS_SELECT_PIECE:
+            if (((SCL_Game *)state->game)->state != SCL_GAME_STATE_PLAYING) {
+                _smallchess_get_endgame_string(state, buf, sizeof(buf));
+                break;
+            }
+            start_square = state->moveable_pieces[state->moveable_pieces_idx];
+            SCL_squareToString(start_square, start_coord);
+            snprintf(buf,
+                    sizeof(buf),
+                    "%c %2d %s-  ",
+                    _smallchess_make_lowercase(((SCL_Game *)state->game)->board[start_square]),
+                    ply + 1,
+                    start_coord);
+            break;
+        case SMALLCHESS_SELECT_DEST:
+            start_square = state->moveable_pieces[state->moveable_pieces_idx];
+            SCL_squareToString(start_square, start_coord);
+            end_square = state->moveable_dests[state->moveable_dests_idx];
+            SCL_squareToString(end_square, end_coord);
+            snprintf(buf,
+                    sizeof(buf),
+                    "%c %2d %s-%s",
+                    _smallchess_make_lowercase(((SCL_Game *)state->game)->board[start_square]),
+                    ply + 1,
+                    start_coord,
+                    end_coord);
+            break;
+        default:
+            break;
+    }
+
+    watch_display_string(buf, 0);
+}
+
+static void _smallchess_select_main_menu_subitem(smallchess_face_state_t *state) {
+    char from_str[3] = {0};
+    char to_str[3] = {0};
+    char prom;
+
+    switch (state->state) {
+        case SMALLCHESS_MENU_RESUME:
+            state->state = SMALLCHESS_SELECT_PIECE;
+            break;
+        case SMALLCHESS_MENU_UNDO:
+            /* undo twice to undo the CPU's move and our move */
+            SCL_gameUndoMove((SCL_Game *)state->game);
+            SCL_gameUndoMove((SCL_Game *)state->game);
+            /* and re-calculate the moveable pieces for this new state */
+            _smallchess_calc_moveable_pieces(state);
+            break;
+        case SMALLCHESS_MENU_NEW_WHITE:
+            SCL_gameInit((SCL_Game *)state->game, 0);
+            _smallchess_calc_moveable_pieces(state);
+            state->state = SMALLCHESS_SELECT_PIECE;
+            break;
+        case SMALLCHESS_MENU_NEW_BLACK:
+            SCL_gameInit((SCL_Game *)state->game, 0);
+            /* force a move since black is playing */
+            _smallchess_make_ai_move(state);
+            state->state = SMALLCHESS_SHOW_CPU_MOVE;
+            break;
+        case SMALLCHESS_MENU_SHOW_LAST_MOVE:
+            /* fetch the move */
+            SCL_recordGetMove(((SCL_Game *)state->game)->record, ((SCL_Game *)state->game)->ply - 1, &state->ai_from_square, &state->ai_to_square, &prom);
+            SCL_squareToString(state->ai_from_square, from_str);
+            SCL_squareToString(state->ai_to_square, to_str);
+            snprintf(state->last_move_str, sizeof(state->last_move_str), " %s-%s", from_str, to_str);
+            state->state = SMALLCHESS_SHOW_LAST_MOVE;
+            break;
+        default:
+            break;
+    }
+}
+
+static void _smallchess_handle_select_piece_button_event(smallchess_face_state_t *state, movement_event_t event) {
+    SCL_SquareSet moveable_dests = SCL_SQUARE_SET_EMPTY;
+
+    /* back to main menu on any event when game ends */
+    if (((SCL_Game *)state->game)->state != SCL_GAME_STATE_PLAYING) {
+        state->state = SMALLCHESS_MENU_RESUME;
+        return;
+    }
+
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_UP:
+            // check for no moves possible state (shouldn't happen but this will prevent weirdness)
+            if (state->moveable_pieces[0] == PIECE_LIST_END_MARKER) {
+                return;
+            }
+
+            state->moveable_pieces_idx += 1;
+            if (state->moveable_pieces_idx >= NUM_ELEMENTS(state->moveable_pieces)) {
+                state->moveable_pieces_idx = 0;
+            }
+
+            if (state->moveable_pieces[state->moveable_pieces_idx] == PIECE_LIST_END_MARKER) {
+                state->moveable_pieces_idx = 0;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            // check for no moves possible state (shouldn't happen but this will prevent weirdness)
+            if (state->moveable_pieces[0] == PIECE_LIST_END_MARKER) {
+                return;
+            }
+
+            /* handle wrap around */
+            if (state->moveable_pieces_idx == 0) {
+                for (unsigned int i = 0; i < NUM_ELEMENTS(state->moveable_pieces); i++) {
+                    if (state->moveable_pieces[i] == 0xff) {
+                        state->moveable_pieces_idx = i - 1;
+                        break;
+                    }
+                }
+            } else {
+                state->moveable_pieces_idx -= 1;
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (((SCL_Game *)state->game)->ply == 0) {
+                state->state = SMALLCHESS_MENU_NEW_WHITE;
+            } else {
+                state->state = SMALLCHESS_MENU_RESUME;
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            /* pre-calculate the possible moves this piece can make */
+            SCL_boardGetMoves(((SCL_Game *)state->game)->board, state->moveable_pieces[state->moveable_pieces_idx], moveable_dests);
+            state->moveable_dests_idx = 0;
+            SCL_SQUARE_SET_ITERATE_BEGIN(moveable_dests)
+                state->moveable_dests[state->moveable_dests_idx] = iteratedSquare;
+                state->moveable_dests_idx++;
+            SCL_SQUARE_SET_ITERATE_END
+            state->moveable_dests[state->moveable_dests_idx] = PIECE_LIST_END_MARKER;
+            state->moveable_dests_idx = 0;
+            state->state = SMALLCHESS_SELECT_DEST;
+        default:
+            break;
+    }
+}
+
+static void _smallchess_handle_select_dest_button_event(smallchess_face_state_t *state, movement_event_t event) {
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_UP:
+            // check for no moves possible state (shouldn't happen but this will prevent weirdness)
+            if (state->moveable_dests[0] == PIECE_LIST_END_MARKER) {
+                return;
+            }
+            state->moveable_dests_idx += 1;
+            if (state->moveable_dests_idx >= (sizeof(state->moveable_dests) / sizeof(state->moveable_dests[0]))) {
+                state->moveable_dests_idx = 0;
+            }
+
+            if (state->moveable_dests[state->moveable_dests_idx] == PIECE_LIST_END_MARKER) {
+                state->moveable_dests_idx = 0;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            // check for no moves possible state (shouldn't happen but this will prevent weirdness)
+            if (state->moveable_dests[0] == PIECE_LIST_END_MARKER) {
+                return;
+            }
+
+            /* handle wrap around */
+            if (state->moveable_dests_idx == 0) {
+                for (unsigned int i = 0; i < NUM_ELEMENTS(state->moveable_dests); i++) {
+                    if (state->moveable_dests[i] == 0xff) {
+                        state->moveable_dests_idx = i - 1;
+                        break;
+                    }
+                }
+            } else {
+                state->moveable_dests_idx -= 1;
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            state->state = SMALLCHESS_SELECT_PIECE;
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            SCL_gameMakeMove((SCL_Game *)state->game, state->moveable_pieces[state->moveable_pieces_idx], state->moveable_dests[state->moveable_dests_idx], 'q');
+
+            /* if the player didn't win or draw here, calculate a move */
+            if (((SCL_Game *)state->game)->state == SCL_GAME_STATE_PLAYING) {
+                _smallchess_make_ai_move(state);
+                state->state = SMALLCHESS_SHOW_CPU_MOVE;
+            } else {
+                /* player ended the game through mate or draw; jump to select piece screen to show state */
+                state->state = SMALLCHESS_SELECT_PIECE;
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+/* this just waits until any button is hit */
+static void _smallchess_handle_show_cpu_move_button_event(smallchess_face_state_t *state, movement_event_t event) {
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_UP:
+        case EVENT_LIGHT_BUTTON_UP:
+        case EVENT_ALARM_LONG_PRESS:
+        case EVENT_LIGHT_LONG_PRESS:
+            state->state = SMALLCHESS_SELECT_PIECE;
+            break;
+        default:
+            break;
+    }
+}
+
+static void _smallchess_handle_show_last_move_button_event(smallchess_face_state_t *state, movement_event_t event) {
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_UP:
+        case EVENT_LIGHT_BUTTON_UP:
+        case EVENT_ALARM_LONG_PRESS:
+        case EVENT_LIGHT_LONG_PRESS:
+            state->state = SMALLCHESS_MENU_SHOW_LAST_MOVE;
+            break;
+        default:
+            break;
+    }
+}
+
+static void _smallchess_handle_playing_button_event(smallchess_face_state_t *state, movement_event_t event) {
+    if (state->state == SMALLCHESS_SELECT_PIECE) {
+        _smallchess_handle_select_piece_button_event(state, event);
+    } else if (state->state == SMALLCHESS_SELECT_DEST) {
+        _smallchess_handle_select_dest_button_event(state, event);
+    } else if (state->state == SMALLCHESS_SHOW_CPU_MOVE) {
+        _smallchess_handle_show_cpu_move_button_event(state, event);
+    } else if (state->state == SMALLCHESS_SHOW_LAST_MOVE) {
+        _smallchess_handle_show_last_move_button_event(state, event);
+    }
+}
+
+static void _smallchess_handle_main_menu_button_event(smallchess_face_state_t *state, movement_event_t event) {
+    uint16_t ply = ((SCL_Game *)state->game)->ply;
+
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_UP:
+            /* no game started; only offer start white/start black */
+            if (ply == 0) {
+                if (state->state == SMALLCHESS_MENU_NEW_WHITE) {
+                    state->state = SMALLCHESS_MENU_NEW_BLACK;
+                } else {
+                    state->state = SMALLCHESS_MENU_NEW_WHITE;
+                }
+            } else {
+                state->state++;
+                if (state->state >= SMALLCHESS_PLAYING_SPLIT) {
+                    state->state = SMALLCHESS_MENU_RESUME;
+                }
+            }
+
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            /* no game started; only offer start white/start black */
+            if (ply == 0) {
+                if (state->state == SMALLCHESS_MENU_NEW_BLACK) {
+                    state->state = SMALLCHESS_MENU_NEW_WHITE;
+                } else {
+                    state->state = SMALLCHESS_MENU_NEW_BLACK;
+                }
+            } else {
+                if (state->state == SMALLCHESS_MENU_RESUME) {
+                    state->state = SMALLCHESS_PLAYING_SPLIT - 1;
+                } else {
+                    state->state--;
+                }
+            }
+
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            _smallchess_select_main_menu_subitem(state);
+            break;
+        default:
+            break;
+    }
+}
+
+static void _smallchess_handle_button_event(smallchess_face_state_t *state, movement_event_t event) {
+    if (state->state < SMALLCHESS_PLAYING_SPLIT) {
+        /* in main menu */
+        _smallchess_handle_main_menu_button_event(state, event);
+    } else if (state->state > SMALLCHESS_PLAYING_SPLIT) {
+        /* in piece selection */
+        _smallchess_handle_playing_button_event(state, event);
+    }
+}
+
+bool smallchess_face_loop(movement_event_t event, void *context) {
+    smallchess_face_state_t *state = (smallchess_face_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            if (((SCL_Game *)state->game)->ply == 0) {
+                state->state = SMALLCHESS_MENU_NEW_WHITE;
+            } else {
+                state->state = SMALLCHESS_MENU_RESUME;
+            }
+            _smallchess_face_update_lcd(state);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+        case EVENT_LIGHT_LONG_PRESS:
+        case EVENT_ALARM_BUTTON_UP:
+        case EVENT_ALARM_LONG_PRESS:
+            _smallchess_handle_button_event(state, event);
+            _smallchess_face_update_lcd(state);
+            break;
+        case EVENT_TICK:
+            break;
+        case EVENT_TIMEOUT:
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void smallchess_face_resign(void *context) {
+    (void) context;
+    watch_set_led_off();
+}
diff --git a/legacy/watch_faces/complication/smallchess_face.h b/legacy/watch_faces/complication/smallchess_face.h
new file mode 100644 (file)
index 0000000..97d8179
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jeremy O'Brien
+ *
+ * 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 SMALLCHESS_FACE_H_
+#define SMALLCHESS_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Chess watchface
+ *
+ * Implements a (very) simple chess engine.
+ * Uses smallchesslib for the engine: https://codeberg.org/drummyfish/smallchesslib
+ *
+ * When moving a piece, only valid pieces and moves are presented.
+ *
+ * Interaction is done through a simple menu/submenu system:
+ * - Light button: navigate backwards through the current menu
+ * - Alarm button: navigate forwards through the current menu
+ * - Light button (long press): navigate up to the parent menu
+ * - Alarm button (long press): select the current item or submenu
+ */
+
+enum smallchess_state {
+    /* main menu */
+    SMALLCHESS_MENU_RESUME,
+    SMALLCHESS_MENU_SHOW_LAST_MOVE,
+    SMALLCHESS_MENU_UNDO,
+    SMALLCHESS_MENU_NEW_WHITE,
+    SMALLCHESS_MENU_NEW_BLACK,
+
+    SMALLCHESS_PLAYING_SPLIT,
+
+    /* playing game submenu */
+    SMALLCHESS_SHOW_LAST_MOVE,
+    SMALLCHESS_SHOW_CPU_MOVE,
+    SMALLCHESS_SELECT_PIECE,
+    SMALLCHESS_SELECT_DEST,
+};
+
+#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]))
+#define SMALLCHESS_NUM_PIECES 16 // number of pieces each player has
+
+typedef struct {
+    void *game;
+    enum smallchess_state state;
+    uint8_t moveable_pieces[SMALLCHESS_NUM_PIECES + 1];
+    uint8_t moveable_pieces_idx;
+    uint8_t moveable_dests[29]; // this magic number represents the maximum number of moves a piece can make (queen in center of board)
+                                // plus one for the end list marker
+    uint8_t moveable_dests_idx;
+    char last_move_str[7];
+    uint8_t ai_from_square, ai_to_square;
+} smallchess_face_state_t;
+
+void smallchess_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void smallchess_face_activate(void *context);
+bool smallchess_face_loop(movement_event_t event, void *context);
+void smallchess_face_resign(void *context);
+
+#define smallchess_face ((const watch_face_t){ \
+    smallchess_face_setup, \
+    smallchess_face_activate, \
+    smallchess_face_loop, \
+    smallchess_face_resign, \
+    NULL, \
+})
+
+#endif // SMALLCHESS_FACE_H_
diff --git a/legacy/watch_faces/complication/solstice_face.c b/legacy/watch_faces/complication/solstice_face.c
new file mode 100644 (file)
index 0000000..d981c4f
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Wesley Aptekar-Cassels
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "watch_utility.h"
+#include "solstice_face.h"
+
+// Find solstice or equinox time in JDE for a given year, via method from Meeus Ch 27
+static double calculate_solstice_equinox(uint16_t year, uint8_t k) {
+    double Y = ((double)year - 2000) / 1000;
+    double approx_terms[4][5] = {
+        {2451623.80984, 365242.37404, 0.05169, -0.00411, -0.00057}, // March equinox
+        {2451716.56767, 365241.62603, 0.00325, 0.00888, -0.00030}, // June solstice
+        {2451810.21715, 365242.01767, -0.11575, 0.00337, 0.00078}, // September equinox
+        {2451900.05952, 365242.74049, -0.06223, -0.00823, 0.00032}, // December solstice
+    };
+    double JDE0 = approx_terms[k][0] + Y * (approx_terms[k][1] + Y * (approx_terms[k][2] + Y * (approx_terms[k][3] + Y * approx_terms[k][4])));
+    double T = (JDE0 - 2451545.0) / 36525;
+    double W = 35999.373 * T - 2.47;
+    double dlambda = 1 + (0.0334 * cos(W * M_PI / 180.0)) + (0.0007 * cos(2 * W * M_PI / 180.0));
+    double correction_terms[25][3] = {
+        {485,324.96,1934.136},
+        {203,337.23,32964.467},
+        {199,342.08,20.186},
+        {182,27.85,445267.112},
+        {156,73.14,45036.886},
+        {136,171.52,22518.443},
+        {77,222.54,65928.934},
+        {74,296.72,3034.906},
+        {70,243.58,9037.513},
+        {58,119.81,33718.147},
+        {52,297.17,150.678},
+        {50,21.02,2281.226},
+        {45,247.54,29929.562},
+        {44,325.15,31555.956},
+        {29,60.93,4443.417},
+        {18,155.12,67555.328},
+        {17,288.79,4562.452},
+        {16,198.04,62894.029},
+        {14,199.76,31436.921},
+        {12,95.39,14577.848},
+        {12,287.11,31931.756},
+        {12,320.81,34777.259},
+        {9,227.73,1222.114},
+        {8,15.45,16859.074},
+    };
+    double S = 0;
+    for (int i = 0; i < 25; i++) {
+        S += correction_terms[i][0] * cos((correction_terms[i][1] + correction_terms[i][2] * T) * M_PI / 180.0);
+    }
+    double JDE = JDE0 + (0.00001 * S) / dlambda;
+
+    return JDE;
+}
+
+// Convert JDE to Gergorian datetime as per Meeus Ch 7
+static watch_date_time_t jde_to_date_time(double JDE) {
+    double tmp = JDE + 0.5;
+    double Z = floor(tmp);
+    double F = fmod(tmp, 1);
+    double A;
+    if (Z < 2299161) {
+        A = Z;
+    } else {
+        double alpha = floor((Z - 1867216.25) / 36524.25);
+        A = Z + 1 + alpha - floor(alpha / 4);
+    }
+    double B = A + 1524;
+    double C = floor((B - 122.1) / 365.25);
+    double D = floor(365.25 * C);
+    double E = floor((B - D) / 30.6001);
+    double day = B - D - floor(30.6001 * E) + F;
+    double month;
+    if (E < 14) {
+        month = E - 1;
+    } else {
+        month = E - 13;
+    }
+    double year;
+    if (month > 2) {
+        year = C - 4716;
+    } else {
+        year = C - 4715;
+    }
+
+    double hours = fmod(day, 1) * 24;
+    double minutes = fmod(hours, 1) * 60;
+    double seconds = fmod(minutes, 1) * 60;
+
+    watch_date_time_t result = {.unit = {
+        floor(seconds),
+        floor(minutes),
+        floor(hours),
+        floor(day),
+        floor(month),
+        floor(year - 2020)
+    }};
+
+    return result;
+}
+
+static void calculate_datetimes(solstice_state_t *state) {
+    for (int i = 0; i < 4; i++) {
+        // TODO: handle DST changes
+        state->datetimes[i] = jde_to_date_time(calculate_solstice_equinox(2020 + state->year, i) + (movement_get_current_timezone_offset() / (3600.0*24.0)));
+    }
+}
+
+void solstice_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(solstice_state_t));
+        solstice_state_t *state = (solstice_state_t *)*context_ptr;
+
+        watch_date_time_t now = watch_rtc_get_date_time();
+        state->year = now.unit.year;
+        state->index = 0;
+        calculate_datetimes(state, settings);
+
+        uint32_t now_unix = watch_utility_date_time_to_unix_time(now, 0);
+        for (int i = 0; i < 4; i++) {
+            if (state->index == 0 && watch_utility_date_time_to_unix_time(state->datetimes[i], 0) > now_unix) {
+                state->index = i;
+            }
+        }
+    }
+}
+
+void solstice_face_activate(void *context) {
+    (void) context;
+}
+
+static void show_main_screen(solstice_state_t *state) {
+    char buf[11];
+    watch_date_time_t date_time = state->datetimes[state->index];
+    sprintf(buf, "  %2d  %2d%02d", date_time.unit.year + 20, date_time.unit.month, date_time.unit.day);
+    watch_display_string(buf, 0);
+}
+
+static void show_date_time(solstice_state_t *state) {
+    char buf[11];
+    watch_date_time_t date_time = state->datetimes[state->index];
+    if (!movement_clock_mode_24h()) {
+        if (date_time.unit.hour < 12) {
+            watch_clear_indicator(WATCH_INDICATOR_PM);
+        } else {
+            watch_set_indicator(WATCH_INDICATOR_PM);
+        }
+        date_time.unit.hour %= 12;
+        if (date_time.unit.hour == 0) date_time.unit.hour = 12;
+    }
+    sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
+    watch_set_colon();
+    watch_display_string(buf, 0);
+}
+
+bool solstice_face_loop(movement_event_t event, void *context) {
+    solstice_state_t *state = (solstice_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ALARM_LONG_PRESS:
+            show_date_time(state);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (state->index == 0) {
+                if (state->year == 0) {
+                    break;
+                }
+                state->year--;
+                state->index = 3;
+                calculate_datetimes(state, settings);
+            } else {
+                state->index--;
+            }
+            show_main_screen(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            state->index++;
+            if (state->index > 3) {
+                if (state->year == 83) {
+                    break;
+                }
+                state->year++;
+                state->index = 0;
+                calculate_datetimes(state, settings);
+            }
+            show_main_screen(state);
+            break;
+        case EVENT_ALARM_LONG_UP:
+            watch_clear_colon();
+            watch_clear_indicator(WATCH_INDICATOR_PM);
+            show_main_screen(state);
+            break;
+        case EVENT_ACTIVATE:
+            show_main_screen(state);
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void solstice_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/complication/solstice_face.h b/legacy/watch_faces/complication/solstice_face.h
new file mode 100644 (file)
index 0000000..57dcb4e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Wesley Aptekar-Cassels
+ *
+ * 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 SOLSTICE_FACE_H_
+#define SOLSTICE_FACE_H_
+
+#include "movement.h"
+
+/*
+ * A face for telling the dates and times of solstices and equinoxes
+ *
+ * It shows the upcoming solstice or equinox by default. The upper right number
+ * is the year, and the bottom numbers are the date in MMDD format. Use the
+ * alarm / light buttons to go forwards / backwards in time. Long press the
+ * alarm button to show the time of the event, including what weekday it is on,
+ * in your local timezone (DST is not handled).
+ *
+ * Supports the years 2020 - 2083. The calculations are reasonably accurate for
+ * years between 1000 and 3000, but limitations in the sensor watch libraries
+ * (which could easily be worked around) prevent making use of that.
+ */
+
+typedef struct {
+    watch_date_time_t datetimes[4];
+    uint8_t year;
+    uint8_t index;
+} solstice_state_t;
+
+void solstice_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void solstice_face_activate(void *context);
+bool solstice_face_loop(movement_event_t event, void *context);
+void solstice_face_resign(void *context);
+
+#define solstice_face ((const watch_face_t){ \
+    solstice_face_setup, \
+    solstice_face_activate, \
+    solstice_face_loop, \
+    solstice_face_resign, \
+    NULL, \
+})
+
+#endif // SOLSTICE_FACE_H_
+
diff --git a/legacy/watch_faces/complication/stopwatch_face.c b/legacy/watch_faces/complication/stopwatch_face.c
new file mode 100644 (file)
index 0000000..c2ef899
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Wesley Ellis
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "stopwatch_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+// distant future for background task: January 1, 2083
+// see stopwatch_face_activate for details
+static const watch_date_time_t distant_future = {
+    .unit = {0, 0, 0, 1, 1, 63}
+};
+
+void stopwatch_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(stopwatch_state_t));
+        memset(*context_ptr, 0, sizeof(stopwatch_state_t));
+    }
+}
+
+static void _stopwatch_face_update_display(stopwatch_state_t *stopwatch_state, bool show_seconds) {
+    if (stopwatch_state->running) {
+        watch_date_time_t now = watch_rtc_get_date_time();
+        uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
+        uint32_t start_timestamp = watch_utility_date_time_to_unix_time(stopwatch_state->start_time, 0);
+        stopwatch_state->seconds_counted = now_timestamp - start_timestamp;
+    }
+
+    if (stopwatch_state->seconds_counted >= 3456000) {
+        // display maxes out just shy of 40 days, thanks to the limit on the day digits (0-39)
+        stopwatch_state->running = false;
+        movement_cancel_background_task();
+        watch_display_string("st39235959", 0);
+        return;
+    }
+
+    watch_duration_t duration = watch_utility_seconds_to_duration(stopwatch_state->seconds_counted);
+    char buf[14];
+
+    sprintf(buf, "st  %02d%02d  ", duration.hours, duration.minutes);
+    watch_display_string(buf, 0);
+
+    if (duration.days != 0) {
+        sprintf(buf, "%2d", (uint8_t)duration.days);
+        watch_display_string(buf, 2);
+    }
+
+    if (show_seconds) {
+        sprintf(buf, "%02d", duration.seconds);
+        watch_display_string(buf, 8);
+    }
+}
+
+void stopwatch_face_activate(void *context) {
+    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
+
+    stopwatch_state_t *stopwatch_state = (stopwatch_state_t *)context;
+    if (stopwatch_state->running) {
+        // because the low power update happens on the minute mark, and the wearer could start
+        // the stopwatch anytime, the low power update could fire up to 59 seconds later than
+        // we need it to, causing the stopwatch to display stale data.
+        // So let's schedule a background task that will never fire. This will keep the watch
+        // from entering low energy mode while the stopwatch is on screen. This background task
+        // will remain scheduled until the stopwatch stops OR this watch face resigns.
+        movement_schedule_background_task(distant_future);
+    }
+}
+
+bool stopwatch_face_loop(movement_event_t event, void *context) {
+    stopwatch_state_t *stopwatch_state = (stopwatch_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_set_colon();
+            // fall through
+        case EVENT_TICK:
+            if (stopwatch_state->start_time.reg == 0) {
+                watch_display_string("st  000000", 0);
+            } else {
+                _stopwatch_face_update_display(stopwatch_state, true);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            movement_illuminate_led();
+            if (!stopwatch_state->running) {
+                stopwatch_state->start_time.reg = 0;
+                stopwatch_state->seconds_counted = 0;
+                watch_display_string("st  000000", 0);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+            if (movement_button_should_sound()) {
+                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+            }
+            stopwatch_state->running = !stopwatch_state->running;
+            if (stopwatch_state->running) {
+                // we're running now, so we need to set the start_time.
+                if (stopwatch_state->start_time.reg == 0) {
+                    // if starting from the reset state, easy: we start now.
+                    stopwatch_state->start_time = watch_rtc_get_date_time();
+                } else {
+                    // if resuming with time already on the clock, the original start time isn't valid anymore!
+                    // so let's fetch the current time...
+                    uint32_t timestamp = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), 0);
+                    // ...subtract the seconds we've already counted...
+                    timestamp -= stopwatch_state->seconds_counted;
+                    // and resume from the "virtual" start time that's that many seconds ago.
+                    stopwatch_state->start_time = watch_utility_date_time_from_unix_time(timestamp, 0);
+                }
+                // schedule our keepalive task when running...
+                movement_schedule_background_task(distant_future);
+            } else {
+                // and cancel it when stopped.
+                movement_cancel_background_task();
+            }
+            break;
+        case EVENT_TIMEOUT:
+            // explicitly ignore the timeout event so we stay on screen
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(1000);
+            if (!stopwatch_state->running) {
+                // since the tick animation is running, displaying the stopped time could be misleading,
+                // as it could imply that the stopwatch is running. instead, show a blank display to
+                // indicate that we are in sleep mode.
+                watch_display_string("st  ----  ", 0);
+            } else {
+                // this OTOH shouldn't happen anymore; if we're running, we shouldn't enter low energy mode
+                _stopwatch_face_update_display(stopwatch_state, false);
+                watch_set_indicator(WATCH_INDICATOR_BELL);
+            }
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void stopwatch_face_resign(void *context) {
+    (void) context;
+
+    // regardless of whether we're running or stopped, cancel the task
+    // that was keeping us awake while on screen.
+    movement_cancel_background_task();
+}
diff --git a/legacy/watch_faces/complication/stopwatch_face.h b/legacy/watch_faces/complication/stopwatch_face.h
new file mode 100644 (file)
index 0000000..5f9d66b
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Wesley Ellis
+ * 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 STOPWATCH_FACE_H_
+#define STOPWATCH_FACE_H_
+
+/*
+ * STOPWATCH FACE
+ *
+ * The Stopwatch face provides basic stopwatch functionality: you can start
+ * and stop the stopwatch with the alarm button. Pressing the light button
+ * when the timer is stopped resets it.
+ *
+ * This face does not count sub-seconds.
+ * See also: "fast_stopwatch_face.h"
+ */
+
+#include "movement.h"
+
+typedef struct {
+    bool running;
+    watch_date_time_t start_time; // while running, show the difference between this time and now
+    uint32_t seconds_counted;   // set this value when paused, and show that instead.
+} stopwatch_state_t;
+
+void stopwatch_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void stopwatch_face_activate(void *context);
+bool stopwatch_face_loop(movement_event_t event, void *context);
+void stopwatch_face_resign(void *context);
+
+#define stopwatch_face ((const watch_face_t){ \
+    stopwatch_face_setup, \
+    stopwatch_face_activate, \
+    stopwatch_face_loop, \
+    stopwatch_face_resign, \
+    NULL, \
+})
+
+#endif // STOPWATCH_FACE_H_
diff --git a/legacy/watch_faces/complication/tachymeter_face.c b/legacy/watch_faces/complication/tachymeter_face.c
new file mode 100644 (file)
index 0000000..ddaafc5
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Raymundo Cassani
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "tachymeter_face.h"
+#include "watch_utility.h"
+
+static uint32_t _distance_from_struct(distance_digits_t dist_digits) {
+    // distance from digitwise distance
+    uint32_t retval = (dist_digits.thousands * 100000 +
+                       dist_digits.hundreds  *  10000 +
+                       dist_digits.tens      *   1000 +
+                       dist_digits.ones      *    100 +
+                       dist_digits.tenths    *     10 +
+                       dist_digits.hundredths);// * 1
+    return retval;
+}
+
+void tachymeter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void)watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(tachymeter_state_t));
+        memset(*context_ptr, 0, sizeof(tachymeter_state_t));
+        tachymeter_state_t *state = (tachymeter_state_t *)*context_ptr;
+        // Default distance
+        state->dist_digits.ones = 1;
+        state->distance = _distance_from_struct(state->dist_digits);
+    }
+}
+
+void tachymeter_face_activate(void *context) {
+    (void)context;
+    movement_request_tick_frequency(4); // 4Hz
+}
+
+static void _tachymeter_face_distance_lcd(movement_event_t event, tachymeter_state_t *state){
+    char buf[11];
+    // Distance from digits
+    state->distance = _distance_from_struct(state->dist_digits);
+    sprintf(buf, "TC %c%06lu", state->running ? ' ' : 'd',  state->distance);
+    // Blinking display when editing
+    if (state->editing) {
+        // Blink 'd'
+        if (event.subsecond < 2) {
+            buf[3] = ' ';
+        }
+        // Blink active digit
+        if (event.subsecond % 2) {
+            buf[state->active_digit + 4] = ' ';
+        }
+    }
+    watch_display_string(buf, 0);
+}
+
+static void _tachymeter_face_totals_lcd(tachymeter_state_t *state, bool show_time){
+    char buf[15];
+    if (!show_time){
+        sprintf(buf, "TC %c%6lu", 'h',  state->total_speed);
+    } else {
+        sprintf(buf, "TC %c%6lu", 't',  state->total_time);
+    }
+    watch_display_string(buf, 0);
+    if (!show_time){
+        // Show '/' besides 'H'
+        watch_set_pixel(0, 9);
+        watch_set_pixel(0, 10);
+    }
+}
+
+bool tachymeter_face_loop(movement_event_t event, void *context) {
+    tachymeter_state_t *state = (tachymeter_state_t *)context;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Show distance in UI
+            if (state->total_time == 0) {
+                _tachymeter_face_distance_lcd(event, state);
+            }
+            break;
+        case EVENT_TICK:
+            // Show editing distance (blinking)
+            if (state->editing) {
+                _tachymeter_face_distance_lcd(event, state);
+            }
+            if (!state->running && state->total_time != 0) {
+            // Display results if finished and not cleared
+                if (event.subsecond < 2) {
+                     _tachymeter_face_totals_lcd(state, true);
+                } else {
+                     _tachymeter_face_totals_lcd(state, false);
+                }
+            } else if (state->running){
+                watch_display_string("  ", 2);
+                switch (state->animation_state) {
+                    case 0:
+                        watch_set_pixel(0, 7);
+                        break;
+                    case 1:
+                        watch_set_pixel(1, 7);
+                        break;
+                    case 2:
+                        watch_set_pixel(2, 7);
+                        break;
+                    case 3:
+                        watch_set_pixel(2, 6);
+                        break;
+                    case 4:
+                        watch_set_pixel(2, 8);
+                        break;
+                    case 5:
+                        watch_set_pixel(0, 8);
+                        break;
+                }
+                state->animation_state = (state->animation_state + 1) % 6;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (state->editing){
+                // Go to next digit
+                state->active_digit = (state->active_digit + 1) % 6;
+            } else {
+                movement_illuminate_led();
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (!state->running && !state->editing){
+                if (state->total_time != 0){
+                    // Clear results
+                    state->total_time = 0;
+                    state->total_speed = 0;
+                } else {
+                    // Default distance
+                    state->dist_digits.thousands  = 0;
+                    state->dist_digits.hundreds   = 0;
+                    state->dist_digits.tens       = 0;
+                    state->dist_digits.ones       = 1;
+                    state->dist_digits.tenths     = 0;
+                    state->dist_digits.hundredths = 0;
+                    state->distance = _distance_from_struct(state->dist_digits);
+                }
+            _tachymeter_face_distance_lcd(event, state);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if (!state->running && state->total_time == 0){
+                if (movement_button_should_sound() && !state->editing) {
+                    watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+                }
+                if (!state->editing) {
+                    // Start running
+                    state->running = true;
+                    state->start_seconds = watch_rtc_get_date_time();
+                    state->start_subsecond = event.subsecond;
+                    state->total_time = 0;
+                } else {
+                    // Alarm button to increase active digit
+                    switch (state->active_digit) {
+                        case 0:
+                            state->dist_digits.thousands = (state->dist_digits.thousands + 1) % 10;
+                            break;
+                        case 1:
+                            state->dist_digits.hundreds = (state->dist_digits.hundreds + 1) % 10;
+                            break;
+                        case 2:
+                            state->dist_digits.tens = (state->dist_digits.tens + 1) % 10;
+                            break;
+                        case 3:
+                            state->dist_digits.ones = (state->dist_digits.ones + 1) % 10;
+                            break;
+                        case 4:
+                            state->dist_digits.tenths = (state->dist_digits.tenths + 1) % 10;
+                            break;
+                        case 5:
+                            state->dist_digits.hundredths = (state->dist_digits.hundredths + 1) % 10;
+                            break;
+                    }
+                }
+            } else if (state->running) {
+                if (movement_button_should_sound() && !state->editing) {
+                    watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+                }
+                // Stop running
+                state->running = false;
+                watch_date_time_t now = watch_rtc_get_date_time();
+                uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
+                uint32_t start_timestamp = watch_utility_date_time_to_unix_time(state->start_seconds, 0);
+                // Total time in centiseconds
+                state->total_time = ((now_timestamp*100) + (event.subsecond*25)) - ((start_timestamp*100) + (state->start_subsecond*25));
+                // Total speed in distance units per hour
+                state->total_speed = (uint32_t)(3600 * 100 * state->distance / state->total_time);
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (!state->running && state->total_time == 0){
+                if (!state->editing) {
+                    // Enter editing
+                    state->editing = true;
+                    state->active_digit = 0;
+                    if (movement_button_should_sound()) {
+                        watch_buzzer_play_note(BUZZER_NOTE_C7, 80);
+                        watch_buzzer_play_note(BUZZER_NOTE_C8, 80);
+                    }
+                } else {
+                    // Exit editing
+                    state->editing = false;
+                    // Validate distance
+                    if(_distance_from_struct(state->dist_digits) == 0){
+                        state->dist_digits.ones = 1;
+                    }
+                    _tachymeter_face_distance_lcd(event, state);
+                    if (movement_button_should_sound()) {
+                        watch_buzzer_play_note(BUZZER_NOTE_C8, 80);
+                        watch_buzzer_play_note(BUZZER_NOTE_C7, 80);
+                    }
+                }
+            }
+            break;
+        case EVENT_TIMEOUT:
+            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
+            // you may uncomment this line to move back to the first watch face in the list:
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
+            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
+            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
+            // watch_start_sleep_animation(500);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
+    // you should return false since the PWM driver does not operate in standby mode.
+    return true;
+}
+
+void tachymeter_face_resign(void *context) {
+    (void)context;
+    // handle any cleanup before your watch face goes off-screen.
+}
diff --git a/legacy/watch_faces/complication/tachymeter_face.h b/legacy/watch_faces/complication/tachymeter_face.h
new file mode 100644 (file)
index 0000000..d299cb8
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Raymundo Cassani
+ *
+ * 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 TACHYMETER_FACE_H_
+#define TACHYMETER_FACE_H_
+
+/*
+ * TACHYMETER face
+ *
+ * The Tachymeter complication emulates the tachymeter function often
+ * present in watches, that computes the average speed in [units per hour]
+ * for a given distance given in [units].
+ *
+ * Use case:
+ *     User sets the distance
+ *     User starts the tachymeter when the trip begins
+ *     User stops the tachymeter when the trip ends
+ *     The watch presents the average speed and trip duration in seconds
+ * 
+ * Usage:
+ *     Go to tachymeter face, TC is shown in the Weekday Digits
+ *     A steady d in the Day Digits indicates the distance to be used.
+ *         To edit the distance:
+ *         Long-press the Alarm button, the distance edition page (d will blink)
+ *         Use the Light button to change the editing (blinking) digit, and press Alarm to increase its value
+ *         Once done, long-press the Alarm button to exit the distance edition page
+ *     Press the Alarm button to start the tachymeter.
+ *         A running animation will appear in the Day Digits
+ *     Press the Alarm button to stop the tachymeter
+ *     The average speed and total time information will alternate.
+ *         The average speed will be shown alongside /h in the Day Digits;
+ *         and the total time will be shown alongside t in the Day Digits.
+ *     Long press the Light button to return to the distance d page,
+ *         and restart the tachymeter from there.
+ *     Long-press the light button in the steady distance page to reset
+ *         the distance to 1.00
+ * 
+ * Pending design points
+ * o movement_request_tick_frequency(4) is used to obtain a 4Hz ticking, thus
+ *   having a time resolution of 250 ms. Not sure if using event.subsecond`
+ *   is the proper way to get the fractions of second for the start and
+ *   final times.
+ * o For distance and average speed, the Second Digits (position 8 and 9)
+ *   can be seen as decimals, thus possible to show distances as short as
+ *   0.01 km (or miles) and speeds as low as 0.01 km/h (or mph). However,
+ *   if the same idea is used for the total time (showing hundredths),
+ *   this limits the display to 9999.99 seconds (~2h:45m).
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint8_t thousands: 4;   // 0-9 (must wrap at 10)
+    uint8_t hundreds: 4;    // 0-9 (must wrap at 10)
+    uint8_t tens: 4;        // 0-9 (must wrap at 10)
+    uint8_t ones: 4;        // 0-9 (must wrap at 10)
+    uint8_t tenths: 4;      // 0-9 (must wrap at 10)
+    uint8_t hundredths: 4;  // 0-9 (must wrap at 10)
+} distance_digits_t;
+
+typedef struct {
+    bool running;                  // tachymeter status
+    bool editing;                  // editing distance
+    uint8_t active_digit;          // active digit at editing distance
+    uint8_t animation_state;       // running animation state
+    watch_date_time_t start_seconds; // start_seconds
+    int8_t start_subsecond;        // start_subsecond count (each count = 250 ms)
+    distance_digits_t dist_digits; // distance digitwise
+    uint32_t distance;             // distance
+    uint32_t total_time;           // total_time = now - start_time (in cs)
+    uint32_t total_speed;          // 3600 * 100 * distance / total_time
+} tachymeter_state_t;
+
+void tachymeter_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void tachymeter_face_activate(void *context);
+bool tachymeter_face_loop(movement_event_t event, void *context);
+void tachymeter_face_resign(void *context);
+
+#define tachymeter_face ((const watch_face_t){ \
+    tachymeter_face_setup, \
+    tachymeter_face_activate, \
+    tachymeter_face_loop, \
+    tachymeter_face_resign, \
+    NULL, \
+})
+
+#endif // TACHYMETER_FACE_H_
+
diff --git a/legacy/watch_faces/complication/tally_face.c b/legacy/watch_faces/complication/tally_face.c
new file mode 100644 (file)
index 0000000..128e84a
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andrew Mike
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "tally_face.h"
+#include "watch.h"
+
+#define TALLY_FACE_MAX 9999
+#define TALLY_FACE_MIN -99
+
+static bool _init_val;
+static bool _quick_ticks_running;
+
+static const int16_t _tally_default[] = {
+    0,
+
+#ifdef TALLY_FACE_PRESETS_MTG
+    20,
+    40,
+#endif /* TALLY_FACE_PRESETS_MTG */
+
+#ifdef TALLY_FACE_PRESETS_YUGIOH
+    4000,
+    8000,
+#endif /* TALLY_FACE_PRESETS_YUGIOH */
+
+};
+
+#define TALLY_FACE_PRESETS_SIZE() (sizeof(_tally_default) / sizeof(int16_t))
+
+void tally_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(tally_state_t));
+        memset(*context_ptr, 0, sizeof(tally_state_t));
+        tally_state_t *state = (tally_state_t *)*context_ptr;
+        state->tally_default_idx = 0;
+        state->tally_idx = _tally_default[state->tally_default_idx];
+        _init_val = true;
+    }
+}
+
+void tally_face_activate(void *context) {
+    (void) context;
+    _quick_ticks_running = false;
+}
+
+static void start_quick_cyc(void){
+    _quick_ticks_running = true;
+    movement_request_tick_frequency(8);
+}
+
+static void stop_quick_cyc(void){
+    _quick_ticks_running = false;
+    movement_request_tick_frequency(1);
+}
+
+static void tally_face_increment(tally_state_t *state, bool sound_on) {
+        bool soundOn = !_quick_ticks_running && sound_on;
+        _init_val = false;
+        if (state->tally_idx >= TALLY_FACE_MAX){
+            if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_E7, 30);
+        }
+        else {
+            state->tally_idx++;
+            print_tally(state, sound_on);
+            if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_E6, 30);
+        }
+}
+
+static void tally_face_decrement(tally_state_t *state, bool sound_on) {
+        bool soundOn = !_quick_ticks_running && sound_on;
+        _init_val = false;
+        if (state->tally_idx <= TALLY_FACE_MIN){
+            if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_C5SHARP_D5FLAT, 30);
+        }
+        else {
+            state->tally_idx--;
+            print_tally(state, sound_on);
+            if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_C6SHARP_D6FLAT, 30);
+        }
+}
+
+static bool tally_face_should_move_back(tally_state_t *state) {
+    if (TALLY_FACE_PRESETS_SIZE() <= 1) { return false; }
+    return state->tally_idx == _tally_default[state->tally_default_idx];
+}
+
+bool tally_face_loop(movement_event_t event, void *context) {
+    tally_state_t *state = (tally_state_t *)context;
+    static bool using_led = false;
+
+    if (using_led) {
+        if(!HAL_GPIO_BTN_MODE_read() && !HAL_GPIO_BTN_LIGHT_read() && !HAL_GPIO_BTN_ALARM_read())
+            using_led = false;
+        else {
+            if (event.event_type == EVENT_LIGHT_BUTTON_DOWN || event.event_type == EVENT_ALARM_BUTTON_DOWN)
+                movement_illuminate_led();
+            return true;
+        }
+    }
+
+    switch (event.event_type) {
+        case EVENT_TICK:
+            if (_quick_ticks_running) {
+                bool light_pressed = HAL_GPIO_BTN_LIGHT_read();
+                bool alarm_pressed = HAL_GPIO_BTN_ALARM_read();
+                if (light_pressed && alarm_pressed) stop_quick_cyc();
+                else if (light_pressed) tally_face_increment(state, movement_button_should_sound());
+                else if (alarm_pressed) tally_face_decrement(state, movement_button_should_sound());
+                else stop_quick_cyc();
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            tally_face_decrement(state, movement_button_should_sound());
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            tally_face_decrement(state, movement_button_should_sound());
+            start_quick_cyc();
+            break;
+        case EVENT_MODE_LONG_PRESS:
+            if (tally_face_should_move_back(state)) {
+                _init_val = true;
+                movement_move_to_face(0);
+            }
+            else {
+                state->tally_idx = _tally_default[state->tally_default_idx]; // reset tally index
+                _init_val = true;
+                //play a reset tune
+                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_G6, 30);
+                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_REST, 30);
+                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_E6, 30);
+                print_tally(state, movement_button_should_sound());
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            tally_face_increment(state, movement_button_should_sound());
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+        case EVENT_ALARM_BUTTON_DOWN:
+            if (HAL_GPIO_BTN_MODE_read()) {
+                movement_illuminate_led();
+                using_led = true;
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (TALLY_FACE_PRESETS_SIZE() > 1 && _init_val){
+                state->tally_default_idx = (state->tally_default_idx + 1) % TALLY_FACE_PRESETS_SIZE();
+                state->tally_idx = _tally_default[state->tally_default_idx];
+                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_E6, 30);
+                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_REST, 30);
+                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_G6, 30);
+                print_tally(state, movement_button_should_sound());
+            }
+            else{
+                tally_face_increment(state, movement_button_should_sound());
+                start_quick_cyc();
+            }
+            break;
+        case EVENT_ACTIVATE:
+            print_tally(state, movement_button_should_sound());
+            break;
+        case EVENT_TIMEOUT:
+            // ignore timeout
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+// print tally index at the center of display.
+void print_tally(tally_state_t *state, bool sound_on) {
+    char buf[14];
+    if (sound_on)
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    else
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+    if (state->tally_idx >= 0)
+        sprintf(buf, "TA  %4d  ", (int)(state->tally_idx)); // center of LCD display
+    else
+        sprintf(buf, "TA     %-3d", (int)(state->tally_idx)); // center of LCD display
+    watch_display_string(buf, 0);
+}
+
+void tally_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/tally_face.h b/legacy/watch_faces/complication/tally_face.h
new file mode 100644 (file)
index 0000000..7b18a33
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andrew Mike
+ *
+ * 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 TALLY_FACE_H_
+#define TALLY_FACE_H_
+
+/*
+ * TALLY face
+ *
+ * Tally face is designed to act as a tally counter.
+ *
+ * Alarm
+ *    Press: Decrement
+ *    Hold : Fast Decrement
+ *
+ * Light
+ *    Press: Increment
+ *    Hold : On initial value: Cycles through other initial values.
+ *                       Else: Fast Increment
+ *
+ * Mode
+ *    Press: Next face
+ *    Hold : On initial value: Go to first face.
+ *                       Else: Resets counter
+ * 
+ * Incrementing or Decrementing the tally will beep if Beeping is set in the global Preferences
+ */
+
+#include "movement.h"
+
+typedef struct {
+    int16_t tally_idx;
+    uint8_t tally_default_idx;
+} tally_state_t;
+
+//#define TALLY_FACE_PRESETS_MTG
+//#define TALLY_FACE_PRESETS_YUGIOH
+
+
+void tally_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void tally_face_activate(void *context);
+bool tally_face_loop(movement_event_t event, void *context);
+void tally_face_resign(void *context);
+
+void print_tally(tally_state_t *state, bool sound_on);
+
+#define tally_face ((const watch_face_t){ \
+    tally_face_setup, \
+    tally_face_activate, \
+    tally_face_loop, \
+    tally_face_resign, \
+    NULL, \
+})
+
+#endif // TALLY_FACE_H_
diff --git a/legacy/watch_faces/complication/tarot_face.c b/legacy/watch_faces/complication/tarot_face.c
new file mode 100644 (file)
index 0000000..ae81941
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Jeremy O'Brien
+ * Base code copied from Spencer Bywater's probability face
+ *
+ * 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.
+ */
+
+// Emulator only: need time() to seed the random number generator.
+#if __EMSCRIPTEN__
+#include <time.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "tarot_face.h"
+
+#define TAROT_ANIMATION_TICK_FREQUENCY 8
+#define FLIPPED_BIT_POS 7
+#define FLIPPED_MASK ((uint8_t)(1 << FLIPPED_BIT_POS))
+
+
+// --------------
+// Custom methods
+// --------------
+
+static char major_arcana[][7] = {
+    " FOOL ",
+    "Mgcian",
+    "HPrsts",
+    "En&prs", // Empress
+    "En&por", // Emperor
+    "Hiroph",
+    "Lovers",
+    "Chriot",
+    "Strgth",
+    "Hrn&it", // Hermit
+    " Frtun",
+    "Justce",
+    "Hangn&", // Hangman
+    " Death",
+    " tmprn",
+    " Devil",
+    " Tower",
+    "  Star",
+    "n&OON ", // Moon
+    "  Sun ",
+    "Jdgmnt",
+    " World",
+};
+#define NUM_MAJOR_ARCANA (sizeof(major_arcana) / sizeof(*major_arcana))
+
+static char suits[][7] = {
+    " wands",
+    "  cups",
+    "swords",
+    " coins",
+};
+
+#define NUM_MINOR_ARCANA 56
+#define NUM_CARDS_PER_SUIT 14
+
+#define NUM_TAROT_CARDS (NUM_MAJOR_ARCANA + NUM_MINOR_ARCANA)
+
+static void init_deck(tarot_state_t *state) {
+    memset(state->drawn_cards, 0xff, sizeof(state->drawn_cards));
+    state->current_card = 0;
+}
+
+static void tarot_display(tarot_state_t *state) {
+    char buf[12];
+    char *start_end_string;
+    uint8_t card;
+    bool flipped;
+
+    // deck is initialized; show current draw mode and return
+    if (state->drawn_cards[0] == 0xff) {
+        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+        if (state->major_arcana_only) {
+            sprintf(buf, "TA%2dn&ajor", state->num_cards_to_draw);
+        } else {
+            sprintf(buf, "TA%2d   All", state->num_cards_to_draw);
+        }
+        watch_display_string(buf, 0);
+        return;
+    }
+
+    // show a special status if we're looking at the first or last card in the spread
+    if (state->current_card == 0) {
+        start_end_string = "St";
+    } else if (state->current_card == state->num_cards_to_draw - 1) {
+        start_end_string = "En";
+    } else {
+        start_end_string = "  ";
+    }
+
+    // figure out the card we're showing
+    card = state->drawn_cards[state->current_card];
+    flipped = (card & FLIPPED_MASK) ? true : false; // check flipped bit
+    card &= ~FLIPPED_MASK; // remove the flipped bit
+    if (card < NUM_MAJOR_ARCANA) {
+        // major arcana
+
+        // show start/end, no rank, card name
+        sprintf(buf, "%s  %s", start_end_string, major_arcana[card]);
+    } else {
+        // minor arcana
+        uint8_t suit = (card - NUM_MAJOR_ARCANA) / NUM_CARDS_PER_SUIT;
+        uint8_t rank = ((card - NUM_MAJOR_ARCANA) % NUM_CARDS_PER_SUIT) + 1;
+
+        // show start/end, rank + suit
+        sprintf(buf, "%s%2d%s", start_end_string, rank, suits[suit]);
+    }
+
+    watch_display_string(buf, 0);
+
+    if (flipped) {
+        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    } else {
+        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+    }
+}
+
+static uint8_t get_rand_num(uint8_t num_values) {
+    // Emulator: use rand. Hardware: use arc4random.
+#if __EMSCRIPTEN__
+    return rand() % num_values;
+#else
+    return arc4random_uniform(num_values);
+#endif
+}
+
+static uint8_t draw_one_card(tarot_state_t *state) {
+    if (state->major_arcana_only) {
+        return get_rand_num(NUM_MAJOR_ARCANA);
+    } else {
+        return get_rand_num(NUM_TAROT_CARDS);
+    }
+}
+
+static bool already_drawn(tarot_state_t *state, uint8_t drawn_card) {
+    for (int i = 0; state->drawn_cards[i] != 0xff && i < state->num_cards_to_draw; i++) {
+        if ((state->drawn_cards[i] & ~FLIPPED_MASK) == drawn_card) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static void pick_cards(tarot_state_t *state) {
+    uint8_t card;
+
+    for (int i = 0; i < state->num_cards_to_draw; i++) {
+        card = draw_one_card(state);
+        while (already_drawn(state, card)) {
+            card = draw_one_card(state);
+        }
+        card |= get_rand_num(2) << FLIPPED_BIT_POS; // randomly flip the card
+        state->drawn_cards[i] = card;
+    }
+}
+
+static void display_animation(tarot_state_t *state) {
+    if (state->animation_frame == 0) {
+        watch_display_string("   ", 7);
+        watch_set_pixel(1, 4);
+        watch_set_pixel(1, 6);
+        state->animation_frame = 1;
+    } else if (state->animation_frame == 1) {
+        watch_clear_pixel(1, 4);
+        watch_clear_pixel(1, 6);
+        watch_set_pixel(2, 4);
+        watch_set_pixel(0, 6);
+        state->animation_frame = 2;
+    } else if (state->animation_frame == 2) {
+        watch_clear_pixel(2, 4);
+        watch_clear_pixel(0, 6);
+        watch_set_pixel(2, 5);
+        watch_set_pixel(0, 5);
+        state->animation_frame = 3;
+    } else if (state->animation_frame == 3) {
+        state->animation_frame = 0;
+        state->is_picking = false;
+        movement_request_tick_frequency(1);
+        tarot_display(state);
+    }
+}
+
+
+// ---------------------------
+// Standard watch face methods
+// ---------------------------
+void tarot_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(tarot_state_t));
+        memset(*context_ptr, 0, sizeof(tarot_state_t));
+    }
+    // Emulator only: Seed random number generator
+    #if __EMSCRIPTEN__
+    srand(time(NULL));
+    #endif
+}
+
+void tarot_face_activate(void *context) {
+    tarot_state_t *state = (tarot_state_t *)context;
+
+    watch_display_string("TA", 0);
+    init_deck(state);
+    state->num_cards_to_draw = 3;
+    state->major_arcana_only = true;
+}
+
+bool tarot_face_loop(movement_event_t event, void *context) {
+    tarot_state_t *state = (tarot_state_t *)context;
+
+    if (state->is_picking && event.event_type != EVENT_TICK) {
+        return true;
+    }
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            tarot_display(state);
+            break;
+        case EVENT_TICK:
+            if (state->is_picking) {
+                display_animation(state);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (state->drawn_cards[0] == 0xff) {
+                // deck is inited; cycle through # cards to draw
+                state->num_cards_to_draw++;
+                if (state->num_cards_to_draw > 10) {
+                    state->num_cards_to_draw = 3;
+                }
+            } else {
+                // cycle through the drawn cards
+                state->current_card = (state->current_card + 1) % state->num_cards_to_draw;
+            }
+            tarot_display(state);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (state->drawn_cards[0] == 0xff) {
+                // at main screen; cycle major arcana mode
+                state->major_arcana_only = !state->major_arcana_only;
+            } else {
+                // at card view screen; go back to draw screen
+                init_deck(state);
+            }
+            tarot_display(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // Draw cards
+            watch_display_string("      ", 4);
+            watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+            init_deck(state);
+            pick_cards(state);
+            state->is_picking = true;
+            // card picking animation begins on next tick and new cards will be displayed on completion
+            movement_request_tick_frequency(TAROT_ANIMATION_TICK_FREQUENCY);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            watch_display_string("SLEEP ", 4);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void tarot_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/tarot_face.h b/legacy/watch_faces/complication/tarot_face.h
new file mode 100644 (file)
index 0000000..8c83d51
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Jeremy O'Brien
+ *
+ * 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 TAROT_FACE_H_
+#define TAROT_FACE_H_
+
+/*
+ * TAROT CARD watch face
+ *
+ * Draw from a deck of tarot cards. Can choose between major arcana only or
+ * entire deck.
+ *
+ * In tarot reading, a card orientation can be upright or inverted, and the
+ * interpertation of the card can change depending on this state. This face
+ * lights the alarm indicator to show when a card is inverted. Just ignore it
+ * if you prefer not to deal with card inversions.
+ *
+ * This face uses the terms "Wands", "Cups", "Swords" and "Coins" for the four
+ * suits, and numbers to represent the 14 ranked cards, with the cards 11-14
+ * representing the Page, the Knight, the Queen, and King respectively.
+ *
+ * Default draw is a 3-card major arcana spread.
+ *
+ * To make it easier to keep track of where you are in the list of drawn cards,
+ * after drawing, "St" is shown for the 1st card in the spread and "En" is
+ * shown for the last card.
+ *
+ * At any point, the mode button can be held to return to your first configured
+ * watch face.
+ *
+ * When "Major" or "All" is shown:
+ * - Light button: cycle # of cards to draw
+ * - Light button (long press): toggle between major arcana and all cards
+ * - Alarm button: shuffle deck and draw cards
+ *
+ * After cards are drawn/showing:
+ * - Light button: view the next drawn card
+ * - Alarm button: shuffle and re-draw new cards
+ * - Light button (long press): go back to Draw screen, for choosing different draw parameters.
+ */
+
+#include "movement.h"
+
+#define MAX_CARDS_TO_DRAW 10
+
+typedef struct {
+    uint8_t drawn_cards[MAX_CARDS_TO_DRAW];
+    uint8_t current_card;
+    uint8_t animation_frame;
+    uint8_t num_cards_to_draw;
+    bool major_arcana_only;
+    bool is_picking;
+} tarot_state_t;
+
+void tarot_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void tarot_face_activate(void *context);
+bool tarot_face_loop(movement_event_t event, void *context);
+void tarot_face_resign(void *context);
+
+#define tarot_face ((const watch_face_t){ \
+    tarot_face_setup, \
+    tarot_face_activate, \
+    tarot_face_loop, \
+    tarot_face_resign, \
+    NULL, \
+})
+
+#endif // TAROT_FACE_H_
diff --git a/legacy/watch_faces/complication/tempchart_face.c b/legacy/watch_faces/complication/tempchart_face.c
new file mode 100644 (file)
index 0000000..e8e1cfa
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Mikhail Svarichevsky
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "tempchart_face.h"
+#include "watch.h"
+#include "watch_private_display.h"
+#include "filesystem.h"
+#include "thermistor_driver.h"
+
+struct {
+    uint8_t stat[24 * 70];
+    uint16_t num_div;
+} tempchart_state;
+
+static void tempchart_save(void) {
+    filesystem_write_file("tempchart.ini", (char*)&tempchart_state, sizeof(tempchart_state));
+}
+
+void tempchart_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    // These next two lines just silence the compiler warnings associated with unused parameters.
+    // We have no use for the settings or the watch_face_index, so we make that explicit here.
+    (void) context_ptr;
+    (void) watch_face_index;
+    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
+    if (filesystem_get_file_size("tempchart.ini") != sizeof(tempchart_state)) {
+        // No previous ini or old version of ini file - create new config file
+        tempchart_state.num_div = 0;
+        for (int i = 0; i < 24 * 70; i++)
+            tempchart_state.stat[i] = 0;
+        tempchart_save();
+    } else
+        filesystem_read_file("tempchart.ini", (char*)&tempchart_state, sizeof(tempchart_state));
+
+}
+
+void tempchart_face_activate(void *context) {
+    // same as above: silence the warning, we don't need to check the settings.
+    (void) context;
+}
+
+static void display(void) {
+    int sum = 0;
+    for (int i = 0; i < 24 * 70; i++)
+        sum += tempchart_state.stat[i];
+
+    char buf[24];
+    sprintf(buf, "TS%2d%6d", tempchart_state.num_div, sum);
+    watch_display_string(buf, 0);
+}
+
+bool tempchart_face_loop(movement_event_t event, void *context) {
+    (void) context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+             display();
+        case EVENT_TICK:
+            // on activate and tick, if we are animating,
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // This low energy mode update occurs once a minute, if the watch face is in the
+            // foreground when Movement enters low energy mode. We have the option of supporting
+            // this mode, but since our watch face animates once a second, the "Hello there" face
+            // isn't very useful in this mode. So we choose not to support it. (continued below)
+            break;
+        case EVENT_TIMEOUT:
+            // ... Instead, we respond to the timeout event. This event happens after a configurable
+            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
+            // resign control if we want to, and in this case, we do.
+            // This function will return the watch to the first screen (usually a simple clock),
+            // and it will do it long before the watch enters low energy mode. This ensures we
+            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
+            movement_move_to_face(0);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            // Here we measure temperature and do main frequency correction
+            thermistor_driver_enable();
+            float temperature_c = thermistor_driver_get_temperature();
+            thermistor_driver_disable();
+            watch_date_time_t date_time = watch_rtc_get_date_time();
+
+            int temp = round(temperature_c * 2);
+            if ((temp < 0) || (temp >= 70)) break;
+
+            if (tempchart_state.stat[date_time.unit.hour + temp * 24] == 255) { // We've reached the limit
+              tempchart_state.num_div++;
+              for (int i = 0; i < 24 * 70; i++)
+                tempchart_state.stat[i] = (tempchart_state.stat[i] + 1) >> 1; // So that we don't lose 1
+            }
+            tempchart_state.stat[date_time.unit.hour+temp*24]++;
+
+            if (date_time.unit.hour == 0 && date_time.unit.minute == 10)
+                tempchart_save();
+
+            break;
+
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void tempchart_face_resign(void *context) {
+    // our watch face, like most watch faces, has nothing special to do when resigning.
+    // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
+    (void) context;
+}
+
+//background freq correction
+movement_watch_face_advisory_t tempchart_face_advise(void *context) {
+    (void) context;
+    movement_watch_face_advisory_t retval = { 0 };
+
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    // Updating data every 5 minutes
+    retval.wants_background_task = date_time.unit.minute % 5 == 0;
+
+    return retval;
+}
diff --git a/legacy/watch_faces/complication/tempchart_face.h b/legacy/watch_faces/complication/tempchart_face.h
new file mode 100644 (file)
index 0000000..3c462cd
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 TEMPCHART_FACE_H_
+#define TEMPCHART_FACE_H_
+
+/*
+ * TEMPERATURE CHART face
+ *
+ * Gathers temperature statistics in a chart form.
+ * Statistics bins are per hour / per 0.5°C.
+ *
+ * Saved to file every day at 00:00.
+ * Can help improve watch precision in the future. 
+ *
+ * If you can gather statistics over few months, and then send "tempchart.ini"
+ * to "3@14.by", it will help future generations of precision quartz watches.
+ */
+
+#include "movement.h"
+
+void tempchart_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void tempchart_face_activate(void *context);
+bool tempchart_face_loop(movement_event_t event, void *context);
+void tempchart_face_resign(void *context);
+movement_watch_face_advisory_t tempchart_face_advise(void *context);
+
+
+#define tempchart_face ((const watch_face_t){ \
+    tempchart_face_setup, \
+    tempchart_face_activate, \
+    tempchart_face_loop, \
+    tempchart_face_resign, \
+    tempchart_face_advise, \
+})
+
+#endif // TEMPCHART_FACE_H_
diff --git a/legacy/watch_faces/complication/time_left_face.c b/legacy/watch_faces/complication/time_left_face.c
new file mode 100644 (file)
index 0000000..29a76fe
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andreas Nebinger, based on the work of 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "time_left_face.h"
+#include "watch.h"
+#include "watch_private_display.h"
+#include "watch_utility.h"
+
+const char _state_titles[][3] = {{'D', 'L', ' '}, {'D', 'L', ' '}, {'D', 'A', ' '}, {'D', 'A', ' '}, {'Y', 'R', 'b'}, {'M', 'O', 'b'}, {'D', 'A', 'b'},
+                                 {'Y', 'R', 'd'}, {'M', 'O', 'd'}, {'D', 'A', 'd'}};
+const uint8_t TIME_LEFT_FACE_STATES = sizeof(_state_titles) / 3;            // total number of state pages
+#define TIME_LEFT_FACE_SETTINGS_STATE 4                                     // number of first settings state
+const uint8_t _percentage_segdata[][2] = {{1, 2}, {2, 2}, {2, 3}, {1, 3}};  // segment data for drawing the percentage sign
+const uint8_t _animation_segdata[][2] = {{2, 8}, {1, 8}, {2, 7}, {2, 6}};  // segment data for the ticking animation
+
+static bool _quick_ticks_running;
+
+static uint32_t _juliandaynum(uint16_t year, uint16_t month, uint16_t day) {
+    // from here: https://en.wikipedia.org/wiki/Julian_day#Julian_day_number_calculation
+    return (1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100))/4 + day - 32075;
+}
+
+/// @brief displays an integer value with or without using positions 8 and 9
+static void _display_integer(char *buf) {
+    if (buf[1] == ' ') { 
+        // display at position 4 if the value is short enough, don't use positions 8 and 9
+        watch_display_character(' ', 8);
+        watch_display_character(' ', 9);
+        watch_display_string((char *)buf + 2, 4);
+    } else  {
+        // otherwise just display using position 8 and 9
+        watch_display_string(buf, 4);
+    }
+    watch_clear_colon();
+}
+
+///@brief display a percentage value
+static void _display_percentage(float percentage, char *buf) {
+    // always stay positive
+    if (percentage < 0) {
+        percentage *= -1;
+        // Switch display to days 'O'ver
+        watch_display_character('O', 1);
+    }
+    int32_t integral = percentage;
+    if (integral >= 100) {
+        // percentage equals 100 or more: don't do fractional part
+        sprintf(buf, " %3li o", integral);
+        watch_clear_colon();
+    } else {
+        // display percentage with two decimal places
+        uint8_t fraction = (int)(percentage * (float)100) % 100;
+        sprintf(buf, "%2li%02u o", integral, fraction);
+        watch_set_colon();
+    }
+    watch_display_string(buf, 4);
+    // draw (parts of) percentage symbol
+    for (uint8_t i = 0; i < sizeof(_percentage_segdata) / 2; i++) 
+        watch_set_pixel(_percentage_segdata[i][0], _percentage_segdata[i][1]);
+}
+
+/// @brief draw the current state to the display
+static void _draw(time_left_state_t *state, uint8_t subsecond) {
+    char buf[17];
+    watch_display_character(_state_titles[state->current_page][0], 0);
+    watch_display_character(_state_titles[state->current_page][1], 1);
+    watch_display_character(' ', 2);
+    watch_display_character(_state_titles[state->current_page][2], 3);
+    if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) {
+        // we are displaying days left or days from birth
+        watch_date_time_t date_time = watch_rtc_get_date_time();
+        uint32_t julian_current_day = _juliandaynum(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day);
+        uint32_t julian_target_day = _juliandaynum(state->target_date.bit.year, state->target_date.bit.month, state->target_date.bit.day);
+        int32_t days_left = julian_target_day - julian_current_day;
+        if (state->current_page == 0) {
+            // display number of days left
+            sprintf(buf, "%6li", days_left);
+            _display_integer(buf);
+        } else {
+            // calculate starting date
+            uint32_t julian_start_day = _juliandaynum(state->birth_date.bit.year, state->birth_date.bit.month, state->birth_date.bit.day);
+            if ((state->current_page & 1) == 1) {
+                float percentage_left;
+                if (julian_start_day == julian_target_day) {
+                    // failsafe
+                    percentage_left = 0;
+                } else {
+                    // display correct percentages
+                    percentage_left = (float)days_left * (float)100 / (float)(julian_target_day - julian_start_day);
+                }
+                _display_percentage(state->current_page == 1 ? percentage_left : 100 - percentage_left, buf);
+            } else {
+                // display days from birth
+                sprintf(buf, "%6li", (int32_t)julian_current_day - julian_start_day);
+                _display_integer(buf);
+            }
+        }
+    } else {
+        // we are in settings mode
+        switch (state->current_page) {
+        case TIME_LEFT_FACE_SETTINGS_STATE:
+            // birth year
+            sprintf(buf, "%04u  ", state->birth_date.bit.year);
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 1:
+            // birth month
+            sprintf(buf, "  %02u", state->birth_date.bit.month);
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 2:
+            // birth day of month
+            sprintf(buf, "  %02u", state->birth_date.bit.day);
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 3:
+            // target year
+            sprintf(buf, "%04u", state->target_date.bit.year);
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 4:
+            // target month
+            sprintf(buf, "  %02u", state->target_date.bit.month);
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 5:
+            // target day of month
+            sprintf(buf, "  %02u", state->target_date.bit.day);
+            break;
+        default:
+            break;
+        }
+        // blink current settings position or display value
+        if ((subsecond & 1) == 1) 
+            watch_display_string("    ", 4);
+        else
+            watch_display_string(buf, 4);
+    }
+}
+
+/// @brief handle short or long pressing the alarm button
+static void _handle_alarm_button(time_left_state_t *state) {
+    
+    switch (state->current_page) {
+        case TIME_LEFT_FACE_SETTINGS_STATE: // birth year
+            state->birth_date.bit.year++;
+            if (state->birth_date.bit.year > state->current_year + 10) state->birth_date.bit.year = 1959;
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 1: // birth month
+            state->birth_date.bit.month = (state->birth_date.bit.month % 12) + 1;
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 2: // birth day
+            state->birth_date.bit.day = (state->birth_date.bit.day % watch_utility_days_in_month(state->birth_date.bit.month, state->birth_date.bit.year)) + 1;
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 3: // target year
+            state->target_date.bit.year++;
+            if (state->target_date.bit.year  >2083) state->target_date.bit.year = state->current_year - 10;
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 4: // target month
+            state->target_date.bit.month = (state->target_date.bit.month % 12) + 1;
+            break;
+        case TIME_LEFT_FACE_SETTINGS_STATE + 5: // target day
+            state->target_date.bit.day = (state->target_date.bit.day % watch_utility_days_in_month(state->target_date.bit.month, state->birth_date.bit.year)) + 1;
+            break;
+    }
+}
+
+static void _initiate_setting(time_left_state_t *state) {
+    state->current_page = TIME_LEFT_FACE_SETTINGS_STATE;
+    watch_clear_colon();
+    movement_request_tick_frequency(4);
+}
+
+static void _resume_setting(time_left_state_t *state) {
+    state->current_page = 0;
+    movement_request_tick_frequency(1);
+}
+
+static void _abort_quick_ticks() {
+    if (_quick_ticks_running) {
+        _quick_ticks_running = false;
+        movement_request_tick_frequency(4);
+    }
+}
+
+void time_left_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(time_left_state_t));
+        memset(*context_ptr, 0, sizeof(time_left_state_t));
+        time_left_state_t *state = (time_left_state_t *)*context_ptr;
+        state->birth_date.reg = watch_get_backup_data(2);
+        if (state->birth_date.reg == 0) {
+            // if birth date is totally blank, set a reasonable starting date. this works well for anyone under 63, but
+            // you can keep pressing to go back to 1900; just pass the current year. also picked this date because if you
+            // set it to 1959-01-02, it counts up from the launch of Luna-1, the first spacecraft to leave the well.
+            state->birth_date.bit.year = 1959;
+            state->birth_date.bit.month = 1;
+            state->birth_date.bit.day = 1;
+            watch_store_backup_data(state->birth_date.reg, 2);
+            // set target date to today + 10 years (just to have any value)
+            watch_date_time_t date_time = watch_rtc_get_date_time();
+            state->target_date.bit.year = date_time.unit.year + WATCH_RTC_REFERENCE_YEAR + 10;
+            state->target_date.bit.month = date_time.unit.month;
+            state->target_date.bit.day = date_time.unit.day;
+        }
+    }
+}
+
+void time_left_face_activate(void *context) {
+    time_left_state_t *state = (time_left_state_t *)context;
+
+    // stash the current year, useful in birthday setting mode
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    state->current_year = date_time.unit.year + WATCH_RTC_REFERENCE_YEAR;
+    _quick_ticks_running = false;
+    // fetch the user's birth date from the birthday register
+    state->birth_date.reg = watch_get_backup_data(2);
+    // store original value of the birthdate to be able to detect changes on resigning
+    state->birth_date_when_activated.reg = state->birth_date.reg;
+}
+
+bool time_left_face_loop(movement_event_t event, void *context) {
+    time_left_state_t *state = (time_left_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _draw(state, event.subsecond);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+        case EVENT_TICK: {
+            uint8_t subsecond = event.subsecond;
+            if (_quick_ticks_running) {
+                if (HAL_GPIO_BTN_ALARM_read()) {
+                    _handle_alarm_button(state);
+                    subsecond = 0;
+                } else _abort_quick_ticks();
+            }
+            if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
+                // we are in settings mode. Draw to blink
+                _draw(state, subsecond);
+            } else {
+                // otherwise, check if we have to update. the display only needs to change at midnight
+                watch_date_time_t date_time = watch_rtc_get_date_time();
+                if (date_time.unit.hour == 0 &&  date_time.unit.minute == 0 && date_time.unit.second == 0) {
+                    _draw(state, subsecond);
+                }
+                // handle the ticking animation
+                uint8_t animation_step = date_time.unit.second % (sizeof(_animation_segdata) / 2);
+                watch_set_pixel(_animation_segdata[animation_step][0], _animation_segdata[animation_step][1]);
+                if (animation_step == 0) animation_step = (sizeof(_animation_segdata) / 2) - 1;
+                else animation_step--;
+                watch_clear_pixel(_animation_segdata[animation_step][0], _animation_segdata[animation_step][1]);
+            }
+            break;
+        }
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // do not illuminate here (this is done when releasing the button)
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) movement_illuminate_led();
+            else {
+                // cycle through the settings pages
+                state->current_page++;
+                if (state->current_page >= TIME_LEFT_FACE_STATES) {
+                    // we have done a full settings cycle, so resume to normal
+                    _resume_setting(state);
+                    _draw(state, event.subsecond);
+                }
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
+                _resume_setting(state);
+            } else {
+                _initiate_setting(state);
+            }
+            _draw(state, event.subsecond);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            _abort_quick_ticks();
+            if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) {
+                // alternate between days left and percentage left
+                state->current_page = (state->current_page + 1) % TIME_LEFT_FACE_SETTINGS_STATE;
+            } else {
+                // we are in settings mode, so increment the current settings value
+                _handle_alarm_button(state);
+            }
+            _draw(state, 0);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
+                // initiate fast cycling for settings values
+                movement_request_tick_frequency(8);
+                _quick_ticks_running = true;
+                _handle_alarm_button(state);
+                _draw(state, 0);
+            }
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void time_left_face_resign(void *context) {
+    time_left_state_t *state = (time_left_state_t *)context;
+
+    if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) _resume_setting(state);
+
+    // if the user changed their birth date, store it to the birth date register
+    if (state->birth_date_when_activated.reg != state->birth_date.reg) {
+        watch_store_backup_data(state->birth_date.reg, 2);
+    }
+}
diff --git a/legacy/watch_faces/complication/time_left_face.h b/legacy/watch_faces/complication/time_left_face.h
new file mode 100644 (file)
index 0000000..8f20ca0
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andreas Nebinger, based on the work of 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 TIME_LEFT_FACE_H_
+#define TIME_LEFT_FACE_H_
+
+/*
+ * TIME LEFT face
+ *
+ * The Time Left Face helps you to visualize how far you have proceeded in a certain
+ * time span. Much like the Day One Face, you can set your beginning date. In addition
+ * to that, you also set your target or destination date. You can then use the face
+ * to display your progress in different ways.
+ * 
+ * Usage:
+ * 
+ * - Long pressing of the light button starts the settings mode:
+ *   - First, you set the beginning date (indicated by a 'b' in the upper right corner).
+ *   - Start by setting the year (indicated by the letter 'YR'). Use the alarm button
+ *     to cycle the value. Short pressing the light button brings you to the next
+ *     settings page.
+ *   - Set the values in this order:
+ *     a. beginning date (indicated by a 'b'): year - month - day
+ *     b. destination date (indicated by a 'd'): year - month - day
+ *   - After cycling through all settings pages, the face resumes to display mode.
+ * 
+ * - In display mode, use the alarm button (short press) to cycle through these four
+ *   types of display:
+ *   a. number of days left ('DL') until the destination date is reached.
+ *   b. remaining days expressed as percentage of total time span. The value is shown
+ *      with two decimals, using the colon as decimal point.
+ *   c. number of days passed ('DA') since the beginning date.
+ *   d. number of days passed expressed as percentage of total time span. (Two decimal
+ *      points.)
+ *
+ * What is this for?
+ *
+ * You can use this watch face to be reminded of any kind of progess between a set
+ * start and end date. The brave among us can use it as a kind of memento mori
+ * visualization. Set your date of birth and look up the average life expectancy of
+ * your age cohort based on publicly available mortality tables. Then, set the
+ * statistically expected day of death as the target date and you will be able to
+ * see how much of your time has passed and how much is still to come.
+ * 
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint8_t current_page;
+    uint16_t current_year;
+    movement_birthdate_t birth_date;
+    movement_birthdate_t birth_date_when_activated;
+    movement_birthdate_t target_date;
+} time_left_state_t;
+
+void time_left_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void time_left_face_activate(void *context);
+bool time_left_face_loop(movement_event_t event, void *context);
+void time_left_face_resign(void *context);
+
+#define time_left_face ((const watch_face_t){ \
+    time_left_face_setup, \
+    time_left_face_activate, \
+    time_left_face_loop, \
+    time_left_face_resign, \
+    NULL, \
+})
+
+#endif // TIME_LEFT_FACE_H_
diff --git a/legacy/watch_faces/complication/timer_face.c b/legacy/watch_faces/complication/timer_face.c
new file mode 100644 (file)
index 0000000..de48e35
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andreas Nebinger, building on Wesley Ellis’ countdown_face.c
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "timer_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+static const uint32_t _default_timer_values[] = {0x000200, 0x000500, 0x000A00, 0x001400, 0x002D02}; // default timers: 2 min, 5 min, 10 min, 20 min, 2 h 45 min
+
+// sound sequence for a single beeping sequence
+static const int8_t _sound_seq_beep[] = {BUZZER_NOTE_C8, 3, BUZZER_NOTE_REST, 3, -2, 2, BUZZER_NOTE_C8, 5, BUZZER_NOTE_REST, 25, 0};
+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 void _signal_callback() {
+    if (_beeps_to_play) {
+        _beeps_to_play--;
+        watch_buzzer_play_sequence((int8_t *)_sound_seq_beep, _signal_callback);
+    }
+}
+
+static void _start(timer_state_t *state, bool with_beep) {
+    if (state->timers[state->current_timer].value == 0) return;
+    watch_date_time_t now = watch_rtc_get_date_time();
+    state->now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
+    if (state->mode == pausing)
+        state->target_ts = state->now_ts + state->paused_left;
+    else
+        state->target_ts = watch_utility_offset_timestamp(state->now_ts, 
+                                                          state->timers[state->current_timer].unit.hours, 
+                                                          state->timers[state->current_timer].unit.minutes, 
+                                                          state->timers[state->current_timer].unit.seconds);
+    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(state->target_ts, movement_get_current_timezone_offset());
+    state->mode = running;
+    movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
+    watch_set_indicator(WATCH_INDICATOR_BELL);
+    if (with_beep) watch_buzzer_play_sequence((int8_t *)_sound_seq_start, NULL);
+}
+
+static void _draw(timer_state_t *state, uint8_t subsecond) {
+    char buf[14];
+    uint32_t delta;
+    div_t result;
+    uint8_t h, min, sec;
+
+    switch (state->mode) {
+        case pausing:
+            if (state->pausing_seconds % 2)
+                watch_clear_indicator(WATCH_INDICATOR_BELL);
+            else
+                watch_set_indicator(WATCH_INDICATOR_BELL);
+            if (state->pausing_seconds != 1)
+                // not 1st iteration (or 256th): do not write anything
+                return;
+            // fall through
+        case running:
+            delta = state->target_ts - state->now_ts;
+            result = div(delta, 60);
+            sec = result.rem;
+            result = div(result.quot, 60);
+            min = result.rem;
+            h = result.quot;
+            sprintf(buf, " %02u%02u%02u", h, min, sec);
+            break;
+        case setting:
+            if (state->settings_state == 1) {
+                // ask it the current timer shall be erased
+                sprintf(buf, " CLEAR%c", state->erase_timer_flag ? 'y' : 'n');
+                watch_clear_colon();
+            } else if (state->settings_state == 5) {
+                sprintf(buf, "  LOOP%c", state->timers[state->current_timer].unit.repeat ? 'y' : 'n');
+                watch_clear_colon();
+            } else {
+                sprintf(buf, " %02u%02u%02u", state->timers[state->current_timer].unit.hours,
+                        state->timers[state->current_timer].unit.minutes,
+                        state->timers[state->current_timer].unit.seconds);
+                watch_set_colon();
+            }
+            break;
+        case waiting:
+            sprintf(buf, " %02u%02u%02u", state->timers[state->current_timer].unit.hours,
+                    state->timers[state->current_timer].unit.minutes,
+                    state->timers[state->current_timer].unit.seconds);
+            break;
+    }
+    buf[0] = 49 + state->current_timer;
+    if (state->mode == setting && subsecond % 2) {
+        // blink the current settings value
+        if (state->settings_state == 0) buf[0] = ' ';
+        else if (state->settings_state == 1 || state->settings_state == 5) buf[6] = ' ';
+        else buf[(state->settings_state - 1) * 2 - 1] = buf[(state->settings_state - 1) * 2] = ' ';
+    }
+    watch_display_string(buf, 3);
+    // set lap indicator when we have a looping timer
+    if (state->timers[state->current_timer].unit.repeat) watch_set_indicator(WATCH_INDICATOR_LAP);
+    else watch_clear_indicator(WATCH_INDICATOR_LAP);
+}
+
+static void _reset(timer_state_t *state) {
+    state->mode = waiting;
+    movement_cancel_background_task_for_face(state->watch_face_index);
+    watch_clear_indicator(WATCH_INDICATOR_BELL);
+}
+
+static void _set_next_valid_timer(timer_state_t *state) {
+    if ((state->timers[state->current_timer].value & 0xFFFFFF) == 0) {
+        uint8_t i = state->current_timer;
+        do {
+            i = (i + 1) % TIMER_SLOTS;
+        } while ((state->timers[i].value & 0xFFFFFF) == 0 && i != state->current_timer);
+        state->current_timer = i;
+    }
+}
+
+static void _resume_setting(timer_state_t *state) {
+    state->settings_state = 0;
+    state->mode = waiting;
+    movement_request_tick_frequency(1);
+    _set_next_valid_timer(state);
+}
+
+static void _settings_increment(timer_state_t *state) {
+    switch(state->settings_state) {
+        case 0:
+            state->current_timer = (state->current_timer + 1) % TIMER_SLOTS;
+            break;
+        case 1:
+            state->erase_timer_flag ^= 1;
+            break;
+        case 2:
+            state->timers[state->current_timer].unit.hours = (state->timers[state->current_timer].unit.hours + 1) % 24;
+            break;
+        case 3:
+            state->timers[state->current_timer].unit.minutes = (state->timers[state->current_timer].unit.minutes + 1) % 60;
+            break;
+        case 4:
+            state->timers[state->current_timer].unit.seconds = (state->timers[state->current_timer].unit.seconds + 1) % 60;
+            break;
+        case 5:
+            state->timers[state->current_timer].unit.repeat ^= 1;
+            break;
+        default:
+            // should never happen
+            break;
+    }
+    return;
+}
+
+static void _abort_quick_cycle(timer_state_t *state) {
+    if (state->quick_cycle) {
+        state->quick_cycle = false;
+        movement_request_tick_frequency(4);
+    }
+}
+
+static inline bool _check_for_signal() {
+    if (_beeps_to_play) {
+        _beeps_to_play = 0;
+        return true;
+    }
+    return false;
+}
+
+void timer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(timer_state_t));
+        timer_state_t *state = (timer_state_t *)*context_ptr;
+        memset(*context_ptr, 0, sizeof(timer_state_t));
+        state->watch_face_index = watch_face_index;
+        for (uint8_t i = 0; i < sizeof(_default_timer_values) / sizeof(uint32_t); i++) {
+            state->timers[i].value = _default_timer_values[i];
+        }
+    }
+}
+
+void timer_face_activate(void *context) {
+    timer_state_t *state = (timer_state_t *)context;
+    watch_display_string("TR", 0);
+    watch_set_colon();
+    if(state->mode == running) {
+        watch_date_time_t now = watch_rtc_get_date_time();
+        state->now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    } else {
+        state->pausing_seconds = 1;
+        _beeps_to_play = 0;
+    }
+}
+
+bool timer_face_loop(movement_event_t event, void *context) {
+    timer_state_t *state = (timer_state_t *)context;
+    uint8_t subsecond = event.subsecond;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _draw(state, event.subsecond);
+            break;
+        case EVENT_TICK:
+            if (state->mode == running) state->now_ts++;
+            else if (state->mode == pausing) state->pausing_seconds++;
+            else if (state->quick_cycle) {
+                if (HAL_GPIO_BTN_ALARM_read()) {
+                    _settings_increment(state);
+                    subsecond = 0;
+                } else _abort_quick_cycle(state);
+            }
+            _draw(state, subsecond);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            switch (state->mode) {
+                case pausing:
+                case running:
+                    movement_illuminate_led();
+                    break;
+                case setting:
+                    if (state->erase_timer_flag) {
+                        state->timers[state->current_timer].value = 0;
+                        state->erase_timer_flag = false;
+                    }
+                    state->settings_state = (state->settings_state + 1) % 6;
+                    if (state->settings_state == 1 && state->timers[state->current_timer].value == 0) state->settings_state = 2;
+                    else if (state->settings_state == 5 && (state->timers[state->current_timer].value & 0xFFFFFF) == 0) state->settings_state = 0;
+                    break;
+                default:
+                    break;
+            }
+            _draw(state, event.subsecond);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (state->mode == waiting) movement_illuminate_led();
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            _abort_quick_cycle(state);
+            if (_check_for_signal()) break;;
+            switch (state->mode) {
+                case running:
+                    state->mode = pausing;
+                    state->pausing_seconds = 0;
+                    state->paused_left = state->target_ts - state->now_ts;
+                    movement_cancel_background_task();
+                    break;
+                case pausing:
+                    _start(statemovement_get_current_timezone_offset(), false);
+                    break;
+                case waiting: {
+                    uint8_t last_timer = state->current_timer;
+                    state->current_timer = (state->current_timer + 1) % TIMER_SLOTS;
+                    _set_next_valid_timer(state);
+                    // start the time immediately if there is only one valid timer slot
+                    if (last_timer == state->current_timer) _start(statemovement_get_current_timezone_offset(), true);
+                    break;
+                }
+                case setting:
+                    _settings_increment(state);
+                    subsecond = 0;
+                    break;
+            }
+            _draw(state, subsecond);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (state->mode == waiting) {
+                // initiate settings
+                state->mode = setting;
+                state->settings_state = 0;
+                state->erase_timer_flag = false;
+                movement_request_tick_frequency(4);
+            } else if (state->mode == setting) {
+                _resume_setting(state);
+            }
+            _draw(state, event.subsecond);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            // play the alarm
+            _beeps_to_play = 4;
+            watch_buzzer_play_sequence((int8_t *)_sound_seq_beep, _signal_callback);
+            _reset(state);
+            if (state->timers[state->current_timer].unit.repeat) _start(statemovement_get_current_timezone_offset(), false);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            switch(state->mode) {
+                case setting:
+                    switch (state->settings_state) {
+                        case 0:
+                            state->current_timer = 0;
+                            break;
+                        case 2:
+                        case 3:
+                        case 4:
+                            state->quick_cycle = true;
+                            movement_request_tick_frequency(8);
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+                case waiting:
+                    _start(statemovement_get_current_timezone_offset(), true);
+                    break;
+                case pausing:
+                case running:
+                    _reset(state);
+                    if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+                    break;
+                default:
+                    break;
+            }
+            _draw(state, event.subsecond);
+            break;
+        case EVENT_ALARM_LONG_UP:
+            _abort_quick_cycle(state);
+            break;
+        case EVENT_MODE_LONG_PRESS:
+        case EVENT_TIMEOUT:
+            _abort_quick_cycle(state);
+            movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void timer_face_resign(void *context) {
+    timer_state_t *state = (timer_state_t *)context;
+    if (state->mode == setting) {
+        state->settings_state = 0;
+        state->mode = waiting;
+    }
+}
diff --git a/legacy/watch_faces/complication/timer_face.h b/legacy/watch_faces/complication/timer_face.h
new file mode 100644 (file)
index 0000000..b59c958
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andreas Nebinger, based on Wesley Ellis’ countdown face.
+ *
+ * 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 TIMER_FACE_H_
+#define TIMER_FACE_H_
+
+/*
+ * TIMER face
+ * Advanced timer/countdown face with pre-set timer lengths
+ * 
+ * This watch face provides the functionality of starting a countdown by choosing 
+ * one out of nine programmable timer presets. A timer/countdown can be 23 hours,
+ * 59 minutes, and 59 seconds max. A timer can also be set to auto-repeat, which
+ * is indicated by the lap indicator.
+ *
+ * How to use in NORMAL mode:
+ *   - Short-pressing the alarm button cycles through all pre-set timer lengths.
+ *     Find the current timer slot number in the upper right-hand corner.
+ *   - Long-pressing the alarm button starts the timer.
+ *   - Long-pressing the light button initiates settings mode.
+ * 
+ * How to use in SETTINGS mode:
+ *   - There are up to nine slots for storing a timer setting. The current slot is 
+ *     indicated by the number in the upper right-hand corner.
+ *   - Short-pressing the light button cycles through the settings values of each
+ *     timer slot in the following order: hours - minutes - seconds - timer repeat
+ *   - Short-pressing the alarm button alters the current settings value.
+ *   - Long-pressing the light button resumes to normal mode.
+ * 
+ */
+
+#include "movement.h"
+
+#define TIMER_SLOTS 9           // offer 9 timer slots
+
+typedef enum {
+    waiting,
+    running,
+    setting,
+    pausing
+} timer_mode_t;
+
+typedef union {
+    struct {
+        uint8_t hours;
+        uint8_t minutes;
+        uint8_t seconds;
+        bool repeat;
+    } unit;
+    uint32_t value;
+} timer_setting_t;
+
+typedef struct {
+    uint32_t target_ts;
+    uint32_t now_ts;
+    uint16_t paused_left;
+    uint8_t pausing_seconds;
+    uint8_t watch_face_index;
+    timer_setting_t timers[TIMER_SLOTS]; 
+    uint8_t settings_state : 4;
+    uint8_t current_timer : 4;
+    uint8_t set_timers : 4;
+    bool erase_timer_flag : 1;
+    timer_mode_t mode : 3;
+    bool quick_cycle : 1;
+} timer_state_t;
+
+void timer_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void timer_face_activate(void *context);
+bool timer_face_loop(movement_event_t event, void *context);
+void timer_face_resign(void *context);
+
+#define timer_face ((const watch_face_t){ \
+    timer_face_setup, \
+    timer_face_activate, \
+    timer_face_loop, \
+    timer_face_resign, \
+    NULL, \
+})
+
+
+#endif // TIMER_FACE_H_
diff --git a/legacy/watch_faces/complication/tomato_face.c b/legacy/watch_faces/complication/tomato_face.c
new file mode 100644 (file)
index 0000000..d33f8f0
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Wesley Ellis
+ *
+ * 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 NONINFtomato_ringEMENT. 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "tomato_face.h"
+#include "watch_utility.h"
+
+static uint8_t focus_min = 25;
+static uint8_t break_min = 5;
+
+static uint8_t get_length(tomato_state_t *state) {
+    uint8_t length;
+    if (state->kind == tomato_focus) {
+        length = focus_min;
+    } else {
+        length = break_min;
+    }
+
+    return length;
+}
+
+static void tomato_start(tomato_state_t *state) {
+    watch_date_time_t now = watch_rtc_get_date_time();
+    int8_t length = (int8_t) get_length(state);
+
+    state->mode = tomato_run;
+    state->now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
+    state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, length, 0);
+    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(state->target_ts, movement_get_current_timezone_offset());
+    movement_schedule_background_task(target_dt);
+    watch_set_indicator(WATCH_INDICATOR_BELL);
+}
+
+static void tomato_draw(tomato_state_t *state) {
+    char buf[16];
+
+    uint32_t delta;
+    div_t result;
+    uint8_t min = 0;
+    uint8_t sec = 0;
+    char kind;
+
+    if (state->kind == tomato_break) {
+        kind = 'b';
+    } else {
+        kind = 'f';
+    }
+
+    switch (state->mode) {
+        case tomato_run:
+            delta = state->target_ts - state->now_ts;
+            result = div(delta, 60);
+            min = result.quot;
+            sec = result.rem;
+            break;
+        case tomato_ready:
+            min = get_length(state);
+            sec = 0;
+            break;
+    }
+    if (state->visible) {
+        sprintf(buf, "TO %c%2d%02d%2d", kind, min, sec, state->done_count);
+        watch_display_string(buf, 0);
+    }
+}
+
+static void tomato_reset(tomato_state_t *state) {
+    state->mode = tomato_ready;
+    movement_cancel_background_task();
+    watch_clear_indicator(WATCH_INDICATOR_BELL);
+}
+
+static void tomato_ring(tomato_state_t *state) {
+    movement_play_signal();
+    tomato_reset(state);
+    if (state->kind == tomato_focus) {
+        state->kind = tomato_break;
+        state->done_count++;
+    } else {
+        state->kind = tomato_focus;
+    }
+}
+
+void tomato_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(tomato_state_t));
+        tomato_state_t *state = (tomato_state_t*)*context_ptr;
+        memset(*context_ptr, 0, sizeof(tomato_state_t));
+        state->mode=tomato_ready;
+        state->kind= tomato_focus;
+        state->done_count = 0;
+        state->visible = true;
+    }
+}
+
+void tomato_face_activate(void *context) {
+    tomato_state_t *state = (tomato_state_t *)context;
+    if (state->mode == tomato_run) {
+        watch_date_time_t now = watch_rtc_get_date_time();
+        state->now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    }
+    watch_set_colon();
+    state->visible = true;
+}
+
+bool tomato_face_loop(movement_event_t event, void *context) {
+    tomato_state_t *state = (tomato_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            tomato_draw(state);
+            break;
+        case EVENT_TICK:
+            if (state->mode == tomato_run) {
+                state->now_ts++;
+            }
+            tomato_draw(state);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            movement_illuminate_led();
+            if (state->mode == tomato_ready) {
+                if (state->kind == tomato_break) {
+                    state->kind = tomato_focus;
+                } else {
+                    state->kind = tomato_break;
+                }
+            }
+            tomato_draw(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch(state->mode) {
+                case tomato_run:
+                    tomato_reset(state);
+                    break;
+                case tomato_ready:
+                    tomato_start(state);
+                    break;
+            }
+            tomato_draw(state);
+
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->done_count = 0;
+            break;
+        case EVENT_BACKGROUND_TASK:
+            tomato_ring(state);
+            tomato_draw(state);
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void tomato_face_resign(void *context) {
+    tomato_state_t *state = (tomato_state_t *)context;
+    state->visible = false;
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/complication/tomato_face.h b/legacy/watch_faces/complication/tomato_face.h
new file mode 100644 (file)
index 0000000..e06a7c8
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Wesley Ellis
+ *
+ * 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 TOMATO_FACE_H_
+#define TOMATO_FACE_H_
+
+/*
+ * TOMATO TIMER face
+ *
+ * Add a "tomato" timer watch face that alternates between 25 and 5 minute
+ * timers as in the Pomodoro Technique.
+ *  https://en.wikipedia.org/wiki/Pomodoro_Technique
+ *
+ * The top right letter shows mode (f for focus or b for break).
+ * The bottom right shows how many focus sessions you've completed.
+ * (You can reset the count with a long press of alarm)
+ *
+ * When you show up and it says 25 minutes, you can start it (alarm),
+ *  switch to 5 minute (light) mode or leave (mode).
+ *
+ * When it's running you can reset (alarm), or leave (mode).
+ *
+ * When it's done, we beep and go back to step 1, changing switching
+ *  mode from focus to break (or break to focus)
+ */
+
+#include "movement.h"
+
+typedef enum {
+    tomato_ready,
+    tomato_run,
+    // to_pause, // TODO implement pausing
+} tomato_mode;
+
+typedef enum {
+    tomato_break,
+    tomato_focus,
+} tomato_kind;
+
+typedef struct {
+    uint32_t target_ts;
+    uint32_t now_ts;
+    tomato_mode mode;
+    tomato_kind kind;
+    uint8_t done_count;
+    bool visible;
+} tomato_state_t;
+
+void tomato_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void tomato_face_activate(void *context);
+bool tomato_face_loop(movement_event_t event, void *context);
+void tomato_face_resign(void *context);
+
+#define tomato_face ((const watch_face_t){ \
+    tomato_face_setup, \
+    tomato_face_activate, \
+    tomato_face_loop, \
+    tomato_face_resign, \
+    NULL, \
+})
+
+#endif // TOMATO_FACE_H_
+
diff --git a/legacy/watch_faces/complication/toss_up_face.c b/legacy/watch_faces/complication/toss_up_face.c
new file mode 100644 (file)
index 0000000..28b99c9
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "toss_up_face.h"
+#if __EMSCRIPTEN__
+#include <time.h>
+#else
+#include "saml22j18a.h"
+#endif
+
+static const char heads[] = { '8', 'h', '4', 'E', '(' };
+static const char tails[] = { '0', '+', 'N', '3', ')' };
+static const uint8_t dd[] = {2, 4, 6, 8, 10,12,20,24,30,32,36,48,99}; 
+
+static void _roll_dice_multiple(char* result, uint8_t* dice, uint8_t num_dice);
+static void _sort_coins(char* token, uint8_t num_bits, uint8_t bits, char* heads, char* tails);
+void _display_coins(char* token, bool* bit_array, uint8_t length, toss_up_state_t *state);
+static void _toss_up_face_display(toss_up_state_t *state);
+static void _dice_animation(toss_up_state_t *state);
+static void _coin_animation(toss_up_state_t *state);
+
+// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
+
+void toss_up_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(toss_up_state_t));
+        memset(*context_ptr, 0, sizeof(toss_up_state_t));
+        toss_up_state_t *state = (toss_up_state_t *)*context_ptr;
+
+        // defaults
+        state->coin_num = 1;
+        state->dice_num = 1;
+        state->dice_sides[0] = 6;
+        state->dice_sides[1] = 6;
+        state->dice_sides[2] = 6;
+        state->coin_style[0] = '8';
+        state->coin_style[1] = '0';
+    }
+}
+
+void toss_up_face_activate(void *context) {
+    (void) context;
+}
+
+bool toss_up_face_loop(movement_event_t event, void *context) {
+    toss_up_state_t *state = (toss_up_state_t *)context;
+    uint8_t i = 0;
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            watch_display_string("    Coins ", 0);
+            break;
+        case EVENT_TICK:
+            if ( state->animate ) {
+                state->animation = (state->animation + 1);
+                _toss_up_face_display(state);
+            } 
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if ( state->animate ) break;
+            // change between coins and dice
+            if ( state->mode <= 1 ) state->mode = 2;
+            else if ( state->mode >= 2 ) state->mode = 0;
+            _toss_up_face_display(state);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // toss
+            if ( state->animate ) break;
+            switch (state->mode) {
+                case 0:
+                    state->mode++;
+                    // fall through
+                case 1:
+                    state->animate = true;
+                    for (i = 0; i < state->coin_num; i++) {
+                        state->coins[i] = divine_bit();
+                    }             
+                    break;
+                case 2:
+                    state->mode++;
+                    // fall through
+                case 3:
+                    state->animate = true;
+                    for (i = 0; i < state->dice_num; i++) {
+                        state->dice[i] = roll_dice(state->dice_sides[i]);
+                    }  
+                    break;
+                default:
+                    break;
+            }
+            _toss_up_face_display(state);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if ( state->animate ) break;
+            state->animate = false;
+            switch (state->mode) {
+                case 0: // change to default coin style
+                    state->coin_style[0] = heads[0];
+                    state->coin_style[1] = tails[0];
+                    state->coinface = 0;
+                    break;
+                case 1: // change the coin style
+                    state->coinface = (state->coinface + 1) % 5;
+                    state->coin_style[0] = heads[state->coinface];
+                    state->coin_style[1] = tails[state->coinface];         
+                    break;
+                case 2: // change to default dice sides
+                    state->dice_sides[0] = 6;
+                    state->dice_sides[1] = 6;
+                    state->dice_sides[2] = 6;
+                    state->dd = 0;
+                    break;
+                case 3: // change the sides of the dice
+                    state->dd = (state->dd + 1) % 13;
+                    state->dice_sides[state->dice_num-1] = dd[state->dd];
+                    state->dice[state->dice_num-1] = dd[state->dd];
+                    break;
+                default:
+                    break;
+            }
+            _toss_up_face_display(state);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if ( state->animate ) break;
+            state->animate = false;
+            switch (state->mode) {
+                case 0: // back to one coin
+                    state->coin_num = 1;
+                    break;
+                case 1: // up to 6 coins total
+                    state->coin_num = (state->coin_num % 6) + 1;             
+                    break;
+                case 2: // back to one dice
+                    state->dice_num = 1;
+                    break;
+                case 3: // add up to 3 dice total
+                    state->dice_num = (state->dice_num % 3) + 1;
+                    state->dd = 0;
+                    break;
+                default:
+                    break;
+            }
+            _toss_up_face_display(state);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void toss_up_face_resign(void *context) {
+    (void) context;
+}
+
+// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
+
+/** @brief handles the display 
+ */
+static void _toss_up_face_display(toss_up_state_t *state) {
+    char buf[11] = {0};
+    char token[7] = {0};
+    switch ( state->mode ) {
+        case 0: // coins title
+            sprintf(buf, "    Coins ");
+            break;
+        case 1: // coins divination
+            _coin_animation(state);
+            if ( !state->animate ) {
+                watch_clear_display();
+                _display_coins(token, state->coins, state->coin_num, state);
+                sprintf(buf, "    %s", token);
+            }
+            break;
+        case 2: // dice title
+            sprintf(buf, "    Dice ");
+            break;
+        case 3: // dice divination
+            _dice_animation(state);
+            if ( !state->animate ) {
+                _roll_dice_multiple(token, state->dice, state->dice_num + 1);
+                sprintf(buf, "    %s", token);
+            }
+            break;
+        default:
+            break;
+    }
+    watch_display_string(buf, 0);
+}
+
+/** @brief divination method to derive a bit from 32 TRNG bits
+ */
+uint8_t divine_bit(void) {
+    uint32_t stalks;
+    do { // modulo bias filter
+        stalks = get_true_entropy(); // get 32 TRNG bits as stalks
+    } while (stalks >= INT32_MAX || stalks <= 0);
+
+    uint8_t pile1_xor = 0;
+    uint8_t pile2_xor = 0;
+    // Divide the stalks into two piles, alternating ends
+    for (uint8_t i = 0; i < 16; i++) {
+        uint8_t left_bit = (stalks >> (31 - 2*i)) & 1;
+        uint8_t right_bit = (stalks >> (30 - 2*i)) & 1;
+        if (i % 2 == 0) {
+            pile1_xor ^= left_bit;
+            pile2_xor ^= right_bit;
+        } else {
+            pile1_xor ^= right_bit;
+            pile2_xor ^= left_bit;
+        }
+    }
+    // Take the XOR of the pile results
+    uint8_t result_xor = pile1_xor ^ pile2_xor;
+    // Output 1 if result_xor is 1, 0 otherwise
+    return result_xor;
+}
+
+/** @brief get 32 True Random Number bits
+ */
+uint32_t get_true_entropy(void) {
+    #if __EMSCRIPTEN__
+    return rand() % INT32_MAX;
+    #else
+    hri_mclk_set_APBCMASK_TRNG_bit(MCLK);
+    hri_trng_set_CTRLA_ENABLE_bit(TRNG);
+
+    while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready
+
+    watch_disable_TRNG();
+
+    hri_mclk_clear_APBCMASK_TRNG_bit(MCLK);
+    return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it
+    #endif
+}
+
+// COIN FUNCTIONS /////////////////////////////////////////////////////////////
+
+/** @brief sort tossed coins into a pile of heads and a pile of tails
+ */
+static void _sort_coins(char* token, uint8_t num_bits, uint8_t bits, char* heads, char* tails) {
+    uint8_t num_ones = 0;
+    for (uint8_t i = 0; i < num_bits; i++) {
+        if ((bits >> i) & 1) {
+            *token++ = *heads;
+            num_ones++;
+        }
+    }
+    if ( num_bits < 6 ) {
+        for (uint8_t i = 0; i < (6 - num_bits); i++) {
+            *token++ = ' ';
+        }
+    }
+    for (uint8_t i = 0; i < (num_bits - num_ones); i++) {
+
+        *token++ = *tails;
+    }
+}
+
+/** @brief convert bool array of coinflips to integer for sorting
+ */
+void _display_coins(char* token, bool* bit_array, uint8_t length, toss_up_state_t *state) {
+    uint8_t bits = 0;
+    for (uint8_t i = 0; i < length; i++) {
+        if (bit_array[i]) {
+            bits |= (1 << (length - 1 - i));
+        }
+    }
+    _sort_coins(token, length, bits, &state->coin_style[0], &state->coin_style[1]);
+}
+
+/** @brief coin animation
+ */
+static void _coin_animation(toss_up_state_t *state) {
+    bool heads = false;
+    bool tails = false;
+    for (uint8_t i = 0; i < state->coin_num; i++) {
+        if (state->coins[i] == true) {
+            heads = true;
+        } else {
+            tails = true;
+        }
+    }
+    movement_request_tick_frequency(32);
+    switch ( state->animation ) {
+        case 0:
+            watch_display_string("      ", 4);
+            if ( heads ) {
+                watch_set_pixel(0, 18);
+                watch_set_pixel(2, 18);
+            } else {
+                state->animation = 12;
+            }
+            break;
+        case 1:
+            if ( heads ) {
+                watch_set_pixel(1, 18);
+            }
+            break;
+        case 2:
+            if ( heads ) {
+                watch_set_pixel(0, 19);
+                watch_set_pixel(2, 19);
+            }
+            break;
+        case 3:
+            if ( heads ) {
+                watch_clear_pixel(0, 18);
+                watch_clear_pixel(2, 18);
+            }
+            break;
+        case 4:
+            if ( heads ) {
+                watch_clear_pixel(1, 18);
+            }
+            break;
+        case 5:
+            if ( heads ) {
+                watch_clear_pixel(0, 19);
+                watch_clear_pixel(2, 19);
+                watch_set_pixel(1, 17);
+                watch_set_pixel(0, 20);
+            }
+            break;
+        case 6:
+            if ( heads ) {
+                watch_set_pixel(2, 20);
+                watch_set_pixel(0, 21);
+            }
+            break;
+        case 7:
+            if ( heads ) {
+                watch_set_pixel(1, 21);
+                watch_set_pixel(2, 21);
+            }
+            break;
+        case 8:
+            if ( heads ) {
+                watch_clear_pixel(1, 17);
+                watch_clear_pixel(0, 20);
+            }
+            break;
+        case 9:
+            if ( heads ) {
+                watch_clear_pixel(2, 20);
+                watch_clear_pixel(0, 21);
+            }
+            break;
+        case 10:
+            if ( heads ) {
+                watch_clear_pixel(1, 21);
+                watch_clear_pixel(2, 21);
+                watch_set_pixel(1, 22);
+                watch_set_pixel(2, 22);
+            }
+            break;
+        case 11:
+            if ( heads ) {
+                watch_set_pixel(0, 22);
+            }
+            break;
+        case 12:
+            if ( heads ) {
+                watch_set_pixel(2, 23);
+                watch_set_pixel(0, 23);
+            }
+            if ( tails ) {
+                watch_set_pixel(0, 18);
+                watch_set_pixel(2, 18);
+            }
+            break;
+        case 13:
+            if ( heads ) {
+                watch_clear_pixel(1, 22);
+                watch_clear_pixel(2, 22);
+            }
+            if ( tails ) {
+                watch_set_pixel(1, 18);
+            }
+            break;
+        case 14:
+            if ( heads ) {
+                watch_clear_pixel(0, 22);
+            }
+            if ( tails ) {
+                watch_set_pixel(0, 19);
+                watch_set_pixel(2, 19);
+            }
+            break;
+        case 15:
+            if ( heads ) {
+                watch_clear_pixel(2, 23);
+                watch_clear_pixel(0, 23);
+                watch_set_pixel(2, 0);
+                watch_set_pixel(1, 0);
+            }
+            if ( tails ) {
+                watch_clear_pixel(0, 18);
+                watch_clear_pixel(2, 18);
+            }
+            break;
+        case 16:
+            if ( heads ) {
+                watch_set_pixel(2, 1);
+                watch_set_pixel(0, 0);
+            }
+            if ( tails ) {
+                watch_clear_pixel(1, 18);
+            }
+            break;
+        case 17:
+            if ( heads ) {
+                watch_set_pixel(2, 10);
+                watch_set_pixel(0, 1);
+            }
+            if ( tails ) {
+                watch_clear_pixel(0, 19);
+                watch_clear_pixel(2, 19);
+                watch_set_pixel(1, 17);
+                watch_set_pixel(0, 20);
+            }
+            break;
+        case 18:
+            if ( heads ) {
+                watch_clear_pixel(2, 0);
+                watch_clear_pixel(1, 0);
+            }
+            if ( tails ) {
+                watch_set_pixel(2, 20);
+                watch_set_pixel(0, 21);
+            }
+            break;
+        case 19:
+            if ( heads ) {
+                watch_clear_pixel(2, 1);
+                watch_clear_pixel(0, 0);
+            }
+            if ( tails ) {
+                watch_set_pixel(1, 21);
+                watch_set_pixel(2, 21);
+            }
+            break;
+        case 20:
+            if ( heads ) {
+                watch_set_pixel(2, 1);
+                watch_set_pixel(0, 0);
+            }
+            if ( tails ) {
+                watch_clear_pixel(1, 17);
+                watch_clear_pixel(0, 20);
+            }
+            break;
+        case 21:
+            if ( heads ) {
+                watch_set_pixel(2, 0);
+                watch_set_pixel(1, 0);
+            }
+            if ( tails ) {
+                watch_clear_pixel(2, 20);
+                watch_clear_pixel(0, 21);
+            }
+            break;
+        case 22:
+            if ( heads ) {
+                watch_clear_pixel(2, 10);
+                watch_clear_pixel(0, 1);
+            }
+            if ( tails ) {
+                watch_clear_pixel(1, 21);
+                watch_clear_pixel(2, 21);
+                watch_set_pixel(1, 22);
+                watch_set_pixel(2, 22);
+            }
+            break;
+        case 23:
+            if ( heads ) {
+                watch_clear_pixel(2, 1);
+                watch_clear_pixel(0, 0);
+            }
+            if ( tails ) {
+                watch_set_pixel(0, 22);
+            }
+            break;
+        case 24:
+            if ( heads ) {
+                watch_set_pixel(2, 23);
+                watch_set_pixel(0, 23);
+                watch_clear_pixel(2, 0);
+                watch_clear_pixel(1, 0);
+            }
+            if ( tails ) {
+                watch_set_pixel(2, 23);
+                watch_set_pixel(0, 23);
+            }
+            break;
+        case 25:
+            if ( heads ) {
+                watch_set_pixel(0, 22);
+            }
+            if ( tails ) {
+                watch_clear_pixel(1, 22);
+                watch_clear_pixel(2, 22);
+            }
+            break;
+        case 26:
+            if ( heads ) {
+                watch_set_pixel(1, 22);
+                watch_set_pixel(2, 22);
+            }
+            if ( tails ) {
+                watch_clear_pixel(0, 22);
+            }
+            break;
+        case 27:
+            if ( heads ) {
+                watch_clear_pixel(2, 23);
+                watch_clear_pixel(0, 23);
+            }
+            if ( tails ) {
+                watch_clear_pixel(2, 23);
+                watch_clear_pixel(0, 23);
+                watch_set_pixel(2, 0);
+                watch_set_pixel(1, 0);
+            }
+            break;
+        case 28:
+            if ( heads ) {
+                watch_clear_pixel(0, 22);
+            }
+            if ( tails ) {
+                watch_set_pixel(2, 1);
+                watch_set_pixel(0, 0);
+            }
+            break;
+        case 29:
+            if ( heads ) {
+                watch_set_pixel(1, 21);
+                watch_set_pixel(2, 21);
+                watch_clear_pixel(1, 22);
+                watch_clear_pixel(2, 22);
+            }
+            if ( tails ) {
+                watch_set_pixel(2, 10);
+                watch_set_pixel(0, 1);
+            }
+            break;
+        case 30:
+            if ( heads ) {
+                watch_set_pixel(2, 20);
+                watch_set_pixel(0, 21);
+            }
+            if ( tails ) {
+                watch_clear_pixel(1, 0);
+                watch_clear_pixel(2, 0);
+            }
+            break;
+        case 31:
+            if ( heads ) {
+                watch_set_pixel(1, 17);
+                watch_set_pixel(0, 20);
+            }
+            if ( tails ) {
+                watch_clear_pixel(2, 1);
+                watch_clear_pixel(0, 0);
+            }
+            break;
+        case 32:
+            if ( heads ) {
+                watch_clear_pixel(1, 21);
+                watch_clear_pixel(2, 21);
+            }
+            if ( tails ) {
+                watch_clear_pixel(2, 10);
+                watch_clear_pixel(0, 1);
+                watch_set_pixel(0, 2);
+                watch_set_pixel(1, 2);
+            }
+            break;
+        case 33:
+            if ( heads ) {
+                watch_clear_pixel(2, 20);
+                watch_clear_pixel(0, 21);
+            }
+            if ( tails ) {
+                watch_set_pixel(2, 2);
+                watch_set_pixel(0, 3);
+            }
+            break;
+        case 34:
+            if ( heads ) {
+                watch_set_pixel(0, 19);
+                watch_set_pixel(2, 19);
+                watch_clear_pixel(1, 17);
+                watch_clear_pixel(0, 20);
+            }
+            if ( tails ) {
+                watch_set_pixel(2, 3);
+                watch_set_pixel(0, 4);
+            }
+            break;
+        case 35:
+            if ( heads ) {
+                watch_set_pixel(1, 18);
+            }
+            if ( tails ) {
+                watch_clear_pixel(1, 2);
+                watch_clear_pixel(0, 2);
+            }
+            break;
+        case 36:
+            if ( heads ) {
+                watch_set_pixel(0, 18);
+                watch_set_pixel(2, 18);
+            }
+            if ( tails ) {
+                watch_clear_pixel(2, 2);
+                watch_clear_pixel(0, 3);
+            }
+            break;
+        case 37:
+            if ( heads ) {
+                watch_clear_pixel(0, 19);
+                watch_clear_pixel(2, 19);
+            }
+            if ( tails ) {
+                watch_clear_pixel(2, 3);
+                watch_clear_pixel(0, 4);
+                watch_set_pixel(1, 4);
+                watch_set_pixel(0, 5);
+            }
+            break;
+        case 38:
+            if ( heads ) {
+                watch_clear_pixel(1, 18);
+            }
+            if ( tails ) {
+                watch_set_pixel(2, 4);
+                watch_set_pixel(0, 6);
+            }
+            break;
+        case 39:
+            if ( heads ) {
+                watch_clear_pixel(0, 18);
+                watch_clear_pixel(2, 18);
+            }
+            if ( tails ) {
+                watch_set_pixel(1, 6);
+                watch_set_pixel(2, 5);
+            }
+            state->animate = false;
+            state->animation = 0;
+            movement_request_tick_frequency(1);
+    }
+}
+
+// DICE FUNCTIONS /////////////////////////////////////////////////////////////
+
+/** @brief rolls a dice
+ */
+uint8_t roll_dice(uint8_t sides) {
+    uint8_t bits_needed = 0;
+    uint8_t temp_sides = sides - 1;
+    uint8_t result = 0;
+    while (temp_sides > 0) {
+        bits_needed++; // how many bits do we need to represent this number?
+        temp_sides >>= 1; // Shift right to check the next bit
+    }
+    do {
+        result = 0;
+        for (int i = 0; i < bits_needed; i++) {
+            result <<= 1; // Shift left to make room for the next bit
+            result |= divine_bit(); // Add the next bit to the result
+        }
+    } while ( result > sides -1 );
+    return result + 1; // Add 1 to convert the range from 0 to sides-1 to 1 to sides
+}
+
+/** @brief roll multiple dice and print a char array for displaying them
+ */
+static void _roll_dice_multiple(char* result, uint8_t* dice, uint8_t num_dice) {
+    // initialize the result array to all spaces
+    memset(result, ' ', 6);
+
+    // roll the dice and write the result to the result array
+    for (uint8_t i = 0; i < num_dice-1; i++) {
+        uint8_t dice_result = dice[i];
+        uint8_t tens_digit = dice_result / 10;
+        uint8_t ones_digit = dice_result % 10;
+        result[(i * 2)] = tens_digit == 0 ? ' ' : (char)('0' + tens_digit);
+        result[(i * 2) + 1] = (char)('0' + ones_digit);
+    }
+}
+
+/** @brief dice animation
+ */
+static void _dice_animation(toss_up_state_t *state) {
+    watch_display_string("      ", 4);
+    for (uint8_t i = 0; i < state->dice_num; i++) {
+        watch_display_string("0",i*2 + 5);
+    }
+    movement_request_tick_frequency(16);
+    switch ( state->animation ) {
+        case 0:
+            watch_clear_pixel(1, 17);
+            watch_clear_pixel(0, 0);
+            watch_clear_pixel(1, 6);
+            break;
+        case 1:
+            watch_clear_pixel(2, 20);
+            watch_clear_pixel(1, 0);
+            watch_clear_pixel(0, 6);
+            break;
+        case 2:
+            watch_clear_pixel(2, 21);
+            watch_clear_pixel(2, 0);
+            watch_clear_pixel(0, 5);
+            break;
+        case 3:
+            watch_clear_pixel(1, 21);
+            watch_clear_pixel(2, 1);
+            watch_clear_pixel(1, 4);
+            break;
+        case 4:
+            watch_clear_pixel(0, 21);
+            watch_clear_pixel(2, 10);
+            watch_clear_pixel(2, 4);
+            break;
+        case 5:
+            watch_clear_pixel(0, 20);
+            watch_clear_pixel(0, 1);
+            watch_clear_pixel(2, 5);
+            break;
+        case 6:
+            watch_clear_pixel(1, 17);
+            watch_clear_pixel(0, 0);
+            watch_clear_pixel(1, 6);
+            break;
+        case 7:
+            watch_clear_pixel(2, 20);
+            watch_clear_pixel(1, 0);
+            watch_clear_pixel(0, 6);
+            break;
+        case 8:
+            watch_clear_pixel(2, 21);
+            watch_clear_pixel(2, 0);
+            watch_clear_pixel(0, 5);
+            break;
+        case 9:
+            watch_clear_pixel(1, 21);
+            watch_clear_pixel(2, 1);
+            watch_clear_pixel(1, 4);
+            break;
+        case 10:
+            watch_clear_pixel(0, 21);
+            watch_clear_pixel(2, 10);
+            watch_clear_pixel(2, 4);
+            break;
+        case 11:
+            watch_clear_pixel(0, 20);
+            watch_clear_pixel(0, 1);
+            watch_clear_pixel(2, 5);
+            state->animate = false;
+            state->animation = 0;
+            movement_request_tick_frequency(1);
+    }
+}
\ No newline at end of file
diff --git a/legacy/watch_faces/complication/toss_up_face.h b/legacy/watch_faces/complication/toss_up_face.h
new file mode 100644 (file)
index 0000000..d6a77d7
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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 TOSS_UP_FACE_H_
+#define TOSS_UP_FACE_H_
+
+/*
+ * TOSS UP face
+ * ============
+ *
+ * Playful watch face for games of chance or divination using coins or dice.
+ * 
+ * LIGHT switches between Coins and Dice mode
+ * 
+ * COINS
+ * =====
+ * 
+ * ALARM tosses a coin. If it lands on heads it gets sorted to the left side of the
+ * display, if it lands on tails then sorted to the right side.
+ * 
+ * LONG PRESSING ALARM adds up to 5 more coins to the toss for more nuance in the decision
+ * making (e.g. three heads vs two tails could be read as "yes, but with serious doubts").
+ * 
+ * LONG PRESSING LIGHT flips through additional style for the coins from the default Ө/O 
+ * to H/T (heads/tails), Y/N (yes/no), E/Ǝ, C/Ↄ
+ * 
+ * LONG PRESSING ALARM on the "Coins" title page resets to one coin.
+ * LONG PRESSING LIGHT on the "Coins" title page resets the style to Ө/O
+ * 
+ * DICE
+ * ====
+ * 
+ * ALARM rolls a six sided dice.
+ * 
+ * LONG PRESSING ALARM adds up to 2 more dice to the roll.
+ * 
+ * LONG PRESSING LIGHT flips through other available polyhedral dice types with less or more
+ * than the default 6 sides. The options are D2, D4, D6, D8, D10, D12, D20, D24, D30, D32, D36, 
+ * D48, and a hypothetical D99. 
+ * 
+ * When more than one dice is used for a roll this changes only the last added dice. (see Note
+ * below)
+ *
+ * LONG PRESSING ALARM on the "Dice" title page resets to one dice.
+ * LONG PRESSING LIGHT on the "Dice" title page resets the dice to D6.
+ * 
+ * Please Note: If you need let's say a D8, D12, and D20 for your rolls then the procedure to
+ * set this up would be as follows: from the default screen where you can roll the one D6 dice 
+ * you would LONG PRESS LIGHT a few times to change the D6 to a D8, then LONG PRESS ALARM to add 
+ * a second dice, LONG PRESS LIGHT again until the second dice changes to D12, then LONG PRESS
+ * ALARM to add the third dice and LONG PRESS LIGHT again a few times until it becomes a D20.
+ *
+ */
+
+#include "movement.h"
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint32_t entropy;
+    uint8_t mode : 4; // 1 coin, 2 coins, 3 coins, 4 coins, dice, iching, geomnc
+    bool setup;
+    bool coins[6];
+    uint8_t coin_num : 3;
+    char coin_style[2];
+    uint8_t coinface : 3;
+    uint8_t dice[3];
+    uint8_t dice_num : 2;
+    uint8_t dd : 6;
+    uint8_t dice_sides[3];
+    uint8_t animation;
+    bool animate;
+} toss_up_state_t;
+
+uint32_t get_true_entropy(void);
+uint8_t divine_bit(void);
+uint8_t roll_dice(uint8_t sides);
+void toss_up_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void toss_up_face_activate(void *context);
+bool toss_up_face_loop(movement_event_t event, void *context);
+void toss_up_face_resign(void *context);
+
+#define toss_up_face ((const watch_face_t){ \
+    toss_up_face_setup, \
+    toss_up_face_activate, \
+    toss_up_face_loop, \
+    toss_up_face_resign, \
+    NULL, \
+})
+
+#endif // TOSS_UP_FACE_H_
+
diff --git a/legacy/watch_faces/complication/totp_face.c b/legacy/watch_faces/complication/totp_face.c
new file mode 100644 (file)
index 0000000..c159f59
--- /dev/null
@@ -0,0 +1,243 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * MIT License
+ *
+ * Copyright © 2021 Wesley Ellis (https://github.com/tahnok)
+ * Copyright © 2021-2023 Joey Castillo <joeycastillo@utexas.edu>
+ * Copyright © 2022 Jack Bond-Preston <jackbondpreston@outlook.com>
+ * Copyright © 2023 Alex Utter <ooterness@gmail.com>
+ * Copyright © 2023 Emilien Court <emilien.court@telecomnancy.net>
+ * Copyright © 2023 Jeremy O'Brien <neutral@fastmail.com>
+ * Copyright © 2024 Matheus Afonso Martins Moreira <matheus.a.m.moreira@gmail.com> (https://www.matheusmoreira.com/)
+ * Copyright © 2024 Max Zettlmeißl <max@zettlmeissl.de>
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "totp_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "TOTP.h"
+#include "base32.h"
+
+#ifndef TOTP_FACE_MAX_KEY_LENGTH
+#define TOTP_FACE_MAX_KEY_LENGTH 128
+#endif
+
+typedef struct {
+    unsigned char labels[2];
+    hmac_alg algorithm;
+    uint32_t period;
+    size_t encoded_key_length;
+    unsigned char *encoded_key;
+} totp_t;
+
+#define CREDENTIAL(label, key_array, algo, timestep) \
+    (const totp_t) { \
+        .encoded_key = ((unsigned char *) key_array), \
+        .encoded_key_length = sizeof(key_array) - 1, \
+        .period = (timestep), \
+        .labels = (#label), \
+        .algorithm = (algo), \
+    }
+
+////////////////////////////////////////////////////////////////////////////////
+// Enter your TOTP key data below
+
+static totp_t credentials[] = {
+    CREDENTIAL(2F, "JBSWY3DPEHPK3PXP", SHA1, 30),
+    CREDENTIAL(AC, "JBSWY3DPEHPK3PXP", SHA1, 30),
+};
+
+// END OF KEY DATA.
+////////////////////////////////////////////////////////////////////////////////
+
+static inline totp_t *totp_at(size_t i) {
+    return &credentials[i];
+}
+
+static inline totp_t *totp_current(totp_state_t *totp_state) {
+    return totp_at(totp_state->current_index);
+}
+
+static inline size_t totp_total(void) {
+    return sizeof(credentials) / sizeof(*credentials);
+}
+
+static void totp_validate_key_lengths(void) {
+    for (size_t n = totp_total(), i = 0; i < n; ++i) {
+        totp_t *totp = totp_at(i);
+
+        if (UNBASE32_LEN(totp->encoded_key_length) > TOTP_FACE_MAX_KEY_LENGTH) {
+            // Key exceeds static limits, turn it off by zeroing the length
+            totp->encoded_key_length = 0;
+        }
+    }
+}
+
+static void totp_generate(totp_state_t *totp_state) {
+    totp_t *totp = totp_current(totp_state);
+
+    if (totp->encoded_key_length <= 0) {
+        // Key exceeded static limits and was turned off
+        totp_state->current_decoded_key_length = 0;
+        return;
+    }
+
+    totp_state->current_decoded_key_length = base32_decode(totp->encoded_key, totp_state->current_decoded_key);
+
+    if (totp_state->current_decoded_key_length == 0) {
+        // Decoding failed for some reason
+        // Not a base 32 string?
+        return;
+    }
+
+    TOTP(
+        totp_state->current_decoded_key,
+        totp_state->current_decoded_key_length,
+        totp->period,
+        totp->algorithm
+    );
+}
+
+static void totp_display_error(totp_state_t *totp_state) {
+    char buf[10 + 1];
+    totp_t *totp = totp_current(totp_state);
+
+    snprintf(buf, sizeof(buf), "%c%c  ERROR ", totp->labels[0], totp->labels[1]);
+    watch_display_string(buf, 0);
+}
+
+static void totp_display_code(totp_state_t *totp_state) {
+    char buf[14];
+    div_t result;
+    uint8_t valid_for;
+    totp_t *totp = totp_current(totp_state);
+
+    result = div(totp_state->timestamp, totp->period);
+    if (result.quot != totp_state->steps) {
+        totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
+        totp_state->steps = result.quot;
+    }
+    valid_for = totp->period - result.rem;
+    sprintf(buf, "%c%c%2d%06lu", totp->labels[0], totp->labels[1], valid_for, totp_state->current_code);
+
+    watch_display_string(buf, 0);
+}
+
+static void totp_display(totp_state_t *totp_state) {
+    if (totp_state->current_decoded_key_length > 0) {
+        totp_display_code(totp_state);
+    } else {
+        totp_display_error(totp_state);
+    }
+}
+
+static void totp_generate_and_display(totp_state_t *totp_state) {
+    totp_generate(totp_state);
+    totp_display(totp_state);
+}
+
+static inline uint32_t totp_compute_base_timestamp() {
+    return watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_get_current_timezone_offset());
+}
+
+void totp_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    totp_validate_key_lengths();
+
+    if (*context_ptr == NULL) {
+        totp_state_t *totp = malloc(sizeof(totp_state_t));
+        totp->current_decoded_key = malloc(TOTP_FACE_MAX_KEY_LENGTH);
+        *context_ptr = totp;
+    }
+}
+
+void totp_face_activate(void *context) {
+
+    totp_state_t *totp = (totp_state_t *) context;
+
+    totp->timestamp = totp_compute_base_timestamp();
+    totp->steps = 0;
+    totp->current_code = 0;
+    totp->current_index = 0;
+    totp->current_decoded_key_length = 0;
+    // totp->current_decoded_key is already initialized in setup
+
+    totp_generate_and_display(totp);
+}
+
+bool totp_face_loop(movement_event_t event, void *context) {
+
+    totp_state_t *totp_state = (totp_state_t *) context;
+
+    switch (event.event_type) {
+        case EVENT_TICK:
+            totp_state->timestamp++;
+            // fall through
+        case EVENT_ACTIVATE:
+            totp_display(totp_state);
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if ((size_t)totp_state->current_index + 1 < totp_total()) {
+                totp_state->current_index++;
+            } else {
+                // wrap around to first key
+                totp_state->current_index = 0;
+            }
+
+            totp_generate_and_display(totp_state);
+
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (totp_state->current_index == 0) {
+                // Wrap around to the last credential.
+                totp_state->current_index = totp_total() - 1;
+            } else {
+                totp_state->current_index--;
+            }
+
+            totp_generate_and_display(totp_state);
+
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+        case EVENT_ALARM_LONG_PRESS:
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            movement_illuminate_led();
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void totp_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/totp_face.h b/legacy/watch_faces/complication/totp_face.h
new file mode 100644 (file)
index 0000000..0dc1b8e
--- /dev/null
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * MIT License
+ *
+ * Copyright © 2021 Wesley Ellis (https://github.com/tahnok)
+ * Copyright © 2021-2022 Joey Castillo <joeycastillo@utexas.edu>
+ * Copyright © 2022 Alexsander Akers <me@a2.io>
+ * Copyright © 2022 Jack Bond-Preston <jackbondpreston@outlook.com>
+ * Copyright © 2023 Alex Utter <ooterness@gmail.com>
+ * Copyright © 2024 Matheus Afonso Martins Moreira <matheus.a.m.moreira@gmail.com> (https://www.matheusmoreira.com/)
+ * Copyright © 2024 Max Zettlmeißl <max@zettlmeissl.de>
+ *
+ * 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 TOTP_FACE_H_
+#define TOTP_FACE_H_
+
+/*
+ * TOTP face
+ * Time-based one-time password (TOTP) generator
+ *
+ * Generate one-time passwords often used for two-factor authentication.
+ * The secret key must be set by hand, by editing "totp_face.c".
+ *
+ * Available algorithms:
+ *  o SHA1 (most TOTP codes use this)
+ *  o SHA224
+ *  o SHA256
+ *  o SHA384
+ *  o SHA512
+ *
+ * Instructions:
+ *  o Find your secret key(s).
+ *      o Use https://github.com/susam/mintotp to generate test codes for
+ *        verification
+ *  o Edit global `credentials` variable in "totp_face.c" to configure your
+ *    TOTP credentials. The file includes two examples that you can use as a
+ *    reference. Credentials are added with the `CREDENTIAL` macro in the form
+ *    `CREDENTIAL(label, key, algorithm, timestep)` where:
+ *      o `label` is a 2 character label that is displayed in the weekday digits
+ *        to identify the TOTP credential.
+ *      o `key` is a string with the base32 encoded secret.
+ *      o `algorithm` is one of the supported hashing algorithms listed above.
+ *      o `timestep` is how often the TOTP refreshes in seconds. This is usually
+ *        30 seconds.
+ *
+ * If you have more than one secret key, press ALARM to cycle through them.
+ * Press LIGHT to cycle in the other direction or keep it pressed longer to
+ * activate the light.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint32_t timestamp;
+    uint8_t steps;
+    uint32_t current_code;
+    uint8_t current_index;
+    uint8_t *current_decoded_key;
+    size_t current_decoded_key_length;
+} totp_state_t;
+
+void totp_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void totp_face_activate(void *context);
+bool totp_face_loop(movement_event_t event, void *context);
+void totp_face_resign(void *context);
+
+#define totp_face ((const watch_face_t){ \
+    totp_face_setup, \
+    totp_face_activate, \
+    totp_face_loop, \
+    totp_face_resign, \
+    NULL, \
+})
+
+#endif // TOTP_FACE_H_
diff --git a/legacy/watch_faces/complication/totp_face_lfs.c b/legacy/watch_faces/complication/totp_face_lfs.c
new file mode 100644 (file)
index 0000000..439aca3
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Wesley Ellis (https://github.com/tahnok)
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "TOTP.h"
+#include "base32.h"
+
+#include "watch.h"
+#include "watch_utility.h"
+#include "filesystem.h"
+
+#include "totp_face_lfs.h"
+
+#define MAX_TOTP_RECORDS 30
+#define MAX_TOTP_SECRET_SIZE 128
+#define TOTP_FILE "totp_uris.txt"
+
+const char* TOTP_URI_START = "otpauth://totp/";
+
+struct totp_record {
+    char label[2];
+    hmac_alg algorithm;
+    uint8_t period;
+    uint8_t secret_size;
+
+    union {
+        uint8_t *secret;
+        struct {
+            uint16_t file_secret_offset;
+            uint16_t file_secret_length;
+        };
+    };
+};
+
+/* This is used if we're not storing all the secrets but instead
+ * calculating them on demand. Avoids malloc in normal operation.
+ */
+static uint8_t current_secret[MAX_TOTP_SECRET_SIZE];
+
+static struct totp_record totp_records[MAX_TOTP_RECORDS];
+static uint8_t num_totp_records = 0;
+
+static void init_totp_record(struct totp_record *totp_record) {
+    totp_record->label[0] = 'A';
+    totp_record->label[1] = 'A';
+    totp_record->algorithm = SHA1;
+    totp_record->period = 30;
+    totp_record->secret_size = 0;
+}
+
+static bool totp_face_lfs_read_param(struct totp_record *totp_record, char *param, char *value) {
+    if (!strcmp(param, "issuer")) {
+        if (value[0] == '\0' || value[1] == '\0') {
+            printf("TOTP issuer must be >= 2 chars, got '%s'\n", value);
+            return false;
+        }
+        totp_record->label[0] = value[0];
+        totp_record->label[1] = value[1];
+    } else if (!strcmp(param, "secret")) {
+        totp_record->file_secret_length = strlen(value);
+        if (UNBASE32_LEN(totp_record->file_secret_length) > MAX_TOTP_SECRET_SIZE) {
+            printf("TOTP secret too long: %s\n", value);
+            return false;
+        }
+        totp_record->secret_size = base32_decode((unsigned char *)value, current_secret);
+        if (totp_record->secret_size == 0) {
+            printf("TOTP can't decode secret: %s\n", value);
+            return false;
+        }
+    } else if (!strcmp(param, "digits")) {
+        if (!strcmp(param, "6")) {
+            printf("TOTP got %s, not 6 digits\n", value);
+            return false;
+        }
+    } else if (!strcmp(param, "period")) {
+        totp_record->period = atoi(value);
+        if (totp_record->period == 0) {
+            printf("TOTP invalid period %s\n", value);
+            return false;
+        }
+    } else if (!strcmp(param, "algorithm")) {
+        if (!strcmp(value, "SHA1")) {
+            totp_record->algorithm = SHA1;
+        }
+        else if (!strcmp(value, "SHA224")) {
+            totp_record->algorithm = SHA224;
+        }
+        else if (!strcmp(value, "SHA256")) {
+            totp_record->algorithm = SHA256;
+        }
+        else if (!strcmp(value, "SHA384")) {
+            totp_record->algorithm = SHA384;
+        }
+        else if (!strcmp(value, "SHA512")) {
+            totp_record->algorithm = SHA512;
+        }
+        else {
+            printf("TOTP ignored due to algorithm %s\n", value);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static void totp_face_lfs_read_file(char *filename) {
+    // For 'format' of file, see comment at top.
+    const size_t uri_start_len = strlen(TOTP_URI_START);
+
+    if (!filesystem_file_exists(filename)) {
+        printf("TOTP file error: %s\n", filename);
+        return;
+    }
+
+    char line[256];
+    int32_t offset = 0, old_offset = 0;
+    while (old_offset = offset, filesystem_read_line(filename, line, &offset, 255) && strlen(line)) {
+        if (num_totp_records == MAX_TOTP_RECORDS) {
+            printf("TOTP max records: %d\n", MAX_TOTP_RECORDS);
+            break;
+        }
+
+        // Check that it looks like a URI
+        if (strncmp(TOTP_URI_START, line, uri_start_len)) {
+            printf("TOTP invalid uri start: %s\n", line);
+            continue;
+        }
+
+        // Check that we can find a '?' (to start our parameters)
+        char *param;
+        char *param_saveptr = NULL;
+        char *params = strchr(line + uri_start_len, '?');
+        if (params == NULL) {
+            printf("TOTP no params: %s\n", line);
+            continue;
+        }
+
+        // Process the parameters and put them in the record
+        init_totp_record(&totp_records[num_totp_records]);
+        bool error = false;
+        param = strtok_r(params + 1, "&", &param_saveptr);
+        do {
+            char *param_middle = strchr(param, '=');
+            *param_middle = '\0';
+            if (totp_face_lfs_read_param(&totp_records[num_totp_records], param, param_middle + 1)) {
+                if (!strcmp(param, "secret")) {
+                    totp_records[num_totp_records].file_secret_offset = old_offset + (param_middle + 1 - line);
+                }
+            } else {
+                error = true;
+            }
+        } while ((param = strtok_r(NULL, "&", &param_saveptr)));
+
+        if (error) {
+            totp_records[num_totp_records].secret_size = 0;
+            continue;
+        }
+
+        // If we found a probably valid TOTP record, keep it.
+        if (totp_records[num_totp_records].secret_size) {
+            num_totp_records += 1;
+        } else {
+            printf("TOTP missing secret: %s\n", line);
+        }
+    }
+}
+
+void totp_face_lfs_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(totp_lfs_state_t));
+    }
+
+#if !(__EMSCRIPTEN__)
+    if (num_totp_records == 0) {
+        totp_face_lfs_read_file(TOTP_FILE);
+    }
+#endif
+}
+
+static uint8_t *totp_face_lfs_get_file_secret(struct totp_record *record) {
+    char buffer[BASE32_LEN(MAX_TOTP_SECRET_SIZE) + 1];
+    int32_t file_secret_offset = record->file_secret_offset;
+
+    /* TODO filesystem_read_line is quite inefficient. Consider writing a new function,
+     * and keeping the file open?
+     */
+    if (!filesystem_read_line(TOTP_FILE, buffer, &file_secret_offset, record->file_secret_length + 1)) {
+        /* Shouldn't happen at this point. Return current_secret, which is misleading but will not cause a crash. */
+        printf("TOTP can't read expected secret from totp_uris.txt (failed readline)\n");
+        return current_secret;
+    }
+    if (base32_decode((unsigned char *)buffer, current_secret) != record->secret_size) {
+        printf("TOTP can't properly decode secret '%s' from totp_uris.txt; failed at offset %d; read to %ld\n", buffer, record->file_secret_offset, file_secret_offset);
+    }
+    return current_secret;
+}
+
+static void totp_face_set_record(totp_lfs_state_t *totp_state, int i) {
+    struct totp_record *record;
+
+    if (num_totp_records == 0 && i >= num_totp_records) {
+        return;
+    }
+
+    totp_state->current_index = i;
+    record = &totp_records[i];
+
+    TOTP(
+        totp_face_lfs_get_file_secret(record),
+        record->secret_size,
+        record->period,
+        record->algorithm
+    );
+    totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
+    totp_state->steps = totp_state->timestamp / record->period;
+}
+
+void totp_face_lfs_activate(void *context) {
+    memset(context, 0, sizeof(totp_lfs_state_t));
+    totp_lfs_state_t *totp_state = (totp_lfs_state_t *)context;
+
+#if __EMSCRIPTEN__
+    if (num_totp_records == 0) {
+        // Doing this here rather than in setup makes things a bit more pleasant in the simulator, since there's no easy way to trigger
+        // setup again after uploading the data.
+        totp_face_lfs_read_file(TOTP_FILE);
+    }
+#endif
+
+    totp_state->timestamp = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_get_current_timezone_offset());
+    totp_face_set_record(totp_state, 0);
+}
+
+static void totp_face_display(totp_lfs_state_t *totp_state) {
+    uint8_t index = totp_state->current_index;
+    char buf[14];
+
+    if (num_totp_records == 0) {
+        watch_display_string("No2F Codes", 0);
+        return;
+    }
+
+    div_t result = div(totp_state->timestamp, totp_records[index].period);
+    if (result.quot != totp_state->steps) {
+        totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
+        totp_state->steps = result.quot;
+    }
+    uint8_t valid_for = totp_records[index].period - result.rem;
+
+    sprintf(buf, "%c%c%2d%06lu", totp_records[index].label[0], totp_records[index].label[1], valid_for, totp_state->current_code);
+
+    watch_display_string(buf, 0);
+}
+
+bool totp_face_lfs_loop(movement_event_t event, void *context) {
+
+    totp_lfs_state_t *totp_state = (totp_lfs_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_TICK:
+            totp_state->timestamp++;
+            totp_face_display(totp_state);
+            break;
+        case EVENT_ACTIVATE:
+            totp_face_display(totp_state);
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            totp_face_set_record(totp_state, (totp_state->current_index + 1) % num_totp_records);
+            totp_face_display(totp_state);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            totp_face_set_record(totp_state, (totp_state->current_index + num_totp_records - 1) % num_totp_records);
+            totp_face_display(totp_state);
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+        case EVENT_ALARM_LONG_PRESS:
+        case EVENT_LIGHT_BUTTON_DOWN:
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            movement_illuminate_led();
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void totp_face_lfs_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/complication/totp_face_lfs.h b/legacy/watch_faces/complication/totp_face_lfs.h
new file mode 100644 (file)
index 0000000..16276a2
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Wesley Ellis (https://github.com/tahnok)
+ *
+ * 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 TOTP_FACE_LFS_H_
+#define TOTP_FACE_LFS_H_
+
+/*
+ * TOTP-LFS face
+ * Time-based one-time password (TOTP) generator using LFS
+ *
+ * Reads from a file "totp_uris.txt", containing a single secret key in a
+ * series of URLs. Each line is what's in a QR code, e.g.:
+ *   otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
+ *   otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30
+ *
+ * This is also the same as what Aegis exports in plain-text format.
+ * This face performs minimal sanitisation of input, however, and you
+ * will only see errors if you use the simulator or view the serial console.
+ *
+ * At the moment, to get the records onto the filesystem, start a serial connection and do:
+ *   echo otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example > totp_uris.txt
+ *   echo otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 >> totp_uris.txt
+ * (note the double >> in the second one)
+ *
+ * You may want to customise the characters that appear to identify the 2FA
+ * code. These are just the first two characters of the issuer, and it's fine
+ * to modify the URI.
+ *
+ * If you have more than one secret key, press ALARM to cycle through them.
+ * Press LIGHT to cycle in the other direction or keep it pressed longer to
+ * activate the light.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint32_t timestamp;
+    uint8_t steps;
+    uint32_t current_code;
+    uint8_t current_index;
+} totp_lfs_state_t;
+
+void totp_face_lfs_setup(uint8_t watch_face_index, void ** context_ptr);
+void totp_face_lfs_activate(void *context);
+bool totp_face_lfs_loop(movement_event_t event, void *context);
+void totp_face_lfs_resign(void *context);
+
+#define totp_face_lfs ((const watch_face_t){ \
+    totp_face_lfs_setup, \
+    totp_face_lfs_activate, \
+    totp_face_lfs_loop, \
+    totp_face_lfs_resign, \
+    NULL, \
+})
+
+#endif // TOTP_FACE_LFS_H_
diff --git a/legacy/watch_faces/complication/tuning_tones_face.c b/legacy/watch_faces/complication/tuning_tones_face.c
new file mode 100644 (file)
index 0000000..686de68
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Per Waagø
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "tuning_tones_face.h"
+
+/*
+
+    This face plays a tone that can be used as a reference when tuning
+    musical instrument.
+
+    - The alarm button (short press) starts and stops the tone
+    - The light button (short press) changes which note is played. The name
+    of the note is shown in the display.
+
+*/
+
+typedef struct Note {
+    watch_buzzer_note_t note;
+    char * name;
+} Note;
+
+static Note notes[] = {
+    { .note = BUZZER_NOTE_C5, .name = "C " },
+    { .note = BUZZER_NOTE_C5SHARP_D5FLAT, .name = "Db" },
+    { .note = BUZZER_NOTE_D5, .name = "D " },
+    { .note = BUZZER_NOTE_D5SHARP_E5FLAT, .name = "Eb" },
+    { .note = BUZZER_NOTE_E5, .name = "E " },
+    { .note = BUZZER_NOTE_F5, .name = "F " },
+    { .note = BUZZER_NOTE_F5SHARP_G5FLAT, .name = "Gb" },
+    { .note = BUZZER_NOTE_G5, .name = "G " },
+    { .note = BUZZER_NOTE_G5SHARP_A5FLAT, .name = "Ab" },
+    { .note = BUZZER_NOTE_A5, .name = "A " },
+    { .note = BUZZER_NOTE_A5SHARP_B5FLAT, .name = "Bb" },
+    { .note = BUZZER_NOTE_B5, .name = "B " },
+};
+
+static size_t note_count = sizeof notes / sizeof *notes;
+
+static void draw(tuning_tones_state_t *state)
+{
+    watch_display_string(notes[state->note_ind].name, 8);
+}
+
+void tuning_tones_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        tuning_tones_state_t *state = malloc(sizeof *state);
+        memset(state, 0, sizeof *state);
+        state->note_ind = 9;
+        *context_ptr = state;
+    }
+}
+
+void tuning_tones_face_activate(void *context) {
+    (void) context;
+}
+
+static void update_buzzer(const tuning_tones_state_t *state)
+{
+    if (state->playing) {
+        watch_set_buzzer_off();
+        watch_set_buzzer_period_and_duty_cycle(NotePeriods[notes[state->note_ind].note], 25);
+        watch_set_buzzer_on();
+    }
+}
+
+bool tuning_tones_face_loop(movement_event_t event, void *context) {
+    tuning_tones_state_t *state = (tuning_tones_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            draw(state);
+            break;
+        case EVENT_TICK:
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            state->note_ind++;
+            if (state->note_ind == note_count) {
+                state->note_ind = 0;
+            }
+            update_buzzer(state);
+            draw(state);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+            state->playing = !state->playing;
+            if (!state->playing) {
+                watch_set_buzzer_off();
+            } else {
+                update_buzzer(state);
+            }
+        case EVENT_ALARM_BUTTON_UP:
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return !state->playing;
+}
+
+void tuning_tones_face_resign(void *context) {
+    tuning_tones_state_t *state = (tuning_tones_state_t *)context;
+
+    if (state->playing) {
+        state->playing = false;
+        watch_set_buzzer_off();
+    }
+}
diff --git a/legacy/watch_faces/complication/tuning_tones_face.h b/legacy/watch_faces/complication/tuning_tones_face.h
new file mode 100644 (file)
index 0000000..08672a5
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Per Waagø
+ *
+ * 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 TUNING_TONES_FACE_H_
+#define TUNING_TONES_FACE_H_
+
+#include "movement.h"
+
+/*
+ * A DESCRIPTION OF YOUR WATCH FACE
+ *
+ * and a description of how use it
+ *
+ */
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    bool playing;
+    size_t note_ind;
+} tuning_tones_state_t;
+
+void tuning_tones_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void tuning_tones_face_activate(void *context);
+bool tuning_tones_face_loop(movement_event_t event, void *context);
+void tuning_tones_face_resign(void *context);
+
+#define tuning_tones_face ((const watch_face_t){ \
+    tuning_tones_face_setup, \
+    tuning_tones_face_activate, \
+    tuning_tones_face_loop, \
+    tuning_tones_face_resign, \
+    NULL, \
+})
+
+#endif // TUNING_TONES_FACE_H_
+
diff --git a/legacy/watch_faces/complication/wake_face.c b/legacy/watch_faces/complication/wake_face.c
new file mode 100644 (file)
index 0000000..30982da
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Josh Berson, building on Wesley Ellis’ countdown_face.c
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "wake_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+//
+// Private
+//
+
+static
+void _wake_face_update_display(wake_face_state_t *state) {
+    uint8_t hour = state->hour;
+
+    watch_clear_display();
+    if ( movement_clock_mode_24h() )
+        watch_set_indicator(WATCH_INDICATOR_24H);
+    else {
+        if ( hour >= 12 )
+            watch_set_indicator(WATCH_INDICATOR_PM);
+        hour = hour % 12 ? hour % 12 : 12;
+    }
+
+    if ( state->mode )
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+
+    static char lcdbuf[11];
+    sprintf(lcdbuf, "WA  %2d%02d  ", hour, state->minute);
+
+    watch_set_colon();
+    watch_display_string(lcdbuf, 0);
+}
+
+//
+// Exported
+//
+
+void wake_face_setup(uint8_t watch_face_index, void **context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(wake_face_state_t));
+        wake_face_state_t *state = (wake_face_state_t *)*context_ptr;
+        memset(*context_ptr, 0, sizeof(wake_face_state_t));
+
+        state->hour = 5;
+        state->minute = 0;
+        state->mode = 0;
+    }
+}
+
+void wake_face_activate(void *context) {
+    (void) context;
+}
+void wake_face_resign(void *context) {
+    (void) context;
+}
+
+movement_watch_face_advisory_t wake_face_advise(void *context) {
+    wake_face_state_t *state = (wake_face_state_t *)context;
+    movement_watch_face_advisory_t retval = { 0 };
+
+    if ( state->mode ) {
+        watch_date_time_t now = watch_rtc_get_date_time();
+        retval.wants_background_task = state->hour==now.unit.hour && state->minute==now.unit.minute;
+        // We’re at the mercy of the advise handler
+        // In Safari, the emulator triggers at the ›end‹ of the minute
+        // Converting to Unix timestamps and taking a difference between now and wake
+        // is not an easy win — because the timestamp for wake has to rely on now
+        // for its date. So first we’d have to see if the TOD of wake is after that
+        // of now. If it is, take tomorrow’s date, calculating month and year rollover
+        // if need be.
+    }
+
+    return retval;
+}
+
+bool wake_face_loop(movement_event_t event, void *context) {
+    wake_face_state_t *state = (wake_face_state_t *)context;
+
+    switch (event.event_type) {
+    case EVENT_ACTIVATE:
+    case EVENT_TICK:
+        _wake_face_update_display(state);
+        break;
+    case EVENT_LIGHT_BUTTON_UP:
+        state->hour = (state->hour + 1) % 24;
+        _wake_face_update_display(state);
+        break;
+    case EVENT_LIGHT_LONG_PRESS:
+        state->hour = (state->hour + 6) % 24;
+        _wake_face_update_display(state);
+        break;
+    case EVENT_ALARM_BUTTON_UP:
+        state->minute = (state->minute + 10) % 60;
+        _wake_face_update_display(state);
+        break;
+    case EVENT_ALARM_LONG_PRESS:
+        state->mode ^= 1;
+        _wake_face_update_display(state);
+        break;
+    case EVENT_BACKGROUND_TASK:
+        movement_play_alarm();
+            // 2022-07-23: Thx @joeycastillo for the dedicated “alarm” signal
+        break;
+    case EVENT_TIMEOUT:
+        movement_move_to_face(0);
+        break;
+    case EVENT_LOW_ENERGY_UPDATE:
+        break;
+    case EVENT_LIGHT_BUTTON_DOWN:
+        // don't light up every time light is hit
+        break;
+    default:
+        movement_default_loop_handler(event);
+        break;
+    }
+
+    return true;
+}
diff --git a/legacy/watch_faces/complication/wake_face.h b/legacy/watch_faces/complication/wake_face.h
new file mode 100644 (file)
index 0000000..008a540
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Josh Berson
+ *
+ * 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 WAKE_FACE_H_
+#define WAKE_FACE_H_
+
+/*
+ * WAKE daily alarm face
+ *
+ * Basic daily alarm clock face. Seems useful if nothing else in the interest
+ * of feature parity with the F-91W’s OEM module, 593.
+ *
+ * Also experiments with caret-free UI: One button cycles hours, the other
+ * minutes, so there’s no toggling between display and adjust modes and no
+ * cycling the caret through the UI.
+ *   º LIGHT advances hour by 1
+ *   º LIGHT long press advances hour by 6
+ *   º ALARM advances minute by 10
+ *   º ALARM long press cycles through signal modes (just one at the moment)
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint32_t hour : 5;
+    uint32_t minute : 6;
+    uint32_t mode : 1;
+} wake_face_state_t;
+
+void wake_face_setup(uint8_t watch_face_index, void **context_ptr);
+void wake_face_activate(void *context);
+bool wake_face_loop(movement_event_t event, void *context);
+void wake_face_resign(void *context);
+movement_watch_face_advisory_t wake_face_advise(void *context);
+
+#define wake_face ((const watch_face_t){ \
+    wake_face_setup, \
+    wake_face_activate, \
+    wake_face_loop, \
+    wake_face_resign, \
+    wake_face_advise \
+})
+
+#endif // WAKE_FACE_H_
+
diff --git a/legacy/watch_faces/complication/wareki_face.c b/legacy/watch_faces/complication/wareki_face.c
new file mode 100644 (file)
index 0000000..ec87659
--- /dev/null
@@ -0,0 +1,218 @@
+#include <stdlib.h>
+#include <string.h>
+#include "wareki_face.h"
+#include "filesystem.h"
+#include "watch_utility.h"
+
+
+//Long press status flag
+static bool _alarm_button_press;
+static bool _light_button_press;
+
+
+void wareki_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    
+    //printf("wareki_setup() \n");
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(wareki_state_t));
+        memset(*context_ptr, 0, sizeof(wareki_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+
+}
+
+
+// splash view
+static void draw_wareki_splash(wareki_state_t *state) {    
+    (void) state;
+    char buf[11];
+    
+    watch_clear_colon();
+
+    sprintf(buf, "%s","wa  ------");
+    
+    watch_display_string(buf, 0);
+}
+
+
+//draw year and Japanese wareki
+static void draw_year_and_wareki(wareki_state_t *state) {
+    char buf[27];
+    
+    if(state->disp_year < REIWA_GANNEN){
+        //Heisei
+        sprintf(buf, " h%2d%4d  ", (int)state->disp_year - HEISEI_GANNEN + 1, (int)state->disp_year);
+    }
+    else{
+        //Reiwa
+        sprintf(buf, " r%2d%4d  ", (int)state->disp_year - REIWA_GANNEN + 1 , (int)state->disp_year);
+    }
+    watch_display_string(buf, 0);
+}
+
+
+void wareki_activate(void *context) {
+
+    //printf("wareki_activate() \n");
+
+    wareki_state_t *state = (wareki_state_t *)context;
+
+    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
+
+    state->active = false;
+
+    _alarm_button_press = false;
+    _light_button_press = false;
+
+    state->real_year = watch_rtc_get_date_time().unit.year + WATCH_RTC_REFERENCE_YEAR;
+    state->start_year = state->real_year;
+    state->disp_year = state->real_year;
+
+    movement_request_tick_frequency(1);
+}
+
+
+void addYear(wareki_state_t* state,int count){
+
+    state->disp_year = state->disp_year + count;
+
+    if(state->disp_year > REIWA_LIMIT ){
+        state->disp_year = REIWA_LIMIT;
+    } 
+    else{
+        //watch_buzzer_play_note(BUZZER_NOTE_C8, 30);        
+    }
+}
+
+
+void subYear(wareki_state_t* state,int count){
+
+    state->disp_year = state->disp_year - count;
+
+    if(state->disp_year < 1989 ){
+        state->disp_year = 1989;
+    } 
+    else{
+        //watch_buzzer_play_note(BUZZER_NOTE_C7, 30);
+    }
+}
+
+
+bool wareki_loop(movement_event_t event, void *context) {
+    wareki_state_t *state = (wareki_state_t *)context;
+
+    state->real_year = watch_rtc_get_date_time().unit.year + WATCH_RTC_REFERENCE_YEAR;
+
+    if( state->real_year != state->start_year ){
+        state->start_year = state->real_year;
+        state->disp_year = state->real_year;
+    }
+
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            draw_wareki_splash(state);
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            movement_move_to_next_face();
+            break;
+
+        case EVENT_LOW_ENERGY_UPDATE:
+        case EVENT_TICK:          
+
+            //printf("tick %d\n",state->disp_year );
+
+            if (_alarm_button_press && HAL_GPIO_BTN_ALARM_read()){
+                //printf("ALARM ON\n");
+            }
+            else{
+                //printf("ALARM OFF\n");
+                _alarm_button_press = false;
+            }
+
+            if (_light_button_press && HAL_GPIO_BTN_LIGHT_read()){
+                //printf("LIGHT ON\n");
+            }
+            else{
+                //printf("LIGHT OFF\n");
+                _light_button_press = false;
+            }
+
+            if (_alarm_button_press) {
+                addYear(state,1);
+            }
+            if (_light_button_press) {
+                subYear(state,1);
+            }
+            
+            draw_year_and_wareki(state);
+
+            break;
+
+        case EVENT_LIGHT_BUTTON_DOWN:
+            //printf("LIGHT DOWN\n");
+            subYear(state,1);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            //printf("LIGHTPRESS \n"); 
+            _light_button_press = true;
+            movement_request_tick_frequency(8);
+            break;
+        case EVENT_LIGHT_LONG_UP:
+            //printf("LIGHTPRESS UP\n");
+            _light_button_press = false;
+            movement_request_tick_frequency(4);
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            //printf("LIGHT UP\n");
+            _light_button_press = false;
+            movement_request_tick_frequency(4);
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+            //printf("ALARM DOWN\n");
+            addYear(state,1);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            //printf("LONGPRESS \n");
+            _alarm_button_press = true;
+            movement_request_tick_frequency(8);
+            break;
+        case EVENT_ALARM_LONG_UP:
+            //printf("LONGPRESS UP\n");
+            _alarm_button_press = false;
+            movement_request_tick_frequency(4);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            //printf("ALARM UP\n");
+            movement_request_tick_frequency(4);
+            break;
+
+        case EVENT_TIMEOUT:
+            //printf("time out ! \n");
+            movement_move_to_face(0);
+
+
+            break;
+        //case EVENT_LOW_ENERGY_UPDATE:
+            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
+            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
+            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
+            // watch_start_sleep_animation(500);
+            //break;
+        default:
+            // Movement's default loop handler will step in for any cases you don't handle above:
+            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
+            // You can override any of these behaviors by adding a case for these events to this switch statement.
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void wareki_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/complication/wareki_face.h b/legacy/watch_faces/complication/wareki_face.h
new file mode 100644 (file)
index 0000000..4dffc46
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef WAREKI_FACE_H_
+#define WAREKI_FACE_H_
+
+#include "movement.h"
+
+#define REIWA_LIMIT 2018 + 31
+#define REIWA_GANNEN  2019
+#define HEISEI_GANNEN 1989
+
+typedef struct {
+    bool active;
+    uint32_t disp_year;     //Current displayed year 
+    uint32_t start_year;    //Year when this screen was launched
+    uint32_t real_year;     //The actual current year     
+} wareki_state_t;
+
+
+void wareki_setup(uint8_t watch_face_index, void ** context_ptr);
+void wareki_activate(void *context);
+bool wareki_loop(movement_event_t event, void *context);
+void wareki_resign(void *context);
+void addYear(wareki_state_t* state,int count);
+void subYear(wareki_state_t* state,int count);
+
+#define wareki_face ((const watch_face_t){ \
+    wareki_setup, \
+    wareki_activate, \
+    wareki_loop, \
+    wareki_resign, \
+    NULL, \
+})
+
+#endif // WAREKI_FACE_H_
+
diff --git a/legacy/watch_faces/complication/wordle_face.c b/legacy/watch_faces/complication/wordle_face.c
new file mode 100644 (file)
index 0000000..a93a0e0
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 <David Volovskiy>
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "wordle_face.h"
+#include "watch_utility.h"
+
+static uint32_t get_random(uint32_t max) {
+    #if __EMSCRIPTEN__
+    return rand() % max;
+    #else
+    return arc4random_uniform(max);
+    #endif
+}
+
+static uint8_t get_first_pos(WordleLetterResult *word_elements_result) {
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        if (word_elements_result[i] != WORDLE_LETTER_CORRECT)
+            return i;
+    }
+    return 0;
+}
+
+static uint8_t get_next_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) {
+    for (size_t pos = curr_pos; pos < WORDLE_LENGTH;) {
+        if (word_elements_result[++pos] != WORDLE_LETTER_CORRECT)
+            return pos;
+    }
+    return WORDLE_LENGTH;
+}
+
+static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) {
+    if (curr_pos == 0) return 0;
+    for (int8_t pos = curr_pos; pos >= 0;) {
+        if (word_elements_result[--pos] != WORDLE_LETTER_CORRECT)
+            return pos;
+    }
+    return curr_pos;
+}
+
+static void get_next_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters, const bool skip_wrong_letter) {
+    do {
+        if (word_elements[curr_pos] >= WORDLE_NUM_VALID_LETTERS) word_elements[curr_pos] = 0;
+        else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % WORDLE_NUM_VALID_LETTERS;
+    } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]);  
+}
+
+static void get_prev_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters, const bool skip_wrong_letter) {
+    do {
+        if (word_elements[curr_pos] >= WORDLE_NUM_VALID_LETTERS) word_elements[curr_pos] = WORDLE_NUM_VALID_LETTERS - 1;
+        else word_elements[curr_pos] = (word_elements[curr_pos] + WORDLE_NUM_VALID_LETTERS - 1) % WORDLE_NUM_VALID_LETTERS;
+    } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]);  
+}
+
+static void display_letter(wordle_state_t *state, bool display_dash) {
+    char buf[1 + 1];
+    if (state->word_elements[state->position] >= WORDLE_NUM_VALID_LETTERS) {
+        if (display_dash)
+            watch_display_string("-", state->position + 5);
+        else
+            watch_display_string(" ", state->position + 5);
+        return;
+    }
+    sprintf(buf, "%c", _valid_letters[state->word_elements[state->position]]);
+    watch_display_string(buf, state->position + 5);
+}
+
+static void display_all_letters(wordle_state_t *state) {
+    uint8_t prev_pos = state->position;
+    watch_display_string(" ", 4);
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        state->position = i;
+        display_letter(state, false);
+    }
+    state->position = prev_pos;
+}
+
+#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
+static void display_not_in_dict(wordle_state_t *state) {
+    state->curr_screen = SCREEN_NO_DICT;
+    watch_display_string("nodict", 4);
+}
+
+static void display_already_guessed(wordle_state_t *state) {
+    state->curr_screen = SCREEN_ALREADY_GUESSED;
+    watch_display_string("GUESSD", 4);
+}
+
+static uint32_t check_word_in_dict(uint8_t *word_elements) {
+    bool is_exact_match;
+    for (uint16_t i = 0; i < WORDLE_NUM_WORDS; i++) {
+        is_exact_match = true;
+        for (size_t j = 0; j < WORDLE_LENGTH; j++) {
+            if (_valid_letters[word_elements[j]] != _valid_words[i][j]) {
+                is_exact_match = false;
+                break;  
+            }
+        }
+        if (is_exact_match) return i;
+    }
+    for (uint16_t i = 0; i < WORDLE_NUM_POSSIBLE_WORDS; i++) {
+        is_exact_match = true;
+        for (size_t j = 0; j < WORDLE_LENGTH; j++) {
+            if (_valid_letters[word_elements[j]] != _possible_words[i][j]) {
+                is_exact_match = false;
+                break;  
+            }
+        }
+        if (is_exact_match) return WORDLE_NUM_WORDS + i;
+    }
+    return WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS;
+}
+#endif
+
+static bool check_word(wordle_state_t *state) {
+    // Exact
+    bool is_exact_match = true;
+    bool answer_already_accounted[WORDLE_LENGTH] = { false };
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        if (_valid_letters[state->word_elements[i]] == _valid_words[state->curr_answer][i]) {
+            state->word_elements_result[i] = WORDLE_LETTER_CORRECT;
+            answer_already_accounted[i] = true;
+        }
+        else {
+            state->word_elements_result[i] = WORDLE_LETTER_WRONG;
+            is_exact_match = false;
+        }
+    }
+    if (is_exact_match) return true;
+    // Wrong Location
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        if (state->word_elements_result[i] != WORDLE_LETTER_WRONG) continue;
+        for (size_t j = 0; j < WORDLE_LENGTH; j++) {
+            if (answer_already_accounted[j]) continue;
+            if (_valid_letters[state->word_elements[i]] == _valid_words[state->curr_answer][j]) {
+                state->word_elements_result[i] = WORDLE_LETTER_WRONG_LOC;
+                answer_already_accounted[j] = true;
+                break;
+            }
+        }
+    }
+    return false;
+}
+
+static void show_skip_wrong_letter_indicator(bool skipping, WordleScreen curr_screen) {
+    if (curr_screen >= SCREEN_PLAYING) return;
+    if (skipping)
+        watch_display_string("H", 3);
+    else
+        watch_display_string(" ", 3);
+}
+
+static void update_known_wrong_letters(wordle_state_t *state) {
+    bool wrong_loc[WORDLE_NUM_VALID_LETTERS] = {false};
+    // To ignore letters that appear, but are in the wrong location, as letters that are guessed
+    // more often than they appear in the word will display as WORDLE_LETTER_WRONG
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        if (state->word_elements_result[i] == WORDLE_LETTER_WRONG_LOC) {
+            for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) {
+                if (state->word_elements[i] == j)
+                    wrong_loc[j] = true;
+            }
+        }
+    }
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        if (state->word_elements_result[i] == WORDLE_LETTER_WRONG) {
+            for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) {
+                if (state->word_elements[i] == j && !wrong_loc[j])
+                    state->known_wrong_letters[j] = true;
+            }
+        }
+    }
+}
+
+static void display_attempt(uint8_t attempt) {
+    char buf[3];
+    sprintf(buf, "%d", attempt+1);
+    watch_display_string(buf, 3);
+}
+
+static void display_playing(wordle_state_t *state) {
+    state->curr_screen = SCREEN_PLAYING;
+    display_attempt(state->attempt);
+    display_all_letters(state);
+}
+
+static void reset_all_elements(wordle_state_t *state) {
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        state->word_elements[i] = WORDLE_NUM_VALID_LETTERS;
+        state->word_elements_result[i] = WORDLE_LETTER_WRONG;
+    }
+    for (size_t i = 0; i < WORDLE_NUM_VALID_LETTERS; i++){
+        state->known_wrong_letters[i] = false;
+    }
+#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
+    for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) {
+        state->guessed_words[i] = WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS;
+    }
+#endif
+    state->using_random_guess = false;
+    state->attempt = 0;
+}
+
+static void reset_incorrect_elements(wordle_state_t *state) {
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT)
+            state->word_elements[i] = WORDLE_NUM_VALID_LETTERS;
+    }
+}
+
+static void reset_board(wordle_state_t *state) {
+    reset_all_elements(state);
+    state->curr_answer = get_random(WORDLE_NUM_WORDS);
+    watch_clear_colon();
+    state->position = get_first_pos(state->word_elements_result);
+    display_playing(state);
+    watch_display_string(" -", 4);
+#if __EMSCRIPTEN__
+    printf("ANSWER: %s\r\n", _valid_words[state->curr_answer]);
+#endif
+}
+
+static void display_title(wordle_state_t *state) {
+    state->curr_screen = SCREEN_TITLE;
+    watch_display_string("WO  WordLE", 0);
+    show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
+}
+
+#if WORDLE_USE_DAILY_STREAK != 2
+static void display_continue_result(bool continuing) {
+    watch_display_string(continuing ? "y" : "n", 9);
+}
+
+static void display_continue(wordle_state_t *state) {
+    state->curr_screen = SCREEN_CONTINUE;
+    watch_display_string("Cont ", 4);
+    show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
+    display_continue_result(state->continuing);
+}
+#endif
+
+static void display_streak(wordle_state_t *state) {
+    char buf[12];
+    state->curr_screen = SCREEN_STREAK;
+#if WORDLE_USE_DAILY_STREAK == 2
+    if (state->streak > 99)
+        sprintf(buf, "WO  St--dy");
+    else
+        sprintf(buf, "WO  St%2ddy", state->streak);
+#else
+    sprintf(buf, "WO  St%4d", state->streak);
+#endif
+    watch_display_string(buf, 0);
+    watch_set_colon();
+    show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
+}
+
+#if WORDLE_USE_DAILY_STREAK == 2
+static void display_wait(wordle_state_t *state) {
+    state->curr_screen = SCREEN_WAIT;
+    if (state->streak < 40) {
+        char buf[13];
+        sprintf(buf,"WO%2d WaIt ", state->streak);
+        watch_display_string(buf, 0);
+    }
+    else {  // Streak too long to display in top-right
+        watch_display_string("WO   WaIt ", 0);
+    }
+    show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
+}
+#endif
+
+static uint32_t get_day_unix_time(void) {
+    watch_date_time_t now = watch_rtc_get_date_time();
+#if WORDLE_USE_DAILY_STREAK == 2
+    now.unit.hour = now.unit.minute = now.unit.second = 0;
+#endif
+    return watch_utility_date_time_to_unix_time(now, 0);
+}
+
+static void display_lose(wordle_state_t *state, uint8_t subsecond) {
+    char buf[WORDLE_LENGTH + 6];
+    sprintf(buf," L   %s", subsecond % 2 ? _valid_words[state->curr_answer] : "     ");
+    watch_display_string(buf, 0);
+}
+
+static void display_win(wordle_state_t *state, uint8_t subsecond) {
+    (void) state;
+    char buf[13];
+    sprintf(buf," W   %s  ", subsecond % 2 ? "NICE" : "JOb ");
+    watch_display_string(buf, 0);
+}
+
+static bool is_playing(const wordle_state_t *state) {
+    if (state->attempt > 0) return true;
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        if (state->word_elements[i] != WORDLE_NUM_VALID_LETTERS)
+            return true;
+    }
+    return false;
+}
+
+static void display_result(wordle_state_t *state, uint8_t subsecond) {
+    char buf[WORDLE_LENGTH + 1];
+    for (size_t i = 0; i < WORDLE_LENGTH; i++)
+    {
+        switch (state->word_elements_result[i])
+        {
+        case WORDLE_LETTER_WRONG:
+            buf[i] = '-';
+            break;
+        case WORDLE_LETTER_CORRECT:
+            buf[i] = _valid_letters[state->word_elements[i]];
+            break;
+        case WORDLE_LETTER_WRONG_LOC:
+            if (subsecond % 2)
+                buf[i] = ' ';
+            else
+               buf[i] = _valid_letters[state->word_elements[i]]; 
+        default:
+            break;
+        }
+    }
+    watch_display_string(buf, 5);
+}
+
+static bool act_on_btn(wordle_state_t *state, const uint8_t pin) {
+    switch (state->curr_screen)
+    {
+    case SCREEN_RESULT:
+        reset_incorrect_elements(state);
+        state->position = get_first_pos(state->word_elements_result);
+        display_playing(state);
+        return true;
+    case SCREEN_TITLE:
+#if WORDLE_USE_DAILY_STREAK == 2
+        if (state->day_last_game_started == get_day_unix_time()) {
+            display_wait(state);
+        }
+        else if (is_playing(state))
+            display_playing(state);
+        else
+            display_streak(state);
+#else
+        if (is_playing(state)) {
+            state->continuing = true;
+            display_continue(state);
+        }
+        else
+            display_streak(state);
+#endif
+        return true;
+    case SCREEN_STREAK:
+        state->day_last_game_started = get_day_unix_time();
+        reset_board(state);
+        return true;
+    case SCREEN_WIN:
+    case SCREEN_LOSE:
+        display_title(state);
+        return true;
+    case SCREEN_NO_DICT:
+    case SCREEN_ALREADY_GUESSED:
+        state->position = get_first_pos(state->word_elements_result);
+        display_playing(state);
+        return true;
+#if WORDLE_USE_DAILY_STREAK == 2
+    case SCREEN_WAIT:
+        (void) pin;
+        display_title(state);
+        return true;
+#else
+    case SCREEN_CONTINUE:
+        switch (pin)
+        {
+        case BTN_ALARM:
+            if (state->continuing)
+                display_playing(state);
+            else {
+                reset_board(state);
+                state->streak = 0;
+                display_streak(state);
+            }
+            break;
+        case BTN_LIGHT:
+            state->continuing = !state->continuing;
+            display_continue_result(state->continuing);
+            break;
+        }
+        return true;
+#endif
+    default:
+        return false;
+    }
+    return false;
+}
+
+static void get_result(wordle_state_t *state) {
+#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
+    // Check if it's in the dict
+    uint16_t in_dict = check_word_in_dict(state->word_elements);
+    if (in_dict == WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS) {
+        display_not_in_dict(state);
+        return;
+    }
+
+    // Check if already guessed
+    for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) {
+        if(in_dict == state->guessed_words[i]) {
+            display_already_guessed(state);
+            return;
+        }
+    }
+
+    state->guessed_words[state->attempt] = in_dict;
+#endif
+    bool exact_match = check_word(state);
+    if (exact_match) {
+        reset_all_elements(state);
+        state->curr_screen = SCREEN_WIN;
+        if (state->streak < 0x7F)
+            state->streak++;
+#if WORDLE_USE_DAILY_STREAK == 2
+        state->day_last_game_started = get_day_unix_time();  // On the edge-case where we solve the puzzle at midnight
+#endif
+        return;
+    }
+    if (++state->attempt >= WORDLE_MAX_ATTEMPTS) {
+        reset_all_elements(state);
+        state->curr_screen = SCREEN_LOSE;
+        state->streak = 0;
+        return;
+    }
+    update_known_wrong_letters(state);
+    state->curr_screen = SCREEN_RESULT;
+    return;
+}
+
+#if (WORDLE_USE_RANDOM_GUESS != 0)
+static void insert_random_guess(wordle_state_t *state) {
+    uint16_t random_guess;
+    do {  // Don't allow the guess to be the same as the answer
+        random_guess = get_random(_num_random_guess_words);
+    } while (random_guess == state->curr_answer); 
+    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
+        for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++)
+        {
+            if (_valid_words[random_guess][i] == _valid_letters[j])
+                state->word_elements[i] = j;     
+        }
+    } 
+    state->position = WORDLE_LENGTH - 1;
+    display_all_letters(state);
+    state->using_random_guess = true;
+}
+#endif
+
+void wordle_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(wordle_state_t));
+        memset(*context_ptr, 0, sizeof(wordle_state_t));
+        wordle_state_t *state = (wordle_state_t *)*context_ptr;
+        state->curr_screen = SCREEN_TITLE;
+        state->skip_wrong_letter = false;
+        reset_all_elements(state);
+    }
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void wordle_face_activate(void *context) {
+    wordle_state_t *state = (wordle_state_t *)context;
+#if WORDLE_USE_DAILY_STREAK != 0
+    uint32_t now = get_day_unix_time();
+    uint32_t one_day = 60 *60 * 24;
+    if ((WORDLE_USE_DAILY_STREAK == 2 && now >= (state->day_last_game_started + (2*one_day)))
+        || (now >= (state->day_last_game_started + one_day) && is_playing(state))) {
+        state->streak = 0;
+        reset_board(state);
+    }
+#endif
+    state->using_random_guess = false;
+    if (is_playing(state) && state->curr_screen >= SCREEN_RESULT) {
+        reset_incorrect_elements(state);
+        state->position = get_first_pos(state->word_elements_result); 
+    }
+    movement_request_tick_frequency(2);
+    display_title(state);
+}
+
+bool wordle_face_loop(movement_event_t event, void *context) {
+    wordle_state_t *state = (wordle_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_TICK:
+            switch (state->curr_screen)
+            {
+            case SCREEN_PLAYING:
+                if (event.subsecond % 2) {
+                    display_letter(state, true);
+                } else {
+                    watch_display_string(" ", state->position + 5);
+                }
+                break;
+            case SCREEN_RESULT:
+                display_result(state, event.subsecond);
+                break;
+            case SCREEN_LOSE:
+                display_lose(state, event.subsecond);
+                break;
+            case SCREEN_WIN:
+                display_win(state, event.subsecond);
+                break;
+            default:
+                break;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (act_on_btn(state, BTN_LIGHT)) break;
+            get_next_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter);
+            display_letter(state, true);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (state->curr_screen < SCREEN_PLAYING) {
+                state->skip_wrong_letter = !state->skip_wrong_letter;
+                show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
+                break;
+            }
+            if (state->curr_screen != SCREEN_PLAYING) break;
+            get_prev_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter);
+            display_letter(state, true);
+            break; 
+        case EVENT_ALARM_BUTTON_UP:
+            if (act_on_btn(state, BTN_ALARM)) break;
+            display_letter(state, true);
+            if (state->word_elements[state->position] == WORDLE_NUM_VALID_LETTERS) break;
+#if (WORDLE_USE_RANDOM_GUESS != 0)
+            if (HAL_GPIO_BTN_LIGHT_read() &&
+            (state->using_random_guess || (state->attempt == 0 && state->position == 0))) {
+                insert_random_guess(state);
+                break;
+            }
+#endif
+            state->position = get_next_pos(state->position, state->word_elements_result);
+            if (state->position >= WORDLE_LENGTH) {
+                get_result(state);
+                state->using_random_guess = false;
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->curr_screen != SCREEN_PLAYING) break;
+            display_letter(state, true);
+            state->position = get_prev_pos(state->position, state->word_elements_result);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+        case EVENT_ACTIVATE:
+            break;
+        case EVENT_TIMEOUT:
+            if (state->curr_screen >= SCREEN_RESULT) {
+                reset_incorrect_elements(state);
+                state->position = get_first_pos(state->word_elements_result); 
+                display_title(state);
+            }
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            if (state->curr_screen != SCREEN_TITLE)
+                display_title(state);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+    return true;
+}
+
+void wordle_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/complication/wordle_face.h b/legacy/watch_faces/complication/wordle_face.h
new file mode 100644 (file)
index 0000000..5cc2f09
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 <David Volovskiy>
+ *
+ * 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 WORDLE_FACE_H_
+#define WORDLE_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Wordle Face
+ * A port of NY Times' Wordle game (https://www.nytimes.com/games/wordle/index.html)
+ * A random 5 letter word is chosen and you have WORDLE_MAX_ATTEMPTS attempts to guess it.
+ * Each guess must be a valid 5-letter word found in _legal_words in the C file.
+ * The only letters used are _valid_letters, also found in the C file.
+ * After a guess, the letters in the correct spot will remain, 
+ * and the letters found in the word, but in the incorrect spot will blink.
+ * The screen after the title screen if a new game is started shows the streak of games won in a row.
+ * 
+ * If WORDLE_USE_DAILY_STREAK is set to True, then the game can only be played once per day,
+ *                  and the streak resets to 0 if a day goes by without playing the game.
+ * 
+ * Controls:
+ *    Light Press
+ *        If Playing: Next letter
+ *        Else: Next screen
+ *    Light Hold
+ *        If Playing: Previous letter
+ *        Else: Toggle Hard-Mode. This is skipping over letters that have been confirmed
+ *              to not be in the word (indicated via 'H' in the top-right)
+ *
+ *    Alarm Press
+ *        If Playing: If WORDLE_USE_RANDOM_GUESS is set and Light btn held and 
+ *                    (on first letter or already used a random guess) 
+ *                    and first attempt: Use a random 5 letter word with all letters that are different.
+ *                    Else: Next position
+ *        Else: Next screen
+ *    Alarm Hold
+ *        If Playing: Previous position
+ *        Else: None
+ * 
+ * Note: Actual Hard Mode in Wordle game is "Any revealed hints must be used in subsequent guesses"
+ *       But that came off as clunky UX on the Casio. So instead it only removes unused letters from the keyboard
+ *       as that also simplifies the keyboard.
+ */
+
+#define WORDLE_LENGTH 5
+#define WORDLE_MAX_ATTEMPTS 6
+/*  WORDLE_USE_DAILY_STREAK
+ *  0 = Don't ever reset the streak or the puzzle.
+ *  1 = Reset the streak and puzzle 24hrs after starting a puzzle and not finishing it.
+ *      If the last puzzle was started at 8AM, it'll be considered failed at 8AM the next day.
+ *  2 = Reset the streak and puzzle if a puzzle goes unsolved or not started a day after the previous one.
+ *      If the last puzzle was started at 8AM, it'll be considered failed at midnight the next day.
+ *      This will not be the case if the puzzle is started at 8AM, continued at 11:59PM and solved at 12:01AM, the game will let that slide.
+ *      Starting a new game instead of continuing is not allowed in this state.
+*/
+#define WORDLE_USE_DAILY_STREAK 1
+#define WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES false  // This allows non-words to be entered and repeat guesses to be made. It saves ~11.5KB of ROM.
+/*  WORDLE_USE_RANDOM_GUESS
+ *  0 = Don't allow quickly choosing a random quess
+ *  1 = Allow using a random guess of any value that can be an answer
+ *  2 = Allow using a random guess of any value that can be an answer where all of its letters are unique
+ *  3 = Allow using a random guess of any value that can be an answer, and it's considered one of the best initial choices.
+*/
+#define WORDLE_USE_RANDOM_GUESS 2
+#include "wordle_face_dict.h"
+
+#define WORDLE_NUM_WORDS (sizeof(_valid_words) / sizeof(_valid_words[0]))
+#define WORDLE_NUM_POSSIBLE_WORDS (sizeof(_possible_words) / sizeof(_possible_words[0]))
+#define WORDLE_NUM_VALID_LETTERS (sizeof(_valid_letters) / sizeof(_valid_letters[0]))
+
+typedef enum {
+    WORDLE_LETTER_WRONG = 0,
+    WORDLE_LETTER_WRONG_LOC,
+    WORDLE_LETTER_CORRECT,
+    WORDLE_LETTER_COUNT
+} WordleLetterResult;
+
+typedef enum {
+    SCREEN_TITLE = 0,
+    SCREEN_STREAK,
+    SCREEN_CONTINUE,
+#if WORDLE_USE_DAILY_STREAK
+    SCREEN_WAIT,
+#endif
+    SCREEN_PLAYING,
+    SCREEN_RESULT,
+    SCREEN_WIN,
+    SCREEN_LOSE,
+    SCREEN_NO_DICT,
+    SCREEN_ALREADY_GUESSED,
+    SCREEN_COUNT
+} WordleScreen;
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint8_t word_elements[WORDLE_LENGTH];
+    WordleLetterResult word_elements_result[WORDLE_LENGTH];
+#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
+    uint16_t guessed_words[WORDLE_MAX_ATTEMPTS];
+#endif
+    uint8_t attempt : 4;
+    uint8_t position : 3;
+    bool using_random_guess : 1;
+    uint16_t curr_answer : 14;
+    bool continuing : 1;
+    bool skip_wrong_letter : 1;
+    uint8_t streak;
+    WordleScreen curr_screen;
+    bool known_wrong_letters[WORDLE_NUM_VALID_LETTERS];
+    uint32_t day_last_game_started;
+} wordle_state_t;
+
+void wordle_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void wordle_face_activate(void *context);
+bool wordle_face_loop(movement_event_t event, void *context);
+void wordle_face_resign(void *context);
+
+#define wordle_face ((const watch_face_t){ \
+    wordle_face_setup, \
+    wordle_face_activate, \
+    wordle_face_loop, \
+    wordle_face_resign, \
+    NULL, \
+})
+
+#endif // WORDLE_FACE_H_
+
diff --git a/legacy/watch_faces/complication/wordle_face_dict.h b/legacy/watch_faces/complication/wordle_face_dict.h
new file mode 100644 (file)
index 0000000..6b82976
--- /dev/null
@@ -0,0 +1,293 @@
+#ifndef WORDLE_FACE_DICT_H_
+#define WORDLE_FACE_DICT_H_
+
+#ifndef WORDLE_LENGTH
+#define WORDLE_LENGTH 5
+#endif
+
+#ifndef WORDLE_USE_RANDOM_GUESS
+#define WORDLE_USE_RANDOM_GUESS 2
+#endif
+
+static const char _valid_letters[] = {'A', 'C', 'E', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S', 'T'};
+
+// From: https://matthewminer.name/projects/calculators/wordle-words-left/
+// Number of words found: 432
+static const char _valid_words[][WORDLE_LENGTH + 1] = {
+    "SLATE", "STARE", "SNARE", "SANER", "CRANE", "STALE", "CRATE", "RAISE", "TRACE", 
+    "SHARE", "ARISE", "SCARE", "SPARE", "CHAOS", "TAPIR", "CAIRN", "TENOR", "CLEAN", 
+    "HEART", "SCOPE", "SNARL", "SLEPT", "SINCE", "EPOCH", "SPACE", "RELIC", "SPOIL", 
+    "LITER", "LEAPT", "LANCE", "RANCH", "HORSE", "LEACH", "LATER", "STEAL", "CHEAP", 
+    "SHORT", "ETHIC", "CHANT", "ACTOR", "REACH", "SEPIA", "ONSET", "SPLAT", "LEANT", 
+    "REACT", "OCTAL", "SPORE", "IRATE", "CORAL", "NICER", "SPILT", "SCENT", "PANIC", 
+    "SHIRT", "PECAN", "SLAIN", "SPLIT", "ROACH", "ASCOT", "PHONE", "LITHE", "STOIC", 
+    "STRIP", "RENAL", "POISE", "ENACT", "CHEAT", "PITCH", "NOISE", "INLET", "PEARL", 
+    "POLAR", "PEACH", "STOLE", "CASTE", "CREST", "CRONE", "ETHOS", "THEIR", "STONE", 
+    "SHIRE", "LATCH", "HASTE", "CLOSE", "SPINE", "SLANT", "SPEAR", "SCALE", "CAPER", 
+    "RETCH", "PESTO", "CHIRP", "SPORT", "OPTIC", "SNAIL", "PRICE", "PLANE", "TORCH", 
+    "PASTE", "RECAP", "SOLAR", "CRASH", "LINER", "OPINE", "ASHEN", "PALER", "ECLAT", 
+    "SPELT", "TRIAL", "PERIL", "SLICE", "SCANT", "SAINT", "POSIT", "ATONE", "SPIRE", 
+    "COAST", "INEPT", "SHOAL", "CLASH", "THORN", "PHASE", "SCORE", "TRICE", "PERCH", 
+    "PORCH", "SHEAR", "CHOIR", "RHINO", "PLANT", "SHONE", "CHORE", "LEARN", "ALTER", 
+    "CHAIN", "PANEL", "PLIER", "STEIN", "COPSE", "SONIC", "ALIEN", "CHOSE", "ACORN", 
+    "ANTIC", "CHEST", "OTHER", "CHINA", "TALON", "SCORN", "PLAIN", "PILOT", "RIPEN", 
+    "PATCH", "SPICE", "CLONE", "SCION", "SCONE", "STRAP", "PARSE", "SHALE", "RISEN", 
+    "CANOE", "INTER", "LEASH", "ISLET", "PRINT", "SHINE", "NORTH", "CLEAT", "PLAIT", 
+    "SCRAP", "CLEAR", "SLOTH", "LAPSE", "CHAIR", "SNORT", "SHARP", "OPERA", "STAIN", 
+    "TEACH", "TRAIL", "TRAIN", "LATHE", "PIANO", "PINCH", "PETAL", "STERN", "PRONE", 
+    "PROSE", "PLEAT", "TROPE", "PLACE", "POSER", "INERT", "CHASE", "CAROL", "STAIR", 
+    "SATIN", "SPITE", "LOATH", "ROAST", "ARSON", "SHAPE", "CLASP", "LOSER", "SALON", 
+    "CATER", "SHALT", "INTRO", "ALERT", "PENAL", "SHORE", "RINSE", "CREPT", "APRON", 
+    "SONAR", "AISLE", "AROSE", "HATER", "NICHE", "POINT", "EARTH", "PINTO", "THOSE", 
+    "CLOTH", "NOTCH", "TOPIC", "RESIN", "SCALP", "HEIST", "HERON", "TRIPE", "TONAL", 
+    "TAPER", "SHORN", "TONIC", "HOIST", "SNORE", "STORE", "SLOPE", "OCEAN", "CHART", 
+    "PAINT", "SPENT", "SNIPE", "CRISP", "TRASH", "PATIO", "PLATE", "HOTEL", "LEAST", 
+    "ALONE", "RALPH", "SPIEL", "SIREN", "RATIO", "STOOP", "TROLL", "ATOLL", "SLASH", 
+    "RETRO", "CREEP", "STILT", "SPREE", "TASTE", "CACHE", "CANON", "EATEN", "TEPEE", 
+    "SHEET", "SNEER", "ERROR", "NATAL", "SLEEP", "STINT", "TROOP", "SHALL", "STALL", 
+    "PIPER", "TOAST", "NASAL", "CORER", "THERE", "POOCH", "SCREE", "ELITE", "ALTAR", 
+    "PENCE", "EATER", "ALPHA", "TENTH", "LINEN", "SHEER", "TAINT", "HEATH", "CRIER", 
+    "TENSE", "CARAT", "CANAL", "APNEA", "THESE", "HATCH", "SHELL", "CIRCA", "APART", 
+    "SPILL", "STEEL", "LOCAL", "STOOL", "SHEEN", "RESET", "STEEP", "ELATE", "PRESS", 
+    "SLEET", "CROSS", "TOTAL", "TREAT", "ONION", "STATE", "CINCH", "ASSET", "THREE", 
+    "TORSO", "SNOOP", "PENNE", "SPOON", "SHEEP", "PAPAL", "STILL", "CHILL", "THETA", 
+    "LEECH", "INNER", "HONOR", "LOOSE", "CONIC", "SCENE", "COACH", "CONCH", "LATTE", 
+    "ERASE", "ESTER", "PEACE", "PASTA", "INANE", "SPOOL", "TEASE", "HARSH", "PIECE", 
+    "STEER", "SCOOP", "NINTH", "OTTER", "OCTET", "EERIE", "RISER", "LAPEL", "HIPPO", 
+    "PREEN", "ETHER", "AORTA", "SENSE", "TRACT", "SHOOT", "SLOOP", "REPEL", "TITHE", 
+    "IONIC", "CELLO", "CHESS", "SOOTH", "COCOA", "TITAN", "TOOTH", "TIARA", "CRESS", 
+    "SLOSH", "RARER", "TERSE", "ERECT", "HELLO", "PARER", "RIPER", "NOOSE", "CREPE", 
+    "CACAO", "ILIAC", "POSSE", "CACTI", "EASEL", "LASSO", "ROOST", "ALLOT", "COLON", 
+    "LEPER", "TEETH", "TITLE", "HENCE", "NIECE", "PAPER", "TRITE", "SPELL", "RACER", 
+    "ATTIC", "CRASS", "HITCH", "LEASE", "CEASE", "ROTOR", "ELOPE", "APPLE", "CHILI", 
+    "START", "PHOTO", "SALSA", "STASH", "PRIOR", "TAROT", "COLOR", "CHEER", "CLASS", 
+    "ARENA", "ELECT", "ENTER", "CATCH", "TENET", "TACIT", "TRAIT", "TERRA", "LILAC", 
+};
+
+// These are words that'll never be used, but still need to be in the dictionary for guesses.
+// Number of words found: 1898
+static const char _possible_words[][WORDLE_LENGTH + 1] = {
+#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
+    "AALII", "AARTI", "ACAIS", "ACARI", "ACCAS", "ACERS", "ACETA", "ACHAR", "ACHES", 
+    "ACHOO", "ACINI", "ACNES", "ACRES", "ACROS", "ACTIN", "ACTON", "AECIA", "AEONS", 
+    "AERIE", "AEROS", "AESIR", "AHEAP", "AHENT", "AHINT", "AINEE", "AIOLI", "AIRER", 
+    "AIRNS", "AIRTH", "AIRTS", "AITCH", "ALAAP", "ALANE", "ALANS", "ALANT", "ALAPA", 
+    "ALAPS", "ALATE", "ALCOS", "ALECS", "ALEPH", "ALIAS", "ALINE", "ALIST", "ALLEE", 
+    "ALLEL", "ALLIS", "ALOES", "ALOHA", "ALOIN", "ALOOS", "ALTHO", "ALTOS", "ANANA", 
+    "ANATA", "ANCHO", "ANCLE", "ANCON", "ANEAR", "ANELE", "ANENT", "ANILE", "ANILS", 
+    "ANION", "ANISE", "ANLAS", "ANNAL", "ANNAS", "ANNAT", "ANOAS", "ANOLE", "ANSAE", 
+    "ANTAE", "ANTAR", "ANTAS", "ANTES", "ANTIS", "ANTRA", "ANTRE", "APACE", "APERS", 
+    "APERT", "APHIS", "APIAN", "APIOL", "APISH", "APOOP", "APORT", "APPAL", "APPEL", 
+    "APPRO", "APRES", "APSES", "APSIS", "APSOS", "APTER", "ARARS", "ARCHI", "ARCOS", 
+    "AREAE", "AREAL", "AREAR", "AREAS", "ARECA", "AREIC", "ARENE", "AREPA", "ARERE", 
+    "ARETE", "ARETS", "ARETT", "ARHAT", "ARIAS", "ARIEL", "ARILS", "ARIOT", "ARISH", 
+    "ARLES", "ARNAS", "AROHA", "ARPAS", "ARPEN", "ARRAH", "ARRAS", "ARRET", "ARRIS", 
+    "ARSES", "ARSIS", "ARTAL", "ARTEL", "ARTIC", "ARTIS", "ASANA", "ASCON", "ASHES", 
+    "ASHET", "ASPEN", "ASPER", "ASPIC", "ASPIE", "ASPIS", "ASPRO", "ASSAI", "ASSES", 
+    "ASSOT", "ASTER", "ASTIR", "ATAPS", "ATILT", "ATLAS", "ATOCS", "ATRIA", "ATRIP", 
+    "ATTAP", "ATTAR", "CACAS", "CAECA", "CAESE", "CAINS", "CALLA", "CALLS", "CALOS", 
+    "CALPA", "CALPS", "CANEH", "CANER", "CANES", "CANNA", "CANNS", "CANSO", "CANST", 
+    "CANTO", "CANTS", "CAPAS", "CAPES", "CAPHS", "CAPLE", "CAPON", "CAPOS", "CAPOT", 
+    "CAPRI", "CARAP", "CARER", "CARES", "CARET", "CARLE", "CARLS", "CARNS", "CARON", 
+    "CARPI", "CARPS", "CARRS", "CARSE", "CARTA", "CARTE", "CARTS", "CASAS", "CASCO", 
+    "CASES", "CASTS", "CATES", "CECAL", "CEILI", "CEILS", "CELLA", "CELLI", "CELLS", 
+    "CELTS", "CENSE", "CENTO", "CENTS", "CEORL", "CEPES", "CERCI", "CERES", "CERIA", 
+    "CERIC", "CERNE", "CEROC", "CEROS", "CERTS", "CESSE", "CESTA", "CESTI", "CETES", 
+    "CHACE", "CHACO", "CHAIS", "CHALS", "CHANA", "CHAPE", "CHAPS", "CHAPT", "CHARA", 
+    "CHARE", "CHARR", "CHARS", "CHATS", "CHEEP", "CHELA", "CHELP", "CHERE", "CHERT", 
+    "CHETH", "CHIAO", "CHIAS", "CHICA", "CHICH", "CHICO", "CHICS", "CHIEL", "CHILE", 
+    "CHINE", "CHINO", "CHINS", "CHIPS", "CHIRL", "CHIRO", "CHIRR", "CHIRT", "CHITS", 
+    "CHOCO", "CHOCS", "CHOIL", "CHOLA", "CHOLI", "CHOLO", "CHONS", "CHOON", "CHOPS", 
+    "CHOTA", "CHOTT", "CIELS", "CILIA", "CILLS", "CINCT", "CINES", "CIONS", "CIPPI", 
+    "CIRCS", "CIRES", "CIRLS", "CIRRI", "CISCO", "CISTS", "CITAL", "CITER", "CITES", 
+    "CLACH", "CLAES", "CLANS", "CLAPS", "CLAPT", "CLARO", "CLART", "CLAST", "CLATS", 
+    "CLEEP", "CLEPE", "CLEPT", "CLIES", "CLINE", "CLINT", "CLIPE", "CLIPS", "CLIPT", 
+    "CLITS", "CLONS", "CLOOP", "CLOOT", "CLOPS", "CLOTE", "CLOTS", "COACT", "COALA", 
+    "COALS", "COAPT", "COATE", "COATI", "COATS", "COCAS", "COCCI", "COCCO", "COCOS", 
+    "COHEN", "COHOE", "COHOS", "COILS", "COINS", "COIRS", "COITS", "COLAS", "COLES", 
+    "COLIC", "COLIN", "COLLS", "COLTS", "CONES", "CONIA", "CONIN", "CONNE", "CONNS", 
+    "CONTE", "CONTO", "COOCH", "COOEE", "COOER", "COOLS", "COONS", "COOPS", "COOPT", 
+    "COOST", "COOTS", "COPAL", "COPEN", "COPER", "COPES", "COPRA", "CORES", "CORIA", 
+    "CORNI", "CORNO", "CORNS", "CORPS", "CORSE", "CORSO", "COSEC", "COSES", "COSET", 
+    "COSIE", "COSTA", "COSTE", "COSTS", "COTAN", "COTES", "COTHS", "COTTA", "COTTS", 
+    "CRAAL", "CRAIC", "CRANS", "CRAPE", "CRAPS", "CRARE", "CREEL", "CREES", "CRENA", 
+    "CREPS", "CRIAS", "CRIES", "CRINE", "CRIOS", "CRIPE", "CRIPS", "CRISE", "CRITH", 
+    "CRITS", "CROCI", "CROCS", "CRONS", "CROOL", "CROON", "CROPS", "CRORE", "CROST", 
+    "CTENE", "EALES", "EARLS", "EARNS", "EARNT", "EARST", "EASER", "EASES", "EASLE", 
+    "EASTS", "EATHE", "ECHES", "ECHOS", "EISEL", "ELAIN", "ELANS", "ELCHI", "ELINT", 
+    "ELOIN", "ELOPS", "ELPEE", "ELSIN", "ENATE", "ENIAC", "ENLIT", "ENOLS", "ENROL", 
+    "ENTIA", "EORLS", "EOSIN", "EPACT", "EPEES", "EPHAH", "EPHAS", "EPHOR", "EPICS", 
+    "EPOPT", "EPRIS", "ERICA", "ERICS", "ERNES", "EROSE", "ERSES", "ESCAR", "ESCOT", 
+    "ESILE", "ESNES", "ESSES", "ESTOC", "ESTOP", "ESTRO", "ETAPE", "ETATS", "ETENS", 
+    "ETHAL", "ETHNE", "ETICS", "ETNAS", "ETTIN", "ETTLE", "HAARS", "HAETS", "HAHAS", 
+    "HAILS", "HAINS", "HAINT", "HAIRS", "HAITH", "HALAL", "HALER", "HALES", "HALLO", 
+    "HALLS", "HALON", "HALOS", "HALSE", "HALTS", "HANAP", "HANCE", "HANCH", "HANSA", 
+    "HANSE", "HANTS", "HAOLE", "HAPPI", "HARES", "HARLS", "HARNS", "HAROS", "HARPS", 
+    "HARTS", "HASPS", "HASTA", "HATES", "HATHA", "HEALS", "HEAPS", "HEARE", "HEARS", 
+    "HEAST", "HEATS", "HECHT", "HEELS", "HEILS", "HEIRS", "HELES", "HELIO", "HELLS", 
+    "HELOS", "HELOT", "HELPS", "HENCH", "HENNA", "HENTS", "HEPAR", "HERES", "HERLS", 
+    "HERNS", "HEROS", "HERSE", "HESPS", "HESTS", "HETES", "HETHS", "HIANT", "HILAR", 
+    "HILCH", "HILLO", "HILLS", "HILTS", "HINTS", "HIOIS", "HIREE", "HIRER", "HIRES", 
+    "HISTS", "HITHE", "HOARS", "HOAST", "HOERS", "HOISE", "HOLES", "HOLLA", "HOLLO", 
+    "HOLON", "HOLOS", "HOLTS", "HONAN", "HONER", "HONES", "HOOCH", "HOONS", "HOOPS", 
+    "HOORS", "HOOSH", "HOOTS", "HOPER", "HOPES", "HORAH", "HORAL", "HORAS", "HORIS", 
+    "HORNS", "HORST", "HOSEL", "HOSEN", "HOSER", "HOSES", "HOSTA", "HOSTS", "HOTCH", 
+    "HOTEN", "ICERS", "ICHES", "ICHOR", "ICIER", "ICONS", "ICTAL", "ICTIC", "ILEAC", 
+    "ILEAL", "ILIAL", "ILLER", "ILLTH", "INAPT", "INCEL", "INCLE", "INION", "INNIT", 
+    "INSET", "INSPO", "INTEL", "INTIL", "INTIS", "INTRA", "IOTAS", "IPPON", "IRONE", 
+    "IRONS", "ISHES", "ISLES", "ISNAE", "ISSEI", "ISTLE", "ITHER", "LAARI", "LACER", 
+    "LACES", "LACET", "LAERS", "LAHAL", "LAHAR", "LAICH", "LAICS", "LAIRS", "LAITH", 
+    "LALLS", "LANAI", "LANAS", "LANCH", "LANES", "LANTS", "LAPIN", "LAPIS", "LARCH", 
+    "LAREE", "LARES", "LARIS", "LARNS", "LARNT", "LASER", "LASES", "LASSI", "LASTS", 
+    "LATAH", "LATEN", "LATHI", "LATHS", "LEANS", "LEAPS", "LEARE", "LEARS", "LEATS", 
+    "LEEAR", "LEEPS", "LEERS", "LEESE", "LEETS", "LEHRS", "LEIRS", "LEISH", "LENES", 
+    "LENIS", "LENOS", "LENSE", "LENTI", "LENTO", "LEONE", "LEPRA", "LEPTA", "LERES", 
+    "LERPS", "LESES", "LESTS", "LETCH", "LETHE", "LIANA", "LIANE", "LIARS", "LIART", 
+    "LICHI", "LICHT", "LICIT", "LIENS", "LIERS", "LILLS", "LILOS", "LILTS", "LINAC", 
+    "LINCH", "LINES", "LININ", "LINNS", "LINOS", "LINTS", "LIONS", "LIPAS", "LIPES", 
+    "LIPIN", "LIPOS", "LIRAS", "LIROT", "LISLE", "LISPS", "LISTS", "LITAI", "LITAS", 
+    "LITES", "LITHO", "LITHS", "LITRE", "LLANO", "LOACH", "LOANS", "LOAST", "LOCHE", 
+    "LOCHS", "LOCIE", "LOCIS", "LOCOS", "LOESS", "LOHAN", "LOINS", "LOIPE", "LOIRS", 
+    "LOLLS", "LONER", "LOOIE", "LOONS", "LOOPS", "LOOTS", "LOPER", "LOPES", "LORAL", 
+    "LORAN", "LOREL", "LORES", "LORIC", "LORIS", "LOSEL", "LOSEN", "LOSES", "LOTAH", 
+    "LOTAS", "LOTES", "LOTIC", "LOTOS", "LOTSA", "LOTTA", "LOTTE", "LOTTO", "NAANS", 
+    "NACHE", "NACHO", "NACRE", "NAHAL", "NAILS", "NAIRA", "NALAS", "NALLA", "NANAS", 
+    "NANCE", "NANNA", "NANOS", "NAPAS", "NAPES", "NAPOO", "NAPPA", "NAPPE", "NARAS", 
+    "NARCO", "NARCS", "NARES", "NARIC", "NARIS", "NARRE", "NASHI", "NATCH", "NATES", 
+    "NATIS", "NEALS", "NEAPS", "NEARS", "NEATH", "NEATS", "NEELE", "NEEPS", "NEESE", 
+    "NEIST", "NELIS", "NENES", "NEONS", "NEPER", "NEPIT", "NERAL", "NEROL", "NERTS", 
+    "NESTS", "NETES", "NETOP", "NETTS", "NICHT", "NICOL", "NIHIL", "NILLS", "NINER", 
+    "NINES", "NINON", "NIPAS", "NIRLS", "NISEI", "NISSE", "NITER", "NITES", "NITON", 
+    "NITRE", "NITRO", "NOAHS", "NOELS", "NOILS", "NOINT", "NOIRS", "NOLES", "NOLLS", 
+    "NOLOS", "NONAS", "NONCE", "NONES", "NONET", "NONIS", "NOOIT", "NOONS", "NOOPS", 
+    "NOPAL", "NORIA", "NORIS", "NOSER", "NOSES", "NOTAL", "NOTER", "NOTES", "OASES", 
+    "OASIS", "OASTS", "OATEN", "OATER", "OATHS", "OCHER", "OCHES", "OCHRE", "OCREA", 
+    "OCTAN", "OCTAS", "OHIAS", "OHONE", "OILER", "OINTS", "OLEIC", "OLEIN", "OLENT", 
+    "OLEOS", "OLIOS", "OLLAS", "OLLER", "OLLIE", "OLPAE", "OLPES", "ONCER", "ONCES", 
+    "ONCET", "ONERS", "ONTIC", "OONTS", "OORIE", "OOSES", "OPAHS", "OPALS", "OPENS", 
+    "OPEPE", "OPPOS", "OPSIN", "OPTER", "ORACH", "ORALS", "ORANT", "ORATE", "ORCAS", 
+    "ORCIN", "ORIEL", "ORLES", "ORLON", "ORLOP", "ORNIS", "ORPIN", "ORRIS", "ORTHO", 
+    "OSCAR", "OSHAC", "OSIER", "OSSIA", "OSTIA", "OTTAR", "OTTOS", "PAALS", "PAANS", 
+    "PACAS", "PACER", "PACES", "PACHA", "PACOS", "PACTA", "PACTS", "PAEAN", "PAEON", 
+    "PAILS", "PAINS", "PAIRE", "PAIRS", "PAISA", "PAISE", "PALAS", "PALEA", "PALES", 
+    "PALET", "PALIS", "PALLA", "PALLS", "PALPI", "PALPS", "PALSA", "PANCE", "PANES", 
+    "PANNE", "PANNI", "PANTO", "PANTS", "PAOLI", "PAOLO", "PAPAS", "PAPES", "PAPPI", 
+    "PARAE", "PARAS", "PARCH", "PAREN", "PAREO", "PARES", "PARIS", "PARLE", "PAROL", 
+    "PARPS", "PARRA", "PARRS", "PARTI", "PARTS", "PASEO", "PASES", "PASHA", "PASSE", 
+    "PASTS", "PATEN", "PATER", "PATES", "PATHS", "PATIN", "PATTE", "PEALS", "PEANS", 
+    "PEARE", "PEARS", "PEART", "PEASE", "PEATS", "PECHS", "PEECE", "PEELS", "PEENS", 
+    "PEEPE", "PEEPS", "PEERS", "PEINS", "PEISE", "PELAS", "PELES", "PELLS", "PELON", 
+    "PELTA", "PELTS", "PENES", "PENIE", "PENIS", "PENNA", "PENNI", "PENTS", "PEONS", 
+    "PEPLA", "PEPOS", "PEPSI", "PERAI", "PERCE", "PERCS", "PEREA", "PERES", "PERIS", 
+    "PERNS", "PERPS", "PERSE", "PERST", "PERTS", "PESOS", "PESTS", "PETAR", "PETER", 
+    "PETIT", "PETRE", "PETRI", "PETTI", "PETTO", "PHARE", "PHEER", "PHENE", "PHEON", 
+    "PHESE", "PHIAL", "PHISH", "PHOCA", "PHONO", "PHONS", "PHOTS", "PHPHT", "PIANI", 
+    "PIANS", "PICAL", "PICAS", "PICOT", "PICRA", "PIERS", "PIERT", "PIETA", "PIETS", 
+    "PILAE", "PILAO", "PILAR", "PILCH", "PILEA", "PILEI", "PILER", "PILES", "PILIS", 
+    "PILLS", "PINAS", "PINES", "PINNA", "PINON", "PINOT", "PINTA", "PINTS", "PIONS", 
+    "PIPAL", "PIPAS", "PIPES", "PIPET", "PIPIS", "PIPIT", "PIRAI", "PIRLS", "PIRNS", 
+    "PISCO", "PISES", "PISOS", "PISTE", "PITAS", "PITHS", "PITON", "PITOT", "PITTA", 
+    "PLAAS", "PLANS", "PLAPS", "PLASH", "PLAST", "PLATS", "PLATT", "PLEAS", "PLENA", 
+    "PLEON", "PLESH", "PLICA", "PLIES", "PLOAT", "PLOPS", "PLOTS", "POACH", "POEPS", 
+    "POETS", "POLER", "POLES", "POLIO", "POLIS", "POLLS", "POLOS", "POLTS", "PONCE", 
+    "PONES", "PONTS", "POOHS", "POOLS", "POONS", "POOPS", "POORI", "POORT", "POOTS", 
+    "POPES", "POPPA", "PORAE", "PORAL", "PORER", "PORES", "PORIN", "PORNO", "PORNS", 
+    "PORTA", "PORTS", "POSES", "POSHO", "POSTS", "POTAE", "POTCH", "POTES", "POTIN", 
+    "POTOO", "POTTO", "POTTS", "PRANA", "PRAOS", "PRASE", "PRATE", "PRATS", "PRATT", 
+    "PREES", "PRENT", "PREON", "PREOP", "PREPS", "PRESA", "PRESE", "PREST", "PRIAL", 
+    "PRIER", "PRIES", "PRILL", "PRION", "PRISE", "PRISS", "PROAS", "PROIN", "PROLE", 
+    "PROLL", "PROPS", "PRORE", "PROSO", "PROSS", "PROST", "PROTO", "PSION", "PSOAE", 
+    "PSOAI", "PSOAS", "PSORA", "RACES", "RACHE", "RACON", "RAIAS", "RAILE", "RAILS", 
+    "RAINE", "RAINS", "RAITA", "RAITS", "RALES", "RANAS", "RANCE", "RANEE", "RANIS", 
+    "RANTS", "RAPER", "RAPES", "RAPHE", "RAPPE", "RAREE", "RARES", "RASER", "RASES", 
+    "RASPS", "RASSE", "RASTA", "RATAL", "RATAN", "RATAS", "RATCH", "RATEL", "RATER", 
+    "RATES", "RATHA", "RATHE", "RATHS", "RATOO", "RATOS", "REAIS", "REALO", "REALS", 
+    "REANS", "REAPS", "REARS", "REAST", "REATA", "REATE", "RECAL", "RECCE", "RECCO", 
+    "RECIT", "RECON", "RECTA", "RECTI", "RECTO", "REECH", "REELS", "REENS", "REEST", 
+    "REINS", "REIST", "RELET", "RELIE", "RELIT", "RELLO", "RENIN", "RENNE", "RENOS", 
+    "RENTE", "RENTS", "REOIL", "REPIN", "REPLA", "REPOS", "REPOT", "REPPS", "REPRO", 
+    "RERAN", "RESAT", "RESEE", "RESES", "RESIT", "RESTO", "RESTS", "RETIA", "RETIE", 
+    "RHEAS", "RHIES", "RHINE", "RHONE", "RIALS", "RIANT", "RIATA", "RICER", "RICES", 
+    "RICHT", "RICIN", "RIELS", "RILES", "RILLE", "RILLS", "RINES", "RIOTS", "RIPES", 
+    "RIPPS", "RISES", "RISHI", "RISPS", "RITES", "RITTS", "ROANS", "ROARS", "ROATE", 
+    "ROHES", "ROILS", "ROINS", "ROIST", "ROLES", "ROLLS", "RONEO", "RONES", "RONIN", 
+    "RONNE", "RONTE", "RONTS", "ROONS", "ROOPS", "ROOSA", "ROOSE", "ROOTS", "ROPER", 
+    "ROPES", "RORAL", "RORES", "RORIC", "RORIE", "RORTS", "ROSES", "ROSET", "ROSHI", 
+    "ROSIN", "ROSIT", "ROSTI", "ROSTS", "ROTAL", "ROTAN", "ROTAS", "ROTCH", "ROTES", 
+    "ROTIS", "ROTLS", "ROTON", "ROTOS", "ROTTE", "SACRA", "SAICE", "SAICS", "SAILS", 
+    "SAINE", "SAINS", "SAIRS", "SAIST", "SAITH", "SALAL", "SALAT", "SALEP", "SALES", 
+    "SALET", "SALIC", "SALLE", "SALOL", "SALOP", "SALPA", "SALPS", "SALSE", "SALTO", 
+    "SALTS", "SANES", "SANSA", "SANTO", "SANTS", "SAOLA", "SAPAN", "SAPOR", "SARAN", 
+    "SAREE", "SARIN", "SARIS", "SAROS", "SASER", "SASIN", "SASSE", "SATAI", "SATES", 
+    "SATIS", "SCAIL", "SCALA", "SCALL", "SCANS", "SCAPA", "SCAPE", "SCAPI", "SCARP", 
+    "SCARS", "SCART", "SCATH", "SCATS", "SCATT", "SCEAT", "SCENA", "SCOOT", "SCOPA", 
+    "SCOPS", "SCOTS", "SCRAE", "SCRAN", "SCRAT", "SCRIP", "SEALS", "SEANS", "SEARE", 
+    "SEARS", "SEASE", "SEATS", "SECCO", "SECHS", "SECTS", "SEELS", "SEEPS", "SEERS", 
+    "SEHRI", "SEILS", "SEINE", "SEIRS", "SEISE", "SELAH", "SELES", "SELLA", "SELLE", 
+    "SELLS", "SENAS", "SENES", "SENNA", "SENOR", "SENSA", "SENSI", "SENTE", "SENTI", 
+    "SENTS", "SEPAL", "SEPIC", "SEPTA", "SEPTS", "SERAC", "SERAI", "SERAL", "SERER", 
+    "SERES", "SERIC", "SERIN", "SERON", "SERRA", "SERRE", "SERRS", "SESSA", "SETAE", 
+    "SETAL", "SETON", "SETTS", "SHAHS", "SHANS", "SHAPS", "SHARN", "SHASH", "SHCHI", 
+    "SHEAL", "SHEAS", "SHEEL", "SHENT", "SHEOL", "SHERE", "SHERO", "SHETS", "SHIAI", 
+    "SHIEL", "SHIER", "SHIES", "SHILL", "SHINS", "SHIPS", "SHIRR", "SHIRS", "SHISH", 
+    "SHISO", "SHIST", "SHITE", "SHITS", "SHLEP", "SHOAT", "SHOER", "SHOES", "SHOLA", 
+    "SHOOL", "SHOON", "SHOOS", "SHOPE", "SHOPS", "SHORL", "SHOTE", "SHOTS", "SHOTT", 
+    "SHRIS", "SIALS", "SICES", "SICHT", "SIENS", "SIENT", "SIETH", "SILEN", "SILER", 
+    "SILES", "SILLS", "SILOS", "SILTS", "SINES", "SINHS", "SIPES", "SIREE", "SIRES", 
+    "SIRIH", "SIRIS", "SIROC", "SIRRA", "SISAL", "SISES", "SISTA", "SISTS", "SITAR", 
+    "SITES", "SITHE", "SLAES", "SLANE", "SLAPS", "SLART", "SLATS", "SLEER", "SLIER", 
+    "SLIPE", "SLIPS", "SLIPT", "SLISH", "SLITS", "SLOAN", "SLOES", "SLOOT", "SLOPS", 
+    "SLOTS", "SNAPS", "SNARS", "SNASH", "SNATH", "SNEAP", "SNEES", "SNELL", "SNIES", 
+    "SNIPS", "SNIRT", "SNITS", "SNOEP", "SNOOL", "SNOOT", "SNOTS", "SOAPS", "SOARE", 
+    "SOARS", "SOCAS", "SOCES", "SOCLE", "SOILS", "SOLAH", "SOLAN", "SOLAS", "SOLEI", 
+    "SOLER", "SOLES", "SOLON", "SOLOS", "SONCE", "SONES", "SONNE", "SONSE", "SOOLE", 
+    "SOOLS", "SOOPS", "SOOTE", "SOOTS", "SOPHS", "SOPOR", "SOPRA", "SORAL", "SORAS", 
+    "SOREE", "SOREL", "SORER", "SORES", "SORNS", "SORRA", "SORTA", "SORTS", "SOTHS", 
+    "SOTOL", "SPAER", "SPAES", "SPAHI", "SPAIL", "SPAIN", "SPAIT", "SPALE", "SPALL", 
+    "SPALT", "SPANE", "SPANS", "SPARS", "SPART", "SPATE", "SPATS", "SPEAL", "SPEAN", 
+    "SPEAT", "SPECS", "SPECT", "SPEEL", "SPEER", "SPEIL", "SPEIR", "SPEOS", "SPETS", 
+    "SPIAL", "SPICA", "SPICS", "SPIER", "SPIES", "SPILE", "SPINA", "SPINS", "SPIRT", 
+    "SPITS", "SPOOR", "SPOOT", "SPOSH", "SPOTS", "SPRAT", "SPRIT", "STANE", "STAPH", 
+    "STAPS", "STARN", "STARR", "STARS", "STATS", "STEAN", "STEAR", "STEEN", "STEIL", 
+    "STELA", "STELE", "STELL", "STENO", "STENS", "STENT", "STEPS", "STEPT", "STERE", 
+    "STETS", "STICH", "STIES", "STILE", "STIPA", "STIPE", "STIRE", "STIRP", "STIRS", 
+    "STOAE", "STOAI", "STOAS", "STOAT", "STOEP", "STOIT", "STOLN", "STONN", "STOOR", 
+    "STOPE", "STOPS", "STOPT", "STOSS", "STOTS", "STOTT", "STRAE", "STREP", "STRIA", 
+    "STROP", "TAALS", "TAATA", "TACAN", "TACES", "TACET", "TACHE", "TACHO", "TACHS", 
+    "TACOS", "TACTS", "TAELS", "TAHAS", "TAHRS", "TAILS", "TAINS", "TAIRA", "TAISH", 
+    "TAITS", "TALAR", "TALAS", "TALCS", "TALEA", "TALER", "TALES", "TALLS", "TALPA", 
+    "TANAS", "TANHS", "TANNA", "TANTI", "TANTO", "TAPAS", "TAPEN", "TAPES", "TAPET", 
+    "TAPIS", "TAPPA", "TARAS", "TARES", "TARNS", "TAROC", "TAROS", "TARPS", "TARRE", 
+    "TARSI", "TARTS", "TASAR", "TASER", "TASES", "TASSA", "TASSE", "TASSO", "TATAR", 
+    "TATER", "TATES", "TATHS", "TATIE", "TATTS", "TEALS", "TEARS", "TEATS", "TECHS", 
+    "TECTA", "TEELS", "TEENE", "TEENS", "TEERS", "TEHRS", "TEILS", "TEINS", "TELAE", 
+    "TELCO", "TELES", "TELIA", "TELIC", "TELLS", "TELOI", "TELOS", "TENCH", "TENES", 
+    "TENIA", "TENNE", "TENNO", "TENON", "TENTS", "TEPAL", "TEPAS", "TERAI", "TERAS", 
+    "TERCE", "TERES", "TERNE", "TERNS", "TERTS", "TESLA", "TESTA", "TESTE", "TESTS", 
+    "TETES", "TETHS", "TETRA", "TETRI", "THALE", "THALI", "THANA", "THANE", "THANS", 
+    "THARS", "THECA", "THEES", "THEIC", "THEIN", "THENS", "THESP", "THETE", "THILL", 
+    "THINE", "THINS", "THIOL", "THIRL", "THOLE", "THOLI", "THORO", "THORP", "THRAE", 
+    "THRIP", "THROE", "TIANS", "TIARS", "TICAL", "TICCA", "TICES", "TIERS", "TILER", 
+    "TILES", "TILLS", "TILTH", "TILTS", "TINAS", "TINCT", "TINEA", "TINES", "TINTS", 
+    "TIPIS", "TIRES", "TIRLS", "TIROS", "TIRRS", "TITCH", "TITER", "TITIS", "TITRE", 
+    "TOCOS", "TOEAS", "TOHOS", "TOILE", "TOILS", "TOISE", "TOITS", "TOLAN", "TOLAR", 
+    "TOLAS", "TOLES", "TOLLS", "TOLTS", "TONER", "TONES", "TONNE", "TOOLS", "TOONS", 
+    "TOOTS", "TOPEE", "TOPER", "TOPES", "TOPHE", "TOPHI", "TOPHS", "TOPIS", "TOPOI", 
+    "TOPOS", "TORAH", "TORAN", "TORAS", "TORCS", "TORES", "TORIC", "TORII", "TOROS", 
+    "TOROT", "TORRS", "TORSE", "TORSI", "TORTA", "TORTE", "TORTS", "TOSAS", "TOSES", 
+    "TOTER", "TOTES", "TRANS", "TRANT", "TRAPE", "TRAPS", "TRAPT", "TRASS", "TRATS", 
+    "TRATT", "TREEN", "TREES", "TRESS", "TREST", "TRETS", "TRIAC", "TRIER", "TRIES", 
+    "TRILL", "TRINE", "TRINS", "TRIOL", "TRIOR", "TRIOS", "TRIPS", "TRIST", "TROAT", 
+    "TROIS", "TRONA", "TRONC", "TRONE", "TRONS", "TROTH", "TROTS", "TSARS", 
+#endif
+};
+
+#if (WORDLE_USE_RANDOM_GUESS == 3)
+static const uint16_t _num_random_guess_words = 13;  // The valid_words array begins with this many words that are considered the top 3% best options.
+#elif (WORDLE_USE_RANDOM_GUESS == 2)
+static const uint16_t _num_random_guess_words = 257;  // The valid_words array begins with this many words where each letter is different.
+#elif (WORDLE_USE_RANDOM_GUESS == 1)
+static const uint16_t _num_random_guess_words = _num_words;
+#endif
+
+#endif // WORDLE_FACE_DICT_H_
\ No newline at end of file
diff --git a/legacy/watch_faces/demo/beeps_face.c b/legacy/watch_faces/demo/beeps_face.c
new file mode 100644 (file)
index 0000000..5211271
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Wesley
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "beeps_face.h"
+
+void beeps_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(beeps_state_t));
+        memset(*context_ptr, 0, sizeof(beeps_state_t));
+        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+    }
+}
+
+void beeps_face_activate(void *context) {
+    (void) context;
+}
+
+static void _beep_face_update_lcd(beeps_state_t *state) {
+    char buf[11];
+    const char buzzernote[][7] = {"   5500", "   5827", "   6174","   6541","   6930","   7342","   7778","   8241","   8731","   9250","   9800","  10383","  11000","  11654","  12347","  13081","  13859","  14683","  15556","  16481","  17461","  18500","  19600","  20765","  22000","  23308","  24694","  26163","  27718","  29366","  31113","  32963","  34923","  36999","  39200","  41530","  44000","  46616","  49388","  52325","  55437","  58733","  62225","  65925","  69846","  73999","  78399","  83061","  88000","  93233","  98777"," 104650"," 110873"," 117466"," 124451"," 131851"," 139691"," 147998"," 156798"," 166122"," 176000"," 186466"," 197553"," 209300"," 221746"," 234932"," 248902"," 263702"," 279383"," 295996"," 313596"," 332244"," 352000"," 372931"," 395107"," 418601"," 443492"," 469863"," 497803"," 527404"," 558765"," 591991"," 627193"," 664488"," 704000"," 745862"," 790213"};
+    sprintf(buf, "HZ %s", buzzernote[state->frequency]);
+    watch_display_string(buf, 0);
+}
+
+bool beeps_face_loop(movement_event_t event, void *context) {
+    beeps_state_t *state = (beeps_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _beep_face_update_lcd(state);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            state->frequency = (state->frequency + 1) % 87;
+            _beep_face_update_lcd(state);
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+            if (state->frequency == 0) {
+                watch_buzzer_play_note(BUZZER_NOTE_A1, 500);
+            } else if (state->frequency == 1) {
+                watch_buzzer_play_note(BUZZER_NOTE_A1SHARP_B1FLAT, 500);
+            } else if (state->frequency == 2) {
+                watch_buzzer_play_note(BUZZER_NOTE_B1, 500);
+            } else if (state->frequency == 3) {
+                watch_buzzer_play_note(BUZZER_NOTE_C2, 500);
+            } else if (state->frequency == 4) {
+                watch_buzzer_play_note(BUZZER_NOTE_C2SHARP_D2FLAT, 500);
+            } else if (state->frequency == 5) {
+                watch_buzzer_play_note(BUZZER_NOTE_D2, 500);
+            } else if (state->frequency == 6) {
+                watch_buzzer_play_note(BUZZER_NOTE_D2SHARP_E2FLAT, 500);
+            } else if (state->frequency == 7) {
+                watch_buzzer_play_note(BUZZER_NOTE_E2, 500);
+            } else if (state->frequency == 8) {
+                watch_buzzer_play_note(BUZZER_NOTE_F2, 500);
+            } else if (state->frequency == 9) {
+                watch_buzzer_play_note(BUZZER_NOTE_F2SHARP_G2FLAT, 500);
+            } else if (state->frequency == 10) {
+                watch_buzzer_play_note(BUZZER_NOTE_G2, 500);
+            } else if (state->frequency == 11) {
+                watch_buzzer_play_note(BUZZER_NOTE_G2SHARP_A2FLAT, 500);
+            } else if (state->frequency == 12) {
+                watch_buzzer_play_note(BUZZER_NOTE_A2, 500);
+            } else if (state->frequency == 13) {
+                watch_buzzer_play_note(BUZZER_NOTE_A2SHARP_B2FLAT, 500);
+            } else if (state->frequency == 14) {
+                watch_buzzer_play_note(BUZZER_NOTE_B2, 500);
+            } else if (state->frequency == 15) {
+                watch_buzzer_play_note(BUZZER_NOTE_C3, 500);
+            } else if (state->frequency == 16) {
+                watch_buzzer_play_note(BUZZER_NOTE_C3SHARP_D3FLAT, 500);
+            } else if (state->frequency == 17) {
+                watch_buzzer_play_note(BUZZER_NOTE_D3, 500);
+            } else if (state->frequency == 18) {
+                watch_buzzer_play_note(BUZZER_NOTE_D3SHARP_E3FLAT, 500);
+            } else if (state->frequency == 19) {
+                watch_buzzer_play_note(BUZZER_NOTE_E3, 500);
+            } else if (state->frequency == 20) {
+                watch_buzzer_play_note(BUZZER_NOTE_F3, 500);
+            } else if (state->frequency == 21) {
+                watch_buzzer_play_note(BUZZER_NOTE_F3SHARP_G3FLAT, 500);
+            } else if (state->frequency == 22) {
+                watch_buzzer_play_note(BUZZER_NOTE_G3, 500);
+            } else if (state->frequency == 23) {
+                watch_buzzer_play_note(BUZZER_NOTE_G3SHARP_A3FLAT, 500);
+            } else if (state->frequency == 24) {
+                watch_buzzer_play_note(BUZZER_NOTE_A3, 500);
+            } else if (state->frequency == 25) {
+                watch_buzzer_play_note(BUZZER_NOTE_A3SHARP_B3FLAT, 500);
+            } else if (state->frequency == 26) {
+                watch_buzzer_play_note(BUZZER_NOTE_B3, 500);
+            } else if (state->frequency == 27) {
+                watch_buzzer_play_note(BUZZER_NOTE_C4, 500);
+            } else if (state->frequency == 28) {
+                watch_buzzer_play_note(BUZZER_NOTE_C4SHARP_D4FLAT, 500);
+            } else if (state->frequency == 29) {
+                watch_buzzer_play_note(BUZZER_NOTE_D4, 500);
+            } else if (state->frequency == 30) {
+                watch_buzzer_play_note(BUZZER_NOTE_D4SHARP_E4FLAT, 500);
+            } else if (state->frequency == 31) {
+                watch_buzzer_play_note(BUZZER_NOTE_E4, 500);
+            } else if (state->frequency == 32) {
+                watch_buzzer_play_note(BUZZER_NOTE_F4, 500);
+            } else if (state->frequency == 33) {
+                watch_buzzer_play_note(BUZZER_NOTE_F4SHARP_G4FLAT, 500);
+            } else if (state->frequency == 34) {
+                watch_buzzer_play_note(BUZZER_NOTE_G4, 500);
+            } else if (state->frequency == 35) {
+                watch_buzzer_play_note(BUZZER_NOTE_G4SHARP_A4FLAT, 500);
+            } else if (state->frequency == 36) {
+                watch_buzzer_play_note(BUZZER_NOTE_A4, 500);
+            } else if (state->frequency == 37) {
+                watch_buzzer_play_note(BUZZER_NOTE_A4SHARP_B4FLAT, 500);
+            } else if (state->frequency == 38) {
+                watch_buzzer_play_note(BUZZER_NOTE_B4, 500);
+            } else if (state->frequency == 39) {
+                watch_buzzer_play_note(BUZZER_NOTE_C5, 500);
+            } else if (state->frequency == 40) {
+                watch_buzzer_play_note(BUZZER_NOTE_C5SHARP_D5FLAT, 500);
+            } else if (state->frequency == 41) {
+                watch_buzzer_play_note(BUZZER_NOTE_D5, 500);
+            } else if (state->frequency == 42) {
+                watch_buzzer_play_note(BUZZER_NOTE_D5SHARP_E5FLAT, 500);
+            } else if (state->frequency == 43) {
+                watch_buzzer_play_note(BUZZER_NOTE_E5, 500);
+            } else if (state->frequency == 44) {
+                watch_buzzer_play_note(BUZZER_NOTE_F5, 500);
+            } else if (state->frequency == 45) {
+                watch_buzzer_play_note(BUZZER_NOTE_F5SHARP_G5FLAT, 500);
+            } else if (state->frequency == 46) {
+                watch_buzzer_play_note(BUZZER_NOTE_G5, 500);
+            } else if (state->frequency == 47) {
+                watch_buzzer_play_note(BUZZER_NOTE_G5SHARP_A5FLAT, 500);
+            } else if (state->frequency == 48) {
+                watch_buzzer_play_note(BUZZER_NOTE_A5, 500);
+            } else if (state->frequency == 49) {
+                watch_buzzer_play_note(BUZZER_NOTE_A5SHARP_B5FLAT, 500);
+            } else if (state->frequency == 50) {
+                watch_buzzer_play_note(BUZZER_NOTE_B5, 500);
+            } else if (state->frequency == 51) {
+                watch_buzzer_play_note(BUZZER_NOTE_C6, 500);
+            } else if (state->frequency == 52) {
+                watch_buzzer_play_note(BUZZER_NOTE_C6SHARP_D6FLAT, 500);
+            } else if (state->frequency == 53) {
+                watch_buzzer_play_note(BUZZER_NOTE_D6, 500);
+            } else if (state->frequency == 54) {
+                watch_buzzer_play_note(BUZZER_NOTE_D6SHARP_E6FLAT, 500);
+            } else if (state->frequency == 55) {
+                watch_buzzer_play_note(BUZZER_NOTE_E6, 500);
+            } else if (state->frequency == 56) {
+                watch_buzzer_play_note(BUZZER_NOTE_F6, 500);
+            } else if (state->frequency == 57) {
+                watch_buzzer_play_note(BUZZER_NOTE_F6SHARP_G6FLAT, 500);
+            } else if (state->frequency == 58) {
+                watch_buzzer_play_note(BUZZER_NOTE_G6, 500);
+            } else if (state->frequency == 59) {
+                watch_buzzer_play_note(BUZZER_NOTE_G6SHARP_A6FLAT, 500);
+            } else if (state->frequency == 60) {
+                watch_buzzer_play_note(BUZZER_NOTE_A6, 500);
+            } else if (state->frequency == 61) {
+                watch_buzzer_play_note(BUZZER_NOTE_A6SHARP_B6FLAT, 500);
+            } else if (state->frequency == 62) {
+                watch_buzzer_play_note(BUZZER_NOTE_B6, 500);
+            } else if (state->frequency == 63) {
+                watch_buzzer_play_note(BUZZER_NOTE_C7, 500);
+            } else if (state->frequency == 64) {
+                watch_buzzer_play_note(BUZZER_NOTE_C7SHARP_D7FLAT, 500);
+            } else if (state->frequency == 65) {
+                watch_buzzer_play_note(BUZZER_NOTE_D7, 500);
+            } else if (state->frequency == 66) {
+                watch_buzzer_play_note(BUZZER_NOTE_D7SHARP_E7FLAT, 500);
+            } else if (state->frequency == 67) {
+                watch_buzzer_play_note(BUZZER_NOTE_E7, 500);
+            } else if (state->frequency == 68) {
+                watch_buzzer_play_note(BUZZER_NOTE_F7, 500);
+            } else if (state->frequency == 69) {
+                watch_buzzer_play_note(BUZZER_NOTE_F7SHARP_G7FLAT, 500);
+            } else if (state->frequency == 70) {
+                watch_buzzer_play_note(BUZZER_NOTE_G7, 500);
+            } else if (state->frequency == 71) {
+                watch_buzzer_play_note(BUZZER_NOTE_G7SHARP_A7FLAT, 500);
+            } else if (state->frequency == 72) {
+                watch_buzzer_play_note(BUZZER_NOTE_A7, 500);
+            } else if (state->frequency == 73) {
+                watch_buzzer_play_note(BUZZER_NOTE_A7SHARP_B7FLAT, 500);
+            } else if (state->frequency == 74) {
+                watch_buzzer_play_note(BUZZER_NOTE_B7, 500);
+            } else if (state->frequency == 75) {
+                watch_buzzer_play_note(BUZZER_NOTE_C8, 500);
+            } else if (state->frequency == 76) {
+                watch_buzzer_play_note(BUZZER_NOTE_C8SHARP_D8FLAT, 500);
+            } else if (state->frequency == 77) {
+                watch_buzzer_play_note(BUZZER_NOTE_D8, 500);
+            } else if (state->frequency == 78) {
+                watch_buzzer_play_note(BUZZER_NOTE_D8SHARP_E8FLAT, 500);
+            } else if (state->frequency == 79) {
+                watch_buzzer_play_note(BUZZER_NOTE_E8, 500);
+            } else if (state->frequency == 80) {
+                watch_buzzer_play_note(BUZZER_NOTE_F8, 500);
+            } else if (state->frequency == 81) {
+                watch_buzzer_play_note(BUZZER_NOTE_F8SHARP_G8FLAT, 500);
+            } else if (state->frequency == 82) {
+                watch_buzzer_play_note(BUZZER_NOTE_G8, 500);
+            } else if (state->frequency == 83) {
+                watch_buzzer_play_note(BUZZER_NOTE_G8SHARP_A8FLAT, 500);
+            } else if (state->frequency == 84) {
+                watch_buzzer_play_note(BUZZER_NOTE_A8, 500);
+            } else if (state->frequency == 85) {
+                watch_buzzer_play_note(BUZZER_NOTE_A8SHARP_B8FLAT, 500);
+            } else if (state->frequency == 86) {
+                watch_buzzer_play_note(BUZZER_NOTE_B8, 500);
+            }
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+    return true;
+}
+
+void beeps_face_resign(void *context) {
+    (void) context;
+}
+
diff --git a/legacy/watch_faces/demo/beeps_face.h b/legacy/watch_faces/demo/beeps_face.h
new file mode 100644 (file)
index 0000000..5e44c90
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Wesley
+ *
+ * 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 BEEPS_FACE_H_
+#define BEEPS_FACE_H_
+
+#include "movement.h"
+
+/*
+ * A simple watch face to test the different Buzzer Notes.
+ *
+ * Press the Light button to play a sound.
+ * Press the Alarm button to change the frequency.
+ *
+ * The watch face displays the frequency of the buzzer it will play
+ * this allows you to reference the watch_buzzer.h file to find the
+ * corresponding note.
+ *
+ * The watch_buzzer.h file is found at watch-library/shared/watch/watch_buzzer.h
+ */
+
+typedef struct {
+    uint8_t frequency;
+} beeps_state_t;
+
+void beeps_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void beeps_face_activate(void *context);
+bool beeps_face_loop(movement_event_t event, void *context);
+void beeps_face_resign(void *context);
+
+#define beeps_face ((const watch_face_t){ \
+    beeps_face_setup, \
+    beeps_face_activate, \
+    beeps_face_loop, \
+    beeps_face_resign, \
+    NULL, \
+})
+
+#endif // BEEPS_FACE_H_
+
diff --git a/legacy/watch_faces/demo/demo_face.c b/legacy/watch_faces/demo/demo_face.c
new file mode 100644 (file)
index 0000000..e423b7d
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "demo_face.h"
+#include "watch.h"
+
+typedef enum {
+    DEMO_FACE_TIME = 0,
+    DEMO_FACE_WORLD_TIME,
+    DEMO_FACE_BEATS,
+    DEMO_FACE_TOTP,
+    DEMO_FACE_TEMP_F,
+    DEMO_FACE_TEMP_C,
+    DEMO_FACE_TEMP_LOG_1,
+    DEMO_FACE_TEMP_LOG_2,
+    DEMO_FACE_DAY_ONE,
+    DEMO_FACE_STOPWATCH,
+    DEMO_FACE_PULSOMETER,
+    DEMO_FACE_BATTERY_VOLTAGE,
+    DEMO_FACE_NUM_FACES
+} demo_face_index_t;
+
+void demo_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(demo_face_index_t));
+        memset(*context_ptr, 0, sizeof(demo_face_index_t));
+    }
+}
+
+void demo_face_activate(void *context) {
+    (void) context;
+    movement_request_tick_frequency(0);
+    // ensure the watch never enters low energy mode
+    movement_set_low_energy_timeout(0);
+    movement_set_backlight_dwell(3);
+}
+
+bool demo_face_loop(movement_event_t event, void *context) {
+    demo_face_index_t *screen = (demo_face_index_t *)context;
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_UP:
+            *screen = ((*screen) + 1) % DEMO_FACE_NUM_FACES;
+            // fall through
+        case EVENT_ACTIVATE:
+            switch (*screen) {
+                case DEMO_FACE_TIME:
+                    watch_display_string("TH10101036", 0);
+                    watch_set_colon();
+                    break;
+                case DEMO_FACE_WORLD_TIME:
+                    watch_display_string("UT10 21036", 0);
+                    watch_set_indicator(WATCH_INDICATOR_PM);
+                    break;
+                case DEMO_FACE_BEATS:
+                    watch_display_string("bt   64125", 0);
+                    watch_clear_indicator(WATCH_INDICATOR_PM);
+                    watch_clear_colon();
+                    break;
+                case DEMO_FACE_TOTP:
+                    watch_display_string("2F29808494", 0);
+                    break;
+                case DEMO_FACE_TEMP_F:
+                    watch_display_string("TE  72.1#F", 0);
+                    break;
+                case DEMO_FACE_TEMP_C:
+                    watch_display_string("TE  22.3#C", 0);
+                    break;
+                case DEMO_FACE_TEMP_LOG_1:
+                    watch_display_string("TL  43.6#F", 0);
+                    break;
+                case DEMO_FACE_TEMP_LOG_2:
+                    watch_display_string("AT 6100000", 0);
+                    watch_set_colon();
+                    break;
+                case DEMO_FACE_DAY_ONE:
+                    watch_clear_colon();
+                    watch_display_string("DA   12879", 0);
+                    break;
+                case DEMO_FACE_STOPWATCH:
+                    watch_display_string("ST 01042  ", 0);
+                    watch_set_colon();
+                    break;
+                case DEMO_FACE_PULSOMETER:
+                    watch_display_string("    68 bpn", 0);
+                    watch_clear_colon();
+                    break;
+                case DEMO_FACE_BATTERY_VOLTAGE:
+                    watch_display_string("BA  2.97 V", 0);
+                    break;
+                case DEMO_FACE_NUM_FACES:
+                    // we won't get here, but silence the warning
+                    break;
+            }
+            break;
+        case EVENT_TIMEOUT:
+            // ignore timeout
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void demo_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/demo/demo_face.h b/legacy/watch_faces/demo/demo_face.h
new file mode 100644 (file)
index 0000000..ec336e5
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 DEMO_FACE_H_
+#define DEMO_FACE_H_
+
+/*
+ * DEMO FACE
+ *
+ * This watch was designed for the Crowd Supply marketing team, so they could
+ * photograph the various functions of Sensor Watch. The Alarm button advances
+ * through static screens that simulate different watch faces.
+ *
+ * This watch face may only be useful to you if you need to photograph Sensor
+ * Watch, i.e. for a blog post.
+ */
+
+#include "movement.h"
+
+void demo_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void demo_face_activate(void *context);
+bool demo_face_loop(movement_event_t event, void *context);
+void demo_face_resign(void *context);
+
+#define demo_face ((const watch_face_t){ \
+    demo_face_setup, \
+    demo_face_activate, \
+    demo_face_loop, \
+    demo_face_resign, \
+    NULL, \
+})
+
+#endif // DEMO_FACE_H_
diff --git a/legacy/watch_faces/demo/frequency_correction_face.c b/legacy/watch_faces/demo/frequency_correction_face.c
new file mode 100644 (file)
index 0000000..badd327
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 <#author_name#>
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "frequency_correction_face.h"
+
+// NOTE: since this face deals directly with the SAM L22's SUPC and RTC registers,
+// it won't build for the simulator or really do anything. so let's not.
+#ifndef __EMSCRIPTEN__
+
+// Waveform output. Comes out on pin A1 of the 9-pin connector. Output is enabled
+// when the watch face is activated and disabled when deactivated.
+// * The RTC's PEREOn enables an event at a frequency of 1024/(2^(n+3)), allowing
+//   event generation from 128 Hz to 1 Hz.
+// * The SUPC's RTCTGL causes the selected output to toggle on an RTC event.
+// Putting it together, PEREO0 generates a 64 Hz square wave on A1, PEREO1 a 32 Hz
+// wave, on and on to an 0.5 Hz square wave for PEREO7.
+static void _enable_output(uint8_t period_event_output) {
+    SUPC->BKOUT.bit.EN = 1;
+    SUPC->BKOUT.bit.RTCTGL = 1;
+    RTC->MODE2.CTRLA.bit.ENABLE = 0;
+    while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
+    RTC->MODE2.EVCTRL.reg = RTC_MODE2_EVCTRL_PEREO(1 << period_event_output);
+    RTC->MODE2.CTRLA.bit.ENABLE = 1;
+    while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
+}
+
+// The display: "FC" for "Frequency Correction" in the title slot; at the top right,
+// the periodic event output that's enabled:: 0 for PEREO0, 1 for PEREO1, etc.
+// The main line displays the frequency correction value from -127 to 127.
+// Controls: A short press on the Light button increases FREQCORR by 1 and a short
+// press on the Alarm button decreases FREQCORR by 1. A long press on ALARM cycles
+// through the available output frequencies.
+static void _update_display(uint8_t period_event_output) {
+    char buf[14];
+    sprintf(buf, "FC%2d%6d", period_event_output, (int8_t)RTC->MODE2.FREQCORR.reg);
+    watch_display_string(buf, 0);
+}
+
+static void _disable_output() {
+    SUPC->BKOUT.bit.EN = 0;
+    SUPC->BKOUT.bit.RTCTGL = 0;
+    RTC->MODE2.CTRLA.bit.ENABLE = 0;
+    while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
+    RTC->MODE2.EVCTRL.reg = RTC_MODE2_EVCTRL_PEREO(0);
+    RTC->MODE2.CTRLA.bit.ENABLE = 1;
+    while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
+}
+
+void frequency_correction_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(frequency_correction_state_t));
+        frequency_correction_state_t *state = (frequency_correction_state_t *)*context_ptr;
+        state->period_event_output = 0;
+    }
+}
+
+void frequency_correction_face_activate(void *context) {
+    frequency_correction_state_t *state = (frequency_correction_state_t *)context;
+    _enable_output(state->period_event_output);
+}
+
+bool frequency_correction_face_loop(movement_event_t event, void *context) {
+    frequency_correction_state_t *state = (frequency_correction_state_t *)context;
+    int8_t freqcorr;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _update_display(state->period_event_output);
+            break;
+        case EVENT_TICK:
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            freqcorr = RTC->MODE2.FREQCORR.reg;
+            if (freqcorr < 127) {
+                RTC->MODE2.FREQCORR.reg++;
+                while(RTC->MODE2.SYNCBUSY.reg);
+            }
+            _update_display(state->period_event_output);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            freqcorr = RTC->MODE2.FREQCORR.reg;
+            if (freqcorr > -127) {
+                RTC->MODE2.FREQCORR.reg--;
+                while(RTC->MODE2.SYNCBUSY.reg);
+            }
+            _update_display(state->period_event_output);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            state->period_event_output = (state->period_event_output + 1) % 8;
+            _update_display(state->period_event_output);
+            _enable_output(state->period_event_output);
+            break;
+        case EVENT_TIMEOUT:
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            watch_start_sleep_animation(500);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void frequency_correction_face_resign(void *context) {
+    (void) context;
+
+    _disable_output();
+}
+
+#else
+
+void frequency_correction_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    (void) context_ptr;
+}
+
+void frequency_correction_face_activate(void *context) {
+    (void) context;
+}
+
+bool frequency_correction_face_loop(movement_event_t event, void *context) {
+    (void) context;
+    (void) event;
+
+    return true;
+}
+
+void frequency_correction_face_resign(void *context) {
+    (void) context;
+}
+
+#endif
diff --git a/legacy/watch_faces/demo/frequency_correction_face.h b/legacy/watch_faces/demo/frequency_correction_face.h
new file mode 100644 (file)
index 0000000..7d25866
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 <#author_name#>
+ *
+ * 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 FREQUENCY_CORRECTION_FACE_H_
+#define FREQUENCY_CORRECTION_FACE_H_
+
+/*
+ * FREQUENCY CORRECTION FACE
+ *
+ * While active, this face generates a square-wave on pin A1 of the 9-pin
+ * connector. The output frequency is adjustable from 64 Hz to 0.5 Hz.
+ * Long-press ALARM to cycle through available frequencies.
+ *
+ * This face also displays the value of the watch's frequency-correction
+ * register. This setting varies from -127 to +127. Press LIGHT to increment
+ * or ALARM to decrement the setting.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint8_t period_event_output;
+} frequency_correction_state_t;
+
+void frequency_correction_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void frequency_correction_face_activate(void *context);
+bool frequency_correction_face_loop(movement_event_t event, void *context);
+void frequency_correction_face_resign(void *context);
+
+#define frequency_correction_face ((const watch_face_t){ \
+    frequency_correction_face_setup, \
+    frequency_correction_face_activate, \
+    frequency_correction_face_loop, \
+    frequency_correction_face_resign, \
+    NULL, \
+})
+
+#endif // FREQUENCY_CORRECTION_FACE_H_
+
diff --git a/legacy/watch_faces/demo/hello_there_face.c b/legacy/watch_faces/demo/hello_there_face.c
new file mode 100644 (file)
index 0000000..39ab8bc
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "hello_there_face.h"
+#include "watch.h"
+
+void hello_there_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    // These next two lines just silence the compiler warnings associated with unused parameters.
+    // We have no use for the settings or the watch_face_index, so we make that explicit here.
+    (void) watch_face_index;
+    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
+    if (*context_ptr == NULL) {
+        // in this case, we allocate an area of memory sufficient to store the stuff we need to track.
+        *context_ptr = malloc(sizeof(hello_there_state_t));
+    }
+}
+
+void hello_there_face_activate(void *context) {
+    // same as above: silence the warning, we don't need to check the settings.
+    // we do however need to set some things in our context. Here we cast it to the correct type...
+    hello_there_state_t *state = (hello_there_state_t *)context;
+    // ...and set the initial state of our watch face. We start out displaying the word 'Hello',
+    state->current_word = 0;
+    // and animate by default.
+    state->animating = true;
+}
+
+bool hello_there_face_loop(movement_event_t event, void *context) {
+    hello_there_state_t *state = (hello_there_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            // on activate and tick, if we are animating,
+            if (state->animating) {
+                // we display the current word,
+                if (state->current_word == 0) watch_display_string("Hello ", 4);
+                else watch_display_string(" there", 4);
+                // and increment it so that it will update on the next tick.
+                state->current_word = (state->current_word + 1) % 2;
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            // when the user presses 'alarm', we toggle the state of the animation. If animating,
+            // we stop; if stopped, we resume.
+            state->animating = !state->animating;   
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // This low energy mode update occurs once a minute, if the watch face is in the
+            // foreground when Movement enters low energy mode. We have the option of supporting
+            // this mode, but since our watch face animates once a second, the "Hello there" face
+            // isn't very useful in this mode. So we choose not to support it. (continued below)
+            break;
+        case EVENT_TIMEOUT:
+            // ... Instead, we respond to the timeout event. This event happens after a configurable
+            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
+            // resign control if we want to, and in this case, we do.
+            // This function will return the watch to the first screen (usually a simple clock),
+            // and it will do it long before the watch enters low energy mode. This ensures we
+            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
+            movement_move_to_face(0);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void hello_there_face_resign(void *context) {
+    // our watch face, like most watch faces, has nothing special to do when resigning.
+    // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
+    (void) context;
+}
diff --git a/legacy/watch_faces/demo/hello_there_face.h b/legacy/watch_faces/demo/hello_there_face.h
new file mode 100644 (file)
index 0000000..df942d0
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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 HELLO_THERE_FACE_H_
+#define HELLO_THERE_FACE_H_
+
+/*
+ * HELLO THERE FACE
+ *
+ * A simple demo that displays the word "Hello" and then the word "there",
+ * on an endless loop. Press ALARM to pause or resume the animation.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    uint8_t current_word;
+    bool animating;
+} hello_there_state_t;
+
+void hello_there_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void hello_there_face_activate(void *context);
+bool hello_there_face_loop(movement_event_t event, void *context);
+void hello_there_face_resign(void *context);
+
+#define hello_there_face ((const watch_face_t){ \
+    hello_there_face_setup, \
+    hello_there_face_activate, \
+    hello_there_face_loop, \
+    hello_there_face_resign, \
+    NULL, \
+})
+
+#endif // HELLO_THERE_FACE_H_
diff --git a/legacy/watch_faces/demo/lis2dw_logging_face.c b/legacy/watch_faces/demo/lis2dw_logging_face.c
new file mode 100644 (file)
index 0000000..467a99f
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "lis2dw_logging_face.h"
+#include "lis2dw.h"
+#include "watch.h"
+
+// This watch face is just for testing; if we want to build accelerometer support, it will likely have to be part of Movement itself.
+// The watch face only logs events when it is on screen and not in low energy mode, so you should set LE mode to Never when using it
+// and make it the first watch face in the list (so we come back to it from other modes).
+// On an interrupt, it flashes the Signal icon, and displays the axis or axes that were over the threshold.
+// The main display contains, from left to right, the number of interrupt events that were detected in each of the last three minutes.
+// Pressing the alarm button enters the log mode, where the main display shows the number of interrupts detected in each of the last
+// 24 hours (the hour is shown in the top right digit and AM/PM indicator, if the clock is set to 12 hour mode)
+
+static void _lis2dw_logging_face_update_display(lis2dw_logger_state_t *logger_state, lis2dw_wakeup_source_t wakeup_source) {
+    char buf[14];
+    char time_indication_character;
+    int8_t pos;
+    watch_date_time_t date_time;
+
+    if (logger_state->log_ticks) {
+        pos = (logger_state->data_points - 1 - logger_state->display_index) % LIS2DW_LOGGING_NUM_DATA_POINTS;
+        if (pos < 0) {
+            watch_clear_colon();
+            sprintf(buf, "NO   data ");
+        } else {
+            date_time = logger_state->data[pos].timestamp;
+            watch_set_colon();
+            if (movement_clock_mode_24h()) {
+                watch_set_indicator(WATCH_INDICATOR_24H);
+            } else {
+                if (date_time.unit.hour > 11) watch_set_indicator(WATCH_INDICATOR_PM);
+                date_time.unit.hour %= 12;
+                if (date_time.unit.hour == 0) date_time.unit.hour = 12;
+            }
+            switch (logger_state->axis_index) {
+                case 0:
+                    sprintf(buf, "3A%2d%02d%4lu", date_time.unit.hour, date_time.unit.minute, logger_state->data[pos].x_interrupts + logger_state->data[pos].y_interrupts + logger_state->data[pos].z_interrupts);
+                    break;
+                case 1:
+                    sprintf(buf, "XA%2d%02d%4lu", date_time.unit.hour, date_time.unit.minute, logger_state->data[pos].x_interrupts);
+                    break;
+                case 2:
+                    sprintf(buf, "YA%2d%02d%4lu", date_time.unit.hour, date_time.unit.minute, logger_state->data[pos].y_interrupts);
+                    break;
+                case 3:
+                    sprintf(buf, "ZA%2d%02d%4lu", date_time.unit.hour, date_time.unit.minute, logger_state->data[pos].z_interrupts);
+                    break;
+            }
+        }
+    } else {
+        date_time = watch_rtc_get_date_time();
+        watch_clear_colon();
+        watch_clear_indicator(WATCH_INDICATOR_PM);
+        watch_clear_indicator(WATCH_INDICATOR_24H);
+        if ((59 - date_time.unit.second) < 10) time_indication_character = '0' + (59 - date_time.unit.second);
+        else time_indication_character = (date_time.unit.second % 2) ? 'i' : '_';
+        sprintf(buf, "%c%c%c%c%2d%2d%2d",
+            (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_Y) ? 'Y' : ' ',
+            (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_X) ? 'X' : ' ',
+            (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_Z) ? 'Z' : ' ',
+            time_indication_character,
+            logger_state->interrupts[0],
+            logger_state->interrupts[1],
+            logger_state->interrupts[2]);
+    }
+    watch_display_string(buf, 0);
+}
+
+static void _lis2dw_logging_face_log_data(lis2dw_logger_state_t *logger_state) {
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    // we get this call 15 minutes late; i.e. at 6:15 we're logging events for 6:00.
+    // so: if we're at the top of the hour, roll the hour back too (7:00 task logs data for 6:45)
+    if (date_time.unit.minute == 0) date_time.unit.hour = (date_time.unit.hour + 23) % 24;
+
+    // // then roll the minute back.
+    date_time.unit.minute = (date_time.unit.minute + 45) % 60;
+
+    size_t pos = logger_state->data_points % LIS2DW_LOGGING_NUM_DATA_POINTS;
+    logger_state->data[pos].timestamp.reg = date_time.reg;
+    logger_state->data[pos].x_interrupts = logger_state->x_interrupts_this_hour;
+    logger_state->data[pos].y_interrupts = logger_state->y_interrupts_this_hour;
+    logger_state->data[pos].z_interrupts = logger_state->z_interrupts_this_hour;
+    logger_state->data_points++;
+    logger_state->x_interrupts_this_hour = 0;
+    logger_state->y_interrupts_this_hour = 0;
+    logger_state->z_interrupts_this_hour = 0;
+}
+
+void lis2dw_logging_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(lis2dw_logger_state_t));
+        memset(*context_ptr, 0, sizeof(lis2dw_logger_state_t));
+        watch_enable_i2c();
+        lis2dw_begin();
+        lis2dw_set_low_power_mode(LIS2DW_LP_MODE_2); // lowest power 14-bit mode, 25 Hz is 3.5 µA @ 1.8V w/ low noise, 3µA without
+        lis2dw_set_low_noise_mode(true); // consumes a little more power
+        lis2dw_set_range(LIS2DW_CTRL6_VAL_RANGE_4G);
+        lis2dw_set_data_rate(LIS2DW_DATA_RATE_25_HZ); // is this enough for training?
+        // threshold is 1/64th of full scale, so for a FS of ±4G this is 1.25G
+        lis2dw_configure_wakeup_int1(10, true, false);
+    }
+}
+
+void lis2dh_logging_face_activate(void *context) {
+    lis2dh_logger_state_t *logger_state = (lis2dh_logger_state_t *)context;
+    logger_state->display_index = 0;
+    logger_state->log_ticks = 0;
+    HAL_GPIO_A4_in();
+}
+
+bool lis2dw_logging_face_loop(movement_event_t event, void *context) {
+    lis2dw_logger_state_t *logger_state = (lis2dw_logger_state_t *)context;
+    lis2dw_wakeup_source_t wakeup_source = 0;
+    lis2dw_interrupt_source_t interrupt_source = 0;
+
+    switch (event.event_type) {
+        case EVENT_LIGHT_BUTTON_DOWN:
+            logger_state->axis_index = (logger_state->axis_index + 1) % 4;
+            logger_state->log_ticks = 255;
+            _lis2dw_logging_face_update_display(logger_state, wakeup_source);
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            if (logger_state->log_ticks) logger_state->display_index = (logger_state->display_index + 1) % LIS2DW_LOGGING_NUM_DATA_POINTS;
+            logger_state->log_ticks = 255;
+            logger_state->axis_index = 0;
+            _lis2dw_logging_face_update_display(logger_state, wakeup_source);
+            break;
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            if (logger_state->log_ticks > 0) {
+                logger_state->log_ticks--;
+            } else {
+                logger_state->display_index = 0;
+            }
+            interrupt_source = lis2dw_get_interrupt_source();
+            if (interrupt_source) {
+                watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+                wakeup_source = lis2dw_get_wakeup_source();
+                logger_state->interrupts[0]++;
+                if (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_X) logger_state->x_interrupts_this_hour++;
+                if (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_Y) logger_state->y_interrupts_this_hour++;
+                if (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_Z) logger_state->z_interrupts_this_hour++;
+            } else {
+                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+            }
+            _lis2dw_logging_face_update_display(logger_state, wakeup_source);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            _lis2dw_logging_face_log_data(logger_state);
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void lis2dw_logging_face_resign(void *context) {
+    (void) context;
+    HAL_GPIO_A4_off();
+}
+
+movement_watch_face_advisory_t lis2dw_logging_face_advise(void *context) {
+    lis2dw_logger_state_t *logger_state = (lis2dw_logger_state_t *)context;
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    movement_watch_face_advisory_t retval = { 0 };
+
+    // this is kind of an abuse of the API, but, let's use the 1 minute tick to shift all our data over.
+    logger_state->interrupts[2] = logger_state->interrupts[1];
+    logger_state->interrupts[1] = logger_state->interrupts[0];
+    logger_state->interrupts[0] = 0;
+
+    // and do our logging task every 15 minutes
+    retval.wants_background_task = date_time.unit.minute % 15 == 0;
+
+    return retval;
+}
diff --git a/legacy/watch_faces/demo/lis2dw_logging_face.h b/legacy/watch_faces/demo/lis2dw_logging_face.h
new file mode 100644 (file)
index 0000000..49d48ee
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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 LIS2DW_LOGGING_FACE_H_
+#define LIS2DW_LOGGING_FACE_H_
+
+/*
+ * LIS2DW Accelerometer Data Logger
+ *
+ * This is an experimental watch face for logging data on the “Sensor Watch
+ * Motion Express” board. I will add more documentation for this watch face
+ * once this sensor board is more widely available.
+ */
+
+#include "movement.h"
+#include "watch.h"
+
+#define LIS2DW_LOGGING_NUM_DATA_POINTS (96)
+
+typedef struct {
+    watch_date_time_t timestamp;
+    uint32_t x_interrupts;
+    uint32_t y_interrupts;
+    uint32_t z_interrupts;
+} lis2dw_logger_data_point_t;
+
+typedef struct {
+    uint8_t display_index;  // the index we are displaying on screen
+    uint8_t axis_index;     // the index we are displaying on screen
+    uint8_t log_ticks;      // when the user taps the ALARM button, we enter log mode
+    int32_t data_points;    // the absolute number of data points logged
+    uint8_t interrupts[3];  // the number of interrupts we have logged in each of the last 3 minutes
+    uint32_t x_interrupts_this_hour;  // the number of interrupts we have logged in the last hour
+    uint32_t y_interrupts_this_hour;  // the number of interrupts we have logged in the last hour
+    uint32_t z_interrupts_this_hour;  // the number of interrupts we have logged in the last hour
+    lis2dw_logger_data_point_t data[LIS2DW_LOGGING_NUM_DATA_POINTS];
+} lis2dw_logger_state_t;
+
+void lis2dw_logging_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void lis2dw_logging_face_activate(void *context);
+bool lis2dw_logging_face_loop(movement_event_t event, void *context);
+void lis2dw_logging_face_resign(void *context);
+movement_watch_face_advisory_t lis2dw_logging_face_advise(void *context);
+
+#define lis2dw_logging_face ((const watch_face_t){ \
+    lis2dw_logging_face_setup, \
+    lis2dw_logging_face_activate, \
+    lis2dw_logging_face_loop, \
+    lis2dw_logging_face_resign, \
+    lis2dw_logging_face_advise, \
+})
+
+#endif // LIS2DW_LOGGING_FACE_H_
diff --git a/legacy/watch_faces/sensor/accelerometer_data_acquisition_face.c b/legacy/watch_faces/sensor/accelerometer_data_acquisition_face.c
new file mode 100644 (file)
index 0000000..7258474
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "accelerometer_data_acquisition_face.h"
+#include "watch_utility.h"
+#include "lis2dw.h"
+#include "spiflash.h"
+
+#define ACCELEROMETER_RANGE LIS2DW_RANGE_4_G
+#define ACCELEROMETER_LPMODE LIS2DW_LP_MODE_2
+#define ACCELEROMETER_FILTER LIS2DW_BANDWIDTH_FILTER_DIV2
+#define ACCELEROMETER_LOW_NOISE true
+#define SECONDS_TO_RECORD 15
+
+static const char activity_types[][3] = {
+    "TE",   // Testing
+    "ID",   // Idle
+    "OF",   // Off-wrist
+    "SL",   // Sleeping
+    "WH",   // Washing Hands
+    "WA",   // Walking
+    "WB",   // Walking with Beverage
+    "JO",   // Jogging
+    "RU",   // Running
+    "BI",   // Biking
+    "HI",   // Hiking
+    "EL",   // Elliptical
+    "SU",   // Stairs Up
+    "SD",   // Stairs Down
+    "WL",   // Weight Lifting
+};
+
+static void update(accelerometer_data_acquisition_state_t *state);
+static void update_settings(accelerometer_data_acquisition_state_t *state);
+static void advance_current_setting(accelerometer_data_acquisition_state_t *state);
+static void start_reading(accelerometer_data_acquisition_state_t *state);
+static void continue_reading(accelerometer_data_acquisition_state_t *state);
+static void finish_reading(accelerometer_data_acquisition_state_t *state);
+static bool wait_for_flash_ready(void);
+static int16_t get_next_available_page(void);
+static void write_buffer_to_page(uint8_t *buf, uint16_t page);
+static void write_page(accelerometer_data_acquisition_state_t *state);
+static void log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading, uint8_t centiseconds);
+
+void accelerometer_data_acquisition_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)*context_ptr;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(accelerometer_data_acquisition_state_t));
+        memset(*context_ptr, 0, sizeof(accelerometer_data_acquisition_state_t));
+        state = (accelerometer_data_acquisition_state_t *)*context_ptr;
+        state->beep_with_countdown = true;
+        state->countdown_length = 3;
+    }
+    spi_flash_init();
+    wait_for_flash_ready();
+    uint8_t buf[256] = {0xFF};
+    spi_flash_read_data(0, buf, 256);
+    if (buf[0] & 0xF0) {
+        // mark first four pages as used
+        buf[0] = 0x0F;
+        wait_for_flash_ready();
+        HAL_GPIO_A3_clr();
+        spi_flash_command(CMD_ENABLE_WRITE);
+        wait_for_flash_ready();
+        spi_flash_write_data(0, buf, 256);
+    }
+
+}
+
+void accelerometer_data_acquisition_face_activate(void *context) {
+    accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
+    state->next_available_page = get_next_available_page();
+}
+
+bool accelerometer_data_acquisition_face_loop(movement_event_t event, void *context) {
+    accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            switch (state->mode) {
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
+                    update(state);
+                    if (state->repeat_ticks > 0) {
+                        state->repeat_ticks--;
+                        if (state->repeat_ticks == 0) {
+                            state->countdown_ticks = state->countdown_length;
+                            state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN;
+                        }
+                    }
+                    break;
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
+                    if (state->next_available_page < 0) {
+                        state->countdown_ticks = 0;
+                        state->repeat_ticks = 0;
+                        state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
+                    }
+                    if (state->countdown_ticks > 0) {
+                        state->countdown_ticks--;
+                        printf("countdown: %d\n", state->countdown_ticks);
+                        if (state->countdown_ticks == 0) {
+                            // at zero, begin reading
+                            state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING;
+                            state->reading_ticks = SECONDS_TO_RECORD + 1;
+                            // also beep if the user asked for it
+                            if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
+                            start_reading(state);
+                        } else if (state->countdown_ticks < 3) {
+                            // beep for last two ticks before reading
+                            if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C5, 75);
+                        }
+                    }
+                    update(state);
+                    break;
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING:
+                    if (state->reading_ticks > 0) {
+                        state->reading_ticks--;
+                        if (state->reading_ticks > 0) {
+                            continue_reading(state);
+                        } else {
+                            finish_reading(state);
+                            state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
+                            watch_buzzer_play_note(BUZZER_NOTE_C4, 125);
+                            watch_buzzer_play_note(BUZZER_NOTE_REST, 50);
+                            watch_buzzer_play_note(BUZZER_NOTE_C4, 125);
+                        }
+                    }
+                    update(state);
+                    break;
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
+                    update_settings(state);
+                    break;
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            switch (state->mode) {
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
+                    state->activity_type_index = (state->activity_type_index + 1) % (sizeof(activity_types) / sizeof(activity_types[0]));
+                    update(state);
+                    break;
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
+                    state->settings_page++;
+                    if (state->settings_page > ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT) {
+                        state->settings_page = 0;
+                        state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
+                        update(state);
+                    } else {
+                        update_settings(state);
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            movement_illuminate_led();
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            printf("Alarm up! Mode is %d\n", state->mode);
+            switch (state->mode) {
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
+                    state->countdown_ticks = state->countdown_length;
+                    printf("Setting countdown ticks to %d\n", state->countdown_ticks);
+                    state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN;
+                    printf("and mode to %d\n", state->mode);
+                    update(state);
+                    break;
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
+                    // cancel countdown
+                    state->countdown_ticks = 0;
+                    state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
+                    update(state);
+                    break;
+                case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
+                    advance_current_setting(state);
+                    update_settings(state);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            printf("Alarm long\n");
+            if (state->mode == ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE) {
+                state->repeat_ticks = 0;
+                state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS;
+                update_settings(state);
+            }
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void accelerometer_data_acquisition_face_resign(void *context) {
+    accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
+    if (state->reading_ticks) {
+        state->reading_ticks = 0;
+        finish_reading(state);
+    }
+    state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
+    state->settings_page = 0;
+    state->countdown_ticks = 0;
+    state->repeat_ticks = 0;
+    state->reading_ticks = 0;
+}
+
+static void update(accelerometer_data_acquisition_state_t *state) {
+    char buf[14];
+    uint8_t ticks = 0;
+    switch (state->mode) {
+        case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
+            ticks = state->countdown_length;
+            break;
+        case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
+            ticks = state->countdown_ticks;
+            break;
+        case ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING:
+            ticks = state->reading_ticks;
+            break;
+        default:
+            ticks = 0;
+            break;
+    }
+    sprintf(buf, "%s%2dre%2d#o",
+            activity_types[state->activity_type_index],
+            ticks,
+            (8192 - state->next_available_page) / 82);
+    watch_display_string(buf, 0);
+
+    watch_set_colon();
+
+    // special case: display full if full, <1% if nearly full
+    if (state->next_available_page < 0) watch_display_string(" FUL", 6);
+    else if (state->next_available_page > 8110) watch_display_string("<1", 6);
+
+    // Bell if beep enabled
+    if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL);
+    else watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+    // Signal if sensing
+    if (state->reading_ticks) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+
+    // LAP if repeating
+    if (state->repeat_ticks) watch_set_indicator(WATCH_INDICATOR_LAP);
+    else watch_clear_indicator(WATCH_INDICATOR_LAP);
+
+}
+
+static void update_settings(accelerometer_data_acquisition_state_t *state) {
+    char buf[13];
+    watch_clear_colon();
+    if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL);
+    else watch_clear_indicator(WATCH_INDICATOR_BELL);
+    switch (state->settings_page) {
+        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND:
+            sprintf(buf, "SO  Beep %c", state->beep_with_countdown ? 'Y' : 'N');
+            watch_display_string(buf, 0);
+            break;
+        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY:
+            sprintf(buf, "DL  %2d SeC", state->countdown_length);
+            watch_display_string(buf, 0);
+            break;
+        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT:
+            if (state->repeat_interval == 0) {
+                watch_display_string("rE  none  ", 0);
+            } else {
+                sprintf(buf, "rE  %2dn&in", state->repeat_interval / 60);
+                watch_display_string(buf, 0);
+            }
+            break;
+    }
+}
+
+static void advance_current_setting(accelerometer_data_acquisition_state_t *state) {
+    switch (state->settings_page) {
+        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND:
+            state->beep_with_countdown = !state->beep_with_countdown;
+            break;
+        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY:
+            // this is so lazy, i'm sorry
+            if (state->countdown_length == 1) state->countdown_length = 3;
+            else if (state->countdown_length == 3) state->countdown_length = 10;
+            else if (state->countdown_length == 10) state->countdown_length = 30;
+            else state->countdown_length = 1;
+            break;
+        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT:
+            if (state->repeat_interval == 0) state->repeat_interval = 60;
+            else if (state->repeat_interval == 60) state->repeat_interval = 600;
+            else if (state->repeat_interval == 600) state->repeat_interval = 1800;
+            else if (state->repeat_interval == 1800) state->repeat_interval = 3600;
+            else state->repeat_interval = 0;
+            break;
+    }
+}
+
+static int16_t get_next_available_page(void) {
+    uint8_t buf[256] = {0};
+
+    uint16_t page = 0;
+    for(int16_t i = 0; i < 4; i++) {
+        wait_for_flash_ready();
+        spi_flash_read_data(i * 256, buf, 256);
+        for(int16_t j = 0; j < 256; j++) {
+            if(buf[j] == 0) {
+                page += 8;
+            } else {
+                page += __builtin_clz(((uint32_t)buf[j]) << 24);
+                break;
+            }
+        }
+    }
+
+    if (page >= 8192) return -1;
+
+    return page;
+}
+
+static void write_buffer_to_page(uint8_t *buf, uint16_t page) {
+    uint32_t address = 256 * page;
+
+    wait_for_flash_ready();
+    HAL_GPIO_A3_clr();
+    spi_flash_command(CMD_ENABLE_WRITE);
+    wait_for_flash_ready();
+    HAL_GPIO_A3_clr();
+    spi_flash_write_data(address, buf, 256);
+    wait_for_flash_ready();
+
+    uint8_t buf2[256];
+    HAL_GPIO_A3_clr();
+    spi_flash_read_data(address, buf2, 256);
+    wait_for_flash_ready();
+
+    uint8_t used_pages[256] = {0xFF};
+    uint16_t address_to_mark_used = page / 8;
+    uint8_t header_page = address_to_mark_used / 256;
+    uint8_t used_byte = 0x7F >> (page % 8);
+    uint8_t offset_in_buf = address_to_mark_used % 256;
+
+    printf("\twrite 256 bytes to address %ld, page %d.\n", address, page);
+    for(int i = 0; i < 256; i++) {
+        if (buf[i] != buf2[i]) {
+            printf("\tData mismatch detected at offset  %d: %d != %d.\n", i, buf[i], buf2[i]);
+        }
+    }
+
+    HAL_GPIO_A3_clr();
+    spi_flash_read_data(header_page * 256, used_pages, 256);
+    used_pages[offset_in_buf] = used_byte;
+    HAL_GPIO_A3_clr();
+    spi_flash_command(CMD_ENABLE_WRITE);
+    wait_for_flash_ready();
+    HAL_GPIO_A3_clr();
+    spi_flash_write_data(header_page * 256, used_pages, 256);
+    wait_for_flash_ready();
+}
+
+static bool wait_for_flash_ready(void) {
+    HAL_GPIO_A3_clr();
+    bool ok = true;
+    uint8_t read_status_response[1] = {0x00};
+    do {
+        ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1);
+    } while ((read_status_response[0] & 0x3) != 0);
+    delay_ms(1); // why do i need this?
+    HAL_GPIO_A3_set();
+    return ok;
+}
+
+static void write_page(accelerometer_data_acquisition_state_t *state) {
+    if (state->next_available_page > 0) {
+        write_buffer_to_page((uint8_t *)(state->records), state->next_available_page);
+        wait_for_flash_ready();
+        state->next_available_page++;
+    }
+    state->pos = 0;
+    memset(state->records, 0xFF, sizeof(state->records));
+}
+
+static void log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading, uint8_t centiseconds) {
+    accelerometer_data_acquisition_record_t record;
+    record.data.x.record_type = ACCELEROMETER_DATA_ACQUISITION_DATA;
+    record.data.y.lpmode = ACCELEROMETER_LPMODE;
+    record.data.z.filter = ACCELEROMETER_FILTER;
+    record.data.x.accel = (reading.x >> 2) + 8192;
+    record.data.y.accel = (reading.y >> 2) + 8192;
+    record.data.z.accel = (reading.z >> 2) + 8192;
+    record.data.counter = 100 * (SECONDS_TO_RECORD - state->reading_ticks + 1) + centiseconds;
+    printf("logged data point for %d\n", record.data.counter);
+    state->records[state->pos++] = record;
+    if (state->pos >= 32) {
+        write_page(state);
+    }
+}
+
+static void start_reading(accelerometer_data_acquisition_state_t *state) {
+    printf("Start reading\n");
+    watch_enable_i2c();
+    lis2dw_begin();
+    lis2dw_set_data_rate(LIS2DW_DATA_RATE_25_HZ);
+    lis2dw_set_range(ACCELEROMETER_RANGE);
+    lis2dw_set_low_power_mode(ACCELEROMETER_LPMODE);
+    lis2dw_set_bandwidth_filtering(ACCELEROMETER_FILTER);
+    if (ACCELEROMETER_LOW_NOISE) lis2dw_set_low_noise_mode(true);
+    lis2dw_enable_fifo();
+
+    accelerometer_data_acquisition_record_t record;
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
+    record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER;
+    record.header.info.range = ACCELEROMETER_RANGE;
+    record.header.info.temperature = lis2dw_get_temperature();
+    record.header.char1 = activity_types[state->activity_type_index][0];
+    record.header.char2 = activity_types[state->activity_type_index][1];
+    record.header.timestamp = state->starting_timestamp;
+
+    state->records[state->pos++] = record;
+    lis2dw_fifo_t fifo;
+    lis2dw_read_fifo(&fifo); // dump the fifo, this starts a fresh round of data in continue_reading
+}
+
+static void continue_reading(accelerometer_data_acquisition_state_t *state) {
+    printf("Continue reading\n");
+    lis2dw_fifo_t fifo;
+    lis2dw_read_fifo(&fifo);
+
+    fifo.count = min(fifo.count, 25); // hacky, but we need a consistent data rate; if we got a 26th data point, chuck it.
+    uint8_t offset = 4 * (25 - fifo.count); // also hacky: we're sometimes short at the start. align to beginning of next second.
+    // TODO: use the threshold interrupt for this, will mean we get consistent 25 Hz data as the accelerometer sees it.
+
+    for(int i = 0; i < fifo.count; i++) {
+        log_data_point(state, fifo.readings[i], i * 4 + offset);
+    }
+}
+
+static void finish_reading(accelerometer_data_acquisition_state_t *state) {
+    printf("Finish reading\n");
+    if (state->pos != 0) {
+        write_page(state);
+    }
+    lis2dw_set_data_rate(LIS2DW_DATA_RATE_POWERDOWN);
+    watch_disable_i2c();
+
+    state->repeat_ticks = state->repeat_interval;
+}
diff --git a/legacy/watch_faces/sensor/accelerometer_data_acquisition_face.h b/legacy/watch_faces/sensor/accelerometer_data_acquisition_face.h
new file mode 100644 (file)
index 0000000..107faea
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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 ACCELEROMETER_DATA_ACQUISITION_FACE_H_
+#define ACCELEROMETER_DATA_ACQUISITION_FACE_H_
+
+/*
+ * ACCELEROMETER DATA ACQUISITION
+ *
+ * TODO: Add description here, including controls.
+ */
+
+#include "movement.h"
+
+#define ACCELEROMETER_DATA_ACQUISITION_INVALID ((uint64_t)(0b11))   // all bits are 1 when the flash is erased
+#define ACCELEROMETER_DATA_ACQUISITION_HEADER ((uint64_t)(0b10))
+#define ACCELEROMETER_DATA_ACQUISITION_DATA ((uint64_t)(0b01))    
+#define ACCELEROMETER_DATA_ACQUISITION_DELETED ((uint64_t)(0b00))   // You can always write a 0 to any 1 bit
+
+typedef union {
+    struct {
+        struct {
+            uint16_t record_type : 2;   // see above, helps us identify record types when reading back
+            uint16_t range : 2;         // accelerometer range (see lis2dw_range_t)
+            uint16_t temperature : 12;  // raw value from the temperature sensor
+        } info;
+        uint8_t char1 : 8;              // First character of the activity type
+        uint8_t char2 : 8;              // Second character of the activity type
+        uint32_t timestamp : 32;        // UNIX timestamp for the measurement
+    } header;
+    struct {
+        struct {
+            uint16_t record_type : 2;   // duplicate; this is the same field as info above
+            uint16_t accel : 14;        // X acceleration value, raw, offset by 8192
+        } x;
+        struct {
+            uint16_t lpmode : 2;        // low power mode (see lis2dw_low_power_mode_t)
+            uint16_t accel : 14;        // Y acceleration value, raw, offset by 8192
+        } y;
+        struct {
+            uint16_t filter : 2;        // bandwidth filtering selection (see lis2dw_bandwidth_filtering_mode_t)
+            uint16_t accel : 14;        // Z acceleration value, raw, offset by 8192
+        } z;
+        uint32_t counter : 16;          // number of centiseconds since timestamp in header
+    } data;
+    uint64_t value;
+} accelerometer_data_acquisition_record_t;
+
+typedef enum {
+    ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE,
+    ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN,
+    ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING,
+    ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS,
+} accelerometer_data_acquisition_mode_t;
+
+typedef enum {
+    ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND,
+    ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY,
+    ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT,
+    // ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_NAME,
+} accelerometer_data_acquisition_settings_page_t;
+
+typedef struct {
+    // mode
+    accelerometer_data_acquisition_mode_t mode;
+    accelerometer_data_acquisition_settings_page_t settings_page;
+    // current settings
+    uint8_t activity_type_index;   // active activity type
+    bool beep_with_countdown;   // should we beep at the countdown
+    uint8_t countdown_length;   // how many seconds to count down
+    uint16_t repeat_interval;   // how many seconds to wait for a repeat
+    // info about the flash chip
+    int16_t next_available_page;
+    // transient properties
+    uint8_t countdown_ticks;
+    uint8_t repeat_ticks;
+    uint8_t reading_ticks;
+    uint32_t starting_timestamp;
+    accelerometer_data_acquisition_record_t records[32];
+    uint16_t pos;
+} accelerometer_data_acquisition_state_t;
+
+void accelerometer_data_acquisition_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void accelerometer_data_acquisition_face_activate(void *context);
+bool accelerometer_data_acquisition_face_loop(movement_event_t event, void *context);
+void accelerometer_data_acquisition_face_resign(void *context);
+
+#define accelerometer_data_acquisition_face ((const watch_face_t){ \
+    accelerometer_data_acquisition_face_setup, \
+    accelerometer_data_acquisition_face_activate, \
+    accelerometer_data_acquisition_face_loop, \
+    accelerometer_data_acquisition_face_resign, \
+    NULL, \
+})
+
+#endif // ACCELEROMETER_DATA_ACQUISITION_FACE_H_
diff --git a/legacy/watch_faces/sensor/alarm_thermometer_face.c b/legacy/watch_faces/sensor/alarm_thermometer_face.c
new file mode 100644 (file)
index 0000000..62b0c88
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Christian Buschau
+ *
+ * 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.
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "alarm_thermometer_face.h"
+#include "thermistor_driver.h"
+
+static float _alarm_thermometer_face_update(bool in_fahrenheit) {
+    thermistor_driver_enable();
+    float temperature_c = thermistor_driver_get_temperature();
+    char buf[14];
+    if (in_fahrenheit) {
+        sprintf(buf, "%4.1f#F", temperature_c * 1.8 + 32.0);
+    } else {
+        sprintf(buf, "%4.1f#C", temperature_c);
+    }
+    watch_display_string(buf, 4);
+    thermistor_driver_disable();
+    return temperature_c;
+}
+
+static void _alarm_thermometer_face_clear(int last[]) {
+    for (size_t i = 0; i < LAST_SIZE; i++) {
+        last[i] = INT_MIN;
+    }
+}
+
+void alarm_thermometer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(alarm_thermometer_state_t));
+        memset(*context_ptr, 0, sizeof(alarm_thermometer_state_t));
+    }
+}
+
+void alarm_thermometer_face_activate(void *context) {
+    alarm_thermometer_state_t *state = (alarm_thermometer_state_t *)context;
+    state->mode = MODE_NORMAL;
+    _alarm_thermometer_face_clear(state->last);
+    watch_display_string("AT", 0);
+}
+
+bool alarm_thermometer_face_loop(movement_event_t event, void *context) {
+    alarm_thermometer_state_t *state = (alarm_thermometer_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            _alarm_thermometer_face_update(movement_use_imperial_units());
+            break;
+        case EVENT_TICK:
+            if (watch_rtc_get_date_time().unit.second % 5 == 0) {
+                switch (state->mode) {
+                    case MODE_NORMAL:
+                        _alarm_thermometer_face_update(movement_use_imperial_units());
+                        break;
+                    case MODE_ALARM:
+                        for (size_t i = LAST_SIZE - 1; i > 0; i--) {
+                            state->last[i] = state->last[i - 1];
+                        }
+                        state->last[0] = roundf(_alarm_thermometer_face_update(movement_use_imperial_units()) * 10.0f);
+                        bool constant = true;
+                        for (size_t i = 1; i < LAST_SIZE; i++) {
+                            if (state->last[i - 1] != state->last[i]) {
+                                constant = false;
+                                break;
+                            }
+                        }
+                        if (constant) {
+                            state->mode = MODE_FREEZE;
+                            watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+                            movement_play_alarm();
+                        }
+                        break;
+                    case MODE_FREEZE:
+                        break;
+                }
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch (state->mode) {
+                case MODE_NORMAL:
+                    state->mode = MODE_ALARM;
+                    watch_set_indicator(WATCH_INDICATOR_BELL);
+                    _alarm_thermometer_face_clear(state->last);
+                    break;
+                case MODE_FREEZE:
+                    state->mode = MODE_NORMAL;
+                    watch_clear_indicator(WATCH_INDICATOR_BELL);
+                    watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+                    break;
+                case MODE_ALARM:
+                    state->mode = MODE_NORMAL;
+                    watch_clear_indicator(WATCH_INDICATOR_BELL);
+                    _alarm_thermometer_face_update(movement_use_imperial_units());
+                    break;
+            }
+            if (movement_button_should_sound()) {
+                watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->mode != MODE_FREEZE) {
+                movement_set_use_imperial_units(!movement_use_imperial_units());
+                _alarm_thermometer_face_update(movement_use_imperial_units());
+            }
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            if (!watch_sleep_animation_is_running()) {
+                state->mode = MODE_NORMAL;
+                watch_clear_indicator(WATCH_INDICATOR_BELL);
+                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+                watch_start_sleep_animation(1000);
+            }
+            if (watch_rtc_get_date_time().unit.minute % 5 == 0) {
+                _alarm_thermometer_face_update(movement_use_imperial_units());
+                watch_display_string("  ", 8);
+            }
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void alarm_thermometer_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/sensor/alarm_thermometer_face.h b/legacy/watch_faces/sensor/alarm_thermometer_face.h
new file mode 100644 (file)
index 0000000..6b1ecde
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 Christian Buschau
+ *
+ * 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 ALARM_THERMOMETER_FACE_H_
+#define ALARM_THERMOMETER_FACE_H_
+
+#include <limits.h>
+#include "movement.h"
+
+/*
+ * ALARM THERMOMETER
+ *
+ * This watch face shows the current temperature in degrees Celsius. Press and
+ * hold the alarm button to toggle between Celsius and Fahrenheit. Press and
+ * release the alarm button to start a "timer". The watch will sound an alarm
+ * when the temperature remains constant for at least 30 seconds and the
+ * temperature will stop updating until you press the alarm button. You can
+ * cancel the alarm by pressing the button again. If the temperature doesn't
+ * remain constant until the low energy timeout is reached, the alarm will stop.
+ * This is useful to measure e.g. the room temperature. If you lay off your
+ * watch from your wrist, it will take some time until it cools down, and will
+ * notify you when the measurement is constant enough.
+ * THIS WATCH FACE IS NOT INTENDED TO DIAGNOSE, TREAT, CURE OR PREVENT ANY
+ * DISEASE.
+ */
+
+#define LAST_SIZE 6
+
+typedef enum {
+    MODE_NORMAL,
+    MODE_ALARM,
+    MODE_FREEZE
+} alarm_thermometer_mode_t;
+
+typedef struct {
+    int last[LAST_SIZE];
+    alarm_thermometer_mode_t mode;
+} alarm_thermometer_state_t;
+
+void alarm_thermometer_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void alarm_thermometer_face_activate(void *context);
+bool alarm_thermometer_face_loop(movement_event_t event, void *context);
+void alarm_thermometer_face_resign(void *context);
+
+#define alarm_thermometer_face ((const watch_face_t){ \
+    alarm_thermometer_face_setup, \
+    alarm_thermometer_face_activate, \
+    alarm_thermometer_face_loop, \
+    alarm_thermometer_face_resign, \
+    NULL, \
+})
+
+#endif // ALARM_THERMOMETER_FACE_H_
diff --git a/legacy/watch_faces/sensor/lightmeter_face.c b/legacy/watch_faces/sensor/lightmeter_face.c
new file mode 100644 (file)
index 0000000..243613c
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 CC
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "lightmeter_face.h"
+#include "watch_utility.h"
+#include "watch_slcd.h"
+
+uint16_t lightmeter_mod(uint16_t m, uint16_t n) { return (m%n + n)%n; }  
+
+void lightmeter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(lightmeter_state_t));
+        lightmeter_state_t *state = (lightmeter_state_t*) *context_ptr;
+        state->waiting_for_conversion = 0;
+        state->lux = 0.0;
+        state->mode = 0;
+        state->iso = LIGHTMETER_ISO_100;
+        state->ap = LIGHTMETER_AP_4P0;
+    }
+}
+
+void lightmeter_face_activate(void *context) {
+    lightmeter_state_t *state = (lightmeter_state_t*) context;
+    state->waiting_for_conversion = 0;
+    lightmeter_show_ev(state); // Print most current reading
+    watch_enable_i2c();
+    return;
+}
+
+void lightmeter_show_ev(lightmeter_state_t *state) {
+
+    float ev = max(min(
+                 log2(state->lux) + 
+                 lightmeter_isos[state->iso].ev + 
+                 LIGHTMETER_CALIBRATION,
+            99), -9);
+    int evt = round(2*ev); // Truncated EV
+
+    // Print EV
+    char strbuff[7];
+    watch_clear_all_indicators();
+    watch_display_string("EV        ", 0); 
+
+    sprintf(strbuff, "%2i", (uint16_t) abs(evt/2)); // Print whole part of EV 
+    watch_display_string(strbuff, 2); 
+    if(evt%2) watch_set_indicator(WATCH_INDICATOR_LAP); // Indicate half stop
+    if(ev<0) watch_set_pixel(1,9);  // Indicate negative EV
+
+    // Handle lux mode
+    if(state->mode == 1) {
+        sprintf(strbuff, "%6.0f", min(state->lux, 999999.0)); 
+        watch_display_string(strbuff, 4); 
+        return;
+    }
+
+    // Find and print best shutter speed
+    uint16_t bestsh = 0;
+    float besterr = 1.0/0.0;
+    float errbuf = 1.0/0.0;
+    float comp_ev = ev + lightmeter_aps[state->ap].ev; 
+    for(uint16_t ind = 2; ind < LIGHTMETER_N_SHS; ind++) {
+        errbuf = comp_ev + lightmeter_shs[ind].ev;
+        if( fabs(errbuf) < fabs(besterr)) {
+            besterr = errbuf;
+            bestsh = ind;
+        }
+    }
+    if(besterr >= 0.5) watch_display_string(lightmeter_shs[LIGHTMETER_SH_HIGH].str, 4); 
+    else if(besterr <= -0.5) watch_display_string(lightmeter_shs[LIGHTMETER_SH_LOW].str, 4); 
+    else watch_display_string(lightmeter_shs[bestsh].str, 4); 
+
+    // Print aperture
+    watch_display_string(lightmeter_aps[state->ap].str, 7); 
+    return;
+}
+
+bool lightmeter_face_loop(movement_event_t event, void *context) {
+    lightmeter_state_t *state = (lightmeter_state_t*) context;
+    
+    opt3001_Config_t c;
+    switch (event.event_type) {
+        case EVENT_TICK:
+            if(state->waiting_for_conversion) { // Check if measurement is ready...
+                c = opt3001_readConfig(lightmeter_addr);
+                if(c.ConversionReady) {
+                    state->waiting_for_conversion = 0;
+                    opt3001_t result = opt3001_readResult(lightmeter_addr);
+                    state->lux = result.lux;
+                    lightmeter_show_ev(state); 
+                } 
+            }
+            break;
+
+        case EVENT_ALARM_BUTTON_UP: // Increment aperture 
+            state->ap = lightmeter_mod(state->ap+1, LIGHTMETER_N_APS);
+
+            lightmeter_show_ev(state); 
+            break;
+
+        case EVENT_LIGHT_BUTTON_UP: // Decrement aperture 
+            if(state->ap == 0) state->ap = LIGHTMETER_N_APS-1; 
+            else state->ap = lightmeter_mod(state->ap-1, LIGHTMETER_N_APS);
+
+            lightmeter_show_ev(state); 
+            break;
+
+        case EVENT_LIGHT_LONG_PRESS: // Cycle ISO
+            state->iso = lightmeter_mod(state->iso+1, LIGHTMETER_N_ISOS);
+
+            watch_clear_all_indicators();
+            watch_display_string("EV  ", 0); 
+            watch_display_string(lightmeter_isos[state->iso].str, 4); 
+            break;
+
+        case EVENT_ALARM_LONG_PRESS: // Take measurement
+            opt3001_writeConfig(lightmeter_addr, lightmeter_takeNewReading);
+            state->waiting_for_conversion = 1;
+
+            watch_clear_all_indicators();
+            watch_display_string("EV  ", 0); 
+            watch_display_string(lightmeter_isos[state->iso].str, 4); 
+            watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+            break;
+
+        case EVENT_MODE_LONG_PRESS: // Toggle mode
+            state->mode = !state->mode;
+            lightmeter_show_ev(state); 
+            break;
+
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+    return true;
+}
+
+void lightmeter_face_resign(void *context) {
+    (void) context;
+    opt3001_writeConfig(lightmeter_addr, lightmeter_off);
+    watch_disable_i2c();
+    return;
+}
diff --git a/legacy/watch_faces/sensor/lightmeter_face.h b/legacy/watch_faces/sensor/lightmeter_face.h
new file mode 100644 (file)
index 0000000..fcb7cda
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 CC
+ *
+ * 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 LIGHTMETER_FACE_H_
+#define LIGHTMETER_FACE_H_
+
+/*
+ * Aperture-priority Light Meter Face
+ *
+ * Tested with the "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001" flexboard.
+ * This flexboard could use a revision: 
+ *
+ *  - The thermistor components should be moved west a mm or flipped to the backside
+ *    to avoid stressing the flexboard against the processor so much.
+ *  - The 'no connect' pad falls off easily.
+ *
+ * Controls:
+ *
+ *  - Trigger a measurement by long-pressing Alarm.
+ *    Sensor integration is happening when the Signal indicator is on.
+ *
+ *  - ISO setting can be cycled by long-pressing Light.
+ *    During integration the current ISO setting will be displayed. 
+ *
+ *  - EV measurement in the top right: "LAP" indicates "half stop". 
+ *    So "LAP -1" means EV = -1.5. Likewise "LAP 13" means EV = +13.5  
+ *
+ *  - Aperture in the bottom right: the last 3 main digits are the f-stop. 
+ *    Adjust this number in half-stop increments using Alarm = +1/2 and Light = -1/2. 
+ *
+ *  - Best shutter speed in the bottom left: the first 3 digits are the shutter speed. 
+ *    Some special chars are needed here: "-" = seconds, "h" = extra half second, "K" = thousands.
+ *    "HI" or "LO" if there's no shutter in the dictionary within 0.5 stops of correct exposure.
+ *
+ *  - Mode long-press changes the main digits to show raw sensor lux measurements.
+ */
+
+#include "movement.h"
+#include "opt3001.h"
+
+#define LIGHTMETER_CALIBRATION 2.58
+typedef struct { 
+    char * str;
+    float ev;
+} lightmeter_ev_t;
+
+static const lightmeter_ev_t lightmeter_isos[] = {
+    {" i  25", -2},
+    {" i  50", -1},
+    {" i 100", 0},
+    {" i 160", 0.68},
+    {" i 200", 1},
+    {" i 400", 2},
+    {" i 800", 3},
+    {" i1600", 4}};
+typedef enum { 
+    LIGHTMETER_ISO_25, LIGHTMETER_ISO_50, LIGHTMETER_ISO_100, LIGHTMETER_ISO_160, LIGHTMETER_ISO_200, LIGHTMETER_ISO_400, LIGHTMETER_ISO_800, LIGHTMETER_ISO_1600, 
+    LIGHTMETER_N_ISOS
+} lightmeter_iso_t; 
+
+static const lightmeter_ev_t lightmeter_aps[] = {
+    {"1.4", 0},
+    {"1.8", -0.5},
+    {"2.0", -1},
+    {"2.4", -1.5},
+    {"2.8", -2},
+    {"3.3", -2.5},
+    {"4.0", -3},
+    {"4.8", -3.5},
+    {"5.6", -4},
+    {"6.7", -4.5},
+    {"8.0", -5},
+    {"9.5", -5.5},
+    {"11.", -6},
+    {"13.", -6.5},
+    {"16.", -7},
+    {"19.", -7.5},
+    {"22.", -8}};
+typedef enum { 
+    LIGHTMETER_AP_1P4, LIGHTMETER_AP_1P8, LIGHTMETER_AP_2P0, LIGHTMETER_AP_2P4, LIGHTMETER_AP_2P8, LIGHTMETER_AP_3P3, LIGHTMETER_AP_4P0, LIGHTMETER_AP_4P8, LIGHTMETER_AP_5P6, LIGHTMETER_AP_6P7, LIGHTMETER_AP_8P0, LIGHTMETER_AP_9P5, 
+    LIGHTMETER_AP_11, LIGHTMETER_AP_13, LIGHTMETER_AP_16, LIGHTMETER_AP_19, LIGHTMETER_AP_22,
+    LIGHTMETER_N_APS
+} lightmeter_ap_t; 
+
+static const lightmeter_ev_t lightmeter_shs[] = {
+    {"LO-",   99},
+    {"HI ",  -99},
+    {"30-",  5.0},
+    {"20-",  4.5},
+    {"15-",  4.0},
+    {"11-",  3.5},
+    {"8- ",  3.0},
+    {"6- ",  2.5},
+    {"4- ",  2.0},
+    {"3- ",  1.5},
+    {"2- ",  1.0},
+    {"1h-",  0.5},
+    {"1  ",  0.0},
+    {"1h ", -0.5},
+    {"2  ", -1.0},
+    {"3  ", -1.5},
+    {"4  ", -2.0},
+    {"6  ", -2.5},
+    {"8  ", -3.0},
+    {"12 ", -3.5},
+    {"15 ", -4.0},
+    {"20 ", -4.5},
+    {"30 ", -5.0},
+    {"45 ", -5.5},
+    {"60 ", -6.0},
+    {"90 ", -6.5},
+    {"125", -7.0},
+    {"180", -7.5},
+    {"250", -8.0},
+    {"350", -8.5},
+    {"500", -9.0},
+    {"750", -9.5},
+    {"1K ", -10.0},
+    {"1K5", -10.5},
+    {"2K ", -11.0},
+    {"3K ", -11.5},
+    {"4K ", -12.0},
+    {"6K ", -12.5},
+    {"8K ", -13.0}};
+typedef enum { 
+    LIGHTMETER_SH_LOW, LIGHTMETER_SH_HIGH, 
+    LIGHTMETER_SH_30S, LIGHTMETER_SH_20S, LIGHTMETER_SH_15S, LIGHTMETER_SH_11S, LIGHTMETER_SH_8S, LIGHTMETER_SH_6S, LIGHTMETER_SH_3S, LIGHTMETER_SH_4S, LIGHTMETER_SH_2S, LIGHTMETER_SH_1HS, 
+    LIGHTMETER_SH_1, LIGHTMETER_SH_1H, LIGHTMETER_SH_2, LIGHTMETER_SH_3, LIGHTMETER_SH_4, LIGHTMETER_SH_6, LIGHTMETER_SH_8, LIGHTMETER_SH_12, LIGHTMETER_SH_15, LIGHTMETER_SH_20, LIGHTMETER_SH_30, LIGHTMETER_SH_45, LIGHTMETER_SH_60, LIGHTMETER_SH_90, LIGHTMETER_SH_125, LIGHTMETER_SH_180, LIGHTMETER_SH_250, LIGHTMETER_SH_350, LIGHTMETER_SH_500, LIGHTMETER_SH_750,
+    LIGHTMETER_SH_1K, LIGHTMETER_SH_1K5, LIGHTMETER_SH_2K, LIGHTMETER_SH_3K, LIGHTMETER_SH_4K, LIGHTMETER_SH_6K, LIGHTMETER_SH_8K,
+    LIGHTMETER_N_SHS
+} lightmeter_sh_t; 
+
+typedef struct {
+    lightmeter_iso_t iso;
+    lightmeter_ap_t ap;
+    bool waiting_for_conversion;
+    float lux;
+    int mode; 
+} lightmeter_state_t;
+
+static const opt3001_Config_t lightmeter_takeNewReading = { 
+    .RangeNumber = 0B1100,
+    .ConversionTime = 0B1,
+    .Latch = 0B1,
+    .ModeOfConversionOperation = 0B01
+};
+
+static const opt3001_Config_t lightmeter_off = { 
+    .ModeOfConversionOperation = 0B00
+};
+
+uint16_t lightmeter_mod(uint16_t m, uint16_t n); 
+
+void lightmeter_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void lightmeter_face_activate(void *context);
+void lightmeter_show_ev(lightmeter_state_t *state);
+bool lightmeter_face_loop(movement_event_t event, void *context);
+void lightmeter_face_resign(void *context);
+
+static const uint8_t lightmeter_addr = 0x44;
+
+#define lightmeter_face ((const watch_face_t){ \
+    lightmeter_face_setup, \
+    lightmeter_face_activate, \
+    lightmeter_face_loop, \
+    lightmeter_face_resign, \
+    NULL, \
+})
+
+#endif // LIGHTMETER_FACE_H_
diff --git a/legacy/watch_faces/sensor/minmax_face.c b/legacy/watch_faces/sensor/minmax_face.c
new file mode 100644 (file)
index 0000000..7f52006
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Mark Blyth
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "minmax_face.h"
+#include "thermistor_driver.h"
+#include "watch.h"
+
+
+static float _get_displayed_temperature_c(minmax_state_t *state){
+    float min_temp = 1000;
+    float max_temp = -1000;
+    for(int i = 0; i < LOGGING_DATA_POINTS; i++){
+      if(state->hourly_maxs[i] > max_temp){
+         max_temp = state->hourly_maxs[i];
+       }
+      if(state->hourly_mins[i] < min_temp){
+         min_temp = state->hourly_mins[i];
+       }
+    }
+    if(state->show_min) return min_temp;
+    return max_temp;
+}
+
+
+static void _minmax_face_log_data(minmax_state_t *logger_state) {
+    thermistor_driver_enable();
+    size_t pos = (size_t) watch_rtc_get_date_time().unit.hour;
+    float temp_c = thermistor_driver_get_temperature();
+    // If no data yet, initialise with current temperature
+    if(!logger_state->have_logged){
+      logger_state->have_logged = true;
+      for(int i=0; i<LOGGING_DATA_POINTS; i++){
+       logger_state->hourly_mins[i] = temp_c;
+       logger_state->hourly_maxs[i] = temp_c;
+      }
+    }
+    // On new hour, update lists to current temperature
+    else if(watch_rtc_get_date_time().unit.minute < 2){
+      logger_state->hourly_mins[pos] = temp_c;
+      logger_state->hourly_maxs[pos] = temp_c;
+    }
+    // Log hourly highs and lows
+    else if(logger_state->hourly_mins[pos] > temp_c){
+      logger_state->hourly_mins[pos] = temp_c;
+    }
+    else if(logger_state->hourly_maxs[pos] < temp_c){
+      logger_state->hourly_maxs[pos] = temp_c;
+    }
+    thermistor_driver_disable();
+}
+
+static void _minmax_face_update_display(float temperature_c, bool in_fahrenheit) {
+    char buf[14];
+    if (in_fahrenheit) {
+        sprintf(buf, "%4.0f#F", temperature_c * 1.8 + 32.0);
+    } else {
+        sprintf(buf, "%4.0f#C", temperature_c);
+    }
+    watch_display_string(buf, 4);
+}
+
+
+void minmax_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(minmax_state_t));
+        memset(*context_ptr, 0, sizeof(minmax_state_t));
+    }
+}
+
+
+void minmax_face_activate(void *context) {
+    minmax_state_t *state = (minmax_state_t *)context;
+    state->show_min = true;
+    watch_display_string("MN", 0); // Start with minimum temp
+}
+
+bool minmax_face_loop(movement_event_t event, void *context) {
+    minmax_state_t *state = (minmax_state_t *)context;
+    float temp_c;
+
+    switch (event.event_type) {
+       case EVENT_ACTIVATE:
+           temp_c = _get_displayed_temperature_c(state);
+           _minmax_face_update_display(temp_c, movement_use_imperial_units());
+            break;
+
+       case EVENT_LIGHT_LONG_PRESS:
+        movement_set_use_imperial_units(!movement_use_imperial_units());
+           temp_c = _get_displayed_temperature_c(state);
+           _minmax_face_update_display(temp_c, movement_use_imperial_units());
+            break;
+
+       case EVENT_ALARM_BUTTON_UP:
+           state->show_min = !state->show_min;
+           if(state->show_min){
+               watch_display_string("MN", 0);
+           } else {
+               watch_display_string("MX", 0);
+           }
+           temp_c = _get_displayed_temperature_c(state);
+           _minmax_face_update_display(temp_c, movement_use_imperial_units());
+            break;
+
+       case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+       case EVENT_BACKGROUND_TASK:
+            _minmax_face_log_data(state);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+    return true;
+}
+
+
+void minmax_face_resign(void *context) {
+    (void) context;
+}
+
+
+movement_watch_face_advisory_t minmax_face_advise(void *context) {
+    (void) context;
+
+    // this will get called at the top of each minute; always request bg task
+    movement_watch_face_advisory_t retval = {
+        .wants_background_task = 1,
+    };
+
+    return retval;
+}
diff --git a/legacy/watch_faces/sensor/minmax_face.h b/legacy/watch_faces/sensor/minmax_face.h
new file mode 100644 (file)
index 0000000..330f532
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Mark Blyth
+ *
+ * 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 MINMAX_FACE_H_
+#define MINMAX_FACE_H_
+
+#include "movement.h"
+#include "watch.h"
+
+#define LOGGING_DATA_POINTS (24)
+
+/*
+ * Log for the min. and max. temperature over the last 24h.
+ *
+ * Temperature is logged once a minute, every minute. Results are
+ * stored, noting the highest and lowest temperatures observed within
+ * any given hour. The watch face then displays the minimum or maximum
+ * temperature recorded over the last 24h.
+ *
+ * A long press of the light button changes units between Celsius and
+ * Fahrenheit. Pressing the alarm button switches between displaying the
+ * minimum and maximum observed temperatures. If no buttons are pressed,
+ * the watch face will eventually time out and return home.
+ */
+
+typedef struct {
+  bool show_min;
+  bool have_logged;
+  float hourly_mins[LOGGING_DATA_POINTS];
+  float hourly_maxs[LOGGING_DATA_POINTS];
+} minmax_state_t;
+
+void minmax_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void minmax_face_activate(void *context);
+bool minmax_face_loop(movement_event_t event, void *context);
+void minmax_face_resign(void *context);
+movement_watch_face_advisory_t minmax_face_advise(void *context);
+
+#define minmax_face ((const watch_face_t){ \
+    minmax_face_setup, \
+    minmax_face_activate, \
+    minmax_face_loop, \
+    minmax_face_resign, \
+    minmax_face_advise, \
+})
+
+#endif // MINMAX_FACE_H_
+
diff --git a/legacy/watch_faces/sensor/thermistor_testing_face.c b/legacy/watch_faces/sensor/thermistor_testing_face.c
new file mode 100644 (file)
index 0000000..964ef44
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "thermistor_testing_face.h"
+#include "thermistor_driver.h"
+#include "watch.h"
+
+static void _thermistor_testing_face_update_display(bool in_fahrenheit) {
+    thermistor_driver_enable();
+    float temperature_c = thermistor_driver_get_temperature();
+    char buf[14];
+    if (in_fahrenheit) {
+        sprintf(buf, "%4.1f#F", temperature_c * 1.8 + 32.0);
+    } else {
+        sprintf(buf, "%4.1f#C", temperature_c);
+    }
+    watch_display_string(buf, 4);
+    thermistor_driver_disable();
+}
+
+void thermistor_testing_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    (void) context_ptr;
+    // force one setting: never enter low energy mode.
+    // I'm using this watch face to test the temperature sensor boards; there's no need for it.
+    movement_set_low_energy_timeout(0);
+}
+
+void thermistor_testing_face_activate(void *context) {
+    (void) context;
+    watch_display_string("TE", 0);
+    movement_request_tick_frequency(8);
+}
+
+bool thermistor_testing_face_loop(movement_event_t event, void *context) {
+    (void) context;
+    switch (event.event_type) {
+        case EVENT_ALARM_BUTTON_DOWN:
+            movement_set_use_imperial_units(!movement_use_imperial_units());
+            _thermistor_testing_face_update_display(movement_use_imperial_units());
+            break;
+        case EVENT_ACTIVATE:
+        case EVENT_TICK:
+            _thermistor_testing_face_update_display(movement_use_imperial_units());
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    return true;
+}
+
+void thermistor_testing_face_resign(void *context) {
+    (void) context;
+}
diff --git a/legacy/watch_faces/sensor/thermistor_testing_face.h b/legacy/watch_faces/sensor/thermistor_testing_face.h
new file mode 100644 (file)
index 0000000..720a8ea
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 THERMISTOR_TESTING_FACE_H_
+#define THERMISTOR_TESTING_FACE_H_
+
+/*
+ * THERMISTOR TESTING FACE
+ *
+ * This watch face is designed for testing temperature sensor boards.
+ * It displays temperature readings at a relatively fast rate of 8 Hz,
+ * and disables low energy mode so my testing device doesn't sleep.
+ * You more than likely want to use temperature_display_face instead.
+ *
+ * Press ALARM to toggle display of metric vs. imperial units.
+ */
+
+#include "movement.h"
+
+void thermistor_testing_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void thermistor_testing_face_activate(void *context);
+bool thermistor_testing_face_loop(movement_event_t event, void *context);
+void thermistor_testing_face_resign(void *context);
+
+#define thermistor_testing_face ((const watch_face_t){ \
+    thermistor_testing_face_setup, \
+    thermistor_testing_face_activate, \
+    thermistor_testing_face_loop, \
+    thermistor_testing_face_resign, \
+    NULL, \
+})
+
+#endif // THERMISTOR_TESTING_FACE_H_
diff --git a/legacy/watch_faces/settings/finetune_face.c b/legacy/watch_faces/settings/finetune_face.c
new file mode 100644 (file)
index 0000000..e1b64a9
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "finetune_face.h"
+#include "nanosec_face.h"
+#include "watch_utility.h"
+
+extern nanosec_state_t nanosec_state;
+
+int total_adjustment;
+int8_t finetune_page;
+
+void finetune_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    (void) context_ptr;
+    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void finetune_face_activate(void *context) {
+    (void) context;
+
+    // Handle any tasks related to your watch face coming on screen.
+    watch_display_string("FT", 0);
+    total_adjustment = 0;
+    finetune_page = 0;
+}
+
+static float finetune_get_hours_passed(void) {
+    uint32_t current_time = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), 0);
+    return (current_time - nanosec_state.last_correction_time) / 3600.0f;
+}
+
+static float finetune_get_correction(void) {
+    return total_adjustment / (finetune_get_hours_passed() * 3600.0f) * 1000.0f;
+}
+
+static void finetune_update_display(void) {
+    char buf[25];
+
+    if (finetune_page == 0) {
+        watch_display_string("FT", 0);
+        watch_date_time_t date_time = watch_rtc_get_date_time();
+        sprintf(buf, "%02d", date_time.unit.second);
+        watch_display_string(buf, 8);
+
+        sprintf(buf, "%04d", abs(total_adjustment));
+        watch_display_string(buf, 4);
+
+        if (total_adjustment < 0) {
+            watch_display_string("--", 2);
+        } else {
+            watch_display_string("  ", 2);
+        }
+    } else if (finetune_page == 1) {
+        float hours = finetune_get_hours_passed();
+
+        sprintf(buf, "DT  %4d%02d", (int)hours, (int)(fmodf(hours, 1.) * 100));
+        watch_display_string(buf, 0);
+    } else if (finetune_page == 2) {
+        if (finetune_get_hours_passed() < 6) {
+            sprintf(buf, " F  6HR   ");
+            watch_display_string(buf, 0);
+        } else {
+            float correction = finetune_get_correction();
+            sprintf(buf, " F%s%2d%04d", (total_adjustment < 0) ? " -" : "  ", (int)fabsf(correction), (int)(remainderf(fabsf(correction), 1.) * 10000));
+            watch_display_string(buf, 0);
+        }
+    }
+}
+
+static void finetune_adjust_subseconds(int delta) {
+    // Update display first ot make it appear faster for the user
+    if (delta > 500)
+        total_adjustment += (delta - 1000);
+    else
+        total_adjustment += delta;
+    finetune_update_display();
+
+    // Then delay clock
+    watch_rtc_enable(false);
+    delay_ms(delta);
+    if (delta > 500) {
+        watch_date_time_t date_time = watch_rtc_get_date_time();
+        date_time.unit.second = (date_time.unit.second + 1) % 60;
+        if (date_time.unit.second == 0) { // Overflow
+            date_time.unit.minute = (date_time.unit.minute + 1) % 60;
+            if (date_time.unit.minute == 0) { // Overflow
+                date_time.unit.hour = (date_time.unit.hour + 1) % 24;
+                if (date_time.unit.hour == 0) // Overflow
+                    date_time.unit.day++;
+            }
+        }
+        watch_rtc_set_date_time(date_time);
+    }
+    watch_rtc_enable(true);
+}
+
+static void finetune_update_correction_time(void) {
+    // Update aging, as we update correciton time - we must bake accrued aging into static offset
+    nanosec_state.freq_correction += roundf(nanosec_get_aging() * 100);
+
+    // Remember when we last corrected time
+    nanosec_state.last_correction_time = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), 0);
+    nanosec_save();
+    movement_move_to_face(0); // Go to main face after saving settings
+}
+
+bool finetune_face_loop(movement_event_t event, void *context) {
+
+    (void) context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Show your initial UI here.
+            finetune_update_display();
+            break;
+
+        case EVENT_TICK:
+            // If needed, update your display here, at canonical 0.5sec position.
+            // We flash green LED once per minute to measure clock error, when we are not on first screen
+            if (finetune_page!=0) {
+                watch_date_time_t date_time;
+                date_time = watch_rtc_get_date_time();
+                if (date_time.unit.second == 0) {
+                    watch_set_led_green();
+                    #ifndef __EMSCRIPTEN__
+                    delay_us(500);
+                    #endif
+                    watch_set_led_off();
+                }
+            }
+
+            finetune_update_display();
+            break;
+
+        case EVENT_MODE_BUTTON_UP:
+            // Only allow for fast exit when correction is 0!!!
+            if (finetune_page == 0 && total_adjustment == 0) {
+                movement_move_to_next_face();
+            } else {
+                finetune_page = (finetune_page + 1) % 3;
+                finetune_update_display();
+            }
+            break;
+
+        case EVENT_MODE_LONG_PRESS:
+            // You shouldn't need to change this case; Mode almost always moves to the next watch face.
+            finetune_page = (finetune_page + 1) % 3;
+            finetune_update_display();
+            break;
+
+        case EVENT_LIGHT_LONG_PRESS:
+            // We are making it slower by 250ms
+            if (finetune_page == 0) {
+                finetune_adjust_subseconds(250);
+            } else if (finetune_page == 2 && finetune_get_hours_passed() >= 6) {
+                // Applying ppm correction, only if >6 hours passed
+                nanosec_state.freq_correction += (int)round(finetune_get_correction() * 100);
+                finetune_update_correction_time();
+            }
+            break;
+
+        case EVENT_LIGHT_BUTTON_UP:
+            // We are making it slower by 25ms
+            if (finetune_page == 0) {
+                finetune_adjust_subseconds(25);
+            }
+            break;
+
+        case EVENT_ALARM_LONG_PRESS:
+            if (finetune_page == 0) {
+                finetune_adjust_subseconds(750);
+            } else if (finetune_page == 2) {
+                // Exit without applying correction to ppm, but update correction time
+                finetune_update_correction_time();
+            }
+            break;
+
+        case EVENT_ALARM_BUTTON_UP:
+            if (finetune_page == 0) {
+                finetune_adjust_subseconds(975);
+            }
+            break;
+
+        case EVENT_TIMEOUT:
+            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
+            // you may uncomment this line to move back to the first watch face in the list:
+            if (total_adjustment == 0) // Timeout only works if no adjustment was made
+                movement_move_to_face(0);
+            break;
+
+        case EVENT_LOW_ENERGY_UPDATE:
+            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
+            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
+            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
+            // watch_start_sleep_animation(500);
+            break;
+
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
+    // you should return false since the PWM driver does not operate in standby mode.
+    return true;
+}
+
+void finetune_face_resign(void *context) {
+    (void) context;
+
+    if (total_adjustment != 0) {
+        finetune_update_correction_time();
+    }
+}
diff --git a/legacy/watch_faces/settings/finetune_face.h b/legacy/watch_faces/settings/finetune_face.h
new file mode 100644 (file)
index 0000000..905df76
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
+ *
+ * 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 FINETUNE_FACE_H_
+#define FINETUNE_FACE_H_
+
+/*
+ * FINETUNE face
+ *
+ * FineTune face allows to align watch with sub-second precision in 25/250ms
+ * accuracy. Counts time since previous finetune, and allows to calculate &
+ * apply ppm correction for nanosec.
+ *
+ * Best used in conjunction with the NANOSEC face.
+ *
+ * Main screen - adjust delay (light/alarm)
+ * Long MODE press - show hours since previous finetune
+ * Long MODE press - show calculated ppm correction.
+ *  You can apply it with long LIGHT, or just reset finetune timer with long ALARM.
+ *
+ * Finetune will apply crystal aging correction on every finetune save
+ * (as aging is calculated since "last finetune" timestamp); but you should
+ * worry about aging only on second/third years of watch calibration (if you
+ * are really looking at less than 10 seconds per year of error).
+ *
+ * Warning, do not use at the first second of a month, as you might stay at
+ * the same month and it will surprise you. Just wait 1 second...We are not
+ * fully replicating RTC timer behavior when RTC is off.
+ * Simulating months and years is... too much complexity.
+ *
+ * For full usage instructions, please refer to the wiki:
+ *  https://www.sensorwatch.net/docs/watchfaces/nanosec/
+ */
+
+#include "movement.h"
+
+typedef struct {
+    // Anything you need to keep track of, put it here!
+    uint8_t unused;
+} finetune_state_t;
+
+void finetune_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void finetune_face_activate(void *context);
+bool finetune_face_loop(movement_event_t event, void *context);
+void finetune_face_resign(void *context);
+
+#define finetune_face ((const watch_face_t){ \
+    finetune_face_setup, \
+    finetune_face_activate, \
+    finetune_face_loop, \
+    finetune_face_resign, \
+    NULL, \
+})
+
+#endif // FINETUNE_FACE_H_
+
diff --git a/legacy/watch_faces/settings/nanosec_face.c b/legacy/watch_faces/settings/nanosec_face.c
new file mode 100644 (file)
index 0000000..8efdacc
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "thermistor_driver.h"
+#include "nanosec_face.h"
+#include "filesystem.h"
+#include "watch_utility.h"
+
+int16_t freq_correction_residual = 0; // Dithering 0.1ppm correction, does not need to be configured.
+int16_t freq_correction_previous = -30000;
+#define dithering 31
+
+nanosec_state_t nanosec_state;
+
+#define nanosec_max_screen 7
+int8_t nanosec_screen = 0;
+bool nanosec_changed = false; // We try to avoid saving settings when no changes were made, for example when just browsing through face
+
+const float voltage_coefficient = 0.241666667 * dithering; // 10 * ppm/V. Nominal frequency is at 3V.
+
+static void nanosec_init_profile(void) {
+    nanosec_changed = true;
+    nanosec_state.correction_cadence = 10;
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    nanosec_state.last_correction_time = watch_utility_date_time_to_unix_time(date_time, 0);
+
+    // init data after changing profile - do that once per profile selection
+    switch (nanosec_state.correction_profile) {
+        case 0: // No tempco, no dithering
+            nanosec_state.freq_correction = 0;
+            nanosec_state.center_temperature = 2500;
+            nanosec_state.quadratic_tempco = 0;
+            nanosec_state.cubic_tempco = 0;
+            nanosec_state.aging_ppm_pa = 0;
+            break;
+        case 1: // No tempco, with dithering
+            nanosec_state.freq_correction = 0;
+            nanosec_state.center_temperature = 2500;
+            nanosec_state.quadratic_tempco = 0;
+            nanosec_state.cubic_tempco = 0;
+            nanosec_state.aging_ppm_pa = 0;
+            break;
+        case 2: // Datasheet correction
+            nanosec_state.freq_correction = 0;
+            nanosec_state.center_temperature = 2500;
+            nanosec_state.quadratic_tempco = 3400;
+            nanosec_state.cubic_tempco = 0;
+            nanosec_state.aging_ppm_pa = 0;
+            break;
+        case 3: // Datasheet correction + cubic coefficient
+            nanosec_state.freq_correction = 0;
+            nanosec_state.center_temperature = 2500;
+            nanosec_state.quadratic_tempco = 3400;
+            nanosec_state.cubic_tempco = 1360;
+            nanosec_state.aging_ppm_pa = 0;
+            break;
+        case 4: // Full custom
+            nanosec_state.freq_correction = 1768;
+            nanosec_state.center_temperature = 2653;
+            nanosec_state.quadratic_tempco = 4091;
+            nanosec_state.cubic_tempco = 1359;
+            nanosec_state.aging_ppm_pa = 0;
+            break;
+    }
+}
+
+static void nanosec_internal_write_RTC_correction(int16_t value, int16_t sign) {
+    if (sign == 0) {
+        if (value == freq_correction_previous)
+            return; // Do not write same correction value twice
+        freq_correction_previous = value;
+    } else {
+        if (value == -freq_correction_previous)
+            return; // Do not write same correction value twice
+        freq_correction_previous = -value;
+    }
+
+    watch_rtc_freqcorr_write(value, sign);
+}
+
+// Receives clock correction, already corrected for temperature and battery voltage, multiplied by "dithering"
+static void apply_RTC_correction(int16_t correction) {
+    correction += freq_correction_residual;
+    int32_t correction_lr = (int32_t)correction * 2 / dithering; // int division
+    if (correction_lr & 1) {
+        if (correction_lr > 0) {
+            correction_lr++;
+        } else {
+            correction_lr--;
+        }
+    }
+    correction_lr >>= 1;
+    freq_correction_residual = correction - correction_lr * dithering;
+
+    // Warning! Freqcorr is not signed int8!!
+    // First we clamp it to 8-bit range
+    if (correction_lr > 127) {
+        nanosec_internal_write_RTC_correction(127, 0);
+    } else if (correction_lr < -127) {
+        nanosec_internal_write_RTC_correction(127, 1);
+    } else if (correction_lr < 0) {
+        nanosec_internal_write_RTC_correction(abs(correction_lr), 1);
+    } else { // correction
+        nanosec_internal_write_RTC_correction(correction_lr, 0);
+    }
+}
+
+// User-related saves
+void nanosec_ui_save(void) {
+    if (nanosec_changed)
+        nanosec_save();
+}
+
+// This is low-level save function, that can be used by other faces
+void nanosec_save(void) {
+    if (nanosec_state.correction_profile == 0) {
+        freq_correction_residual = 0;
+        apply_RTC_correction(nanosec_state.freq_correction * 1.0f * dithering / 100); // Will be divided by dithering inside, final resolution is mere 1ppm
+    }
+
+    filesystem_write_file("nanosec.ini", (char*)&nanosec_state, sizeof(nanosec_state));
+    nanosec_changed = false;
+}
+
+void nanosec_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+
+    if (*context_ptr == NULL) {
+        if (filesystem_get_file_size("nanosec.ini") != sizeof(nanosec_state)) {
+            // No previous ini or old version of ini file - create new config file
+            nanosec_state.correction_profile = 3;
+            nanosec_init_profile();
+            nanosec_ui_save();
+        } else {
+            filesystem_read_file("nanosec.ini", (char*)&nanosec_state, sizeof(nanosec_state));
+        }
+
+        freq_correction_residual = 0;
+        nanosec_screen = 0;
+
+        *context_ptr = (void *)1; // No need to re-read from filesystem when exiting low power mode
+    }
+}
+
+void nanosec_face_activate(void *context) {
+    (void) context;
+
+    // Handle any tasks related to your watch face coming on screen.
+    nanosec_changed = false;
+}
+
+static void nanosec_update_display() {
+    char buf[14];
+
+    switch (nanosec_screen) {
+        case 0:
+            sprintf(buf, "FC  %6d", nanosec_state.freq_correction);
+            break;
+        case 1:
+            sprintf(buf, "T0  %6d", nanosec_state.center_temperature);
+            break;
+        case 2:
+            sprintf(buf, "2C  %6d", nanosec_state.quadratic_tempco);
+            break;
+        case 3:
+            sprintf(buf, "3C  %6d", nanosec_state.cubic_tempco);
+            break;
+        case 4: // Profile
+            sprintf(buf, "PR      P%1d", nanosec_state.correction_profile);
+            break;
+        case 5: // Cadence
+            sprintf(buf, "CD      %2d", nanosec_state.correction_cadence);
+            break;
+        case 6: // Aging
+            sprintf(buf, "AA  %6d", nanosec_state.aging_ppm_pa);
+            break;
+    }
+    watch_display_string(buf, 0);
+}
+
+static void value_increase(int16_t delta) {
+    nanosec_changed = true;
+
+    switch (nanosec_screen) {
+        case 0:
+            nanosec_state.freq_correction += delta;
+            break;
+        case 1:
+            nanosec_state.center_temperature += delta;
+            break;
+        case 2:
+            nanosec_state.quadratic_tempco += delta;
+            break;
+        case 3:
+            nanosec_state.cubic_tempco += delta;
+            break;
+        case 4: // Profile
+            nanosec_state.correction_profile = (nanosec_state.correction_profile + delta) % nanosec_profile_count;
+            // if ALARM decreases profile below 0, roll back around
+            if (nanosec_state.correction_profile < 0) {
+                nanosec_state.correction_profile += nanosec_profile_count;
+            }
+            break;
+        case 5: // Cadence
+            switch (nanosec_state.correction_cadence) {
+                case 1:
+                    nanosec_state.correction_cadence = (delta > 0) ? 5 : 60;
+                    break;
+                case 5:
+                    nanosec_state.correction_cadence = (delta > 0) ? 10 : 1;
+                    break;
+                case 10:
+                    nanosec_state.correction_cadence = (delta > 0) ? 20 : 5;
+                    break;
+                case 20:
+                    nanosec_state.correction_cadence = (delta > 0) ? 60 : 10;
+                    break;
+                case 60:
+                    nanosec_state.correction_cadence = (delta > 0) ? 1 : 20;
+                    break;
+            }
+            break;
+        case 6: // Aging
+            nanosec_state.aging_ppm_pa += delta;
+            break;
+    }
+
+    nanosec_update_display();
+}
+
+static void nanosec_next_edit_screen(void) {
+    nanosec_screen = (nanosec_screen + 1) % nanosec_max_screen;
+    nanosec_update_display();
+}
+
+float nanosec_get_aging() // Returns aging correction in ppm
+{
+    watch_date_time_t date_time = watch_rtc_get_date_time();
+    float years = (watch_utility_date_time_to_unix_time(date_time, 0) - nanosec_state.last_correction_time) / 31536000.0f; // Years passed since finetune
+    return years*nanosec_state.aging_ppm_pa/100.0f;
+}
+
+
+bool nanosec_face_loop(movement_event_t event, void *context) {
+    (void) context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            // Show your initial UI here.
+            nanosec_screen = 0; // Start at page 0
+            nanosec_update_display();
+            break;
+        case EVENT_TICK:
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            if (nanosec_screen == 0) { // we can exit face only on the 0th page
+                nanosec_ui_save();
+                movement_move_to_next_face();
+            } else {
+                nanosec_next_edit_screen();
+            }
+            break;
+        case EVENT_MODE_LONG_PRESS:
+            nanosec_next_edit_screen();
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            value_increase(1);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            if (nanosec_screen == 4) { // If we are in profile - apply profiles
+                nanosec_init_profile();
+                nanosec_screen = 0;
+                nanosec_update_display();
+            } else {
+                value_increase(50);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            value_increase(-1);
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (nanosec_screen == 4) { // If we are in profile - still decrease by 1
+                value_increase(-1);
+            } else {
+                value_increase(-50);
+            }
+            break;
+        case EVENT_TIMEOUT:
+            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
+            // you may uncomment this line to move back to the first watch face in the list:
+            // movement_move_to_face(0);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
+            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
+            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
+            // watch_start_sleep_animation(500);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            // Here we measure temperature and do main frequency correction
+            thermistor_driver_enable();
+            float temperature_c = thermistor_driver_get_temperature();
+            float voltage = (float)watch_get_vcc_voltage() / 1000.0;
+            thermistor_driver_disable();
+            // L22 correction scaling is 0.95367ppm per 1 in FREQCORR
+            // At wrong temperature crystall starting to run slow, negative correction will speed up frequency to correct
+            // Default 32kHz correciton factor is -0.034, centered around 25°C
+            float dt = temperature_c - nanosec_state.center_temperature / 100.0;
+
+            int16_t correction = round((
+                        nanosec_state.freq_correction / 100.0f * dithering +
+                        (-nanosec_state.quadratic_tempco / 100000.0 * dithering) * dt * dt +
+                        (nanosec_state.cubic_tempco / 10000000.0 * dithering) * dt * dt * dt +
+                        (voltage - 3.0) * voltage_coefficient +
+                        nanosec_get_aging() * dithering
+                        ) / 0.95367); // 1 correction unit is 0.095367ppm.
+
+            apply_RTC_correction(correction);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
+    // you should return false since the PWM driver does not operate in standby mode.
+    return true;
+}
+
+void nanosec_face_resign(void *context) {
+    (void) context;
+
+    nanosec_ui_save();
+}
+
+// Background freq correction
+movement_watch_face_advisory_t nanosec_face_advise(void *context) {
+    (void) context;
+    movement_watch_face_advisory_t retval = { 0 };
+
+    // No need for background correction if we are on profile 0 - static hardware correction.
+    if (nanosec_state.correction_profile != 0) {
+        watch_date_time_t date_time = watch_rtc_get_date_time();
+        retval.wants_background_task = date_time.unit.minute % nanosec_state.correction_cadence == 0;
+    }
+
+    return retval;
+}
diff --git a/legacy/watch_faces/settings/nanosec_face.h b/legacy/watch_faces/settings/nanosec_face.h
new file mode 100644 (file)
index 0000000..105773a
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
+ *
+ * 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 NANOSEC_FACE_H_
+#define NANOSEC_FACE_H_
+
+/*
+ * NANOSEC face
+ *
+ * The goal of nanosec face is dramatic improvement of SensorWatch accuracy.
+ * Minimum goal is <60 seconds of error per year. Full success is if we can
+ * reach <15 seconds per year (<0.47ppm error).
+ *
+ * Best used in conjunction with the FINETUNE face.
+ *
+ * It implements temperature correction using tempco from datasheet (and
+ * allows to adjust these) and allows to introduce offset fix. Therefore
+ * requires temperature sensor board.
+ *
+ * Most users will need to apply profile 3 ("default") or 2 ("conservative
+ * datasheet"), and tune first parameter "static offset" (as it's different
+ * for every crystal sample).
+ *
+ * Frequency correction is dithered over 31 correction intervals (31x10
+ * minutes or ~5 hours), to allow <0.1ppm correction resolution.
+ *  * 1ppm is 0.0864 sec per day.
+ *  * 0.1ppm is 0.00864 sec per day.
+ *
+ * To stay under 1ppm error you would need calibration of your specific
+ * instance of quartz crystal after some "burn-in" (ideally 1 year).
+ *
+ * Should improve TOTP experience.
+ *
+ * Default funing fork tempco: -0.034 ppm/°C², centered around 25°C
+ * We add optional cubic coefficient, which was measured in practice on my sample.
+ *
+ * Cadence (CD) - how many minutes between corrections. Default 10 minutes.
+ * Every minute might be too much. Every hour - slightly less power
+ * consumption but also less precision.
+ *
+ * Can compensate crystal aging (ppm/year) - but you really should be worrying
+ * about it on second/third years of watch calibration.
+ *
+ * For full usage instructions, please refer to the wiki:
+ *  https://www.sensorwatch.net/docs/watchfaces/nanosec/
+ */
+
+#include "movement.h"
+
+#define nanosec_profile_count 5
+typedef struct {
+    // Correction profiles:
+    // 0 - static hardware correction.
+    // 1 - static correction with dithering.
+    // 2 - datasheet quadratic correction (universal).
+    // 3 - cubic correction conservative (likely universal).
+    // 4 - cubic correction finetuned (sample-specific).
+    int8_t correction_profile;
+    int16_t freq_correction; // Static correction - multiplied by 100
+    int16_t center_temperature; // Multiplied by 100, +25.0 -> +2500
+    int16_t quadratic_tempco; // 0.034 -> 3400, multiplied by 100000. Stored positive, used as negative.
+    int16_t cubic_tempco; // default 0, 0.000136 -> 1360, multiplied by 10000000. Stored positive, used positive.
+    int8_t correction_cadence;
+    uint32_t last_correction_time; // Not used at the moment - but will in the future
+    int16_t aging_ppm_pa; // multiplied by 100. Aging per year. 
+} nanosec_state_t;
+
+void nanosec_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void nanosec_face_activate(void *context);
+bool nanosec_face_loop(movement_event_t event, void *context);
+void nanosec_face_resign(void *context);
+movement_watch_face_advisory_t nanosec_face_advise(void *context);
+void nanosec_ui_save(void);
+void nanosec_save(void);
+float nanosec_get_aging(void);
+
+
+#define nanosec_face ((const watch_face_t) { \
+    nanosec_face_setup, \
+    nanosec_face_activate, \
+    nanosec_face_loop, \
+    nanosec_face_resign, \
+    nanosec_face_advise, \
+})
+
+#endif // NANOSEC_FACE_H_
+
diff --git a/legacy/watch_faces/settings/place_face.h b/legacy/watch_faces/settings/place_face.h
new file mode 100644 (file)
index 0000000..94bf164
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * 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 place_FACE_H_
+#define place_FACE_H_
+
+#include "movement.h"
+
+/*
+ * PLACE FACE
+ * ==========
+ *
+ * Based on and expanded from the Sunrise/Sunset face. Outsourced the location setting functionality to 
+ * its own face. Also serves as a converter between different coordinate notation formats.
+ * 
+ * With the LIGHT button each place coordinate can be shown and edited in 4 different display modes:
+ * 
+ * 1) Decimal Latitude and Longitude (WGS84) up to 5 decimal points
+ * 2) Latitude and Longitude (WGS84) in traditional DD°MM'SS" notation
+ * 3) Ten digit Open Location Code (aka. PlusCode) format
+ * 4) Ten digit Geohash format
+ * 
+ * Using the ALARM button the user can flip through 2 pages of coordinate info to see the first and
+ * second sets of digits.
+ * 
+ * (please also refer to the notes on precision below)
+ *
+ * Editing Mode
+ * ============
+ * 
+ * A LONG PRESS of the LIGHT button toggles editing mode for each of the selected notations.
+ * 
+ * In this mode LIGHT moves the cursor and ALARM changes the letter cycling through the available
+ * alphabet or numbers. 
+ * 
+ * When OLC or Geohash display are edited, Digit Info mode is activated. It serves as a workaround 
+ * for the limitation of how ambiguously alphanumeric characters are displayed on the main seven segment 
+ * digits of the watch face ( S or 5, I or 1, U or W?).
+ * 
+ * The selected letter is also shown in the much easier to read alphanumeric 8 segment weekday digit above.
+ * In addition the '24H' indicator is active when the selected digit represents a number and the 'PM' 
+ * indicator for a letter. 
+ * 
+ * A LONG PRESS of LIGHT saves the changes.
+ * 
+ * Coordinates are read or stored to both the traditional internal location register and a file on 
+ * the LFS file system ("place.loc"). By default the Watch Face loads the coordinates from file
+ * when activated. If no file is present, the coordinates are loaded from the register.
+ * (please also see the notes on precision below)
+ * 
+ * Auxiliary Mode: Digit Info
+ * ==========================
+ * 
+ * A LONG PRESS of the ALARM button toggles Digit Info mode when OLC or Geohash display is active.
+ * (LAP indicator is on) It is a means of being able to see the detailed Digit Info as described above
+ * but without the risk of accidentally editing any of digits.
+ * 
+ * Both ALARM and LIGHT buttons can be used to flip through the letters.
+ * 
+ * Notes on Coordinate Precision
+ * =============================
+ * 
+ * The common WGS84 Latitude and Longitude degrees naturally do not represent meters in distance 
+ * on the ground. 1° Longitude on the equatorial line equals a width of 111.32 kilometers, but 
+ * at 40° latitude further North or South it is approximately 85 kilometers wide. The closer to 
+ * the poles the narrower (and more precise) the latitude degrees get.
+ * 
+ * The Sensor Watch's traditional 16bit location register only stores latitudes and longitudes 
+ * with two decimal points. That equals a longitudal precision of 36 arc seconds, or ~1111 meters
+ * at the equator - precise enough for astronomical calculations, but not if you want to store the 
+ * location of let's say a building.
+ * 
+ * Hence we propose the <place.loc> file that serves the same purpose, but with a precision of 
+ * five decimal digits. That equals 0.04 arc seconds or 1.11 meters at the equator.
+ * 
+ * Please also note that the different notations of this watch face also have varying magnitudes 
+ * of precision:
+ * 
+ * | Format             | Notation               | Precision at Equator | Precision at 67° N/S |
+ * | ------------------ | ---------------------- | -------------------- | -------------------- |
+ * | 2d. Decimal LatLon | 29.98, 31.13           |           1111.320 m |            435.125 m |
+ * | 5d. Decimal LatLon | 29.97916, 31.13417     |              1.111 m |              0.435 m |
+ * | DMS LatLon         | N 29°58′45″, E 31°8′3″ |             30.833 m |             12.083 m |
+ * | Open Location Code | 7GXHX4HM+MM            |             13.875 m |             13.875 m |
+ * | Geohash            | stq4s3x1qu             |              1.189 m |              0.596 m |
+ * 
+ * Since all notations are internally converted into degrees with 5 decimal points, expect some
+ * rounding errors when editing or loading the coordinates in other notation formats.
+ * 
+ */
+
+static const char olc_alphabet[20] = "23456789CFGHJMPQRUWX";
+static const char geohash_alphabet[32] =  "0123456789bCdEfGhjkmnpqrstuVwxyz";
+
+typedef struct {
+    uint8_t sign: 1;        // 0-1
+    uint8_t hundreds: 1;    // 0-1
+    uint8_t tens: 4;        // 0-9
+    uint8_t ones: 4;        // 0-9
+    uint8_t d01: 4;         // 0-9
+    uint8_t d02: 4;         // 0-9
+    uint8_t d03: 4;         // 0-9
+    uint8_t d04: 4;         // 0-9
+    uint8_t d05: 4;         // 0-9
+} place_format_decimal_latlon_t;
+
+typedef struct {
+    uint8_t sign: 1;        // 0-1
+    uint8_t hundreds: 1;    // 0-1
+    uint8_t tens: 4;        // 0-9
+    uint8_t ones: 4;        // 0-9
+    uint8_t mins_tens: 3;   // 0-5
+    uint8_t mins_ones: 4;   // 0-9
+    uint8_t secs_tens: 3;   // 0-5
+    uint8_t secs_ones: 4;   // 0-9
+} place_format_dms_latlon_t;
+
+typedef struct {
+    uint8_t lat1: 5;        // 2-X
+    uint8_t lon1: 5;        // 2-X
+    uint8_t lat2: 5;        // 2-X
+    uint8_t lon2: 5;        // 2-X
+    uint8_t lat3: 5;        // 2-X
+    uint8_t lon3: 5;        // 2-X
+    uint8_t lat4: 5;        // 2-X
+    uint8_t lon4: 5;        // 2-X
+    uint8_t lat5: 5;        // 2-X
+    uint8_t lon5: 5;        // 2-X
+} place_format_olc_t;
+
+typedef struct {
+    int32_t latitude : 25;
+    int32_t longitude : 26;
+} coordinate_t;
+
+typedef struct {
+    place_format_decimal_latlon_t latitude;
+    place_format_decimal_latlon_t longitude;
+} place_coordinate_t;
+
+typedef struct {
+    uint8_t d01: 6;          // 0-z
+    uint8_t d02: 6;          // 0-z 
+    uint8_t d03: 6;          // 0-z
+    uint8_t d04: 6;          // 0-z
+    uint8_t d05: 6;          // 0-z
+    uint8_t d06: 6;          // 0-z
+    uint8_t d07: 6;          // 0-z
+    uint8_t d08: 6;          // 0-z
+    uint8_t d09: 6;          // 0-z
+    uint8_t d10: 6;          // 0-z
+} place_format_geohash_t;
+
+typedef struct {
+    double max;
+    double min;
+} place_format_geohash_interval;
+
+typedef struct {
+    uint8_t min_digit : 1;
+    uint8_t max_digit : 3;
+} place_mode_schema_page_t;
+
+typedef struct {
+    uint8_t max_page : 3;
+    place_mode_schema_page_t page[4];
+} place_mode_schema_mode_t;
+
+enum place_modes_e {
+    MODE_DECIMAL = 0,
+    MODE_DMS,
+    MODE_OLC,
+    MODE_GEOHASH
+};
+
+typedef struct {
+    enum place_modes_e mode;
+    uint8_t page : 3;
+    int8_t active_digit: 4;
+    bool edit;
+    bool digit_info;
+    place_format_decimal_latlon_t working_latitude;
+    place_format_decimal_latlon_t working_longitude;
+    place_format_dms_latlon_t working_dms_latitude;
+    place_format_dms_latlon_t working_dms_longitude;
+    place_format_olc_t working_pluscode;
+    place_format_geohash_t working_geohash;
+    place_mode_schema_mode_t modes[4];
+} place_state_t;
+
+// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
+
+void place_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void place_face_activate(void *context);
+bool place_face_loop(movement_event_t event, void *context);
+void place_face_resign(void *context);
+
+void place_latlon_to_olc(char *pluscode, double latitude, double longitude);
+void place_latlon_to_geohash(char *geohash, double latitude, double longitude);
+
+#define place_face ((const watch_face_t){ \
+    place_face_setup, \
+    place_face_activate, \
+    place_face_loop, \
+    place_face_resign, \
+    NULL, \
+})
+
+#endif // place_FACE_H_
+
diff --git a/legacy/watch_faces/settings/save_load_face.c b/legacy/watch_faces/settings/save_load_face.c
new file mode 100644 (file)
index 0000000..d298229
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Wesley Aptekar-Cassels
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "save_load_face.h"
+#include "filesystem.h"
+
+static void save(save_load_state_t *state) {
+    savefile_t savefile = {
+        1,
+        watch_get_backup_data(0),
+        watch_get_backup_data(1),
+        watch_get_backup_data(2),
+        watch_get_backup_data(3),
+        watch_get_backup_data(4),
+        watch_get_backup_data(5),
+        watch_get_backup_data(6),
+        watch_get_backup_data(7),
+        watch_rtc_get_date_time(),
+    };
+    state->slot[state->index] = savefile;
+    char filename[23];
+    sprintf(filename, "save_load_face_%d.bin", state->index);
+    filesystem_write_file(filename, (char*)&savefile, sizeof(savefile_t));
+}
+
+static void load(save_load_state_t *state) {
+    watch_store_backup_data(state->slot[state->index].b0, 0);
+    watch_store_backup_data(state->slot[state->index].b1, 1);
+    watch_store_backup_data(state->slot[state->index].b2, 2);
+    watch_store_backup_data(state->slot[state->index].b3, 3);
+    watch_store_backup_data(state->slot[state->index].b4, 4);
+    watch_store_backup_data(state->slot[state->index].b5, 5);
+    watch_store_backup_data(state->slot[state->index].b6, 6);
+    watch_store_backup_data(state->slot[state->index].b7, 7);
+}
+
+static void load_saves_to_state(save_load_state_t *state) {
+    for (uint8_t i = 0; i < SAVE_LOAD_SLOTS; i++) {
+        char filename[23];
+        sprintf(filename, "save_load_face_%d.bin", i);
+        if (filesystem_get_file_size(filename) != sizeof(savefile_t)) {
+            state->slot[i].version = 0;
+            continue;
+        }
+        filesystem_read_file(filename, (char*)&state->slot[i], sizeof(savefile_t));
+        if (state->slot[i].version != 1) {
+            state->slot[i].version = 0;
+        }
+    }
+}
+
+void save_load_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(save_load_state_t));
+        memset(*context_ptr, 0, sizeof(save_load_state_t));
+    }
+}
+
+void save_load_face_activate(void *context) {
+    save_load_state_t *state = (save_load_state_t *)context;
+    state->index = 0;
+    state->update_timeout = 0;
+    load_saves_to_state(state);
+}
+
+static void update_display(save_load_state_t *state) {
+    char buf[11];
+    sprintf(buf, "SL %1d", state->index);
+    watch_display_string(buf, 0);
+
+    if (state->slot[state->index].version) {
+        sprintf(buf, "%02d%02d%02d", state->slot[state->index].rtc.unit.year + 20, state->slot[state->index].rtc.unit.month, state->slot[state->index].rtc.unit.day);
+        watch_display_string(buf, 4);
+    } else {
+        watch_display_string("no dat", 4);
+    }
+}
+
+bool save_load_face_loop(movement_event_t event, void *context) {
+    save_load_state_t *state = (save_load_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_ACTIVATE:
+            update_display(state);
+            break;
+        case EVENT_TICK:
+            if (state->update_timeout) {
+                if (!--state->update_timeout) {
+                    update_display(state);
+                }
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            state->index = (state->index + 1) % SAVE_LOAD_SLOTS;
+            update_display(state);
+            break;
+        case EVENT_LIGHT_LONG_PRESS:
+            save(state);
+            watch_display_string("Saved ", 4);
+            state->update_timeout = 3;
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            if (state->slot[state->index].version) {
+                load(state);
+                watch_display_string("Loaded", 4);
+                state->update_timeout = 3;
+            }
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void save_load_face_resign(void *context) {
+    (void) context;
+
+    // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/legacy/watch_faces/settings/save_load_face.h b/legacy/watch_faces/settings/save_load_face.h
new file mode 100644 (file)
index 0000000..4e962fb
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Wesley Aptekar-Cassels
+ *
+ * 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 SAVE_LOAD_FACE_H_
+#define SAVE_LOAD_FACE_H_
+
+#include "movement.h"
+#include "watch_rtc.h"
+
+/*
+ * Save/Load face
+ *
+ * This allows you to save your settings (including location, birthday, etc) to
+ * LFS, which is not wiped when firmware is updated, and then load them again.
+ * It provides multiple save slots (four by default).
+ *
+ * Press ALARM to cycle through save slots. Long press LIGHT to save to the
+ * current slot. Long press ALARM to load from the current slot. The date that
+ * a save was taken is shown in YYMMDD format , or "no dat" if the slot is
+ * empty. The index of the current slot is displayed in the upper right corner.
+ *
+ * While the time that a save was taken is recorded, it is not currently
+ * restored.
+ */
+
+typedef struct savefile {
+    uint8_t version;
+    uint32_t b0;
+    uint32_t b1;
+    uint32_t b2;
+    uint32_t b3;
+    uint32_t b4;
+    uint32_t b5;
+    uint32_t b6;
+    uint32_t b7;
+    watch_date_time_t rtc;
+} savefile_t;
+
+#define SAVE_LOAD_SLOTS 4
+
+typedef struct {
+    uint8_t index;
+    uint8_t update_timeout;
+    savefile_t slot[SAVE_LOAD_SLOTS];
+} save_load_state_t;
+
+void save_load_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void save_load_face_activate(void *context);
+bool save_load_face_loop(movement_event_t event, void *context);
+void save_load_face_resign(void *context);
+
+#define save_load_face ((const watch_face_t){ \
+    save_load_face_setup, \
+    save_load_face_activate, \
+    save_load_face_loop, \
+    save_load_face_resign, \
+    NULL, \
+})
+
+#endif // SAVE_LOAD_FACE_H_
+
diff --git a/legacy/watch_faces/settings/set_time_hackwatch_face.c b/legacy/watch_faces/settings/set_time_hackwatch_face.c
new file mode 100644 (file)
index 0000000..6551019
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Joey Castillo
+ * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include "set_time_hackwatch_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "zones.h"
+
+char set_time_hackwatch_face_titles[][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO"};
+#define set_time_hackwatch_face_NUM_SETTINGS (sizeof(set_time_hackwatch_face_titles) / sizeof(*set_time_hackwatch_face_titles))
+
+watch_date_time_t date_time_settings;
+
+void set_time_hackwatch_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t));
+}
+
+void set_time_hackwatch_face_activate(void *context) {
+    *((uint8_t *)context) = 3;
+    movement_request_tick_frequency(32);
+    date_time_settings = watch_rtc_get_date_time();
+}
+
+bool set_time_hackwatch_face_loop(movement_event_t event, void *context) {
+    uint8_t current_page = *((uint8_t *)context);
+
+    if (event.subsecond == 15) // Delay displayed time update by ~0.5 seconds, to align phase exactly to main clock at 1Hz
+        date_time_settings = watch_rtc_get_date_time();
+
+    static int8_t seconds_reset_sequence;
+
+    switch (event.event_type) {
+        case EVENT_MODE_BUTTON_UP:
+            if (current_page == 2)
+                watch_rtc_enable(true);
+            movement_move_to_next_face();
+            return false;
+        case EVENT_LIGHT_LONG_PRESS:
+            current_page = (current_page + set_time_hackwatch_face_NUM_SETTINGS - 1) % set_time_hackwatch_face_NUM_SETTINGS;
+            if (current_page == 2)
+                seconds_reset_sequence = 0;
+
+            *((uint8_t *)context) = current_page;
+            break;
+        case EVENT_LIGHT_BUTTON_UP:
+            if (current_page == 2)
+                watch_rtc_enable(true);
+
+            current_page = (current_page + 1) % set_time_hackwatch_face_NUM_SETTINGS;
+            if (current_page == 2)
+                seconds_reset_sequence = 0;
+
+            *((uint8_t *)context) = current_page;
+            break;
+        case EVENT_TICK:
+            // We use it to wait for "middle" subsecond position
+            if (current_page == 2 && seconds_reset_sequence == 1 && event.subsecond == 15) { // wait ~0.5sec - until we reach half second point
+                watch_rtc_enable(false);
+                seconds_reset_sequence = 2;
+
+                // Set new time while RTC is off, to get perfect start
+                if (date_time_settings.unit.second > 30) {
+                    date_time_settings.unit.minute = (date_time_settings.unit.minute + 1) % 60; // Roll to next minute if we are almost there
+                    if (date_time_settings.unit.minute == 0) { // Overflow
+                        date_time_settings.unit.hour = (date_time_settings.unit.hour + 1) % 24;
+                        if (date_time_settings.unit.hour == 0) // Overflow
+                            date_time_settings.unit.day++;
+                    }
+                }
+                date_time_settings.unit.second = 0;
+                watch_rtc_set_date_time(date_time_settings);
+            }
+            break;
+        case EVENT_ALARM_BUTTON_DOWN:
+            if (current_page == 2) {
+                watch_rtc_enable(true); // If it is disabled accidentally - re-enable it
+                seconds_reset_sequence = 1; // Waiting for whole second
+            }
+            break;
+        case EVENT_ALARM_LONG_PRESS:
+            switch (current_page) {
+                case 0: // hour
+                    date_time_settings.unit.hour = (date_time_settings.unit.hour + 24 -1) % 24;
+                    break;
+                case 1: // minute
+                    date_time_settings.unit.minute = (date_time_settings.unit.minute + 60 - 1) % 60;
+                    break;
+                case 3: // year
+                    // only allow 2021-2061. fix this sometime later
+                    date_time_settings.unit.year = (date_time_settings.unit.year + 50 - 1) % 50;
+                    break;
+                case 4: // month
+                    date_time_settings.unit.month = (date_time_settings.unit.month + 12 - 2) % 12 + 1;
+                    break;
+                case 5: // day
+                    date_time_settings.unit.day = date_time_settings.unit.day - 2;
+                    if (date_time_settings.unit.day == 0) {
+                        date_time_settings.unit.day = days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR);
+                    } else
+                        date_time_settings.unit.day++;
+                    break;
+                case 6: // time zone
+                    if (movement_get_timezone_index() > 0) {
+                        movement_set_timezone_index(movement_get_timezone_index() - 1);
+                    } else {
+                        movement_set_timezone_index(NUM_ZONE_NAMES - 1);
+                    }
+                    break;
+            }
+            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);
+            break;
+
+        case EVENT_ALARM_LONG_UP://Setting seconds on long release
+            switch (current_page) {
+                case 2: // second
+                    seconds_reset_sequence = 0;
+                    watch_rtc_enable(true);
+                    break;
+            }
+            break;
+        case EVENT_ALARM_BUTTON_UP:
+            switch (current_page) {
+                case 0: // hour
+                    date_time_settings.unit.hour = (date_time_settings.unit.hour + 1) % 24;
+                    break;
+                case 1: // minute
+                    date_time_settings.unit.minute = (date_time_settings.unit.minute + 1) % 60;
+                    break;
+                case 2: // second
+                    seconds_reset_sequence = 0;
+                    watch_rtc_enable(true);
+                    break;
+                case 3: // year
+                    // only allow 2021-2061. fix this sometime later
+                    date_time_settings.unit.year = ((date_time_settings.unit.year % 50) + 1);
+                    break;
+                case 4: // month
+                    date_time_settings.unit.month = (date_time_settings.unit.month % 12) + 1;
+                    break;
+                case 5: // day
+                    date_time_settings.unit.day = (date_time_settings.unit.day % watch_utility_days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR)) + 1;
+                    break;
+                case 6: // time zone
+                    movement_set_timezone_index(movement_get_timezone_index() + 1);
+                    if (movement_get_timezone_index() >= NUM_ZONE_NAMES) movement_set_timezone_index(0);
+                    break;
+            }
+            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);
+            //TODO: Do not update whole RTC, just what we are changing
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        case EVENT_LIGHT_BUTTON_DOWN:
+            // don't light up every time light is hit
+            break;
+        default:
+            movement_default_loop_handler(event);
+            break;
+    }
+
+    char buf[11];
+    if (current_page < 3) {
+        watch_set_colon();
+        if (movement_clock_mode_24h()) {
+            watch_set_indicator(WATCH_INDICATOR_24H);
+            sprintf(buf,
+                    "%s  %2d%02d%02d",
+                    set_time_hackwatch_face_titles[current_page],
+                    date_time_settings.unit.hour,
+                    date_time_settings.unit.minute,
+                    date_time_settings.unit.second);
+        } else {
+            sprintf(buf,
+                    "%s  %2d%02d%02d",
+                    set_time_hackwatch_face_titles[current_page],
+                    (date_time_settings.unit.hour % 12) ? (date_time_settings.unit.hour % 12) : 12,
+                    date_time_settings.unit.minute,
+                    date_time_settings.unit.second);
+            if (date_time_settings.unit.hour < 12) {
+                watch_clear_indicator(WATCH_INDICATOR_PM);
+            } else {
+                watch_set_indicator(WATCH_INDICATOR_PM);
+            }
+        }
+    } else if (current_page < 6) {
+        watch_clear_colon();
+        watch_clear_indicator(WATCH_INDICATOR_24H);
+        watch_clear_indicator(WATCH_INDICATOR_PM);
+        sprintf(buf,
+                "%s  %2d%02d%02d",
+                set_time_hackwatch_face_titles[current_page],
+                date_time_settings.unit.year + 20,
+                date_time_settings.unit.month,
+                date_time_settings.unit.day);
+    } else {
+        if ((event.subsecond / 8 ) % 2) {
+            watch_clear_colon();
+            sprintf(buf, "%s        ", set_time_hackwatch_face_titles[current_page]);
+        } else {
+            watch_set_colon();
+            sprintf(buf,
+                    "%s %3d%02d  ",
+                    set_time_hackwatch_face_titles[current_page],
+                    (int8_t)(movement_get_current_timezone_offset() / 3600),
+                    (int8_t)(movement_get_current_timezone_offset() % 3600) * (movement_get_current_timezone_offset() < 0 ? -1 : 1));
+        }
+    }
+
+    // blink up the parameter we're setting
+    if ( (event.subsecond / 8) % 2) {
+        switch (current_page) {
+            case 0:
+            case 3:
+                buf[4] = buf[5] = ' ';
+                break;
+            case 1:
+            case 4:
+                buf[6] = buf[7] = ' ';
+                break;
+            case 2:
+                // Only blink first number when setting seconds, to make it easier to see subsecond error
+                buf[8] = ' ';
+                break;
+            case 5:
+                buf[8] = buf[9] = ' ';
+                break;
+        }
+    }
+
+    watch_display_string(buf, 0);
+
+    return true;
+}
+
+void set_time_hackwatch_face_resign(void *context) {
+    (void) context;
+    watch_set_led_off();
+    movement_store_settings();
+}
diff --git a/legacy/watch_faces/settings/set_time_hackwatch_face.h b/legacy/watch_faces/settings/set_time_hackwatch_face.h
new file mode 100644 (file)
index 0000000..ad883a6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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 SET_TIME_HACKWATCH_FACE_H_
+#define SET_TIME_HACKWATCH_FACE_H_
+
+/*
+ * SET TIME HACKWATCH
+ *
+ * This is an extended version of set_time face which allow setting seconds
+ * precisely. To achieve that - press and hold alarm button few seconds before
+ * 00 and release exaclty as reference clock turns 00.
+ *
+ * All settings can go up, or down (long alarm press).
+ * 
+ * The challenge is that SensorWatch display is delayed 0.5 seconds vs hardware
+ * RTC clock. It is caused by interrupts being generated by raising edge of
+ * counter. It means there is no way to precisely trigger at 0.5s, as events
+ * at different frequencies slightly mismatch. This watch face achieves this
+ * approximately by triggering at 15th out of 32Hz events.
+ * 
+ * If you are <30 seconds when setting seconds - you will stay in the same
+ * minute. Otherwise - you will go to next minute. 
+ * 
+ * Note that changing anything will slightly delay subseconds counter. This
+ * is why this face sets seconds last to achiveve best precision. Still,
+ * best possible precision is achieved with finetune face. 
+ */
+
+#include "movement.h"
+
+void set_time_hackwatch_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void set_time_hackwatch_face_activate(void *context);
+bool set_time_hackwatch_face_loop(movement_event_t event, void *context);
+void set_time_hackwatch_face_resign(void *context);
+
+#define set_time_hackwatch_face ((const watch_face_t){ \
+    set_time_hackwatch_face_setup, \
+    set_time_hackwatch_face_activate, \
+    set_time_hackwatch_face_loop, \
+    set_time_hackwatch_face_resign, \
+    NULL, \
+})
+
+#endif // SET_TIME_HACKWATCH_FACE_H_
diff --git a/lib/base64/.gitignore b/lib/base64/.gitignore
new file mode 100644 (file)
index 0000000..6b5a4f3
--- /dev/null
@@ -0,0 +1,57 @@
+~*\r
+debug.bat\r
+compile.bat\r
+*.exe\r
+*.lnk\r
+*.url\r
+*.tmp\r
+\r
+# Windows image file caches\r
+Thumbs.db\r
+ehthumbs.db\r
+\r
+# Folder config file\r
+Desktop.ini\r
+\r
+# Recycle Bin used on file shares\r
+$RECYCLE.BIN/\r
+\r
+# Windows Installer files\r
+*.cab\r
+*.msi\r
+*.msm\r
+*.msp\r
+\r
+# =========================\r
+# Operating System Files\r
+# =========================\r
+\r
+# OSX\r
+# =========================\r
+\r
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon\r\r
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+data.7z
+data.7z.b64
+picture.og.png
+data.7z.b64.deco
+picture.b64.txt
+picture.b64.png
diff --git a/lib/base64/LICENSE b/lib/base64/LICENSE
new file mode 100644 (file)
index 0000000..973d540
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Joe DF (joedf@ahkscript.org)
+
+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.
diff --git a/lib/base64/README.md b/lib/base64/README.md
new file mode 100644 (file)
index 0000000..58fba4b
--- /dev/null
@@ -0,0 +1,13 @@
+base64.c
+========
+
+Base64 Library in C  
+  
+by Joe DF (joedf@ahkscript.org)  
+Released under the MIT License  
+  
+Thank you for inspiration:  
+http://www.codeproject.com/Tips/813146/Fast-base-functions-for-encode-decode  
+
+## Usage
+Simply include `base64.c` and `base64.h` in your project and see `base64.h` for instructions.
\ No newline at end of file
diff --git a/lib/base64/base64.c b/lib/base64/base64.c
new file mode 100644 (file)
index 0000000..8604b94
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+       base64.c - by Joe DF (joedf@ahkscript.org)
+       Released under the MIT License
+       
+       See "base64.h", for more information.
+       
+       Thank you for inspiration:
+       http://www.codeproject.com/Tips/813146/Fast-base-functions-for-encode-decode
+*/
+
+#include "base64.h"
+
+//Base64 char table - used internally for encoding
+unsigned char b64_chr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+unsigned int b64_int(unsigned int ch) {
+
+       // ASCII to base64_int
+       // 65-90  Upper Case  >>  0-25
+       // 97-122 Lower Case  >>  26-51
+       // 48-57  Numbers     >>  52-61
+       // 43     Plus (+)    >>  62
+       // 47     Slash (/)   >>  63
+       // 61     Equal (=)   >>  64~
+       if (ch==43)
+       return 62;
+       if (ch==47)
+       return 63;
+       if (ch==61)
+       return 64;
+       if ((ch>47) && (ch<58))
+       return ch + 4;
+       if ((ch>64) && (ch<91))
+       return ch - 'A';
+       if ((ch>96) && (ch<123))
+       return (ch - 'a') + 26;
+       return 0;
+}
+
+unsigned int b64e_size(unsigned int in_size) {
+
+       // size equals 4*floor((1/3)*(in_size+2));
+       unsigned int i, j = 0;
+       for (i=0;i<in_size;i++) {
+               if (i % 3 == 0)
+               j += 1;
+       }
+       return (4*j);
+}
+
+unsigned int b64d_size(unsigned int in_size) {
+
+       return ((3*in_size)/4);
+}
+
+unsigned int b64_encode(const unsigned char* in, unsigned int in_len, unsigned char* out) {
+
+       unsigned int i=0, j=0, k=0, s[3];
+       
+       for (i=0;i<in_len;i++) {
+               s[j++]=*(in+i);
+               if (j==3) {
+                       out[k+0] = b64_chr[ (s[0]&255)>>2 ];
+                       out[k+1] = b64_chr[ ((s[0]&0x03)<<4)+((s[1]&0xF0)>>4) ];
+                       out[k+2] = b64_chr[ ((s[1]&0x0F)<<2)+((s[2]&0xC0)>>6) ];
+                       out[k+3] = b64_chr[ s[2]&0x3F ];
+                       j=0; k+=4;
+               }
+       }
+       
+       if (j) {
+               if (j==1)
+                       s[1] = 0;
+               out[k+0] = b64_chr[ (s[0]&255)>>2 ];
+               out[k+1] = b64_chr[ ((s[0]&0x03)<<4)+((s[1]&0xF0)>>4) ];
+               if (j==2)
+                       out[k+2] = b64_chr[ ((s[1]&0x0F)<<2) ];
+               else
+                       out[k+2] = '=';
+               out[k+3] = '=';
+               k+=4;
+       }
+
+       out[k] = '\0';
+       
+       return k;
+}
+
+unsigned int b64_decode(const unsigned char* in, unsigned int in_len, unsigned char* out) {
+
+       unsigned int i=0, j=0, k=0, s[4];
+       
+       for (i=0;i<in_len;i++) {
+               s[j++]=b64_int(*(in+i));
+               if (j==4) {
+                       out[k+0] = ((s[0]&255)<<2)+((s[1]&0x30)>>4);
+                       if (s[2]!=64) {
+                               out[k+1] = ((s[1]&0x0F)<<4)+((s[2]&0x3C)>>2);
+                               if ((s[3]!=64)) {
+                                       out[k+2] = ((s[2]&0x03)<<6)+(s[3]); k+=3;
+                               } else {
+                                       k+=2;
+                               }
+                       } else {
+                               k+=1;
+                       }
+                       j=0;
+               }
+       }
+       
+       return k;
+}
+
+unsigned int b64_encodef(char *InFile, char *OutFile) {
+
+       FILE *pInFile = fopen(InFile,"rb");
+       FILE *pOutFile = fopen(OutFile,"wb");
+       
+       int i=0;
+       int j=0;
+       int c=0;
+       int s[4];
+       
+       if ((pInFile==NULL) || (pOutFile==NULL) ) {
+               if (pInFile!=NULL){fclose(pInFile);}
+               if (pOutFile!=NULL){fclose(pOutFile);}
+               return 0;
+       }
+       
+       while(c!=EOF) {
+               c=fgetc(pInFile);
+               if (c==EOF)
+               break;
+               s[j++]=c;
+               if (j==3) {
+                       fputc(b64_chr[ (s[0]&255)>>2 ],pOutFile);
+                       fputc(b64_chr[ ((s[0]&0x03)<<4)+((s[1]&0xF0)>>4) ],pOutFile);
+                       fputc(b64_chr[ ((s[1]&0x0F)<<2)+((s[2]&0xC0)>>6) ],pOutFile);
+                       fputc(b64_chr[ s[2]&0x3F ],pOutFile);
+                       j=0; i+=4;
+               }
+       }
+       
+       if (j) {
+               if (j==1)
+                       s[1] = 0;
+               fputc(b64_chr[ (s[0]&255)>>2 ],pOutFile);
+               fputc(b64_chr[ ((s[0]&0x03)<<4)+((s[1]&0xF0)>>4) ],pOutFile);
+               if (j==2)
+                       fputc(b64_chr[ ((s[1]&0x0F)<<2) ],pOutFile);
+               else
+                       fputc('=',pOutFile);
+               fputc('=',pOutFile);
+               i+=4;
+       }
+       
+       fclose(pInFile);
+       fclose(pOutFile);
+       
+       return i;
+}
+
+unsigned int b64_decodef(char *InFile, char *OutFile) {
+
+       FILE *pInFile = fopen(InFile,"rb");
+       FILE *pOutFile = fopen(OutFile,"wb");
+       
+       int c=0;
+       int j=0;
+       int k=0;
+       int s[4];
+       
+       if ((pInFile==NULL) || (pOutFile==NULL) ) {
+               if (pInFile!=NULL){fclose(pInFile);}
+               if (pOutFile!=NULL){fclose(pOutFile);}
+               return 0;
+       }
+       
+       while(c!=EOF) {
+               c=fgetc(pInFile);
+               if (c==EOF)
+               break;
+               s[j++]=b64_int(c);
+               if (j==4) {
+                       fputc(((s[0]&255)<<2)+((s[1]&0x30)>>4),pOutFile);
+                       if (s[2]!=64) {
+                               fputc(((s[1]&0x0F)<<4)+((s[2]&0x3C)>>2),pOutFile);
+                               if ((s[3]!=64)) {
+                                       fputc(((s[2]&0x03)<<6)+(s[3]),pOutFile); k+=3;
+                               } else {
+                                       k+=2;
+                               }
+                       } else {
+                               k+=1;
+                       }
+                       j=0;
+               }
+       }
+
+       fclose(pInFile);
+       fclose(pOutFile);
+       
+       return k;
+}
diff --git a/lib/base64/base64.h b/lib/base64/base64.h
new file mode 100644 (file)
index 0000000..c700c7f
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+       base64.c - by Joe DF (joedf@ahkscript.org)
+       Released under the MIT License
+       
+       Revision: 2015-06-12 01:26:51
+       
+       Thank you for inspiration:
+       http://www.codeproject.com/Tips/813146/Fast-base-functions-for-encode-decode
+*/
+
+#include <stdio.h>
+
+//Base64 char table function - used internally for decoding
+unsigned int b64_int(unsigned int ch);
+
+// in_size : the number bytes to be encoded.
+// Returns the recommended memory size to be allocated for the output buffer excluding the null byte
+unsigned int b64e_size(unsigned int in_size);
+
+// in_size : the number bytes to be decoded.
+// Returns the recommended memory size to be allocated for the output buffer
+unsigned int b64d_size(unsigned int in_size);
+
+// in : buffer of "raw" binary to be encoded.
+// in_len : number of bytes to be encoded.
+// out : pointer to buffer with enough memory, user is responsible for memory allocation, receives null-terminated string
+// returns size of output including null byte
+unsigned int b64_encode(const unsigned char* in, unsigned int in_len, unsigned char* out);
+
+// in : buffer of base64 string to be decoded.
+// in_len : number of bytes to be decoded.
+// out : pointer to buffer with enough memory, user is responsible for memory allocation, receives "raw" binary
+// returns size of output excluding null byte
+unsigned int b64_decode(const unsigned char* in, unsigned int in_len, unsigned char* out);
+
+// file-version b64_encode
+// Input : filenames
+// returns size of output
+unsigned int b64_encodef(char *InFile, char *OutFile);
+
+// file-version b64_decode
+// Input : filenames
+// returns size of output
+unsigned int b64_decodef(char *InFile, char *OutFile);
\ No newline at end of file
diff --git a/lib/chirpy_tx/chirpy_tx.c b/lib/chirpy_tx/chirpy_tx.c
new file mode 100644 (file)
index 0000000..41a42a5
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include "chirpy_tx.h"
+
+static const uint32_t chirpy_min_freq = 2500;
+static const uint32_t cirpy_freq_step = 250;
+
+// This many bytes are followed by a CRC and block separator
+// It's a multiple of 3 so no bits are wasted (a tone encodes 3 bits)
+// Last block can be shorter
+static const uint8_t chirpy_default_block_size = 15;
+
+// The dedicated control tone. This is the highest tone index.
+static const uint8_t chirpy_control_tone = 8;
+
+// Pre-computed tone periods. This is allocated and populated on-demand.
+static uint32_t *chirpy_tone_periods = NULL;
+
+uint8_t chirpy_crc8(const uint8_t *addr, uint16_t len) {
+    uint8_t crc = 0;
+    for (uint16_t i = 0; i < len; i++)
+        crc = chirpy_update_crc8(addr[i], crc);
+    return crc;
+}
+
+uint8_t chirpy_update_crc8(uint8_t next_byte, uint8_t crc) {
+    for (uint8_t j = 0; j < 8; j++) {
+        uint8_t mix = (crc ^ next_byte) & 0x01;
+        crc >>= 1;
+        if (mix)
+            crc ^= 0x8C;
+        next_byte >>= 1;
+    }
+    return crc;
+}
+
+static void _chirpy_append_tone(chirpy_encoder_state_t *ces, uint8_t tone) {
+    // This is BAD and should never happen. But if it does, we'd rather
+    // create a corrupt transmission than corrupt memory #$^@
+    if (ces->tone_count == CHIRPY_TONE_BUF_SIZE)
+        return;
+    ces->tone_buf[ces->tone_count] = tone;
+    ++ces->tone_count;
+}
+
+void chirpy_init_encoder(chirpy_encoder_state_t *ces, chirpy_get_next_byte_t get_next_byte) {
+    memset(ces, 0, sizeof(chirpy_encoder_state_t));
+    ces->block_size = chirpy_default_block_size;
+    ces->get_next_byte = get_next_byte;
+    _chirpy_append_tone(ces, 8);
+    _chirpy_append_tone(ces, 0);
+    _chirpy_append_tone(ces, 8);
+    _chirpy_append_tone(ces, 0);
+}
+
+static uint8_t _chirpy_retrieve_next_tone(chirpy_encoder_state_t *ces) {
+    if (ces->tone_pos == ces->tone_count)
+        return 255;
+
+    uint8_t res = ces->tone_buf[ces->tone_pos];
+    ++ces->tone_pos;
+    if (ces->tone_pos == ces->tone_count) {
+        // End of buffer: reset buffer
+        ces->tone_pos = 0;
+        ces->tone_count = 0;
+    }
+    return res;
+}
+
+static void _chirpy_encode_bits(chirpy_encoder_state_t *ces, uint8_t force_partial) {
+    while (ces->bit_count > 0) {
+        if (ces->bit_count < 3 && !force_partial) break;
+        uint8_t tone = (uint8_t)(ces->bits >> 13);
+        _chirpy_append_tone(ces, tone);
+        if (ces->bit_count >= 3) {
+            ces->bits <<= 3;
+            ces->bit_count -= 3;
+        } else {
+            ces->bits = 0;
+            ces->bit_count = 0;
+        }
+    }
+}
+
+static void _chirpy_finish_block(chirpy_encoder_state_t *ces) {
+    _chirpy_append_tone(ces, chirpy_control_tone);
+    ces->bits = ces->crc;
+    ces->bits <<= 8;
+    ces->bit_count = 8;
+    _chirpy_encode_bits(ces, 1);
+    ces->bit_count = 0;
+    ces->crc = 0;
+    ces->block_len = 0;
+    _chirpy_append_tone(ces, chirpy_control_tone);
+}
+
+static void _chirpy_finish_transmission(chirpy_encoder_state_t *ces) {
+    _chirpy_append_tone(ces, chirpy_control_tone);
+    _chirpy_append_tone(ces, chirpy_control_tone);
+}
+
+uint8_t chirpy_get_next_tone(chirpy_encoder_state_t *ces) {
+    // If there are tones left in the buffer, keep sending those
+    if (ces->tone_pos < ces->tone_count)
+        return _chirpy_retrieve_next_tone(ces);
+
+    // We know data is over: that means we've wrapped up transmission
+    // Just drain tone buffer, and then keep sendig EOB
+    if (ces->get_next_byte == 0)
+        return _chirpy_retrieve_next_tone(ces);
+
+    // Fetch next byte
+    uint8_t next_byte;
+    uint8_t got_more = ces->get_next_byte(&next_byte);
+
+    // Data over: write CRC if we sent a partial buffer; send end signal
+    if (got_more == 0) {
+        ces->get_next_byte = 0;
+        if (ces->bit_count > 0) _chirpy_encode_bits(ces, 1);
+        if (ces->block_len > 0) _chirpy_finish_block(ces);
+        _chirpy_finish_transmission(ces);
+        return _chirpy_retrieve_next_tone(ces);
+    }
+
+    // Got more data: add to bits; convert
+    uint16_t msk = next_byte;
+    msk <<= (8 - ces->bit_count);
+    ces->bits |= msk;
+    ces->bit_count += 8;
+    _chirpy_encode_bits(ces, 0);
+    ++ces->block_len;
+    ces->crc = chirpy_update_crc8(next_byte, ces->crc);
+    if (ces->block_len == ces->block_size)
+        _chirpy_finish_block(ces);
+
+    return _chirpy_retrieve_next_tone(ces);
+}
+
+uint16_t chirpy_get_tone_period(uint8_t tone) {
+    // Create pre-computed tone periods array on first use
+    if (chirpy_tone_periods == NULL) {
+      chirpy_tone_periods = malloc((chirpy_control_tone + 1) * sizeof(uint32_t));
+      for (uint8_t i = 0; i < chirpy_control_tone + 1; ++i) {
+        uint32_t freq = chirpy_min_freq + i * cirpy_freq_step;
+        uint16_t period = 1000000 / freq;
+        chirpy_tone_periods[i] = period;
+      }
+    }
+    // Return pre-computed value, but be paranoid about indexing into array
+    if (tone > chirpy_control_tone)
+      tone = chirpy_control_tone;
+    return chirpy_tone_periods[tone];
+}
diff --git a/lib/chirpy_tx/chirpy_tx.h b/lib/chirpy_tx/chirpy_tx.h
new file mode 100644 (file)
index 0000000..092d05c
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * 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 CHIRPY_TX_H
+#define CHIRPY_TX_H
+
+/** @brief Calculates the CRC of a byte sequence.
+ */
+uint8_t chirpy_crc8(const uint8_t *addr, uint16_t len);
+
+/** @brief Updates a CRC value with the next byte of input.
+ */
+uint8_t chirpy_update_crc8(uint8_t next_byte, uint8_t crc);
+
+/** @brief Function to return next byte to be encoded.
+ * @param next_byte Pointer where implementor must write next byte (if available).
+ * @return 1 if there is a next byte, or 0 if no more data to encode.
+ */
+typedef uint8_t (*chirpy_get_next_byte_t)(uint8_t *next_byte);
+
+#define CHIRPY_TONE_BUF_SIZE 16
+
+// Holds state used by the encoder. Do not manipulate directly.
+typedef struct {
+    uint8_t tone_buf[CHIRPY_TONE_BUF_SIZE];
+    uint8_t tone_pos;
+    uint8_t tone_count;
+    uint8_t block_len;
+    uint8_t block_size;
+    uint8_t crc;
+    uint16_t bits;
+    uint8_t bit_count;
+    chirpy_get_next_byte_t get_next_byte;
+} chirpy_encoder_state_t;
+
+/** @brief Iniitializes the encoder state to be used during the transmission.
+ * @param ces Pointer to encoder state object to be initialized.
+ * @param get_next_byte Pointer to function that the encoder will call to fetch data byte by byte.
+ */
+void chirpy_init_encoder(chirpy_encoder_state_t *ces, chirpy_get_next_byte_t get_next_byte);
+
+/** @brief Returns the next tone to be transmitted.
+ * @details This function will call the get_next_byte function stored in the encoder state to
+ *          retrieve the next byte to be transmitted as needed. As a single byte is encoded as several tones,
+ *          and because the transmission also includes periodic CRC values, not every call to this function
+ *          will result in a callback for the next data byte.
+ * @param ced Pointer to the encoder state object.
+ * @return A tone index from 0 to N (where N is the largest tone index), or 255 if the transmission is over.
+ */
+uint8_t chirpy_get_next_tone(chirpy_encoder_state_t *ces);
+
+/** @brief Returns the period value for buzzing out a tone.
+ * @param tone The tone index, 0 thru 8.
+ * @return The period for the tone's frequency, i.e., 1_000_000 / freq.
+ */
+uint16_t chirpy_get_tone_period(uint8_t tone);
+
+/** @brief Typedef for a tick handler function.
+ */
+typedef void (*chirpy_tick_fun_t)(void *context);
+
+/** @brief Creature-comfort struct for use in your chirping code.
+ * @details The idea is to handle a tick that happens 64 times per second at the outermost level.
+ *          To get to the desired ~20 tones per second, increment a counter and call the actual
+ *          transmission ticker when tick_counter reaches tick_compare, with a compare value of 3.
+ *          seq_pos is for use by the transmission function to keep track of where it is in the data.
+ *          The current transmission function is stored in tick_fun. You can have multiple phases
+ *          by switching to a different function. E.g., intro countdown first, followed by data chirping.
+ */
+typedef struct {
+    uint8_t tick_count;
+    uint8_t tick_compare;
+    uint16_t seq_pos;
+    chirpy_tick_fun_t tick_fun;
+} chirpy_tick_state_t;
+
+#endif
diff --git a/lib/chirpy_tx/test/test_main.c b/lib/chirpy_tx/test/test_main.c
new file mode 100644 (file)
index 0000000..c76a3f3
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * 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.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include "../src/lib/chirpy_tx.h"
+#include "unity.h"
+
+
+void setUp(void) {
+}
+
+void tearDown(void) {
+}
+
+const char *crc_test_str0 = "";
+const char *crc_test_str1 = "h";
+const char *crc_test_str2 = "he";
+const char *crc_test_str3 = "hen";
+const char *crc_test_str4 = "henl";
+const char *crc_test_str5 = "henlO";
+
+#define CRC_VAL_COUNT 5
+const uint8_t crc_vals[] = {167, 118, 95, 92, 127};
+
+void test_crc8() {
+  TEST_ASSERT_EQUAL_UINT8(0, chirpy_crc8((const uint8_t *)crc_test_str0, 0));
+  TEST_ASSERT_EQUAL_UINT8(167, chirpy_crc8((const uint8_t *)crc_test_str1, strlen(crc_test_str1)));
+  TEST_ASSERT_EQUAL_UINT8(118, chirpy_crc8((const uint8_t *)crc_test_str2, strlen(crc_test_str2)));
+  TEST_ASSERT_EQUAL_UINT8(95, chirpy_crc8((const uint8_t *)crc_test_str3, strlen(crc_test_str3)));
+  TEST_ASSERT_EQUAL_UINT8(92, chirpy_crc8((const uint8_t *)crc_test_str4, strlen(crc_test_str4)));
+  TEST_ASSERT_EQUAL_UINT8(127, chirpy_crc8((const uint8_t *)crc_test_str5, strlen(crc_test_str5)));
+
+  uint8_t crc = 0;
+  for (uint16_t i = 0; i < CRC_VAL_COUNT; ++i) {
+    uint8_t next_byte = crc_test_str5[i];
+    crc = chirpy_update_crc8(next_byte, crc);
+    TEST_ASSERT_EQUAL_UINT8(crc_vals[i], crc);
+  }
+
+  crc = chirpy_update_crc8(0, 0);
+  TEST_ASSERT_EQUAL(0, crc);
+
+  crc = chirpy_update_crc8(0x4f, 0);
+  TEST_ASSERT_EQUAL(7, crc);
+}
+
+const uint16_t data_len_01 = 0;
+const uint8_t data_01[] = {};
+const uint16_t tones_len_01 = 6;
+const uint8_t tones_01[] = {8, 0, 8, 0, 8, 8};
+
+const uint16_t data_len_02 = 1;
+const uint8_t data_02[] = {0};
+const uint16_t tones_len_02 = 14;
+const uint8_t tones_02[] = {8, 0, 8, 0, 0, 0, 0, 8, 0, 0, 0, 8, 8, 8};
+
+const uint16_t data_len_03 = 1;
+const uint8_t data_03[] = {0x68};
+const uint16_t tones_len_03 = 14;
+const uint8_t tones_03[] = {8, 0, 8, 0, 3, 2, 0, 8, 5, 1, 6, 8, 8, 8};
+
+const uint16_t data_len_04 = 3;
+const uint8_t data_04[] = {0x68, 0x65, 0x6e};
+const uint16_t tones_len_04 = 19;
+const uint8_t tones_04[] = {8, 0, 8, 0, 3, 2, 0, 6, 2, 5, 5, 6, 8, 2, 7, 6, 8, 8, 8};
+
+const uint16_t data_len_05 = 4;
+const uint8_t data_05[] = {0x68, 0x65, 0x6e, 0x4f};
+const uint16_t tones_len_05 = 27;
+const uint8_t tones_05[] = {
+    8, 0, 8, 0, 3, 2, 0, 6, 2, 5, 5, 6, 8, 2, 7, 6, 8,
+    2, 3, 6, 8, 0, 1, 6, 8, 8, 8};
+
+uint8_t curr_data_pos;
+uint8_t curr_data_len;
+const uint8_t *curr_data;
+
+uint8_t get_next_byte(uint8_t *next_byte) {
+  if (curr_data_pos < curr_data_len) {
+    *next_byte = curr_data[curr_data_pos];
+    ++curr_data_pos;
+    return 1;
+  }
+  return 0;
+}
+
+void test_encoder_one(const uint8_t *data, uint16_t data_len, const uint8_t *tones, uint16_t tones_len) {
+  curr_data = data;
+  curr_data_len = data_len;
+  curr_data_pos = 0;
+  chirpy_encoder_state_t ces;
+  chirpy_init_encoder(&ces, get_next_byte);
+  ces.block_size = 3;
+
+  uint8_t got_tones[2048] = {0};
+  uint16_t got_tone_pos = 0;
+  while (got_tone_pos < 2048) {
+    uint8_t tone = chirpy_get_next_tone(&ces);
+    got_tones[got_tone_pos] = tone;
+    if (tone == 255) break;
+    ++got_tone_pos;
+  }
+  char buf1[65536];
+  char bufx[256];
+  memset(buf1, 0, 65536);
+  for (uint16_t i = 0; i < got_tone_pos; ++i) {
+    if (i == 0)
+      sprintf(bufx, "%d", got_tones[i]);
+    else
+      sprintf(bufx, ", %d", got_tones[i]);
+    strcat(buf1, bufx);
+  }
+  TEST_MESSAGE(buf1);
+  TEST_ASSERT_EQUAL(tones_len, got_tone_pos);
+  uint16_t smaller_len = tones_len < got_tone_pos ? tones_len : got_tone_pos;
+  TEST_ASSERT_EQUAL_UINT8_ARRAY(tones, got_tones, smaller_len);
+}
+
+void test_encoder() {
+  TEST_MESSAGE("Testing encoder with dataset 01");
+  test_encoder_one(data_01, data_len_01, tones_01, tones_len_01);
+  TEST_MESSAGE("Testing encoder with dataset 02");
+  test_encoder_one(data_02, data_len_02, tones_02, tones_len_02);
+  TEST_MESSAGE("Testing encoder with dataset 03");
+  test_encoder_one(data_03, data_len_03, tones_03, tones_len_03);
+  TEST_MESSAGE("Testing encoder with dataset 04");
+  test_encoder_one(data_04, data_len_04, tones_04, tones_len_04);
+  TEST_MESSAGE("Testing encoder with dataset 05");
+  test_encoder_one(data_05, data_len_05, tones_05, tones_len_05);
+}
+
+int main(void) {
+  UNITY_BEGIN();
+  RUN_TEST(test_crc8);
+  RUN_TEST(test_encoder);
+  return UNITY_END();
+}
diff --git a/lib/chirpy_tx/test/unity.c b/lib/chirpy_tx/test/unity.c
new file mode 100644 (file)
index 0000000..3e4bc04
--- /dev/null
@@ -0,0 +1,2466 @@
+/* =========================================================================
+    Unity Project - A Test Framework for C
+    Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
+    [Released under MIT License. Please refer to license.txt for details]
+============================================================================ */
+
+#include "unity.h"
+
+#ifndef UNITY_PROGMEM
+#define UNITY_PROGMEM
+#endif
+
+/* If omitted from header, declare overrideable prototypes here so they're ready for use */
+#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION
+void UNITY_OUTPUT_CHAR(int);
+#endif
+
+/* Helpful macros for us to use here in Assert functions */
+#define UNITY_FAIL_AND_BAIL         do { Unity.CurrentTestFailed  = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0)
+#define UNITY_IGNORE_AND_BAIL       do { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0)
+#define RETURN_IF_FAIL_OR_IGNORE    do { if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) { TEST_ABORT(); } } while (0)
+
+struct UNITY_STORAGE_T Unity;
+
+#ifdef UNITY_OUTPUT_COLOR
+const char UNITY_PROGMEM UnityStrOk[]                            = "\033[42mOK\033[0m";
+const char UNITY_PROGMEM UnityStrPass[]                          = "\033[42mPASS\033[0m";
+const char UNITY_PROGMEM UnityStrFail[]                          = "\033[41mFAIL\033[0m";
+const char UNITY_PROGMEM UnityStrIgnore[]                        = "\033[43mIGNORE\033[0m";
+#else
+const char UNITY_PROGMEM UnityStrOk[]                            = "OK";
+const char UNITY_PROGMEM UnityStrPass[]                          = "PASS";
+const char UNITY_PROGMEM UnityStrFail[]                          = "FAIL";
+const char UNITY_PROGMEM UnityStrIgnore[]                        = "IGNORE";
+#endif
+static const char UNITY_PROGMEM UnityStrNull[]                   = "NULL";
+static const char UNITY_PROGMEM UnityStrSpacer[]                 = ". ";
+static const char UNITY_PROGMEM UnityStrExpected[]               = " Expected ";
+static const char UNITY_PROGMEM UnityStrWas[]                    = " Was ";
+static const char UNITY_PROGMEM UnityStrGt[]                     = " to be greater than ";
+static const char UNITY_PROGMEM UnityStrLt[]                     = " to be less than ";
+static const char UNITY_PROGMEM UnityStrOrEqual[]                = "or equal to ";
+static const char UNITY_PROGMEM UnityStrNotEqual[]               = " to be not equal to ";
+static const char UNITY_PROGMEM UnityStrElement[]                = " Element ";
+static const char UNITY_PROGMEM UnityStrByte[]                   = " Byte ";
+static const char UNITY_PROGMEM UnityStrMemory[]                 = " Memory Mismatch.";
+static const char UNITY_PROGMEM UnityStrDelta[]                  = " Values Not Within Delta ";
+static const char UNITY_PROGMEM UnityStrPointless[]              = " You Asked Me To Compare Nothing, Which Was Pointless.";
+static const char UNITY_PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL";
+static const char UNITY_PROGMEM UnityStrNullPointerForActual[]   = " Actual pointer was NULL";
+#ifndef UNITY_EXCLUDE_FLOAT
+static const char UNITY_PROGMEM UnityStrNot[]                    = "Not ";
+static const char UNITY_PROGMEM UnityStrInf[]                    = "Infinity";
+static const char UNITY_PROGMEM UnityStrNegInf[]                 = "Negative Infinity";
+static const char UNITY_PROGMEM UnityStrNaN[]                    = "NaN";
+static const char UNITY_PROGMEM UnityStrDet[]                    = "Determinate";
+static const char UNITY_PROGMEM UnityStrInvalidFloatTrait[]      = "Invalid Float Trait";
+#endif
+const char UNITY_PROGMEM UnityStrErrShorthand[]                  = "Unity Shorthand Support Disabled";
+const char UNITY_PROGMEM UnityStrErrFloat[]                      = "Unity Floating Point Disabled";
+const char UNITY_PROGMEM UnityStrErrDouble[]                     = "Unity Double Precision Disabled";
+const char UNITY_PROGMEM UnityStrErr64[]                         = "Unity 64-bit Support Disabled";
+static const char UNITY_PROGMEM UnityStrBreaker[]                = "-----------------------";
+static const char UNITY_PROGMEM UnityStrResultsTests[]           = " Tests ";
+static const char UNITY_PROGMEM UnityStrResultsFailures[]        = " Failures ";
+static const char UNITY_PROGMEM UnityStrResultsIgnored[]         = " Ignored ";
+#ifndef UNITY_EXCLUDE_DETAILS
+static const char UNITY_PROGMEM UnityStrDetail1Name[]            = UNITY_DETAIL1_NAME " ";
+static const char UNITY_PROGMEM UnityStrDetail2Name[]            = " " UNITY_DETAIL2_NAME " ";
+#endif
+/*-----------------------------------------------
+ * Pretty Printers & Test Result Output Handlers
+ *-----------------------------------------------*/
+
+/*-----------------------------------------------*/
+/* Local helper function to print characters. */
+static void UnityPrintChar(const char* pch)
+{
+    /* printable characters plus CR & LF are printed */
+    if ((*pch <= 126) && (*pch >= 32))
+    {
+        UNITY_OUTPUT_CHAR(*pch);
+    }
+    /* write escaped carriage returns */
+    else if (*pch == 13)
+    {
+        UNITY_OUTPUT_CHAR('\\');
+        UNITY_OUTPUT_CHAR('r');
+    }
+    /* write escaped line feeds */
+    else if (*pch == 10)
+    {
+        UNITY_OUTPUT_CHAR('\\');
+        UNITY_OUTPUT_CHAR('n');
+    }
+    /* unprintable characters are shown as codes */
+    else
+    {
+        UNITY_OUTPUT_CHAR('\\');
+        UNITY_OUTPUT_CHAR('x');
+        UnityPrintNumberHex((UNITY_UINT)*pch, 2);
+    }
+}
+
+/*-----------------------------------------------*/
+/* Local helper function to print ANSI escape strings e.g. "\033[42m". */
+#ifdef UNITY_OUTPUT_COLOR
+static UNITY_UINT UnityPrintAnsiEscapeString(const char* string)
+{
+    const char* pch = string;
+    UNITY_UINT count = 0;
+
+    while (*pch && (*pch != 'm'))
+    {
+        UNITY_OUTPUT_CHAR(*pch);
+        pch++;
+        count++;
+    }
+    UNITY_OUTPUT_CHAR('m');
+    count++;
+
+    return count;
+}
+#endif
+
+/*-----------------------------------------------*/
+void UnityPrint(const char* string)
+{
+    const char* pch = string;
+
+    if (pch != NULL)
+    {
+        while (*pch)
+        {
+#ifdef UNITY_OUTPUT_COLOR
+            /* print ANSI escape code */
+            if ((*pch == 27) && (*(pch + 1) == '['))
+            {
+                pch += UnityPrintAnsiEscapeString(pch);
+                continue;
+            }
+#endif
+            UnityPrintChar(pch);
+            pch++;
+        }
+    }
+}
+/*-----------------------------------------------*/
+void UnityPrintLen(const char* string, const UNITY_UINT32 length)
+{
+    const char* pch = string;
+
+    if (pch != NULL)
+    {
+        while (*pch && ((UNITY_UINT32)(pch - string) < length))
+        {
+            /* printable characters plus CR & LF are printed */
+            if ((*pch <= 126) && (*pch >= 32))
+            {
+                UNITY_OUTPUT_CHAR(*pch);
+            }
+            /* write escaped carriage returns */
+            else if (*pch == 13)
+            {
+                UNITY_OUTPUT_CHAR('\\');
+                UNITY_OUTPUT_CHAR('r');
+            }
+            /* write escaped line feeds */
+            else if (*pch == 10)
+            {
+                UNITY_OUTPUT_CHAR('\\');
+                UNITY_OUTPUT_CHAR('n');
+            }
+            /* unprintable characters are shown as codes */
+            else
+            {
+                UNITY_OUTPUT_CHAR('\\');
+                UNITY_OUTPUT_CHAR('x');
+                UnityPrintNumberHex((UNITY_UINT)*pch, 2);
+            }
+            pch++;
+        }
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style)
+{
+    if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+    {
+        if (style == UNITY_DISPLAY_STYLE_CHAR)
+        {
+            /* printable characters plus CR & LF are printed */
+            UNITY_OUTPUT_CHAR('\'');
+            if ((number <= 126) && (number >= 32))
+            {
+                UNITY_OUTPUT_CHAR((int)number);
+            }
+            /* write escaped carriage returns */
+            else if (number == 13)
+            {
+                UNITY_OUTPUT_CHAR('\\');
+                UNITY_OUTPUT_CHAR('r');
+            }
+            /* write escaped line feeds */
+            else if (number == 10)
+            {
+                UNITY_OUTPUT_CHAR('\\');
+                UNITY_OUTPUT_CHAR('n');
+            }
+            /* unprintable characters are shown as codes */
+            else
+            {
+                UNITY_OUTPUT_CHAR('\\');
+                UNITY_OUTPUT_CHAR('x');
+                UnityPrintNumberHex((UNITY_UINT)number, 2);
+            }
+            UNITY_OUTPUT_CHAR('\'');
+        }
+        else
+        {
+            UnityPrintNumber(number);
+        }
+    }
+    else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT)
+    {
+        UnityPrintNumberUnsigned((UNITY_UINT)number);
+    }
+    else
+    {
+        UNITY_OUTPUT_CHAR('0');
+        UNITY_OUTPUT_CHAR('x');
+        UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2));
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityPrintNumber(const UNITY_INT number_to_print)
+{
+    UNITY_UINT number = (UNITY_UINT)number_to_print;
+
+    if (number_to_print < 0)
+    {
+        /* A negative number, including MIN negative */
+        UNITY_OUTPUT_CHAR('-');
+        number = (~number) + 1;
+    }
+    UnityPrintNumberUnsigned(number);
+}
+
+/*-----------------------------------------------
+ * basically do an itoa using as little ram as possible */
+void UnityPrintNumberUnsigned(const UNITY_UINT number)
+{
+    UNITY_UINT divisor = 1;
+
+    /* figure out initial divisor */
+    while (number / divisor > 9)
+    {
+        divisor *= 10;
+    }
+
+    /* now mod and print, then divide divisor */
+    do
+    {
+        UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10)));
+        divisor /= 10;
+    } while (divisor > 0);
+}
+
+/*-----------------------------------------------*/
+void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print)
+{
+    int nibble;
+    char nibbles = nibbles_to_print;
+
+    if ((unsigned)nibbles > UNITY_MAX_NIBBLES)
+    {
+        nibbles = UNITY_MAX_NIBBLES;
+    }
+
+    while (nibbles > 0)
+    {
+        nibbles--;
+        nibble = (int)(number >> (nibbles * 4)) & 0x0F;
+        if (nibble <= 9)
+        {
+            UNITY_OUTPUT_CHAR((char)('0' + nibble));
+        }
+        else
+        {
+            UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble));
+        }
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number)
+{
+    UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1);
+    UNITY_INT32 i;
+
+    for (i = 0; i < UNITY_INT_WIDTH; i++)
+    {
+        if (current_bit & mask)
+        {
+            if (current_bit & number)
+            {
+                UNITY_OUTPUT_CHAR('1');
+            }
+            else
+            {
+                UNITY_OUTPUT_CHAR('0');
+            }
+        }
+        else
+        {
+            UNITY_OUTPUT_CHAR('X');
+        }
+        current_bit = current_bit >> 1;
+    }
+}
+
+/*-----------------------------------------------*/
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+/*
+ * This function prints a floating-point value in a format similar to
+ * printf("%.7g") on a single-precision machine or printf("%.9g") on a
+ * double-precision machine.  The 7th digit won't always be totally correct
+ * in single-precision operation (for that level of accuracy, a more
+ * complicated algorithm would be needed).
+ */
+void UnityPrintFloat(const UNITY_DOUBLE input_number)
+{
+#ifdef UNITY_INCLUDE_DOUBLE
+    static const int sig_digits = 9;
+    static const UNITY_INT32 min_scaled = 100000000;
+    static const UNITY_INT32 max_scaled = 1000000000;
+#else
+    static const int sig_digits = 7;
+    static const UNITY_INT32 min_scaled = 1000000;
+    static const UNITY_INT32 max_scaled = 10000000;
+#endif
+
+    UNITY_DOUBLE number = input_number;
+
+    /* print minus sign (does not handle negative zero) */
+    if (number < 0.0f)
+    {
+        UNITY_OUTPUT_CHAR('-');
+        number = -number;
+    }
+
+    /* handle zero, NaN, and +/- infinity */
+    if (number == 0.0f)
+    {
+        UnityPrint("0");
+    }
+    else if (isnan(number))
+    {
+        UnityPrint("nan");
+    }
+    else if (isinf(number))
+    {
+        UnityPrint("inf");
+    }
+    else
+    {
+        UNITY_INT32 n_int = 0;
+        UNITY_INT32 n;
+        int         exponent = 0;
+        int         decimals;
+        int         digits;
+        char        buf[16] = {0};
+
+        /*
+         * Scale up or down by powers of 10.  To minimize rounding error,
+         * start with a factor/divisor of 10^10, which is the largest
+         * power of 10 that can be represented exactly.  Finally, compute
+         * (exactly) the remaining power of 10 and perform one more
+         * multiplication or division.
+         */
+        if (number < 1.0f)
+        {
+            UNITY_DOUBLE factor = 1.0f;
+
+            while (number < (UNITY_DOUBLE)max_scaled / 1e10f)  { number *= 1e10f; exponent -= 10; }
+            while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; }
+
+            number *= factor;
+        }
+        else if (number > (UNITY_DOUBLE)max_scaled)
+        {
+            UNITY_DOUBLE divisor = 1.0f;
+
+            while (number > (UNITY_DOUBLE)min_scaled * 1e10f)   { number  /= 1e10f; exponent += 10; }
+            while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; }
+
+            number /= divisor;
+        }
+        else
+        {
+            /*
+             * In this range, we can split off the integer part before
+             * doing any multiplications.  This reduces rounding error by
+             * freeing up significant bits in the fractional part.
+             */
+            UNITY_DOUBLE factor = 1.0f;
+            n_int = (UNITY_INT32)number;
+            number -= (UNITY_DOUBLE)n_int;
+
+            while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; }
+
+            number *= factor;
+        }
+
+        /* round to nearest integer */
+        n = ((UNITY_INT32)(number + number) + 1) / 2;
+
+#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO
+        /* round to even if exactly between two integers */
+        if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f))
+            n--;
+#endif
+
+        n += n_int;
+
+        if (n >= max_scaled)
+        {
+            n = min_scaled;
+            exponent++;
+        }
+
+        /* determine where to place decimal point */
+        decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1);
+        exponent += decimals;
+
+        /* truncate trailing zeroes after decimal point */
+        while ((decimals > 0) && ((n % 10) == 0))
+        {
+            n /= 10;
+            decimals--;
+        }
+
+        /* build up buffer in reverse order */
+        digits = 0;
+        while ((n != 0) || (digits <= decimals))
+        {
+            buf[digits++] = (char)('0' + n % 10);
+            n /= 10;
+        }
+
+        /* print out buffer (backwards) */
+        while (digits > 0)
+        {
+            if (digits == decimals)
+            {
+                UNITY_OUTPUT_CHAR('.');
+            }
+            UNITY_OUTPUT_CHAR(buf[--digits]);
+        }
+
+        /* print exponent if needed */
+        if (exponent != 0)
+        {
+            UNITY_OUTPUT_CHAR('e');
+
+            if (exponent < 0)
+            {
+                UNITY_OUTPUT_CHAR('-');
+                exponent = -exponent;
+            }
+            else
+            {
+                UNITY_OUTPUT_CHAR('+');
+            }
+
+            digits = 0;
+            while ((exponent != 0) || (digits < 2))
+            {
+                buf[digits++] = (char)('0' + exponent % 10);
+                exponent /= 10;
+            }
+            while (digits > 0)
+            {
+                UNITY_OUTPUT_CHAR(buf[--digits]);
+            }
+        }
+    }
+}
+#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */
+
+/*-----------------------------------------------*/
+static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line)
+{
+#ifdef UNITY_OUTPUT_FOR_ECLIPSE
+    UNITY_OUTPUT_CHAR('(');
+    UnityPrint(file);
+    UNITY_OUTPUT_CHAR(':');
+    UnityPrintNumber((UNITY_INT)line);
+    UNITY_OUTPUT_CHAR(')');
+    UNITY_OUTPUT_CHAR(' ');
+    UnityPrint(Unity.CurrentTestName);
+    UNITY_OUTPUT_CHAR(':');
+#else
+#ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH
+    UnityPrint("<SRCREF line=");
+    UnityPrintNumber((UNITY_INT)line);
+    UnityPrint(" file=\"");
+    UnityPrint(file);
+    UNITY_OUTPUT_CHAR('"');
+    UNITY_OUTPUT_CHAR('>');
+    UnityPrint(Unity.CurrentTestName);
+    UnityPrint("</SRCREF> ");
+#else
+#ifdef UNITY_OUTPUT_FOR_QT_CREATOR
+    UnityPrint("file://");
+    UnityPrint(file);
+    UNITY_OUTPUT_CHAR(':');
+    UnityPrintNumber((UNITY_INT)line);
+    UNITY_OUTPUT_CHAR(' ');
+    UnityPrint(Unity.CurrentTestName);
+    UNITY_OUTPUT_CHAR(':');
+#else
+    UnityPrint(file);
+    UNITY_OUTPUT_CHAR(':');
+    UnityPrintNumber((UNITY_INT)line);
+    UNITY_OUTPUT_CHAR(':');
+    UnityPrint(Unity.CurrentTestName);
+    UNITY_OUTPUT_CHAR(':');
+#endif
+#endif
+#endif
+}
+
+/*-----------------------------------------------*/
+static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line)
+{
+    UnityTestResultsBegin(Unity.TestFile, line);
+    UnityPrint(UnityStrFail);
+    UNITY_OUTPUT_CHAR(':');
+}
+
+/*-----------------------------------------------*/
+void UnityConcludeTest(void)
+{
+    if (Unity.CurrentTestIgnored)
+    {
+        Unity.TestIgnores++;
+    }
+    else if (!Unity.CurrentTestFailed)
+    {
+        UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber);
+        UnityPrint(UnityStrPass);
+    }
+    else
+    {
+        Unity.TestFailures++;
+    }
+
+    Unity.CurrentTestFailed = 0;
+    Unity.CurrentTestIgnored = 0;
+    UNITY_PRINT_EXEC_TIME();
+    UNITY_PRINT_EOL();
+    UNITY_FLUSH_CALL();
+}
+
+/*-----------------------------------------------*/
+static void UnityAddMsgIfSpecified(const char* msg)
+{
+#ifdef UNITY_PRINT_TEST_CONTEXT
+    UnityPrint(UnityStrSpacer);
+    UNITY_PRINT_TEST_CONTEXT();
+#endif
+#ifndef UNITY_EXCLUDE_DETAILS
+    if (Unity.CurrentDetail1)
+    {
+        UnityPrint(UnityStrSpacer);
+        UnityPrint(UnityStrDetail1Name);
+        UnityPrint(Unity.CurrentDetail1);
+        if (Unity.CurrentDetail2)
+        {
+            UnityPrint(UnityStrDetail2Name);
+            UnityPrint(Unity.CurrentDetail2);
+        }
+    }
+#endif
+    if (msg)
+    {
+        UnityPrint(UnityStrSpacer);
+        UnityPrint(msg);
+    }
+}
+
+/*-----------------------------------------------*/
+static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual)
+{
+    UnityPrint(UnityStrExpected);
+    if (expected != NULL)
+    {
+        UNITY_OUTPUT_CHAR('\'');
+        UnityPrint(expected);
+        UNITY_OUTPUT_CHAR('\'');
+    }
+    else
+    {
+        UnityPrint(UnityStrNull);
+    }
+    UnityPrint(UnityStrWas);
+    if (actual != NULL)
+    {
+        UNITY_OUTPUT_CHAR('\'');
+        UnityPrint(actual);
+        UNITY_OUTPUT_CHAR('\'');
+    }
+    else
+    {
+        UnityPrint(UnityStrNull);
+    }
+}
+
+/*-----------------------------------------------*/
+static void UnityPrintExpectedAndActualStringsLen(const char* expected,
+                                                  const char* actual,
+                                                  const UNITY_UINT32 length)
+{
+    UnityPrint(UnityStrExpected);
+    if (expected != NULL)
+    {
+        UNITY_OUTPUT_CHAR('\'');
+        UnityPrintLen(expected, length);
+        UNITY_OUTPUT_CHAR('\'');
+    }
+    else
+    {
+        UnityPrint(UnityStrNull);
+    }
+    UnityPrint(UnityStrWas);
+    if (actual != NULL)
+    {
+        UNITY_OUTPUT_CHAR('\'');
+        UnityPrintLen(actual, length);
+        UNITY_OUTPUT_CHAR('\'');
+    }
+    else
+    {
+        UnityPrint(UnityStrNull);
+    }
+}
+
+/*-----------------------------------------------
+ * Assertion & Control Helpers
+ *-----------------------------------------------*/
+
+/*-----------------------------------------------*/
+static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected,
+                               UNITY_INTERNAL_PTR actual,
+                               const UNITY_LINE_TYPE lineNumber,
+                               const char* msg)
+{
+    /* Both are NULL or same pointer */
+    if (expected == actual) { return 0; }
+
+    /* print and return true if just expected is NULL */
+    if (expected == NULL)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrNullPointerForExpected);
+        UnityAddMsgIfSpecified(msg);
+        return 1;
+    }
+
+    /* print and return true if just actual is NULL */
+    if (actual == NULL)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrNullPointerForActual);
+        UnityAddMsgIfSpecified(msg);
+        return 1;
+    }
+
+    return 0; /* return false if neither is NULL */
+}
+
+/*-----------------------------------------------
+ * Assertion Functions
+ *-----------------------------------------------*/
+
+/*-----------------------------------------------*/
+void UnityAssertBits(const UNITY_INT mask,
+                     const UNITY_INT expected,
+                     const UNITY_INT actual,
+                     const char* msg,
+                     const UNITY_LINE_TYPE lineNumber)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if ((mask & expected) != (mask & actual))
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected);
+        UnityPrint(UnityStrWas);
+        UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualNumber(const UNITY_INT expected,
+                            const UNITY_INT actual,
+                            const char* msg,
+                            const UNITY_LINE_TYPE lineNumber,
+                            const UNITY_DISPLAY_STYLE_T style)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (expected != actual)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        UnityPrintNumberByStyle(expected, style);
+        UnityPrint(UnityStrWas);
+        UnityPrintNumberByStyle(actual, style);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold,
+                                           const UNITY_INT actual,
+                                           const UNITY_COMPARISON_T compare,
+                                           const char *msg,
+                                           const UNITY_LINE_TYPE lineNumber,
+                                           const UNITY_DISPLAY_STYLE_T style)
+{
+    int failed = 0;
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if ((threshold == actual) && (compare & UNITY_EQUAL_TO)) { return; }
+    if ((threshold == actual))                               { failed = 1; }
+
+    if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+    {
+        if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
+        if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
+    }
+    else /* UINT or HEX */
+    {
+        if (((UNITY_UINT)actual > (UNITY_UINT)threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
+        if (((UNITY_UINT)actual < (UNITY_UINT)threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
+    }
+
+    if (failed)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        UnityPrintNumberByStyle(actual, style);
+        if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt);       }
+        if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt);       }
+        if (compare & UNITY_EQUAL_TO)     { UnityPrint(UnityStrOrEqual);  }
+        if (compare == UNITY_NOT_EQUAL)   { UnityPrint(UnityStrNotEqual); }
+        UnityPrintNumberByStyle(threshold, style);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+#define UnityPrintPointlessAndBail()       \
+do {                                       \
+    UnityTestResultsFailBegin(lineNumber); \
+    UnityPrint(UnityStrPointless);         \
+    UnityAddMsgIfSpecified(msg);           \
+    UNITY_FAIL_AND_BAIL;                   \
+} while (0)
+
+/*-----------------------------------------------*/
+void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
+                              UNITY_INTERNAL_PTR actual,
+                              const UNITY_UINT32 num_elements,
+                              const char* msg,
+                              const UNITY_LINE_TYPE lineNumber,
+                              const UNITY_DISPLAY_STYLE_T style,
+                              const UNITY_FLAGS_T flags)
+{
+    UNITY_UINT32 elements  = num_elements;
+    unsigned int length    = style & 0xF;
+    unsigned int increment = 0;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (num_elements == 0)
+    {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+        UnityPrintPointlessAndBail();
+#endif
+    }
+
+    if (expected == actual)
+    {
+        return; /* Both are NULL or same pointer */
+    }
+
+    if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
+    {
+        UNITY_FAIL_AND_BAIL;
+    }
+
+    while ((elements > 0) && (elements--))
+    {
+        UNITY_INT expect_val;
+        UNITY_INT actual_val;
+
+        switch (length)
+        {
+            case 1:
+                expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected;
+                actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual;
+                if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
+                {
+                    expect_val &= 0x000000FF;
+                    actual_val &= 0x000000FF;
+                }
+                increment  = sizeof(UNITY_INT8);
+                break;
+
+            case 2:
+                expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected;
+                actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual;
+                if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
+                {
+                    expect_val &= 0x0000FFFF;
+                    actual_val &= 0x0000FFFF;
+                }
+                increment  = sizeof(UNITY_INT16);
+                break;
+
+#ifdef UNITY_SUPPORT_64
+            case 8:
+                expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected;
+                actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual;
+                increment  = sizeof(UNITY_INT64);
+                break;
+#endif
+
+            default: /* default is length 4 bytes */
+            case 4:
+                expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected;
+                actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual;
+#ifdef UNITY_SUPPORT_64
+                if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
+                {
+                    expect_val &= 0x00000000FFFFFFFF;
+                    actual_val &= 0x00000000FFFFFFFF;
+                }
+#endif
+                increment  = sizeof(UNITY_INT32);
+                length = 4;
+                break;
+        }
+
+        if (expect_val != actual_val)
+        {
+            if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8)))
+            {   /* For UINT, remove sign extension (padding 1's) from signed type casts above */
+                UNITY_INT mask = 1;
+                mask = (mask << 8 * length) - 1;
+                expect_val &= mask;
+                actual_val &= mask;
+            }
+            UnityTestResultsFailBegin(lineNumber);
+            UnityPrint(UnityStrElement);
+            UnityPrintNumberUnsigned(num_elements - elements - 1);
+            UnityPrint(UnityStrExpected);
+            UnityPrintNumberByStyle(expect_val, style);
+            UnityPrint(UnityStrWas);
+            UnityPrintNumberByStyle(actual_val, style);
+            UnityAddMsgIfSpecified(msg);
+            UNITY_FAIL_AND_BAIL;
+        }
+        /* Walk through array by incrementing the pointers */
+        if (flags == UNITY_ARRAY_TO_ARRAY)
+        {
+            expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment);
+        }
+        actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment);
+    }
+}
+
+/*-----------------------------------------------*/
+#ifndef UNITY_EXCLUDE_FLOAT
+/* Wrap this define in a function with variable types as float or double */
+#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff)                           \
+    if (isinf(expected) && isinf(actual) && (((expected) < 0) == ((actual) < 0))) return 1;   \
+    if (UNITY_NAN_CHECK) return 1;                                                            \
+    (diff) = (actual) - (expected);                                                           \
+    if ((diff) < 0) (diff) = -(diff);                                                         \
+    if ((delta) < 0) (delta) = -(delta);                                                      \
+    return !(isnan(diff) || isinf(diff) || ((diff) > (delta)))
+    /* This first part of this condition will catch any NaN or Infinite values */
+#ifndef UNITY_NAN_NOT_EQUAL_NAN
+  #define UNITY_NAN_CHECK isnan(expected) && isnan(actual)
+#else
+  #define UNITY_NAN_CHECK 0
+#endif
+
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+  #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \
+  do {                                                            \
+    UnityPrint(UnityStrExpected);                                 \
+    UnityPrintFloat(expected);                                    \
+    UnityPrint(UnityStrWas);                                      \
+    UnityPrintFloat(actual);                                      \
+  } while (0)
+#else
+  #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \
+    UnityPrint(UnityStrDelta)
+#endif /* UNITY_EXCLUDE_FLOAT_PRINT */
+
+/*-----------------------------------------------*/
+static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual)
+{
+    UNITY_FLOAT diff;
+    UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff);
+}
+
+/*-----------------------------------------------*/
+void UnityAssertWithinFloatArray(const UNITY_FLOAT delta,
+                                 UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected,
+                                 UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual,
+                                 const UNITY_UINT32 num_elements,
+                                 const char* msg,
+                                 const UNITY_LINE_TYPE lineNumber,
+                                 const UNITY_FLAGS_T flags)
+{
+    UNITY_UINT32 elements = num_elements;
+    UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected;
+    UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual;
+    UNITY_FLOAT in_delta = delta;
+    UNITY_FLOAT current_element_delta = delta;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (elements == 0)
+    {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+        UnityPrintPointlessAndBail();
+#endif
+    }
+
+    if (isinf(in_delta))
+    {
+        return; /* Arrays will be force equal with infinite delta */
+    }
+
+    if (isnan(in_delta))
+    {
+        /* Delta must be correct number */
+        UnityPrintPointlessAndBail();
+    }
+
+    if (expected == actual)
+    {
+        return; /* Both are NULL or same pointer */
+    }
+
+    if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
+    {
+        UNITY_FAIL_AND_BAIL;
+    }
+
+    /* fix delta sign if need */
+    if (in_delta < 0)
+    {
+        in_delta = -in_delta;
+    }
+
+    while (elements--)
+    {
+        current_element_delta = *ptr_expected * UNITY_FLOAT_PRECISION;
+
+        if (current_element_delta < 0)
+        {
+            /* fix delta sign for correct calculations */
+            current_element_delta = -current_element_delta;
+        }
+
+        if (!UnityFloatsWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual))
+        {
+            UnityTestResultsFailBegin(lineNumber);
+            UnityPrint(UnityStrElement);
+            UnityPrintNumberUnsigned(num_elements - elements - 1);
+            UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual);
+            UnityAddMsgIfSpecified(msg);
+            UNITY_FAIL_AND_BAIL;
+        }
+        if (flags == UNITY_ARRAY_TO_ARRAY)
+        {
+            ptr_expected++;
+        }
+        ptr_actual++;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertFloatsWithin(const UNITY_FLOAT delta,
+                             const UNITY_FLOAT expected,
+                             const UNITY_FLOAT actual,
+                             const char* msg,
+                             const UNITY_LINE_TYPE lineNumber)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+
+    if (!UnityFloatsWithin(delta, expected, actual))
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta,
+                                const UNITY_FLOAT expected,
+                                const UNITY_FLOAT actual,
+                                const char* msg,
+                                const UNITY_LINE_TYPE lineNumber)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (UnityFloatsWithin(delta, expected, actual))
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        UnityPrintFloat((UNITY_DOUBLE)expected);
+        UnityPrint(UnityStrNotEqual);
+        UnityPrintFloat((UNITY_DOUBLE)actual);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold,
+                                   const UNITY_FLOAT actual,
+                                   const UNITY_COMPARISON_T compare,
+                                   const char* msg,
+                                   const UNITY_LINE_TYPE lineNumber)
+{
+    int failed;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    failed = 0;
+
+    /* Checking for "not success" rather than failure to get the right result for NaN */
+    if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
+    if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
+
+    if ((compare & UNITY_EQUAL_TO) && UnityFloatsWithin(threshold * UNITY_FLOAT_PRECISION, threshold, actual)) { failed = 0; }
+
+    if (failed)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        UnityPrintFloat(actual);
+        if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); }
+        if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); }
+        if (compare & UNITY_EQUAL_TO)     { UnityPrint(UnityStrOrEqual);  }
+        UnityPrintFloat(threshold);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
+                             const char* msg,
+                             const UNITY_LINE_TYPE lineNumber,
+                             const UNITY_FLOAT_TRAIT_T style)
+{
+    const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet};
+    UNITY_INT should_be_trait = ((UNITY_INT)style & 1);
+    UNITY_INT is_trait        = !should_be_trait;
+    UNITY_INT trait_index     = (UNITY_INT)(style >> 1);
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    switch (style)
+    {
+        case UNITY_FLOAT_IS_INF:
+        case UNITY_FLOAT_IS_NOT_INF:
+            is_trait = isinf(actual) && (actual > 0);
+            break;
+        case UNITY_FLOAT_IS_NEG_INF:
+        case UNITY_FLOAT_IS_NOT_NEG_INF:
+            is_trait = isinf(actual) && (actual < 0);
+            break;
+
+        case UNITY_FLOAT_IS_NAN:
+        case UNITY_FLOAT_IS_NOT_NAN:
+            is_trait = isnan(actual) ? 1 : 0;
+            break;
+
+        case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */
+        case UNITY_FLOAT_IS_NOT_DET:
+            is_trait = !isinf(actual) && !isnan(actual);
+            break;
+
+        default: /* including UNITY_FLOAT_INVALID_TRAIT */
+            trait_index = 0;
+            trait_names[0] = UnityStrInvalidFloatTrait;
+            break;
+    }
+
+    if (is_trait != should_be_trait)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        if (!should_be_trait)
+        {
+            UnityPrint(UnityStrNot);
+        }
+        UnityPrint(trait_names[trait_index]);
+        UnityPrint(UnityStrWas);
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+        UnityPrintFloat((UNITY_DOUBLE)actual);
+#else
+        if (should_be_trait)
+        {
+            UnityPrint(UnityStrNot);
+        }
+        UnityPrint(trait_names[trait_index]);
+#endif
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+#endif /* not UNITY_EXCLUDE_FLOAT */
+
+/*-----------------------------------------------*/
+#ifndef UNITY_EXCLUDE_DOUBLE
+static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual)
+{
+    UNITY_DOUBLE diff;
+    UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff);
+}
+
+/*-----------------------------------------------*/
+void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta,
+                                  UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected,
+                                  UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual,
+                                  const UNITY_UINT32 num_elements,
+                                  const char* msg,
+                                  const UNITY_LINE_TYPE lineNumber,
+                                  const UNITY_FLAGS_T flags)
+{
+    UNITY_UINT32 elements = num_elements;
+    UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected;
+    UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual;
+    UNITY_DOUBLE in_delta = delta;
+    UNITY_DOUBLE current_element_delta = delta;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (elements == 0)
+    {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+        UnityPrintPointlessAndBail();
+#endif
+    }
+
+    if (isinf(in_delta))
+    {
+        return; /* Arrays will be force equal with infinite delta */
+    }
+
+    if (isnan(in_delta))
+    {
+        /* Delta must be correct number */
+        UnityPrintPointlessAndBail();
+    }
+
+    if (expected == actual)
+    {
+        return; /* Both are NULL or same pointer */
+    }
+
+    if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
+    {
+        UNITY_FAIL_AND_BAIL;
+    }
+
+    /* fix delta sign if need */
+    if (in_delta < 0)
+    {
+        in_delta = -in_delta;
+    }
+
+    while (elements--)
+    {
+        current_element_delta = *ptr_expected * UNITY_DOUBLE_PRECISION;
+
+        if (current_element_delta < 0)
+        {
+            /* fix delta sign for correct calculations */
+            current_element_delta = -current_element_delta;
+        }
+
+        if (!UnityDoublesWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual))
+        {
+            UnityTestResultsFailBegin(lineNumber);
+            UnityPrint(UnityStrElement);
+            UnityPrintNumberUnsigned(num_elements - elements - 1);
+            UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual);
+            UnityAddMsgIfSpecified(msg);
+            UNITY_FAIL_AND_BAIL;
+        }
+        if (flags == UNITY_ARRAY_TO_ARRAY)
+        {
+            ptr_expected++;
+        }
+        ptr_actual++;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertDoublesWithin(const UNITY_DOUBLE delta,
+                              const UNITY_DOUBLE expected,
+                              const UNITY_DOUBLE actual,
+                              const char* msg,
+                              const UNITY_LINE_TYPE lineNumber)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (!UnityDoublesWithin(delta, expected, actual))
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta,
+                                 const UNITY_DOUBLE expected,
+                                 const UNITY_DOUBLE actual,
+                                 const char* msg,
+                                 const UNITY_LINE_TYPE lineNumber)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (UnityDoublesWithin(delta, expected, actual))
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        UnityPrintFloat((UNITY_DOUBLE)expected);
+        UnityPrint(UnityStrNotEqual);
+        UnityPrintFloat((UNITY_DOUBLE)actual);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold,
+                                    const UNITY_DOUBLE actual,
+                                    const UNITY_COMPARISON_T compare,
+                                    const char* msg,
+                                    const UNITY_LINE_TYPE lineNumber)
+{
+    int failed;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    failed = 0;
+
+    /* Checking for "not success" rather than failure to get the right result for NaN */
+    if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
+    if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
+
+    if ((compare & UNITY_EQUAL_TO) && UnityDoublesWithin(threshold * UNITY_DOUBLE_PRECISION, threshold, actual)) { failed = 0; }
+
+    if (failed)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        UnityPrintFloat(actual);
+        if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); }
+        if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); }
+        if (compare & UNITY_EQUAL_TO)     { UnityPrint(UnityStrOrEqual);  }
+        UnityPrintFloat(threshold);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual,
+                              const char* msg,
+                              const UNITY_LINE_TYPE lineNumber,
+                              const UNITY_FLOAT_TRAIT_T style)
+{
+    const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet};
+    UNITY_INT should_be_trait = ((UNITY_INT)style & 1);
+    UNITY_INT is_trait        = !should_be_trait;
+    UNITY_INT trait_index     = (UNITY_INT)(style >> 1);
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    switch (style)
+    {
+        case UNITY_FLOAT_IS_INF:
+        case UNITY_FLOAT_IS_NOT_INF:
+            is_trait = isinf(actual) && (actual > 0);
+            break;
+        case UNITY_FLOAT_IS_NEG_INF:
+        case UNITY_FLOAT_IS_NOT_NEG_INF:
+            is_trait = isinf(actual) && (actual < 0);
+            break;
+
+        case UNITY_FLOAT_IS_NAN:
+        case UNITY_FLOAT_IS_NOT_NAN:
+            is_trait = isnan(actual) ? 1 : 0;
+            break;
+
+        case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */
+        case UNITY_FLOAT_IS_NOT_DET:
+            is_trait = !isinf(actual) && !isnan(actual);
+            break;
+
+        default: /* including UNITY_FLOAT_INVALID_TRAIT */
+            trait_index = 0;
+            trait_names[0] = UnityStrInvalidFloatTrait;
+            break;
+    }
+
+    if (is_trait != should_be_trait)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrExpected);
+        if (!should_be_trait)
+        {
+            UnityPrint(UnityStrNot);
+        }
+        UnityPrint(trait_names[trait_index]);
+        UnityPrint(UnityStrWas);
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+        UnityPrintFloat(actual);
+#else
+        if (should_be_trait)
+        {
+            UnityPrint(UnityStrNot);
+        }
+        UnityPrint(trait_names[trait_index]);
+#endif
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+#endif /* not UNITY_EXCLUDE_DOUBLE */
+
+/*-----------------------------------------------*/
+void UnityAssertNumbersWithin(const UNITY_UINT delta,
+                              const UNITY_INT expected,
+                              const UNITY_INT actual,
+                              const char* msg,
+                              const UNITY_LINE_TYPE lineNumber,
+                              const UNITY_DISPLAY_STYLE_T style)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+    {
+        if (actual > expected)
+        {
+            Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta);
+        }
+        else
+        {
+            Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta);
+        }
+    }
+    else
+    {
+        if ((UNITY_UINT)actual > (UNITY_UINT)expected)
+        {
+            Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta);
+        }
+        else
+        {
+            Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta);
+        }
+    }
+
+    if (Unity.CurrentTestFailed)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrint(UnityStrDelta);
+        UnityPrintNumberByStyle((UNITY_INT)delta, style);
+        UnityPrint(UnityStrExpected);
+        UnityPrintNumberByStyle(expected, style);
+        UnityPrint(UnityStrWas);
+        UnityPrintNumberByStyle(actual, style);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertNumbersArrayWithin(const UNITY_UINT delta,
+                                   UNITY_INTERNAL_PTR expected,
+                                   UNITY_INTERNAL_PTR actual,
+                                   const UNITY_UINT32 num_elements,
+                                   const char* msg,
+                                   const UNITY_LINE_TYPE lineNumber,
+                                   const UNITY_DISPLAY_STYLE_T style,
+                                   const UNITY_FLAGS_T flags)
+{
+    UNITY_UINT32 elements = num_elements;
+    unsigned int length   = style & 0xF;
+    unsigned int increment = 0;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (num_elements == 0)
+    {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+        UnityPrintPointlessAndBail();
+#endif
+    }
+
+    if (expected == actual)
+    {
+        return; /* Both are NULL or same pointer */
+    }
+
+    if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
+    {
+        UNITY_FAIL_AND_BAIL;
+    }
+
+    while ((elements > 0) && (elements--))
+    {
+        UNITY_INT expect_val;
+        UNITY_INT actual_val;
+
+        switch (length)
+        {
+            case 1:
+                /* fixing problems with signed overflow on unsigned numbers */
+                if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+                {
+                    expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected;
+                    actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual;
+                    increment  = sizeof(UNITY_INT8);
+                }
+                else
+                {
+                    expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT8*)expected;
+                    actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT8*)actual;
+                    increment  = sizeof(UNITY_UINT8);
+                }
+                break;
+
+            case 2:
+                /* fixing problems with signed overflow on unsigned numbers */
+                if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+                {
+                    expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected;
+                    actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual;
+                    increment  = sizeof(UNITY_INT16);
+                }
+                else
+                {
+                    expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT16*)expected;
+                    actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT16*)actual;
+                    increment  = sizeof(UNITY_UINT16);
+                }
+                break;
+
+#ifdef UNITY_SUPPORT_64
+            case 8:
+                /* fixing problems with signed overflow on unsigned numbers */
+                if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+                {
+                    expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected;
+                    actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual;
+                    increment  = sizeof(UNITY_INT64);
+                }
+                else
+                {
+                    expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT64*)expected;
+                    actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT64*)actual;
+                    increment  = sizeof(UNITY_UINT64);
+                }
+                break;
+#endif
+
+            default: /* default is length 4 bytes */
+            case 4:
+                /* fixing problems with signed overflow on unsigned numbers */
+                if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+                {
+                    expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected;
+                    actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual;
+                    increment  = sizeof(UNITY_INT32);
+                }
+                else
+                {
+                    expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT32*)expected;
+                    actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT32*)actual;
+                    increment  = sizeof(UNITY_UINT32);
+                }
+                length = 4;
+                break;
+        }
+
+        if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+        {
+            if (actual_val > expect_val)
+            {
+                Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta);
+            }
+            else
+            {
+                Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta);
+            }
+        }
+        else
+        {
+            if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val)
+            {
+                Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta);
+            }
+            else
+            {
+                Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta);
+            }
+        }
+
+        if (Unity.CurrentTestFailed)
+        {
+            if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8)))
+            {   /* For UINT, remove sign extension (padding 1's) from signed type casts above */
+                UNITY_INT mask = 1;
+                mask = (mask << 8 * length) - 1;
+                expect_val &= mask;
+                actual_val &= mask;
+            }
+            UnityTestResultsFailBegin(lineNumber);
+            UnityPrint(UnityStrDelta);
+            UnityPrintNumberByStyle((UNITY_INT)delta, style);
+            UnityPrint(UnityStrElement);
+            UnityPrintNumberUnsigned(num_elements - elements - 1);
+            UnityPrint(UnityStrExpected);
+            UnityPrintNumberByStyle(expect_val, style);
+            UnityPrint(UnityStrWas);
+            UnityPrintNumberByStyle(actual_val, style);
+            UnityAddMsgIfSpecified(msg);
+            UNITY_FAIL_AND_BAIL;
+        }
+        /* Walk through array by incrementing the pointers */
+        if (flags == UNITY_ARRAY_TO_ARRAY)
+        {
+            expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment);
+        }
+        actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment);
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualString(const char* expected,
+                            const char* actual,
+                            const char* msg,
+                            const UNITY_LINE_TYPE lineNumber)
+{
+    UNITY_UINT32 i;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    /* if both pointers not null compare the strings */
+    if (expected && actual)
+    {
+        for (i = 0; expected[i] || actual[i]; i++)
+        {
+            if (expected[i] != actual[i])
+            {
+                Unity.CurrentTestFailed = 1;
+                break;
+            }
+        }
+    }
+    else
+    { /* handle case of one pointers being null (if both null, test should pass) */
+        if (expected != actual)
+        {
+            Unity.CurrentTestFailed = 1;
+        }
+    }
+
+    if (Unity.CurrentTestFailed)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrintExpectedAndActualStrings(expected, actual);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualStringLen(const char* expected,
+                               const char* actual,
+                               const UNITY_UINT32 length,
+                               const char* msg,
+                               const UNITY_LINE_TYPE lineNumber)
+{
+    UNITY_UINT32 i;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    /* if both pointers not null compare the strings */
+    if (expected && actual)
+    {
+        for (i = 0; (i < length) && (expected[i] || actual[i]); i++)
+        {
+            if (expected[i] != actual[i])
+            {
+                Unity.CurrentTestFailed = 1;
+                break;
+            }
+        }
+    }
+    else
+    { /* handle case of one pointers being null (if both null, test should pass) */
+        if (expected != actual)
+        {
+            Unity.CurrentTestFailed = 1;
+        }
+    }
+
+    if (Unity.CurrentTestFailed)
+    {
+        UnityTestResultsFailBegin(lineNumber);
+        UnityPrintExpectedAndActualStringsLen(expected, actual, length);
+        UnityAddMsgIfSpecified(msg);
+        UNITY_FAIL_AND_BAIL;
+    }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected,
+                                 const char** actual,
+                                 const UNITY_UINT32 num_elements,
+                                 const char* msg,
+                                 const UNITY_LINE_TYPE lineNumber,
+                                 const UNITY_FLAGS_T flags)
+{
+    UNITY_UINT32 i = 0;
+    UNITY_UINT32 j = 0;
+    const char* expd = NULL;
+    const char* act = NULL;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    /* if no elements, it's an error */
+    if (num_elements == 0)
+    {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+        UnityPrintPointlessAndBail();
+#endif
+    }
+
+    if ((const void*)expected == (const void*)actual)
+    {
+        return; /* Both are NULL or same pointer */
+    }
+
+    if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
+    {
+        UNITY_FAIL_AND_BAIL;
+    }
+
+    if (flags != UNITY_ARRAY_TO_ARRAY)
+    {
+        expd = (const char*)expected;
+    }
+
+    do
+    {
+        act = actual[j];
+        if (flags == UNITY_ARRAY_TO_ARRAY)
+        {
+            expd = ((const char* const*)expected)[j];
+        }
+
+        /* if both pointers not null compare the strings */
+        if (expd && act)
+        {
+            for (i = 0; expd[i] || act[i]; i++)
+            {
+                if (expd[i] != act[i])
+                {
+                    Unity.CurrentTestFailed = 1;
+                    break;
+                }
+            }
+        }
+        else
+        { /* handle case of one pointers being null (if both null, test should pass) */
+            if (expd != act)
+            {
+                Unity.CurrentTestFailed = 1;
+            }
+        }
+
+        if (Unity.CurrentTestFailed)
+        {
+            UnityTestResultsFailBegin(lineNumber);
+            if (num_elements > 1)
+            {
+                UnityPrint(UnityStrElement);
+                UnityPrintNumberUnsigned(j);
+            }
+            UnityPrintExpectedAndActualStrings(expd, act);
+            UnityAddMsgIfSpecified(msg);
+            UNITY_FAIL_AND_BAIL;
+        }
+    } while (++j < num_elements);
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected,
+                            UNITY_INTERNAL_PTR actual,
+                            const UNITY_UINT32 length,
+                            const UNITY_UINT32 num_elements,
+                            const char* msg,
+                            const UNITY_LINE_TYPE lineNumber,
+                            const UNITY_FLAGS_T flags)
+{
+    UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected;
+    UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual;
+    UNITY_UINT32 elements = num_elements;
+    UNITY_UINT32 bytes;
+
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    if (elements == 0)
+    {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+        UnityPrintPointlessAndBail();
+#endif
+    }
+    if (length == 0)
+    {
+        UnityPrintPointlessAndBail();
+    }
+
+    if (expected == actual)
+    {
+        return; /* Both are NULL or same pointer */
+    }
+
+    if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
+    {
+        UNITY_FAIL_AND_BAIL;
+    }
+
+    while (elements--)
+    {
+        bytes = length;
+        while (bytes--)
+        {
+            if (*ptr_exp != *ptr_act)
+            {
+                UnityTestResultsFailBegin(lineNumber);
+                UnityPrint(UnityStrMemory);
+                if (num_elements > 1)
+                {
+                    UnityPrint(UnityStrElement);
+                    UnityPrintNumberUnsigned(num_elements - elements - 1);
+                }
+                UnityPrint(UnityStrByte);
+                UnityPrintNumberUnsigned(length - bytes - 1);
+                UnityPrint(UnityStrExpected);
+                UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8);
+                UnityPrint(UnityStrWas);
+                UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8);
+                UnityAddMsgIfSpecified(msg);
+                UNITY_FAIL_AND_BAIL;
+            }
+            ptr_exp++;
+            ptr_act++;
+        }
+        if (flags == UNITY_ARRAY_TO_VAL)
+        {
+            ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected;
+        }
+    }
+}
+
+/*-----------------------------------------------*/
+
+static union
+{
+    UNITY_INT8 i8;
+    UNITY_INT16 i16;
+    UNITY_INT32 i32;
+#ifdef UNITY_SUPPORT_64
+    UNITY_INT64 i64;
+#endif
+#ifndef UNITY_EXCLUDE_FLOAT
+    float f;
+#endif
+#ifndef UNITY_EXCLUDE_DOUBLE
+    double d;
+#endif
+} UnityQuickCompare;
+
+UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size)
+{
+    switch(size)
+    {
+        case 1:
+            UnityQuickCompare.i8 = (UNITY_INT8)num;
+            return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8);
+
+        case 2:
+            UnityQuickCompare.i16 = (UNITY_INT16)num;
+            return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16);
+
+#ifdef UNITY_SUPPORT_64
+        case 8:
+            UnityQuickCompare.i64 = (UNITY_INT64)num;
+            return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64);
+#endif
+
+        default: /* 4 bytes */
+            UnityQuickCompare.i32 = (UNITY_INT32)num;
+            return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32);
+    }
+}
+
+#ifndef UNITY_EXCLUDE_FLOAT
+/*-----------------------------------------------*/
+UNITY_INTERNAL_PTR UnityFloatToPtr(const float num)
+{
+    UnityQuickCompare.f = num;
+    return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f);
+}
+#endif
+
+#ifndef UNITY_EXCLUDE_DOUBLE
+/*-----------------------------------------------*/
+UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num)
+{
+    UnityQuickCompare.d = num;
+    return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d);
+}
+#endif
+
+#ifdef UNITY_INCLUDE_PRINT_FORMATTED
+
+/*-----------------------------------------------
+ * printf length modifier helpers
+ *-----------------------------------------------*/
+
+enum UnityLengthModifier {
+    UNITY_LENGTH_MODIFIER_NONE,
+    UNITY_LENGTH_MODIFIER_LONG_LONG,
+    UNITY_LENGTH_MODIFIER_LONG,
+};
+
+#define UNITY_EXTRACT_ARG(NUMBER_T, NUMBER, LENGTH_MOD, VA, ARG_T) \
+do {                                                               \
+    switch (LENGTH_MOD)                                            \
+    {                                                              \
+        case UNITY_LENGTH_MODIFIER_LONG_LONG:                      \
+        {                                                          \
+            NUMBER = (NUMBER_T)va_arg(VA, long long ARG_T);        \
+            break;                                                 \
+        }                                                          \
+        case UNITY_LENGTH_MODIFIER_LONG:                           \
+        {                                                          \
+            NUMBER = (NUMBER_T)va_arg(VA, long ARG_T);             \
+            break;                                                 \
+        }                                                          \
+        case UNITY_LENGTH_MODIFIER_NONE:                           \
+        default:                                                   \
+        {                                                          \
+            NUMBER = (NUMBER_T)va_arg(VA, ARG_T);                  \
+            break;                                                 \
+        }                                                          \
+    }                                                              \
+} while (0)
+
+static enum UnityLengthModifier UnityLengthModifierGet(const char *pch, int *length)
+{
+    enum UnityLengthModifier length_mod;
+    switch (pch[0])
+    {
+        case 'l':
+            {
+                if (pch[1] == 'l')
+                {
+                    *length = 2;
+                    length_mod = UNITY_LENGTH_MODIFIER_LONG_LONG;
+                }
+                else
+                {
+                    *length = 1;
+                    length_mod = UNITY_LENGTH_MODIFIER_LONG;
+                }
+                break;
+            }
+        case 'h':
+            {
+                // short and char are converted to int
+                length_mod = UNITY_LENGTH_MODIFIER_NONE;
+                if (pch[1] == 'h')
+                {
+                    *length = 2;
+                }
+                else
+                {
+                    *length = 1;
+                }
+                break;
+            }
+        case 'j':
+        case 'z':
+        case 't':
+        case 'L':
+            {
+                // Not supported, but should gobble up the length specifier anyway
+                length_mod = UNITY_LENGTH_MODIFIER_NONE;
+                *length = 1;
+                break;
+            }
+        default:
+            {
+                length_mod = UNITY_LENGTH_MODIFIER_NONE;
+                *length = 0;
+            }
+    }
+    return length_mod;
+}
+
+/*-----------------------------------------------
+ * printf helper function
+ *-----------------------------------------------*/
+static void UnityPrintFVA(const char* format, va_list va)
+{
+    const char* pch = format;
+    if (pch != NULL)
+    {
+        while (*pch)
+        {
+            /* format identification character */
+            if (*pch == '%')
+            {
+                pch++;
+
+                if (pch != NULL)
+                {
+                    int length_mod_size;
+                    enum UnityLengthModifier length_mod = UnityLengthModifierGet(pch, &length_mod_size);
+                    pch += length_mod_size;
+
+                    switch (*pch)
+                    {
+                        case 'd':
+                        case 'i':
+                            {
+                                UNITY_INT number;
+                                UNITY_EXTRACT_ARG(UNITY_INT, number, length_mod, va, int);
+                                UnityPrintNumber((UNITY_INT)number);
+                                break;
+                            }
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+                        case 'f':
+                        case 'g':
+                            {
+                                const double number = va_arg(va, double);
+                                UnityPrintFloat((UNITY_DOUBLE)number);
+                                break;
+                            }
+#endif
+                        case 'u':
+                            {
+                                UNITY_UINT number;
+                                UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
+                                UnityPrintNumberUnsigned(number);
+                                break;
+                            }
+                        case 'b':
+                            {
+                                UNITY_UINT number;
+                                UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
+                                const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1;
+                                UNITY_OUTPUT_CHAR('0');
+                                UNITY_OUTPUT_CHAR('b');
+                                UnityPrintMask(mask, number);
+                                break;
+                            }
+                        case 'x':
+                        case 'X':
+                            {
+                                UNITY_UINT number;
+                                UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
+                                UNITY_OUTPUT_CHAR('0');
+                                UNITY_OUTPUT_CHAR('x');
+                                UnityPrintNumberHex(number, 8);
+                                break;
+                            }
+                        case 'p':
+                            {
+                                const unsigned int number = va_arg(va, unsigned int);
+                                UNITY_OUTPUT_CHAR('0');
+                                UNITY_OUTPUT_CHAR('x');
+                                UnityPrintNumberHex((UNITY_UINT)number, 8);
+                                break;
+                            }
+                        case 'c':
+                            {
+                                const int ch = va_arg(va, int);
+                                UnityPrintChar((const char *)&ch);
+                                break;
+                            }
+                        case 's':
+                            {
+                                const char * string = va_arg(va, const char *);
+                                UnityPrint(string);
+                                break;
+                            }
+                        case '%':
+                            {
+                                UnityPrintChar(pch);
+                                break;
+                            }
+                        default:
+                            {
+                                /* print the unknown format character */
+                                UNITY_OUTPUT_CHAR('%');
+                                UnityPrintChar(pch);
+                                break;
+                            }
+                    }
+                }
+            }
+#ifdef UNITY_OUTPUT_COLOR
+            /* print ANSI escape code */
+            else if ((*pch == 27) && (*(pch + 1) == '['))
+            {
+                pch += UnityPrintAnsiEscapeString(pch);
+                continue;
+            }
+#endif
+            else if (*pch == '\n')
+            {
+                UNITY_PRINT_EOL();
+            }
+            else
+            {
+                UnityPrintChar(pch);
+            }
+
+            pch++;
+        }
+    }
+}
+
+void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...)
+{
+    UnityTestResultsBegin(Unity.TestFile, line);
+    UnityPrint("INFO");
+    if(format != NULL)
+    {
+        UnityPrint(": ");
+        va_list va;
+        va_start(va, format);
+        UnityPrintFVA(format, va);
+        va_end(va);
+    }
+    UNITY_PRINT_EOL();
+}
+#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */
+
+
+/*-----------------------------------------------
+ * Control Functions
+ *-----------------------------------------------*/
+
+/*-----------------------------------------------*/
+void UnityFail(const char* msg, const UNITY_LINE_TYPE line)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    UnityTestResultsBegin(Unity.TestFile, line);
+    UnityPrint(UnityStrFail);
+    if (msg != NULL)
+    {
+        UNITY_OUTPUT_CHAR(':');
+
+#ifdef UNITY_PRINT_TEST_CONTEXT
+        UNITY_PRINT_TEST_CONTEXT();
+#endif
+#ifndef UNITY_EXCLUDE_DETAILS
+        if (Unity.CurrentDetail1)
+        {
+            UnityPrint(UnityStrDetail1Name);
+            UnityPrint(Unity.CurrentDetail1);
+            if (Unity.CurrentDetail2)
+            {
+                UnityPrint(UnityStrDetail2Name);
+                UnityPrint(Unity.CurrentDetail2);
+            }
+            UnityPrint(UnityStrSpacer);
+        }
+#endif
+        if (msg[0] != ' ')
+        {
+            UNITY_OUTPUT_CHAR(' ');
+        }
+        UnityPrint(msg);
+    }
+
+    UNITY_FAIL_AND_BAIL;
+}
+
+/*-----------------------------------------------*/
+void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line)
+{
+    RETURN_IF_FAIL_OR_IGNORE;
+
+    UnityTestResultsBegin(Unity.TestFile, line);
+    UnityPrint(UnityStrIgnore);
+    if (msg != NULL)
+    {
+        UNITY_OUTPUT_CHAR(':');
+        UNITY_OUTPUT_CHAR(' ');
+        UnityPrint(msg);
+    }
+    UNITY_IGNORE_AND_BAIL;
+}
+
+/*-----------------------------------------------*/
+void UnityMessage(const char* msg, const UNITY_LINE_TYPE line)
+{
+    UnityTestResultsBegin(Unity.TestFile, line);
+    UnityPrint("INFO");
+    if (msg != NULL)
+    {
+      UNITY_OUTPUT_CHAR(':');
+      UNITY_OUTPUT_CHAR(' ');
+      UnityPrint(msg);
+    }
+    UNITY_PRINT_EOL();
+}
+
+/*-----------------------------------------------*/
+/* If we have not defined our own test runner, then include our default test runner to make life easier */
+#ifndef UNITY_SKIP_DEFAULT_RUNNER
+void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum)
+{
+    Unity.CurrentTestName = FuncName;
+    Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum;
+    Unity.NumberOfTests++;
+    UNITY_CLR_DETAILS();
+    UNITY_EXEC_TIME_START();
+    if (TEST_PROTECT())
+    {
+        setUp();
+        Func();
+    }
+    if (TEST_PROTECT())
+    {
+        tearDown();
+    }
+    UNITY_EXEC_TIME_STOP();
+    UnityConcludeTest();
+}
+#endif
+
+/*-----------------------------------------------*/
+void UnitySetTestFile(const char* filename)
+{
+    Unity.TestFile = filename;
+}
+
+/*-----------------------------------------------*/
+void UnityBegin(const char* filename)
+{
+    Unity.TestFile = filename;
+    Unity.CurrentTestName = NULL;
+    Unity.CurrentTestLineNumber = 0;
+    Unity.NumberOfTests = 0;
+    Unity.TestFailures = 0;
+    Unity.TestIgnores = 0;
+    Unity.CurrentTestFailed = 0;
+    Unity.CurrentTestIgnored = 0;
+
+    UNITY_CLR_DETAILS();
+    UNITY_OUTPUT_START();
+}
+
+/*-----------------------------------------------*/
+int UnityEnd(void)
+{
+    UNITY_PRINT_EOL();
+    UnityPrint(UnityStrBreaker);
+    UNITY_PRINT_EOL();
+    UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests));
+    UnityPrint(UnityStrResultsTests);
+    UnityPrintNumber((UNITY_INT)(Unity.TestFailures));
+    UnityPrint(UnityStrResultsFailures);
+    UnityPrintNumber((UNITY_INT)(Unity.TestIgnores));
+    UnityPrint(UnityStrResultsIgnored);
+    UNITY_PRINT_EOL();
+    if (Unity.TestFailures == 0U)
+    {
+        UnityPrint(UnityStrOk);
+    }
+    else
+    {
+        UnityPrint(UnityStrFail);
+#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL
+        UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D');
+#endif
+    }
+    UNITY_PRINT_EOL();
+    UNITY_FLUSH_CALL();
+    UNITY_OUTPUT_COMPLETE();
+    return (int)(Unity.TestFailures);
+}
+
+/*-----------------------------------------------
+ * Command Line Argument Support
+ *-----------------------------------------------*/
+#ifdef UNITY_USE_COMMAND_LINE_ARGS
+
+char* UnityOptionIncludeNamed = NULL;
+char* UnityOptionExcludeNamed = NULL;
+int UnityVerbosity            = 1;
+
+/*-----------------------------------------------*/
+int UnityParseOptions(int argc, char** argv)
+{
+    int i;
+    UnityOptionIncludeNamed = NULL;
+    UnityOptionExcludeNamed = NULL;
+
+    for (i = 1; i < argc; i++)
+    {
+        if (argv[i][0] == '-')
+        {
+            switch (argv[i][1])
+            {
+                case 'l': /* list tests */
+                    return -1;
+                case 'n': /* include tests with name including this string */
+                case 'f': /* an alias for -n */
+                    if (argv[i][2] == '=')
+                    {
+                        UnityOptionIncludeNamed = &argv[i][3];
+                    }
+                    else if (++i < argc)
+                    {
+                        UnityOptionIncludeNamed = argv[i];
+                    }
+                    else
+                    {
+                        UnityPrint("ERROR: No Test String to Include Matches For");
+                        UNITY_PRINT_EOL();
+                        return 1;
+                    }
+                    break;
+                case 'q': /* quiet */
+                    UnityVerbosity = 0;
+                    break;
+                case 'v': /* verbose */
+                    UnityVerbosity = 2;
+                    break;
+                case 'x': /* exclude tests with name including this string */
+                    if (argv[i][2] == '=')
+                    {
+                        UnityOptionExcludeNamed = &argv[i][3];
+                    }
+                    else if (++i < argc)
+                    {
+                        UnityOptionExcludeNamed = argv[i];
+                    }
+                    else
+                    {
+                        UnityPrint("ERROR: No Test String to Exclude Matches For");
+                        UNITY_PRINT_EOL();
+                        return 1;
+                    }
+                    break;
+                default:
+                    UnityPrint("ERROR: Unknown Option ");
+                    UNITY_OUTPUT_CHAR(argv[i][1]);
+                    UNITY_PRINT_EOL();
+                    return 1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*-----------------------------------------------*/
+int IsStringInBiggerString(const char* longstring, const char* shortstring)
+{
+    const char* lptr = longstring;
+    const char* sptr = shortstring;
+    const char* lnext = lptr;
+
+    if (*sptr == '*')
+    {
+        return 1;
+    }
+
+    while (*lptr)
+    {
+        lnext = lptr + 1;
+
+        /* If they current bytes match, go on to the next bytes */
+        while (*lptr && *sptr && (*lptr == *sptr))
+        {
+            lptr++;
+            sptr++;
+
+            /* We're done if we match the entire string or up to a wildcard */
+            if (*sptr == '*')
+                return 1;
+            if (*sptr == ',')
+                return 1;
+            if (*sptr == '"')
+                return 1;
+            if (*sptr == '\'')
+                return 1;
+            if (*sptr == ':')
+                return 2;
+            if (*sptr == 0)
+                return 1;
+        }
+
+        /* Otherwise we start in the long pointer 1 character further and try again */
+        lptr = lnext;
+        sptr = shortstring;
+    }
+
+    return 0;
+}
+
+/*-----------------------------------------------*/
+int UnityStringArgumentMatches(const char* str)
+{
+    int retval;
+    const char* ptr1;
+    const char* ptr2;
+    const char* ptrf;
+
+    /* Go through the options and get the substrings for matching one at a time */
+    ptr1 = str;
+    while (ptr1[0] != 0)
+    {
+        if ((ptr1[0] == '"') || (ptr1[0] == '\''))
+        {
+            ptr1++;
+        }
+
+        /* look for the start of the next partial */
+        ptr2 = ptr1;
+        ptrf = 0;
+        do
+        {
+            ptr2++;
+            if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ','))
+            {
+                ptrf = &ptr2[1];
+            }
+        } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ','));
+
+        while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ',')))
+        {
+            ptr2++;
+        }
+
+        /* done if complete filename match */
+        retval = IsStringInBiggerString(Unity.TestFile, ptr1);
+        if (retval == 1)
+        {
+            return retval;
+        }
+
+        /* done if testname match after filename partial match */
+        if ((retval == 2) && (ptrf != 0))
+        {
+            if (IsStringInBiggerString(Unity.CurrentTestName, ptrf))
+            {
+                return 1;
+            }
+        }
+
+        /* done if complete testname match */
+        if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1)
+        {
+            return 1;
+        }
+
+        ptr1 = ptr2;
+    }
+
+    /* we couldn't find a match for any substrings */
+    return 0;
+}
+
+/*-----------------------------------------------*/
+int UnityTestMatches(void)
+{
+    /* Check if this test name matches the included test pattern */
+    int retval;
+    if (UnityOptionIncludeNamed)
+    {
+        retval = UnityStringArgumentMatches(UnityOptionIncludeNamed);
+    }
+    else
+    {
+        retval = 1;
+    }
+
+    /* Check if this test name matches the excluded test pattern */
+    if (UnityOptionExcludeNamed)
+    {
+        if (UnityStringArgumentMatches(UnityOptionExcludeNamed))
+        {
+            retval = 0;
+        }
+    }
+
+    return retval;
+}
+
+#endif /* UNITY_USE_COMMAND_LINE_ARGS */
+/*-----------------------------------------------*/
diff --git a/lib/chirpy_tx/test/unity.h b/lib/chirpy_tx/test/unity.h
new file mode 100644 (file)
index 0000000..e321a1d
--- /dev/null
@@ -0,0 +1,687 @@
+/* ==========================================
+    Unity Project - A Test Framework for C
+    Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
+    [Released under MIT License. Please refer to license.txt for details]
+========================================== */
+
+#ifndef UNITY_FRAMEWORK_H
+#define UNITY_FRAMEWORK_H
+#define UNITY
+
+#define UNITY_VERSION_MAJOR    2
+#define UNITY_VERSION_MINOR    5
+#define UNITY_VERSION_BUILD    4
+#define UNITY_VERSION          ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "unity_internals.h"
+
+/*-------------------------------------------------------
+ * Test Setup / Teardown
+ *-------------------------------------------------------*/
+
+/* These functions are intended to be called before and after each test.
+ * If using unity directly, these will need to be provided for each test
+ * executable built. If you are using the test runner generator and/or
+ * Ceedling, these are optional. */
+void setUp(void);
+void tearDown(void);
+
+/* These functions are intended to be called at the beginning and end of an
+ * entire test suite.  suiteTearDown() is passed the number of tests that
+ * failed, and its return value becomes the exit code of main(). If using
+ * Unity directly, you're in charge of calling these if they are desired.
+ * If using Ceedling or the test runner generator, these will be called
+ * automatically if they exist. */
+void suiteSetUp(void);
+int suiteTearDown(int num_failures);
+
+/*-------------------------------------------------------
+ * Test Reset and Verify
+ *-------------------------------------------------------*/
+
+/* These functions are intended to be called before during tests in order
+ * to support complex test loops, etc. Both are NOT built into Unity. Instead
+ * the test runner generator will create them. resetTest will run teardown and
+ * setup again, verifying any end-of-test needs between. verifyTest will only
+ * run the verification. */
+void resetTest(void);
+void verifyTest(void);
+
+/*-------------------------------------------------------
+ * Configuration Options
+ *-------------------------------------------------------
+ * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above.
+
+ * Integers/longs/pointers
+ *     - Unity attempts to automatically discover your integer sizes
+ *       - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in <stdint.h>
+ *       - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in <limits.h>
+ *     - If you cannot use the automatic methods above, you can force Unity by using these options:
+ *       - define UNITY_SUPPORT_64
+ *       - set UNITY_INT_WIDTH
+ *       - set UNITY_LONG_WIDTH
+ *       - set UNITY_POINTER_WIDTH
+
+ * Floats
+ *     - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons
+ *     - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT
+ *     - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats
+ *     - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons
+ *     - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default)
+ *     - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE
+ *     - define UNITY_DOUBLE_TYPE to specify something other than double
+ *     - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors
+
+ * Output
+ *     - by default, Unity prints to standard out with putchar.  define UNITY_OUTPUT_CHAR(a) with a different function if desired
+ *     - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure
+
+ * Optimization
+ *     - by default, line numbers are stored in unsigned shorts.  Define UNITY_LINE_TYPE with a different type if your files are huge
+ *     - by default, test and failure counters are unsigned shorts.  Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests.
+
+ * Test Cases
+ *     - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script
+
+ * Parameterized Tests
+ *     - you'll want to create a define of TEST_CASE(...) and/or TEST_RANGE(...) which basically evaluates to nothing
+
+ * Tests with Arguments
+ *     - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity
+
+ *-------------------------------------------------------
+ * Basic Fail and Ignore
+ *-------------------------------------------------------*/
+
+#define TEST_FAIL_MESSAGE(message)                                                                 UNITY_TEST_FAIL(__LINE__, (message))
+#define TEST_FAIL()                                                                                UNITY_TEST_FAIL(__LINE__, NULL)
+#define TEST_IGNORE_MESSAGE(message)                                                               UNITY_TEST_IGNORE(__LINE__, (message))
+#define TEST_IGNORE()                                                                              UNITY_TEST_IGNORE(__LINE__, NULL)
+#define TEST_MESSAGE(message)                                                                      UnityMessage((message), __LINE__)
+#define TEST_ONLY()
+#ifdef UNITY_INCLUDE_PRINT_FORMATTED
+#define TEST_PRINTF(message, ...)                                                                  UnityPrintF(__LINE__, (message), __VA_ARGS__)
+#endif
+
+/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails.
+ * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */
+#define TEST_PASS()                                                                                TEST_ABORT()
+#define TEST_PASS_MESSAGE(message)                                                                 do { UnityMessage((message), __LINE__); TEST_ABORT(); } while (0)
+
+/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out
+ * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */
+#define TEST_FILE(a)
+
+/*-------------------------------------------------------
+ * Test Asserts (simple)
+ *-------------------------------------------------------*/
+
+/* Boolean */
+#define TEST_ASSERT(condition)                                                                     UNITY_TEST_ASSERT(       (condition), __LINE__, " Expression Evaluated To FALSE")
+#define TEST_ASSERT_TRUE(condition)                                                                UNITY_TEST_ASSERT(       (condition), __LINE__, " Expected TRUE Was FALSE")
+#define TEST_ASSERT_UNLESS(condition)                                                              UNITY_TEST_ASSERT(      !(condition), __LINE__, " Expression Evaluated To TRUE")
+#define TEST_ASSERT_FALSE(condition)                                                               UNITY_TEST_ASSERT(      !(condition), __LINE__, " Expected FALSE Was TRUE")
+#define TEST_ASSERT_NULL(pointer)                                                                  UNITY_TEST_ASSERT_NULL(    (pointer), __LINE__, " Expected NULL")
+#define TEST_ASSERT_NOT_NULL(pointer)                                                              UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL")
+#define TEST_ASSERT_EMPTY(pointer)                                                                 UNITY_TEST_ASSERT_EMPTY(    (pointer), __LINE__, " Expected Empty")
+#define TEST_ASSERT_NOT_EMPTY(pointer)                                                             UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty")
+
+/* Integers (of all sizes) */
+#define TEST_ASSERT_EQUAL_INT(expected, actual)                                                    UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT8(expected, actual)                                                   UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT16(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT32(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT64(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT(expected, actual)                                                   UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT8(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT16(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT32(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT64(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_size_t(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_UINT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX(expected, actual)                                                    UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX8(expected, actual)                                                   UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX16(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX32(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX64(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_CHAR(expected, actual)                                                   UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BITS(mask, expected, actual)                                                   UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BITS_HIGH(mask, actual)                                                        UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BITS_LOW(mask, actual)                                                         UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BIT_HIGH(bit, actual)                                                          UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BIT_LOW(bit, actual)                                                           UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL)
+
+/* Integer Not Equal To (of all sizes) */
+#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual)                                               UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual)                                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual)                                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual)                                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual)                                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
+
+/* Integer Greater Than/ Less Than (of all sizes) */
+#define TEST_ASSERT_GREATER_THAN(threshold, actual)                                                UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual)                                            UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual)                                           UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual)                                           UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_size_t(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual)                                           UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual)                                           UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, NULL)
+
+#define TEST_ASSERT_LESS_THAN(threshold, actual)                                                   UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT(threshold, actual)                                               UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual)                                              UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual)                                              UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual)                                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual)                                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual)                                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_size_t(threshold, actual)                                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual)                                              UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_CHAR(threshold, actual)                                              UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, NULL)
+
+#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual)                                            UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual)                                        UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual)                                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual)                                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_size_t(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual)                                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual)                                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
+
+#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual)                                               UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual)                                           UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual)                                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual)                                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual)                                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual)                                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual)                                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_size_t(threshold, actual)                                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual)                                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_CHAR(threshold, actual)                                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
+
+/* Integer Ranges (of all sizes) */
+#define TEST_ASSERT_INT_WITHIN(delta, expected, actual)                                            UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual)                                           UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual)                                           UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_size_t_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual)                                            UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual)                                           UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_CHAR_WITHIN(delta, expected, actual)                                           UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+
+/* Integer Array Ranges (of all sizes) */
+#define TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements)                        UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements)                       UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements)                       UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_size_t_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX_ARRAY_WITHIN(delta, expected, actual, num_elements)                        UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements)                       UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements)                       UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+
+
+/* Structs and Strings */
+#define TEST_ASSERT_EQUAL_PTR(expected, actual)                                                    UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_STRING(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len)                                        UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)                                            UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL)
+
+/* Arrays */
+#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_size_t_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements)                        UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+
+/* Arrays Compared To Single Value */
+#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements)                                 UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_size_t(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements)                                 UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements)                                 UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements)                         UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, NULL)
+
+/* Floating Point (If Enabled) */
+#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual)                                      UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_FLOAT(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual)                                             UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual)                                         UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_INF(actual)                                                           UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual)                                                       UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NAN(actual)                                                           UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual)                                                   UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual)                                                       UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual)                                                   UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual)                                                       UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual)                                               UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL)
+
+/* Double (If Enabled) */
+#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual)                                     UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual)                                            UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual)                                        UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_INF(actual)                                                          UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual)                                                      UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NAN(actual)                                                          UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual)                                                  UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual)                                                      UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual)                                                  UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual)                                                      UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual)                                              UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL)
+
+/* Shorthand */
+#ifdef UNITY_SHORTHAND_AS_OLD
+#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal")
+#endif
+#ifdef UNITY_SHORTHAND_AS_INT
+#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+#ifdef UNITY_SHORTHAND_AS_MEM
+#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+#ifdef UNITY_SHORTHAND_AS_RAW
+#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, " Expected Equal")
+#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal")
+#endif
+#ifdef UNITY_SHORTHAND_AS_NONE
+#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+
+/*-------------------------------------------------------
+ * Test Asserts (with additional messages)
+ *-------------------------------------------------------*/
+
+/* Boolean */
+#define TEST_ASSERT_MESSAGE(condition, message)                                                    UNITY_TEST_ASSERT(       (condition), __LINE__, (message))
+#define TEST_ASSERT_TRUE_MESSAGE(condition, message)                                               UNITY_TEST_ASSERT(       (condition), __LINE__, (message))
+#define TEST_ASSERT_UNLESS_MESSAGE(condition, message)                                             UNITY_TEST_ASSERT(      !(condition), __LINE__, (message))
+#define TEST_ASSERT_FALSE_MESSAGE(condition, message)                                              UNITY_TEST_ASSERT(      !(condition), __LINE__, (message))
+#define TEST_ASSERT_NULL_MESSAGE(pointer, message)                                                 UNITY_TEST_ASSERT_NULL(    (pointer), __LINE__, (message))
+#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message)                                             UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message))
+#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message)                                                UNITY_TEST_ASSERT_EMPTY(    (pointer), __LINE__, (message))
+#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message)                                            UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message))
+
+/* Integers (of all sizes) */
+#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message)                                  UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message)                                  UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_size_t_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message)                                  UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message)                                  UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message)                                       UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message))
+#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message)                                        UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message))
+#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message)                                         UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message))
+#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message)                                          UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message)                                  UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message))
+
+/* Integer Not Equal To (of all sizes) */
+#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message)                              UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
+
+
+/* Integer Greater Than/ Less Than (of all sizes) */
+#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message)                               UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_size_t_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_CHAR_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, (message))
+
+#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message)                                  UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message)                              UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_size_t_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_CHAR_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, (message))
+
+#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message)                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message)                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message)                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message)                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
+
+#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message)                              UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_size_t_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
+
+/* Integer Ranges (of all sizes) */
+#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message)                           UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message)                          UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message)                          UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_size_t_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message)                           UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message)                          UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_CHAR_WITHIN_MESSAGE(delta, expected, actual, message)                          UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, (message))
+
+/* Integer Array Ranges (of all sizes) */
+#define TEST_ASSERT_INT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)       UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_INT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)      UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_INT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_INT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_INT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)      UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_size_t_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)       UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)      UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_CHAR_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)      UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+
+
+/* Structs and Strings */
+#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message)                       UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message)                           UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message))
+
+/* Arrays */
+#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_size_t_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message)       UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_CHAR_ARRAY_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+
+/* Arrays Compared To Single Value*/
+#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message)                UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_size_t_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message)                UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message)                UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message)        UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_CHAR_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, (message))
+
+/* Floating Point (If Enabled) */
+#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_FLOAT_MESSAGE(expected, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_FLOAT_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_FLOAT_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message)                                          UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message)                                      UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message)                                          UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message)                                  UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message)                                      UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message)                                  UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message)                                      UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message)                              UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message))
+
+/* Double (If Enabled) */
+#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_DOUBLE_MESSAGE(expected, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_DOUBLE_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_DOUBLE_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message)                                         UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message)                                     UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message)                                         UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message)                                 UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message)                                     UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message)                                 UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message)                                     UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message)                             UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message))
+
+/* Shorthand */
+#ifdef UNITY_SHORTHAND_AS_OLD
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message))
+#endif
+#ifdef UNITY_SHORTHAND_AS_INT
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, message)
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+#ifdef  UNITY_SHORTHAND_AS_MEM
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, message)
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+#ifdef  UNITY_SHORTHAND_AS_RAW
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, message)
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, message)
+#endif
+#ifdef UNITY_SHORTHAND_AS_NONE
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+
+/* end of UNITY_FRAMEWORK_H */
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/chirpy_tx/test/unity_internals.h b/lib/chirpy_tx/test/unity_internals.h
new file mode 100644 (file)
index 0000000..47bd370
--- /dev/null
@@ -0,0 +1,1145 @@
+/* ==========================================
+    Unity Project - A Test Framework for C
+    Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
+    [Released under MIT License. Please refer to license.txt for details]
+========================================== */
+
+#ifndef UNITY_INTERNALS_H
+#define UNITY_INTERNALS_H
+
+#ifdef UNITY_INCLUDE_CONFIG_H
+#include "unity_config.h"
+#endif
+
+#ifndef UNITY_EXCLUDE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifndef UNITY_EXCLUDE_MATH_H
+#include <math.h>
+#endif
+
+#ifndef UNITY_EXCLUDE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef UNITY_INCLUDE_PRINT_FORMATTED
+#include <stdarg.h>
+#endif
+
+/* Unity Attempts to Auto-Detect Integer Types
+ * Attempt 1: UINT_MAX, ULONG_MAX in <limits.h>, or default to 32 bits
+ * Attempt 2: UINTPTR_MAX in <stdint.h>, or default to same size as long
+ * The user may override any of these derived constants:
+ * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */
+#ifndef UNITY_EXCLUDE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifndef UNITY_EXCLUDE_LIMITS_H
+#include <limits.h>
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+  #define UNITY_FUNCTION_ATTR(a)    __attribute__((a))
+#else
+  #define UNITY_FUNCTION_ATTR(a)    /* ignore */
+#endif
+
+#ifndef UNITY_NORETURN
+  #if defined(__cplusplus)
+    #if __cplusplus >= 201103L
+      #define UNITY_NORETURN [[ noreturn ]]
+    #endif
+  #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+    #if defined(_WIN32) && defined(_MSC_VER)
+      /* We are using MSVC compiler on Windows platform. */
+      /* Not all Windows SDKs supports <stdnoreturn.h>, but compiler can support C11: */
+      /* https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/ */
+      /* Not sure, that Mingw compilers has Windows SDK headers at all. */
+      #include <sdkddkver.h>
+    #endif
+
+    /* Using Windows SDK predefined macro for detecting supported SDK with MSVC compiler. */
+    /* Mingw GCC should work without that fixes. */
+    /* Based on: */
+    /* https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170 */
+    /* NTDDI_WIN10_FE is equal to Windows 10 SDK 2104 */
+    #if defined(_MSC_VER) && ((!defined(NTDDI_WIN10_FE)) || WDK_NTDDI_VERSION < NTDDI_WIN10_FE)
+      /* Based on tests and: */
+      /* https://docs.microsoft.com/en-us/cpp/c-language/noreturn?view=msvc-170 */
+      /* https://en.cppreference.com/w/c/language/_Noreturn */
+      #define UNITY_NORETURN _Noreturn
+    #else /* Using newer Windows SDK or not MSVC compiler */
+      #include <stdnoreturn.h>
+      #define UNITY_NORETURN noreturn
+    #endif
+  #endif
+#endif
+#ifndef UNITY_NORETURN
+  #define UNITY_NORETURN UNITY_FUNCTION_ATTR(noreturn)
+#endif
+
+/*-------------------------------------------------------
+ * Guess Widths If Not Specified
+ *-------------------------------------------------------*/
+
+/* Determine the size of an int, if not already specified.
+ * We cannot use sizeof(int), because it is not yet defined
+ * at this stage in the translation of the C program.
+ * Also sizeof(int) does return the size in addressable units on all platforms,
+ * which may not necessarily be the size in bytes.
+ * Therefore, infer it from UINT_MAX if possible. */
+#ifndef UNITY_INT_WIDTH
+  #ifdef UINT_MAX
+    #if (UINT_MAX == 0xFFFF)
+      #define UNITY_INT_WIDTH (16)
+    #elif (UINT_MAX == 0xFFFFFFFF)
+      #define UNITY_INT_WIDTH (32)
+    #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF)
+      #define UNITY_INT_WIDTH (64)
+    #endif
+  #else /* Set to default */
+    #define UNITY_INT_WIDTH (32)
+  #endif /* UINT_MAX */
+#endif
+
+/* Determine the size of a long, if not already specified. */
+#ifndef UNITY_LONG_WIDTH
+  #ifdef ULONG_MAX
+    #if (ULONG_MAX == 0xFFFF)
+      #define UNITY_LONG_WIDTH (16)
+    #elif (ULONG_MAX == 0xFFFFFFFF)
+      #define UNITY_LONG_WIDTH (32)
+    #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF)
+      #define UNITY_LONG_WIDTH (64)
+    #endif
+  #else /* Set to default */
+    #define UNITY_LONG_WIDTH (32)
+  #endif /* ULONG_MAX */
+#endif
+
+/* Determine the size of a pointer, if not already specified. */
+#ifndef UNITY_POINTER_WIDTH
+  #ifdef UINTPTR_MAX
+    #if (UINTPTR_MAX <= 0xFFFF)
+      #define UNITY_POINTER_WIDTH (16)
+    #elif (UINTPTR_MAX <= 0xFFFFFFFF)
+      #define UNITY_POINTER_WIDTH (32)
+    #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF)
+      #define UNITY_POINTER_WIDTH (64)
+    #endif
+  #else /* Set to default */
+    #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH
+  #endif /* UINTPTR_MAX */
+#endif
+
+/*-------------------------------------------------------
+ * Int Support (Define types based on detected sizes)
+ *-------------------------------------------------------*/
+
+#if (UNITY_INT_WIDTH == 32)
+    typedef unsigned char   UNITY_UINT8;
+    typedef unsigned short  UNITY_UINT16;
+    typedef unsigned int    UNITY_UINT32;
+    typedef signed char     UNITY_INT8;
+    typedef signed short    UNITY_INT16;
+    typedef signed int      UNITY_INT32;
+#elif (UNITY_INT_WIDTH == 16)
+    typedef unsigned char   UNITY_UINT8;
+    typedef unsigned int    UNITY_UINT16;
+    typedef unsigned long   UNITY_UINT32;
+    typedef signed char     UNITY_INT8;
+    typedef signed int      UNITY_INT16;
+    typedef signed long     UNITY_INT32;
+#else
+    #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported)
+#endif
+
+/*-------------------------------------------------------
+ * 64-bit Support
+ *-------------------------------------------------------*/
+
+/* Auto-detect 64 Bit Support */
+#ifndef UNITY_SUPPORT_64
+  #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64
+    #define UNITY_SUPPORT_64
+  #endif
+#endif
+
+/* 64-Bit Support Dependent Configuration */
+#ifndef UNITY_SUPPORT_64
+    /* No 64-bit Support */
+    typedef UNITY_UINT32 UNITY_UINT;
+    typedef UNITY_INT32  UNITY_INT;
+    #define UNITY_MAX_NIBBLES (8)  /* Maximum number of nibbles in a UNITY_(U)INT */
+#else
+  /* 64-bit Support */
+  #if (UNITY_LONG_WIDTH == 32)
+    typedef unsigned long long UNITY_UINT64;
+    typedef signed long long   UNITY_INT64;
+  #elif (UNITY_LONG_WIDTH == 64)
+    typedef unsigned long      UNITY_UINT64;
+    typedef signed long        UNITY_INT64;
+  #else
+    #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported)
+  #endif
+    typedef UNITY_UINT64 UNITY_UINT;
+    typedef UNITY_INT64  UNITY_INT;
+    #define UNITY_MAX_NIBBLES (16) /* Maximum number of nibbles in a UNITY_(U)INT */
+#endif
+
+/*-------------------------------------------------------
+ * Pointer Support
+ *-------------------------------------------------------*/
+
+#if (UNITY_POINTER_WIDTH == 32)
+  #define UNITY_PTR_TO_INT UNITY_INT32
+  #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32
+#elif (UNITY_POINTER_WIDTH == 64)
+  #define UNITY_PTR_TO_INT UNITY_INT64
+  #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64
+#elif (UNITY_POINTER_WIDTH == 16)
+  #define UNITY_PTR_TO_INT UNITY_INT16
+  #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16
+#else
+  #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported)
+#endif
+
+#ifndef UNITY_PTR_ATTRIBUTE
+  #define UNITY_PTR_ATTRIBUTE
+#endif
+
+#ifndef UNITY_INTERNAL_PTR
+  #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void*
+#endif
+
+/* optionally define UNITY_COMPARE_PTRS_ON_ZERO_ARRAY */
+
+/*-------------------------------------------------------
+ * Float Support
+ *-------------------------------------------------------*/
+
+#ifdef UNITY_EXCLUDE_FLOAT
+
+/* No Floating Point Support */
+#ifndef UNITY_EXCLUDE_DOUBLE
+#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */
+#endif
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+#define UNITY_EXCLUDE_FLOAT_PRINT
+#endif
+
+#else
+
+/* Floating Point Support */
+#ifndef UNITY_FLOAT_PRECISION
+#define UNITY_FLOAT_PRECISION (0.00001f)
+#endif
+#ifndef UNITY_FLOAT_TYPE
+#define UNITY_FLOAT_TYPE float
+#endif
+typedef UNITY_FLOAT_TYPE UNITY_FLOAT;
+
+/* isinf & isnan macros should be provided by math.h */
+#ifndef isinf
+/* The value of Inf - Inf is NaN */
+#define isinf(n) (isnan((n) - (n)) && !isnan(n))
+#endif
+
+#ifndef isnan
+/* NaN is the only floating point value that does NOT equal itself.
+ * Therefore if n != n, then it is NaN. */
+#define isnan(n) ((n != n) ? 1 : 0)
+#endif
+
+#endif
+
+/*-------------------------------------------------------
+ * Double Float Support
+ *-------------------------------------------------------*/
+
+/* unlike float, we DON'T include by default */
+#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE)
+
+  /* No Floating Point Support */
+  #ifndef UNITY_EXCLUDE_DOUBLE
+  #define UNITY_EXCLUDE_DOUBLE
+  #else
+    #undef UNITY_INCLUDE_DOUBLE
+  #endif
+
+  #ifndef UNITY_EXCLUDE_FLOAT
+    #ifndef UNITY_DOUBLE_TYPE
+    #define UNITY_DOUBLE_TYPE double
+    #endif
+  typedef UNITY_FLOAT UNITY_DOUBLE;
+  /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */
+  #endif
+
+#else
+
+  /* Double Floating Point Support */
+  #ifndef UNITY_DOUBLE_PRECISION
+  #define UNITY_DOUBLE_PRECISION (1e-12)
+  #endif
+
+  #ifndef UNITY_DOUBLE_TYPE
+  #define UNITY_DOUBLE_TYPE double
+  #endif
+  typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE;
+
+#endif
+
+/*-------------------------------------------------------
+ * Output Method: stdout (DEFAULT)
+ *-------------------------------------------------------*/
+#ifndef UNITY_OUTPUT_CHAR
+  /* Default to using putchar, which is defined in stdio.h */
+  #include <stdio.h>
+  #define UNITY_OUTPUT_CHAR(a) (void)putchar(a)
+#else
+  /* If defined as something else, make sure we declare it here so it's ready for use */
+  #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION
+    extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION;
+  #endif
+#endif
+
+#ifndef UNITY_OUTPUT_FLUSH
+  #ifdef UNITY_USE_FLUSH_STDOUT
+    /* We want to use the stdout flush utility */
+    #include <stdio.h>
+    #define UNITY_OUTPUT_FLUSH()    (void)fflush(stdout)
+  #else
+    /* We've specified nothing, therefore flush should just be ignored */
+    #define UNITY_OUTPUT_FLUSH()    (void)0
+  #endif
+#else
+  /* If defined as something else, make sure we declare it here so it's ready for use */
+  #ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION
+    extern void UNITY_OUTPUT_FLUSH_HEADER_DECLARATION;
+  #endif
+#endif
+
+#ifndef UNITY_OUTPUT_FLUSH
+#define UNITY_FLUSH_CALL()
+#else
+#define UNITY_FLUSH_CALL()  UNITY_OUTPUT_FLUSH()
+#endif
+
+#ifndef UNITY_PRINT_EOL
+#define UNITY_PRINT_EOL()   UNITY_OUTPUT_CHAR('\n')
+#endif
+
+#ifndef UNITY_OUTPUT_START
+#define UNITY_OUTPUT_START()
+#endif
+
+#ifndef UNITY_OUTPUT_COMPLETE
+#define UNITY_OUTPUT_COMPLETE()
+#endif
+
+#ifdef UNITY_INCLUDE_EXEC_TIME
+  #if !defined(UNITY_EXEC_TIME_START) && \
+      !defined(UNITY_EXEC_TIME_STOP) && \
+      !defined(UNITY_PRINT_EXEC_TIME) && \
+      !defined(UNITY_TIME_TYPE)
+      /* If none any of these macros are defined then try to provide a default implementation */
+
+    #if defined(UNITY_CLOCK_MS)
+      /* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */
+      #define UNITY_TIME_TYPE UNITY_UINT
+      #define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS()
+      #define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS()
+      #define UNITY_PRINT_EXEC_TIME() { \
+        UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
+        UnityPrint(" ("); \
+        UnityPrintNumberUnsigned(execTimeMs); \
+        UnityPrint(" ms)"); \
+        }
+    #elif defined(_WIN32)
+      #include <time.h>
+      #define UNITY_TIME_TYPE clock_t
+      #define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC)
+      #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
+      #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime)
+      #define UNITY_PRINT_EXEC_TIME() { \
+        UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
+        UnityPrint(" ("); \
+        UnityPrintNumberUnsigned(execTimeMs); \
+        UnityPrint(" ms)"); \
+        }
+    #elif defined(__unix__) || defined(__APPLE__)
+      #include <time.h>
+      #define UNITY_TIME_TYPE struct timespec
+      #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t)
+      #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
+      #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime)
+      #define UNITY_PRINT_EXEC_TIME() { \
+        UNITY_UINT execTimeMs = ((Unity.CurrentTestStopTime.tv_sec - Unity.CurrentTestStartTime.tv_sec) * 1000L); \
+        execTimeMs += ((Unity.CurrentTestStopTime.tv_nsec - Unity.CurrentTestStartTime.tv_nsec) / 1000000L); \
+        UnityPrint(" ("); \
+        UnityPrintNumberUnsigned(execTimeMs); \
+        UnityPrint(" ms)"); \
+        }
+    #endif
+  #endif
+#endif
+
+#ifndef UNITY_EXEC_TIME_START
+#define UNITY_EXEC_TIME_START() do { /* nothing*/ } while (0)
+#endif
+
+#ifndef UNITY_EXEC_TIME_STOP
+#define UNITY_EXEC_TIME_STOP()  do { /* nothing*/ } while (0)
+#endif
+
+#ifndef UNITY_TIME_TYPE
+#define UNITY_TIME_TYPE         UNITY_UINT
+#endif
+
+#ifndef UNITY_PRINT_EXEC_TIME
+#define UNITY_PRINT_EXEC_TIME() do { /* nothing*/ } while (0)
+#endif
+
+/*-------------------------------------------------------
+ * Footprint
+ *-------------------------------------------------------*/
+
+#ifndef UNITY_LINE_TYPE
+#define UNITY_LINE_TYPE UNITY_UINT
+#endif
+
+#ifndef UNITY_COUNTER_TYPE
+#define UNITY_COUNTER_TYPE UNITY_UINT
+#endif
+
+/*-------------------------------------------------------
+ * Internal Structs Needed
+ *-------------------------------------------------------*/
+
+typedef void (*UnityTestFunction)(void);
+
+#define UNITY_DISPLAY_RANGE_INT  (0x10)
+#define UNITY_DISPLAY_RANGE_UINT (0x20)
+#define UNITY_DISPLAY_RANGE_HEX  (0x40)
+#define UNITY_DISPLAY_RANGE_CHAR (0x80)
+
+typedef enum
+{
+    UNITY_DISPLAY_STYLE_INT      = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_INT,
+    UNITY_DISPLAY_STYLE_INT8     = 1 + UNITY_DISPLAY_RANGE_INT,
+    UNITY_DISPLAY_STYLE_INT16    = 2 + UNITY_DISPLAY_RANGE_INT,
+    UNITY_DISPLAY_STYLE_INT32    = 4 + UNITY_DISPLAY_RANGE_INT,
+#ifdef UNITY_SUPPORT_64
+    UNITY_DISPLAY_STYLE_INT64    = 8 + UNITY_DISPLAY_RANGE_INT,
+#endif
+
+    UNITY_DISPLAY_STYLE_UINT     = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_UINT,
+    UNITY_DISPLAY_STYLE_UINT8    = 1 + UNITY_DISPLAY_RANGE_UINT,
+    UNITY_DISPLAY_STYLE_UINT16   = 2 + UNITY_DISPLAY_RANGE_UINT,
+    UNITY_DISPLAY_STYLE_UINT32   = 4 + UNITY_DISPLAY_RANGE_UINT,
+#ifdef UNITY_SUPPORT_64
+    UNITY_DISPLAY_STYLE_UINT64   = 8 + UNITY_DISPLAY_RANGE_UINT,
+#endif
+
+    UNITY_DISPLAY_STYLE_HEX8     = 1 + UNITY_DISPLAY_RANGE_HEX,
+    UNITY_DISPLAY_STYLE_HEX16    = 2 + UNITY_DISPLAY_RANGE_HEX,
+    UNITY_DISPLAY_STYLE_HEX32    = 4 + UNITY_DISPLAY_RANGE_HEX,
+#ifdef UNITY_SUPPORT_64
+    UNITY_DISPLAY_STYLE_HEX64    = 8 + UNITY_DISPLAY_RANGE_HEX,
+#endif
+
+    UNITY_DISPLAY_STYLE_CHAR     = 1 + UNITY_DISPLAY_RANGE_CHAR + UNITY_DISPLAY_RANGE_INT,
+
+    UNITY_DISPLAY_STYLE_UNKNOWN
+} UNITY_DISPLAY_STYLE_T;
+
+typedef enum
+{
+    UNITY_WITHIN           = 0x0,
+    UNITY_EQUAL_TO         = 0x1,
+    UNITY_GREATER_THAN     = 0x2,
+    UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO,
+    UNITY_SMALLER_THAN     = 0x4,
+    UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO,
+    UNITY_NOT_EQUAL        = 0x0,
+    UNITY_UNKNOWN
+} UNITY_COMPARISON_T;
+
+#ifndef UNITY_EXCLUDE_FLOAT
+typedef enum UNITY_FLOAT_TRAIT
+{
+    UNITY_FLOAT_IS_NOT_INF       = 0,
+    UNITY_FLOAT_IS_INF,
+    UNITY_FLOAT_IS_NOT_NEG_INF,
+    UNITY_FLOAT_IS_NEG_INF,
+    UNITY_FLOAT_IS_NOT_NAN,
+    UNITY_FLOAT_IS_NAN,
+    UNITY_FLOAT_IS_NOT_DET,
+    UNITY_FLOAT_IS_DET,
+    UNITY_FLOAT_INVALID_TRAIT
+} UNITY_FLOAT_TRAIT_T;
+#endif
+
+typedef enum
+{
+    UNITY_ARRAY_TO_VAL = 0,
+    UNITY_ARRAY_TO_ARRAY,
+    UNITY_ARRAY_UNKNOWN
+} UNITY_FLAGS_T;
+
+struct UNITY_STORAGE_T
+{
+    const char* TestFile;
+    const char* CurrentTestName;
+#ifndef UNITY_EXCLUDE_DETAILS
+    const char* CurrentDetail1;
+    const char* CurrentDetail2;
+#endif
+    UNITY_LINE_TYPE CurrentTestLineNumber;
+    UNITY_COUNTER_TYPE NumberOfTests;
+    UNITY_COUNTER_TYPE TestFailures;
+    UNITY_COUNTER_TYPE TestIgnores;
+    UNITY_COUNTER_TYPE CurrentTestFailed;
+    UNITY_COUNTER_TYPE CurrentTestIgnored;
+#ifdef UNITY_INCLUDE_EXEC_TIME
+    UNITY_TIME_TYPE CurrentTestStartTime;
+    UNITY_TIME_TYPE CurrentTestStopTime;
+#endif
+#ifndef UNITY_EXCLUDE_SETJMP_H
+    jmp_buf AbortFrame;
+#endif
+};
+
+extern struct UNITY_STORAGE_T Unity;
+
+/*-------------------------------------------------------
+ * Test Suite Management
+ *-------------------------------------------------------*/
+
+void UnityBegin(const char* filename);
+int  UnityEnd(void);
+void UnitySetTestFile(const char* filename);
+void UnityConcludeTest(void);
+
+#ifndef RUN_TEST
+void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum);
+#else
+#define UNITY_SKIP_DEFAULT_RUNNER
+#endif
+
+/*-------------------------------------------------------
+ * Details Support
+ *-------------------------------------------------------*/
+
+#ifdef UNITY_EXCLUDE_DETAILS
+#define UNITY_CLR_DETAILS()
+#define UNITY_SET_DETAIL(d1)
+#define UNITY_SET_DETAILS(d1,d2)
+#else
+#define UNITY_CLR_DETAILS()         do { Unity.CurrentDetail1 = 0;     Unity.CurrentDetail2 = 0;    } while (0)
+#define UNITY_SET_DETAIL(d1)        do { Unity.CurrentDetail1 = (d1);  Unity.CurrentDetail2 = 0;    } while (0)
+#define UNITY_SET_DETAILS(d1,d2)    do { Unity.CurrentDetail1 = (d1);  Unity.CurrentDetail2 = (d2); } while (0)
+
+#ifndef UNITY_DETAIL1_NAME
+#define UNITY_DETAIL1_NAME "Function"
+#endif
+
+#ifndef UNITY_DETAIL2_NAME
+#define UNITY_DETAIL2_NAME "Argument"
+#endif
+#endif
+
+#ifdef UNITY_PRINT_TEST_CONTEXT
+void UNITY_PRINT_TEST_CONTEXT(void);
+#endif
+
+/*-------------------------------------------------------
+ * Test Output
+ *-------------------------------------------------------*/
+
+void UnityPrint(const char* string);
+
+#ifdef UNITY_INCLUDE_PRINT_FORMATTED
+void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...);
+#endif
+
+void UnityPrintLen(const char* string, const UNITY_UINT32 length);
+void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number);
+void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style);
+void UnityPrintNumber(const UNITY_INT number_to_print);
+void UnityPrintNumberUnsigned(const UNITY_UINT number);
+void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print);
+
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+void UnityPrintFloat(const UNITY_DOUBLE input_number);
+#endif
+
+/*-------------------------------------------------------
+ * Test Assertion Functions
+ *-------------------------------------------------------
+ *  Use the macros below this section instead of calling
+ *  these directly. The macros have a consistent naming
+ *  convention and will pull in file and line information
+ *  for you. */
+
+void UnityAssertEqualNumber(const UNITY_INT expected,
+                            const UNITY_INT actual,
+                            const char* msg,
+                            const UNITY_LINE_TYPE lineNumber,
+                            const UNITY_DISPLAY_STYLE_T style);
+
+void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold,
+                                           const UNITY_INT actual,
+                                           const UNITY_COMPARISON_T compare,
+                                           const char *msg,
+                                           const UNITY_LINE_TYPE lineNumber,
+                                           const UNITY_DISPLAY_STYLE_T style);
+
+void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
+                              UNITY_INTERNAL_PTR actual,
+                              const UNITY_UINT32 num_elements,
+                              const char* msg,
+                              const UNITY_LINE_TYPE lineNumber,
+                              const UNITY_DISPLAY_STYLE_T style,
+                              const UNITY_FLAGS_T flags);
+
+void UnityAssertBits(const UNITY_INT mask,
+                     const UNITY_INT expected,
+                     const UNITY_INT actual,
+                     const char* msg,
+                     const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertEqualString(const char* expected,
+                            const char* actual,
+                            const char* msg,
+                            const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertEqualStringLen(const char* expected,
+                            const char* actual,
+                            const UNITY_UINT32 length,
+                            const char* msg,
+                            const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected,
+                                  const char** actual,
+                                  const UNITY_UINT32 num_elements,
+                                  const char* msg,
+                                  const UNITY_LINE_TYPE lineNumber,
+                                  const UNITY_FLAGS_T flags);
+
+void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected,
+                             UNITY_INTERNAL_PTR actual,
+                             const UNITY_UINT32 length,
+                             const UNITY_UINT32 num_elements,
+                             const char* msg,
+                             const UNITY_LINE_TYPE lineNumber,
+                             const UNITY_FLAGS_T flags);
+
+void UnityAssertNumbersWithin(const UNITY_UINT delta,
+                              const UNITY_INT expected,
+                              const UNITY_INT actual,
+                              const char* msg,
+                              const UNITY_LINE_TYPE lineNumber,
+                              const UNITY_DISPLAY_STYLE_T style);
+
+void UnityAssertNumbersArrayWithin(const UNITY_UINT delta,
+                                   UNITY_INTERNAL_PTR expected,
+                                   UNITY_INTERNAL_PTR actual,
+                                   const UNITY_UINT32 num_elements,
+                                   const char* msg,
+                                   const UNITY_LINE_TYPE lineNumber,
+                                   const UNITY_DISPLAY_STYLE_T style,
+                                   const UNITY_FLAGS_T flags);
+
+#ifndef UNITY_EXCLUDE_SETJMP_H
+UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line);
+UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line);
+#else
+void UnityFail(const char* message, const UNITY_LINE_TYPE line);
+void UnityIgnore(const char* message, const UNITY_LINE_TYPE line);
+#endif
+
+void UnityMessage(const char* message, const UNITY_LINE_TYPE line);
+
+#ifndef UNITY_EXCLUDE_FLOAT
+void UnityAssertFloatsWithin(const UNITY_FLOAT delta,
+                             const UNITY_FLOAT expected,
+                             const UNITY_FLOAT actual,
+                             const char* msg,
+                             const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta,
+                                const UNITY_FLOAT expected,
+                                const UNITY_FLOAT actual,
+                                const char* msg,
+                                const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold,
+                                   const UNITY_FLOAT actual,
+                                   const UNITY_COMPARISON_T compare,
+                                   const char* msg,
+                                   const UNITY_LINE_TYPE linenumber);
+
+void UnityAssertWithinFloatArray(const UNITY_FLOAT delta,
+                                 UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected,
+                                 UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual,
+                                 const UNITY_UINT32 num_elements,
+                                 const char* msg,
+                                 const UNITY_LINE_TYPE lineNumber,
+                                 const UNITY_FLAGS_T flags);
+
+void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
+                             const char* msg,
+                             const UNITY_LINE_TYPE lineNumber,
+                             const UNITY_FLOAT_TRAIT_T style);
+#endif
+
+#ifndef UNITY_EXCLUDE_DOUBLE
+void UnityAssertDoublesWithin(const UNITY_DOUBLE delta,
+                              const UNITY_DOUBLE expected,
+                              const UNITY_DOUBLE actual,
+                              const char* msg,
+                              const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta,
+                                 const UNITY_DOUBLE expected,
+                                 const UNITY_DOUBLE actual,
+                                 const char* msg,
+                                 const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold,
+                                    const UNITY_DOUBLE actual,
+                                    const UNITY_COMPARISON_T compare,
+                                    const char* msg,
+                                    const UNITY_LINE_TYPE linenumber);
+
+void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta,
+                                  UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected,
+                                  UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual,
+                                  const UNITY_UINT32 num_elements,
+                                  const char* msg,
+                                  const UNITY_LINE_TYPE lineNumber,
+                                  const UNITY_FLAGS_T flags);
+
+void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual,
+                              const char* msg,
+                              const UNITY_LINE_TYPE lineNumber,
+                              const UNITY_FLOAT_TRAIT_T style);
+#endif
+
+/*-------------------------------------------------------
+ * Helpers
+ *-------------------------------------------------------*/
+
+UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size);
+#ifndef UNITY_EXCLUDE_FLOAT
+UNITY_INTERNAL_PTR UnityFloatToPtr(const float num);
+#endif
+#ifndef UNITY_EXCLUDE_DOUBLE
+UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num);
+#endif
+
+/*-------------------------------------------------------
+ * Error Strings We Might Need
+ *-------------------------------------------------------*/
+
+extern const char UnityStrOk[];
+extern const char UnityStrPass[];
+extern const char UnityStrFail[];
+extern const char UnityStrIgnore[];
+
+extern const char UnityStrErrFloat[];
+extern const char UnityStrErrDouble[];
+extern const char UnityStrErr64[];
+extern const char UnityStrErrShorthand[];
+
+/*-------------------------------------------------------
+ * Test Running Macros
+ *-------------------------------------------------------*/
+
+#ifndef UNITY_EXCLUDE_SETJMP_H
+#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0)
+#define TEST_ABORT() longjmp(Unity.AbortFrame, 1)
+#else
+#define TEST_PROTECT() 1
+#define TEST_ABORT() return
+#endif
+
+/* Automatically enable variadic macros support, if it not enabled before */
+#ifndef UNITY_SUPPORT_VARIADIC_MACROS
+  #ifdef __STDC_VERSION__
+    #if __STDC_VERSION__ >= 199901L
+      #define UNITY_SUPPORT_VARIADIC_MACROS
+    #endif
+  #endif
+#endif
+
+/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */
+#ifndef RUN_TEST
+#ifdef UNITY_SUPPORT_VARIADIC_MACROS
+#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway)
+#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line)
+#endif
+#endif
+
+/* Enable default macros for masking param tests test cases */
+#ifdef UNITY_SUPPORT_TEST_CASES
+  #ifdef UNITY_SUPPORT_VARIADIC_MACROS
+    #if !defined(TEST_CASE) && !defined(UNITY_EXCLUDE_TEST_CASE)
+      #define TEST_CASE(...)
+    #endif
+    #if !defined(TEST_RANGE) && !defined(UNITY_EXCLUDE_TEST_RANGE)
+      #define TEST_RANGE(...)
+    #endif
+  #endif
+#endif
+
+/* If we can't do the tricky version, we'll just have to require them to always include the line number */
+#ifndef RUN_TEST
+#ifdef CMOCK
+#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num)
+#else
+#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__)
+#endif
+#endif
+
+#define TEST_LINE_NUM (Unity.CurrentTestLineNumber)
+#define TEST_IS_IGNORED (Unity.CurrentTestIgnored)
+#define UNITY_NEW_TEST(a) \
+    Unity.CurrentTestName = (a); \
+    Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \
+    Unity.NumberOfTests++;
+
+#ifndef UNITY_BEGIN
+#define UNITY_BEGIN() UnityBegin(__FILE__)
+#endif
+
+#ifndef UNITY_END
+#define UNITY_END() UnityEnd()
+#endif
+
+#ifndef UNITY_SHORTHAND_AS_INT
+#ifndef UNITY_SHORTHAND_AS_MEM
+#ifndef UNITY_SHORTHAND_AS_NONE
+#ifndef UNITY_SHORTHAND_AS_RAW
+#define UNITY_SHORTHAND_AS_OLD
+#endif
+#endif
+#endif
+#endif
+
+/*-----------------------------------------------
+ * Command Line Argument Support
+ *-----------------------------------------------*/
+
+#ifdef UNITY_USE_COMMAND_LINE_ARGS
+int UnityParseOptions(int argc, char** argv);
+int UnityTestMatches(void);
+#endif
+
+/*-------------------------------------------------------
+ * Basic Fail and Ignore
+ *-------------------------------------------------------*/
+
+#define UNITY_TEST_FAIL(line, message)   UnityFail(   (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line))
+
+/*-------------------------------------------------------
+ * Test Asserts
+ *-------------------------------------------------------*/
+
+#define UNITY_TEST_ASSERT(condition, line, message)                                              do { if (condition) { /* nothing*/ } else { UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message)); } } while (0)
+#define UNITY_TEST_ASSERT_NULL(pointer, line, message)                                           UNITY_TEST_ASSERT(((pointer) == NULL),  (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message)                                       UNITY_TEST_ASSERT(((pointer) != NULL),  (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message)                                          UNITY_TEST_ASSERT(((pointer[0]) == 0),  (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message)                                      UNITY_TEST_ASSERT(((pointer[0]) != 0),  (UNITY_LINE_TYPE)(line), (message))
+
+#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message)                             UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message)                            UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message)                            UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message)                          UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message)                          UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message)                            UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message)                            UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message)                            UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line))
+
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message)                        UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),               (UNITY_INT)(actual),               UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message)                       UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold),  (UNITY_INT)(UNITY_INT8 )(actual),  UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold),  (UNITY_INT)(UNITY_INT16)(actual),  UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold),  (UNITY_INT)(UNITY_INT32)(actual),  UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message)                       UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),               (UNITY_INT)(actual),               UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message)                       UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message)                       UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold),  (UNITY_INT)(UNITY_INT8 )(actual),  UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),              (UNITY_INT)(actual),              UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),              (UNITY_INT)(actual),              UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),              (UNITY_INT)(actual),              UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),              (UNITY_INT)(actual),              UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_CHAR(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message)                 UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)              (threshold), (UNITY_INT)              (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16) (threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32) (threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)              (threshold), (UNITY_INT)              (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message)                 UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)             (threshold),  (UNITY_INT)              (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold),  (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold),  (UNITY_INT)(UNITY_INT16) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold),  (UNITY_INT)(UNITY_INT32) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)             (threshold),  (UNITY_INT)              (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold),  (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message)                     UnityAssertNumbersWithin(              (delta), (UNITY_INT)                          (expected), (UNITY_INT)                          (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message)                    UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 )             (expected), (UNITY_INT)(UNITY_INT8 )             (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16)             (expected), (UNITY_INT)(UNITY_INT16)             (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32)             (expected), (UNITY_INT)(UNITY_INT32)             (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message)                    UnityAssertNumbersWithin(              (delta), (UNITY_INT)                          (expected), (UNITY_INT)                          (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message)                  UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message)                  UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message)                    UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_CHAR_WITHIN(delta, expected, actual, line, message)                    UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 )             (expected), (UNITY_INT)(UNITY_INT8 )             (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)     UnityAssertNumbersArrayWithin(              (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)    UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)    UnityAssertNumbersArrayWithin(              (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)    UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)    UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY)
+
+
+#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message)                             UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER)
+#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message)                          UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message)                 UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message)                     UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+
+#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT,     UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8,    UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16,   UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32,   UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT,    UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8,   UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message)      UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16,  UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message)      UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32,  UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8,    UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16,   UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32,   UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message)      UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR,    UNITY_ARRAY_TO_ARRAY)
+
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message)          UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)              (expected), (UNITY_INT_WIDTH / 8)),          (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT,     UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8  )(expected), 1),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8,    UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16,   UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32,   UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)              (expected), (UNITY_INT_WIDTH / 8)),          (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT,    UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )(expected), 1),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8,   UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)(expected), 2),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16,  UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)(expected), 4),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32,  UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8  )(expected), 1),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8,    UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16,   UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32,   UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message)          UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT)       (expected), (UNITY_POINTER_WIDTH / 8)),      (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message)       UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message)  UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8  )(expected), 1),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR,    UNITY_ARRAY_TO_VAL)
+
+#ifdef UNITY_SUPPORT_64
+#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message)                          UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64,  UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message)      UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64,  UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64,  UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64,  UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message)                  UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL,        (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL,        (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL,        (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY)
+#else
+#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message)                          UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message)       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message)      UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message)       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message)              UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message)              UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#endif
+
+#ifdef UNITY_EXCLUDE_FLOAT
+#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message)                       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message)       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message)        UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message)                      UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message)                                    UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message)                                UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message)                                    UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message)                            UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message)                                UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message)                            UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message)                                UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message)                        UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#else
+#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message)                   UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message)               UnityAssertFloatsNotWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message)                           UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message)                       UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UnityAssertWithinFloatArray((UNITY_FLOAT)(delta), (UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message)       UnityAssertWithinFloatArray((UNITY_FLOAT)0, (UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message)        UnityAssertWithinFloatArray((UNITY_FLOAT)0, UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message)                   UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message)               UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message)                      UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message)                  UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message)                                    UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message)                                UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message)                                    UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN)
+#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message)                            UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message)                                UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message)                            UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message)                                UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message)                        UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET)
+#endif
+
+#ifdef UNITY_EXCLUDE_DOUBLE
+#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message)              UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message)                          UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message)                      UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message)      UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message)       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message)              UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message)                     UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message)                 UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message)                                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message)                               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message)                                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message)                               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message)                               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message)                       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#else
+#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message)                  UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message)              UnityAssertDoublesNotWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message)                          UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message)                      UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)(delta), (UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message)      UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, (UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message)       UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message)                  UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message)              UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message)                     UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message)                 UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message)                                   UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message)                               UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message)                                   UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message)                           UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message)                               UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message)                           UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message)                               UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message)                       UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET)
+#endif
+
+/* End of UNITY_INTERNALS_H */
+#endif
diff --git a/lib/sunriset/sunriset.c b/lib/sunriset/sunriset.c
new file mode 100644 (file)
index 0000000..178f2ff
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+
+SUNRISET.C - computes Sun rise/set times, start/end of twilight, and
+             the length of the day at any date and latitude
+
+Written as DAYLEN.C, 1989-08-16
+
+Modified to SUNRISET.C, 1992-12-01
+
+(c) Paul Schlyter, 1989, 1992
+
+Released to the public domain by Paul Schlyter, December 1992
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include "sunriset.h"
+
+static void sunpos( double d, double *lon, double *r );
+
+/* A macro to compute the number of days elapsed since 2000 Jan 0.0 */
+/* (which is equal to 1999 Dec 31, 0h UT)                           */
+
+#define days_since_2000_Jan_0(y,m,d) \
+    (367L*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530L)
+
+/* Some conversion factors between radians and degrees */
+
+#ifndef PI
+ #define PI        3.1415926535897932384
+#endif
+
+#define RADEG     ( 180.0 / PI )
+#define DEGRAD    ( PI / 180.0 )
+
+/* The trigonometric functions in degrees */
+
+#define sind(x)  sin((x)*DEGRAD)
+#define cosd(x)  cos((x)*DEGRAD)
+#define tand(x)  tan((x)*DEGRAD)
+
+#define atand(x)    (RADEG*atan(x))
+#define asind(x)    (RADEG*asin(x))
+#define acosd(x)    (RADEG*acos(x))
+#define atan2d(y,x) (RADEG*atan2(y,x))
+
+/* The "workhorse" function for sun rise/set times */
+
+int __sunriset__( int year, int month, int day, double lon, double lat,
+                  double altit, int upper_limb, double *trise, double *tset )
+/***************************************************************************/
+/* Note: year,month,date = calendar date, 1801-2099 only.             */
+/*       Eastern longitude positive, Western longitude negative       */
+/*       Northern latitude positive, Southern latitude negative       */
+/*       The longitude value IS critical in this function!            */
+/*       altit = the altitude which the Sun should cross              */
+/*               Set to -35/60 degrees for rise/set, -6 degrees       */
+/*               for civil, -12 degrees for nautical and -18          */
+/*               degrees for astronomical twilight.                   */
+/*         upper_limb: non-zero -> upper limb, zero -> center         */
+/*               Set to non-zero (e.g. 1) when computing rise/set     */
+/*               times, and to zero when computing start/end of       */
+/*               twilight.                                            */
+/*        *rise = where to store the rise time                        */
+/*        *set  = where to store the set  time                        */
+/*                Both times are relative to the specified altitude,  */
+/*                and thus this function can be used to compute       */
+/*                various twilight times, as well as rise/set times   */
+/* Return value:  0 = sun rises/sets this day, times stored at        */
+/*                    *trise and *tset.                               */
+/*               +1 = sun above the specified "horizon" 24 hours.     */
+/*                    *trise set to time when the sun is at south,    */
+/*                    minus 12 hours while *tset is set to the south  */
+/*                    time plus 12 hours. "Day" length = 24 hours     */
+/*               -1 = sun is below the specified "horizon" 24 hours   */
+/*                    "Day" length = 0 hours, *trise and *tset are    */
+/*                    both set to the time when the sun is at south.  */
+/*                                                                    */
+/**********************************************************************/
+{
+      double  d,  /* Days since 2000 Jan 0.0 (negative before) */
+      sr,         /* Solar distance, astronomical units */
+      sRA,        /* Sun's Right Ascension */
+      sdec,       /* Sun's declination */
+      sradius,    /* Sun's apparent radius */
+      t,          /* Diurnal arc */
+      tsouth,     /* Time when Sun is at south */
+      sidtime;    /* Local sidereal time */
+
+      int rc = 0; /* Return cde from function - usually 0 */
+
+      /* Compute d of 12h local mean solar time */
+      d = days_since_2000_Jan_0(year,month,day) + 0.5 - lon/360.0;
+
+      /* Compute the local sidereal time of this moment */
+      sidtime = revolution( GMST0(d) + 180.0 + lon );
+
+      /* Compute Sun's RA, Decl and distance at this moment */
+      sun_RA_dec( d, &sRA, &sdec, &sr );
+
+      /* Compute time when Sun is at south - in hours UT */
+      tsouth = 12.0 - rev180(sidtime - sRA)/15.0;
+
+      /* Compute the Sun's apparent radius in degrees */
+      sradius = 0.2666 / sr;
+
+      /* Do correction to upper limb, if necessary */
+      if ( upper_limb )
+            altit -= sradius;
+
+      /* Compute the diurnal arc that the Sun traverses to reach */
+      /* the specified altitude altit: */
+      {
+            double cost;
+            cost = ( sind(altit) - sind(lat) * sind(sdec) ) /
+                  ( cosd(lat) * cosd(sdec) );
+            if ( cost >= 1.0 )
+                  rc = -1, t = 0.0;       /* Sun always below altit */
+            else if ( cost <= -1.0 )
+                  rc = +1, t = 12.0;      /* Sun always above altit */
+            else
+                  t = acosd(cost)/15.0;   /* The diurnal arc, hours */
+      }
+
+      /* Store rise and set times - in hours UT */
+      *trise = tsouth - t;
+      *tset  = tsouth + t;
+
+      return rc;
+}  /* __sunriset__ */
+
+
+
+/* The "workhorse" function */
+
+
+double __daylen__( int year, int month, int day, double lon, double lat,
+                   double altit, int upper_limb )
+/**********************************************************************/
+/* Note: year,month,date = calendar date, 1801-2099 only.             */
+/*       Eastern longitude positive, Western longitude negative       */
+/*       Northern latitude positive, Southern latitude negative       */
+/*       The longitude value is not critical. Set it to the correct   */
+/*       longitude if you're picky, otherwise set to to, say, 0.0     */
+/*       The latitude however IS critical - be sure to get it correct */
+/*       altit = the altitude which the Sun should cross              */
+/*               Set to -35/60 degrees for rise/set, -6 degrees       */
+/*               for civil, -12 degrees for nautical and -18          */
+/*               degrees for astronomical twilight.                   */
+/*         upper_limb: non-zero -> upper limb, zero -> center         */
+/*               Set to non-zero (e.g. 1) when computing day length   */
+/*               and to zero when computing day+twilight length.      */
+/**********************************************************************/
+{
+      double  d,  /* Days since 2000 Jan 0.0 (negative before) */
+      obl_ecl,    /* Obliquity (inclination) of Earth's axis */
+      sr,         /* Solar distance, astronomical units */
+      slon,       /* True solar longitude */
+      sin_sdecl,  /* Sine of Sun's declination */
+      cos_sdecl,  /* Cosine of Sun's declination */
+      sradius,    /* Sun's apparent radius */
+      t;          /* Diurnal arc */
+
+      /* Compute d of 12h local mean solar time */
+      d = days_since_2000_Jan_0(year,month,day) + 0.5 - lon/360.0;
+
+      /* Compute obliquity of ecliptic (inclination of Earth's axis) */
+      obl_ecl = 23.4393 - 3.563E-7 * d;
+
+      /* Compute Sun's ecliptic longitude and distance */
+      sunpos( d, &slon, &sr );
+
+      /* Compute sine and cosine of Sun's declination */
+      sin_sdecl = sind(obl_ecl) * sind(slon);
+      cos_sdecl = sqrt( 1.0 - sin_sdecl * sin_sdecl );
+
+      /* Compute the Sun's apparent radius, degrees */
+      sradius = 0.2666 / sr;
+
+      /* Do correction to upper limb, if necessary */
+      if ( upper_limb )
+            altit -= sradius;
+
+      /* Compute the diurnal arc that the Sun traverses to reach */
+      /* the specified altitude altit: */
+      {
+            double cost;
+            cost = ( sind(altit) - sind(lat) * sin_sdecl ) /
+                  ( cosd(lat) * cos_sdecl );
+            if ( cost >= 1.0 )
+                  t = 0.0;                      /* Sun always below altit */
+            else if ( cost <= -1.0 )
+                  t = 24.0;                     /* Sun always above altit */
+            else  t = (2.0/15.0) * acosd(cost); /* The diurnal arc, hours */
+      }
+      return t;
+}  /* __daylen__ */
+
+
+/* This function computes the Sun's position at any instant */
+
+static void sunpos( double d, double *lon, double *r )
+/******************************************************/
+/* Computes the Sun's ecliptic longitude and distance */
+/* at an instant given in d, number of days since     */
+/* 2000 Jan 0.0.  The Sun's ecliptic latitude is not  */
+/* computed, since it's always very near 0.           */
+/******************************************************/
+{
+      double M,         /* Mean anomaly of the Sun */
+             w,         /* Mean longitude of perihelion */
+                        /* Note: Sun's mean longitude = M + w */
+             e,         /* Eccentricity of Earth's orbit */
+             E,         /* Eccentric anomaly */
+             x, y,      /* x, y coordinates in orbit */
+             v;         /* True anomaly */
+
+      /* Compute mean elements */
+      M = revolution( 356.0470 + 0.9856002585 * d );
+      w = 282.9404 + 4.70935E-5 * d;
+      e = 0.016709 - 1.151E-9 * d;
+
+      /* Compute true longitude and radius vector */
+      E = M + e * RADEG * sind(M) * ( 1.0 + e * cosd(M) );
+            x = cosd(E) - e;
+      y = sqrt( 1.0 - e*e ) * sind(E);
+      *r = sqrt( x*x + y*y );              /* Solar distance */
+      v = atan2d( y, x );                  /* True anomaly */
+      *lon = v + w;                        /* True solar longitude */
+      if ( *lon >= 360.0 )
+            *lon -= 360.0;                   /* Make it 0..360 degrees */
+}
+
+void sun_RA_dec( double d, double *RA, double *dec, double *r )
+/******************************************************/
+/* Computes the Sun's equatorial coordinates RA, Decl */
+/* and also its distance, at an instant given in d,   */
+/* the number of days since 2000 Jan 0.0.             */
+/******************************************************/
+{
+      double lon, obl_ecl, x, y, z;
+
+      /* Compute Sun's ecliptical coordinates */
+      sunpos( d, &lon, r );
+
+      /* Compute ecliptic rectangular coordinates (z=0) */
+      x = *r * cosd(lon);
+      y = *r * sind(lon);
+
+      /* Compute obliquity of ecliptic (inclination of Earth's axis) */
+      obl_ecl = 23.4393 - 3.563E-7 * d;
+
+      /* Convert to equatorial rectangular coordinates - x is unchanged */
+      z = y * sind(obl_ecl);
+      y = y * cosd(obl_ecl);
+
+      /* Convert to spherical coordinates */
+      *RA = atan2d( y, x );
+      *dec = atan2d( z, sqrt(x*x + y*y) );
+
+}  /* sun_RA_dec */
+
+
+/******************************************************************/
+/* This function reduces any angle to within the first revolution */
+/* by subtracting or adding even multiples of 360.0 until the     */
+/* result is >= 0.0 and < 360.0                                   */
+/******************************************************************/
+
+#define INV360    ( 1.0 / 360.0 )
+
+double revolution( double x )
+/*****************************************/
+/* Reduce angle to within 0..360 degrees */
+/*****************************************/
+{
+      return( x - 360.0 * floor( x * INV360 ) );
+}  /* revolution */
+
+double rev180( double x )
+/*********************************************/
+/* Reduce angle to within +180..+180 degrees */
+/*********************************************/
+{
+      return( x - 360.0 * floor( x * INV360 + 0.5 ) );
+}  /* revolution */
+
+
+/*******************************************************************/
+/* This function computes GMST0, the Greenwich Mean Sidereal Time  */
+/* at 0h UT (i.e. the sidereal time at the Greenwhich meridian at  */
+/* 0h UT).  GMST is then the sidereal time at Greenwich at any     */
+/* time of the day.  I've generalized GMST0 as well, and define it */
+/* as:  GMST0 = GMST - UT  --  this allows GMST0 to be computed at */
+/* other times than 0h UT as well.  While this sounds somewhat     */
+/* contradictory, it is very practical:  instead of computing      */
+/* GMST like:                                                      */
+/*                                                                 */
+/*  GMST = (GMST0) + UT * (366.2422/365.2422)                      */
+/*                                                                 */
+/* where (GMST0) is the GMST last time UT was 0 hours, one simply  */
+/* computes:                                                       */
+/*                                                                 */
+/*  GMST = GMST0 + UT                                              */
+/*                                                                 */
+/* where GMST0 is the GMST "at 0h UT" but at the current moment!   */
+/* Defined in this way, GMST0 will increase with about 4 min a     */
+/* day.  It also happens that GMST0 (in degrees, 1 hr = 15 degr)   */
+/* is equal to the Sun's mean longitude plus/minus 180 degrees!    */
+/* (if we neglect aberration, which amounts to 20 seconds of arc   */
+/* or 1.33 seconds of time)                                        */
+/*                                                                 */
+/*******************************************************************/
+
+double GMST0( double d )
+{
+      double sidtim0;
+      /* Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr  */
+      /* L = M + w, as defined in sunpos().  Since I'm too lazy to */
+      /* add these numbers, I'll let the C compiler do it for me.  */
+      /* Any decent C compiler will add the constants at compile   */
+      /* time, imposing no runtime or code overhead.               */
+      sidtim0 = revolution( ( 180.0 + 356.0470 + 282.9404 ) +
+                          ( 0.9856002585 + 4.70935E-5 ) * d );
+      return sidtim0;
+}  /* GMST0 */
\ No newline at end of file
diff --git a/lib/sunriset/sunriset.h b/lib/sunriset/sunriset.h
new file mode 100644 (file)
index 0000000..7b2b6c8
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+
+SUNRISET.H - computes Sun rise/set times, start/end of twilight, and
+             the length of the day at any date and latitude
+
+Written as DAYLEN.C, 1989-08-16
+
+Modified to SUNRISET.C, 1992-12-01
+
+(c) Paul Schlyter, 1989, 1992
+
+Released to the public domain by Paul Schlyter, December 1992
+
+*/
+
+#ifndef SUNRISET_H_
+#define SUNRISET_H_
+
+/* Function prototypes */
+
+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 );
+
+void sun_RA_dec( double d, double *RA, double *dec, double *r );
+
+double revolution( double x );
+
+double rev180( double x );
+
+double GMST0( double d );
+
+
+/* Following are some macros around the "workhorse" function __daylen__ */
+/* They mainly fill in the desired values for the reference altitude    */
+/* below the horizon, and also selects whether this altitude should     */
+/* refer to the Sun's center or its upper limb.                         */
+
+
+/* This macro computes the length of the day, from sunrise to 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 */
+/* of the Earth's atmosphere).                                        */
+#define day_length(year,month,day,lon,lat)  \
+        __daylen__( year, month, day, lon, lat, -35.0/60.0, 1 )
+
+/* This macro computes the length of the day, including civil twilight. */
+/* Civil twilight starts/ends when the Sun's center is 6 degrees below  */
+/* the horizon.                                                         */
+#define day_civil_twilight_length(year,month,day,lon,lat)  \
+        __daylen__( year, month, day, lon, lat, -6.0, 0 )
+
+/* This macro computes the length of the day, incl. nautical twilight.  */
+/* Nautical twilight starts/ends when the Sun's center is 12 degrees    */
+/* below the horizon.                                                   */
+#define day_nautical_twilight_length(year,month,day,lon,lat)  \
+        __daylen__( year, month, day, lon, lat, -12.0, 0 )
+
+/* This macro computes the length of the day, incl. astronomical twilight. */
+/* Astronomical twilight starts/ends when the Sun's center is 18 degrees   */
+/* below the horizon.                                                      */
+#define day_astronomical_twilight_length(year,month,day,lon,lat)  \
+        __daylen__( year, month, day, lon, lat, -18.0, 0 )
+
+
+/* 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 */
+/* of the Earth's atmosphere).                                        */
+#define sun_rise_set(year,month,day,lon,lat,rise,set)  \
+        __sunriset__( year, month, day, lon, lat, -35.0/60.0, 1, rise, set )
+
+/* This macro computes the start and end times of civil twilight.       */
+/* Civil twilight starts/ends when the Sun's center is 6 degrees below  */
+/* the horizon.                                                         */
+#define civil_twilight(year,month,day,lon,lat,start,end)  \
+        __sunriset__( year, month, day, lon, lat, -6.0, 0, start, end )
+
+/* This macro computes the start and end times of nautical twilight.    */
+/* Nautical twilight starts/ends when the Sun's center is 12 degrees    */
+/* below the horizon.                                                   */
+#define nautical_twilight(year,month,day,lon,lat,start,end)  \
+        __sunriset__( year, month, day, lon, lat, -12.0, 0, start, end )
+
+/* This macro computes the start and end times of astronomical twilight.   */
+/* Astronomical twilight starts/ends when the Sun's center is 18 degrees   */
+/* below the horizon.                                                      */
+#define astronomical_twilight(year,month,day,lon,lat,start,end)  \
+        __sunriset__( year, month, day, lon, lat, -18.0, 0, start, end )
+
+#endif // SUNRISET_H_
diff --git a/movement/alt_fw/alt_time.h b/movement/alt_fw/alt_time.h
deleted file mode 100644 (file)
index 956928d..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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 MOVEMENT_CONFIG_H_
-#define MOVEMENT_CONFIG_H_
-
-#include "movement_faces.h"
-
-const watch_face_t watch_faces[] = {
-    simple_clock_face,
-    beats_face,
-    day_one_face,
-
-    preferences_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/alt_fw/backer.h b/movement/alt_fw/backer.h
deleted file mode 100644 (file)
index ff04222..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 MOVEMENT_CONFIG_H_
-#define MOVEMENT_CONFIG_H_
-
-#include "movement_faces.h"
-
-const watch_face_t watch_faces[] = {
-    simple_clock_face,
-    world_clock_face,
-    sunrise_sunset_face,
-    moon_phase_face,
-    temperature_display_face,
-
-    preferences_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/alt_fw/deep_space_now.h b/movement/alt_fw/deep_space_now.h
deleted file mode 100644 (file)
index d2096a6..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 MOVEMENT_CONFIG_H_
-#define MOVEMENT_CONFIG_H_
-
-#include "movement_faces.h"
-
-// Preset Goldstone (GO), Madrid (MA), and Canberra (CA) time zones.
-// Also prepopulate the Day One register with Voyager 1's launch (September 5, 1977)
-
-#define MOVEMENT_CUSTOM_BOOT_COMMANDS() { \
-    /* Standard Time */\
-    watch_store_backup_data(0x1e0c0c, 4);\
-    watch_store_backup_data(0x010115, 5);\
-    watch_store_backup_data(0x130105, 6);\
-    /* Daylight Saving Time */\
-    /*\
-    watch_store_backup_data(0x1f0c0c, 4);\
-    watch_store_backup_data(0x020115, 5);\
-    watch_store_backup_data(0x110105, 6);\
-    */\
-    watch_store_backup_data(0x0597b9, 2);\
-}
-
-const watch_face_t watch_faces[] = {
-    simple_clock_face,
-    mars_time_face,
-    world_clock_face,
-    world_clock_face,
-    world_clock_face,
-    day_one_face,
-
-    preferences_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/alt_fw/focus.h b/movement/alt_fw/focus.h
deleted file mode 100644 (file)
index a8fd573..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 MOVEMENT_CONFIG_H_
-#define MOVEMENT_CONFIG_H_
-
-#include "movement_faces.h"
-
-const watch_face_t watch_faces[] = {
-    simple_clock_face,
-    tomato_face,
-    stopwatch_face,
-    countdown_face,
-    wake_face, // added by @joshber 2022-07-23, per @joeycastillo
-
-    preferences_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/alt_fw/the_athlete.h b/movement/alt_fw/the_athlete.h
deleted file mode 100644 (file)
index 660a304..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 MOVEMENT_CONFIG_H_
-#define MOVEMENT_CONFIG_H_
-
-#include "movement_faces.h"
-
-const watch_face_t watch_faces[] = {
-    simple_clock_face,
-    stopwatch_face,
-    countdown_face,
-    counter_face,
-    pulsometer_face,
-
-    preferences_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/alt_fw/the_backpacker.h b/movement/alt_fw/the_backpacker.h
deleted file mode 100644 (file)
index 29622cc..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 MOVEMENT_CONFIG_H_
-#define MOVEMENT_CONFIG_H_
-
-#include "movement_faces.h"
-
-const watch_face_t watch_faces[] = {
-    simple_clock_face,
-    sunrise_sunset_face,
-    moon_phase_face,
-    temperature_display_face,
-    temperature_logging_face,
-    blinky_face,
-
-    preferences_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/alt_fw/the_stargazer.h b/movement/alt_fw/the_stargazer.h
deleted file mode 100644 (file)
index 844c680..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 MOVEMENT_CONFIG_H_
-#define MOVEMENT_CONFIG_H_
-
-#include "movement_faces.h"
-
-#define MOVEMENT_DEFAULT_RED_COLOR 0xF
-#define MOVEMENT_DEFAULT_GREEN_COLOR 0x0
-
-const watch_face_t watch_faces[] = {
-    simple_clock_face,
-    astronomy_face,
-    sunrise_sunset_face,
-    moon_phase_face,
-
-    preferences_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/alt_fw/timers.h b/movement/alt_fw/timers.h
deleted file mode 100644 (file)
index 4d36c66..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 MOVEMENT_CONFIG_H_
-#define MOVEMENT_CONFIG_H_
-
-#include "movement_faces.h"
-
-const watch_face_t watch_faces[] = {
-    simple_clock_face,
-    wake_face,
-    interval_face,
-    stopwatch_face,
-    sunrise_sunset_face,
-
-    preferences_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/lib/TOTP/LICENSE b/movement/lib/TOTP/LICENSE
deleted file mode 100644 (file)
index 6de4c0f..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2019 Weravech
-
-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.
diff --git a/movement/lib/TOTP/README.md b/movement/lib/TOTP/README.md
deleted file mode 100644 (file)
index bb23122..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-TOTP Pure C Library
-====================
-
-Library to generate Time-based One-Time Passwords.
-
-Implements the Time-based One-Time Password algorithm specified in [RFC 6238](https://tools.ietf.org/html/rfc6238). 
-
-Supports different time steps and is compatible with tokens that use the same standard (including software ones, like the Google Authenticator app).
-
-The code is made of :
-
-- [TOTP-MCU](https://github.com/Netthaw/TOTP-MCU) for `TimeStruct2Timestamp`, `getCodeFromTimestamp`, `getCodeFromTimeStruct`, part of `getCodeFromSteps` and `TOTP_HMAC_SHA*` functions
-- [mbedtls](https://github.com/Mbed-TLS/mbedtls) for SHA1/SHA224/SHA256/SHA384/SHA512 implementations
-- [this project](https://github.com/mygityf/cipher/blob/master/cipher/hmac.c) as an inspiration for writing the code to compute the TOTP using the key and the text to hash
-
-
-
-Supported algorithms are SHA1/SHA224/SHA256/SHA384/SHA512.
-
-
-
-Installation & usage:
---------------------
-First include header to your file
-```c
-#include "TOTP.h"
-```
-After included, define key ex. Key is ```MyLegoDoor```
-- Note: The format of hmacKey is array of hexadecimal bytes.
-- Most websites provide the key encoded in base32 - RFC3548/RFC4648, either upper or lower case. You can use [this site](https://cryptii.com/pipes/base32-to-hex) to convert the base32 string to hex (make sure you upcase it first if it's lowercase and remove all whitespaces).
-```c
-uint8_t hmacKey[] = {0x4d, 0x79, 0x4c, 0x65, 0x67, 0x6f, 0x44, 0x6f, 0x6f, 0x72};               // Secret key
-```
-Instantiate the TOTP class by providing the secret hmacKey, the length of the hmacKey, the Timestep between codes and the algorithm used (most of the time, `SHA1`).
-```c
-TOTP(hmacKey, 10, 30, SHA1);                                     // Secret key, Secret key length, Timestep (30s), Algorithm
-```
-Use the ```getCodeFromTimestamp()``` function to get a TOTP from a unix epoch timestamp
-```c
-uint32_t newCode = getCodeFromTimestamp(1557414000);       // Current timestamp since Unix epoch in seconds
-```
-Or ```getCodeFromTimeStruct()``` if you want to get a TOTP from a tm struct (Time Struct in C), 
-```c
-struct tm datetime;
-datetime.tm_hour = 9;
-datetime.tm_min = 0;
-datetime.tm_sec = 0;
-datetime.tm_mday = 13;
-datetime.tm_mon = 5;
-datetime.tm_year = 2019;
-uint32_t newCode = getCodeFromTimeStruct(datetime);
-```
-
-If the provided unix timestamp isn't in UTC±0, use ```setTimezone()``` before ```getCodeFromTimestamp()``` or ```getCodeFromTimeStruct()``` to offset the time.
-
-```c
-setTimezone(9);                                            // Set timezone +9 Japan
-```
-
-You can see an example in example.c (compile it with `gcc -o example example.c sha1.c sha256.c sha512.c TOTP.c -I.`)
-
-Thanks to:
-----------
-
-* Netthaw, https://github.com/Netthaw/TOTP-MCU
-* Mbed-TLS, https://github.com/Mbed-TLS/mbedtls
-* mygityf, https://github.com/mygityf/cipher/blob/master/cipher/hmac.c
-* susam, https://github.com/susam/mintotp
diff --git a/movement/lib/TOTP/TOTP.c b/movement/lib/TOTP/TOTP.c
deleted file mode 100644 (file)
index 7f60133..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#include "TOTP.h"\r
-#include "sha1.h"\r
-#include "sha256.h"\r
-#include "sha512.h"\r
-#include <stdio.h>\r
-\r
-uint8_t* _hmacKey;\r
-uint8_t _keyLength;\r
-uint8_t _timeZoneOffset;\r
-uint32_t _timeStep;\r
-hmac_alg _algorithm;\r
-\r
-// Init the library with the private key, its length, the timeStep duration and the algorithm that should be used\r
-void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep, hmac_alg algorithm) {\r
-    _hmacKey = hmacKey;\r
-    _keyLength = keyLength;\r
-    _timeStep = timeStep;\r
-    _algorithm = algorithm;\r
-}\r
-\r
-void setTimezone(uint8_t timezone){\r
-    _timeZoneOffset = timezone;\r
-}\r
-\r
-static uint32_t TimeStruct2Timestamp(struct tm time){\r
-    //time.tm_mon -= 1;\r
-    //time.tm_year -= 1900;\r
-    return mktime(&(time)) - (_timeZoneOffset * 3600) - 2208988800;\r
-}\r
-\r
-// Generate a code, using the timestamp provided\r
-uint32_t getCodeFromTimestamp(uint32_t timeStamp) {\r
-    uint32_t steps = timeStamp / _timeStep;\r
-    return getCodeFromSteps(steps);\r
-}\r
-\r
-// Generate a code, using the timestamp provided\r
-uint32_t getCodeFromTimeStruct(struct tm time) {\r
-    return getCodeFromTimestamp(TimeStruct2Timestamp(time));\r
-}\r
-\r
-// Generate a code, using the number of steps provided\r
-uint32_t getCodeFromSteps(uint32_t steps) {\r
-    // STEP 0, map the number of steps in a 8-bytes array (counter value)\r
-    uint8_t _byteArray[8];\r
-    _byteArray[0] = 0x00;\r
-    _byteArray[1] = 0x00;\r
-    _byteArray[2] = 0x00;\r
-    _byteArray[3] = 0x00;\r
-    _byteArray[4] = (uint8_t)((steps >> 24) & 0xFF);\r
-    _byteArray[5] = (uint8_t)((steps >> 16) & 0xFF);\r
-    _byteArray[6] = (uint8_t)((steps >> 8) & 0XFF);\r
-    _byteArray[7] = (uint8_t)((steps & 0XFF));\r
-\r
-    switch(_algorithm){\r
-        case SHA1:\r
-            return(TOTP_HMAC_SHA1(_hmacKey, _keyLength, _byteArray, 8));\r
-        case SHA224:\r
-            return(TOTP_HMAC_SHA256(_hmacKey, _keyLength, _byteArray, 8, 1));\r
-        case SHA256:\r
-            return(TOTP_HMAC_SHA256(_hmacKey, _keyLength, _byteArray, 8, 0));\r
-        case SHA384:\r
-            return(TOTP_HMAC_SHA512(_hmacKey, _keyLength, _byteArray, 8, 1));\r
-        case SHA512:\r
-            return(TOTP_HMAC_SHA512(_hmacKey, _keyLength, _byteArray, 8, 0));\r
-        default:\r
-            return(0);\r
-    }\r
-}\r
diff --git a/movement/lib/TOTP/TOTP.h b/movement/lib/TOTP/TOTP.h
deleted file mode 100644 (file)
index adde66d..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef TOTP_H_\r
-#define TOTP_H_\r
-\r
-#include <inttypes.h>\r
-#include "time.h"\r
-\r
-typedef enum __attribute__ ((__packed__)) {\r
-    SHA1,\r
-    SHA224,\r
-    SHA256,\r
-    SHA384,\r
-    SHA512\r
-} hmac_alg;\r
-\r
-void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep, hmac_alg algorithm);\r
-void setTimezone(uint8_t timezone);\r
-uint32_t getCodeFromTimestamp(uint32_t timeStamp);\r
-uint32_t getCodeFromTimeStruct(struct tm time);\r
-uint32_t getCodeFromSteps(uint32_t steps);\r
-\r
-#endif // TOTP_H_\r
diff --git a/movement/lib/TOTP/example.c b/movement/lib/TOTP/example.c
deleted file mode 100644 (file)
index a218ff8..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "TOTP.h"\r
-#include <stdio.h>\r
-\r
-/**\r
- * example.c\r
- */\r
-void main(void)\r
-{\r
-       uint8_t hmacKey[] = {0x4d, 0x79, 0x4c, 0x65, 0x67, 0x6f, 0x44, 0x6f, 0x6f, 0x72};               // Secret key\r
-       TOTP(hmacKey, 10, 7200, SHA1);                                                                        // Secret key, Key length, Timestep (7200s - 2hours)\r
-\r
-       setTimezone(9);                                                                                 // Set timezone\r
-       uint32_t newCode = getCodeFromTimestamp(1557414000);                                            // Timestamp Now\r
-\r
-       ///////////////// For struct tm //////////////////\r
-    // struct tm datetime;\r
-    // datetime.tm_hour = 9;\r
-    // datetime.tm_min = 0;\r
-    // datetime.tm_sec = 0;\r
-    // datetime.tm_mday = 13;\r
-    // datetime.tm_mon = 5;\r
-    // datetime.tm_year = 2019;\r
-       //  uint32_t newCode = getCodeFromTimeStruct(datetime);\r
-       ///////////////////////////////////////////////////\r
-\r
-       printf("Code : %06u\n",newCode);\r
-}\r
diff --git a/movement/lib/TOTP/sha1.c b/movement/lib/TOTP/sha1.c
deleted file mode 100644 (file)
index 84fc83d..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-/*\r
- *  FIPS-180-1 compliant SHA-1 implementation\r
- *\r
- *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
- *  SPDX-License-Identifier: Apache-2.0\r
- *\r
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
- *  not use this file except in compliance with the License.\r
- *  You may obtain a copy of the License at\r
- *\r
- *  http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- *  Unless required by applicable law or agreed to in writing, software\r
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- *  See the License for the specific language governing permissions and\r
- *  limitations under the License.\r
- *\r
- *  This file is part of mbed TLS (https://tls.mbed.org)\r
- */\r
-/*\r
- *  The SHA-1 standard was published by NIST in 1993.\r
- *\r
- *  http://www.itl.nist.gov/fipspubs/fip180-1.htm\r
- */\r
-\r
-#include "sha1.h"\r
-#include <string.h>\r
-#include <stdio.h>\r
-\r
-/* Implementation that should never be optimized out by the compiler */\r
-static void mbedtls_zeroize( void *v, size_t n ) {\r
-    volatile unsigned char *p = v; while( n-- ) *p++ = 0;\r
-}\r
-\r
-/*\r
- * 32-bit integer manipulation macros (big endian)\r
- */\r
-#ifndef GET_UINT32_BE\r
-#define GET_UINT32_BE(n,b,i)                            \\r
-{                                                       \\r
-    (n) = ( (uint32_t) (b)[(i)    ] << 24 )             \\r
-        | ( (uint32_t) (b)[(i) + 1] << 16 )             \\r
-        | ( (uint32_t) (b)[(i) + 2] <<  8 )             \\r
-        | ( (uint32_t) (b)[(i) + 3]       );            \\r
-}\r
-#endif\r
-\r
-#ifndef PUT_UINT32_BE\r
-#define PUT_UINT32_BE(n,b,i)                            \\r
-{                                                       \\r
-    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \\r
-    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \\r
-    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \\r
-    (b)[(i) + 3] = (unsigned char) ( (n)       );       \\r
-}\r
-#endif\r
-\r
-void mbedtls_sha1_init( mbedtls_sha1_context *ctx )\r
-{\r
-    memset( ctx, 0, sizeof( mbedtls_sha1_context ) );\r
-}\r
-\r
-void mbedtls_sha1_free( mbedtls_sha1_context *ctx )\r
-{\r
-    if( ctx == NULL )\r
-        return;\r
-\r
-    mbedtls_zeroize( ctx, sizeof( mbedtls_sha1_context ) );\r
-}\r
-\r
-/*\r
- * SHA-1 context setup\r
- */\r
-void mbedtls_sha1_starts( mbedtls_sha1_context *ctx )\r
-{\r
-    ctx->total[0] = 0;\r
-    ctx->total[1] = 0;\r
-\r
-    ctx->state[0] = 0x67452301;\r
-    ctx->state[1] = 0xEFCDAB89;\r
-    ctx->state[2] = 0x98BADCFE;\r
-    ctx->state[3] = 0x10325476;\r
-    ctx->state[4] = 0xC3D2E1F0;\r
-}\r
-\r
-void mbedtls_sha1_process( mbedtls_sha1_context *ctx, const unsigned char data[SHA1_BLOCK_LENGTH] )\r
-{\r
-    uint32_t temp, W[16], A, B, C, D, E;\r
-\r
-    GET_UINT32_BE( W[ 0], data,  0 );\r
-    GET_UINT32_BE( W[ 1], data,  4 );\r
-    GET_UINT32_BE( W[ 2], data,  8 );\r
-    GET_UINT32_BE( W[ 3], data, 12 );\r
-    GET_UINT32_BE( W[ 4], data, 16 );\r
-    GET_UINT32_BE( W[ 5], data, 20 );\r
-    GET_UINT32_BE( W[ 6], data, 24 );\r
-    GET_UINT32_BE( W[ 7], data, 28 );\r
-    GET_UINT32_BE( W[ 8], data, 32 );\r
-    GET_UINT32_BE( W[ 9], data, 36 );\r
-    GET_UINT32_BE( W[10], data, 40 );\r
-    GET_UINT32_BE( W[11], data, 44 );\r
-    GET_UINT32_BE( W[12], data, 48 );\r
-    GET_UINT32_BE( W[13], data, 52 );\r
-    GET_UINT32_BE( W[14], data, 56 );\r
-    GET_UINT32_BE( W[15], data, 60 );\r
-\r
-#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))\r
-\r
-#define R(t)                                            \\r
-(                                                       \\r
-    temp = W[( t -  3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \\r
-           W[( t - 14 ) & 0x0F] ^ W[  t       & 0x0F],  \\r
-    ( W[t & 0x0F] = S(temp,1) )                         \\r
-)\r
-\r
-#define P(a,b,c,d,e,x)                                  \\r
-{                                                       \\r
-    e += S(a,5) + F(b,c,d) + K + x; b = S(b,30);        \\r
-}\r
-\r
-    A = ctx->state[0];\r
-    B = ctx->state[1];\r
-    C = ctx->state[2];\r
-    D = ctx->state[3];\r
-    E = ctx->state[4];\r
-\r
-#define F(x,y,z) (z ^ (x & (y ^ z)))\r
-#define K 0x5A827999\r
-\r
-    P( A, B, C, D, E, W[0]  );\r
-    P( E, A, B, C, D, W[1]  );\r
-    P( D, E, A, B, C, W[2]  );\r
-    P( C, D, E, A, B, W[3]  );\r
-    P( B, C, D, E, A, W[4]  );\r
-    P( A, B, C, D, E, W[5]  );\r
-    P( E, A, B, C, D, W[6]  );\r
-    P( D, E, A, B, C, W[7]  );\r
-    P( C, D, E, A, B, W[8]  );\r
-    P( B, C, D, E, A, W[9]  );\r
-    P( A, B, C, D, E, W[10] );\r
-    P( E, A, B, C, D, W[11] );\r
-    P( D, E, A, B, C, W[12] );\r
-    P( C, D, E, A, B, W[13] );\r
-    P( B, C, D, E, A, W[14] );\r
-    P( A, B, C, D, E, W[15] );\r
-    P( E, A, B, C, D, R(16) );\r
-    P( D, E, A, B, C, R(17) );\r
-    P( C, D, E, A, B, R(18) );\r
-    P( B, C, D, E, A, R(19) );\r
-\r
-#undef K\r
-#undef F\r
-\r
-#define F(x,y,z) (x ^ y ^ z)\r
-#define K 0x6ED9EBA1\r
-\r
-    P( A, B, C, D, E, R(20) );\r
-    P( E, A, B, C, D, R(21) );\r
-    P( D, E, A, B, C, R(22) );\r
-    P( C, D, E, A, B, R(23) );\r
-    P( B, C, D, E, A, R(24) );\r
-    P( A, B, C, D, E, R(25) );\r
-    P( E, A, B, C, D, R(26) );\r
-    P( D, E, A, B, C, R(27) );\r
-    P( C, D, E, A, B, R(28) );\r
-    P( B, C, D, E, A, R(29) );\r
-    P( A, B, C, D, E, R(30) );\r
-    P( E, A, B, C, D, R(31) );\r
-    P( D, E, A, B, C, R(32) );\r
-    P( C, D, E, A, B, R(33) );\r
-    P( B, C, D, E, A, R(34) );\r
-    P( A, B, C, D, E, R(35) );\r
-    P( E, A, B, C, D, R(36) );\r
-    P( D, E, A, B, C, R(37) );\r
-    P( C, D, E, A, B, R(38) );\r
-    P( B, C, D, E, A, R(39) );\r
-\r
-#undef K\r
-#undef F\r
-\r
-#define F(x,y,z) ((x & y) | (z & (x | y)))\r
-#define K 0x8F1BBCDC\r
-\r
-    P( A, B, C, D, E, R(40) );\r
-    P( E, A, B, C, D, R(41) );\r
-    P( D, E, A, B, C, R(42) );\r
-    P( C, D, E, A, B, R(43) );\r
-    P( B, C, D, E, A, R(44) );\r
-    P( A, B, C, D, E, R(45) );\r
-    P( E, A, B, C, D, R(46) );\r
-    P( D, E, A, B, C, R(47) );\r
-    P( C, D, E, A, B, R(48) );\r
-    P( B, C, D, E, A, R(49) );\r
-    P( A, B, C, D, E, R(50) );\r
-    P( E, A, B, C, D, R(51) );\r
-    P( D, E, A, B, C, R(52) );\r
-    P( C, D, E, A, B, R(53) );\r
-    P( B, C, D, E, A, R(54) );\r
-    P( A, B, C, D, E, R(55) );\r
-    P( E, A, B, C, D, R(56) );\r
-    P( D, E, A, B, C, R(57) );\r
-    P( C, D, E, A, B, R(58) );\r
-    P( B, C, D, E, A, R(59) );\r
-\r
-#undef K\r
-#undef F\r
-\r
-#define F(x,y,z) (x ^ y ^ z)\r
-#define K 0xCA62C1D6\r
-\r
-    P( A, B, C, D, E, R(60) );\r
-    P( E, A, B, C, D, R(61) );\r
-    P( D, E, A, B, C, R(62) );\r
-    P( C, D, E, A, B, R(63) );\r
-    P( B, C, D, E, A, R(64) );\r
-    P( A, B, C, D, E, R(65) );\r
-    P( E, A, B, C, D, R(66) );\r
-    P( D, E, A, B, C, R(67) );\r
-    P( C, D, E, A, B, R(68) );\r
-    P( B, C, D, E, A, R(69) );\r
-    P( A, B, C, D, E, R(70) );\r
-    P( E, A, B, C, D, R(71) );\r
-    P( D, E, A, B, C, R(72) );\r
-    P( C, D, E, A, B, R(73) );\r
-    P( B, C, D, E, A, R(74) );\r
-    P( A, B, C, D, E, R(75) );\r
-    P( E, A, B, C, D, R(76) );\r
-    P( D, E, A, B, C, R(77) );\r
-    P( C, D, E, A, B, R(78) );\r
-    P( B, C, D, E, A, R(79) );\r
-\r
-#undef K\r
-#undef F\r
-\r
-    ctx->state[0] += A;\r
-    ctx->state[1] += B;\r
-    ctx->state[2] += C;\r
-    ctx->state[3] += D;\r
-    ctx->state[4] += E;\r
-}\r
-\r
-/*\r
- * SHA-1 process buffer\r
- */\r
-void mbedtls_sha1_update( mbedtls_sha1_context *ctx, const unsigned char *input, size_t ilen )\r
-{\r
-    size_t fill;\r
-    uint32_t left;\r
-\r
-    if( ilen == 0 )\r
-        return;\r
-\r
-    left = ctx->total[0] & 0x3F;\r
-    fill = 64 - left;\r
-\r
-    ctx->total[0] += (uint32_t) ilen;\r
-    ctx->total[0] &= 0xFFFFFFFF;\r
-\r
-    if( ctx->total[0] < (uint32_t) ilen )\r
-        ctx->total[1]++;\r
-\r
-    if( left && ilen >= fill )\r
-    {\r
-        memcpy( (void *) (ctx->buffer + left), input, fill );\r
-        mbedtls_sha1_process( ctx, ctx->buffer );\r
-        input += fill;\r
-        ilen  -= fill;\r
-        left = 0;\r
-    }\r
-\r
-    while( ilen >= 64 )\r
-    {\r
-        mbedtls_sha1_process( ctx, input );\r
-        input += 64;\r
-        ilen  -= 64;\r
-    }\r
-\r
-    if( ilen > 0 )\r
-        memcpy( (void *) (ctx->buffer + left), input, ilen );\r
-}\r
-\r
-static const unsigned char sha1_padding[SHA1_BLOCK_LENGTH] =\r
-{\r
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
-};\r
-\r
-/*\r
- * SHA-1 final digest\r
- */\r
-void mbedtls_sha1_finish( mbedtls_sha1_context *ctx, unsigned char output[SHA1_DIGEST_LENGTH] )\r
-{\r
-    uint32_t last, padn;\r
-    uint32_t high, low;\r
-    unsigned char msglen[8];\r
-\r
-    high = ( ctx->total[0] >> 29 )\r
-         | ( ctx->total[1] <<  3 );\r
-    low  = ( ctx->total[0] <<  3 );\r
-\r
-    PUT_UINT32_BE( high, msglen, 0 );\r
-    PUT_UINT32_BE( low,  msglen, 4 );\r
-\r
-    last = ctx->total[0] & 0x3F;\r
-    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );\r
-\r
-    mbedtls_sha1_update( ctx, sha1_padding, padn );\r
-    mbedtls_sha1_update( ctx, msglen, 8 );\r
-\r
-    PUT_UINT32_BE( ctx->state[0], output,  0 );\r
-    PUT_UINT32_BE( ctx->state[1], output,  4 );\r
-    PUT_UINT32_BE( ctx->state[2], output,  8 );\r
-    PUT_UINT32_BE( ctx->state[3], output, 12 );\r
-    PUT_UINT32_BE( ctx->state[4], output, 16 );\r
-}\r
-\r
-/*\r
- * output = SHA-1( input buffer )\r
- */\r
-void mbedtls_sha1( const unsigned char *input, size_t ilen, unsigned char output[SHA1_DIGEST_LENGTH] )\r
-{\r
-    mbedtls_sha1_context ctx;\r
-\r
-    mbedtls_sha1_init( &ctx );\r
-    mbedtls_sha1_starts( &ctx );\r
-    mbedtls_sha1_update( &ctx, input, ilen );\r
-    mbedtls_sha1_finish( &ctx, output );\r
-    mbedtls_sha1_free( &ctx );\r
-}\r
-\r
-/*\r
-* Compute HMAC_SHA1 using key, key length, text to hash, size of the text, and output buffer\r
-*/\r
-void HMAC_SHA1(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t out[SHA1_DIGEST_LENGTH]){\r
-\r
-  uint8_t i;\r
-  uint8_t k_ipad[SHA1_BLOCK_LENGTH]; /* inner padding - key XORd with ipad */\r
-  uint8_t k_opad[SHA1_BLOCK_LENGTH]; /* outer padding - key XORd with opad */\r
-  uint8_t buffer[SHA1_BLOCK_LENGTH + SHA1_DIGEST_LENGTH];\r
-\r
-  /* start out by storing key in pads */\r
-  memset(k_ipad, 0, sizeof(k_ipad));\r
-  memset(k_opad, 0, sizeof(k_opad));\r
-\r
-  if (key_length <= SHA1_BLOCK_LENGTH) {\r
-      memcpy(k_ipad, key, key_length);\r
-      memcpy(k_opad, key, key_length);\r
-  }\r
-\r
-  else {\r
-      mbedtls_sha1(key, key_length, k_ipad);\r
-      memcpy(k_opad, k_ipad, SHA1_BLOCK_LENGTH);\r
-  }\r
-\r
-  /* XOR key with ipad and opad values */\r
-  for (i = 0; i < SHA1_BLOCK_LENGTH; i++) {\r
-      k_ipad[i] ^= HMAC_IPAD;\r
-      k_opad[i] ^= HMAC_OPAD;\r
-  }\r
-  \r
-  // perform inner SHA1\r
-  memcpy(buffer, k_ipad, SHA1_BLOCK_LENGTH);\r
-  memcpy(buffer + SHA1_BLOCK_LENGTH, in, n);\r
-  mbedtls_sha1(buffer, SHA1_BLOCK_LENGTH + n, out);\r
-  \r
-  memset(buffer, 0, SHA1_BLOCK_LENGTH + n);\r
-\r
-  // perform outer SHA1\r
-  memcpy(buffer, k_opad, SHA1_BLOCK_LENGTH);\r
-  memcpy(buffer + SHA1_BLOCK_LENGTH, out, SHA1_DIGEST_LENGTH);\r
-  mbedtls_sha1(buffer, SHA1_BLOCK_LENGTH + SHA1_DIGEST_LENGTH, out);\r
-}\r
-/*\r
-* Compute TOTP_HMAC_SHA1 using key, key length, text to hash, size of the text\r
-*/\r
-uint32_t TOTP_HMAC_SHA1(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n){\r
-    // STEP 1, get the HMAC-SHA1 hash from counter and key\r
-    uint8_t hash[SHA1_DIGEST_LENGTH];\r
-    HMAC_SHA1(key, key_length, in, n, hash);\r
-\r
-    // STEP 2, apply dynamic truncation to obtain a 4-bytes string\r
-    uint32_t truncated_hash = 0;\r
-    uint8_t _offset = hash[SHA1_DIGEST_LENGTH - 1] & 0xF;\r
-    uint8_t j;\r
-    for (j = 0; j < 4; ++j) {\r
-        truncated_hash <<= 8;\r
-        truncated_hash  |= hash[_offset + j];\r
-    }\r
-\r
-    // STEP 3, compute the OTP value\r
-    truncated_hash &= 0x7FFFFFFF;    //Disabled\r
-    truncated_hash %= 1000000;\r
-\r
-    return truncated_hash;\r
-}\r
diff --git a/movement/lib/TOTP/sha1.h b/movement/lib/TOTP/sha1.h
deleted file mode 100644 (file)
index 2743c5a..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/**\r
- * \file sha1.h\r
- *\r
- * \brief SHA-1 cryptographic hash function\r
- *\r
- *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
- *  SPDX-License-Identifier: Apache-2.0\r
- *\r
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
- *  not use this file except in compliance with the License.\r
- *  You may obtain a copy of the License at\r
- *\r
- *  http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- *  Unless required by applicable law or agreed to in writing, software\r
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- *  See the License for the specific language governing permissions and\r
- *  limitations under the License.\r
- *\r
- *  This file is part of mbed TLS (https://tls.mbed.org)\r
- */\r
-#ifndef MBEDTLS_SHA1_H\r
-#define MBEDTLS_SHA1_H\r
-\r
-#define SHA1_DIGEST_LENGTH 20\r
-#define SHA1_BLOCK_LENGTH 64\r
-#define HMAC_IPAD 0x36\r
-#define HMAC_OPAD 0x5c\r
-\r
-#include <stddef.h>\r
-#include <stdint.h>\r
-\r
-/**\r
- * \brief          SHA-1 context structure\r
- */\r
-typedef struct\r
-{\r
-    uint32_t total[2];          /*!< number of bytes processed  */\r
-    uint32_t state[5];          /*!< intermediate digest state  */\r
-    unsigned char buffer[SHA1_BLOCK_LENGTH];   /*!< data block being processed */\r
-}\r
-mbedtls_sha1_context;\r
-\r
-/**\r
- * \brief          Initialize SHA-1 context\r
- *\r
- * \param ctx      SHA-1 context to be initialized\r
- */\r
-void mbedtls_sha1_init( mbedtls_sha1_context *ctx );\r
-\r
-/**\r
- * \brief          Clear SHA-1 context\r
- *\r
- * \param ctx      SHA-1 context to be cleared\r
- */\r
-void mbedtls_sha1_free( mbedtls_sha1_context *ctx );\r
-\r
-/**\r
- * \brief          SHA-1 context setup\r
- *\r
- * \param ctx      context to be initialized\r
- */\r
-void mbedtls_sha1_starts( mbedtls_sha1_context *ctx );\r
-\r
-/**\r
- * \brief          SHA-1 process buffer\r
- *\r
- * \param ctx      SHA-1 context\r
- * \param input    buffer holding the  data\r
- * \param ilen     length of the input data\r
- */\r
-void mbedtls_sha1_update( mbedtls_sha1_context *ctx, const unsigned char *input, size_t ilen );\r
-\r
-/**\r
- * \brief          SHA-1 final digest\r
- *\r
- * \param ctx      SHA-1 context\r
- * \param output   SHA-1 checksum result\r
- */\r
-void mbedtls_sha1_finish( mbedtls_sha1_context *ctx, unsigned char output[SHA1_DIGEST_LENGTH] );\r
-\r
-/* Internal use */\r
-void mbedtls_sha1_process( mbedtls_sha1_context *ctx, const unsigned char data[SHA1_BLOCK_LENGTH] );\r
-\r
-/**\r
- * \brief          Output = SHA-1( input buffer )\r
- *\r
- * \param input    buffer holding the  data\r
- * \param ilen     length of the input data\r
- * \param output   SHA-1 checksum result\r
- */\r
-void mbedtls_sha1( const unsigned char *input, size_t ilen, unsigned char output[SHA1_DIGEST_LENGTH] );\r
-void HMAC_SHA1(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t out[SHA1_DIGEST_LENGTH]);\r
-uint32_t TOTP_HMAC_SHA1(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n);\r
-\r
-\r
-#endif /* mbedtls_sha1.h */\r
diff --git a/movement/lib/TOTP/sha256.c b/movement/lib/TOTP/sha256.c
deleted file mode 100644 (file)
index cb83f53..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-/*\r
- *  FIPS-180-2 compliant SHA-256 implementation\r
- *\r
- *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
- *  SPDX-License-Identifier: Apache-2.0\r
- *\r
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
- *  not use this file except in compliance with the License.\r
- *  You may obtain a copy of the License at\r
- *\r
- *  http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- *  Unless required by applicable law or agreed to in writing, software\r
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- *  See the License for the specific language governing permissions and\r
- *  limitations under the License.\r
- *\r
- *  This file is part of mbed TLS (https://tls.mbed.org)\r
- */\r
-/*\r
- *  The SHA-256 Secure Hash Standard was published by NIST in 2002.\r
- *\r
- *  http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf\r
- */\r
-\r
-#include "sha256.h"\r
-\r
-#include <string.h>\r
-#include <stdio.h>\r
-\r
-/* Implementation that should never be optimized out by the compiler */\r
-static void mbedtls_zeroize( void *v, size_t n ) {\r
-    volatile unsigned char *p = v; while( n-- ) *p++ = 0;\r
-}\r
-\r
-/*\r
- * 32-bit integer manipulation macros (big endian)\r
- */\r
-#ifndef GET_UINT32_BE\r
-#define GET_UINT32_BE(n,b,i)                            \\r
-do {                                                    \\r
-    (n) = ( (uint32_t) (b)[(i)    ] << 24 )             \\r
-        | ( (uint32_t) (b)[(i) + 1] << 16 )             \\r
-        | ( (uint32_t) (b)[(i) + 2] <<  8 )             \\r
-        | ( (uint32_t) (b)[(i) + 3]       );            \\r
-} while( 0 )\r
-#endif\r
-\r
-#ifndef PUT_UINT32_BE\r
-#define PUT_UINT32_BE(n,b,i)                            \\r
-do {                                                    \\r
-    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \\r
-    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \\r
-    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \\r
-    (b)[(i) + 3] = (unsigned char) ( (n)       );       \\r
-} while( 0 )\r
-#endif\r
-\r
-void mbedtls_sha256_init( mbedtls_sha256_context *ctx )\r
-{\r
-    memset( ctx, 0, sizeof( mbedtls_sha256_context ) );\r
-}\r
-\r
-void mbedtls_sha256_free( mbedtls_sha256_context *ctx )\r
-{\r
-    if( ctx == NULL )\r
-        return;\r
-\r
-    mbedtls_zeroize( ctx, sizeof( mbedtls_sha256_context ) );\r
-}\r
-\r
-void mbedtls_sha256_clone( mbedtls_sha256_context *dst,\r
-                           const mbedtls_sha256_context *src )\r
-{\r
-    *dst = *src;\r
-}\r
-\r
-/*\r
- * SHA-256 context setup\r
- */\r
-void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, int is224 )\r
-{\r
-    ctx->total[0] = 0;\r
-    ctx->total[1] = 0;\r
-\r
-    if( is224 == 0 )\r
-    {\r
-        /* SHA-256 */\r
-        ctx->state[0] = 0x6A09E667;\r
-        ctx->state[1] = 0xBB67AE85;\r
-        ctx->state[2] = 0x3C6EF372;\r
-        ctx->state[3] = 0xA54FF53A;\r
-        ctx->state[4] = 0x510E527F;\r
-        ctx->state[5] = 0x9B05688C;\r
-        ctx->state[6] = 0x1F83D9AB;\r
-        ctx->state[7] = 0x5BE0CD19;\r
-    }\r
-    else\r
-    {\r
-        /* SHA-224 */\r
-        ctx->state[0] = 0xC1059ED8;\r
-        ctx->state[1] = 0x367CD507;\r
-        ctx->state[2] = 0x3070DD17;\r
-        ctx->state[3] = 0xF70E5939;\r
-        ctx->state[4] = 0xFFC00B31;\r
-        ctx->state[5] = 0x68581511;\r
-        ctx->state[6] = 0x64F98FA7;\r
-        ctx->state[7] = 0xBEFA4FA4;\r
-    }\r
-\r
-    ctx->is224 = is224;\r
-}\r
-\r
-static const uint32_t K[] =\r
-{\r
-    0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,\r
-    0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,\r
-    0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,\r
-    0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,\r
-    0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,\r
-    0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,\r
-    0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,\r
-    0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,\r
-    0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,\r
-    0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,\r
-    0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,\r
-    0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,\r
-    0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,\r
-    0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,\r
-    0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,\r
-    0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,\r
-};\r
-\r
-#define SHR(x,n) ((x & 0xFFFFFFFF) >> n)\r
-#define ROTR(x,n) (SHR(x,n) | (x << (32 - n)))\r
-\r
-#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^  SHR(x, 3))\r
-#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^  SHR(x,10))\r
-\r
-#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22))\r
-#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25))\r
-\r
-#define F0(x,y,z) ((x & y) | (z & (x | y)))\r
-#define F1(x,y,z) (z ^ (x & (y ^ z)))\r
-\r
-#define R(t)                                    \\r
-(                                               \\r
-    W[t] = S1(W[t -  2]) + W[t -  7] +          \\r
-           S0(W[t - 15]) + W[t - 16]            \\r
-)\r
-\r
-#define P(a,b,c,d,e,f,g,h,x,K)                  \\r
-{                                               \\r
-    temp1 = h + S3(e) + F1(e,f,g) + K + x;      \\r
-    temp2 = S2(a) + F0(a,b,c);                  \\r
-    d += temp1; h = temp1 + temp2;              \\r
-}\r
-\r
-void mbedtls_sha256_process( mbedtls_sha256_context *ctx, const unsigned char data[SHA256_BLOCK_LENGTH] )\r
-{\r
-    uint32_t temp1, temp2, W[64];\r
-    uint32_t A[8];\r
-    unsigned int i;\r
-\r
-    for( i = 0; i < 8; i++ )\r
-        A[i] = ctx->state[i];\r
-\r
-    for( i = 0; i < 16; i++ )\r
-        GET_UINT32_BE( W[i], data, 4 * i );\r
-\r
-    for( i = 0; i < 16; i += 8 )\r
-    {\r
-        P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i+0], K[i+0] );\r
-        P( A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i+1], K[i+1] );\r
-        P( A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i+2], K[i+2] );\r
-        P( A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i+3], K[i+3] );\r
-        P( A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i+4], K[i+4] );\r
-        P( A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i+5], K[i+5] );\r
-        P( A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i+6], K[i+6] );\r
-        P( A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i+7], K[i+7] );\r
-    }\r
-\r
-    for( i = 16; i < 64; i += 8 )\r
-    {\r
-        P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], R(i+0), K[i+0] );\r
-        P( A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], R(i+1), K[i+1] );\r
-        P( A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], R(i+2), K[i+2] );\r
-        P( A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], R(i+3), K[i+3] );\r
-        P( A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], R(i+4), K[i+4] );\r
-        P( A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], R(i+5), K[i+5] );\r
-        P( A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], R(i+6), K[i+6] );\r
-        P( A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], R(i+7), K[i+7] );\r
-    }\r
-\r
-    for( i = 0; i < 8; i++ )\r
-        ctx->state[i] += A[i];\r
-}\r
-\r
-/*\r
- * SHA-256 process buffer\r
- */\r
-void mbedtls_sha256_update( mbedtls_sha256_context *ctx, const unsigned char *input,\r
-                    size_t ilen )\r
-{\r
-    size_t fill;\r
-    uint32_t left;\r
-\r
-    if( ilen == 0 )\r
-        return;\r
-\r
-    left = ctx->total[0] & 0x3F;\r
-    fill = 64 - left;\r
-\r
-    ctx->total[0] += (uint32_t) ilen;\r
-    ctx->total[0] &= 0xFFFFFFFF;\r
-\r
-    if( ctx->total[0] < (uint32_t) ilen )\r
-        ctx->total[1]++;\r
-\r
-    if( left && ilen >= fill )\r
-    {\r
-        memcpy( (void *) (ctx->buffer + left), input, fill );\r
-        mbedtls_sha256_process( ctx, ctx->buffer );\r
-        input += fill;\r
-        ilen  -= fill;\r
-        left = 0;\r
-    }\r
-\r
-    while( ilen >= 64 )\r
-    {\r
-        mbedtls_sha256_process( ctx, input );\r
-        input += 64;\r
-        ilen  -= 64;\r
-    }\r
-\r
-    if( ilen > 0 )\r
-        memcpy( (void *) (ctx->buffer + left), input, ilen );\r
-}\r
-\r
-static const unsigned char sha256_padding[SHA256_BLOCK_LENGTH] =\r
-{\r
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
-};\r
-\r
-/*\r
- * SHA-256 final digest\r
- */\r
-void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, unsigned char* output )\r
-{\r
-    uint32_t last, padn;\r
-    uint32_t high, low;\r
-    unsigned char msglen[8];\r
-\r
-    high = ( ctx->total[0] >> 29 )\r
-         | ( ctx->total[1] <<  3 );\r
-    low  = ( ctx->total[0] <<  3 );\r
-\r
-    PUT_UINT32_BE( high, msglen, 0 );\r
-    PUT_UINT32_BE( low,  msglen, 4 );\r
-\r
-    last = ctx->total[0] & 0x3F;\r
-    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );\r
-\r
-    mbedtls_sha256_update( ctx, sha256_padding, padn );\r
-    mbedtls_sha256_update( ctx, msglen, 8 );\r
-\r
-    PUT_UINT32_BE( ctx->state[0], output,  0 );\r
-    PUT_UINT32_BE( ctx->state[1], output,  4 );\r
-    PUT_UINT32_BE( ctx->state[2], output,  8 );\r
-    PUT_UINT32_BE( ctx->state[3], output, 12 );\r
-    PUT_UINT32_BE( ctx->state[4], output, 16 );\r
-    PUT_UINT32_BE( ctx->state[5], output, 20 );\r
-    PUT_UINT32_BE( ctx->state[6], output, 24 );\r
-\r
-    if( ctx->is224 == 0 )\r
-        PUT_UINT32_BE( ctx->state[7], output, 28 );\r
-}\r
-\r
-/*\r
- * output = SHA-256( input buffer )\r
- */\r
-void mbedtls_sha256( const unsigned char *input, size_t ilen,\r
-             unsigned char* output, int is224 )\r
-{\r
-    mbedtls_sha256_context ctx;\r
-\r
-    mbedtls_sha256_init( &ctx );\r
-    mbedtls_sha256_starts( &ctx, is224 );\r
-    mbedtls_sha256_update( &ctx, input, ilen );\r
-    mbedtls_sha256_finish( &ctx, output );\r
-    mbedtls_sha256_free( &ctx );\r
-}\r
-\r
-/*\r
-* Compute HMAC_SHA224/256 using key, key length, text to hash, size of the text, output buffer and a switch for SHA224\r
-*/\r
-void HMAC_SHA256(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t* out, int is224){\r
-  int digest_length = SHA256_DIGEST_LENGTH;\r
-  if (is224 == 1) {\r
-    digest_length = SHA224_DIGEST_LENGTH;\r
-  }\r
-  \r
-  uint8_t i;\r
-  uint8_t k_ipad[SHA256_BLOCK_LENGTH]; /* inner padding - key XORd with ipad */\r
-  uint8_t k_opad[SHA256_BLOCK_LENGTH]; /* outer padding - key XORd with opad */\r
-  uint8_t buffer[SHA256_BLOCK_LENGTH + digest_length];\r
-\r
-  /* start out by storing key in pads */\r
-  memset(k_ipad, 0, sizeof(k_ipad));\r
-  memset(k_opad, 0, sizeof(k_opad));\r
-\r
-  if (key_length <= SHA256_BLOCK_LENGTH) {\r
-      memcpy(k_ipad, key, key_length);\r
-      memcpy(k_opad, key, key_length);\r
-  }\r
-\r
-  else {\r
-      mbedtls_sha256(key, key_length, k_ipad, is224);\r
-      memcpy(k_opad, k_ipad, SHA256_BLOCK_LENGTH);\r
-  }\r
-\r
-  /* XOR key with ipad and opad values */\r
-  for (i = 0; i < SHA256_BLOCK_LENGTH; i++) {\r
-      k_ipad[i] ^= HMAC_IPAD;\r
-      k_opad[i] ^= HMAC_OPAD;\r
-  }\r
-  \r
-  // perform inner SHA256\r
-  memcpy(buffer, k_ipad, SHA256_BLOCK_LENGTH);\r
-  memcpy(buffer + SHA256_BLOCK_LENGTH, in, n);\r
-  mbedtls_sha256(buffer, SHA256_BLOCK_LENGTH + n, out, is224);\r
-  \r
-  memset(buffer, 0, SHA256_BLOCK_LENGTH + n);\r
-\r
-  // perform outer SHA256\r
-  memcpy(buffer, k_opad, SHA256_BLOCK_LENGTH);\r
-  memcpy(buffer + SHA256_BLOCK_LENGTH, out, digest_length);\r
-  mbedtls_sha256(buffer, SHA256_BLOCK_LENGTH + digest_length, out, is224);\r
-}\r
-\r
-/*\r
-* Compute TOTP_HMAC_SHA224/256 using key, key length, text to hash, size of the text and a switch for SHA224\r
-*/\r
-uint32_t TOTP_HMAC_SHA256(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, int is224){\r
-    int digest_length = SHA256_DIGEST_LENGTH;\r
-    if (is224 == 1) {\r
-      digest_length = SHA224_DIGEST_LENGTH;\r
-    }\r
-\r
-    // STEP 1, get the HMAC-SHA256 hash from counter and key\r
-    uint8_t hash[digest_length];\r
-    HMAC_SHA256(key, key_length, in, n, hash, is224);\r
-\r
-    // STEP 2, apply dynamic truncation to obtain a 4-bytes string\r
-    uint32_t truncated_hash = 0;\r
-    uint8_t _offset = hash[digest_length - 1] & 0xF;\r
-    uint8_t j;\r
-    for (j = 0; j < 4; ++j) {\r
-        truncated_hash <<= 8;\r
-        truncated_hash  |= hash[_offset + j];\r
-    }\r
-\r
-    // STEP 3, compute the OTP value\r
-    truncated_hash &= 0x7FFFFFFF;    //Disabled\r
-    truncated_hash %= 1000000;\r
-\r
-    return truncated_hash;\r
-}
\ No newline at end of file
diff --git a/movement/lib/TOTP/sha256.h b/movement/lib/TOTP/sha256.h
deleted file mode 100644 (file)
index 2f0febc..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/**\r
- * \file sha256.h\r
- *\r
- * \brief SHA-224 and SHA-256 cryptographic hash function\r
- *\r
- *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
- *  SPDX-License-Identifier: Apache-2.0\r
- *\r
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
- *  not use this file except in compliance with the License.\r
- *  You may obtain a copy of the License at\r
- *\r
- *  http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- *  Unless required by applicable law or agreed to in writing, software\r
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- *  See the License for the specific language governing permissions and\r
- *  limitations under the License.\r
- *\r
- *  This file is part of mbed TLS (https://tls.mbed.org)\r
- */\r
-#ifndef MBEDTLS_SHA256_H\r
-#define MBEDTLS_SHA256_H\r
-\r
-#define SHA224_DIGEST_LENGTH 28\r
-#define SHA256_DIGEST_LENGTH 32\r
-#define SHA256_BLOCK_LENGTH 64\r
-#define HMAC_IPAD 0x36\r
-#define HMAC_OPAD 0x5c\r
-\r
-#include <stddef.h>\r
-#include <stdint.h>\r
-\r
-/**\r
- * \brief          SHA-256 context structure\r
- */\r
-typedef struct\r
-{\r
-    uint32_t total[2];          /*!< number of bytes processed  */\r
-    uint32_t state[8];          /*!< intermediate digest state  */\r
-    unsigned char buffer[SHA256_BLOCK_LENGTH];   /*!< data block being processed */\r
-    int is224;                  /*!< 0 => SHA-256, else SHA-224 */\r
-}\r
-mbedtls_sha256_context;\r
-\r
-/**\r
- * \brief          Initialize SHA-256 context\r
- *\r
- * \param ctx      SHA-256 context to be initialized\r
- */\r
-void mbedtls_sha256_init( mbedtls_sha256_context *ctx );\r
-\r
-/**\r
- * \brief          Clear SHA-256 context\r
- *\r
- * \param ctx      SHA-256 context to be cleared\r
- */\r
-void mbedtls_sha256_free( mbedtls_sha256_context *ctx );\r
-\r
-/**\r
- * \brief          Clone (the state of) a SHA-256 context\r
- *\r
- * \param dst      The destination context\r
- * \param src      The context to be cloned\r
- */\r
-void mbedtls_sha256_clone( mbedtls_sha256_context *dst,\r
-                           const mbedtls_sha256_context *src );\r
-\r
-/**\r
- * \brief          SHA-256 context setup\r
- *\r
- * \param ctx      context to be initialized\r
- * \param is224    0 = use SHA256, 1 = use SHA224\r
- */\r
-void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, int is224 );\r
-\r
-/**\r
- * \brief          SHA-256 process buffer\r
- *\r
- * \param ctx      SHA-256 context\r
- * \param input    buffer holding the  data\r
- * \param ilen     length of the input data\r
- */\r
-void mbedtls_sha256_update( mbedtls_sha256_context *ctx, const unsigned char *input,\r
-                    size_t ilen );\r
-\r
-/**\r
- * \brief          SHA-256 final digest\r
- *\r
- * \param ctx      SHA-256 context\r
- * \param output   SHA-224/256 checksum result\r
- */\r
-void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, unsigned char* output );\r
-\r
-/* Internal use */\r
-void mbedtls_sha256_process( mbedtls_sha256_context *ctx, const unsigned char data[SHA256_BLOCK_LENGTH] );\r
-\r
-/**\r
- * \brief          Output = SHA-256( input buffer )\r
- *\r
- * \param input    buffer holding the  data\r
- * \param ilen     length of the input data\r
- * \param output   SHA-224/256 checksum result\r
- * \param is224    0 = use SHA256, 1 = use SHA224\r
- */\r
-void mbedtls_sha256( const unsigned char *input, size_t ilen,\r
-           unsigned char* output, int is224 );\r
-void HMAC_SHA256(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t* out, int is224);\r
-uint32_t TOTP_HMAC_SHA256(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, int is224);\r
-\r
-#endif /* mbedtls_sha256.h */\r
diff --git a/movement/lib/TOTP/sha512.c b/movement/lib/TOTP/sha512.c
deleted file mode 100644 (file)
index d35462b..0000000
+++ /dev/null
@@ -1,422 +0,0 @@
-/*\r
- *  FIPS-180-2 compliant SHA-384/512 implementation\r
- *\r
- *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
- *  SPDX-License-Identifier: Apache-2.0\r
- *\r
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
- *  not use this file except in compliance with the License.\r
- *  You may obtain a copy of the License at\r
- *\r
- *  http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- *  Unless required by applicable law or agreed to in writing, software\r
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- *  See the License for the specific language governing permissions and\r
- *  limitations under the License.\r
- *\r
- *  This file is part of mbed TLS (https://tls.mbed.org)\r
- */\r
-/*\r
- *  The SHA-512 Secure Hash Standard was published by NIST in 2002.\r
- *\r
- *  http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf\r
- */\r
-\r
-#include "sha512.h"\r
-\r
-#include <string.h>\r
-#include <stdio.h>\r
-\r
-#if defined(_MSC_VER) || defined(__WATCOMC__)\r
-  #define UL64(x) x##ui64\r
-#else\r
-  #define UL64(x) x##ULL\r
-#endif\r
-\r
-/* Implementation that should never be optimized out by the compiler */\r
-static void mbedtls_zeroize( void *v, size_t n ) {\r
-    volatile unsigned char *p = v; while( n-- ) *p++ = 0;\r
-}\r
-\r
-/*\r
- * 64-bit integer manipulation macros (big endian)\r
- */\r
-#ifndef GET_UINT64_BE\r
-#define GET_UINT64_BE(n,b,i)                            \\r
-{                                                       \\r
-    (n) = ( (uint64_t) (b)[(i)    ] << 56 )       \\r
-        | ( (uint64_t) (b)[(i) + 1] << 48 )       \\r
-        | ( (uint64_t) (b)[(i) + 2] << 40 )       \\r
-        | ( (uint64_t) (b)[(i) + 3] << 32 )       \\r
-        | ( (uint64_t) (b)[(i) + 4] << 24 )       \\r
-        | ( (uint64_t) (b)[(i) + 5] << 16 )       \\r
-        | ( (uint64_t) (b)[(i) + 6] <<  8 )       \\r
-        | ( (uint64_t) (b)[(i) + 7]       );      \\r
-}\r
-#endif /* GET_UINT64_BE */\r
-\r
-#ifndef PUT_UINT64_BE\r
-#define PUT_UINT64_BE(n,b,i)                            \\r
-{                                                       \\r
-    (b)[(i)    ] = (unsigned char) ( (n) >> 56 );       \\r
-    (b)[(i) + 1] = (unsigned char) ( (n) >> 48 );       \\r
-    (b)[(i) + 2] = (unsigned char) ( (n) >> 40 );       \\r
-    (b)[(i) + 3] = (unsigned char) ( (n) >> 32 );       \\r
-    (b)[(i) + 4] = (unsigned char) ( (n) >> 24 );       \\r
-    (b)[(i) + 5] = (unsigned char) ( (n) >> 16 );       \\r
-    (b)[(i) + 6] = (unsigned char) ( (n) >>  8 );       \\r
-    (b)[(i) + 7] = (unsigned char) ( (n)       );       \\r
-}\r
-#endif /* PUT_UINT64_BE */\r
-\r
-/*\r
- * Round constants\r
- */\r
-static const uint64_t K[80] =\r
-{\r
-    UL64(0x428A2F98D728AE22),  UL64(0x7137449123EF65CD),\r
-    UL64(0xB5C0FBCFEC4D3B2F),  UL64(0xE9B5DBA58189DBBC),\r
-    UL64(0x3956C25BF348B538),  UL64(0x59F111F1B605D019),\r
-    UL64(0x923F82A4AF194F9B),  UL64(0xAB1C5ED5DA6D8118),\r
-    UL64(0xD807AA98A3030242),  UL64(0x12835B0145706FBE),\r
-    UL64(0x243185BE4EE4B28C),  UL64(0x550C7DC3D5FFB4E2),\r
-    UL64(0x72BE5D74F27B896F),  UL64(0x80DEB1FE3B1696B1),\r
-    UL64(0x9BDC06A725C71235),  UL64(0xC19BF174CF692694),\r
-    UL64(0xE49B69C19EF14AD2),  UL64(0xEFBE4786384F25E3),\r
-    UL64(0x0FC19DC68B8CD5B5),  UL64(0x240CA1CC77AC9C65),\r
-    UL64(0x2DE92C6F592B0275),  UL64(0x4A7484AA6EA6E483),\r
-    UL64(0x5CB0A9DCBD41FBD4),  UL64(0x76F988DA831153B5),\r
-    UL64(0x983E5152EE66DFAB),  UL64(0xA831C66D2DB43210),\r
-    UL64(0xB00327C898FB213F),  UL64(0xBF597FC7BEEF0EE4),\r
-    UL64(0xC6E00BF33DA88FC2),  UL64(0xD5A79147930AA725),\r
-    UL64(0x06CA6351E003826F),  UL64(0x142929670A0E6E70),\r
-    UL64(0x27B70A8546D22FFC),  UL64(0x2E1B21385C26C926),\r
-    UL64(0x4D2C6DFC5AC42AED),  UL64(0x53380D139D95B3DF),\r
-    UL64(0x650A73548BAF63DE),  UL64(0x766A0ABB3C77B2A8),\r
-    UL64(0x81C2C92E47EDAEE6),  UL64(0x92722C851482353B),\r
-    UL64(0xA2BFE8A14CF10364),  UL64(0xA81A664BBC423001),\r
-    UL64(0xC24B8B70D0F89791),  UL64(0xC76C51A30654BE30),\r
-    UL64(0xD192E819D6EF5218),  UL64(0xD69906245565A910),\r
-    UL64(0xF40E35855771202A),  UL64(0x106AA07032BBD1B8),\r
-    UL64(0x19A4C116B8D2D0C8),  UL64(0x1E376C085141AB53),\r
-    UL64(0x2748774CDF8EEB99),  UL64(0x34B0BCB5E19B48A8),\r
-    UL64(0x391C0CB3C5C95A63),  UL64(0x4ED8AA4AE3418ACB),\r
-    UL64(0x5B9CCA4F7763E373),  UL64(0x682E6FF3D6B2B8A3),\r
-    UL64(0x748F82EE5DEFB2FC),  UL64(0x78A5636F43172F60),\r
-    UL64(0x84C87814A1F0AB72),  UL64(0x8CC702081A6439EC),\r
-    UL64(0x90BEFFFA23631E28),  UL64(0xA4506CEBDE82BDE9),\r
-    UL64(0xBEF9A3F7B2C67915),  UL64(0xC67178F2E372532B),\r
-    UL64(0xCA273ECEEA26619C),  UL64(0xD186B8C721C0C207),\r
-    UL64(0xEADA7DD6CDE0EB1E),  UL64(0xF57D4F7FEE6ED178),\r
-    UL64(0x06F067AA72176FBA),  UL64(0x0A637DC5A2C898A6),\r
-    UL64(0x113F9804BEF90DAE),  UL64(0x1B710B35131C471B),\r
-    UL64(0x28DB77F523047D84),  UL64(0x32CAAB7B40C72493),\r
-    UL64(0x3C9EBE0A15C9BEBC),  UL64(0x431D67C49C100D4C),\r
-    UL64(0x4CC5D4BECB3E42B6),  UL64(0x597F299CFC657E2A),\r
-    UL64(0x5FCB6FAB3AD6FAEC),  UL64(0x6C44198C4A475817)\r
-};\r
-\r
-void mbedtls_sha512_init( mbedtls_sha512_context *ctx )\r
-{\r
-    memset( ctx, 0, sizeof( mbedtls_sha512_context ) );\r
-}\r
-\r
-void mbedtls_sha512_free( mbedtls_sha512_context *ctx )\r
-{\r
-    if( ctx == NULL )\r
-        return;\r
-\r
-    mbedtls_zeroize( ctx, sizeof( mbedtls_sha512_context ) );\r
-}\r
-\r
-void mbedtls_sha512_clone( mbedtls_sha512_context *dst,\r
-                           const mbedtls_sha512_context *src )\r
-{\r
-    *dst = *src;\r
-}\r
-\r
-/*\r
- * SHA-512 context setup\r
- */\r
-void mbedtls_sha512_starts( mbedtls_sha512_context *ctx, int is384 )\r
-{\r
-    ctx->total[0] = 0;\r
-    ctx->total[1] = 0;\r
-\r
-    if( is384 == 0 )\r
-    {\r
-        /* SHA-512 */\r
-        ctx->state[0] = UL64(0x6A09E667F3BCC908);\r
-        ctx->state[1] = UL64(0xBB67AE8584CAA73B);\r
-        ctx->state[2] = UL64(0x3C6EF372FE94F82B);\r
-        ctx->state[3] = UL64(0xA54FF53A5F1D36F1);\r
-        ctx->state[4] = UL64(0x510E527FADE682D1);\r
-        ctx->state[5] = UL64(0x9B05688C2B3E6C1F);\r
-        ctx->state[6] = UL64(0x1F83D9ABFB41BD6B);\r
-        ctx->state[7] = UL64(0x5BE0CD19137E2179);\r
-    }\r
-    else\r
-    {\r
-        /* SHA-384 */\r
-        ctx->state[0] = UL64(0xCBBB9D5DC1059ED8);\r
-        ctx->state[1] = UL64(0x629A292A367CD507);\r
-        ctx->state[2] = UL64(0x9159015A3070DD17);\r
-        ctx->state[3] = UL64(0x152FECD8F70E5939);\r
-        ctx->state[4] = UL64(0x67332667FFC00B31);\r
-        ctx->state[5] = UL64(0x8EB44A8768581511);\r
-        ctx->state[6] = UL64(0xDB0C2E0D64F98FA7);\r
-        ctx->state[7] = UL64(0x47B5481DBEFA4FA4);\r
-    }\r
-\r
-    ctx->is384 = is384;\r
-}\r
-\r
-void mbedtls_sha512_process( mbedtls_sha512_context *ctx, const unsigned char data[SHA512_BLOCK_LENGTH] )\r
-{\r
-    int i;\r
-    uint64_t temp1, temp2, W[80];\r
-    uint64_t A, B, C, D, E, F, G, H;\r
-\r
-#define SHR(x,n) (x >> n)\r
-#define ROTR(x,n) (SHR(x,n) | (x << (64 - n)))\r
-\r
-#define S0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^  SHR(x, 7))\r
-#define S1(x) (ROTR(x,19) ^ ROTR(x,61) ^  SHR(x, 6))\r
-\r
-#define S2(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39))\r
-#define S3(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41))\r
-\r
-#define F0(x,y,z) ((x & y) | (z & (x | y)))\r
-#define F1(x,y,z) (z ^ (x & (y ^ z)))\r
-\r
-#define P(a,b,c,d,e,f,g,h,x,K)                  \\r
-{                                               \\r
-    temp1 = h + S3(e) + F1(e,f,g) + K + x;      \\r
-    temp2 = S2(a) + F0(a,b,c);                  \\r
-    d += temp1; h = temp1 + temp2;              \\r
-}\r
-\r
-    for( i = 0; i < 16; i++ )\r
-    {\r
-        GET_UINT64_BE( W[i], data, i << 3 );\r
-    }\r
-\r
-    for( ; i < 80; i++ )\r
-    {\r
-        W[i] = S1(W[i -  2]) + W[i -  7] +\r
-               S0(W[i - 15]) + W[i - 16];\r
-    }\r
-\r
-    A = ctx->state[0];\r
-    B = ctx->state[1];\r
-    C = ctx->state[2];\r
-    D = ctx->state[3];\r
-    E = ctx->state[4];\r
-    F = ctx->state[5];\r
-    G = ctx->state[6];\r
-    H = ctx->state[7];\r
-    i = 0;\r
-\r
-    do\r
-    {\r
-        P( A, B, C, D, E, F, G, H, W[i], K[i] ); i++;\r
-        P( H, A, B, C, D, E, F, G, W[i], K[i] ); i++;\r
-        P( G, H, A, B, C, D, E, F, W[i], K[i] ); i++;\r
-        P( F, G, H, A, B, C, D, E, W[i], K[i] ); i++;\r
-        P( E, F, G, H, A, B, C, D, W[i], K[i] ); i++;\r
-        P( D, E, F, G, H, A, B, C, W[i], K[i] ); i++;\r
-        P( C, D, E, F, G, H, A, B, W[i], K[i] ); i++;\r
-        P( B, C, D, E, F, G, H, A, W[i], K[i] ); i++;\r
-    }\r
-    while( i < 80 );\r
-\r
-    ctx->state[0] += A;\r
-    ctx->state[1] += B;\r
-    ctx->state[2] += C;\r
-    ctx->state[3] += D;\r
-    ctx->state[4] += E;\r
-    ctx->state[5] += F;\r
-    ctx->state[6] += G;\r
-    ctx->state[7] += H;\r
-}\r
-\r
-/*\r
- * SHA-512 process buffer\r
- */\r
-void mbedtls_sha512_update( mbedtls_sha512_context *ctx, const unsigned char *input,\r
-                    size_t ilen )\r
-{\r
-    size_t fill;\r
-    unsigned int left;\r
-\r
-    if( ilen == 0 )\r
-        return;\r
-\r
-    left = (unsigned int) (ctx->total[0] & 0x7F);\r
-    fill = 128 - left;\r
-\r
-    ctx->total[0] += (uint64_t) ilen;\r
-\r
-    if( ctx->total[0] < (uint64_t) ilen )\r
-        ctx->total[1]++;\r
-\r
-    if( left && ilen >= fill )\r
-    {\r
-        memcpy( (void *) (ctx->buffer + left), input, fill );\r
-        mbedtls_sha512_process( ctx, ctx->buffer );\r
-        input += fill;\r
-        ilen  -= fill;\r
-        left = 0;\r
-    }\r
-\r
-    while( ilen >= 128 )\r
-    {\r
-        mbedtls_sha512_process( ctx, input );\r
-        input += 128;\r
-        ilen  -= 128;\r
-    }\r
-\r
-    if( ilen > 0 )\r
-        memcpy( (void *) (ctx->buffer + left), input, ilen );\r
-}\r
-\r
-static const unsigned char sha512_padding[SHA512_BLOCK_LENGTH] =\r
-{\r
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
-};\r
-\r
-/*\r
- * SHA-512 final digest\r
- */\r
-void mbedtls_sha512_finish( mbedtls_sha512_context *ctx, unsigned char* output )\r
-{\r
-    size_t last, padn;\r
-    uint64_t high, low;\r
-    unsigned char msglen[16];\r
-\r
-    high = ( ctx->total[0] >> 61 )\r
-         | ( ctx->total[1] <<  3 );\r
-    low  = ( ctx->total[0] <<  3 );\r
-\r
-    PUT_UINT64_BE( high, msglen, 0 );\r
-    PUT_UINT64_BE( low,  msglen, 8 );\r
-\r
-    last = (size_t)( ctx->total[0] & 0x7F );\r
-    padn = ( last < 112 ) ? ( 112 - last ) : ( 240 - last );\r
-\r
-    mbedtls_sha512_update( ctx, sha512_padding, padn );\r
-    mbedtls_sha512_update( ctx, msglen, 16 );\r
-\r
-    PUT_UINT64_BE( ctx->state[0], output,  0 );\r
-    PUT_UINT64_BE( ctx->state[1], output,  8 );\r
-    PUT_UINT64_BE( ctx->state[2], output, 16 );\r
-    PUT_UINT64_BE( ctx->state[3], output, 24 );\r
-    PUT_UINT64_BE( ctx->state[4], output, 32 );\r
-    PUT_UINT64_BE( ctx->state[5], output, 40 );\r
-\r
-    if( ctx->is384 == 0 )\r
-    {\r
-        PUT_UINT64_BE( ctx->state[6], output, 48 );\r
-        PUT_UINT64_BE( ctx->state[7], output, 56 );\r
-    }\r
-}\r
-\r
-/*\r
- * output = SHA-512( input buffer )\r
- */\r
-void mbedtls_sha512( const unsigned char *input, size_t ilen,\r
-             unsigned char* output, int is384 )\r
-{\r
-    mbedtls_sha512_context ctx;\r
-\r
-    mbedtls_sha512_init( &ctx );\r
-    mbedtls_sha512_starts( &ctx, is384 );\r
-    mbedtls_sha512_update( &ctx, input, ilen );\r
-    mbedtls_sha512_finish( &ctx, output );\r
-    mbedtls_sha512_free( &ctx );\r
-}\r
-\r
-/*\r
-* Compute HMAC_SHA384/512 using key, key length, text to hash, size of the text, output buffer and a switch for SHA384\r
-*/\r
-void HMAC_SHA512(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t* out, int is384){\r
-  int digest_length = SHA512_DIGEST_LENGTH;\r
-  if (is384 == 1) {\r
-    digest_length = SHA384_DIGEST_LENGTH;\r
-  }\r
-\r
-  uint8_t i;\r
-  uint8_t k_ipad[SHA512_BLOCK_LENGTH]; /* inner padding - key XORd with ipad */\r
-  uint8_t k_opad[SHA512_BLOCK_LENGTH]; /* outer padding - key XORd with opad */\r
-  uint8_t buffer[SHA512_BLOCK_LENGTH + digest_length];\r
-\r
-  /* start out by storing key in pads */\r
-  memset(k_ipad, 0, sizeof(k_ipad));\r
-  memset(k_opad, 0, sizeof(k_opad));\r
-\r
-  if (key_length <= SHA512_BLOCK_LENGTH) {\r
-      memcpy(k_ipad, key, key_length);\r
-      memcpy(k_opad, key, key_length);\r
-  }\r
-\r
-  else {\r
-      mbedtls_sha512(key, key_length, k_ipad, is384);\r
-      memcpy(k_opad, k_ipad, SHA512_BLOCK_LENGTH);\r
-  }\r
-\r
-  /* XOR key with ipad and opad values */\r
-  for (i = 0; i < SHA512_BLOCK_LENGTH; i++) {\r
-      k_ipad[i] ^= HMAC_IPAD;\r
-      k_opad[i] ^= HMAC_OPAD;\r
-  }\r
-  \r
-  // perform inner SHA512\r
-  memcpy(buffer, k_ipad, SHA512_BLOCK_LENGTH);\r
-  memcpy(buffer + SHA512_BLOCK_LENGTH, in, n);\r
-  mbedtls_sha512(buffer, SHA512_BLOCK_LENGTH + n, out, is384);\r
-  \r
-  memset(buffer, 0, SHA512_BLOCK_LENGTH + n);\r
-\r
-  // perform outer SHA512\r
-  memcpy(buffer, k_opad, SHA512_BLOCK_LENGTH);\r
-  memcpy(buffer + SHA512_BLOCK_LENGTH, out, digest_length);\r
-  mbedtls_sha512(buffer, SHA512_BLOCK_LENGTH + digest_length, out, is384);\r
-}\r
-\r
-/*\r
-* Compute TOTP_HMAC_SHA384/512 using key, key length, text to hash, size of the text and a switch for SHA384\r
-*/\r
-uint32_t TOTP_HMAC_SHA512(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, int is384){\r
-    int digest_length = SHA512_DIGEST_LENGTH;\r
-    if (is384 == 1) {\r
-      digest_length = SHA384_DIGEST_LENGTH;\r
-    }\r
-\r
-    // STEP 1, get the HMAC-SHA512 hash from counter and key\r
-    uint8_t hash[digest_length];\r
-    HMAC_SHA512(key, key_length, in, n, hash, is384);\r
-\r
-    // STEP 2, apply dynamic truncation to obtain a 4-bytes string\r
-    uint32_t truncated_hash = 0;\r
-    uint8_t _offset = hash[digest_length - 1] & 0xF;\r
-    uint8_t j;\r
-    for (j = 0; j < 4; ++j) {\r
-        truncated_hash <<= 8;\r
-        truncated_hash  |= hash[_offset + j];\r
-    }\r
-\r
-    // STEP 3, compute the OTP value\r
-    truncated_hash &= 0x7FFFFFFF;    //Disabled\r
-    truncated_hash %= 1000000;\r
-\r
-    return truncated_hash;\r
-}
\ No newline at end of file
diff --git a/movement/lib/TOTP/sha512.h b/movement/lib/TOTP/sha512.h
deleted file mode 100644 (file)
index 991ebfa..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/**\r
- * \file sha512.h\r
- *\r
- * \brief SHA-384 and SHA-512 cryptographic hash function\r
- *\r
- *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\r
- *  SPDX-License-Identifier: Apache-2.0\r
- *\r
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may\r
- *  not use this file except in compliance with the License.\r
- *  You may obtain a copy of the License at\r
- *\r
- *  http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- *  Unless required by applicable law or agreed to in writing, software\r
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- *  See the License for the specific language governing permissions and\r
- *  limitations under the License.\r
- *\r
- *  This file is part of mbed TLS (https://tls.mbed.org)\r
- */\r
-#ifndef MBEDTLS_SHA512_H\r
-#define MBEDTLS_SHA512_H\r
-\r
-#define SHA384_DIGEST_LENGTH 48\r
-#define SHA512_DIGEST_LENGTH 64\r
-#define SHA512_BLOCK_LENGTH 128\r
-#define HMAC_IPAD 0x36\r
-#define HMAC_OPAD 0x5c\r
-\r
-#include <stddef.h>\r
-#include <stdint.h>\r
-\r
-/**\r
- * \brief          SHA-512 context structure\r
- */\r
-typedef struct\r
-{\r
-    uint64_t total[2];          /*!< number of bytes processed  */\r
-    uint64_t state[8];          /*!< intermediate digest state  */\r
-    unsigned char buffer[SHA512_BLOCK_LENGTH];  /*!< data block being processed */\r
-    int is384;                  /*!< 0 => SHA-512, else SHA-384 */\r
-}\r
-mbedtls_sha512_context;\r
-\r
-/**\r
- * \brief          Initialize SHA-512 context\r
- *\r
- * \param ctx      SHA-512 context to be initialized\r
- */\r
-void mbedtls_sha512_init( mbedtls_sha512_context *ctx );\r
-\r
-/**\r
- * \brief          Clear SHA-512 context\r
- *\r
- * \param ctx      SHA-512 context to be cleared\r
- */\r
-void mbedtls_sha512_free( mbedtls_sha512_context *ctx );\r
-\r
-/**\r
- * \brief          Clone (the state of) a SHA-512 context\r
- *\r
- * \param dst      The destination context\r
- * \param src      The context to be cloned\r
- */\r
-void mbedtls_sha512_clone( mbedtls_sha512_context *dst,\r
-                           const mbedtls_sha512_context *src );\r
-\r
-/**\r
- * \brief          SHA-512 context setup\r
- *\r
- * \param ctx      context to be initialized\r
- * \param is384    0 = use SHA512, 1 = use SHA384\r
- */\r
-void mbedtls_sha512_starts( mbedtls_sha512_context *ctx, int is384 );\r
-\r
-/**\r
- * \brief          SHA-512 process buffer\r
- *\r
- * \param ctx      SHA-512 context\r
- * \param input    buffer holding the  data\r
- * \param ilen     length of the input data\r
- */\r
-void mbedtls_sha512_update( mbedtls_sha512_context *ctx, const unsigned char *input,\r
-                    size_t ilen );\r
-\r
-/**\r
- * \brief          SHA-512 final digest\r
- *\r
- * \param ctx      SHA-512 context\r
- * \param output   SHA-384/512 checksum result\r
- */\r
-void mbedtls_sha512_finish( mbedtls_sha512_context *ctx, unsigned char* output );\r
-\r
-/**\r
- * \brief          Output = SHA-512( input buffer )\r
- *\r
- * \param input    buffer holding the  data\r
- * \param ilen     length of the input data\r
- * \param output   SHA-384/512 checksum result\r
- * \param is384    0 = use SHA512, 1 = use SHA384\r
- */\r
-void mbedtls_sha512( const unsigned char *input, size_t ilen,\r
-             unsigned char* output, int is384 );\r
-\r
-/**\r
- * \brief          Checkup routine\r
- *\r
- * \return         0 if successful, or 1 if the test failed\r
- */\r
-int mbedtls_sha512_self_test( int verbose );\r
-\r
-/* Internal use */\r
-void mbedtls_sha512_process( mbedtls_sha512_context *ctx, const unsigned char data[SHA512_BLOCK_LENGTH] );\r
-void HMAC_SHA512(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, uint8_t* out, int is384);\r
-uint32_t TOTP_HMAC_SHA512(const uint8_t* key, size_t key_length, const uint8_t *in, size_t n, int is384);\r
-\r
-#endif /* mbedtls_sha512.h */\r
diff --git a/movement/lib/astrolib/LICENSE.txt b/movement/lib/astrolib/LICENSE.txt
deleted file mode 100755 (executable)
index 26356b0..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Public domain\r
-\r
-THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-SOFTWARE.\r
diff --git a/movement/lib/astrolib/README.md b/movement/lib/astrolib/README.md
deleted file mode 100644 (file)
index d63bc46..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# Astrolib
-
-This is a relatively straightforward C port of Greg Miller's [JavaScript astro library](https://github.com/gmiller123456/astrogreg), done by Joey Castillo, for the Sensor Watch Astronomy watch face.
-
-He released his work into the public domain and so I release this into the public domain as well.
-
-I have tested the output of this library against [NASA's Horizons system](https://ssd.jpl.nasa.gov/horizons/app.html#/) and found the results to match (within reason, as we're using a truncated version of VSOP87). Moon calculations still seem a bit iffy, but it doesn't surprise me seeing as there are three calculations involved and the error could stack up.
-
-THIS 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.
diff --git a/movement/lib/astrolib/astrolib.c b/movement/lib/astrolib/astrolib.c
deleted file mode 100644 (file)
index f1bacc5..0000000
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Partial C port of Greg Miller's public domain astro library (gmiller@gregmiller.net) 2019
- * https://github.com/gmiller123456/astrogreg
- * 
- * Ported by Joey Castillo for Sensor Watch
- * https://github.com/joeycastillo/Sensor-Watch/
- *
- * Public Domain
- *
- * THIS 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.
- */
-
-#include <math.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include "astrolib.h"
-#include "vsop87a_milli.h"
-
-double astro_convert_utc_to_tt(double jd) ;
-double astro_get_GMST(double ut1);
-astro_cartesian_coordinates_t astro_subtract_cartesian(astro_cartesian_coordinates_t a, astro_cartesian_coordinates_t b);
-astro_cartesian_coordinates_t astro_rotate_from_vsop_to_J2000(astro_cartesian_coordinates_t c);
-astro_matrix_t astro_get_x_rotation_matrix(double r);
-astro_matrix_t astro_get_y_rotation_matrix(double r);
-astro_matrix_t astro_get_z_rotation_matrix(double r);
-astro_matrix_t astro_transpose_matrix(astro_matrix_t m);
-astro_matrix_t astro_dot_product(astro_matrix_t a, astro_matrix_t b);
-astro_matrix_t astro_get_precession_matrix(double jd);
-astro_cartesian_coordinates_t astro_matrix_multiply(astro_cartesian_coordinates_t v, astro_matrix_t m);
-astro_cartesian_coordinates_t astro_convert_geodedic_latlon_to_ITRF_XYZ(double lat, double lon, double height);
-astro_cartesian_coordinates_t astro_convert_ITRF_to_GCRS(astro_cartesian_coordinates_t r, double ut1);
-astro_cartesian_coordinates_t astro_convert_coordinates_from_meters_to_AU(astro_cartesian_coordinates_t c);
-astro_cartesian_coordinates_t astro_get_observer_geocentric_coords(double jd, double lat, double lon);
-astro_cartesian_coordinates_t astro_get_body_coordinates(astro_body_t bodyNum, double et);
-astro_cartesian_coordinates_t astro_get_body_coordinates_light_time_adjusted(astro_body_t body, astro_cartesian_coordinates_t origin, double t);
-astro_equatorial_coordinates_t astro_convert_cartesian_to_polar(astro_cartesian_coordinates_t xyz);
-
-//Special "Math.floor()" function used by convertDateToJulianDate()
-static double _astro_special_floor(double d) {
-    if(d > 0) {
-        return floor(d);
-    }
-    return floor(d) - 1;
-}
-
-double astro_convert_date_to_julian_date(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
-    if (month < 3){
-        year = year - 1;
-        month = month + 12;
-    }
-
-    double b = 0;
-    if (!(year < 1582 || (year == 1582 && (month < 10 || (month == 10 && day < 5))))) {
-        double a = _astro_special_floor(year / 100.0);
-        b = 2 - a + _astro_special_floor(a / 4.0);
-    }
-
-    double jd = _astro_special_floor(365.25 * (year + 4716)) + _astro_special_floor(30.6001 * (month + 1)) + day + b - 1524.5;
-    jd += hour / 24.0;
-    jd += minute / 24.0 / 60.0;
-    jd += second / 24.0 / 60.0 / 60.0;
-
-    return jd;
-}
-
-//Return all values in radians.
-//The positions are adjusted for the parallax of the Earth, and the offset of the observer from the Earth's center
-//All input and output angles are in radians!
-astro_equatorial_coordinates_t astro_get_ra_dec(double jd, astro_body_t body, double lat, double lon, bool calculate_precession) {
-    double jdTT = astro_convert_utc_to_tt(jd);
-    double t = astro_convert_jd_to_julian_millenia_since_j2000(jdTT);
-    
-    // Get current position of Earth and the target body
-    astro_cartesian_coordinates_t earth_coords = astro_get_body_coordinates(ASTRO_BODY_EARTH, t);
-    astro_cartesian_coordinates_t body_coords = astro_get_body_coordinates_light_time_adjusted(body, earth_coords, t);
-
-    // Convert to Geocentric coordinate
-    body_coords = astro_subtract_cartesian(body_coords, earth_coords);
-
-    //Rotate ecliptic coordinates to J2000 coordinates
-    body_coords = astro_rotate_from_vsop_to_J2000(body_coords);
-
-    astro_matrix_t precession;
-    // TODO: rotate body for precession, nutation and bias
-    if(calculate_precession) {
-        precession = astro_get_precession_matrix(jdTT);
-        body_coords = astro_matrix_multiply(body_coords, precession);
-    }
-
-    //Convert to topocentric
-    astro_cartesian_coordinates_t observerXYZ = astro_get_observer_geocentric_coords(jdTT, lat, lon);
-
-    if(calculate_precession) {
-        //TODO: rotate observerXYZ for precession, nutation and bias
-        astro_matrix_t precessionInv = astro_transpose_matrix(precession);
-        observerXYZ = astro_matrix_multiply(observerXYZ, precessionInv);
-    }
-
-    body_coords = astro_subtract_cartesian(body_coords, observerXYZ);
-
-    //Convert to topocentric RA DEC by converting from cartesian coordinates to polar coordinates
-    astro_equatorial_coordinates_t retval = astro_convert_cartesian_to_polar(body_coords);
-    
-    retval.declination = M_PI/2.0 - retval.declination;  //Dec.  Offset to make 0 the equator, and the poles +/-90 deg
-    if(retval.right_ascension < 0) retval.right_ascension += 2*M_PI; //Ensure RA is positive
-    
-    return retval;
-}
-
-//Converts a Julian Date in UTC to Terrestrial Time (TT)
-double astro_convert_utc_to_tt(double jd) {
-    //Leap seconds are hard coded, should be updated from the IERS website for other times
-    
-    //TAI = UTC + leap seconds (e.g. 32)
-    //TT=TAI + 32.184
-
-    //return jd + (32.0 + 32.184) / 24.0 / 60.0 / 60.0;
-    return jd + (37.0 + 32.184) / 24.0 / 60.0 / 60.0;
-
-    /*
-    https://data.iana.org/time-zones/tzdb-2018a/leap-seconds.list
-    2272060800  10  # 1 Jan 1972
-    2287785600  11  # 1 Jul 1972
-    2303683200  12  # 1 Jan 1973
-    2335219200  13  # 1 Jan 1974
-    2366755200  14  # 1 Jan 1975
-    2398291200  15  # 1 Jan 1976
-    2429913600  16  # 1 Jan 1977
-    2461449600  17  # 1 Jan 1978
-    2492985600  18  # 1 Jan 1979
-    2524521600  19  # 1 Jan 1980
-    2571782400  20  # 1 Jul 1981
-    2603318400  21  # 1 Jul 1982
-    2634854400  22  # 1 Jul 1983
-    2698012800  23  # 1 Jul 1985
-    2776982400  24  # 1 Jan 1988
-    2840140800  25  # 1 Jan 1990
-    2871676800  26  # 1 Jan 1991
-    2918937600  27  # 1 Jul 1992
-    2950473600  28  # 1 Jul 1993
-    2982009600  29  # 1 Jul 1994
-    3029443200  30  # 1 Jan 1996
-    3076704000  31  # 1 Jul 1997
-    3124137600  32  # 1 Jan 1999
-    3345062400  33  # 1 Jan 2006
-    3439756800  34  # 1 Jan 2009
-    3550089600  35  # 1 Jul 2012
-    3644697600  36  # 1 Jul 2015
-    3692217600  37  # 1 Jan 2017
-    */
-}
-
-double astro_convert_jd_to_julian_millenia_since_j2000(double jd) {
-    return (jd - 2451545.0) / 365250.0;
-}
-
-astro_cartesian_coordinates_t astro_subtract_cartesian(astro_cartesian_coordinates_t a, astro_cartesian_coordinates_t b) {
-    astro_cartesian_coordinates_t retval;
-
-    retval.x = a.x - b.x;
-    retval.y = a.y - b.y;
-    retval.z = a.z - b.z;
-
-    return retval;
-}
-
-// Performs the rotation from ecliptic coordinates to J2000 coordinates for the given vector x
-astro_cartesian_coordinates_t astro_rotate_from_vsop_to_J2000(astro_cartesian_coordinates_t c) {
-    /* From VSOP87.doc
-        X        +1.000000000000  +0.000000440360  -0.000000190919   X
-        Y     =  -0.000000479966  +0.917482137087  -0.397776982902   Y
-        Z FK5     0.000000000000  +0.397776982902  +0.917482137087   Z VSOP87A
-    */
-    astro_cartesian_coordinates_t t;
-    t.x = c.x + c.y * 0.000000440360 + c.z * -0.000000190919;
-    t.y = c.x * -0.000000479966 + c.y * 0.917482137087 + c.z * -0.397776982902;
-    t.z = c.y * 0.397776982902 + c.z * 0.917482137087;
-
-    return t;
-}
-
-double astro_get_GMST(double ut1) {
-    double D = ut1 - 2451545.0;
-    double T = D/36525.0;
-    double gmst = fmod((280.46061837 + 360.98564736629*D + 0.000387933*T*T - T*T*T/38710000.0), 360.0);
-
-    if(gmst<0) {
-        gmst+=360;
-    }
-
-    return gmst/15;
-}
-
-static astro_matrix_t _astro_get_empty_matrix() {
-    astro_matrix_t t;
-    for(uint8_t i = 0; i < 3 ; i++) {
-        for(uint8_t j = 0 ; j < 3 ; j++) {
-            t.elements[i][j] = 0;
-        }
-    }
-    return t;
-}
-
-//Gets a rotation matrix about the x axis.  Angle R is in radians
-astro_matrix_t astro_get_x_rotation_matrix(double r) {
-    astro_matrix_t t = _astro_get_empty_matrix();
-
-    t.elements[0][0]=1;
-    t.elements[0][1]=0;
-    t.elements[0][2]=0;
-    t.elements[1][0]=0;
-    t.elements[1][1]=cos(r);
-    t.elements[1][2]=sin(r);
-    t.elements[2][0]=0;
-    t.elements[2][1]=-sin(r);
-    t.elements[2][2]=cos(r);
-
-    return t;
-}
-
-//Gets a rotation matrix about the y axis.  Angle R is in radians
-astro_matrix_t astro_get_y_rotation_matrix(double r) {
-    astro_matrix_t t = _astro_get_empty_matrix();
-
-    t.elements[0][0]=cos(r);
-    t.elements[0][1]=0;
-    t.elements[0][2]=-sin(r);
-    t.elements[1][0]=0;
-    t.elements[1][1]=1;
-    t.elements[1][2]=0;
-    t.elements[2][0]=sin(r);
-    t.elements[2][1]=0;
-    t.elements[2][2]=cos(r);
-
-    return t;
-}
-
-//Gets a rotation matrix about the z axis.  Angle R is in radians
-astro_matrix_t astro_get_z_rotation_matrix(double r) {
-    astro_matrix_t t = _astro_get_empty_matrix();
-
-    t.elements[0][0]=cos(r);
-    t.elements[0][1]=sin(r);
-    t.elements[0][2]=0;
-    t.elements[1][0]=-sin(r);
-    t.elements[1][1]=cos(r);
-    t.elements[1][2]=0;
-    t.elements[2][0]=0;
-    t.elements[2][1]=0;
-    t.elements[2][2]=1;
-
-    return t;
-}
-
-void astro_print_matrix(char * title, astro_matrix_t matrix);
-void astro_print_matrix(char * title, astro_matrix_t matrix) {
-    printf("%s\n", title);
-    for(uint8_t i = 0; i < 3 ; i++) {
-        printf("\t");
-        for(uint8_t j = 0 ; j < 3 ; j++) {
-            printf("%12f", matrix.elements[i][j]);
-        }
-        printf("\n");
-    }
-    printf("\n");
-}
-
-astro_matrix_t astro_dot_product(astro_matrix_t a, astro_matrix_t b) {
-    astro_matrix_t retval;
-
-    for(uint8_t i = 0; i < 3 ; i++) {
-        for(uint8_t j = 0 ; j < 3 ; j++) {
-            double temp = 0;
-            for(uint8_t k = 0; k < 3 ; k++) {
-                temp += a.elements[i][k] * b.elements[k][j];
-            }
-            retval.elements[i][j]=temp;
-        }
-    }
-
-    return retval;
-}
-
-astro_matrix_t astro_transpose_matrix(astro_matrix_t m) {
-    astro_matrix_t retval;
-    for(uint8_t i = 0; i < 3 ; i++) {
-        for(uint8_t j = 0 ; j < 3 ; j++) {
-            retval.elements[i][j] = m.elements[j][i];
-        }
-    }
-    return retval;
-}
-
-astro_matrix_t astro_get_precession_matrix(double jd) {
-    //2006 IAU Precession.  Implemented from IERS Technical Note No 36 ch5.
-    //https://www.iers.org/SharedDocs/Publikationen/EN/IERS/Publications/tn/TechnNote36/tn36_043.pdf?__blob=publicationFile&v=1
-
-    double t = (jd - 2451545.0) / 36525.0;  //5.2
-    const double Arcsec2Radians = M_PI/180.0/60.0/60.0; //Converts arc seconds used in equations below to radians
-
-    double e0 = 84381.406 * Arcsec2Radians; //5.6.4
-    double omegaA = e0 + ((-0.025754 + (0.0512623 +    (-0.00772503 + (-0.000000467 + 0.0000003337*t) * t) * t) * t) * t) * Arcsec2Radians; //5.39
-    double psiA = ((5038.481507 +      (-1.0790069 + (-0.00114045 + (0.000132851 - 0.0000000951*t) * t) * t) * t) * t) * Arcsec2Radians; //5.39
-    double chiA = ((10.556403 + (-2.3814292 + (-0.00121197 + (0.000170663 - 0.0000000560*t) * t) * t) * t) * t) * Arcsec2Radians; //5.40
-    //Rotation matrix from 5.4.5
-    //(R1(−e0) · R3(psiA) · R1(omegaA) · R3(−chiA))
-    //Above eq rotates from "of date" to J2000, so we reverse the signs to go from J2000 to "of date"
-    astro_matrix_t m1 = astro_get_x_rotation_matrix(e0);
-    astro_matrix_t m2 = astro_get_z_rotation_matrix(-psiA);
-    astro_matrix_t m3 = astro_get_x_rotation_matrix(-omegaA);
-    astro_matrix_t m4 = astro_get_z_rotation_matrix(chiA);
-
-    astro_matrix_t m5 = astro_dot_product(m4, m3);
-    astro_matrix_t m6 = astro_dot_product(m5, m2);
-    astro_matrix_t precessionMatrix = astro_dot_product(m6, m1);
-
-    return precessionMatrix;
-}
-
-astro_cartesian_coordinates_t astro_matrix_multiply(astro_cartesian_coordinates_t v, astro_matrix_t m) {
-    astro_cartesian_coordinates_t t;
-
-    t.x = v.x*m.elements[0][0] + v.y*m.elements[0][1] + v.z*m.elements[0][2];
-    t.y = v.x*m.elements[1][0] + v.y*m.elements[1][1] + v.z*m.elements[1][2];
-    t.z = v.x*m.elements[2][0] + v.y*m.elements[2][1] + v.z*m.elements[2][2];
-
-    return t;
-}
-
-//Converts cartesian XYZ coordinates to polar (e.g. J2000 xyz to Right Accention and Declication)
-astro_equatorial_coordinates_t astro_convert_cartesian_to_polar(astro_cartesian_coordinates_t xyz) {
-    astro_equatorial_coordinates_t t;
-
-    t.distance = sqrt(xyz.x * xyz.x + xyz.y * xyz.y + xyz.z * xyz.z);
-    t.declination = acos(xyz.z / t.distance);
-    t.right_ascension = atan2(xyz.y, xyz.x);
-
-    if(t.declination < 0) t.declination += 2 * M_PI;
-
-    if(t.right_ascension < 0) t.right_ascension += 2 * M_PI;
-
-    return t;
-}
-
-//Convert Geodedic Lat Lon to geocentric XYZ position vector
-//All angles are input as radians
-astro_cartesian_coordinates_t astro_convert_geodedic_latlon_to_ITRF_XYZ(double lat, double lon, double height) {
-    //Algorithm from Explanatory Supplement to the Astronomical Almanac 3rd ed. P294
-    const double a = 6378136.6;
-    const double f = 1 / 298.25642;
-
-    const double C = sqrt(((cos(lat)*cos(lat)) + (1.0-f)*(1.0-f) * (sin(lat)*sin(lat))));
-
-    const double S = (1-f)*(1-f)*C;
-    
-    double h = height;
-
-    astro_cartesian_coordinates_t r;
-    r.x = (a*C+h) * cos(lat) * cos(lon);
-    r.y = (a*C+h) * cos(lat) * sin(lon);
-    r.z = (a*S+h) * sin(lat);
-    
-    return r;
-}
-
-//Convert position vector to celestial "of date" system.
-//g(t)=R3(-GAST) r
-//(Remember to use UT1 for GAST, not ET)
-//All angles are input and output as radians
-astro_cartesian_coordinates_t astro_convert_ITRF_to_GCRS(astro_cartesian_coordinates_t r, double ut1) {
-    //This is a simple rotation matrix implemenation about the Z axis, rotation angle is -GMST
-
-    double GMST = astro_get_GMST(ut1);
-    GMST =- GMST * 15.0 * M_PI / 180.0;
-
-    astro_matrix_t m = astro_get_z_rotation_matrix(GMST);
-    astro_cartesian_coordinates_t t = astro_matrix_multiply(r, m);
-
-    return t;
-}
-
-astro_cartesian_coordinates_t astro_convert_coordinates_from_meters_to_AU(astro_cartesian_coordinates_t c) {
-    astro_cartesian_coordinates_t t;
-    
-    t.x = c.x / 1.49597870691E+11;
-    t.y = c.y / 1.49597870691E+11;
-    t.z = c.z / 1.49597870691E+11;
-    
-    return t;
-}
-
-astro_cartesian_coordinates_t astro_get_observer_geocentric_coords(double jd, double lat, double lon) {
-    astro_cartesian_coordinates_t r = astro_convert_geodedic_latlon_to_ITRF_XYZ(lat, lon,0);
-    r = astro_convert_ITRF_to_GCRS(r, jd);
-    r = astro_convert_coordinates_from_meters_to_AU(r);
-    
-    return r;
-}
-
-//Returns a body's cartesian coordinates centered on the Sun.
-//Requires vsop87a_milli_js, if you wish to use a different version of VSOP87, replace the class name vsop87a_milli below
-astro_cartesian_coordinates_t astro_get_body_coordinates(astro_body_t body, double et) {
-    astro_cartesian_coordinates_t retval = {0};
-    double coords[3];
-    switch(body) {
-        case ASTRO_BODY_SUN: 
-            return retval; //Sun is at the center for vsop87a
-        case ASTRO_BODY_MERCURY:
-             vsop87a_milli_getMercury(et, coords);
-             break;
-        case ASTRO_BODY_VENUS:
-             vsop87a_milli_getVenus(et, coords);
-             break;
-        case ASTRO_BODY_EARTH:
-             vsop87a_milli_getEarth(et, coords);
-             break;
-        case ASTRO_BODY_MARS:
-             vsop87a_milli_getMars(et, coords);
-             break;
-        case ASTRO_BODY_JUPITER:
-             vsop87a_milli_getJupiter(et, coords);
-             break;
-        case ASTRO_BODY_SATURN:
-             vsop87a_milli_getSaturn(et, coords);
-             break;
-        case ASTRO_BODY_URANUS:
-             vsop87a_milli_getUranus(et, coords);
-             break;
-        case ASTRO_BODY_NEPTUNE:
-             vsop87a_milli_getNeptune(et, coords);
-             break;
-        case ASTRO_BODY_EMB:
-             vsop87a_milli_getEmb(et, coords);
-             break;
-        case ASTRO_BODY_MOON:
-            {
-                double earth_coords[3];
-                double emb_coords[3];
-                vsop87a_milli_getEarth(et, earth_coords);
-                vsop87a_milli_getEmb(et, emb_coords);
-                vsop87a_milli_getMoon(earth_coords, emb_coords, coords);
-            }
-             break;
-    }
-
-    retval.x = coords[0];
-    retval.y = coords[1];
-    retval.z = coords[2];
-
-    return retval;
-}
-
-astro_cartesian_coordinates_t astro_get_body_coordinates_light_time_adjusted(astro_body_t body, astro_cartesian_coordinates_t origin, double t) {
-    //Get current position of body
-    astro_cartesian_coordinates_t body_coords = astro_get_body_coordinates(body, t);
-
-    double newT = t;
-
-    for(uint8_t i = 0 ; i < 2 ; i++) {
-        //Calculate light time to body
-        body_coords = astro_subtract_cartesian(body_coords, origin);
-        double distance = sqrt(body_coords.x*body_coords.x + body_coords.y*body_coords.y + body_coords.z*body_coords.z);
-        distance *= 1.496e+11; //Convert from AU to meters
-        double lightTime = distance / 299792458.0;
-
-        //Convert light time to Julian Millenia, and subtract it from the original value of t
-        newT -= lightTime / 24.0 / 60.0 / 60.0 / 365250.0;  
-        //Recalculate body position adjusted for light time
-        body_coords = astro_get_body_coordinates(body, newT);
-    }
-
-    return body_coords;
-}
-
-astro_horizontal_coordinates_t astro_ra_dec_to_alt_az(double jd, double lat, double lon, double ra, double dec) {
-    double GMST = astro_get_GMST(jd) * M_PI/180.0 * 15.0;
-    double h = GMST + lon - ra;
-
-    double sina = sin(dec)*sin(lat) + cos(dec)*cos(h)*cos(lat);
-    double a = asin(sina);
-
-    double cosAz = (sin(dec)*cos(lat) - cos(dec)*cos(h)*sin(lat)) / cos(a);
-    double Az = acos(cosAz);
-
-    if(sin(h) > 0) Az = 2.0*M_PI - Az;
-
-    astro_horizontal_coordinates_t retval;
-    retval.altitude = a;
-    retval.azimuth = Az;
-
-    return retval;
-}
-
-double astro_degrees_to_radians(double degrees) {
-    return degrees * M_PI / 180;
-}
-
-double astro_radians_to_degrees(double radians) {
-    return radians * 180.0 / M_PI;
-}
-
-astro_angle_dms_t astro_radians_to_dms(double radians) {
-    astro_angle_dms_t retval;
-    int8_t sign = (radians < 0) ? -1 : 1;
-    double degrees = fabs(astro_radians_to_degrees(radians));
-
-    retval.degrees = (uint16_t)degrees;
-    double temp = 60.0 * (degrees - retval.degrees);
-    retval.minutes = (uint8_t)temp;
-    retval.seconds = (uint8_t)round(60.0 * (temp - retval.minutes));
-
-    if (retval.seconds > 59) {
-        retval.seconds = 0.0;
-        retval.minutes++;
-    }
-
-    if (retval.minutes > 59) {
-        retval.minutes = 0;
-        retval.degrees++;
-    }
-
-    degrees *= sign;
-
-    return retval;
-}
-
-astro_angle_hms_t astro_radians_to_hms(double radians) {
-    astro_angle_hms_t retval;
-    double degrees = astro_radians_to_degrees(radians);
-    double temp = degrees / 15.0;
-
-    retval.hours = (uint8_t)temp;
-    temp = 60.0 * (temp - retval.hours);
-    retval.minutes = (uint8_t)temp;
-    retval.seconds = (uint8_t)round(60.0 * (temp - retval.minutes));
-
-    if (retval.seconds > 59) {
-        retval.seconds = 0;
-        retval.minutes++;
-    }
-
-    if (retval.minutes > 59) {
-        retval.minutes = 0;
-        retval.hours++;
-    }
-
-    return retval;
-}
diff --git a/movement/lib/astrolib/astrolib.h b/movement/lib/astrolib/astrolib.h
deleted file mode 100644 (file)
index 2523ead..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Partial C port of Greg Miller's public domain astro library (gmiller@gregmiller.net) 2019
- * https://github.com/gmiller123456/astrogreg
- *
- * Ported by Joey Castillo for Sensor Watch
- * https://github.com/joeycastillo/Sensor-Watch/
- *
- * Public Domain
- *
- * THIS 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 ASTROLIB_H_
-#define ASTROLIB_H_
-
-typedef enum {
-    ASTRO_BODY_SUN = 0,
-    ASTRO_BODY_MERCURY,
-    ASTRO_BODY_VENUS,
-    ASTRO_BODY_EARTH,
-    ASTRO_BODY_MARS,
-    ASTRO_BODY_JUPITER,
-    ASTRO_BODY_SATURN,
-    ASTRO_BODY_URANUS,
-    ASTRO_BODY_NEPTUNE,
-    ASTRO_BODY_EMB,
-    ASTRO_BODY_MOON
-} astro_body_t;
-
-typedef struct {
-    double elements[3][3];
-} astro_matrix_t;
-
-typedef struct {
-    double x;
-    double y;
-    double z;
-} astro_cartesian_coordinates_t;
-
-typedef struct {
-    double right_ascension;
-    double declination;
-    double distance;
-} astro_equatorial_coordinates_t;
-
-typedef struct {
-    double altitude;
-    double azimuth;
-} astro_horizontal_coordinates_t;
-
-typedef struct {
-    int16_t degrees;
-    uint8_t minutes;
-    uint8_t seconds; // you may want this to be a float, watch just can't display any more digits
-} astro_angle_dms_t;
-
-typedef struct {
-    uint8_t hours;
-    uint8_t minutes;
-    uint8_t seconds; // you may want this to be a float, watch just can't display any more digits
-} astro_angle_hms_t;
-
-// Convert a date to a julian date. Must be in UTC+0 time zone!
-double astro_convert_date_to_julian_date(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
-
-// Converts a Julan Date to Julian Millenia since J2000, which is what VSOP87 expects as input.
-double astro_convert_jd_to_julian_millenia_since_j2000(double jd);
-
-// Get right ascension / declination for a given body in the list above.
-astro_equatorial_coordinates_t astro_get_ra_dec(double jd, astro_body_t bodyNum, double lat, double lon, bool calculate_precession);
-
-// Convert right ascension / declination to altitude/azimuth for a given location.
-astro_horizontal_coordinates_t astro_ra_dec_to_alt_az(double jd, double lat, double lon, double ra, double dec);
-
-// these are self-explanatory
-double astro_degrees_to_radians(double degrees);
-double astro_radians_to_degrees(double radians);
-astro_angle_dms_t astro_radians_to_dms(double radians);
-astro_angle_hms_t astro_radians_to_hms(double radians);
-
-#endif // ASTROLIB_H_
diff --git a/movement/lib/base32/base32.c b/movement/lib/base32/base32.c
deleted file mode 100644 (file)
index 548f9a4..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/**
- * base32 (de)coder implementation as specified by RFC4648.
- *
- * Copyright (c) 2010 Adrien Kunysz
- *
- * 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.
- **/
-
-#include <assert.h>  // assert()
-#include <limits.h>  // CHAR_BIT
-
-#include "base32.h"
-
-/**
- * Let this be a sequence of plain data before encoding:
- *
- *  01234567 01234567 01234567 01234567 01234567
- * +--------+--------+--------+--------+--------+
- * |< 0 >< 1| >< 2 ><|.3 >< 4.|>< 5 ><.|6 >< 7 >|
- * +--------+--------+--------+--------+--------+
- *
- * There are 5 octets of 8 bits each in each sequence.
- * There are 8 blocks of 5 bits each in each sequence.
- *
- * You probably want to refer to that graph when reading the algorithms in this
- * file. We use "octet" instead of "byte" intentionnaly as we really work with
- * 8 bits quantities. This implementation will probably not work properly on
- * systems that don't have exactly 8 bits per (unsigned) char.
- **/
-
-static size_t min(size_t x, size_t y)
-{
-       return x < y ? x : y;
-}
-
-static const unsigned char PADDING_CHAR = '=';
-
-/**
- * Pad the given buffer with len padding characters.
- */
-static void pad(unsigned char *buf, int len)
-{
-       for (int i = 0; i < len; i++)
-               buf[i] = PADDING_CHAR;
-}
-
-/**
- * This convert a 5 bits value into a base32 character.
- * Only the 5 least significant bits are used.
- */
-static unsigned char encode_char(unsigned char c)
-{
-       static unsigned char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
-       return base32[c & 0x1F];  // 0001 1111
-}
-
-/**
- * Decode given character into a 5 bits value. 
- * Returns -1 iff the argument given was an invalid base32 character
- * or a padding character.
- */
-static int decode_char(unsigned char c)
-{
-       int retval = -1;
-
-       if (c >= 'A' && c <= 'Z')
-               retval = c - 'A';
-       if (c >= '2' && c <= '7')
-               retval = c - '2' + 26;
-
-       assert(retval == -1 || ((retval & 0x1F) == retval));
-
-       return  retval;
-}
-
-/**
- * Given a block id between 0 and 7 inclusive, this will return the index of
- * the octet in which this block starts. For example, given 3 it will return 1
- * because block 3 starts in octet 1:
- *
- * +--------+--------+
- * | ......<|.3 >....|
- * +--------+--------+
- *  octet 1 | octet 2
- */
-static int get_octet(int block)
-{
-       assert(block >= 0 && block < 8);
-       return (block*5) / 8;
-}
-
-/**
- * Given a block id between 0 and 7 inclusive, this will return how many bits
- * we can drop at the end of the octet in which this block starts. 
- * For example, given block 0 it will return 3 because there are 3 bits
- * we don't care about at the end:
- *
- *  +--------+-
- *  |< 0 >...|
- *  +--------+-
- *
- * Given block 1, it will return -2 because there
- * are actually two bits missing to have a complete block:
- *
- *  +--------+-
- *  |.....< 1|..
- *  +--------+-
- **/
-static int get_offset(int block)
-{
-       assert(block >= 0 && block < 8);
-       return (8 - 5 - (5*block) % 8);
-}
-
-/**
- * Like "b >> offset" but it will do the right thing with negative offset.
- * We need this as bitwise shifting by a negative offset is undefined
- * behavior.
- */
-static unsigned char shift_right(unsigned char byte, int offset)
-{
-       if (offset > 0)
-               return byte >>  offset;
-       else
-               return byte << -offset;
-}
-
-static unsigned char shift_left(unsigned char byte, int offset)
-{
-       return shift_right(byte, - offset);
-}
-
-/**
- * Encode a sequence. A sequence is no longer than 5 octets by definition.
- * Thus passing a length greater than 5 to this function is an error. Encoding
- * sequences shorter than 5 octets is supported and padding will be added to the
- * output as per the specification.
- */
-static void encode_sequence(const unsigned char *plain, int len, unsigned char *coded)
-{
-       assert(CHAR_BIT == 8);  // not sure this would work otherwise
-       assert(len >= 0 && len <= 5);
-
-       for (int block = 0; block < 8; block++) {
-               int octet = get_octet(block);  // figure out which octet this block starts in
-               int junk = get_offset(block);  // how many bits do we drop from this octet?
-
-               if (octet >= len) { // we hit the end of the buffer
-                       pad(&coded[block], 8 - block);
-                       return;
-               }
-
-               unsigned char c = shift_right(plain[octet], junk);  // first part
-
-               if (junk < 0  // is there a second part?
-               &&  octet < len - 1)  // is there still something to read?
-               {
-                       c |= shift_right(plain[octet+1], 8 + junk);
-               }
-               coded[block] = encode_char(c);
-       }
-}
-
-void base32_encode(const unsigned char *plain, size_t len, unsigned char *coded)
-{
-       // All the hard work is done in encode_sequence(),
-       // here we just need to feed it the data sequence by sequence.
-       for (size_t i = 0, j = 0; i < len; i += 5, j += 8) {
-               encode_sequence(&plain[i], min(len - i, 5), &coded[j]);
-       }
-}
-
-static int decode_sequence(const unsigned char *coded, unsigned char *plain)
-{
-       assert(CHAR_BIT == 8);
-       assert(coded && plain);
-
-       plain[0] = 0;
-       for (int block = 0; block < 8; block++) {
-               int offset = get_offset(block);
-               int octet = get_octet(block);
-
-               int c = decode_char(coded[block]);
-               if (c < 0)  // invalid char, stop here
-                       return octet;
-
-               plain[octet] |= shift_left(c, offset);
-               if (offset < 0) {  // does this block overflows to next octet?
-                       assert(octet < 4);
-                       plain[octet+1] = shift_left(c, 8 + offset);
-               }
-       }
-       return 5;
-}
-
-size_t base32_decode(const unsigned char *coded, unsigned char *plain)
-{
-       size_t written = 0;
-       for (size_t i = 0, j = 0; ; i += 8, j += 5) {
-               int n = decode_sequence(&coded[i], &plain[j]);
-               written += n;
-               if (n < 5)
-                       return written;
-       }
-}
diff --git a/movement/lib/base32/base32.h b/movement/lib/base32/base32.h
deleted file mode 100644 (file)
index abf2d03..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * base32 (de)coder implementation as specified by RFC4648.
- *
- * Copyright (c) 2010 Adrien Kunysz
- *
- * 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 __BASE32_H_
-#define __BASE32_H_
-
-#include <stddef.h>   // size_t
-
-/**
- * Returns the length of the output buffer required to encode len bytes of
- * data into base32. This is a macro to allow users to define buffer size at
- * compilation time.
- */
-#define BASE32_LEN(len)  (((len)/5)*8 + ((len) % 5 ? 8 : 0))
-
-/**
- * Returns the length of the output buffer required to decode a base32 string
- * of len characters. Please note that len must be a multiple of 8 as per
- * definition of a base32 string. This is a macro to allow users to define
- * buffer size at compilation time.
- */
-#define UNBASE32_LEN(len)  (((len)/8)*5)
-
-/**
- * Encode the data pointed to by plain into base32 and store the
- * result at the address pointed to by coded. The "coded" argument
- * must point to a location that has enough available space
- * to store the whole coded string. The resulting string will only
- * contain characters from the [A-Z2-7=] set. The "len" arguments
- * define how many bytes will be read from the "plain" buffer.
- **/
-void base32_encode(const unsigned char *plain, size_t len, unsigned char *coded);
-
-/**
- * Decode the null terminated string pointed to by coded and write
- * the decoded data into the location pointed to by plain. The
- * "plain" argument must point to a location that has enough available
- * space to store the whole decoded string.
- * Returns the length of the decoded string. This may be less than
- * expected due to padding. If an invalid base32 character is found
- * in the coded string, decoding will stop at that point.
- **/
-size_t base32_decode(const unsigned char *coded, unsigned char *plain);
-
-#endif
diff --git a/movement/lib/base64/.gitignore b/movement/lib/base64/.gitignore
deleted file mode 100644 (file)
index 6b5a4f3..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-~*\r
-debug.bat\r
-compile.bat\r
-*.exe\r
-*.lnk\r
-*.url\r
-*.tmp\r
-\r
-# Windows image file caches\r
-Thumbs.db\r
-ehthumbs.db\r
-\r
-# Folder config file\r
-Desktop.ini\r
-\r
-# Recycle Bin used on file shares\r
-$RECYCLE.BIN/\r
-\r
-# Windows Installer files\r
-*.cab\r
-*.msi\r
-*.msm\r
-*.msp\r
-\r
-# =========================\r
-# Operating System Files\r
-# =========================\r
-\r
-# OSX\r
-# =========================\r
-\r
-.DS_Store
-.AppleDouble
-.LSOverride
-
-# Icon must end with two \r
-Icon\r\r
-
-# Thumbnails
-._*
-
-# Files that might appear on external disk
-.Spotlight-V100
-.Trashes
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
-data.7z
-data.7z.b64
-picture.og.png
-data.7z.b64.deco
-picture.b64.txt
-picture.b64.png
diff --git a/movement/lib/base64/LICENSE b/movement/lib/base64/LICENSE
deleted file mode 100644 (file)
index 973d540..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2016 Joe DF (joedf@ahkscript.org)
-
-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.
diff --git a/movement/lib/base64/README.md b/movement/lib/base64/README.md
deleted file mode 100644 (file)
index 58fba4b..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-base64.c
-========
-
-Base64 Library in C  
-  
-by Joe DF (joedf@ahkscript.org)  
-Released under the MIT License  
-  
-Thank you for inspiration:  
-http://www.codeproject.com/Tips/813146/Fast-base-functions-for-encode-decode  
-
-## Usage
-Simply include `base64.c` and `base64.h` in your project and see `base64.h` for instructions.
\ No newline at end of file
diff --git a/movement/lib/base64/base64.c b/movement/lib/base64/base64.c
deleted file mode 100644 (file)
index 8604b94..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
-       base64.c - by Joe DF (joedf@ahkscript.org)
-       Released under the MIT License
-       
-       See "base64.h", for more information.
-       
-       Thank you for inspiration:
-       http://www.codeproject.com/Tips/813146/Fast-base-functions-for-encode-decode
-*/
-
-#include "base64.h"
-
-//Base64 char table - used internally for encoding
-unsigned char b64_chr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-unsigned int b64_int(unsigned int ch) {
-
-       // ASCII to base64_int
-       // 65-90  Upper Case  >>  0-25
-       // 97-122 Lower Case  >>  26-51
-       // 48-57  Numbers     >>  52-61
-       // 43     Plus (+)    >>  62
-       // 47     Slash (/)   >>  63
-       // 61     Equal (=)   >>  64~
-       if (ch==43)
-       return 62;
-       if (ch==47)
-       return 63;
-       if (ch==61)
-       return 64;
-       if ((ch>47) && (ch<58))
-       return ch + 4;
-       if ((ch>64) && (ch<91))
-       return ch - 'A';
-       if ((ch>96) && (ch<123))
-       return (ch - 'a') + 26;
-       return 0;
-}
-
-unsigned int b64e_size(unsigned int in_size) {
-
-       // size equals 4*floor((1/3)*(in_size+2));
-       unsigned int i, j = 0;
-       for (i=0;i<in_size;i++) {
-               if (i % 3 == 0)
-               j += 1;
-       }
-       return (4*j);
-}
-
-unsigned int b64d_size(unsigned int in_size) {
-
-       return ((3*in_size)/4);
-}
-
-unsigned int b64_encode(const unsigned char* in, unsigned int in_len, unsigned char* out) {
-
-       unsigned int i=0, j=0, k=0, s[3];
-       
-       for (i=0;i<in_len;i++) {
-               s[j++]=*(in+i);
-               if (j==3) {
-                       out[k+0] = b64_chr[ (s[0]&255)>>2 ];
-                       out[k+1] = b64_chr[ ((s[0]&0x03)<<4)+((s[1]&0xF0)>>4) ];
-                       out[k+2] = b64_chr[ ((s[1]&0x0F)<<2)+((s[2]&0xC0)>>6) ];
-                       out[k+3] = b64_chr[ s[2]&0x3F ];
-                       j=0; k+=4;
-               }
-       }
-       
-       if (j) {
-               if (j==1)
-                       s[1] = 0;
-               out[k+0] = b64_chr[ (s[0]&255)>>2 ];
-               out[k+1] = b64_chr[ ((s[0]&0x03)<<4)+((s[1]&0xF0)>>4) ];
-               if (j==2)
-                       out[k+2] = b64_chr[ ((s[1]&0x0F)<<2) ];
-               else
-                       out[k+2] = '=';
-               out[k+3] = '=';
-               k+=4;
-       }
-
-       out[k] = '\0';
-       
-       return k;
-}
-
-unsigned int b64_decode(const unsigned char* in, unsigned int in_len, unsigned char* out) {
-
-       unsigned int i=0, j=0, k=0, s[4];
-       
-       for (i=0;i<in_len;i++) {
-               s[j++]=b64_int(*(in+i));
-               if (j==4) {
-                       out[k+0] = ((s[0]&255)<<2)+((s[1]&0x30)>>4);
-                       if (s[2]!=64) {
-                               out[k+1] = ((s[1]&0x0F)<<4)+((s[2]&0x3C)>>2);
-                               if ((s[3]!=64)) {
-                                       out[k+2] = ((s[2]&0x03)<<6)+(s[3]); k+=3;
-                               } else {
-                                       k+=2;
-                               }
-                       } else {
-                               k+=1;
-                       }
-                       j=0;
-               }
-       }
-       
-       return k;
-}
-
-unsigned int b64_encodef(char *InFile, char *OutFile) {
-
-       FILE *pInFile = fopen(InFile,"rb");
-       FILE *pOutFile = fopen(OutFile,"wb");
-       
-       int i=0;
-       int j=0;
-       int c=0;
-       int s[4];
-       
-       if ((pInFile==NULL) || (pOutFile==NULL) ) {
-               if (pInFile!=NULL){fclose(pInFile);}
-               if (pOutFile!=NULL){fclose(pOutFile);}
-               return 0;
-       }
-       
-       while(c!=EOF) {
-               c=fgetc(pInFile);
-               if (c==EOF)
-               break;
-               s[j++]=c;
-               if (j==3) {
-                       fputc(b64_chr[ (s[0]&255)>>2 ],pOutFile);
-                       fputc(b64_chr[ ((s[0]&0x03)<<4)+((s[1]&0xF0)>>4) ],pOutFile);
-                       fputc(b64_chr[ ((s[1]&0x0F)<<2)+((s[2]&0xC0)>>6) ],pOutFile);
-                       fputc(b64_chr[ s[2]&0x3F ],pOutFile);
-                       j=0; i+=4;
-               }
-       }
-       
-       if (j) {
-               if (j==1)
-                       s[1] = 0;
-               fputc(b64_chr[ (s[0]&255)>>2 ],pOutFile);
-               fputc(b64_chr[ ((s[0]&0x03)<<4)+((s[1]&0xF0)>>4) ],pOutFile);
-               if (j==2)
-                       fputc(b64_chr[ ((s[1]&0x0F)<<2) ],pOutFile);
-               else
-                       fputc('=',pOutFile);
-               fputc('=',pOutFile);
-               i+=4;
-       }
-       
-       fclose(pInFile);
-       fclose(pOutFile);
-       
-       return i;
-}
-
-unsigned int b64_decodef(char *InFile, char *OutFile) {
-
-       FILE *pInFile = fopen(InFile,"rb");
-       FILE *pOutFile = fopen(OutFile,"wb");
-       
-       int c=0;
-       int j=0;
-       int k=0;
-       int s[4];
-       
-       if ((pInFile==NULL) || (pOutFile==NULL) ) {
-               if (pInFile!=NULL){fclose(pInFile);}
-               if (pOutFile!=NULL){fclose(pOutFile);}
-               return 0;
-       }
-       
-       while(c!=EOF) {
-               c=fgetc(pInFile);
-               if (c==EOF)
-               break;
-               s[j++]=b64_int(c);
-               if (j==4) {
-                       fputc(((s[0]&255)<<2)+((s[1]&0x30)>>4),pOutFile);
-                       if (s[2]!=64) {
-                               fputc(((s[1]&0x0F)<<4)+((s[2]&0x3C)>>2),pOutFile);
-                               if ((s[3]!=64)) {
-                                       fputc(((s[2]&0x03)<<6)+(s[3]),pOutFile); k+=3;
-                               } else {
-                                       k+=2;
-                               }
-                       } else {
-                               k+=1;
-                       }
-                       j=0;
-               }
-       }
-
-       fclose(pInFile);
-       fclose(pOutFile);
-       
-       return k;
-}
diff --git a/movement/lib/base64/base64.h b/movement/lib/base64/base64.h
deleted file mode 100644 (file)
index c700c7f..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-       base64.c - by Joe DF (joedf@ahkscript.org)
-       Released under the MIT License
-       
-       Revision: 2015-06-12 01:26:51
-       
-       Thank you for inspiration:
-       http://www.codeproject.com/Tips/813146/Fast-base-functions-for-encode-decode
-*/
-
-#include <stdio.h>
-
-//Base64 char table function - used internally for decoding
-unsigned int b64_int(unsigned int ch);
-
-// in_size : the number bytes to be encoded.
-// Returns the recommended memory size to be allocated for the output buffer excluding the null byte
-unsigned int b64e_size(unsigned int in_size);
-
-// in_size : the number bytes to be decoded.
-// Returns the recommended memory size to be allocated for the output buffer
-unsigned int b64d_size(unsigned int in_size);
-
-// in : buffer of "raw" binary to be encoded.
-// in_len : number of bytes to be encoded.
-// out : pointer to buffer with enough memory, user is responsible for memory allocation, receives null-terminated string
-// returns size of output including null byte
-unsigned int b64_encode(const unsigned char* in, unsigned int in_len, unsigned char* out);
-
-// in : buffer of base64 string to be decoded.
-// in_len : number of bytes to be decoded.
-// out : pointer to buffer with enough memory, user is responsible for memory allocation, receives "raw" binary
-// returns size of output excluding null byte
-unsigned int b64_decode(const unsigned char* in, unsigned int in_len, unsigned char* out);
-
-// file-version b64_encode
-// Input : filenames
-// returns size of output
-unsigned int b64_encodef(char *InFile, char *OutFile);
-
-// file-version b64_decode
-// Input : filenames
-// returns size of output
-unsigned int b64_decodef(char *InFile, char *OutFile);
\ No newline at end of file
diff --git a/movement/lib/chirpy_tx/chirpy_tx.c b/movement/lib/chirpy_tx/chirpy_tx.c
deleted file mode 100644 (file)
index 41a42a5..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Gabor L Ugray
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <string.h>
-#include "chirpy_tx.h"
-
-static const uint32_t chirpy_min_freq = 2500;
-static const uint32_t cirpy_freq_step = 250;
-
-// This many bytes are followed by a CRC and block separator
-// It's a multiple of 3 so no bits are wasted (a tone encodes 3 bits)
-// Last block can be shorter
-static const uint8_t chirpy_default_block_size = 15;
-
-// The dedicated control tone. This is the highest tone index.
-static const uint8_t chirpy_control_tone = 8;
-
-// Pre-computed tone periods. This is allocated and populated on-demand.
-static uint32_t *chirpy_tone_periods = NULL;
-
-uint8_t chirpy_crc8(const uint8_t *addr, uint16_t len) {
-    uint8_t crc = 0;
-    for (uint16_t i = 0; i < len; i++)
-        crc = chirpy_update_crc8(addr[i], crc);
-    return crc;
-}
-
-uint8_t chirpy_update_crc8(uint8_t next_byte, uint8_t crc) {
-    for (uint8_t j = 0; j < 8; j++) {
-        uint8_t mix = (crc ^ next_byte) & 0x01;
-        crc >>= 1;
-        if (mix)
-            crc ^= 0x8C;
-        next_byte >>= 1;
-    }
-    return crc;
-}
-
-static void _chirpy_append_tone(chirpy_encoder_state_t *ces, uint8_t tone) {
-    // This is BAD and should never happen. But if it does, we'd rather
-    // create a corrupt transmission than corrupt memory #$^@
-    if (ces->tone_count == CHIRPY_TONE_BUF_SIZE)
-        return;
-    ces->tone_buf[ces->tone_count] = tone;
-    ++ces->tone_count;
-}
-
-void chirpy_init_encoder(chirpy_encoder_state_t *ces, chirpy_get_next_byte_t get_next_byte) {
-    memset(ces, 0, sizeof(chirpy_encoder_state_t));
-    ces->block_size = chirpy_default_block_size;
-    ces->get_next_byte = get_next_byte;
-    _chirpy_append_tone(ces, 8);
-    _chirpy_append_tone(ces, 0);
-    _chirpy_append_tone(ces, 8);
-    _chirpy_append_tone(ces, 0);
-}
-
-static uint8_t _chirpy_retrieve_next_tone(chirpy_encoder_state_t *ces) {
-    if (ces->tone_pos == ces->tone_count)
-        return 255;
-
-    uint8_t res = ces->tone_buf[ces->tone_pos];
-    ++ces->tone_pos;
-    if (ces->tone_pos == ces->tone_count) {
-        // End of buffer: reset buffer
-        ces->tone_pos = 0;
-        ces->tone_count = 0;
-    }
-    return res;
-}
-
-static void _chirpy_encode_bits(chirpy_encoder_state_t *ces, uint8_t force_partial) {
-    while (ces->bit_count > 0) {
-        if (ces->bit_count < 3 && !force_partial) break;
-        uint8_t tone = (uint8_t)(ces->bits >> 13);
-        _chirpy_append_tone(ces, tone);
-        if (ces->bit_count >= 3) {
-            ces->bits <<= 3;
-            ces->bit_count -= 3;
-        } else {
-            ces->bits = 0;
-            ces->bit_count = 0;
-        }
-    }
-}
-
-static void _chirpy_finish_block(chirpy_encoder_state_t *ces) {
-    _chirpy_append_tone(ces, chirpy_control_tone);
-    ces->bits = ces->crc;
-    ces->bits <<= 8;
-    ces->bit_count = 8;
-    _chirpy_encode_bits(ces, 1);
-    ces->bit_count = 0;
-    ces->crc = 0;
-    ces->block_len = 0;
-    _chirpy_append_tone(ces, chirpy_control_tone);
-}
-
-static void _chirpy_finish_transmission(chirpy_encoder_state_t *ces) {
-    _chirpy_append_tone(ces, chirpy_control_tone);
-    _chirpy_append_tone(ces, chirpy_control_tone);
-}
-
-uint8_t chirpy_get_next_tone(chirpy_encoder_state_t *ces) {
-    // If there are tones left in the buffer, keep sending those
-    if (ces->tone_pos < ces->tone_count)
-        return _chirpy_retrieve_next_tone(ces);
-
-    // We know data is over: that means we've wrapped up transmission
-    // Just drain tone buffer, and then keep sendig EOB
-    if (ces->get_next_byte == 0)
-        return _chirpy_retrieve_next_tone(ces);
-
-    // Fetch next byte
-    uint8_t next_byte;
-    uint8_t got_more = ces->get_next_byte(&next_byte);
-
-    // Data over: write CRC if we sent a partial buffer; send end signal
-    if (got_more == 0) {
-        ces->get_next_byte = 0;
-        if (ces->bit_count > 0) _chirpy_encode_bits(ces, 1);
-        if (ces->block_len > 0) _chirpy_finish_block(ces);
-        _chirpy_finish_transmission(ces);
-        return _chirpy_retrieve_next_tone(ces);
-    }
-
-    // Got more data: add to bits; convert
-    uint16_t msk = next_byte;
-    msk <<= (8 - ces->bit_count);
-    ces->bits |= msk;
-    ces->bit_count += 8;
-    _chirpy_encode_bits(ces, 0);
-    ++ces->block_len;
-    ces->crc = chirpy_update_crc8(next_byte, ces->crc);
-    if (ces->block_len == ces->block_size)
-        _chirpy_finish_block(ces);
-
-    return _chirpy_retrieve_next_tone(ces);
-}
-
-uint16_t chirpy_get_tone_period(uint8_t tone) {
-    // Create pre-computed tone periods array on first use
-    if (chirpy_tone_periods == NULL) {
-      chirpy_tone_periods = malloc((chirpy_control_tone + 1) * sizeof(uint32_t));
-      for (uint8_t i = 0; i < chirpy_control_tone + 1; ++i) {
-        uint32_t freq = chirpy_min_freq + i * cirpy_freq_step;
-        uint16_t period = 1000000 / freq;
-        chirpy_tone_periods[i] = period;
-      }
-    }
-    // Return pre-computed value, but be paranoid about indexing into array
-    if (tone > chirpy_control_tone)
-      tone = chirpy_control_tone;
-    return chirpy_tone_periods[tone];
-}
diff --git a/movement/lib/chirpy_tx/chirpy_tx.h b/movement/lib/chirpy_tx/chirpy_tx.h
deleted file mode 100644 (file)
index 092d05c..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Gabor L Ugray
- *
- * 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 CHIRPY_TX_H
-#define CHIRPY_TX_H
-
-/** @brief Calculates the CRC of a byte sequence.
- */
-uint8_t chirpy_crc8(const uint8_t *addr, uint16_t len);
-
-/** @brief Updates a CRC value with the next byte of input.
- */
-uint8_t chirpy_update_crc8(uint8_t next_byte, uint8_t crc);
-
-/** @brief Function to return next byte to be encoded.
- * @param next_byte Pointer where implementor must write next byte (if available).
- * @return 1 if there is a next byte, or 0 if no more data to encode.
- */
-typedef uint8_t (*chirpy_get_next_byte_t)(uint8_t *next_byte);
-
-#define CHIRPY_TONE_BUF_SIZE 16
-
-// Holds state used by the encoder. Do not manipulate directly.
-typedef struct {
-    uint8_t tone_buf[CHIRPY_TONE_BUF_SIZE];
-    uint8_t tone_pos;
-    uint8_t tone_count;
-    uint8_t block_len;
-    uint8_t block_size;
-    uint8_t crc;
-    uint16_t bits;
-    uint8_t bit_count;
-    chirpy_get_next_byte_t get_next_byte;
-} chirpy_encoder_state_t;
-
-/** @brief Iniitializes the encoder state to be used during the transmission.
- * @param ces Pointer to encoder state object to be initialized.
- * @param get_next_byte Pointer to function that the encoder will call to fetch data byte by byte.
- */
-void chirpy_init_encoder(chirpy_encoder_state_t *ces, chirpy_get_next_byte_t get_next_byte);
-
-/** @brief Returns the next tone to be transmitted.
- * @details This function will call the get_next_byte function stored in the encoder state to
- *          retrieve the next byte to be transmitted as needed. As a single byte is encoded as several tones,
- *          and because the transmission also includes periodic CRC values, not every call to this function
- *          will result in a callback for the next data byte.
- * @param ced Pointer to the encoder state object.
- * @return A tone index from 0 to N (where N is the largest tone index), or 255 if the transmission is over.
- */
-uint8_t chirpy_get_next_tone(chirpy_encoder_state_t *ces);
-
-/** @brief Returns the period value for buzzing out a tone.
- * @param tone The tone index, 0 thru 8.
- * @return The period for the tone's frequency, i.e., 1_000_000 / freq.
- */
-uint16_t chirpy_get_tone_period(uint8_t tone);
-
-/** @brief Typedef for a tick handler function.
- */
-typedef void (*chirpy_tick_fun_t)(void *context);
-
-/** @brief Creature-comfort struct for use in your chirping code.
- * @details The idea is to handle a tick that happens 64 times per second at the outermost level.
- *          To get to the desired ~20 tones per second, increment a counter and call the actual
- *          transmission ticker when tick_counter reaches tick_compare, with a compare value of 3.
- *          seq_pos is for use by the transmission function to keep track of where it is in the data.
- *          The current transmission function is stored in tick_fun. You can have multiple phases
- *          by switching to a different function. E.g., intro countdown first, followed by data chirping.
- */
-typedef struct {
-    uint8_t tick_count;
-    uint8_t tick_compare;
-    uint16_t seq_pos;
-    chirpy_tick_fun_t tick_fun;
-} chirpy_tick_state_t;
-
-#endif
diff --git a/movement/lib/chirpy_tx/test/test_main.c b/movement/lib/chirpy_tx/test/test_main.c
deleted file mode 100644 (file)
index c76a3f3..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Gabor L Ugray
- *
- * 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.
- */
-
-#include <stddef.h>
-#include <stdint.h>
-#include <string.h>
-#include "../src/lib/chirpy_tx.h"
-#include "unity.h"
-
-
-void setUp(void) {
-}
-
-void tearDown(void) {
-}
-
-const char *crc_test_str0 = "";
-const char *crc_test_str1 = "h";
-const char *crc_test_str2 = "he";
-const char *crc_test_str3 = "hen";
-const char *crc_test_str4 = "henl";
-const char *crc_test_str5 = "henlO";
-
-#define CRC_VAL_COUNT 5
-const uint8_t crc_vals[] = {167, 118, 95, 92, 127};
-
-void test_crc8() {
-  TEST_ASSERT_EQUAL_UINT8(0, chirpy_crc8((const uint8_t *)crc_test_str0, 0));
-  TEST_ASSERT_EQUAL_UINT8(167, chirpy_crc8((const uint8_t *)crc_test_str1, strlen(crc_test_str1)));
-  TEST_ASSERT_EQUAL_UINT8(118, chirpy_crc8((const uint8_t *)crc_test_str2, strlen(crc_test_str2)));
-  TEST_ASSERT_EQUAL_UINT8(95, chirpy_crc8((const uint8_t *)crc_test_str3, strlen(crc_test_str3)));
-  TEST_ASSERT_EQUAL_UINT8(92, chirpy_crc8((const uint8_t *)crc_test_str4, strlen(crc_test_str4)));
-  TEST_ASSERT_EQUAL_UINT8(127, chirpy_crc8((const uint8_t *)crc_test_str5, strlen(crc_test_str5)));
-
-  uint8_t crc = 0;
-  for (uint16_t i = 0; i < CRC_VAL_COUNT; ++i) {
-    uint8_t next_byte = crc_test_str5[i];
-    crc = chirpy_update_crc8(next_byte, crc);
-    TEST_ASSERT_EQUAL_UINT8(crc_vals[i], crc);
-  }
-
-  crc = chirpy_update_crc8(0, 0);
-  TEST_ASSERT_EQUAL(0, crc);
-
-  crc = chirpy_update_crc8(0x4f, 0);
-  TEST_ASSERT_EQUAL(7, crc);
-}
-
-const uint16_t data_len_01 = 0;
-const uint8_t data_01[] = {};
-const uint16_t tones_len_01 = 6;
-const uint8_t tones_01[] = {8, 0, 8, 0, 8, 8};
-
-const uint16_t data_len_02 = 1;
-const uint8_t data_02[] = {0};
-const uint16_t tones_len_02 = 14;
-const uint8_t tones_02[] = {8, 0, 8, 0, 0, 0, 0, 8, 0, 0, 0, 8, 8, 8};
-
-const uint16_t data_len_03 = 1;
-const uint8_t data_03[] = {0x68};
-const uint16_t tones_len_03 = 14;
-const uint8_t tones_03[] = {8, 0, 8, 0, 3, 2, 0, 8, 5, 1, 6, 8, 8, 8};
-
-const uint16_t data_len_04 = 3;
-const uint8_t data_04[] = {0x68, 0x65, 0x6e};
-const uint16_t tones_len_04 = 19;
-const uint8_t tones_04[] = {8, 0, 8, 0, 3, 2, 0, 6, 2, 5, 5, 6, 8, 2, 7, 6, 8, 8, 8};
-
-const uint16_t data_len_05 = 4;
-const uint8_t data_05[] = {0x68, 0x65, 0x6e, 0x4f};
-const uint16_t tones_len_05 = 27;
-const uint8_t tones_05[] = {
-    8, 0, 8, 0, 3, 2, 0, 6, 2, 5, 5, 6, 8, 2, 7, 6, 8,
-    2, 3, 6, 8, 0, 1, 6, 8, 8, 8};
-
-uint8_t curr_data_pos;
-uint8_t curr_data_len;
-const uint8_t *curr_data;
-
-uint8_t get_next_byte(uint8_t *next_byte) {
-  if (curr_data_pos < curr_data_len) {
-    *next_byte = curr_data[curr_data_pos];
-    ++curr_data_pos;
-    return 1;
-  }
-  return 0;
-}
-
-void test_encoder_one(const uint8_t *data, uint16_t data_len, const uint8_t *tones, uint16_t tones_len) {
-  curr_data = data;
-  curr_data_len = data_len;
-  curr_data_pos = 0;
-  chirpy_encoder_state_t ces;
-  chirpy_init_encoder(&ces, get_next_byte);
-  ces.block_size = 3;
-
-  uint8_t got_tones[2048] = {0};
-  uint16_t got_tone_pos = 0;
-  while (got_tone_pos < 2048) {
-    uint8_t tone = chirpy_get_next_tone(&ces);
-    got_tones[got_tone_pos] = tone;
-    if (tone == 255) break;
-    ++got_tone_pos;
-  }
-  char buf1[65536];
-  char bufx[256];
-  memset(buf1, 0, 65536);
-  for (uint16_t i = 0; i < got_tone_pos; ++i) {
-    if (i == 0)
-      sprintf(bufx, "%d", got_tones[i]);
-    else
-      sprintf(bufx, ", %d", got_tones[i]);
-    strcat(buf1, bufx);
-  }
-  TEST_MESSAGE(buf1);
-  TEST_ASSERT_EQUAL(tones_len, got_tone_pos);
-  uint16_t smaller_len = tones_len < got_tone_pos ? tones_len : got_tone_pos;
-  TEST_ASSERT_EQUAL_UINT8_ARRAY(tones, got_tones, smaller_len);
-}
-
-void test_encoder() {
-  TEST_MESSAGE("Testing encoder with dataset 01");
-  test_encoder_one(data_01, data_len_01, tones_01, tones_len_01);
-  TEST_MESSAGE("Testing encoder with dataset 02");
-  test_encoder_one(data_02, data_len_02, tones_02, tones_len_02);
-  TEST_MESSAGE("Testing encoder with dataset 03");
-  test_encoder_one(data_03, data_len_03, tones_03, tones_len_03);
-  TEST_MESSAGE("Testing encoder with dataset 04");
-  test_encoder_one(data_04, data_len_04, tones_04, tones_len_04);
-  TEST_MESSAGE("Testing encoder with dataset 05");
-  test_encoder_one(data_05, data_len_05, tones_05, tones_len_05);
-}
-
-int main(void) {
-  UNITY_BEGIN();
-  RUN_TEST(test_crc8);
-  RUN_TEST(test_encoder);
-  return UNITY_END();
-}
diff --git a/movement/lib/chirpy_tx/test/unity.c b/movement/lib/chirpy_tx/test/unity.c
deleted file mode 100644 (file)
index 3e4bc04..0000000
+++ /dev/null
@@ -1,2466 +0,0 @@
-/* =========================================================================
-    Unity Project - A Test Framework for C
-    Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
-    [Released under MIT License. Please refer to license.txt for details]
-============================================================================ */
-
-#include "unity.h"
-
-#ifndef UNITY_PROGMEM
-#define UNITY_PROGMEM
-#endif
-
-/* If omitted from header, declare overrideable prototypes here so they're ready for use */
-#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION
-void UNITY_OUTPUT_CHAR(int);
-#endif
-
-/* Helpful macros for us to use here in Assert functions */
-#define UNITY_FAIL_AND_BAIL         do { Unity.CurrentTestFailed  = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0)
-#define UNITY_IGNORE_AND_BAIL       do { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0)
-#define RETURN_IF_FAIL_OR_IGNORE    do { if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) { TEST_ABORT(); } } while (0)
-
-struct UNITY_STORAGE_T Unity;
-
-#ifdef UNITY_OUTPUT_COLOR
-const char UNITY_PROGMEM UnityStrOk[]                            = "\033[42mOK\033[0m";
-const char UNITY_PROGMEM UnityStrPass[]                          = "\033[42mPASS\033[0m";
-const char UNITY_PROGMEM UnityStrFail[]                          = "\033[41mFAIL\033[0m";
-const char UNITY_PROGMEM UnityStrIgnore[]                        = "\033[43mIGNORE\033[0m";
-#else
-const char UNITY_PROGMEM UnityStrOk[]                            = "OK";
-const char UNITY_PROGMEM UnityStrPass[]                          = "PASS";
-const char UNITY_PROGMEM UnityStrFail[]                          = "FAIL";
-const char UNITY_PROGMEM UnityStrIgnore[]                        = "IGNORE";
-#endif
-static const char UNITY_PROGMEM UnityStrNull[]                   = "NULL";
-static const char UNITY_PROGMEM UnityStrSpacer[]                 = ". ";
-static const char UNITY_PROGMEM UnityStrExpected[]               = " Expected ";
-static const char UNITY_PROGMEM UnityStrWas[]                    = " Was ";
-static const char UNITY_PROGMEM UnityStrGt[]                     = " to be greater than ";
-static const char UNITY_PROGMEM UnityStrLt[]                     = " to be less than ";
-static const char UNITY_PROGMEM UnityStrOrEqual[]                = "or equal to ";
-static const char UNITY_PROGMEM UnityStrNotEqual[]               = " to be not equal to ";
-static const char UNITY_PROGMEM UnityStrElement[]                = " Element ";
-static const char UNITY_PROGMEM UnityStrByte[]                   = " Byte ";
-static const char UNITY_PROGMEM UnityStrMemory[]                 = " Memory Mismatch.";
-static const char UNITY_PROGMEM UnityStrDelta[]                  = " Values Not Within Delta ";
-static const char UNITY_PROGMEM UnityStrPointless[]              = " You Asked Me To Compare Nothing, Which Was Pointless.";
-static const char UNITY_PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL";
-static const char UNITY_PROGMEM UnityStrNullPointerForActual[]   = " Actual pointer was NULL";
-#ifndef UNITY_EXCLUDE_FLOAT
-static const char UNITY_PROGMEM UnityStrNot[]                    = "Not ";
-static const char UNITY_PROGMEM UnityStrInf[]                    = "Infinity";
-static const char UNITY_PROGMEM UnityStrNegInf[]                 = "Negative Infinity";
-static const char UNITY_PROGMEM UnityStrNaN[]                    = "NaN";
-static const char UNITY_PROGMEM UnityStrDet[]                    = "Determinate";
-static const char UNITY_PROGMEM UnityStrInvalidFloatTrait[]      = "Invalid Float Trait";
-#endif
-const char UNITY_PROGMEM UnityStrErrShorthand[]                  = "Unity Shorthand Support Disabled";
-const char UNITY_PROGMEM UnityStrErrFloat[]                      = "Unity Floating Point Disabled";
-const char UNITY_PROGMEM UnityStrErrDouble[]                     = "Unity Double Precision Disabled";
-const char UNITY_PROGMEM UnityStrErr64[]                         = "Unity 64-bit Support Disabled";
-static const char UNITY_PROGMEM UnityStrBreaker[]                = "-----------------------";
-static const char UNITY_PROGMEM UnityStrResultsTests[]           = " Tests ";
-static const char UNITY_PROGMEM UnityStrResultsFailures[]        = " Failures ";
-static const char UNITY_PROGMEM UnityStrResultsIgnored[]         = " Ignored ";
-#ifndef UNITY_EXCLUDE_DETAILS
-static const char UNITY_PROGMEM UnityStrDetail1Name[]            = UNITY_DETAIL1_NAME " ";
-static const char UNITY_PROGMEM UnityStrDetail2Name[]            = " " UNITY_DETAIL2_NAME " ";
-#endif
-/*-----------------------------------------------
- * Pretty Printers & Test Result Output Handlers
- *-----------------------------------------------*/
-
-/*-----------------------------------------------*/
-/* Local helper function to print characters. */
-static void UnityPrintChar(const char* pch)
-{
-    /* printable characters plus CR & LF are printed */
-    if ((*pch <= 126) && (*pch >= 32))
-    {
-        UNITY_OUTPUT_CHAR(*pch);
-    }
-    /* write escaped carriage returns */
-    else if (*pch == 13)
-    {
-        UNITY_OUTPUT_CHAR('\\');
-        UNITY_OUTPUT_CHAR('r');
-    }
-    /* write escaped line feeds */
-    else if (*pch == 10)
-    {
-        UNITY_OUTPUT_CHAR('\\');
-        UNITY_OUTPUT_CHAR('n');
-    }
-    /* unprintable characters are shown as codes */
-    else
-    {
-        UNITY_OUTPUT_CHAR('\\');
-        UNITY_OUTPUT_CHAR('x');
-        UnityPrintNumberHex((UNITY_UINT)*pch, 2);
-    }
-}
-
-/*-----------------------------------------------*/
-/* Local helper function to print ANSI escape strings e.g. "\033[42m". */
-#ifdef UNITY_OUTPUT_COLOR
-static UNITY_UINT UnityPrintAnsiEscapeString(const char* string)
-{
-    const char* pch = string;
-    UNITY_UINT count = 0;
-
-    while (*pch && (*pch != 'm'))
-    {
-        UNITY_OUTPUT_CHAR(*pch);
-        pch++;
-        count++;
-    }
-    UNITY_OUTPUT_CHAR('m');
-    count++;
-
-    return count;
-}
-#endif
-
-/*-----------------------------------------------*/
-void UnityPrint(const char* string)
-{
-    const char* pch = string;
-
-    if (pch != NULL)
-    {
-        while (*pch)
-        {
-#ifdef UNITY_OUTPUT_COLOR
-            /* print ANSI escape code */
-            if ((*pch == 27) && (*(pch + 1) == '['))
-            {
-                pch += UnityPrintAnsiEscapeString(pch);
-                continue;
-            }
-#endif
-            UnityPrintChar(pch);
-            pch++;
-        }
-    }
-}
-/*-----------------------------------------------*/
-void UnityPrintLen(const char* string, const UNITY_UINT32 length)
-{
-    const char* pch = string;
-
-    if (pch != NULL)
-    {
-        while (*pch && ((UNITY_UINT32)(pch - string) < length))
-        {
-            /* printable characters plus CR & LF are printed */
-            if ((*pch <= 126) && (*pch >= 32))
-            {
-                UNITY_OUTPUT_CHAR(*pch);
-            }
-            /* write escaped carriage returns */
-            else if (*pch == 13)
-            {
-                UNITY_OUTPUT_CHAR('\\');
-                UNITY_OUTPUT_CHAR('r');
-            }
-            /* write escaped line feeds */
-            else if (*pch == 10)
-            {
-                UNITY_OUTPUT_CHAR('\\');
-                UNITY_OUTPUT_CHAR('n');
-            }
-            /* unprintable characters are shown as codes */
-            else
-            {
-                UNITY_OUTPUT_CHAR('\\');
-                UNITY_OUTPUT_CHAR('x');
-                UnityPrintNumberHex((UNITY_UINT)*pch, 2);
-            }
-            pch++;
-        }
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style)
-{
-    if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
-    {
-        if (style == UNITY_DISPLAY_STYLE_CHAR)
-        {
-            /* printable characters plus CR & LF are printed */
-            UNITY_OUTPUT_CHAR('\'');
-            if ((number <= 126) && (number >= 32))
-            {
-                UNITY_OUTPUT_CHAR((int)number);
-            }
-            /* write escaped carriage returns */
-            else if (number == 13)
-            {
-                UNITY_OUTPUT_CHAR('\\');
-                UNITY_OUTPUT_CHAR('r');
-            }
-            /* write escaped line feeds */
-            else if (number == 10)
-            {
-                UNITY_OUTPUT_CHAR('\\');
-                UNITY_OUTPUT_CHAR('n');
-            }
-            /* unprintable characters are shown as codes */
-            else
-            {
-                UNITY_OUTPUT_CHAR('\\');
-                UNITY_OUTPUT_CHAR('x');
-                UnityPrintNumberHex((UNITY_UINT)number, 2);
-            }
-            UNITY_OUTPUT_CHAR('\'');
-        }
-        else
-        {
-            UnityPrintNumber(number);
-        }
-    }
-    else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT)
-    {
-        UnityPrintNumberUnsigned((UNITY_UINT)number);
-    }
-    else
-    {
-        UNITY_OUTPUT_CHAR('0');
-        UNITY_OUTPUT_CHAR('x');
-        UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2));
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityPrintNumber(const UNITY_INT number_to_print)
-{
-    UNITY_UINT number = (UNITY_UINT)number_to_print;
-
-    if (number_to_print < 0)
-    {
-        /* A negative number, including MIN negative */
-        UNITY_OUTPUT_CHAR('-');
-        number = (~number) + 1;
-    }
-    UnityPrintNumberUnsigned(number);
-}
-
-/*-----------------------------------------------
- * basically do an itoa using as little ram as possible */
-void UnityPrintNumberUnsigned(const UNITY_UINT number)
-{
-    UNITY_UINT divisor = 1;
-
-    /* figure out initial divisor */
-    while (number / divisor > 9)
-    {
-        divisor *= 10;
-    }
-
-    /* now mod and print, then divide divisor */
-    do
-    {
-        UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10)));
-        divisor /= 10;
-    } while (divisor > 0);
-}
-
-/*-----------------------------------------------*/
-void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print)
-{
-    int nibble;
-    char nibbles = nibbles_to_print;
-
-    if ((unsigned)nibbles > UNITY_MAX_NIBBLES)
-    {
-        nibbles = UNITY_MAX_NIBBLES;
-    }
-
-    while (nibbles > 0)
-    {
-        nibbles--;
-        nibble = (int)(number >> (nibbles * 4)) & 0x0F;
-        if (nibble <= 9)
-        {
-            UNITY_OUTPUT_CHAR((char)('0' + nibble));
-        }
-        else
-        {
-            UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble));
-        }
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number)
-{
-    UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1);
-    UNITY_INT32 i;
-
-    for (i = 0; i < UNITY_INT_WIDTH; i++)
-    {
-        if (current_bit & mask)
-        {
-            if (current_bit & number)
-            {
-                UNITY_OUTPUT_CHAR('1');
-            }
-            else
-            {
-                UNITY_OUTPUT_CHAR('0');
-            }
-        }
-        else
-        {
-            UNITY_OUTPUT_CHAR('X');
-        }
-        current_bit = current_bit >> 1;
-    }
-}
-
-/*-----------------------------------------------*/
-#ifndef UNITY_EXCLUDE_FLOAT_PRINT
-/*
- * This function prints a floating-point value in a format similar to
- * printf("%.7g") on a single-precision machine or printf("%.9g") on a
- * double-precision machine.  The 7th digit won't always be totally correct
- * in single-precision operation (for that level of accuracy, a more
- * complicated algorithm would be needed).
- */
-void UnityPrintFloat(const UNITY_DOUBLE input_number)
-{
-#ifdef UNITY_INCLUDE_DOUBLE
-    static const int sig_digits = 9;
-    static const UNITY_INT32 min_scaled = 100000000;
-    static const UNITY_INT32 max_scaled = 1000000000;
-#else
-    static const int sig_digits = 7;
-    static const UNITY_INT32 min_scaled = 1000000;
-    static const UNITY_INT32 max_scaled = 10000000;
-#endif
-
-    UNITY_DOUBLE number = input_number;
-
-    /* print minus sign (does not handle negative zero) */
-    if (number < 0.0f)
-    {
-        UNITY_OUTPUT_CHAR('-');
-        number = -number;
-    }
-
-    /* handle zero, NaN, and +/- infinity */
-    if (number == 0.0f)
-    {
-        UnityPrint("0");
-    }
-    else if (isnan(number))
-    {
-        UnityPrint("nan");
-    }
-    else if (isinf(number))
-    {
-        UnityPrint("inf");
-    }
-    else
-    {
-        UNITY_INT32 n_int = 0;
-        UNITY_INT32 n;
-        int         exponent = 0;
-        int         decimals;
-        int         digits;
-        char        buf[16] = {0};
-
-        /*
-         * Scale up or down by powers of 10.  To minimize rounding error,
-         * start with a factor/divisor of 10^10, which is the largest
-         * power of 10 that can be represented exactly.  Finally, compute
-         * (exactly) the remaining power of 10 and perform one more
-         * multiplication or division.
-         */
-        if (number < 1.0f)
-        {
-            UNITY_DOUBLE factor = 1.0f;
-
-            while (number < (UNITY_DOUBLE)max_scaled / 1e10f)  { number *= 1e10f; exponent -= 10; }
-            while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; }
-
-            number *= factor;
-        }
-        else if (number > (UNITY_DOUBLE)max_scaled)
-        {
-            UNITY_DOUBLE divisor = 1.0f;
-
-            while (number > (UNITY_DOUBLE)min_scaled * 1e10f)   { number  /= 1e10f; exponent += 10; }
-            while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; }
-
-            number /= divisor;
-        }
-        else
-        {
-            /*
-             * In this range, we can split off the integer part before
-             * doing any multiplications.  This reduces rounding error by
-             * freeing up significant bits in the fractional part.
-             */
-            UNITY_DOUBLE factor = 1.0f;
-            n_int = (UNITY_INT32)number;
-            number -= (UNITY_DOUBLE)n_int;
-
-            while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; }
-
-            number *= factor;
-        }
-
-        /* round to nearest integer */
-        n = ((UNITY_INT32)(number + number) + 1) / 2;
-
-#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO
-        /* round to even if exactly between two integers */
-        if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f))
-            n--;
-#endif
-
-        n += n_int;
-
-        if (n >= max_scaled)
-        {
-            n = min_scaled;
-            exponent++;
-        }
-
-        /* determine where to place decimal point */
-        decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1);
-        exponent += decimals;
-
-        /* truncate trailing zeroes after decimal point */
-        while ((decimals > 0) && ((n % 10) == 0))
-        {
-            n /= 10;
-            decimals--;
-        }
-
-        /* build up buffer in reverse order */
-        digits = 0;
-        while ((n != 0) || (digits <= decimals))
-        {
-            buf[digits++] = (char)('0' + n % 10);
-            n /= 10;
-        }
-
-        /* print out buffer (backwards) */
-        while (digits > 0)
-        {
-            if (digits == decimals)
-            {
-                UNITY_OUTPUT_CHAR('.');
-            }
-            UNITY_OUTPUT_CHAR(buf[--digits]);
-        }
-
-        /* print exponent if needed */
-        if (exponent != 0)
-        {
-            UNITY_OUTPUT_CHAR('e');
-
-            if (exponent < 0)
-            {
-                UNITY_OUTPUT_CHAR('-');
-                exponent = -exponent;
-            }
-            else
-            {
-                UNITY_OUTPUT_CHAR('+');
-            }
-
-            digits = 0;
-            while ((exponent != 0) || (digits < 2))
-            {
-                buf[digits++] = (char)('0' + exponent % 10);
-                exponent /= 10;
-            }
-            while (digits > 0)
-            {
-                UNITY_OUTPUT_CHAR(buf[--digits]);
-            }
-        }
-    }
-}
-#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */
-
-/*-----------------------------------------------*/
-static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line)
-{
-#ifdef UNITY_OUTPUT_FOR_ECLIPSE
-    UNITY_OUTPUT_CHAR('(');
-    UnityPrint(file);
-    UNITY_OUTPUT_CHAR(':');
-    UnityPrintNumber((UNITY_INT)line);
-    UNITY_OUTPUT_CHAR(')');
-    UNITY_OUTPUT_CHAR(' ');
-    UnityPrint(Unity.CurrentTestName);
-    UNITY_OUTPUT_CHAR(':');
-#else
-#ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH
-    UnityPrint("<SRCREF line=");
-    UnityPrintNumber((UNITY_INT)line);
-    UnityPrint(" file=\"");
-    UnityPrint(file);
-    UNITY_OUTPUT_CHAR('"');
-    UNITY_OUTPUT_CHAR('>');
-    UnityPrint(Unity.CurrentTestName);
-    UnityPrint("</SRCREF> ");
-#else
-#ifdef UNITY_OUTPUT_FOR_QT_CREATOR
-    UnityPrint("file://");
-    UnityPrint(file);
-    UNITY_OUTPUT_CHAR(':');
-    UnityPrintNumber((UNITY_INT)line);
-    UNITY_OUTPUT_CHAR(' ');
-    UnityPrint(Unity.CurrentTestName);
-    UNITY_OUTPUT_CHAR(':');
-#else
-    UnityPrint(file);
-    UNITY_OUTPUT_CHAR(':');
-    UnityPrintNumber((UNITY_INT)line);
-    UNITY_OUTPUT_CHAR(':');
-    UnityPrint(Unity.CurrentTestName);
-    UNITY_OUTPUT_CHAR(':');
-#endif
-#endif
-#endif
-}
-
-/*-----------------------------------------------*/
-static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line)
-{
-    UnityTestResultsBegin(Unity.TestFile, line);
-    UnityPrint(UnityStrFail);
-    UNITY_OUTPUT_CHAR(':');
-}
-
-/*-----------------------------------------------*/
-void UnityConcludeTest(void)
-{
-    if (Unity.CurrentTestIgnored)
-    {
-        Unity.TestIgnores++;
-    }
-    else if (!Unity.CurrentTestFailed)
-    {
-        UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber);
-        UnityPrint(UnityStrPass);
-    }
-    else
-    {
-        Unity.TestFailures++;
-    }
-
-    Unity.CurrentTestFailed = 0;
-    Unity.CurrentTestIgnored = 0;
-    UNITY_PRINT_EXEC_TIME();
-    UNITY_PRINT_EOL();
-    UNITY_FLUSH_CALL();
-}
-
-/*-----------------------------------------------*/
-static void UnityAddMsgIfSpecified(const char* msg)
-{
-#ifdef UNITY_PRINT_TEST_CONTEXT
-    UnityPrint(UnityStrSpacer);
-    UNITY_PRINT_TEST_CONTEXT();
-#endif
-#ifndef UNITY_EXCLUDE_DETAILS
-    if (Unity.CurrentDetail1)
-    {
-        UnityPrint(UnityStrSpacer);
-        UnityPrint(UnityStrDetail1Name);
-        UnityPrint(Unity.CurrentDetail1);
-        if (Unity.CurrentDetail2)
-        {
-            UnityPrint(UnityStrDetail2Name);
-            UnityPrint(Unity.CurrentDetail2);
-        }
-    }
-#endif
-    if (msg)
-    {
-        UnityPrint(UnityStrSpacer);
-        UnityPrint(msg);
-    }
-}
-
-/*-----------------------------------------------*/
-static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual)
-{
-    UnityPrint(UnityStrExpected);
-    if (expected != NULL)
-    {
-        UNITY_OUTPUT_CHAR('\'');
-        UnityPrint(expected);
-        UNITY_OUTPUT_CHAR('\'');
-    }
-    else
-    {
-        UnityPrint(UnityStrNull);
-    }
-    UnityPrint(UnityStrWas);
-    if (actual != NULL)
-    {
-        UNITY_OUTPUT_CHAR('\'');
-        UnityPrint(actual);
-        UNITY_OUTPUT_CHAR('\'');
-    }
-    else
-    {
-        UnityPrint(UnityStrNull);
-    }
-}
-
-/*-----------------------------------------------*/
-static void UnityPrintExpectedAndActualStringsLen(const char* expected,
-                                                  const char* actual,
-                                                  const UNITY_UINT32 length)
-{
-    UnityPrint(UnityStrExpected);
-    if (expected != NULL)
-    {
-        UNITY_OUTPUT_CHAR('\'');
-        UnityPrintLen(expected, length);
-        UNITY_OUTPUT_CHAR('\'');
-    }
-    else
-    {
-        UnityPrint(UnityStrNull);
-    }
-    UnityPrint(UnityStrWas);
-    if (actual != NULL)
-    {
-        UNITY_OUTPUT_CHAR('\'');
-        UnityPrintLen(actual, length);
-        UNITY_OUTPUT_CHAR('\'');
-    }
-    else
-    {
-        UnityPrint(UnityStrNull);
-    }
-}
-
-/*-----------------------------------------------
- * Assertion & Control Helpers
- *-----------------------------------------------*/
-
-/*-----------------------------------------------*/
-static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected,
-                               UNITY_INTERNAL_PTR actual,
-                               const UNITY_LINE_TYPE lineNumber,
-                               const char* msg)
-{
-    /* Both are NULL or same pointer */
-    if (expected == actual) { return 0; }
-
-    /* print and return true if just expected is NULL */
-    if (expected == NULL)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrNullPointerForExpected);
-        UnityAddMsgIfSpecified(msg);
-        return 1;
-    }
-
-    /* print and return true if just actual is NULL */
-    if (actual == NULL)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrNullPointerForActual);
-        UnityAddMsgIfSpecified(msg);
-        return 1;
-    }
-
-    return 0; /* return false if neither is NULL */
-}
-
-/*-----------------------------------------------
- * Assertion Functions
- *-----------------------------------------------*/
-
-/*-----------------------------------------------*/
-void UnityAssertBits(const UNITY_INT mask,
-                     const UNITY_INT expected,
-                     const UNITY_INT actual,
-                     const char* msg,
-                     const UNITY_LINE_TYPE lineNumber)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if ((mask & expected) != (mask & actual))
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected);
-        UnityPrint(UnityStrWas);
-        UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertEqualNumber(const UNITY_INT expected,
-                            const UNITY_INT actual,
-                            const char* msg,
-                            const UNITY_LINE_TYPE lineNumber,
-                            const UNITY_DISPLAY_STYLE_T style)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (expected != actual)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        UnityPrintNumberByStyle(expected, style);
-        UnityPrint(UnityStrWas);
-        UnityPrintNumberByStyle(actual, style);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold,
-                                           const UNITY_INT actual,
-                                           const UNITY_COMPARISON_T compare,
-                                           const char *msg,
-                                           const UNITY_LINE_TYPE lineNumber,
-                                           const UNITY_DISPLAY_STYLE_T style)
-{
-    int failed = 0;
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if ((threshold == actual) && (compare & UNITY_EQUAL_TO)) { return; }
-    if ((threshold == actual))                               { failed = 1; }
-
-    if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
-    {
-        if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
-        if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
-    }
-    else /* UINT or HEX */
-    {
-        if (((UNITY_UINT)actual > (UNITY_UINT)threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
-        if (((UNITY_UINT)actual < (UNITY_UINT)threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
-    }
-
-    if (failed)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        UnityPrintNumberByStyle(actual, style);
-        if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt);       }
-        if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt);       }
-        if (compare & UNITY_EQUAL_TO)     { UnityPrint(UnityStrOrEqual);  }
-        if (compare == UNITY_NOT_EQUAL)   { UnityPrint(UnityStrNotEqual); }
-        UnityPrintNumberByStyle(threshold, style);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-#define UnityPrintPointlessAndBail()       \
-do {                                       \
-    UnityTestResultsFailBegin(lineNumber); \
-    UnityPrint(UnityStrPointless);         \
-    UnityAddMsgIfSpecified(msg);           \
-    UNITY_FAIL_AND_BAIL;                   \
-} while (0)
-
-/*-----------------------------------------------*/
-void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
-                              UNITY_INTERNAL_PTR actual,
-                              const UNITY_UINT32 num_elements,
-                              const char* msg,
-                              const UNITY_LINE_TYPE lineNumber,
-                              const UNITY_DISPLAY_STYLE_T style,
-                              const UNITY_FLAGS_T flags)
-{
-    UNITY_UINT32 elements  = num_elements;
-    unsigned int length    = style & 0xF;
-    unsigned int increment = 0;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (num_elements == 0)
-    {
-#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
-        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
-#else
-        UnityPrintPointlessAndBail();
-#endif
-    }
-
-    if (expected == actual)
-    {
-        return; /* Both are NULL or same pointer */
-    }
-
-    if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
-    {
-        UNITY_FAIL_AND_BAIL;
-    }
-
-    while ((elements > 0) && (elements--))
-    {
-        UNITY_INT expect_val;
-        UNITY_INT actual_val;
-
-        switch (length)
-        {
-            case 1:
-                expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected;
-                actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual;
-                if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
-                {
-                    expect_val &= 0x000000FF;
-                    actual_val &= 0x000000FF;
-                }
-                increment  = sizeof(UNITY_INT8);
-                break;
-
-            case 2:
-                expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected;
-                actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual;
-                if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
-                {
-                    expect_val &= 0x0000FFFF;
-                    actual_val &= 0x0000FFFF;
-                }
-                increment  = sizeof(UNITY_INT16);
-                break;
-
-#ifdef UNITY_SUPPORT_64
-            case 8:
-                expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected;
-                actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual;
-                increment  = sizeof(UNITY_INT64);
-                break;
-#endif
-
-            default: /* default is length 4 bytes */
-            case 4:
-                expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected;
-                actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual;
-#ifdef UNITY_SUPPORT_64
-                if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
-                {
-                    expect_val &= 0x00000000FFFFFFFF;
-                    actual_val &= 0x00000000FFFFFFFF;
-                }
-#endif
-                increment  = sizeof(UNITY_INT32);
-                length = 4;
-                break;
-        }
-
-        if (expect_val != actual_val)
-        {
-            if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8)))
-            {   /* For UINT, remove sign extension (padding 1's) from signed type casts above */
-                UNITY_INT mask = 1;
-                mask = (mask << 8 * length) - 1;
-                expect_val &= mask;
-                actual_val &= mask;
-            }
-            UnityTestResultsFailBegin(lineNumber);
-            UnityPrint(UnityStrElement);
-            UnityPrintNumberUnsigned(num_elements - elements - 1);
-            UnityPrint(UnityStrExpected);
-            UnityPrintNumberByStyle(expect_val, style);
-            UnityPrint(UnityStrWas);
-            UnityPrintNumberByStyle(actual_val, style);
-            UnityAddMsgIfSpecified(msg);
-            UNITY_FAIL_AND_BAIL;
-        }
-        /* Walk through array by incrementing the pointers */
-        if (flags == UNITY_ARRAY_TO_ARRAY)
-        {
-            expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment);
-        }
-        actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment);
-    }
-}
-
-/*-----------------------------------------------*/
-#ifndef UNITY_EXCLUDE_FLOAT
-/* Wrap this define in a function with variable types as float or double */
-#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff)                           \
-    if (isinf(expected) && isinf(actual) && (((expected) < 0) == ((actual) < 0))) return 1;   \
-    if (UNITY_NAN_CHECK) return 1;                                                            \
-    (diff) = (actual) - (expected);                                                           \
-    if ((diff) < 0) (diff) = -(diff);                                                         \
-    if ((delta) < 0) (delta) = -(delta);                                                      \
-    return !(isnan(diff) || isinf(diff) || ((diff) > (delta)))
-    /* This first part of this condition will catch any NaN or Infinite values */
-#ifndef UNITY_NAN_NOT_EQUAL_NAN
-  #define UNITY_NAN_CHECK isnan(expected) && isnan(actual)
-#else
-  #define UNITY_NAN_CHECK 0
-#endif
-
-#ifndef UNITY_EXCLUDE_FLOAT_PRINT
-  #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \
-  do {                                                            \
-    UnityPrint(UnityStrExpected);                                 \
-    UnityPrintFloat(expected);                                    \
-    UnityPrint(UnityStrWas);                                      \
-    UnityPrintFloat(actual);                                      \
-  } while (0)
-#else
-  #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \
-    UnityPrint(UnityStrDelta)
-#endif /* UNITY_EXCLUDE_FLOAT_PRINT */
-
-/*-----------------------------------------------*/
-static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual)
-{
-    UNITY_FLOAT diff;
-    UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff);
-}
-
-/*-----------------------------------------------*/
-void UnityAssertWithinFloatArray(const UNITY_FLOAT delta,
-                                 UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected,
-                                 UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual,
-                                 const UNITY_UINT32 num_elements,
-                                 const char* msg,
-                                 const UNITY_LINE_TYPE lineNumber,
-                                 const UNITY_FLAGS_T flags)
-{
-    UNITY_UINT32 elements = num_elements;
-    UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected;
-    UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual;
-    UNITY_FLOAT in_delta = delta;
-    UNITY_FLOAT current_element_delta = delta;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (elements == 0)
-    {
-#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
-        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
-#else
-        UnityPrintPointlessAndBail();
-#endif
-    }
-
-    if (isinf(in_delta))
-    {
-        return; /* Arrays will be force equal with infinite delta */
-    }
-
-    if (isnan(in_delta))
-    {
-        /* Delta must be correct number */
-        UnityPrintPointlessAndBail();
-    }
-
-    if (expected == actual)
-    {
-        return; /* Both are NULL or same pointer */
-    }
-
-    if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
-    {
-        UNITY_FAIL_AND_BAIL;
-    }
-
-    /* fix delta sign if need */
-    if (in_delta < 0)
-    {
-        in_delta = -in_delta;
-    }
-
-    while (elements--)
-    {
-        current_element_delta = *ptr_expected * UNITY_FLOAT_PRECISION;
-
-        if (current_element_delta < 0)
-        {
-            /* fix delta sign for correct calculations */
-            current_element_delta = -current_element_delta;
-        }
-
-        if (!UnityFloatsWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual))
-        {
-            UnityTestResultsFailBegin(lineNumber);
-            UnityPrint(UnityStrElement);
-            UnityPrintNumberUnsigned(num_elements - elements - 1);
-            UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual);
-            UnityAddMsgIfSpecified(msg);
-            UNITY_FAIL_AND_BAIL;
-        }
-        if (flags == UNITY_ARRAY_TO_ARRAY)
-        {
-            ptr_expected++;
-        }
-        ptr_actual++;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertFloatsWithin(const UNITY_FLOAT delta,
-                             const UNITY_FLOAT expected,
-                             const UNITY_FLOAT actual,
-                             const char* msg,
-                             const UNITY_LINE_TYPE lineNumber)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-
-    if (!UnityFloatsWithin(delta, expected, actual))
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta,
-                                const UNITY_FLOAT expected,
-                                const UNITY_FLOAT actual,
-                                const char* msg,
-                                const UNITY_LINE_TYPE lineNumber)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (UnityFloatsWithin(delta, expected, actual))
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        UnityPrintFloat((UNITY_DOUBLE)expected);
-        UnityPrint(UnityStrNotEqual);
-        UnityPrintFloat((UNITY_DOUBLE)actual);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold,
-                                   const UNITY_FLOAT actual,
-                                   const UNITY_COMPARISON_T compare,
-                                   const char* msg,
-                                   const UNITY_LINE_TYPE lineNumber)
-{
-    int failed;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    failed = 0;
-
-    /* Checking for "not success" rather than failure to get the right result for NaN */
-    if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
-    if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
-
-    if ((compare & UNITY_EQUAL_TO) && UnityFloatsWithin(threshold * UNITY_FLOAT_PRECISION, threshold, actual)) { failed = 0; }
-
-    if (failed)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        UnityPrintFloat(actual);
-        if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); }
-        if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); }
-        if (compare & UNITY_EQUAL_TO)     { UnityPrint(UnityStrOrEqual);  }
-        UnityPrintFloat(threshold);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
-                             const char* msg,
-                             const UNITY_LINE_TYPE lineNumber,
-                             const UNITY_FLOAT_TRAIT_T style)
-{
-    const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet};
-    UNITY_INT should_be_trait = ((UNITY_INT)style & 1);
-    UNITY_INT is_trait        = !should_be_trait;
-    UNITY_INT trait_index     = (UNITY_INT)(style >> 1);
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    switch (style)
-    {
-        case UNITY_FLOAT_IS_INF:
-        case UNITY_FLOAT_IS_NOT_INF:
-            is_trait = isinf(actual) && (actual > 0);
-            break;
-        case UNITY_FLOAT_IS_NEG_INF:
-        case UNITY_FLOAT_IS_NOT_NEG_INF:
-            is_trait = isinf(actual) && (actual < 0);
-            break;
-
-        case UNITY_FLOAT_IS_NAN:
-        case UNITY_FLOAT_IS_NOT_NAN:
-            is_trait = isnan(actual) ? 1 : 0;
-            break;
-
-        case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */
-        case UNITY_FLOAT_IS_NOT_DET:
-            is_trait = !isinf(actual) && !isnan(actual);
-            break;
-
-        default: /* including UNITY_FLOAT_INVALID_TRAIT */
-            trait_index = 0;
-            trait_names[0] = UnityStrInvalidFloatTrait;
-            break;
-    }
-
-    if (is_trait != should_be_trait)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        if (!should_be_trait)
-        {
-            UnityPrint(UnityStrNot);
-        }
-        UnityPrint(trait_names[trait_index]);
-        UnityPrint(UnityStrWas);
-#ifndef UNITY_EXCLUDE_FLOAT_PRINT
-        UnityPrintFloat((UNITY_DOUBLE)actual);
-#else
-        if (should_be_trait)
-        {
-            UnityPrint(UnityStrNot);
-        }
-        UnityPrint(trait_names[trait_index]);
-#endif
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-#endif /* not UNITY_EXCLUDE_FLOAT */
-
-/*-----------------------------------------------*/
-#ifndef UNITY_EXCLUDE_DOUBLE
-static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual)
-{
-    UNITY_DOUBLE diff;
-    UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff);
-}
-
-/*-----------------------------------------------*/
-void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta,
-                                  UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected,
-                                  UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual,
-                                  const UNITY_UINT32 num_elements,
-                                  const char* msg,
-                                  const UNITY_LINE_TYPE lineNumber,
-                                  const UNITY_FLAGS_T flags)
-{
-    UNITY_UINT32 elements = num_elements;
-    UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected;
-    UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual;
-    UNITY_DOUBLE in_delta = delta;
-    UNITY_DOUBLE current_element_delta = delta;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (elements == 0)
-    {
-#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
-        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
-#else
-        UnityPrintPointlessAndBail();
-#endif
-    }
-
-    if (isinf(in_delta))
-    {
-        return; /* Arrays will be force equal with infinite delta */
-    }
-
-    if (isnan(in_delta))
-    {
-        /* Delta must be correct number */
-        UnityPrintPointlessAndBail();
-    }
-
-    if (expected == actual)
-    {
-        return; /* Both are NULL or same pointer */
-    }
-
-    if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
-    {
-        UNITY_FAIL_AND_BAIL;
-    }
-
-    /* fix delta sign if need */
-    if (in_delta < 0)
-    {
-        in_delta = -in_delta;
-    }
-
-    while (elements--)
-    {
-        current_element_delta = *ptr_expected * UNITY_DOUBLE_PRECISION;
-
-        if (current_element_delta < 0)
-        {
-            /* fix delta sign for correct calculations */
-            current_element_delta = -current_element_delta;
-        }
-
-        if (!UnityDoublesWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual))
-        {
-            UnityTestResultsFailBegin(lineNumber);
-            UnityPrint(UnityStrElement);
-            UnityPrintNumberUnsigned(num_elements - elements - 1);
-            UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual);
-            UnityAddMsgIfSpecified(msg);
-            UNITY_FAIL_AND_BAIL;
-        }
-        if (flags == UNITY_ARRAY_TO_ARRAY)
-        {
-            ptr_expected++;
-        }
-        ptr_actual++;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertDoublesWithin(const UNITY_DOUBLE delta,
-                              const UNITY_DOUBLE expected,
-                              const UNITY_DOUBLE actual,
-                              const char* msg,
-                              const UNITY_LINE_TYPE lineNumber)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (!UnityDoublesWithin(delta, expected, actual))
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta,
-                                 const UNITY_DOUBLE expected,
-                                 const UNITY_DOUBLE actual,
-                                 const char* msg,
-                                 const UNITY_LINE_TYPE lineNumber)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (UnityDoublesWithin(delta, expected, actual))
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        UnityPrintFloat((UNITY_DOUBLE)expected);
-        UnityPrint(UnityStrNotEqual);
-        UnityPrintFloat((UNITY_DOUBLE)actual);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold,
-                                    const UNITY_DOUBLE actual,
-                                    const UNITY_COMPARISON_T compare,
-                                    const char* msg,
-                                    const UNITY_LINE_TYPE lineNumber)
-{
-    int failed;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    failed = 0;
-
-    /* Checking for "not success" rather than failure to get the right result for NaN */
-    if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
-    if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
-
-    if ((compare & UNITY_EQUAL_TO) && UnityDoublesWithin(threshold * UNITY_DOUBLE_PRECISION, threshold, actual)) { failed = 0; }
-
-    if (failed)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        UnityPrintFloat(actual);
-        if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); }
-        if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); }
-        if (compare & UNITY_EQUAL_TO)     { UnityPrint(UnityStrOrEqual);  }
-        UnityPrintFloat(threshold);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual,
-                              const char* msg,
-                              const UNITY_LINE_TYPE lineNumber,
-                              const UNITY_FLOAT_TRAIT_T style)
-{
-    const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet};
-    UNITY_INT should_be_trait = ((UNITY_INT)style & 1);
-    UNITY_INT is_trait        = !should_be_trait;
-    UNITY_INT trait_index     = (UNITY_INT)(style >> 1);
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    switch (style)
-    {
-        case UNITY_FLOAT_IS_INF:
-        case UNITY_FLOAT_IS_NOT_INF:
-            is_trait = isinf(actual) && (actual > 0);
-            break;
-        case UNITY_FLOAT_IS_NEG_INF:
-        case UNITY_FLOAT_IS_NOT_NEG_INF:
-            is_trait = isinf(actual) && (actual < 0);
-            break;
-
-        case UNITY_FLOAT_IS_NAN:
-        case UNITY_FLOAT_IS_NOT_NAN:
-            is_trait = isnan(actual) ? 1 : 0;
-            break;
-
-        case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */
-        case UNITY_FLOAT_IS_NOT_DET:
-            is_trait = !isinf(actual) && !isnan(actual);
-            break;
-
-        default: /* including UNITY_FLOAT_INVALID_TRAIT */
-            trait_index = 0;
-            trait_names[0] = UnityStrInvalidFloatTrait;
-            break;
-    }
-
-    if (is_trait != should_be_trait)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrExpected);
-        if (!should_be_trait)
-        {
-            UnityPrint(UnityStrNot);
-        }
-        UnityPrint(trait_names[trait_index]);
-        UnityPrint(UnityStrWas);
-#ifndef UNITY_EXCLUDE_FLOAT_PRINT
-        UnityPrintFloat(actual);
-#else
-        if (should_be_trait)
-        {
-            UnityPrint(UnityStrNot);
-        }
-        UnityPrint(trait_names[trait_index]);
-#endif
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-#endif /* not UNITY_EXCLUDE_DOUBLE */
-
-/*-----------------------------------------------*/
-void UnityAssertNumbersWithin(const UNITY_UINT delta,
-                              const UNITY_INT expected,
-                              const UNITY_INT actual,
-                              const char* msg,
-                              const UNITY_LINE_TYPE lineNumber,
-                              const UNITY_DISPLAY_STYLE_T style)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
-    {
-        if (actual > expected)
-        {
-            Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta);
-        }
-        else
-        {
-            Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta);
-        }
-    }
-    else
-    {
-        if ((UNITY_UINT)actual > (UNITY_UINT)expected)
-        {
-            Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta);
-        }
-        else
-        {
-            Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta);
-        }
-    }
-
-    if (Unity.CurrentTestFailed)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrint(UnityStrDelta);
-        UnityPrintNumberByStyle((UNITY_INT)delta, style);
-        UnityPrint(UnityStrExpected);
-        UnityPrintNumberByStyle(expected, style);
-        UnityPrint(UnityStrWas);
-        UnityPrintNumberByStyle(actual, style);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertNumbersArrayWithin(const UNITY_UINT delta,
-                                   UNITY_INTERNAL_PTR expected,
-                                   UNITY_INTERNAL_PTR actual,
-                                   const UNITY_UINT32 num_elements,
-                                   const char* msg,
-                                   const UNITY_LINE_TYPE lineNumber,
-                                   const UNITY_DISPLAY_STYLE_T style,
-                                   const UNITY_FLAGS_T flags)
-{
-    UNITY_UINT32 elements = num_elements;
-    unsigned int length   = style & 0xF;
-    unsigned int increment = 0;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (num_elements == 0)
-    {
-#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
-        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
-#else
-        UnityPrintPointlessAndBail();
-#endif
-    }
-
-    if (expected == actual)
-    {
-        return; /* Both are NULL or same pointer */
-    }
-
-    if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
-    {
-        UNITY_FAIL_AND_BAIL;
-    }
-
-    while ((elements > 0) && (elements--))
-    {
-        UNITY_INT expect_val;
-        UNITY_INT actual_val;
-
-        switch (length)
-        {
-            case 1:
-                /* fixing problems with signed overflow on unsigned numbers */
-                if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
-                {
-                    expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected;
-                    actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual;
-                    increment  = sizeof(UNITY_INT8);
-                }
-                else
-                {
-                    expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT8*)expected;
-                    actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT8*)actual;
-                    increment  = sizeof(UNITY_UINT8);
-                }
-                break;
-
-            case 2:
-                /* fixing problems with signed overflow on unsigned numbers */
-                if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
-                {
-                    expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected;
-                    actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual;
-                    increment  = sizeof(UNITY_INT16);
-                }
-                else
-                {
-                    expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT16*)expected;
-                    actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT16*)actual;
-                    increment  = sizeof(UNITY_UINT16);
-                }
-                break;
-
-#ifdef UNITY_SUPPORT_64
-            case 8:
-                /* fixing problems with signed overflow on unsigned numbers */
-                if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
-                {
-                    expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected;
-                    actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual;
-                    increment  = sizeof(UNITY_INT64);
-                }
-                else
-                {
-                    expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT64*)expected;
-                    actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT64*)actual;
-                    increment  = sizeof(UNITY_UINT64);
-                }
-                break;
-#endif
-
-            default: /* default is length 4 bytes */
-            case 4:
-                /* fixing problems with signed overflow on unsigned numbers */
-                if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
-                {
-                    expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected;
-                    actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual;
-                    increment  = sizeof(UNITY_INT32);
-                }
-                else
-                {
-                    expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT32*)expected;
-                    actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT32*)actual;
-                    increment  = sizeof(UNITY_UINT32);
-                }
-                length = 4;
-                break;
-        }
-
-        if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
-        {
-            if (actual_val > expect_val)
-            {
-                Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta);
-            }
-            else
-            {
-                Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta);
-            }
-        }
-        else
-        {
-            if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val)
-            {
-                Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta);
-            }
-            else
-            {
-                Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta);
-            }
-        }
-
-        if (Unity.CurrentTestFailed)
-        {
-            if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8)))
-            {   /* For UINT, remove sign extension (padding 1's) from signed type casts above */
-                UNITY_INT mask = 1;
-                mask = (mask << 8 * length) - 1;
-                expect_val &= mask;
-                actual_val &= mask;
-            }
-            UnityTestResultsFailBegin(lineNumber);
-            UnityPrint(UnityStrDelta);
-            UnityPrintNumberByStyle((UNITY_INT)delta, style);
-            UnityPrint(UnityStrElement);
-            UnityPrintNumberUnsigned(num_elements - elements - 1);
-            UnityPrint(UnityStrExpected);
-            UnityPrintNumberByStyle(expect_val, style);
-            UnityPrint(UnityStrWas);
-            UnityPrintNumberByStyle(actual_val, style);
-            UnityAddMsgIfSpecified(msg);
-            UNITY_FAIL_AND_BAIL;
-        }
-        /* Walk through array by incrementing the pointers */
-        if (flags == UNITY_ARRAY_TO_ARRAY)
-        {
-            expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment);
-        }
-        actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment);
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertEqualString(const char* expected,
-                            const char* actual,
-                            const char* msg,
-                            const UNITY_LINE_TYPE lineNumber)
-{
-    UNITY_UINT32 i;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    /* if both pointers not null compare the strings */
-    if (expected && actual)
-    {
-        for (i = 0; expected[i] || actual[i]; i++)
-        {
-            if (expected[i] != actual[i])
-            {
-                Unity.CurrentTestFailed = 1;
-                break;
-            }
-        }
-    }
-    else
-    { /* handle case of one pointers being null (if both null, test should pass) */
-        if (expected != actual)
-        {
-            Unity.CurrentTestFailed = 1;
-        }
-    }
-
-    if (Unity.CurrentTestFailed)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrintExpectedAndActualStrings(expected, actual);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertEqualStringLen(const char* expected,
-                               const char* actual,
-                               const UNITY_UINT32 length,
-                               const char* msg,
-                               const UNITY_LINE_TYPE lineNumber)
-{
-    UNITY_UINT32 i;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    /* if both pointers not null compare the strings */
-    if (expected && actual)
-    {
-        for (i = 0; (i < length) && (expected[i] || actual[i]); i++)
-        {
-            if (expected[i] != actual[i])
-            {
-                Unity.CurrentTestFailed = 1;
-                break;
-            }
-        }
-    }
-    else
-    { /* handle case of one pointers being null (if both null, test should pass) */
-        if (expected != actual)
-        {
-            Unity.CurrentTestFailed = 1;
-        }
-    }
-
-    if (Unity.CurrentTestFailed)
-    {
-        UnityTestResultsFailBegin(lineNumber);
-        UnityPrintExpectedAndActualStringsLen(expected, actual, length);
-        UnityAddMsgIfSpecified(msg);
-        UNITY_FAIL_AND_BAIL;
-    }
-}
-
-/*-----------------------------------------------*/
-void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected,
-                                 const char** actual,
-                                 const UNITY_UINT32 num_elements,
-                                 const char* msg,
-                                 const UNITY_LINE_TYPE lineNumber,
-                                 const UNITY_FLAGS_T flags)
-{
-    UNITY_UINT32 i = 0;
-    UNITY_UINT32 j = 0;
-    const char* expd = NULL;
-    const char* act = NULL;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    /* if no elements, it's an error */
-    if (num_elements == 0)
-    {
-#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
-        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
-#else
-        UnityPrintPointlessAndBail();
-#endif
-    }
-
-    if ((const void*)expected == (const void*)actual)
-    {
-        return; /* Both are NULL or same pointer */
-    }
-
-    if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
-    {
-        UNITY_FAIL_AND_BAIL;
-    }
-
-    if (flags != UNITY_ARRAY_TO_ARRAY)
-    {
-        expd = (const char*)expected;
-    }
-
-    do
-    {
-        act = actual[j];
-        if (flags == UNITY_ARRAY_TO_ARRAY)
-        {
-            expd = ((const char* const*)expected)[j];
-        }
-
-        /* if both pointers not null compare the strings */
-        if (expd && act)
-        {
-            for (i = 0; expd[i] || act[i]; i++)
-            {
-                if (expd[i] != act[i])
-                {
-                    Unity.CurrentTestFailed = 1;
-                    break;
-                }
-            }
-        }
-        else
-        { /* handle case of one pointers being null (if both null, test should pass) */
-            if (expd != act)
-            {
-                Unity.CurrentTestFailed = 1;
-            }
-        }
-
-        if (Unity.CurrentTestFailed)
-        {
-            UnityTestResultsFailBegin(lineNumber);
-            if (num_elements > 1)
-            {
-                UnityPrint(UnityStrElement);
-                UnityPrintNumberUnsigned(j);
-            }
-            UnityPrintExpectedAndActualStrings(expd, act);
-            UnityAddMsgIfSpecified(msg);
-            UNITY_FAIL_AND_BAIL;
-        }
-    } while (++j < num_elements);
-}
-
-/*-----------------------------------------------*/
-void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected,
-                            UNITY_INTERNAL_PTR actual,
-                            const UNITY_UINT32 length,
-                            const UNITY_UINT32 num_elements,
-                            const char* msg,
-                            const UNITY_LINE_TYPE lineNumber,
-                            const UNITY_FLAGS_T flags)
-{
-    UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected;
-    UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual;
-    UNITY_UINT32 elements = num_elements;
-    UNITY_UINT32 bytes;
-
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    if (elements == 0)
-    {
-#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
-        UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
-#else
-        UnityPrintPointlessAndBail();
-#endif
-    }
-    if (length == 0)
-    {
-        UnityPrintPointlessAndBail();
-    }
-
-    if (expected == actual)
-    {
-        return; /* Both are NULL or same pointer */
-    }
-
-    if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
-    {
-        UNITY_FAIL_AND_BAIL;
-    }
-
-    while (elements--)
-    {
-        bytes = length;
-        while (bytes--)
-        {
-            if (*ptr_exp != *ptr_act)
-            {
-                UnityTestResultsFailBegin(lineNumber);
-                UnityPrint(UnityStrMemory);
-                if (num_elements > 1)
-                {
-                    UnityPrint(UnityStrElement);
-                    UnityPrintNumberUnsigned(num_elements - elements - 1);
-                }
-                UnityPrint(UnityStrByte);
-                UnityPrintNumberUnsigned(length - bytes - 1);
-                UnityPrint(UnityStrExpected);
-                UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8);
-                UnityPrint(UnityStrWas);
-                UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8);
-                UnityAddMsgIfSpecified(msg);
-                UNITY_FAIL_AND_BAIL;
-            }
-            ptr_exp++;
-            ptr_act++;
-        }
-        if (flags == UNITY_ARRAY_TO_VAL)
-        {
-            ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected;
-        }
-    }
-}
-
-/*-----------------------------------------------*/
-
-static union
-{
-    UNITY_INT8 i8;
-    UNITY_INT16 i16;
-    UNITY_INT32 i32;
-#ifdef UNITY_SUPPORT_64
-    UNITY_INT64 i64;
-#endif
-#ifndef UNITY_EXCLUDE_FLOAT
-    float f;
-#endif
-#ifndef UNITY_EXCLUDE_DOUBLE
-    double d;
-#endif
-} UnityQuickCompare;
-
-UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size)
-{
-    switch(size)
-    {
-        case 1:
-            UnityQuickCompare.i8 = (UNITY_INT8)num;
-            return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8);
-
-        case 2:
-            UnityQuickCompare.i16 = (UNITY_INT16)num;
-            return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16);
-
-#ifdef UNITY_SUPPORT_64
-        case 8:
-            UnityQuickCompare.i64 = (UNITY_INT64)num;
-            return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64);
-#endif
-
-        default: /* 4 bytes */
-            UnityQuickCompare.i32 = (UNITY_INT32)num;
-            return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32);
-    }
-}
-
-#ifndef UNITY_EXCLUDE_FLOAT
-/*-----------------------------------------------*/
-UNITY_INTERNAL_PTR UnityFloatToPtr(const float num)
-{
-    UnityQuickCompare.f = num;
-    return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f);
-}
-#endif
-
-#ifndef UNITY_EXCLUDE_DOUBLE
-/*-----------------------------------------------*/
-UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num)
-{
-    UnityQuickCompare.d = num;
-    return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d);
-}
-#endif
-
-#ifdef UNITY_INCLUDE_PRINT_FORMATTED
-
-/*-----------------------------------------------
- * printf length modifier helpers
- *-----------------------------------------------*/
-
-enum UnityLengthModifier {
-    UNITY_LENGTH_MODIFIER_NONE,
-    UNITY_LENGTH_MODIFIER_LONG_LONG,
-    UNITY_LENGTH_MODIFIER_LONG,
-};
-
-#define UNITY_EXTRACT_ARG(NUMBER_T, NUMBER, LENGTH_MOD, VA, ARG_T) \
-do {                                                               \
-    switch (LENGTH_MOD)                                            \
-    {                                                              \
-        case UNITY_LENGTH_MODIFIER_LONG_LONG:                      \
-        {                                                          \
-            NUMBER = (NUMBER_T)va_arg(VA, long long ARG_T);        \
-            break;                                                 \
-        }                                                          \
-        case UNITY_LENGTH_MODIFIER_LONG:                           \
-        {                                                          \
-            NUMBER = (NUMBER_T)va_arg(VA, long ARG_T);             \
-            break;                                                 \
-        }                                                          \
-        case UNITY_LENGTH_MODIFIER_NONE:                           \
-        default:                                                   \
-        {                                                          \
-            NUMBER = (NUMBER_T)va_arg(VA, ARG_T);                  \
-            break;                                                 \
-        }                                                          \
-    }                                                              \
-} while (0)
-
-static enum UnityLengthModifier UnityLengthModifierGet(const char *pch, int *length)
-{
-    enum UnityLengthModifier length_mod;
-    switch (pch[0])
-    {
-        case 'l':
-            {
-                if (pch[1] == 'l')
-                {
-                    *length = 2;
-                    length_mod = UNITY_LENGTH_MODIFIER_LONG_LONG;
-                }
-                else
-                {
-                    *length = 1;
-                    length_mod = UNITY_LENGTH_MODIFIER_LONG;
-                }
-                break;
-            }
-        case 'h':
-            {
-                // short and char are converted to int
-                length_mod = UNITY_LENGTH_MODIFIER_NONE;
-                if (pch[1] == 'h')
-                {
-                    *length = 2;
-                }
-                else
-                {
-                    *length = 1;
-                }
-                break;
-            }
-        case 'j':
-        case 'z':
-        case 't':
-        case 'L':
-            {
-                // Not supported, but should gobble up the length specifier anyway
-                length_mod = UNITY_LENGTH_MODIFIER_NONE;
-                *length = 1;
-                break;
-            }
-        default:
-            {
-                length_mod = UNITY_LENGTH_MODIFIER_NONE;
-                *length = 0;
-            }
-    }
-    return length_mod;
-}
-
-/*-----------------------------------------------
- * printf helper function
- *-----------------------------------------------*/
-static void UnityPrintFVA(const char* format, va_list va)
-{
-    const char* pch = format;
-    if (pch != NULL)
-    {
-        while (*pch)
-        {
-            /* format identification character */
-            if (*pch == '%')
-            {
-                pch++;
-
-                if (pch != NULL)
-                {
-                    int length_mod_size;
-                    enum UnityLengthModifier length_mod = UnityLengthModifierGet(pch, &length_mod_size);
-                    pch += length_mod_size;
-
-                    switch (*pch)
-                    {
-                        case 'd':
-                        case 'i':
-                            {
-                                UNITY_INT number;
-                                UNITY_EXTRACT_ARG(UNITY_INT, number, length_mod, va, int);
-                                UnityPrintNumber((UNITY_INT)number);
-                                break;
-                            }
-#ifndef UNITY_EXCLUDE_FLOAT_PRINT
-                        case 'f':
-                        case 'g':
-                            {
-                                const double number = va_arg(va, double);
-                                UnityPrintFloat((UNITY_DOUBLE)number);
-                                break;
-                            }
-#endif
-                        case 'u':
-                            {
-                                UNITY_UINT number;
-                                UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
-                                UnityPrintNumberUnsigned(number);
-                                break;
-                            }
-                        case 'b':
-                            {
-                                UNITY_UINT number;
-                                UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
-                                const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1;
-                                UNITY_OUTPUT_CHAR('0');
-                                UNITY_OUTPUT_CHAR('b');
-                                UnityPrintMask(mask, number);
-                                break;
-                            }
-                        case 'x':
-                        case 'X':
-                            {
-                                UNITY_UINT number;
-                                UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
-                                UNITY_OUTPUT_CHAR('0');
-                                UNITY_OUTPUT_CHAR('x');
-                                UnityPrintNumberHex(number, 8);
-                                break;
-                            }
-                        case 'p':
-                            {
-                                const unsigned int number = va_arg(va, unsigned int);
-                                UNITY_OUTPUT_CHAR('0');
-                                UNITY_OUTPUT_CHAR('x');
-                                UnityPrintNumberHex((UNITY_UINT)number, 8);
-                                break;
-                            }
-                        case 'c':
-                            {
-                                const int ch = va_arg(va, int);
-                                UnityPrintChar((const char *)&ch);
-                                break;
-                            }
-                        case 's':
-                            {
-                                const char * string = va_arg(va, const char *);
-                                UnityPrint(string);
-                                break;
-                            }
-                        case '%':
-                            {
-                                UnityPrintChar(pch);
-                                break;
-                            }
-                        default:
-                            {
-                                /* print the unknown format character */
-                                UNITY_OUTPUT_CHAR('%');
-                                UnityPrintChar(pch);
-                                break;
-                            }
-                    }
-                }
-            }
-#ifdef UNITY_OUTPUT_COLOR
-            /* print ANSI escape code */
-            else if ((*pch == 27) && (*(pch + 1) == '['))
-            {
-                pch += UnityPrintAnsiEscapeString(pch);
-                continue;
-            }
-#endif
-            else if (*pch == '\n')
-            {
-                UNITY_PRINT_EOL();
-            }
-            else
-            {
-                UnityPrintChar(pch);
-            }
-
-            pch++;
-        }
-    }
-}
-
-void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...)
-{
-    UnityTestResultsBegin(Unity.TestFile, line);
-    UnityPrint("INFO");
-    if(format != NULL)
-    {
-        UnityPrint(": ");
-        va_list va;
-        va_start(va, format);
-        UnityPrintFVA(format, va);
-        va_end(va);
-    }
-    UNITY_PRINT_EOL();
-}
-#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */
-
-
-/*-----------------------------------------------
- * Control Functions
- *-----------------------------------------------*/
-
-/*-----------------------------------------------*/
-void UnityFail(const char* msg, const UNITY_LINE_TYPE line)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    UnityTestResultsBegin(Unity.TestFile, line);
-    UnityPrint(UnityStrFail);
-    if (msg != NULL)
-    {
-        UNITY_OUTPUT_CHAR(':');
-
-#ifdef UNITY_PRINT_TEST_CONTEXT
-        UNITY_PRINT_TEST_CONTEXT();
-#endif
-#ifndef UNITY_EXCLUDE_DETAILS
-        if (Unity.CurrentDetail1)
-        {
-            UnityPrint(UnityStrDetail1Name);
-            UnityPrint(Unity.CurrentDetail1);
-            if (Unity.CurrentDetail2)
-            {
-                UnityPrint(UnityStrDetail2Name);
-                UnityPrint(Unity.CurrentDetail2);
-            }
-            UnityPrint(UnityStrSpacer);
-        }
-#endif
-        if (msg[0] != ' ')
-        {
-            UNITY_OUTPUT_CHAR(' ');
-        }
-        UnityPrint(msg);
-    }
-
-    UNITY_FAIL_AND_BAIL;
-}
-
-/*-----------------------------------------------*/
-void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line)
-{
-    RETURN_IF_FAIL_OR_IGNORE;
-
-    UnityTestResultsBegin(Unity.TestFile, line);
-    UnityPrint(UnityStrIgnore);
-    if (msg != NULL)
-    {
-        UNITY_OUTPUT_CHAR(':');
-        UNITY_OUTPUT_CHAR(' ');
-        UnityPrint(msg);
-    }
-    UNITY_IGNORE_AND_BAIL;
-}
-
-/*-----------------------------------------------*/
-void UnityMessage(const char* msg, const UNITY_LINE_TYPE line)
-{
-    UnityTestResultsBegin(Unity.TestFile, line);
-    UnityPrint("INFO");
-    if (msg != NULL)
-    {
-      UNITY_OUTPUT_CHAR(':');
-      UNITY_OUTPUT_CHAR(' ');
-      UnityPrint(msg);
-    }
-    UNITY_PRINT_EOL();
-}
-
-/*-----------------------------------------------*/
-/* If we have not defined our own test runner, then include our default test runner to make life easier */
-#ifndef UNITY_SKIP_DEFAULT_RUNNER
-void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum)
-{
-    Unity.CurrentTestName = FuncName;
-    Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum;
-    Unity.NumberOfTests++;
-    UNITY_CLR_DETAILS();
-    UNITY_EXEC_TIME_START();
-    if (TEST_PROTECT())
-    {
-        setUp();
-        Func();
-    }
-    if (TEST_PROTECT())
-    {
-        tearDown();
-    }
-    UNITY_EXEC_TIME_STOP();
-    UnityConcludeTest();
-}
-#endif
-
-/*-----------------------------------------------*/
-void UnitySetTestFile(const char* filename)
-{
-    Unity.TestFile = filename;
-}
-
-/*-----------------------------------------------*/
-void UnityBegin(const char* filename)
-{
-    Unity.TestFile = filename;
-    Unity.CurrentTestName = NULL;
-    Unity.CurrentTestLineNumber = 0;
-    Unity.NumberOfTests = 0;
-    Unity.TestFailures = 0;
-    Unity.TestIgnores = 0;
-    Unity.CurrentTestFailed = 0;
-    Unity.CurrentTestIgnored = 0;
-
-    UNITY_CLR_DETAILS();
-    UNITY_OUTPUT_START();
-}
-
-/*-----------------------------------------------*/
-int UnityEnd(void)
-{
-    UNITY_PRINT_EOL();
-    UnityPrint(UnityStrBreaker);
-    UNITY_PRINT_EOL();
-    UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests));
-    UnityPrint(UnityStrResultsTests);
-    UnityPrintNumber((UNITY_INT)(Unity.TestFailures));
-    UnityPrint(UnityStrResultsFailures);
-    UnityPrintNumber((UNITY_INT)(Unity.TestIgnores));
-    UnityPrint(UnityStrResultsIgnored);
-    UNITY_PRINT_EOL();
-    if (Unity.TestFailures == 0U)
-    {
-        UnityPrint(UnityStrOk);
-    }
-    else
-    {
-        UnityPrint(UnityStrFail);
-#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL
-        UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D');
-#endif
-    }
-    UNITY_PRINT_EOL();
-    UNITY_FLUSH_CALL();
-    UNITY_OUTPUT_COMPLETE();
-    return (int)(Unity.TestFailures);
-}
-
-/*-----------------------------------------------
- * Command Line Argument Support
- *-----------------------------------------------*/
-#ifdef UNITY_USE_COMMAND_LINE_ARGS
-
-char* UnityOptionIncludeNamed = NULL;
-char* UnityOptionExcludeNamed = NULL;
-int UnityVerbosity            = 1;
-
-/*-----------------------------------------------*/
-int UnityParseOptions(int argc, char** argv)
-{
-    int i;
-    UnityOptionIncludeNamed = NULL;
-    UnityOptionExcludeNamed = NULL;
-
-    for (i = 1; i < argc; i++)
-    {
-        if (argv[i][0] == '-')
-        {
-            switch (argv[i][1])
-            {
-                case 'l': /* list tests */
-                    return -1;
-                case 'n': /* include tests with name including this string */
-                case 'f': /* an alias for -n */
-                    if (argv[i][2] == '=')
-                    {
-                        UnityOptionIncludeNamed = &argv[i][3];
-                    }
-                    else if (++i < argc)
-                    {
-                        UnityOptionIncludeNamed = argv[i];
-                    }
-                    else
-                    {
-                        UnityPrint("ERROR: No Test String to Include Matches For");
-                        UNITY_PRINT_EOL();
-                        return 1;
-                    }
-                    break;
-                case 'q': /* quiet */
-                    UnityVerbosity = 0;
-                    break;
-                case 'v': /* verbose */
-                    UnityVerbosity = 2;
-                    break;
-                case 'x': /* exclude tests with name including this string */
-                    if (argv[i][2] == '=')
-                    {
-                        UnityOptionExcludeNamed = &argv[i][3];
-                    }
-                    else if (++i < argc)
-                    {
-                        UnityOptionExcludeNamed = argv[i];
-                    }
-                    else
-                    {
-                        UnityPrint("ERROR: No Test String to Exclude Matches For");
-                        UNITY_PRINT_EOL();
-                        return 1;
-                    }
-                    break;
-                default:
-                    UnityPrint("ERROR: Unknown Option ");
-                    UNITY_OUTPUT_CHAR(argv[i][1]);
-                    UNITY_PRINT_EOL();
-                    return 1;
-            }
-        }
-    }
-
-    return 0;
-}
-
-/*-----------------------------------------------*/
-int IsStringInBiggerString(const char* longstring, const char* shortstring)
-{
-    const char* lptr = longstring;
-    const char* sptr = shortstring;
-    const char* lnext = lptr;
-
-    if (*sptr == '*')
-    {
-        return 1;
-    }
-
-    while (*lptr)
-    {
-        lnext = lptr + 1;
-
-        /* If they current bytes match, go on to the next bytes */
-        while (*lptr && *sptr && (*lptr == *sptr))
-        {
-            lptr++;
-            sptr++;
-
-            /* We're done if we match the entire string or up to a wildcard */
-            if (*sptr == '*')
-                return 1;
-            if (*sptr == ',')
-                return 1;
-            if (*sptr == '"')
-                return 1;
-            if (*sptr == '\'')
-                return 1;
-            if (*sptr == ':')
-                return 2;
-            if (*sptr == 0)
-                return 1;
-        }
-
-        /* Otherwise we start in the long pointer 1 character further and try again */
-        lptr = lnext;
-        sptr = shortstring;
-    }
-
-    return 0;
-}
-
-/*-----------------------------------------------*/
-int UnityStringArgumentMatches(const char* str)
-{
-    int retval;
-    const char* ptr1;
-    const char* ptr2;
-    const char* ptrf;
-
-    /* Go through the options and get the substrings for matching one at a time */
-    ptr1 = str;
-    while (ptr1[0] != 0)
-    {
-        if ((ptr1[0] == '"') || (ptr1[0] == '\''))
-        {
-            ptr1++;
-        }
-
-        /* look for the start of the next partial */
-        ptr2 = ptr1;
-        ptrf = 0;
-        do
-        {
-            ptr2++;
-            if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ','))
-            {
-                ptrf = &ptr2[1];
-            }
-        } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ','));
-
-        while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ',')))
-        {
-            ptr2++;
-        }
-
-        /* done if complete filename match */
-        retval = IsStringInBiggerString(Unity.TestFile, ptr1);
-        if (retval == 1)
-        {
-            return retval;
-        }
-
-        /* done if testname match after filename partial match */
-        if ((retval == 2) && (ptrf != 0))
-        {
-            if (IsStringInBiggerString(Unity.CurrentTestName, ptrf))
-            {
-                return 1;
-            }
-        }
-
-        /* done if complete testname match */
-        if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1)
-        {
-            return 1;
-        }
-
-        ptr1 = ptr2;
-    }
-
-    /* we couldn't find a match for any substrings */
-    return 0;
-}
-
-/*-----------------------------------------------*/
-int UnityTestMatches(void)
-{
-    /* Check if this test name matches the included test pattern */
-    int retval;
-    if (UnityOptionIncludeNamed)
-    {
-        retval = UnityStringArgumentMatches(UnityOptionIncludeNamed);
-    }
-    else
-    {
-        retval = 1;
-    }
-
-    /* Check if this test name matches the excluded test pattern */
-    if (UnityOptionExcludeNamed)
-    {
-        if (UnityStringArgumentMatches(UnityOptionExcludeNamed))
-        {
-            retval = 0;
-        }
-    }
-
-    return retval;
-}
-
-#endif /* UNITY_USE_COMMAND_LINE_ARGS */
-/*-----------------------------------------------*/
diff --git a/movement/lib/chirpy_tx/test/unity.h b/movement/lib/chirpy_tx/test/unity.h
deleted file mode 100644 (file)
index e321a1d..0000000
+++ /dev/null
@@ -1,687 +0,0 @@
-/* ==========================================
-    Unity Project - A Test Framework for C
-    Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
-    [Released under MIT License. Please refer to license.txt for details]
-========================================== */
-
-#ifndef UNITY_FRAMEWORK_H
-#define UNITY_FRAMEWORK_H
-#define UNITY
-
-#define UNITY_VERSION_MAJOR    2
-#define UNITY_VERSION_MINOR    5
-#define UNITY_VERSION_BUILD    4
-#define UNITY_VERSION          ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD)
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#include "unity_internals.h"
-
-/*-------------------------------------------------------
- * Test Setup / Teardown
- *-------------------------------------------------------*/
-
-/* These functions are intended to be called before and after each test.
- * If using unity directly, these will need to be provided for each test
- * executable built. If you are using the test runner generator and/or
- * Ceedling, these are optional. */
-void setUp(void);
-void tearDown(void);
-
-/* These functions are intended to be called at the beginning and end of an
- * entire test suite.  suiteTearDown() is passed the number of tests that
- * failed, and its return value becomes the exit code of main(). If using
- * Unity directly, you're in charge of calling these if they are desired.
- * If using Ceedling or the test runner generator, these will be called
- * automatically if they exist. */
-void suiteSetUp(void);
-int suiteTearDown(int num_failures);
-
-/*-------------------------------------------------------
- * Test Reset and Verify
- *-------------------------------------------------------*/
-
-/* These functions are intended to be called before during tests in order
- * to support complex test loops, etc. Both are NOT built into Unity. Instead
- * the test runner generator will create them. resetTest will run teardown and
- * setup again, verifying any end-of-test needs between. verifyTest will only
- * run the verification. */
-void resetTest(void);
-void verifyTest(void);
-
-/*-------------------------------------------------------
- * Configuration Options
- *-------------------------------------------------------
- * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above.
-
- * Integers/longs/pointers
- *     - Unity attempts to automatically discover your integer sizes
- *       - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in <stdint.h>
- *       - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in <limits.h>
- *     - If you cannot use the automatic methods above, you can force Unity by using these options:
- *       - define UNITY_SUPPORT_64
- *       - set UNITY_INT_WIDTH
- *       - set UNITY_LONG_WIDTH
- *       - set UNITY_POINTER_WIDTH
-
- * Floats
- *     - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons
- *     - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT
- *     - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats
- *     - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons
- *     - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default)
- *     - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE
- *     - define UNITY_DOUBLE_TYPE to specify something other than double
- *     - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors
-
- * Output
- *     - by default, Unity prints to standard out with putchar.  define UNITY_OUTPUT_CHAR(a) with a different function if desired
- *     - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure
-
- * Optimization
- *     - by default, line numbers are stored in unsigned shorts.  Define UNITY_LINE_TYPE with a different type if your files are huge
- *     - by default, test and failure counters are unsigned shorts.  Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests.
-
- * Test Cases
- *     - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script
-
- * Parameterized Tests
- *     - you'll want to create a define of TEST_CASE(...) and/or TEST_RANGE(...) which basically evaluates to nothing
-
- * Tests with Arguments
- *     - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity
-
- *-------------------------------------------------------
- * Basic Fail and Ignore
- *-------------------------------------------------------*/
-
-#define TEST_FAIL_MESSAGE(message)                                                                 UNITY_TEST_FAIL(__LINE__, (message))
-#define TEST_FAIL()                                                                                UNITY_TEST_FAIL(__LINE__, NULL)
-#define TEST_IGNORE_MESSAGE(message)                                                               UNITY_TEST_IGNORE(__LINE__, (message))
-#define TEST_IGNORE()                                                                              UNITY_TEST_IGNORE(__LINE__, NULL)
-#define TEST_MESSAGE(message)                                                                      UnityMessage((message), __LINE__)
-#define TEST_ONLY()
-#ifdef UNITY_INCLUDE_PRINT_FORMATTED
-#define TEST_PRINTF(message, ...)                                                                  UnityPrintF(__LINE__, (message), __VA_ARGS__)
-#endif
-
-/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails.
- * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */
-#define TEST_PASS()                                                                                TEST_ABORT()
-#define TEST_PASS_MESSAGE(message)                                                                 do { UnityMessage((message), __LINE__); TEST_ABORT(); } while (0)
-
-/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out
- * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */
-#define TEST_FILE(a)
-
-/*-------------------------------------------------------
- * Test Asserts (simple)
- *-------------------------------------------------------*/
-
-/* Boolean */
-#define TEST_ASSERT(condition)                                                                     UNITY_TEST_ASSERT(       (condition), __LINE__, " Expression Evaluated To FALSE")
-#define TEST_ASSERT_TRUE(condition)                                                                UNITY_TEST_ASSERT(       (condition), __LINE__, " Expected TRUE Was FALSE")
-#define TEST_ASSERT_UNLESS(condition)                                                              UNITY_TEST_ASSERT(      !(condition), __LINE__, " Expression Evaluated To TRUE")
-#define TEST_ASSERT_FALSE(condition)                                                               UNITY_TEST_ASSERT(      !(condition), __LINE__, " Expected FALSE Was TRUE")
-#define TEST_ASSERT_NULL(pointer)                                                                  UNITY_TEST_ASSERT_NULL(    (pointer), __LINE__, " Expected NULL")
-#define TEST_ASSERT_NOT_NULL(pointer)                                                              UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL")
-#define TEST_ASSERT_EMPTY(pointer)                                                                 UNITY_TEST_ASSERT_EMPTY(    (pointer), __LINE__, " Expected Empty")
-#define TEST_ASSERT_NOT_EMPTY(pointer)                                                             UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty")
-
-/* Integers (of all sizes) */
-#define TEST_ASSERT_EQUAL_INT(expected, actual)                                                    UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_INT8(expected, actual)                                                   UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_INT16(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_INT32(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_INT64(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT(expected, actual)                                                   UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT8(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT16(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT32(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT64(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_size_t(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_UINT((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX(expected, actual)                                                    UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX8(expected, actual)                                                   UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX16(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX32(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX64(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_CHAR(expected, actual)                                                   UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_BITS(mask, expected, actual)                                                   UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_BITS_HIGH(mask, actual)                                                        UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL)
-#define TEST_ASSERT_BITS_LOW(mask, actual)                                                         UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL)
-#define TEST_ASSERT_BIT_HIGH(bit, actual)                                                          UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL)
-#define TEST_ASSERT_BIT_LOW(bit, actual)                                                           UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL)
-
-/* Integer Not Equal To (of all sizes) */
-#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual)                                               UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual)                                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual)                                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual)                                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual)                                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
-
-/* Integer Greater Than/ Less Than (of all sizes) */
-#define TEST_ASSERT_GREATER_THAN(threshold, actual)                                                UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual)                                            UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual)                                           UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual)                                           UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_size_t(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual)                                           UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual)                                           UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, NULL)
-
-#define TEST_ASSERT_LESS_THAN(threshold, actual)                                                   UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_INT(threshold, actual)                                               UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual)                                              UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual)                                              UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual)                                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual)                                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual)                                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_size_t(threshold, actual)                                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual)                                              UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual)                                             UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_CHAR(threshold, actual)                                              UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, NULL)
-
-#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual)                                            UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual)                                        UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual)                                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual)                                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_size_t(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual)                                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual)                                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
-
-#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual)                                               UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual)                                           UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual)                                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual)                                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual)                                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual)                                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual)                                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_size_t(threshold, actual)                                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual)                                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual)                                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_CHAR(threshold, actual)                                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
-
-/* Integer Ranges (of all sizes) */
-#define TEST_ASSERT_INT_WITHIN(delta, expected, actual)                                            UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual)                                           UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual)                                           UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_size_t_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual)                                            UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual)                                           UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_CHAR_WITHIN(delta, expected, actual)                                           UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-
-/* Integer Array Ranges (of all sizes) */
-#define TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements)                        UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements)                       UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements)                       UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_size_t_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_HEX_ARRAY_WITHIN(delta, expected, actual, num_elements)                        UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements)                       UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-#define TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements)                       UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
-
-
-/* Structs and Strings */
-#define TEST_ASSERT_EQUAL_PTR(expected, actual)                                                    UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_STRING(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len)                                        UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)                                            UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL)
-
-/* Arrays */
-#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_size_t_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements)                        UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-
-/* Arrays Compared To Single Value */
-#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements)                                 UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_size_t(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements)                                 UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements)                                 UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements)                         UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, NULL)
-
-/* Floating Point (If Enabled) */
-#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual)                                          UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual)                                      UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_FLOAT(expected, actual)                                                  UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual)                                              UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements)                      UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements)                               UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual)                                          UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual)                                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual)                                             UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual)                                         UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_IS_INF(actual)                                                           UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual)                                                       UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_IS_NAN(actual)                                                           UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual)                                                   UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual)                                                       UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual)                                                   UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual)                                                       UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL)
-#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual)                                               UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL)
-
-/* Double (If Enabled) */
-#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual)                                         UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual)                                     UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual)                                                 UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual)                                             UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements)                     UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements)                             UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements)                              UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual)                                         UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual)                                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual)                                            UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual)                                        UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_IS_INF(actual)                                                          UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual)                                                      UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_IS_NAN(actual)                                                          UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual)                                                  UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual)                                                      UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual)                                                  UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual)                                                      UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL)
-#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual)                                              UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL)
-
-/* Shorthand */
-#ifdef UNITY_SHORTHAND_AS_OLD
-#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal")
-#endif
-#ifdef UNITY_SHORTHAND_AS_INT
-#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
-#endif
-#ifdef UNITY_SHORTHAND_AS_MEM
-#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, NULL)
-#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
-#endif
-#ifdef UNITY_SHORTHAND_AS_RAW
-#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, " Expected Equal")
-#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal")
-#endif
-#ifdef UNITY_SHORTHAND_AS_NONE
-#define TEST_ASSERT_EQUAL(expected, actual)                                                        UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
-#define TEST_ASSERT_NOT_EQUAL(expected, actual)                                                    UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
-#endif
-
-/*-------------------------------------------------------
- * Test Asserts (with additional messages)
- *-------------------------------------------------------*/
-
-/* Boolean */
-#define TEST_ASSERT_MESSAGE(condition, message)                                                    UNITY_TEST_ASSERT(       (condition), __LINE__, (message))
-#define TEST_ASSERT_TRUE_MESSAGE(condition, message)                                               UNITY_TEST_ASSERT(       (condition), __LINE__, (message))
-#define TEST_ASSERT_UNLESS_MESSAGE(condition, message)                                             UNITY_TEST_ASSERT(      !(condition), __LINE__, (message))
-#define TEST_ASSERT_FALSE_MESSAGE(condition, message)                                              UNITY_TEST_ASSERT(      !(condition), __LINE__, (message))
-#define TEST_ASSERT_NULL_MESSAGE(pointer, message)                                                 UNITY_TEST_ASSERT_NULL(    (pointer), __LINE__, (message))
-#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message)                                             UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message))
-#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message)                                                UNITY_TEST_ASSERT_EMPTY(    (pointer), __LINE__, (message))
-#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message)                                            UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message))
-
-/* Integers (of all sizes) */
-#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message)                                  UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message)                                  UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_size_t_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message)                                  UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message)                                  UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message)                                       UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message))
-#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message)                                        UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message))
-#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message)                                         UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message))
-#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message)                                          UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message)                                  UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message))
-
-/* Integer Not Equal To (of all sizes) */
-#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message)                              UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
-
-
-/* Integer Greater Than/ Less Than (of all sizes) */
-#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message)                               UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_size_t_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_CHAR_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, (message))
-
-#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message)                                  UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message)                              UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_size_t_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_CHAR_MESSAGE(threshold, actual, message)                             UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, (message))
-
-#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message)                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message)                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message)                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message)                      UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
-
-#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message)                              UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message)                          UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_size_t_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
-
-/* Integer Ranges (of all sizes) */
-#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message)                           UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message)                          UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message)                          UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_size_t_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message)                           UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message)                          UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_CHAR_WITHIN_MESSAGE(delta, expected, actual, message)                          UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, (message))
-
-/* Integer Array Ranges (of all sizes) */
-#define TEST_ASSERT_INT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)       UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_INT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)      UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_INT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_INT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_INT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_UINT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)      UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_UINT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_UINT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_UINT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_UINT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_size_t_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_HEX_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)       UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_HEX8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)      UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_HEX16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_HEX32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_HEX64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-#define TEST_ASSERT_CHAR_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)      UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
-
-
-/* Structs and Strings */
-#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message)                       UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message)                           UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message))
-
-/* Arrays */
-#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_size_t_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message)       UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_CHAR_ARRAY_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-
-/* Arrays Compared To Single Value*/
-#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message)                UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_size_t_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message)                UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message)                UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message)        UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_CHAR_MESSAGE(expected, actual, num_elements, message)               UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, (message))
-
-/* Floating Point (If Enabled) */
-#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message)                         UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message)                                 UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_FLOAT_MESSAGE(expected, actual, message)                             UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)     UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message)              UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_FLOAT_MESSAGE(threshold, actual, message)                         UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message)                     UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_FLOAT_MESSAGE(threshold, actual, message)                            UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message)                                          UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message)                                      UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message)                                          UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message)                                  UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message)                                      UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message)                                  UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message)                                      UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message))
-#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message)                              UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message))
-
-/* Double (If Enabled) */
-#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message)                        UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message)                                UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_DOUBLE_MESSAGE(expected, actual, message)                            UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message)    UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message)            UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message)             UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message))
-#define TEST_ASSERT_GREATER_THAN_DOUBLE_MESSAGE(threshold, actual, message)                        UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message)                    UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_THAN_DOUBLE_MESSAGE(threshold, actual, message)                           UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message)                       UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message)                                         UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message)                                     UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message)                                         UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message)                                 UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message)                                     UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message)                                 UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message)                                     UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message))
-#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message)                             UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message))
-
-/* Shorthand */
-#ifdef UNITY_SHORTHAND_AS_OLD
-#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message))
-#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message))
-#endif
-#ifdef UNITY_SHORTHAND_AS_INT
-#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, message)
-#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
-#endif
-#ifdef  UNITY_SHORTHAND_AS_MEM
-#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, message)
-#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
-#endif
-#ifdef  UNITY_SHORTHAND_AS_RAW
-#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, message)
-#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, message)
-#endif
-#ifdef UNITY_SHORTHAND_AS_NONE
-#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message)                                       UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
-#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message)                                   UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
-#endif
-
-/* end of UNITY_FRAMEWORK_H */
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/movement/lib/chirpy_tx/test/unity_internals.h b/movement/lib/chirpy_tx/test/unity_internals.h
deleted file mode 100644 (file)
index 47bd370..0000000
+++ /dev/null
@@ -1,1145 +0,0 @@
-/* ==========================================
-    Unity Project - A Test Framework for C
-    Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
-    [Released under MIT License. Please refer to license.txt for details]
-========================================== */
-
-#ifndef UNITY_INTERNALS_H
-#define UNITY_INTERNALS_H
-
-#ifdef UNITY_INCLUDE_CONFIG_H
-#include "unity_config.h"
-#endif
-
-#ifndef UNITY_EXCLUDE_SETJMP_H
-#include <setjmp.h>
-#endif
-
-#ifndef UNITY_EXCLUDE_MATH_H
-#include <math.h>
-#endif
-
-#ifndef UNITY_EXCLUDE_STDDEF_H
-#include <stddef.h>
-#endif
-
-#ifdef UNITY_INCLUDE_PRINT_FORMATTED
-#include <stdarg.h>
-#endif
-
-/* Unity Attempts to Auto-Detect Integer Types
- * Attempt 1: UINT_MAX, ULONG_MAX in <limits.h>, or default to 32 bits
- * Attempt 2: UINTPTR_MAX in <stdint.h>, or default to same size as long
- * The user may override any of these derived constants:
- * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */
-#ifndef UNITY_EXCLUDE_STDINT_H
-#include <stdint.h>
-#endif
-
-#ifndef UNITY_EXCLUDE_LIMITS_H
-#include <limits.h>
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-  #define UNITY_FUNCTION_ATTR(a)    __attribute__((a))
-#else
-  #define UNITY_FUNCTION_ATTR(a)    /* ignore */
-#endif
-
-#ifndef UNITY_NORETURN
-  #if defined(__cplusplus)
-    #if __cplusplus >= 201103L
-      #define UNITY_NORETURN [[ noreturn ]]
-    #endif
-  #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
-    #if defined(_WIN32) && defined(_MSC_VER)
-      /* We are using MSVC compiler on Windows platform. */
-      /* Not all Windows SDKs supports <stdnoreturn.h>, but compiler can support C11: */
-      /* https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/ */
-      /* Not sure, that Mingw compilers has Windows SDK headers at all. */
-      #include <sdkddkver.h>
-    #endif
-
-    /* Using Windows SDK predefined macro for detecting supported SDK with MSVC compiler. */
-    /* Mingw GCC should work without that fixes. */
-    /* Based on: */
-    /* https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170 */
-    /* NTDDI_WIN10_FE is equal to Windows 10 SDK 2104 */
-    #if defined(_MSC_VER) && ((!defined(NTDDI_WIN10_FE)) || WDK_NTDDI_VERSION < NTDDI_WIN10_FE)
-      /* Based on tests and: */
-      /* https://docs.microsoft.com/en-us/cpp/c-language/noreturn?view=msvc-170 */
-      /* https://en.cppreference.com/w/c/language/_Noreturn */
-      #define UNITY_NORETURN _Noreturn
-    #else /* Using newer Windows SDK or not MSVC compiler */
-      #include <stdnoreturn.h>
-      #define UNITY_NORETURN noreturn
-    #endif
-  #endif
-#endif
-#ifndef UNITY_NORETURN
-  #define UNITY_NORETURN UNITY_FUNCTION_ATTR(noreturn)
-#endif
-
-/*-------------------------------------------------------
- * Guess Widths If Not Specified
- *-------------------------------------------------------*/
-
-/* Determine the size of an int, if not already specified.
- * We cannot use sizeof(int), because it is not yet defined
- * at this stage in the translation of the C program.
- * Also sizeof(int) does return the size in addressable units on all platforms,
- * which may not necessarily be the size in bytes.
- * Therefore, infer it from UINT_MAX if possible. */
-#ifndef UNITY_INT_WIDTH
-  #ifdef UINT_MAX
-    #if (UINT_MAX == 0xFFFF)
-      #define UNITY_INT_WIDTH (16)
-    #elif (UINT_MAX == 0xFFFFFFFF)
-      #define UNITY_INT_WIDTH (32)
-    #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF)
-      #define UNITY_INT_WIDTH (64)
-    #endif
-  #else /* Set to default */
-    #define UNITY_INT_WIDTH (32)
-  #endif /* UINT_MAX */
-#endif
-
-/* Determine the size of a long, if not already specified. */
-#ifndef UNITY_LONG_WIDTH
-  #ifdef ULONG_MAX
-    #if (ULONG_MAX == 0xFFFF)
-      #define UNITY_LONG_WIDTH (16)
-    #elif (ULONG_MAX == 0xFFFFFFFF)
-      #define UNITY_LONG_WIDTH (32)
-    #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF)
-      #define UNITY_LONG_WIDTH (64)
-    #endif
-  #else /* Set to default */
-    #define UNITY_LONG_WIDTH (32)
-  #endif /* ULONG_MAX */
-#endif
-
-/* Determine the size of a pointer, if not already specified. */
-#ifndef UNITY_POINTER_WIDTH
-  #ifdef UINTPTR_MAX
-    #if (UINTPTR_MAX <= 0xFFFF)
-      #define UNITY_POINTER_WIDTH (16)
-    #elif (UINTPTR_MAX <= 0xFFFFFFFF)
-      #define UNITY_POINTER_WIDTH (32)
-    #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF)
-      #define UNITY_POINTER_WIDTH (64)
-    #endif
-  #else /* Set to default */
-    #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH
-  #endif /* UINTPTR_MAX */
-#endif
-
-/*-------------------------------------------------------
- * Int Support (Define types based on detected sizes)
- *-------------------------------------------------------*/
-
-#if (UNITY_INT_WIDTH == 32)
-    typedef unsigned char   UNITY_UINT8;
-    typedef unsigned short  UNITY_UINT16;
-    typedef unsigned int    UNITY_UINT32;
-    typedef signed char     UNITY_INT8;
-    typedef signed short    UNITY_INT16;
-    typedef signed int      UNITY_INT32;
-#elif (UNITY_INT_WIDTH == 16)
-    typedef unsigned char   UNITY_UINT8;
-    typedef unsigned int    UNITY_UINT16;
-    typedef unsigned long   UNITY_UINT32;
-    typedef signed char     UNITY_INT8;
-    typedef signed int      UNITY_INT16;
-    typedef signed long     UNITY_INT32;
-#else
-    #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported)
-#endif
-
-/*-------------------------------------------------------
- * 64-bit Support
- *-------------------------------------------------------*/
-
-/* Auto-detect 64 Bit Support */
-#ifndef UNITY_SUPPORT_64
-  #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64
-    #define UNITY_SUPPORT_64
-  #endif
-#endif
-
-/* 64-Bit Support Dependent Configuration */
-#ifndef UNITY_SUPPORT_64
-    /* No 64-bit Support */
-    typedef UNITY_UINT32 UNITY_UINT;
-    typedef UNITY_INT32  UNITY_INT;
-    #define UNITY_MAX_NIBBLES (8)  /* Maximum number of nibbles in a UNITY_(U)INT */
-#else
-  /* 64-bit Support */
-  #if (UNITY_LONG_WIDTH == 32)
-    typedef unsigned long long UNITY_UINT64;
-    typedef signed long long   UNITY_INT64;
-  #elif (UNITY_LONG_WIDTH == 64)
-    typedef unsigned long      UNITY_UINT64;
-    typedef signed long        UNITY_INT64;
-  #else
-    #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported)
-  #endif
-    typedef UNITY_UINT64 UNITY_UINT;
-    typedef UNITY_INT64  UNITY_INT;
-    #define UNITY_MAX_NIBBLES (16) /* Maximum number of nibbles in a UNITY_(U)INT */
-#endif
-
-/*-------------------------------------------------------
- * Pointer Support
- *-------------------------------------------------------*/
-
-#if (UNITY_POINTER_WIDTH == 32)
-  #define UNITY_PTR_TO_INT UNITY_INT32
-  #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32
-#elif (UNITY_POINTER_WIDTH == 64)
-  #define UNITY_PTR_TO_INT UNITY_INT64
-  #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64
-#elif (UNITY_POINTER_WIDTH == 16)
-  #define UNITY_PTR_TO_INT UNITY_INT16
-  #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16
-#else
-  #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported)
-#endif
-
-#ifndef UNITY_PTR_ATTRIBUTE
-  #define UNITY_PTR_ATTRIBUTE
-#endif
-
-#ifndef UNITY_INTERNAL_PTR
-  #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void*
-#endif
-
-/* optionally define UNITY_COMPARE_PTRS_ON_ZERO_ARRAY */
-
-/*-------------------------------------------------------
- * Float Support
- *-------------------------------------------------------*/
-
-#ifdef UNITY_EXCLUDE_FLOAT
-
-/* No Floating Point Support */
-#ifndef UNITY_EXCLUDE_DOUBLE
-#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */
-#endif
-#ifndef UNITY_EXCLUDE_FLOAT_PRINT
-#define UNITY_EXCLUDE_FLOAT_PRINT
-#endif
-
-#else
-
-/* Floating Point Support */
-#ifndef UNITY_FLOAT_PRECISION
-#define UNITY_FLOAT_PRECISION (0.00001f)
-#endif
-#ifndef UNITY_FLOAT_TYPE
-#define UNITY_FLOAT_TYPE float
-#endif
-typedef UNITY_FLOAT_TYPE UNITY_FLOAT;
-
-/* isinf & isnan macros should be provided by math.h */
-#ifndef isinf
-/* The value of Inf - Inf is NaN */
-#define isinf(n) (isnan((n) - (n)) && !isnan(n))
-#endif
-
-#ifndef isnan
-/* NaN is the only floating point value that does NOT equal itself.
- * Therefore if n != n, then it is NaN. */
-#define isnan(n) ((n != n) ? 1 : 0)
-#endif
-
-#endif
-
-/*-------------------------------------------------------
- * Double Float Support
- *-------------------------------------------------------*/
-
-/* unlike float, we DON'T include by default */
-#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE)
-
-  /* No Floating Point Support */
-  #ifndef UNITY_EXCLUDE_DOUBLE
-  #define UNITY_EXCLUDE_DOUBLE
-  #else
-    #undef UNITY_INCLUDE_DOUBLE
-  #endif
-
-  #ifndef UNITY_EXCLUDE_FLOAT
-    #ifndef UNITY_DOUBLE_TYPE
-    #define UNITY_DOUBLE_TYPE double
-    #endif
-  typedef UNITY_FLOAT UNITY_DOUBLE;
-  /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */
-  #endif
-
-#else
-
-  /* Double Floating Point Support */
-  #ifndef UNITY_DOUBLE_PRECISION
-  #define UNITY_DOUBLE_PRECISION (1e-12)
-  #endif
-
-  #ifndef UNITY_DOUBLE_TYPE
-  #define UNITY_DOUBLE_TYPE double
-  #endif
-  typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE;
-
-#endif
-
-/*-------------------------------------------------------
- * Output Method: stdout (DEFAULT)
- *-------------------------------------------------------*/
-#ifndef UNITY_OUTPUT_CHAR
-  /* Default to using putchar, which is defined in stdio.h */
-  #include <stdio.h>
-  #define UNITY_OUTPUT_CHAR(a) (void)putchar(a)
-#else
-  /* If defined as something else, make sure we declare it here so it's ready for use */
-  #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION
-    extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION;
-  #endif
-#endif
-
-#ifndef UNITY_OUTPUT_FLUSH
-  #ifdef UNITY_USE_FLUSH_STDOUT
-    /* We want to use the stdout flush utility */
-    #include <stdio.h>
-    #define UNITY_OUTPUT_FLUSH()    (void)fflush(stdout)
-  #else
-    /* We've specified nothing, therefore flush should just be ignored */
-    #define UNITY_OUTPUT_FLUSH()    (void)0
-  #endif
-#else
-  /* If defined as something else, make sure we declare it here so it's ready for use */
-  #ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION
-    extern void UNITY_OUTPUT_FLUSH_HEADER_DECLARATION;
-  #endif
-#endif
-
-#ifndef UNITY_OUTPUT_FLUSH
-#define UNITY_FLUSH_CALL()
-#else
-#define UNITY_FLUSH_CALL()  UNITY_OUTPUT_FLUSH()
-#endif
-
-#ifndef UNITY_PRINT_EOL
-#define UNITY_PRINT_EOL()   UNITY_OUTPUT_CHAR('\n')
-#endif
-
-#ifndef UNITY_OUTPUT_START
-#define UNITY_OUTPUT_START()
-#endif
-
-#ifndef UNITY_OUTPUT_COMPLETE
-#define UNITY_OUTPUT_COMPLETE()
-#endif
-
-#ifdef UNITY_INCLUDE_EXEC_TIME
-  #if !defined(UNITY_EXEC_TIME_START) && \
-      !defined(UNITY_EXEC_TIME_STOP) && \
-      !defined(UNITY_PRINT_EXEC_TIME) && \
-      !defined(UNITY_TIME_TYPE)
-      /* If none any of these macros are defined then try to provide a default implementation */
-
-    #if defined(UNITY_CLOCK_MS)
-      /* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */
-      #define UNITY_TIME_TYPE UNITY_UINT
-      #define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS()
-      #define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS()
-      #define UNITY_PRINT_EXEC_TIME() { \
-        UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
-        UnityPrint(" ("); \
-        UnityPrintNumberUnsigned(execTimeMs); \
-        UnityPrint(" ms)"); \
-        }
-    #elif defined(_WIN32)
-      #include <time.h>
-      #define UNITY_TIME_TYPE clock_t
-      #define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC)
-      #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
-      #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime)
-      #define UNITY_PRINT_EXEC_TIME() { \
-        UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
-        UnityPrint(" ("); \
-        UnityPrintNumberUnsigned(execTimeMs); \
-        UnityPrint(" ms)"); \
-        }
-    #elif defined(__unix__) || defined(__APPLE__)
-      #include <time.h>
-      #define UNITY_TIME_TYPE struct timespec
-      #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t)
-      #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
-      #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime)
-      #define UNITY_PRINT_EXEC_TIME() { \
-        UNITY_UINT execTimeMs = ((Unity.CurrentTestStopTime.tv_sec - Unity.CurrentTestStartTime.tv_sec) * 1000L); \
-        execTimeMs += ((Unity.CurrentTestStopTime.tv_nsec - Unity.CurrentTestStartTime.tv_nsec) / 1000000L); \
-        UnityPrint(" ("); \
-        UnityPrintNumberUnsigned(execTimeMs); \
-        UnityPrint(" ms)"); \
-        }
-    #endif
-  #endif
-#endif
-
-#ifndef UNITY_EXEC_TIME_START
-#define UNITY_EXEC_TIME_START() do { /* nothing*/ } while (0)
-#endif
-
-#ifndef UNITY_EXEC_TIME_STOP
-#define UNITY_EXEC_TIME_STOP()  do { /* nothing*/ } while (0)
-#endif
-
-#ifndef UNITY_TIME_TYPE
-#define UNITY_TIME_TYPE         UNITY_UINT
-#endif
-
-#ifndef UNITY_PRINT_EXEC_TIME
-#define UNITY_PRINT_EXEC_TIME() do { /* nothing*/ } while (0)
-#endif
-
-/*-------------------------------------------------------
- * Footprint
- *-------------------------------------------------------*/
-
-#ifndef UNITY_LINE_TYPE
-#define UNITY_LINE_TYPE UNITY_UINT
-#endif
-
-#ifndef UNITY_COUNTER_TYPE
-#define UNITY_COUNTER_TYPE UNITY_UINT
-#endif
-
-/*-------------------------------------------------------
- * Internal Structs Needed
- *-------------------------------------------------------*/
-
-typedef void (*UnityTestFunction)(void);
-
-#define UNITY_DISPLAY_RANGE_INT  (0x10)
-#define UNITY_DISPLAY_RANGE_UINT (0x20)
-#define UNITY_DISPLAY_RANGE_HEX  (0x40)
-#define UNITY_DISPLAY_RANGE_CHAR (0x80)
-
-typedef enum
-{
-    UNITY_DISPLAY_STYLE_INT      = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_INT,
-    UNITY_DISPLAY_STYLE_INT8     = 1 + UNITY_DISPLAY_RANGE_INT,
-    UNITY_DISPLAY_STYLE_INT16    = 2 + UNITY_DISPLAY_RANGE_INT,
-    UNITY_DISPLAY_STYLE_INT32    = 4 + UNITY_DISPLAY_RANGE_INT,
-#ifdef UNITY_SUPPORT_64
-    UNITY_DISPLAY_STYLE_INT64    = 8 + UNITY_DISPLAY_RANGE_INT,
-#endif
-
-    UNITY_DISPLAY_STYLE_UINT     = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_UINT,
-    UNITY_DISPLAY_STYLE_UINT8    = 1 + UNITY_DISPLAY_RANGE_UINT,
-    UNITY_DISPLAY_STYLE_UINT16   = 2 + UNITY_DISPLAY_RANGE_UINT,
-    UNITY_DISPLAY_STYLE_UINT32   = 4 + UNITY_DISPLAY_RANGE_UINT,
-#ifdef UNITY_SUPPORT_64
-    UNITY_DISPLAY_STYLE_UINT64   = 8 + UNITY_DISPLAY_RANGE_UINT,
-#endif
-
-    UNITY_DISPLAY_STYLE_HEX8     = 1 + UNITY_DISPLAY_RANGE_HEX,
-    UNITY_DISPLAY_STYLE_HEX16    = 2 + UNITY_DISPLAY_RANGE_HEX,
-    UNITY_DISPLAY_STYLE_HEX32    = 4 + UNITY_DISPLAY_RANGE_HEX,
-#ifdef UNITY_SUPPORT_64
-    UNITY_DISPLAY_STYLE_HEX64    = 8 + UNITY_DISPLAY_RANGE_HEX,
-#endif
-
-    UNITY_DISPLAY_STYLE_CHAR     = 1 + UNITY_DISPLAY_RANGE_CHAR + UNITY_DISPLAY_RANGE_INT,
-
-    UNITY_DISPLAY_STYLE_UNKNOWN
-} UNITY_DISPLAY_STYLE_T;
-
-typedef enum
-{
-    UNITY_WITHIN           = 0x0,
-    UNITY_EQUAL_TO         = 0x1,
-    UNITY_GREATER_THAN     = 0x2,
-    UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO,
-    UNITY_SMALLER_THAN     = 0x4,
-    UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO,
-    UNITY_NOT_EQUAL        = 0x0,
-    UNITY_UNKNOWN
-} UNITY_COMPARISON_T;
-
-#ifndef UNITY_EXCLUDE_FLOAT
-typedef enum UNITY_FLOAT_TRAIT
-{
-    UNITY_FLOAT_IS_NOT_INF       = 0,
-    UNITY_FLOAT_IS_INF,
-    UNITY_FLOAT_IS_NOT_NEG_INF,
-    UNITY_FLOAT_IS_NEG_INF,
-    UNITY_FLOAT_IS_NOT_NAN,
-    UNITY_FLOAT_IS_NAN,
-    UNITY_FLOAT_IS_NOT_DET,
-    UNITY_FLOAT_IS_DET,
-    UNITY_FLOAT_INVALID_TRAIT
-} UNITY_FLOAT_TRAIT_T;
-#endif
-
-typedef enum
-{
-    UNITY_ARRAY_TO_VAL = 0,
-    UNITY_ARRAY_TO_ARRAY,
-    UNITY_ARRAY_UNKNOWN
-} UNITY_FLAGS_T;
-
-struct UNITY_STORAGE_T
-{
-    const char* TestFile;
-    const char* CurrentTestName;
-#ifndef UNITY_EXCLUDE_DETAILS
-    const char* CurrentDetail1;
-    const char* CurrentDetail2;
-#endif
-    UNITY_LINE_TYPE CurrentTestLineNumber;
-    UNITY_COUNTER_TYPE NumberOfTests;
-    UNITY_COUNTER_TYPE TestFailures;
-    UNITY_COUNTER_TYPE TestIgnores;
-    UNITY_COUNTER_TYPE CurrentTestFailed;
-    UNITY_COUNTER_TYPE CurrentTestIgnored;
-#ifdef UNITY_INCLUDE_EXEC_TIME
-    UNITY_TIME_TYPE CurrentTestStartTime;
-    UNITY_TIME_TYPE CurrentTestStopTime;
-#endif
-#ifndef UNITY_EXCLUDE_SETJMP_H
-    jmp_buf AbortFrame;
-#endif
-};
-
-extern struct UNITY_STORAGE_T Unity;
-
-/*-------------------------------------------------------
- * Test Suite Management
- *-------------------------------------------------------*/
-
-void UnityBegin(const char* filename);
-int  UnityEnd(void);
-void UnitySetTestFile(const char* filename);
-void UnityConcludeTest(void);
-
-#ifndef RUN_TEST
-void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum);
-#else
-#define UNITY_SKIP_DEFAULT_RUNNER
-#endif
-
-/*-------------------------------------------------------
- * Details Support
- *-------------------------------------------------------*/
-
-#ifdef UNITY_EXCLUDE_DETAILS
-#define UNITY_CLR_DETAILS()
-#define UNITY_SET_DETAIL(d1)
-#define UNITY_SET_DETAILS(d1,d2)
-#else
-#define UNITY_CLR_DETAILS()         do { Unity.CurrentDetail1 = 0;     Unity.CurrentDetail2 = 0;    } while (0)
-#define UNITY_SET_DETAIL(d1)        do { Unity.CurrentDetail1 = (d1);  Unity.CurrentDetail2 = 0;    } while (0)
-#define UNITY_SET_DETAILS(d1,d2)    do { Unity.CurrentDetail1 = (d1);  Unity.CurrentDetail2 = (d2); } while (0)
-
-#ifndef UNITY_DETAIL1_NAME
-#define UNITY_DETAIL1_NAME "Function"
-#endif
-
-#ifndef UNITY_DETAIL2_NAME
-#define UNITY_DETAIL2_NAME "Argument"
-#endif
-#endif
-
-#ifdef UNITY_PRINT_TEST_CONTEXT
-void UNITY_PRINT_TEST_CONTEXT(void);
-#endif
-
-/*-------------------------------------------------------
- * Test Output
- *-------------------------------------------------------*/
-
-void UnityPrint(const char* string);
-
-#ifdef UNITY_INCLUDE_PRINT_FORMATTED
-void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...);
-#endif
-
-void UnityPrintLen(const char* string, const UNITY_UINT32 length);
-void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number);
-void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style);
-void UnityPrintNumber(const UNITY_INT number_to_print);
-void UnityPrintNumberUnsigned(const UNITY_UINT number);
-void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print);
-
-#ifndef UNITY_EXCLUDE_FLOAT_PRINT
-void UnityPrintFloat(const UNITY_DOUBLE input_number);
-#endif
-
-/*-------------------------------------------------------
- * Test Assertion Functions
- *-------------------------------------------------------
- *  Use the macros below this section instead of calling
- *  these directly. The macros have a consistent naming
- *  convention and will pull in file and line information
- *  for you. */
-
-void UnityAssertEqualNumber(const UNITY_INT expected,
-                            const UNITY_INT actual,
-                            const char* msg,
-                            const UNITY_LINE_TYPE lineNumber,
-                            const UNITY_DISPLAY_STYLE_T style);
-
-void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold,
-                                           const UNITY_INT actual,
-                                           const UNITY_COMPARISON_T compare,
-                                           const char *msg,
-                                           const UNITY_LINE_TYPE lineNumber,
-                                           const UNITY_DISPLAY_STYLE_T style);
-
-void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
-                              UNITY_INTERNAL_PTR actual,
-                              const UNITY_UINT32 num_elements,
-                              const char* msg,
-                              const UNITY_LINE_TYPE lineNumber,
-                              const UNITY_DISPLAY_STYLE_T style,
-                              const UNITY_FLAGS_T flags);
-
-void UnityAssertBits(const UNITY_INT mask,
-                     const UNITY_INT expected,
-                     const UNITY_INT actual,
-                     const char* msg,
-                     const UNITY_LINE_TYPE lineNumber);
-
-void UnityAssertEqualString(const char* expected,
-                            const char* actual,
-                            const char* msg,
-                            const UNITY_LINE_TYPE lineNumber);
-
-void UnityAssertEqualStringLen(const char* expected,
-                            const char* actual,
-                            const UNITY_UINT32 length,
-                            const char* msg,
-                            const UNITY_LINE_TYPE lineNumber);
-
-void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected,
-                                  const char** actual,
-                                  const UNITY_UINT32 num_elements,
-                                  const char* msg,
-                                  const UNITY_LINE_TYPE lineNumber,
-                                  const UNITY_FLAGS_T flags);
-
-void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected,
-                             UNITY_INTERNAL_PTR actual,
-                             const UNITY_UINT32 length,
-                             const UNITY_UINT32 num_elements,
-                             const char* msg,
-                             const UNITY_LINE_TYPE lineNumber,
-                             const UNITY_FLAGS_T flags);
-
-void UnityAssertNumbersWithin(const UNITY_UINT delta,
-                              const UNITY_INT expected,
-                              const UNITY_INT actual,
-                              const char* msg,
-                              const UNITY_LINE_TYPE lineNumber,
-                              const UNITY_DISPLAY_STYLE_T style);
-
-void UnityAssertNumbersArrayWithin(const UNITY_UINT delta,
-                                   UNITY_INTERNAL_PTR expected,
-                                   UNITY_INTERNAL_PTR actual,
-                                   const UNITY_UINT32 num_elements,
-                                   const char* msg,
-                                   const UNITY_LINE_TYPE lineNumber,
-                                   const UNITY_DISPLAY_STYLE_T style,
-                                   const UNITY_FLAGS_T flags);
-
-#ifndef UNITY_EXCLUDE_SETJMP_H
-UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line);
-UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line);
-#else
-void UnityFail(const char* message, const UNITY_LINE_TYPE line);
-void UnityIgnore(const char* message, const UNITY_LINE_TYPE line);
-#endif
-
-void UnityMessage(const char* message, const UNITY_LINE_TYPE line);
-
-#ifndef UNITY_EXCLUDE_FLOAT
-void UnityAssertFloatsWithin(const UNITY_FLOAT delta,
-                             const UNITY_FLOAT expected,
-                             const UNITY_FLOAT actual,
-                             const char* msg,
-                             const UNITY_LINE_TYPE lineNumber);
-
-void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta,
-                                const UNITY_FLOAT expected,
-                                const UNITY_FLOAT actual,
-                                const char* msg,
-                                const UNITY_LINE_TYPE lineNumber);
-
-void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold,
-                                   const UNITY_FLOAT actual,
-                                   const UNITY_COMPARISON_T compare,
-                                   const char* msg,
-                                   const UNITY_LINE_TYPE linenumber);
-
-void UnityAssertWithinFloatArray(const UNITY_FLOAT delta,
-                                 UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected,
-                                 UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual,
-                                 const UNITY_UINT32 num_elements,
-                                 const char* msg,
-                                 const UNITY_LINE_TYPE lineNumber,
-                                 const UNITY_FLAGS_T flags);
-
-void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
-                             const char* msg,
-                             const UNITY_LINE_TYPE lineNumber,
-                             const UNITY_FLOAT_TRAIT_T style);
-#endif
-
-#ifndef UNITY_EXCLUDE_DOUBLE
-void UnityAssertDoublesWithin(const UNITY_DOUBLE delta,
-                              const UNITY_DOUBLE expected,
-                              const UNITY_DOUBLE actual,
-                              const char* msg,
-                              const UNITY_LINE_TYPE lineNumber);
-
-void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta,
-                                 const UNITY_DOUBLE expected,
-                                 const UNITY_DOUBLE actual,
-                                 const char* msg,
-                                 const UNITY_LINE_TYPE lineNumber);
-
-void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold,
-                                    const UNITY_DOUBLE actual,
-                                    const UNITY_COMPARISON_T compare,
-                                    const char* msg,
-                                    const UNITY_LINE_TYPE linenumber);
-
-void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta,
-                                  UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected,
-                                  UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual,
-                                  const UNITY_UINT32 num_elements,
-                                  const char* msg,
-                                  const UNITY_LINE_TYPE lineNumber,
-                                  const UNITY_FLAGS_T flags);
-
-void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual,
-                              const char* msg,
-                              const UNITY_LINE_TYPE lineNumber,
-                              const UNITY_FLOAT_TRAIT_T style);
-#endif
-
-/*-------------------------------------------------------
- * Helpers
- *-------------------------------------------------------*/
-
-UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size);
-#ifndef UNITY_EXCLUDE_FLOAT
-UNITY_INTERNAL_PTR UnityFloatToPtr(const float num);
-#endif
-#ifndef UNITY_EXCLUDE_DOUBLE
-UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num);
-#endif
-
-/*-------------------------------------------------------
- * Error Strings We Might Need
- *-------------------------------------------------------*/
-
-extern const char UnityStrOk[];
-extern const char UnityStrPass[];
-extern const char UnityStrFail[];
-extern const char UnityStrIgnore[];
-
-extern const char UnityStrErrFloat[];
-extern const char UnityStrErrDouble[];
-extern const char UnityStrErr64[];
-extern const char UnityStrErrShorthand[];
-
-/*-------------------------------------------------------
- * Test Running Macros
- *-------------------------------------------------------*/
-
-#ifndef UNITY_EXCLUDE_SETJMP_H
-#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0)
-#define TEST_ABORT() longjmp(Unity.AbortFrame, 1)
-#else
-#define TEST_PROTECT() 1
-#define TEST_ABORT() return
-#endif
-
-/* Automatically enable variadic macros support, if it not enabled before */
-#ifndef UNITY_SUPPORT_VARIADIC_MACROS
-  #ifdef __STDC_VERSION__
-    #if __STDC_VERSION__ >= 199901L
-      #define UNITY_SUPPORT_VARIADIC_MACROS
-    #endif
-  #endif
-#endif
-
-/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */
-#ifndef RUN_TEST
-#ifdef UNITY_SUPPORT_VARIADIC_MACROS
-#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway)
-#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line)
-#endif
-#endif
-
-/* Enable default macros for masking param tests test cases */
-#ifdef UNITY_SUPPORT_TEST_CASES
-  #ifdef UNITY_SUPPORT_VARIADIC_MACROS
-    #if !defined(TEST_CASE) && !defined(UNITY_EXCLUDE_TEST_CASE)
-      #define TEST_CASE(...)
-    #endif
-    #if !defined(TEST_RANGE) && !defined(UNITY_EXCLUDE_TEST_RANGE)
-      #define TEST_RANGE(...)
-    #endif
-  #endif
-#endif
-
-/* If we can't do the tricky version, we'll just have to require them to always include the line number */
-#ifndef RUN_TEST
-#ifdef CMOCK
-#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num)
-#else
-#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__)
-#endif
-#endif
-
-#define TEST_LINE_NUM (Unity.CurrentTestLineNumber)
-#define TEST_IS_IGNORED (Unity.CurrentTestIgnored)
-#define UNITY_NEW_TEST(a) \
-    Unity.CurrentTestName = (a); \
-    Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \
-    Unity.NumberOfTests++;
-
-#ifndef UNITY_BEGIN
-#define UNITY_BEGIN() UnityBegin(__FILE__)
-#endif
-
-#ifndef UNITY_END
-#define UNITY_END() UnityEnd()
-#endif
-
-#ifndef UNITY_SHORTHAND_AS_INT
-#ifndef UNITY_SHORTHAND_AS_MEM
-#ifndef UNITY_SHORTHAND_AS_NONE
-#ifndef UNITY_SHORTHAND_AS_RAW
-#define UNITY_SHORTHAND_AS_OLD
-#endif
-#endif
-#endif
-#endif
-
-/*-----------------------------------------------
- * Command Line Argument Support
- *-----------------------------------------------*/
-
-#ifdef UNITY_USE_COMMAND_LINE_ARGS
-int UnityParseOptions(int argc, char** argv);
-int UnityTestMatches(void);
-#endif
-
-/*-------------------------------------------------------
- * Basic Fail and Ignore
- *-------------------------------------------------------*/
-
-#define UNITY_TEST_FAIL(line, message)   UnityFail(   (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line))
-
-/*-------------------------------------------------------
- * Test Asserts
- *-------------------------------------------------------*/
-
-#define UNITY_TEST_ASSERT(condition, line, message)                                              do { if (condition) { /* nothing*/ } else { UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message)); } } while (0)
-#define UNITY_TEST_ASSERT_NULL(pointer, line, message)                                           UNITY_TEST_ASSERT(((pointer) == NULL),  (UNITY_LINE_TYPE)(line), (message))
-#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message)                                       UNITY_TEST_ASSERT(((pointer) != NULL),  (UNITY_LINE_TYPE)(line), (message))
-#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message)                                          UNITY_TEST_ASSERT(((pointer[0]) == 0),  (UNITY_LINE_TYPE)(line), (message))
-#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message)                                      UNITY_TEST_ASSERT(((pointer[0]) != 0),  (UNITY_LINE_TYPE)(line), (message))
-
-#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message)                             UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
-#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message)                            UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
-#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
-#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
-#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message)                            UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
-#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
-#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message)                          UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
-#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message)                          UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
-#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message)                            UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
-#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
-#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
-#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message)                            UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
-#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message)                            UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line))
-
-#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message)                        UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),               (UNITY_INT)(actual),               UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message)                       UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold),  (UNITY_INT)(UNITY_INT8 )(actual),  UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold),  (UNITY_INT)(UNITY_INT16)(actual),  UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold),  (UNITY_INT)(UNITY_INT32)(actual),  UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message)                       UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),               (UNITY_INT)(actual),               UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message)                       UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message)                       UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold),  (UNITY_INT)(UNITY_INT8 )(actual),  UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
-
-#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),              (UNITY_INT)(actual),              UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
-#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
-#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
-#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
-#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),              (UNITY_INT)(actual),              UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
-#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
-#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
-#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
-#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
-#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
-#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
-#define UNITY_TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
-
-#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),              (UNITY_INT)(actual),              UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold),              (UNITY_INT)(actual),              UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_CHAR(threshold, actual, line, message)                    UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
-
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message)                 UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)              (threshold), (UNITY_INT)              (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16) (threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32) (threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)              (threshold), (UNITY_INT)              (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
-
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message)                 UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)             (threshold),  (UNITY_INT)              (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold),  (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold),  (UNITY_INT)(UNITY_INT16) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold),  (UNITY_INT)(UNITY_INT32) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)             (threshold),  (UNITY_INT)              (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR(threshold, actual, line, message)                UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold),  (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
-
-#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message)                     UnityAssertNumbersWithin(              (delta), (UNITY_INT)                          (expected), (UNITY_INT)                          (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
-#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message)                    UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 )             (expected), (UNITY_INT)(UNITY_INT8 )             (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
-#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16)             (expected), (UNITY_INT)(UNITY_INT16)             (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
-#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32)             (expected), (UNITY_INT)(UNITY_INT32)             (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
-#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message)                    UnityAssertNumbersWithin(              (delta), (UNITY_INT)                          (expected), (UNITY_INT)                          (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
-#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
-#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message)                  UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
-#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message)                  UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
-#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message)                    UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
-#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
-#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
-#define UNITY_TEST_ASSERT_CHAR_WITHIN(delta, expected, actual, line, message)                    UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 )             (expected), (UNITY_INT)(UNITY_INT8 )             (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
-
-#define UNITY_TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)     UnityAssertNumbersArrayWithin(              (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)    UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)    UnityAssertNumbersArrayWithin(              (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)    UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)    UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY)
-
-
-#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message)                             UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER)
-#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message)                          UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message)                 UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message)                     UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
-
-#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT,     UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8,    UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16,   UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32,   UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT,    UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8,   UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message)      UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16,  UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message)      UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32,  UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8,    UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16,   UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32,   UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message)      UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR,    UNITY_ARRAY_TO_ARRAY)
-
-#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message)          UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)              (expected), (UNITY_INT_WIDTH / 8)),          (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT,     UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8  )(expected), 1),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8,    UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16,   UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32,   UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)              (expected), (UNITY_INT_WIDTH / 8)),          (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT,    UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )(expected), 1),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8,   UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)(expected), 2),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16,  UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)(expected), 4),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32,  UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8  )(expected), 1),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8,    UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16,   UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32,   UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message)          UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT)       (expected), (UNITY_POINTER_WIDTH / 8)),      (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message)       UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message)  UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements, line, message)         UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8  )(expected), 1),                              (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR,    UNITY_ARRAY_TO_VAL)
-
-#ifdef UNITY_SUPPORT_64
-#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
-#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message)                          UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
-#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message)                           UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
-#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64,  UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message)      UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64,  UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64,  UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message)       UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message)        UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64,  UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
-#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message)                  UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
-#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message)                   UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL,        (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message)                     UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL,        (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message)                      UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL,        (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
-#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
-#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
-#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message)                  UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message)                   UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN,     (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message)              UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message)               UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
-#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY)
-#else
-#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message)                          UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message)       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message)      UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message)       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message)              UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message)              UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
-#endif
-
-#ifdef UNITY_EXCLUDE_FLOAT
-#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message)                       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message)       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message)        UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message)                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message)               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message)                      UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message)                                    UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message)                                UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message)                                    UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message)                            UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message)                                UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message)                            UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message)                                UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message)                        UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
-#else
-#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message)                   UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message)               UnityAssertFloatsNotWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message)                           UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message))
-#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message)                       UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message))
-#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message)  UnityAssertWithinFloatArray((UNITY_FLOAT)(delta), (UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message)       UnityAssertWithinFloatArray((UNITY_FLOAT)0, (UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message)        UnityAssertWithinFloatArray((UNITY_FLOAT)0, UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message)                   UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message)               UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message)                      UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message)                  UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message)                                    UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message)                                UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message)                                    UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN)
-#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message)                            UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message)                                UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message)                            UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message)                                UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN)
-#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message)                        UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET)
-#endif
-
-#ifdef UNITY_EXCLUDE_DOUBLE
-#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message)              UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message)                          UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message)                      UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message)      UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message)       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message)                  UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message)              UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message)                     UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message)                 UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message)                                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message)                               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message)                                   UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message)                               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message)                           UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message)                               UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message)                       UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
-#else
-#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message)                  UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message)              UnityAssertDoublesNotWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message)                          UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message))
-#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message)                      UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message))
-#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)(delta), (UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message)      UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, (UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
-#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message)       UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
-#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message)                  UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message)              UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message)                     UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message)                 UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
-#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message)                                   UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message)                               UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message)                                   UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message)                           UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message)                               UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message)                           UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message)                               UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN)
-#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message)                       UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET)
-#endif
-
-/* End of UNITY_INTERNALS_H */
-#endif
diff --git a/movement/lib/morsecalc/calc.c b/movement/lib/morsecalc/calc.c
deleted file mode 100644 (file)
index ed7eb25..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "calc.h"
-#include "calc_fns.h"
-
-#define CALC_NAN (0.0/0.0)
-
-/* calc_init 
- * Initialize calculator
- */
-int calc_init(calc_state_t *cs) {    
-    memset(cs->stack, CALC_NAN, N_STACK*sizeof(cs->stack[0]));
-    cs->s = 0; 
-    cs->mem = 0.0;
-    return 0;
-}
-
-/* calc_input_function
- * Try to execute the token as a calculator function
- */
-int calc_input_function(calc_state_t *cs, char *token) {
-    for(uint8_t idx=0; idx<sizeof(calc_dict)/sizeof(calc_dict[0]); idx++) {
-        for(uint8_t idxn=0; idxn<calc_dict[idx].n_names; idxn++) {
-            if(0 == strcmp(calc_dict[idx].names[idxn], token)) { // Found a match
-                return (*calc_dict[idx].fn)(cs); // Run calculator function
-            }
-        }
-    }
-    return -1; // Unrecognized function name
-}
-
-/* calc_input_float
- * Read the token as a float.
- * For convenience, numerals can be written in binary:
- * 0     1    2    3    4    5    6    7    8    9
- * .     -    -.   --   -..  -.-  --.  ---  -... -..-
- * e     t    n    m    d    k    g    o    b    x
- * 
- * Exponent signs must be entered as "p".
- * Decimal place "." can be entered as "h" (code ....)
- * Sign "-" can be entered as "Ch digraph" (code ----)
- * 
- * e.g. "4.2e-3" can be entered directly or as "4h2pC3"
- * similarly, "0.0042" can be "eheedn"
- */ 
-#define REPCHAR(X,Y) for(idx=0; idx<strlen(token); idx++) \
-    if(X==token[idx]) token[idx] = Y
-int calc_input_float(calc_state_t *cs, char *token) {
-    uint8_t idx;
-    REPCHAR('e', '0');
-    REPCHAR('t', '1');
-    REPCHAR('n', '2');
-    REPCHAR('m', '3');
-    REPCHAR('d', '4');
-    REPCHAR('k', '5');
-    REPCHAR('g', '6');
-    REPCHAR('o', '7');
-    REPCHAR('b', '8');
-    REPCHAR('x', '9');
-    REPCHAR('h', '.');
-    REPCHAR('C', '-');
-    REPCHAR('p', 'E');
-    
-    char *endptr;
-    double d = calc_strtof(token, &endptr);
-    if(!endptr || (uint8_t)(endptr-token)<strlen(token)) return -1; // Bad format
-    if(cs->s >= N_STACK) return -2; // Stack full
-    cs->stack[cs->s++] = d;
-    return 0;
-}
-
-/* calc_input
- * Manipulate the stack using the entered token.
- * If the token isn't a calculator function, try to convert it to a number and
- * add it to the stack.
- *
- * Return values: 
- *  0 if function completed successfully.
- * -1 if token isn't a calculator function and couldn't convert to float.
- * -2 if stack is too full or too empty
- * -3 for something else
- */
-int calc_input(calc_state_t *cs, char *token) {
-    int retval = calc_input_function(cs, token);
-    if(-1 == retval) retval = calc_input_float(cs, token);
-    return retval;
-}
diff --git a/movement/lib/morsecalc/calc.h b/movement/lib/morsecalc/calc.h
deleted file mode 100644 (file)
index 1fc4749..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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 CALC_H_INCLUDED
-#define CALC_H_INCLUDED 
-
-#include <stdint.h>
-
-#define N_STACK 10 
-
-typedef struct {
-    double stack[N_STACK];
-    double mem;
-    uint8_t s; // # of items in stack 
-} calc_state_t;
-int calc_init(calc_state_t *cs);
-int calc_input(calc_state_t *cs, char *token);
-int calc_input_function(calc_state_t *cs, char *token);
-int calc_input_float(calc_state_t *cs, char *token);
-double calc_strtof(const char *str, char **endptr);
-
-#endif
diff --git a/movement/lib/morsecalc/calc_fns.c b/movement/lib/morsecalc/calc_fns.c
deleted file mode 100644 (file)
index 9b40a6f..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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.
- */
-
-#include <string.h>
-#include <math.h>
-
-#include "calc_fns.h" 
-
-#define STACK_CHECK_0_IN_1_OUT if(cs->s >= N_STACK) return -2
-#define STACK_CHECK_1_IN_0_OUT if(cs->s < 1) return -2
-#define STACK_CHECK_1_IN_1_OUT if(cs->s < 1) return -2
-#define STACK_CHECK_2_IN_1_OUT if(cs->s < 2) return -2
-#define STACK_CHECK_2_IN_2_OUT if(cs->s < 2) return -2
-
-static const double to_rad = M_PI/180;
-static const double to_deg = 180/M_PI;
-
-// Stack and memory control
-int calc_delete(calc_state_t *cs) {
-    if(cs->s < 1) return -2; // Check stack
-    cs->s--; 
-    return 0;
-}
-int calc_clear_stack(calc_state_t *cs) {
-    memset(cs->stack, (0.0/0.0), N_STACK*sizeof(cs->stack[0]));
-    cs->s = 0; 
-    return 0;
-}
-int calc_flip(calc_state_t *cs) {
-    STACK_CHECK_2_IN_2_OUT;
-    double buff = cs->stack[cs->s-2];
-    cs->stack[cs->s-2] = cs->stack[cs->s-1];
-    cs->stack[cs->s-1] = buff;
-    return 0;
-}
-int calc_mem_clear(calc_state_t *cs) {
-    cs->mem = 0.0;
-    return 0;
-}
-int calc_mem_recall(calc_state_t *cs) { 
-    STACK_CHECK_0_IN_1_OUT;
-    cs->stack[cs->s++] = cs->mem;
-    return 0;
-}
-int calc_mem_add(calc_state_t *cs) {
-    STACK_CHECK_1_IN_0_OUT;
-    cs->mem += cs->stack[cs->s-1];
-    cs->s--;
-    return 0;
-}
-int calc_mem_subtract(calc_state_t *cs) {
-    STACK_CHECK_1_IN_0_OUT;
-    cs->mem -= cs->stack[cs->s-1];
-    cs->s--;
-    return 0;
-}
-
-// Basic operations
-int calc_add(calc_state_t *cs) {
-    STACK_CHECK_2_IN_1_OUT; 
-    cs->stack[cs->s-2] += cs->stack[cs->s-1];
-    cs->s--;
-    return 0;
-}
-int calc_subtract(calc_state_t *cs) {
-    STACK_CHECK_2_IN_1_OUT; 
-    cs->stack[cs->s-2] -= cs->stack[cs->s-1];
-    cs->s--;
-    return 0;
-}
-int calc_negate(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = -cs->stack[cs->s-1];
-    return 0;
-}
-int calc_multiply(calc_state_t *cs) {
-    STACK_CHECK_2_IN_1_OUT; 
-    cs->stack[cs->s-2] *= cs->stack[cs->s-1];
-    cs->s--;
-    return 0;
-}
-int calc_divide(calc_state_t *cs) {
-    STACK_CHECK_2_IN_1_OUT;
-    cs->stack[cs->s-2] /= cs->stack[cs->s-1];
-    cs->s--;
-    return 0;
-}
-
-int calc_invert(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = 1.0/cs->stack[cs->s-1];
-    return 0;
-}
-
-// Constants
-int calc_e(calc_state_t *cs) {
-    STACK_CHECK_0_IN_1_OUT;
-    cs->stack[cs->s++] = M_E;
-    return 0;
-}
-int calc_pi(calc_state_t *cs) {
-    STACK_CHECK_0_IN_1_OUT;
-    cs->stack[cs->s++] = M_PI;
-    return 0;
-}
-
-// Exponential/logarithmic
-int calc_exp(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = exp(cs->stack[cs->s-1]);
-    return 0;
-}
-int calc_pow(calc_state_t *cs) {
-    STACK_CHECK_2_IN_1_OUT;
-    cs->stack[cs->s-2] = pow(cs->stack[cs->s-2], cs->stack[cs->s-1]);
-    cs->s--;
-    return 0;
-}
-int calc_ln(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = log(cs->stack[cs->s-1]);
-    return 0;
-}
-int calc_log(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = log10(cs->stack[cs->s-1]);
-    return 0;
-}
-int calc_sqrt(calc_state_t *cs)  {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = sqrt(cs->stack[cs->s-1]);
-    return 0;
-}
-
-// Trigonometric
-int calc_sin(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = sin(cs->stack[cs->s-1]);
-    return 0;
-}
-int calc_cos(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = cos(cs->stack[cs->s-1]);
-    return 0;
-}
-int calc_tan(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = tan(cs->stack[cs->s-1]);
-    return 0;
-} 
-int calc_asin(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = asin(cs->stack[cs->s-1]);
-    return 0;
-}
-int calc_acos(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = acos(cs->stack[cs->s-1]);
-    return 0;
-}
-int calc_atan(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = atan(cs->stack[cs->s-1]);
-    return 0;
-}
-int calc_atan2(calc_state_t *cs) {
-    STACK_CHECK_2_IN_1_OUT;
-    cs->stack[cs->s-2] = atan2(cs->stack[cs->s-2], cs->stack[cs->s-1]);
-    cs->s--;
-    return 0;
-}
-
-int calc_sind(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = sin(cs->stack[cs->s-1]*to_rad);
-    return 0;
-}
-int calc_cosd(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = cos(cs->stack[cs->s-1]*to_rad);
-    return 0;
-}
-int calc_tand(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = tan(cs->stack[cs->s-1]*to_rad);
-    return 0;
-}
-int calc_asind(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = asin(cs->stack[cs->s-1])*to_deg;
-    return 0;
-}
-int calc_acosd(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = acos(cs->stack[cs->s-1])*to_deg;
-    return 0;
-}
-int calc_atand(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT; 
-    cs->stack[cs->s-1] = atan(cs->stack[cs->s-1])*to_deg;
-    return 0;
-}
-int calc_atan2d(calc_state_t *cs) {
-    STACK_CHECK_2_IN_1_OUT;
-    cs->stack[cs->s-2] = atan2(cs->stack[cs->s-2], cs->stack[cs->s-1])*to_deg;
-    cs->s--;
-    return 0;
-}
-int calc_torad(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT;
-    cs->stack[cs->s-1] = cs->stack[cs->s-1]*to_rad;
-    return 0;
-}
-int calc_todeg(calc_state_t *cs) {
-    STACK_CHECK_1_IN_1_OUT;
-    cs->stack[cs->s-1] = cs->stack[cs->s-1]*to_deg;
-    return 0;
-}
-
diff --git a/movement/lib/morsecalc/calc_fns.h b/movement/lib/morsecalc/calc_fns.h
deleted file mode 100644 (file)
index 15f56b0..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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.
- */
-
-#include "calc.h"
-
-// Stack and register control
-int calc_delete(calc_state_t *cs);
-int calc_clear_stack(calc_state_t *cs);
-int calc_flip(calc_state_t *cs);
-int calc_mem_clear(calc_state_t *cs);
-int calc_mem_recall(calc_state_t *cs);
-int calc_mem_add(calc_state_t *cs);
-int calc_mem_subtract(calc_state_t *cs);
-
-// Basic operations
-int calc_add(calc_state_t *cs);
-int calc_subtract(calc_state_t *cs);
-int calc_negate(calc_state_t *cs);
-int calc_multiply(calc_state_t *cs);
-int calc_divide(calc_state_t *cs);
-int calc_invert(calc_state_t *cs);
-
-// Constants
-int calc_e(calc_state_t *cs);
-int calc_pi(calc_state_t *cs);
-
-// Exponential/logarithmic
-int calc_exp(calc_state_t *cs);
-int calc_pow(calc_state_t *cs);
-int calc_ln(calc_state_t *cs);
-int calc_log(calc_state_t *cs);
-int calc_sqrt(calc_state_t *cs);
-
-// Trigonometric
-int calc_sin(calc_state_t *cs);
-int calc_cos(calc_state_t *cs);
-int calc_tan(calc_state_t *cs);
-int calc_asin(calc_state_t *cs);
-int calc_acos(calc_state_t *cs);
-int calc_atan(calc_state_t *cs);
-int calc_atan2(calc_state_t *cs);
-int calc_sind(calc_state_t *cs);
-int calc_cosd(calc_state_t *cs);
-int calc_tand(calc_state_t *cs);
-int calc_asind(calc_state_t *cs);
-int calc_acosd(calc_state_t *cs);
-int calc_atand(calc_state_t *cs);
-int calc_atan2d(calc_state_t *cs);
-int calc_torad(calc_state_t *cs);
-int calc_todeg(calc_state_t *cs);
-
-// Dictionary definition
-typedef int (*calc_fn_t)(calc_state_t *cs);
-typedef struct {
-    uint8_t n_names; // Number of aliases
-    const char ** names; // Token to use to run this function
-    calc_fn_t fn; // Pointer to function 
-} calc_dict_entry_t;
-
-static const calc_dict_entry_t calc_dict[] = {
-    // Stack and register control
-    {1, (const char*[]){"x"}, &calc_delete},
-    {1, (const char*[]){"xx"}, &calc_clear_stack},
-    {1, (const char*[]){"xxx"}, &calc_init},
-    {1, (const char*[]){"f"}, &calc_flip},
-    {1, (const char*[]){"mc"}, &calc_mem_clear},
-    {1, (const char*[]){"mr"}, &calc_mem_recall},
-    {1, (const char*[]){"ma"}, &calc_mem_add},
-    {1, (const char*[]){"ms"}, &calc_mem_subtract},
-
-    // Basic operations
-    {1, (const char*[]){"a"}, &calc_add}, 
-    {1, (const char*[]){"s"}, &calc_subtract},
-    {1, (const char*[]){"n"}, &calc_negate},
-    {1, (const char*[]){"m"}, &calc_multiply},
-    {1, (const char*[]){"d"}, &calc_divide},
-    {1, (const char*[]){"i"}, &calc_invert},
-    
-    // Constants
-    {1, (const char*[]){"e"}, &calc_e}, 
-    {1, (const char*[]){"pi"}, &calc_pi}, 
-    
-    // Exponential/logarithmic
-    {1, (const char*[]){"exp"}, &calc_exp},
-    {1, (const char*[]){"pow"}, &calc_pow}, 
-    {1, (const char*[]){"ln"}, &calc_ln}, 
-    {1, (const char*[]){"log"}, &calc_log},
-    {1, (const char*[]){"sqrt"}, &calc_sqrt},
-    
-    // Trigonometric 
-    {2, (const char*[]){"sin", "sn"}, &calc_sin},
-    {1, (const char*[]){"cos"}, &calc_cos},
-    {1, (const char*[]){"tan"}, &calc_tan},
-    {1, (const char*[]){"asin"}, &calc_asin},
-    {1, (const char*[]){"acos"}, &calc_acos},
-    {1, (const char*[]){"atan"}, &calc_atan}, 
-    {1, (const char*[]){"atan2"}, &calc_atan2},
-    {1, (const char*[]){"sind"}, &calc_sind},
-    {1, (const char*[]){"cosd"}, &calc_cosd},
-    {1, (const char*[]){"tand"}, &calc_tand},
-    {1, (const char*[]){"asind"}, &calc_asind},
-    {1, (const char*[]){"acosd"}, &calc_acosd},
-    {1, (const char*[]){"atand"}, &calc_atand}, 
-    {1, (const char*[]){"atan2d"}, &calc_atan2d}, 
-    {1, (const char*[]){"tor"}, &calc_torad}, 
-    {1, (const char*[]){"tod"}, &calc_todeg}, 
-}; 
diff --git a/movement/lib/morsecalc/calc_strtof.c b/movement/lib/morsecalc/calc_strtof.c
deleted file mode 100644 (file)
index 35d39fa..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-//
-// strtod.c
-//
-// Convert string to double 
-//
-// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-// 
-// 1. Redistributions of source code must retain the above copyright 
-//    notice, this list of conditions and the following disclaimer.  
-// 2. Redistributions in binary form must reproduce the above copyright
-//    notice, this list of conditions and the following disclaimer in the
-//    documentation and/or other materials provided with the distribution.  
-// 3. Neither the name of the project nor the names of its contributors
-//    may be used to endorse or promote products derived from this software
-//    without specific prior written permission. 
-// 
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
-// SUCH DAMAGE.
-// 
-
-#include <ctype.h>
-#include <math.h>
-#include <float.h>
-#include <stdlib.h>
-
-#include "calc.h"
-
-double calc_strtof(const char *str, char **endptr) {
-  double number;
-  int exponent;
-  int negative;
-  char *p = (char *) str;
-  double p10;
-  int n;
-  int num_digits;
-  int num_decimals;
-
-  // Skip leading whitespace
-  while (isspace((int) *p)) p++;
-
-  // Handle optional sign
-  negative = 0;
-  switch (*p) {
-    case '-': negative = 1; // Fall through to increment position
-    __attribute__ ((fallthrough));
-    case '+': p++;
-  }
-
-  number = 0.;
-  exponent = 0;
-  num_digits = 0;
-  num_decimals = 0;
-
-  // Process string of digits
-  while (isdigit((int) *p)) {
-    number = number * 10. + (*p - '0');
-    p++;
-    num_digits++;
-  }
-
-  // Process decimal part
-  if (*p == '.') {
-    p++;
-
-    while (isdigit((int) *p)) {
-      number = number * 10. + (*p - '0');
-      p++;
-      num_digits++;
-      num_decimals++;
-    }
-
-    exponent -= num_decimals;
-  }
-
-  if (num_digits == 0) {
-    if (endptr) *endptr = p;
-    return 0.0;
-  }
-
-  // Correct for sign
-  if (negative) number = -number;
-
-  // Process an exponent string
-  if (*p == 'e' || *p == 'E') {
-    // Handle optional sign
-    negative = 0;
-    switch (*++p) {
-      case '-': negative = 1;   // Fall through to increment pos
-      __attribute__ ((fallthrough));
-      case '+': p++;
-    }
-
-    // Process string of digits
-    n = 0;
-    while (isdigit((int) *p)) {
-      n = n * 10 + (*p - '0');
-      p++;
-    }
-
-    if (negative) {
-      exponent -= n;
-    } else {
-      exponent += n;
-    }
-  }
-
-  if (exponent < DBL_MIN_EXP  || exponent > DBL_MAX_EXP) {
-    return HUGE_VAL;
-  }
-
-  // Scale the result
-  p10 = 10.;
-  n = exponent;
-  if (n < 0) n = -n;
-  while (n) {
-    if (n & 1) {
-      if (exponent < 0) {
-        number /= p10;
-      } else {
-        number *= p10;
-      }
-    }
-    n >>= 1;
-    p10 *= p10;
-  }
-
-  if (endptr) *endptr = p;
-
-  return number;
-}
-
diff --git a/movement/lib/morsecalc/morsecalc_display.c b/movement/lib/morsecalc/morsecalc_display.c
deleted file mode 100644 (file)
index 68f06fb..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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.
- */
-
-#include <string.h>
-#include <math.h>
-
-#include "watch_private_display.h"
-#include "morsecalc_display.h"
-
-// Display float on screen
-void morsecalc_display_float(double d) { 
-    // Special cases 
-    if(d == 0) {
-        watch_display_string("     0", 4); 
-        return;
-    }
-    else if(isnan(d)) {
-        watch_display_string("   nan", 4);
-        return;
-    }
-    else if(d == (1.0)/(0.0)) {
-        watch_display_string("   inf", 4);
-        return;
-    }
-    else if(d == (-1.0)/(0.0)) {
-        watch_display_character('X', 1);
-        watch_display_string("   inf", 4);
-        return;
-    }
-
-    // Record number properties
-    // Sign
-    int is_negative = d<0;
-    if(is_negative) d = -d; 
-
-    // Order of magnitude
-    int om = (int) floor(log(d)/log(10));
-    int om_is_negative = (om<0);
-
-    // Get the first 4 significant figures
-    int digits;
-    digits = round(d*pow(10.0, 3-om));
-    if(digits>9999) {
-        digits = 1000;
-        om++;
-    }
-
-    // Print signs
-    if(is_negative) {
-               // Xi; see https://joeycastillo.github.io/Sensor-Watch-Documentation/segmap
-               watch_set_pixel(0,11);
-               watch_set_pixel(2,12);
-               watch_set_pixel(2,11); 
-       }
-    else watch_display_character(' ', 1); 
-    if(om_is_negative) watch_set_pixel(1,9); 
-    else watch_display_character(' ', 2); 
-
-    // Print first 4 significant figures
-    watch_display_character('0'+(digits/1000)%10, 4);
-    watch_display_character('0'+(digits/100 )%10, 5);
-    watch_display_character('0'+(digits/10  )%10, 6);
-    watch_display_character('0'+(digits/1   )%10, 7);
-
-    // Prinat exponent
-    if(om_is_negative) om = -om; // Make exponent positive for display
-    if(om<=99) {
-        watch_display_character('0'+(om/10  )%10, 8);
-        watch_display_character('0'+(om/1   )%10, 9);
-    } else { // Over/underflow
-        if(om_is_negative) watch_display_string("    uf", 4);
-        else watch_display_string("    of", 4);
-        if(om<9999) { // Use main display to show order of magnitude
-            // (Should always succeed; max double is <2e308)
-            watch_display_character('0'+(om/1000)%10, 4);
-            watch_display_character('0'+(om/100 )%10, 5);
-            watch_display_character('0'+(om/10  )%10, 6);
-            watch_display_character('0'+(om/1   )%10, 7);
-        }
-    }
-    return;
-}
-
-// Print current input token
-void morsecalc_display_token(morsecalc_state_t *mcs) {
-    watch_display_string("          ", 0); // Clear display
-
-    // Print morse code buffer
-    char c = MORSECODE_TREE[mcs->mc]; // Decode the morse code buffer's current contents
-    if('\0' == c) c = ' '; // Needed for watch_display_character
-    watch_display_character(c, 0); // Display current morse code char in mode position
-    
-    unsigned int v = mcs->mc+1;
-    char bidx = 0; while (v >>= 1) bidx++; 
-    watch_display_character('0'+bidx, 3); // Display buffer position in top right
-
-    // Print last 6 chars of current input line 
-    uint8_t nlen = strlen(mcs->token); // number of characters in token
-    uint8_t nprint = min(nlen,6); // number of characters to print
-    watch_display_string(mcs->token+nlen-nprint, 10-nprint); // print right-aligned
-    return;
-}
-
-// Print stack or memory register contents. 
-void morsecalc_display_stack(morsecalc_state_t * mcs) {
-    watch_display_string("          ", 0); // Clear display
-
-    char c = MORSECODE_TREE[mcs->mc]; 
-    if('m' == c) { // Display memory 
-        morsecalc_display_float(mcs->cs->mem);
-        watch_display_character(c, 0);
-    } 
-    else {
-        // If the morse code buffer has a numeral in it, print that stack item 
-        // Otherwise print top of stack
-        uint8_t idx = 0;
-        if(c >= '0' && c <= '9') idx = c - '0';
-        if(idx >= mcs->cs->s) watch_display_string(" empty", 4); // Stack empty
-        else morsecalc_display_float(mcs->cs->stack[mcs->cs->s-1-idx]); // Print stack item
-
-        watch_display_character('0'+idx, 0); // Print which stack item this is top center
-    }
-    watch_display_character('0'+(mcs->cs->s), 3); // Print the # of stack items top right 
-    return;
-}
-
diff --git a/movement/lib/morsecalc/morsecalc_display.h b/movement/lib/morsecalc/morsecalc_display.h
deleted file mode 100644 (file)
index 74f8b0c..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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.
- */
-
-#include "morsecalc_face.h"
-
-// Display float on screen
-void morsecalc_display_float(double d);
-
-// Print current input token
-void morsecalc_display_token(morsecalc_state_t *mcs);
-
-// Print stack or memory register contents. 
-void morsecalc_display_stack(morsecalc_state_t *mcs);
-
diff --git a/movement/lib/morsecalc/test_morsecalc.c b/movement/lib/morsecalc/test_morsecalc.c
deleted file mode 100644 (file)
index fc640ba..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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.
- */
-
-// Computer console interface to calc and morsecode for testing without involving watch stuff.
-// cc calc_strtof.c calc.c calc_fns.c test_morsecalc.c -lm
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "calc.h"
-#include "calc_fns.h"
-
-int main(void) {
-    calc_state_t cs;
-    calc_init(&cs);
-    
-    char * word = malloc(0);
-    unsigned int nword = 0;
-    char c;
-    int retval = 0;
-    for(unsigned int ii = 0; ii < 100; ii++) {
-        c = getchar();
-        word = realloc(word, (++nword)*sizeof(char));
-        word[nword-1] = c;    
-        if((nword > 0) && isspace(c)) { // Word is finished
-            word[nword-1] = '\0';
-            retval = calc_input(&cs, word); // Submit word
-            word = realloc(word, 0); nword = 0; // Clear word
-            
-            switch(retval) {
-                case 0: printf("Success.\n"); break; 
-                case -1: printf("Bad command.\n"); break;
-                case -2: printf("Stack over/underflow.\n"); break;
-                case -3: printf("Error.\n"); break;
-            }
-            if(cs.s > 0) printf("[%i]: %.4f\n", cs.s, cs.stack[cs.s-1]);
-            else printf("[%i]\n", cs.s);
-        }
-    }
-    
-    free(word);
-    return 0;
-}
diff --git a/movement/lib/smallchesslib/smallchesslib.h b/movement/lib/smallchesslib/smallchesslib.h
deleted file mode 100644 (file)
index 7ac8542..0000000
+++ /dev/null
@@ -1,3704 +0,0 @@
-#ifndef SMALLCHESSLIB_H
-#define SMALLCHESSLIB_H
-
-/**
-  @file smallchesslib.h
-
-  Small and simple single header C99 public domain chess library and engine.
-
-  author: Miloslav Ciz (drummyfish)
-  license: CC0 1.0 (public domain)
-           found at https://creativecommons.org/publicdomain/zero/1.0/
-           + additional waiver of all IP
-  version: 0.8d
-
-  Default notation format for this library is a coordinate one, i.e.
-  
-    squarefrom squareto [promotedpiece]
-
-  e.g.: e2e4 or A2A1q
-
-  This work's goal is to never be encumbered by any exclusive intellectual
-  property rights. The work is therefore provided under CC0 1.0 + additional
-  WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
-  intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
-  INTELLECTUAL PROPERTY RGHTS is as follows:
-
-  Each contributor to this work agrees that they waive any exclusive rights,
-  including but not limited to copyright, patents, trademark, trade dress,
-  industrial design, plant varieties and trade secrets, to any and all ideas,
-  concepts, processes, discoveries, improvements and inventions conceived,
-  discovered, made, designed, researched or developed by the contributor either
-  solely or jointly with others, which relate to this work or result from this
-  work. Should any waiver of such right be judged legally invalid or
-  ineffective under applicable law, the contributor hereby grants to each
-  affected person a royalty-free, non transferable, non sublicensable, non
-  exclusive, irrevocable and unconditional license to this right.
-*/
-
-#include <stdint.h>
-
-#ifndef SCL_DEBUG_AI
-/** AI will print out a Newick-like tree of searched moves. */
-  #define SCL_DEBUG_AI 0
-#endif
-
-/**
-  Maximum number of moves a chess piece can have (a queen in the middle of the
-  board).
-*/
-#define SCL_CHESS_PIECE_MAX_MOVES 25
-#define SCL_BOARD_SQUARES 64
-
-typedef uint8_t (*SCL_RandomFunction)(void);
-
-#if SCL_COUNT_EVALUATED_POSITIONS
-  uint32_t SCL_positionsEvaluated = 0; /**< If enabled by 
-                                            SCL_COUNT_EVALUATED_POSITIONS, this
-                                            will increment with every
-                                            dynamically evaluated position (e.g.
-                                            when AI computes its move). */
-#endif
-
-#ifndef SCL_CALL_WDT_RESET
-  #define SCL_CALL_WDT_RESET 0 /**< Option that should be enabled on some
-                                    Arduinos. If 1, call to watchdog timer
-                                    reset will be performed during dynamic
-                                    evaluation (without it if AI takes long the
-                                    program will reset). */
-#endif
-
-/**
-  Returns a pseudorandom byte. This function has a period 256 and returns each
-  possible byte value exactly once in the period.
-*/
-uint8_t SCL_randomSimple(void);
-void SCL_randomSimpleSeed(uint8_t seed);
-
-/**
-  Like SCL_randomSimple, but internally uses a 16 bit value, so the period is
-  65536.
-*/
-uint8_t SCL_randomBetter(void);
-void SCL_randomBetterSeed(uint16_t seed);
-
-#ifndef SCL_EVALUATION_FUNCTION
-  /**
-    If defined, AI will always use the static evaluation function with this
-    name. This helps avoid pointers to functions and can be faster but the
-    function can't be changed at runtime.
-  */
-  #define SCL_EVALUATION_FUNCTION
-  #undef SCL_EVALUATION_FUNCTION
-#endif
-
-#ifndef SCL_960_CASTLING
-  /**
-    If set, chess 960 (Fisher random) castling will be considered by the library
-    rather than normal castling. 960 castling is slightly different (e.g.
-    requires the inital rook positions to be stored in board state). The
-    castling move is performed as "capturing own rook".
-  */
-  #define SCL_960_CASTLING 0
-#endif
-
-#ifndef SCL_ALPHA_BETA
-  /**
-    Turns alpha-beta pruning (AI optimization) on or off. This can gain
-    performance and should normally be turned on. AI behavior should not
-    change at all.
-  */
-  #define SCL_ALPHA_BETA 1
-#endif
-
-/**
-  A set of game squares as a bit array, each bit representing one game square.
-  Useful for representing e.g. possible moves. To easily iterate over the set
-  use provided macros (SCL_SQUARE_SET_ITERATE, ...).
-*/
-typedef uint8_t SCL_SquareSet[8];
-
-#define SCL_SQUARE_SET_EMPTY {0, 0, 0, 0, 0, 0, 0, 0}
-
-void SCL_squareSetClear(SCL_SquareSet squareSet);
-void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square);
-uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square);
-uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet);
-uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet);
-
-/**
-  Returns a random square from a square set.
-*/
-uint8_t SCL_squareSetGetRandom(const SCL_SquareSet squareSet,
-  SCL_RandomFunction randFunc);
-
-#define SCL_SQUARE_SET_ITERATE_BEGIN(squareSet) \
-  { uint8_t iteratedSquare = 0;\
-    uint8_t iterationEnd = 0;\
-    for (int8_t _i = 0; _i < 8 && !iterationEnd; ++_i) {\
-      uint8_t _row = squareSet[_i];\
-      if (_row == 0) { iteratedSquare += 8; continue; }\
-      \
-      for (uint8_t _j = 0; _j < 8 && !iterationEnd; ++_j) {\
-        if (_row & 0x01) {
-/*
-  Between SCL_SQUARE_SET_ITERATE_BEGIN and _END iteratedSquare variable
-  represents the next square contained in the set. To break out of the
-  iteration set iterationEnd to 1.
-*/
-
-#define SCL_SQUARE_SET_ITERATE_END }\
-  _row >>= 1;\
-  iteratedSquare++;}\
-  } /*for*/ }
-
-#define SCL_SQUARE_SET_ITERATE(squareSet,command)\
-  SCL_SQUARE_SET_ITERATE_BEGIN(squareSet)\
-  { command }\
-  SCL_SQUARE_SET_ITERATE_END
-#define SCL_BOARD_STATE_SIZE 69
-
-/**
-  Represents chess board state as a string in this format:
-  - First 64 characters represent the chess board (A1, B1, ... H8), each field
-    can be either a piece (PRNBKQprnbkq) or empty ('.'). I.e. the state looks
-    like this:
-
-      0 (A1) RNBQKBNR 
-             PPPPPPPP
-             ........
-             ........
-             ........
-             ........
-             pppppppp
-             rnbqkbnr 63 (H8)
-
-  - After this more bytes follow to represent global state, these are:
-    - 64: bits holding en-passant and castling related information:
-      - bits 0-3 (lsb): Column of the pawn that can, in current turn, be
-        taken by en-passant (0xF means no pawn can be taken this way).
-      - bit 4: Whether white is not prevented from short castling by previous
-        king or rook movement.
-      - bit 5: Same as 4, but for long castling.
-      - bit 6: Same as 4, but for black.
-      - bit 7: Same as 4, but for black and long castling.
-    - 65: Number saying the number of ply (half-moves) that have already been
-      played, also determining whose turn it currently is.
-    - 66: Move counter used in the 50 move rule, says the number of ply since
-      the last pawn move or capture.
-    - 67: Extra byte, left for storing additional info in variants. For normal
-      chess this byte should always be 0.
-    - 68: The last byte is always 0 to properly terminate the string in case
-      someone tries to print it.
-  - The state is designed so as to be simple and also print-friendly, i.e. you
-    can simply print it with line break after 8 characters to get a human
-    readable representation of the board.
-
-  NOTE: there is a much more compact representation which however trades some
-  access speed which would affect the AI performance and isn't print friendly,
-  so we don't use it. In it each square takes 4 bits, using 15 out of 16
-  possible values (empty square and W and B pieces including 2 types of pawns,
-  one "en-passant takeable"). Then only one extra byte needed is for castling
-  info (4 bits) and ply count (4 bits).
-*/
-typedef char SCL_Board[SCL_BOARD_STATE_SIZE];
-
-#define SCL_BOARD_ENPASSANT_CASTLE_BYTE 64
-#define SCL_BOARD_PLY_BYTE 65
-#define SCL_BOARD_MOVE_COUNT_BYTE 66
-#define SCL_BOARD_EXTRA_BYTE 67
-
-#if SCL_960_CASTLING
-  #define _SCL_EXTRA_BYTE_VALUE (0 | (7 << 3)) // rooks on classic positions
-#else
-  #define _SCL_EXTRA_BYTE_VALUE 0
-#endif
-
-#define SCL_BOARD_START_STATE \
-  {82, 78, 66, 81, 75, 66, 78, 82,\
-   80, 80, 80, 80, 80, 80, 80, 80,\
-   46, 46, 46, 46, 46, 46, 46, 46,\
-   46, 46, 46, 46, 46, 46, 46, 46,\
-   46, 46, 46, 46, 46, 46, 46, 46,\
-   46, 46, 46, 46, 46, 46, 46, 46,\
-   112,112,112,112,112,112,112,112,\
-   114,110,98, 113,107,98, 110,114,\
-   (char) 0xff,0,0,_SCL_EXTRA_BYTE_VALUE,0}
-
-#define SCL_FEN_START \
-  "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
-
-#define SCL_FEN_HORDE \
-  "ppp2ppp/pppppppp/pppppppp/pppppppp/3pp3/8/PPPPPPPP/RNBQKBNR w KQ - 0 1"
-
-#define SCL_FEN_UPSIDE_DOWN \
-  "RNBKQBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbkqbnr w - - 0 1"
-
-#define SCL_FEN_PEASANT_REVOLT \
-  "1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
-
-#define SCL_FEN_ENDGAME \
-  "4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
-
-#define SCL_FEN_KNIGHTS \
-  "N6n/1N4n1/2N2n2/3Nn3/k2nN2K/2n2N2/1n4N1/n6N w - - 0 1"
-
-/**
-  Holds an info required to undo a single move.
-*/
-typedef struct
-{
-  uint8_t squareFrom;      ///< start square
-  uint8_t squareTo;        ///< target square
-  char enPassantCastle;    ///< previous en passant/castle byte
-  char moveCount;          ///< previous values of the move counter byte
-  uint8_t other;           /**< lowest 7 bits: previous value of target square,
-                                highest bit: if 1 then the move was promotion or
-                                en passant */
-} SCL_MoveUndo;
-
-#define SCL_GAME_STATE_PLAYING         0x00
-#define SCL_GAME_STATE_WHITE_WIN       0x01
-#define SCL_GAME_STATE_BLACK_WIN       0x02
-#define SCL_GAME_STATE_DRAW            0x10 ///< further unspecified draw
-#define SCL_GAME_STATE_DRAW_STALEMATE  0x11 ///< draw by stalemate
-#define SCL_GAME_STATE_DRAW_REPETITION 0x12 ///< draw by repetition
-#define SCL_GAME_STATE_DRAW_50         0x13 ///< draw by 50 move rule
-#define SCL_GAME_STATE_DRAW_DEAD       0x14 ///< draw by dead position
-#define SCL_GAME_STATE_END             0xff ///< end without known result
-
-/**
-  Converts square in common notation (e.g. 'c' 8) to square number. Only accepts
-  lowercase column.
-*/
-#define SCL_SQUARE(colChar,rowInt) (((rowInt) - 1) * 8 + ((colChar) - 'a'))
-#define SCL_S(c,r) SCL_SQUARE(c,r)
-
-void SCL_boardInit(SCL_Board board);
-void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo);
-
-/**
-  Initializes given chess 960 (Fisher random) position. If SCL_960_CASTLING
-  is not set, castling will be disabled by this function.
-*/
-void SCL_boardInit960(SCL_Board board, uint16_t positionNumber);
-
-void SCL_boardDisableCastling(SCL_Board board);
-
-uint32_t SCL_boardHash32(const SCL_Board board);
-
-#define SCL_PHASE_OPENING 0
-#define SCL_PHASE_MIDGAME 1
-#define SCL_PHASE_ENDGAME 2
-
-/**
-  Estimates the game phase: opening, midgame or endgame.
-*/
-uint8_t SCL_boardEstimatePhase(SCL_Board board);
-
-/**
-  Sets the board position. The input string should be 64 characters long zero
-  terminated C string representing the board as squares A1, A2, ..., H8 with
-  each char being either a piece (RKBKQPrkbkqp) or an empty square ('.').
-*/
-void SCL_boardSetPosition(SCL_Board board, const char *pieces,
-  uint8_t castlingEnPassant, uint8_t moveCount, uint8_t ply);
-
-uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2);
-
-/**
-  Function that prints out a single character. This is passed to printing
-  functions.
-*/
-typedef void (*SCL_PutCharFunction)(char);
-
-/**
-  Gets a random move on given board for the player whose move it is.
-*/
-void SCL_boardRandomMove(SCL_Board board, SCL_RandomFunction randFunc,
-  uint8_t *squareFrom, uint8_t *squareTo, char *resultProm);
-
-#define SCL_FEN_MAX_LENGTH 90
-
-/**
-  Converts a position to FEN (Forsyth–Edwards Notation) string. The string has
-  to have at least SCL_FEN_MAX_LENGTH bytes allocated to guarantee the
-  function won't write to unallocated memory. The string will be terminated by
-  0 (this is included in SCL_FEN_MAX_LENGTH). The number of bytes written
-  (including the terminating 0) is returned.
-*/
-uint8_t SCL_boardToFEN(SCL_Board board, char *string);
-
-/**
-  Loads a board from FEN (Forsyth–Edwards Notation) string. Returns 1 on
-  success, 0 otherwise. XFEN isn't supported fully but a start position in
-  chess960 can be loaded with this function. 
-*/
-uint8_t SCL_boardFromFEN(SCL_Board board, const char *string);
-
-/**
-  Returns an approximate/heuristic board rating as a number, 0 meaning equal
-  chances for both players, positive favoring white, negative favoring black.
-*/
-typedef int16_t (*SCL_StaticEvaluationFunction)(SCL_Board);
-
-/* 
-  NOTE: int8_t as a return value was tried for evaluation function, which would
-  be simpler, but it fails to capture important non-material position
-  differences counted in fractions of pawn values, hence we have to use int16_t.
-*/
-
-/**
-  Basic static evaluation function. WARNING: this function supposes a standard
-  chess game, for non-standard positions it may either not work well or even
-  crash the program. You should use a different function for non-standard games.
-*/
-int16_t SCL_boardEvaluateStatic(SCL_Board board);
-
-/**
-  Dynamic evaluation function (search), i.e. unlike SCL_boardEvaluateStatic,
-  this one performs a recursive search for deeper positions to get a more
-  accurate score. Of course, this is much slower and hugely dependent on
-  baseDepth (you mostly want to keep this under 5).
-*/
-int16_t SCL_boardEvaluateDynamic(SCL_Board board, uint8_t baseDepth,
-  uint8_t extensionExtraDepth, SCL_StaticEvaluationFunction evalFunction);
-
-#define SCL_EVALUATION_MAX_SCORE 32600 // don't increase this, we need a margin
-
-/**
-  Checks if the board position is dead, i.e. mate is impossible (e.g. due to
-  insufficient material), which by the rules results in a draw. WARNING: This
-  function may fail to detect some dead positions as this is a non-trivial task.
-*/
-uint8_t SCL_boardDead(SCL_Board board);
-
-/**
-  Tests whether given player is in check.
-*/
-uint8_t SCL_boardCheck(SCL_Board board, uint8_t white);
-
-/**
-  Checks whether given move resets the move counter (used in the 50 move rule).
-*/
-uint8_t SCL_boardMoveResetsCount(SCL_Board board,
-  uint8_t squareFrom, uint8_t squareTo);
-
-uint8_t SCL_boardMate(SCL_Board board);
-
-/**
-  Performs a move on a board WITHOUT checking if the move is legal. Returns an
-  info with which the move can be undone.
-*/
-SCL_MoveUndo SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo,
-  char promotePiece);
-
-void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo);
-
-/**
-  Checks if the game is over, i.e. the current player to move has no legal
-  moves, the game is in dead position etc.
-*/
-uint8_t SCL_boardGameOver(SCL_Board board);
-
-/**
-  Checks if given move is legal.
-*/
-uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom,
-  uint8_t squareTo);
-
-/**
-  Checks if the player to move has at least one legal move.
-*/
-uint8_t SCL_boardMovePossible(SCL_Board board);
-
-#define SCL_POSITION_NORMAL    0x00
-#define SCL_POSITION_CHECK     0x01
-#define SCL_POSITION_MATE      0x02
-#define SCL_POSITION_STALEMATE 0x03
-#define SCL_POSITION_DEAD      0x04
-
-uint8_t SCL_boardGetPosition(SCL_Board board);
-
-/**
-  Returns 1 if the square is attacked by player of given color. This is used to
-  examine checks, so for performance reasons the functions only checks whether
-  or not the square is attacked (not the number of attackers).
-*/
-uint8_t SCL_boardSquareAttacked(SCL_Board board, uint8_t square,
-  uint8_t byWhite);
-
-/**
-  Gets pseudo moves of a piece: all possible moves WITHOUT eliminating moves
-  that lead to own check. To get only legal moves use SCL_boardGetMoves.
-*/
-void SCL_boardGetPseudoMoves(
-  SCL_Board board,
-  uint8_t pieceSquare,
-  uint8_t checkCastling,
-  SCL_SquareSet result);
-
-/**
-  Gets all legal moves of given piece.
-*/
-void SCL_boardGetMoves(
-  SCL_Board board,
-  uint8_t pieceSquare,
-  SCL_SquareSet result);
-
-void _SCL_board960RememberRookPositions(SCL_Board board);
-void _SCL_boardPlaceOnNthAvailable(SCL_Board board, uint8_t pos, char piece);
-void _SCL_handleRookActivity(SCL_Board board, uint8_t rookSquare);
-void SCL_printSquareSet(SCL_SquareSet set, SCL_PutCharFunction putCharFunc);
-int16_t _SCL_rateKingEndgamePosition(uint8_t position);
-int16_t _SCL_boardEvaluateDynamic(SCL_Board board, int8_t depth, int16_t alphaBeta, int8_t takenSquare);
-
-static inline uint8_t SCL_boardWhitesTurn(SCL_Board board);
-
-static inline uint8_t SCL_pieceIsWhite(char piece); 
-static inline uint8_t SCL_squareIsWhite(uint8_t square);
-char SCL_pieceToColor(uint8_t piece, uint8_t toWhite);
-
-/**
-  Converts square coordinates to square number. Each coordinate must be a number
-  <1,8>. Validity of the coordinates is NOT checked.
-*/
-static inline uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column);
-
-#ifndef SCL_VALUE_PAWN
-  #define SCL_VALUE_PAWN 256
-#endif
-
-#ifndef SCL_VALUE_KNIGHT
-  #define SCL_VALUE_KNIGHT 768
-#endif
-
-#ifndef SCL_VALUE_BISHOP
-  #define SCL_VALUE_BISHOP 800
-#endif
-
-#ifndef SCL_VALUE_ROOK
-  #define SCL_VALUE_ROOK 1280
-#endif
-
-#ifndef SCL_VALUE_QUEEN
-  #define SCL_VALUE_QUEEN 2304
-#endif
-
-#ifndef SCL_VALUE_KING
-  #define SCL_VALUE_KING 0
-#endif
-
-#define SCL_ENDGAME_MATERIAL_LIMIT \
-  (2 * (SCL_VALUE_PAWN * 4 + SCL_VALUE_QUEEN + \
-  SCL_VALUE_KING + SCL_VALUE_ROOK + SCL_VALUE_KNIGHT))
-
-#define SCL_START_MATERIAL \
-  (16 * SCL_VALUE_PAWN + 4 * SCL_VALUE_ROOK + 4 * SCL_VALUE_KNIGHT + \
-    4 * SCL_VALUE_BISHOP + 2 * SCL_VALUE_QUEEN + 2 * SCL_VALUE_KING)
-
-#ifndef SCL_RECORD_MAX_LENGTH
-  #define SCL_RECORD_MAX_LENGTH 256
-#endif
-
-#define SCL_RECORD_MAX_SIZE (SCL_RECORD_MAX_LENGTH * 2)
-
-/**
-  Records a single chess game. The format is following:
-
-  Each record item consists of 2 bytes which record a single move (ply):
-
-  abxxxxxx cdyyyyyy
-
-    xxxxxx  Start square of the move, counted as A0, A1, ...
-    yyyyyy  End square of the move in the same format as the start square.
-    ab      00 means this move isn't the last move of the game, other possible
-            values are 01: white wins, 10: black wins, 11: draw or end for
-            other reasons.
-    cd      In case of pawn promotion move this encodes the promoted piece as
-            00: queen, 01: rook, 10: bishop, 11: knight (pawn isn't allowed by
-            chess rules).
-
-  Every record should be ended by an ending move (ab != 00), empty record should
-  have one move where xxxxxx == yyyyyy == 0 and ab == 11.
-*/
-typedef uint8_t SCL_Record[SCL_RECORD_MAX_SIZE];
-
-#define SCL_RECORD_CONT 0x00
-#define SCL_RECORD_W_WIN 0x40
-#define SCL_RECORD_B_WIN 0x80
-#define SCL_RECORD_END 0xc0
-
-#define SCL_RECORD_PROM_Q 0x00
-#define SCL_RECORD_PROM_R 0x40
-#define SCL_RECORD_PROM_B 0x80
-#define SCL_RECORD_PROM_N 0xc0
-
-#define SCL_RECORD_ITEM(s0,s1,p,e) ((e) | (s0)),((p) | (s1))
-
-void SCL_recordInit(SCL_Record r);
-
-void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo);
-
-/**
-  Represents a complete game of chess (or a variant with different staring
-  position). This struct along with associated functions allows to easily
-  implement a chess game that allows undoing moves, detecting draws, recording
-  the moves etc. On platforms with extremely little RAM one can reduce
-  SCL_RECORD_MAX_LENGTH to reduce the size of this struct (which will however
-  possibly limit how many moves can be undone).
-*/
-typedef struct
-{
-  SCL_Board board;
-  SCL_Record record;          /**< Holds the game record. This record is here 
-                              firstly because games are usually recorded and
-                              secondly this allows undoing moves up to the 
-                              beginning of the game. This infinite undoing will
-                              only work as long as the record is able to hold
-                              the whole game; if the record is full, undoing is
-                              no longet possible. */
-  uint16_t state;
-  uint16_t ply;               ///< ply count (board ply counter is only 8 bit)
-
-  uint32_t prevMoves[14];     ///< stores last moves, for repetition detection
-
-  const char *startState;     /**< Optional pointer to the starting board state.
-                              If this is null, standard chess start position is
-                              assumed. This is needed for undoing moves with
-                              game record. */
-} SCL_Game;
-
-/**
-  Initializes a new chess game. The startState parameter is optional and allows
-  for setting up chess variants that differ by starting positions, setting this
-  to 0 will assume traditional starting position. WARNING: if startState is
-  provided, the pointed to board mustn't be deallocated afterwards, the string
-  is not internally copied (for memory saving reasons).
-*/
-void SCL_gameInit(SCL_Game *game, const SCL_Board startState);
-
-void SCL_gameMakeMove(SCL_Game *game, uint8_t squareFrom, uint8_t squareTo,
-  char promoteTo);
-
-uint8_t SCL_gameUndoMove(SCL_Game *game);
-
-/**
-  Gets a move which if played now would cause a draw by repetition. Returns 1
-  if such move exists, 0 otherwise. The results parameters can be set to 0 in
-  which case they will be ignored and only the existence of a draw move will be
-  tested.
-*/
-uint8_t SCL_gameGetRepetiotionMove(SCL_Game *game,
-  uint8_t *squareFrom, uint8_t *squareTo);
-
-/**
-  Leads a game record from PGN string. The function will probably not strictly
-  adhere to the PGN input format, but should accept most sanely written PGN
-  strings.
-*/
-void SCL_recordFromPGN(SCL_Record r, const char *pgn);
-
-uint16_t SCL_recordLength(const SCL_Record r);
-
-/**
-  Gets the move out of a game record, returns the end state of the move
-  (SCL_RECORD_CONT, SCL_RECORD_END etc.)
-*/
-uint8_t SCL_recordGetMove(const SCL_Record r,  uint16_t index,
-  uint8_t *squareFrom, uint8_t *squareTo, char *promotedPiece);
-
-/**
-  Adds another move to the game record. Terminating the record is handled so
-  that the last move is always marked with end flag, endState is here to only
-  indicate possible game result (otherwise pass SCL_RECORD_CONT). Returns 1 if
-  the item was added, otherwise 0 (replay was already of maximum size).
-*/
-uint8_t SCL_recordAdd(SCL_Record r, uint8_t squareFrom,
-  uint8_t squareTo, char promotePiece, uint8_t endState);
-
-/**
-  Removes the last move from the record, returns 1 if the replay is non-empty
-  after the removal, otherwise 0.
-*/
-uint8_t SCL_recordRemoveLast(SCL_Record r);
-
-/**
-  Applies given number of half-moves (ply) to a given board (the board is
-  automatically initialized at the beginning).
-*/
-void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves);
-
-int16_t SCL_pieceValue(char piece);
-int16_t SCL_pieceValuePositive(char piece);
-
-#define SCL_PRINT_FORMAT_NONE 0
-#define SCL_PRINT_FORMAT_NORMAL 1
-#define SCL_PRINT_FORMAT_COMPACT 2
-#define SCL_PRINT_FORMAT_UTF8 3
-#define SCL_PRINT_FORMAT_COMPACT_UTF8 4
-
-/**
-  Gets the best move for the currently moving player as computed by AI. The
-  return value is the value of the move (with the same semantics as the value
-  of an evaluation function). baseDepth is depth in plys to which all moves will
-  be checked. If baseDepth 0 is passed, the function makes a random move and
-  returns the evaluation of the board. extensionExtraDepth is extra depth for
-  checking specific situations like exchanges and checks. endgameExtraDepth is
-  extra depth which is added to baseDepth in the endgame. If the randomness
-  function is 0, AI will always make the first best move it finds, if it is
-  not 0 and randomness is 0, AI will randomly pick between the equally best
-  moves, if it is not 0 and randomness is positive, AI will randomly choose
-  between best moves with some bias (may not pick the best rated move).
-*/
-int16_t SCL_getAIMove(
-  SCL_Board board,
-  uint8_t baseDepth,
-  uint8_t extensionExtraDepth,
-  uint8_t endgameExtraDepth,
-  SCL_StaticEvaluationFunction evalFunc,
-  SCL_RandomFunction randFunc,
-  uint8_t randomness,
-  uint8_t repetitionMoveFrom,
-  uint8_t repetitionMoveTo,
-  uint8_t *resultFrom,
-  uint8_t *resultTo,
-  char *resultProm);
-
-/**
-  Prints given chessboard using given format and an abstract printing function.
-*/
-void SCL_printBoard(
-  SCL_Board board,
-  SCL_PutCharFunction putCharFunc,
-  SCL_SquareSet highlightSquares,
-  uint8_t selectSquare,
-  uint8_t format,
-  uint8_t offset,
-  uint8_t labels,
-  uint8_t blackDown);
-
-void SCL_printBoardSimple(
-  SCL_Board board,
-  SCL_PutCharFunction putCharFunc,
-  uint8_t selectSquare,
-  uint8_t format);
-
-void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc);
-void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc,
-  SCL_Board initialState);
-
-/**
-  Reads a move from string (the notation format is described at the top of this
-  file). The function is safe as long as the string is 0 terminated. Returns 1
-  on success or 0 on fail (invalid move string).
-*/
-uint8_t SCL_stringToMove(const char *moveString, uint8_t *resultFrom, 
-  uint8_t *resultTo, char *resultPromotion);
-
-char *SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1,
-  char promotion, char *string);
-
-/**
-  Function used in drawing, it is called to draw the next pixel. The first
-  parameter is the pixel color, the second one if the sequential number of the
-  pixel.
-*/
-typedef void (*SCL_PutPixelFunction)(uint8_t, uint16_t);
-
-#define SCL_BOARD_PICTURE_WIDTH 64
-
-/**
-  Draws a simple 1bit 64x64 pixels board using a provided abstract function for
-  drawing pixels. The function renders from top left to bottom right, i.e. no
-  frame buffer is required.
-*/
-void SCL_drawBoard(
-  SCL_Board board,
-  SCL_PutPixelFunction putPixel,
-  uint8_t selectedSquare,
-  SCL_SquareSet highlightSquares,
-  uint8_t blackDown);
-
-/**
-  Converts square number to string representation (e.g. "d2"). This function
-  will modify exactly the first two bytes of the provided string.
-*/
-static inline char *SCL_squareToString(uint8_t square, char *string);
-
-/**
-  Converts a string, such as "A1" or "b4", to square number. The string must
-  start with a letter (lower or upper case) and be followed by a number <1,8>.
-  Validity of the string is NOT checked.
-*/
-uint8_t SCL_stringToSquare(const char *square);
-
-//=============================================================================
-// privates:
-
-#define SCL_UNUSED(v) (void)(v)
-
-uint8_t SCL_currentRandom8 = 0;
-
-uint16_t SCL_currentRandom16 = 0;
-
-void SCL_randomSimpleSeed(uint8_t seed)
-{
-  SCL_currentRandom8 = seed;
-}
-
-uint8_t SCL_randomSimple(void)
-{
-  SCL_currentRandom8 *= 13;
-  SCL_currentRandom8 += 7;
-  return SCL_currentRandom8;
-}
-
-uint8_t SCL_randomBetter(void)
-{
-  SCL_currentRandom16 *= 13;
-  SCL_currentRandom16 += 7;
-  return (SCL_currentRandom16 % 256) ^ (SCL_currentRandom16 / 256);
-}
-
-void SCL_randomBetterSeed(uint16_t seed)
-{
-  SCL_currentRandom16 = seed;
-}
-
-void SCL_squareSetClear(SCL_SquareSet squareSet)
-{
-  for (uint8_t i = 0; i < 8; ++i)
-    squareSet[i] = 0;
-}
-
-uint8_t SCL_stringToSquare(const char *square)
-{
-  return (square[1] - '1') * 8 +
-    (square[0] - ((square[0] >= 'A' && square[0] <= 'Z') ? 'A' : 'a'));
-}
-
-char *SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1,
-  char promotion, char *string)
-{
-  char *result = string;
-
-  SCL_squareToString(s0,string);
-  string += 2;
-  string = SCL_squareToString(s1,string);
-  string += 2;
-
-  char c = board[s0];
-
-  if (c == 'p' || c == 'P')
-  {
-    uint8_t rank = s1 / 8;
-
-    if (rank == 0 || rank == 7)
-    {
-      *string = promotion;
-      string++;
-    }
-  }
-
-  *string = 0;
-
-  return result;
-}
-
-uint8_t SCL_boardWhitesTurn(SCL_Board board)
-{
-  return (board[SCL_BOARD_PLY_BYTE] % 2) == 0;
-}
-
-uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column)
-{
-  return row * 8 + column; 
-}
-
-uint8_t SCL_pieceIsWhite(char piece)
-{
-  return piece < 'a';
-}
-
-char *SCL_squareToString(uint8_t square, char *string)
-{
-  string[0] = 'a' + square % 8;
-  string[1] = '1' + square / 8;
-
-  return string;
-}
-  
-uint8_t SCL_squareIsWhite(uint8_t square)
-{
-  return (square % 2) != ((square / 8) % 2);
-}
-
-char SCL_pieceToColor(uint8_t piece, uint8_t toWhite)
-{
-  return (SCL_pieceIsWhite(piece) == toWhite) ?
-    piece : (piece + (toWhite ? -32 : 32));
-}
-
-/**
-  Records the rook starting positions in the board state. This is required in
-  chess 960 in order to be able to correctly perform castling (castling rights
-  knowledge isn't enough as one rook might have moved to the other side and we
-  wouldn't know which one can castle and which not).
-*/
-void _SCL_board960RememberRookPositions(SCL_Board board)
-{
-  uint8_t pos = 0;
-  uint8_t rooks = 2;
-
-  while (pos < 8 && rooks != 0)
-  {
-    if (board[pos] == 'R')
-    {
-      board[SCL_BOARD_EXTRA_BYTE] = rooks == 2 ? pos :
-        (board[SCL_BOARD_EXTRA_BYTE] | (pos << 3));
-
-      rooks--;
-    }
-
-    pos++;
-  }
-}
-
-void SCL_boardInit(SCL_Board board)
-{
-  /*
-    We might use SCL_BOARD_START_STATE and copy it to the board, but that might
-    waste RAM on Arduino, so we init the board by code.
-  */
-
-  char *b = board;
-
-  *b = 'R'; b++; *b = 'N'; b++;
-  *b = 'B'; b++; *b = 'Q'; b++;
-  *b = 'K'; b++; *b = 'B'; b++;
-  *b = 'N'; b++; *b = 'R'; b++;
-
-  char *b2 = board + 48;
-
-  for (uint8_t i = 0; i < 8; ++i, b++, b2++)
-  {
-    *b = 'P';
-    *b2 = 'p';
-  }
-
-  for (uint8_t i = 0; i < 32; ++i, b++)
-    *b = '.';
-
-  b += 8;
-
-  *b = 'r'; b++; *b = 'n'; b++;
-  *b = 'b'; b++; *b = 'q'; b++;
-  *b = 'k'; b++; *b = 'b'; b++;
-  *b = 'n'; b++; *b = 'r'; b++;
-
-  for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE - SCL_BOARD_SQUARES; ++i, ++b)
-    *b = 0;
-
-  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = (char) 0xff;
-
-#if SCL_960_CASTLING
-  _SCL_board960RememberRookPositions(board);
-#endif
-}
-
-void _SCL_boardPlaceOnNthAvailable(SCL_Board board, uint8_t pos, char piece)
-{
-  char *c = board;
-
-  while (1)
-  {
-    if (*c == '.')
-    {
-      if (pos == 0)
-        break;
-
-      pos--;
-    }
-
-    c++;
-  }
-
-  *c = piece;
-}
-
-void SCL_boardInit960(SCL_Board board, uint16_t positionNumber)
-{
-  SCL_Board b;
-
-  SCL_boardInit(b);
-
-  for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
-    board[i] = ((i >= 8 && i < 56) || i >= 64) ? b[i] : '.';
-
-  uint8_t helper = positionNumber % 16;
-
-  board[(helper / 4) * 2] = 'B';
-  board[1 + (helper % 4) * 2] = 'B';
-
-  helper = positionNumber / 16;
-
-  // maybe there's a simpler way :)
-
-  _SCL_boardPlaceOnNthAvailable(board,helper % 6,'Q');
-  _SCL_boardPlaceOnNthAvailable(board,0,helper <= 23 ? 'N' : 'R');
-
-  _SCL_boardPlaceOnNthAvailable(board,0,
-    (helper >= 7 && helper <= 23) ? 'R' :
-      (helper > 41 ? 'K' : 'N' ));
-
-  _SCL_boardPlaceOnNthAvailable(board,0,
-    (helper <= 5 || helper >= 54) ? 'R' : 
-      (((helper >= 12 && helper <= 23) ||
-      (helper >= 30 && helper <= 41)) ? 'K' : 'N'));
-
-  _SCL_boardPlaceOnNthAvailable(board,0,
-    (helper <= 11 || (helper <= 29 && helper >= 24)) ? 'K' :
-      (
-        (
-          (helper >= 18 && helper <= 23) ||
-          (helper >= 36 && helper <= 41) ||
-          (helper >= 48 && helper <= 53)
-        ) ? 'R' : 'N'
-      )
-    );
-
-  uint8_t rooks = 0;
-
-  for (uint8_t i = 0; i < 8; ++i)
-    if (board[i] == 'R')
-      rooks++;
-
-  _SCL_boardPlaceOnNthAvailable(board,0,rooks == 2 ? 'N' : 'R');
-  
-  for (uint8_t i = 0; i < 8; ++i)
-    board[56 + i] = SCL_pieceToColor(board[i],0);
-
-#if SCL_960_CASTLING
-  _SCL_board960RememberRookPositions(board);
-#else
-  SCL_boardDisableCastling(board);
-#endif
-}
-
-uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2)
-{
-  const char *p1 = b1, *p2 = b2;
-
-  while (p1 < b1 + SCL_BOARD_STATE_SIZE)
-  {
-    if (*p1 != *p2)
-      return 1;
-
-    p1++;
-    p2++;
-  }
-
-  return 0;
-}
-
-void SCL_recordInit(SCL_Record r)
-{
-  r[0] = 0 | SCL_RECORD_END;
-  r[1] = 0;
-}
-
-void SCL_recordFromPGN(SCL_Record r, const char *pgn)
-{
-  SCL_Board board;
-
-  SCL_boardInit(board);
-
-  SCL_recordInit(r);
-
-  uint8_t state = 0;
-  uint8_t evenMove = 0;
-
-  while (*pgn != 0)
-  {
-    switch (state)
-    {
-      case 0: // skipping tags and spaces, outside []
-        if (*pgn == '1')
-          state = 2;
-        else if (*pgn == '[')
-          state = 1;
-
-        break;
-
-      case 1: // skipping tags and spaces, inside []
-        if (*pgn == ']')
-          state = 0;
-
-        break;
-
-      case 2: // reading move number
-        if (*pgn == '{')
-          state = 3;
-        else if ((*pgn >= 'a' && *pgn <= 'h') || (*pgn >= 'A' && *pgn <= 'Z'))
-        {
-          state = 4;
-          pgn--;
-        }
-
-        break;
-
-      case 3: // initial comment
-        if (*pgn == '}')
-          state = 2;
-
-        break;
-
-      case 4: // reading move
-      {
-        char piece = 'p';
-        char promoteTo = 'q';
-        uint8_t castle = 0;
-        uint8_t promotion = 0;
-
-        int8_t coords[4];
-
-        uint8_t ranks = 0, files = 0;
-
-        for (uint8_t i = 0; i < 4; ++i)
-          coords[i] = -1;
-
-        while (*pgn != ' ' && *pgn != '\n' && 
-          *pgn != '\t' && *pgn != '{' && *pgn != 0)
-        {
-          if (*pgn == '=')
-            promotion = 1;
-          if (*pgn == 'O' || *pgn == '0')
-            castle++;
-          if (*pgn >= 'A' && *pgn <= 'Z')
-          {
-            if (promotion)
-              promoteTo = *pgn;
-            else
-              piece = *pgn;
-          }
-          else if (*pgn >= 'a' && *pgn <= 'h')
-          {
-            coords[files * 2] = *pgn - 'a';
-            files++;
-          }
-          else if (*pgn >= '1' && *pgn <= '8')
-          {
-            coords[1 + ranks * 2] = *pgn - '1';
-            ranks++;
-          }
-
-          pgn++;
-        }
-
-        if (castle)
-        {
-          piece = 'K';
-
-          coords[0] = 4;
-          coords[1] = 0;
-          coords[2] = castle < 3 ? 6 : 2;
-          coords[3] = 0;
-
-          if (evenMove)
-          {
-            coords[1] = 7;
-            coords[3] = 7;
-          }
-        }
-
-        piece = SCL_pieceToColor(piece,evenMove == 0);
-
-        if (coords[2] < 0)
-        {
-          coords[2] = coords[0];
-          coords[0] = -1;
-        }
-
-        if (coords[3] < 0)
-        {
-          coords[3] = coords[1];
-          coords[1] = -1;
-        }
-          
-        uint8_t squareTo = coords[3] * 8 + coords[2];
-
-        if (coords[0] < 0 || coords[1] < 0)
-        {
-          // without complete starting coords we have to find the piece
-
-          for (int i = 0; i < SCL_BOARD_SQUARES; ++i)
-            if (board[i] == piece)
-            {
-              SCL_SquareSet s;
-
-              SCL_squareSetClear(s);
-
-              SCL_boardGetMoves(board,i,s);
-
-              if (SCL_squareSetContains(s,squareTo) &&
-                (coords[0] < 0 || coords[0] == i % 8) &&
-                (coords[1] < 0 || coords[1] == i / 8))
-              {
-                coords[0] = i % 8;
-                coords[1] = i / 8;
-                break;
-              }
-            }
-        }
-
-        uint8_t squareFrom = coords[1] * 8 + coords[0];
-
-        SCL_boardMakeMove(board,squareFrom,squareTo,promoteTo);
-
-// for some reason tcc bugs here, the above line sets squareFrom to 0 lol
-// can be fixed with doing "squareFrom = coords[1] * 8 + coords[0];" again
-
-        SCL_recordAdd(r,squareFrom,squareTo,promoteTo,SCL_RECORD_CONT);
-
-        while (*pgn == ' ' || *pgn == '\n' || *pgn == '\t' || *pgn == '{')
-        {
-          if (*pgn == '{')
-            while (*pgn != '}')
-              pgn++;
-
-          pgn++;
-        }
-
-        if (*pgn == 0)
-          return;
-
-        pgn--;
-
-        if (evenMove)
-          state = 2;
-
-        evenMove = !evenMove;
-
-        break;
-      }
-
-      default: break;
-    }
-
-    pgn++;
-  }
-}
-
-uint16_t SCL_recordLength(const SCL_Record r)
-{
-  if ((r[0] & 0x3f) == (r[1] & 0x3f)) // empty record that's only terminator
-    return 0;
-
-  uint16_t result = 0;
-
-  while ((r[result] & 0xc0) == 0)
-    result += 2;
-
-  return (result / 2) + 1;
-}
-
-uint8_t SCL_recordGetMove(const SCL_Record r,  uint16_t index,
-  uint8_t *squareFrom, uint8_t *squareTo, char *promotedPiece)
-{ 
-  index *= 2;
-  uint8_t b = r[index];
-
-  *squareFrom = b & 0x3f;
-  uint8_t result = b & 0xc0;
-
-  index++;
-
-  b = r[index];
-  *squareTo = b & 0x3f;
-
-  b &= 0xc0;
-
-  switch (b)
-  {
-    case SCL_RECORD_PROM_Q: *promotedPiece = 'q'; break;
-    case SCL_RECORD_PROM_R: *promotedPiece = 'r'; break;
-    case SCL_RECORD_PROM_B: *promotedPiece = 'b'; break;
-    case SCL_RECORD_PROM_N: 
-    default:            *promotedPiece = 'n'; break;
-  }
-
-  return result;
-}
-
-uint8_t SCL_recordAdd(SCL_Record r, uint8_t squareFrom,
-  uint8_t squareTo, char promotePiece, uint8_t endState)
-{
-  uint16_t l = SCL_recordLength(r);
-
-  if (l >= SCL_RECORD_MAX_LENGTH)
-    return 0;
-
-  l *= 2;
-
-  if (l != 0)
-    r[l - 2] &= 0x3f; // remove the end flag from previous item
-
-  if (endState == SCL_RECORD_CONT)
-    endState = SCL_RECORD_END;
-
-  r[l] = squareFrom | endState;
-
-  uint8_t p;
-
-  switch (promotePiece)
-  {
-    case 'n': case 'N': p = SCL_RECORD_PROM_N; break;
-    case 'b': case 'B': p = SCL_RECORD_PROM_B; break;
-    case 'r': case 'R': p = SCL_RECORD_PROM_R; break;
-    case 'q': case 'Q': 
-    default:            p = SCL_RECORD_PROM_Q; break;
-  }
-
-  l++;
-
-  r[l] = squareTo | p;
-
-  return 1;
-}
-
-uint8_t SCL_recordRemoveLast(SCL_Record r)
-{
-  uint16_t l = SCL_recordLength(r);
-  
-  if (l == 0)
-    return 0;
-
-  if (l == 1)
-    SCL_recordInit(r);
-  else
-  {
-    l = (l - 2) * 2;
-
-    r[l] = (r[l] & 0x3f) | SCL_RECORD_END;
-  }
-  
-  return 1;
-}
-
-void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves)
-{
-  SCL_boardInit(b);
-
-  uint16_t l = SCL_recordLength(r);
-
-  if (moves > l)
-    moves = l;
-
-  for (uint16_t i = 0; i < moves; ++i)
-  {
-    uint8_t s0, s1;
-    char p;
-
-     SCL_recordGetMove(r,i,&s0,&s1,&p);
-     SCL_boardMakeMove(b,s0,s1,p);
-  }
-}
-
-void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo)
-{
-#if SCL_960_CASTLING
-  char squareToNow = board[moveUndo.squareTo];
-#endif
-
-  board[moveUndo.squareFrom] = board[moveUndo.squareTo];
-  board[moveUndo.squareTo] = moveUndo.other & 0x7f;
-  board[SCL_BOARD_PLY_BYTE]--;
-  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = moveUndo.enPassantCastle;
-  board[SCL_BOARD_MOVE_COUNT_BYTE] = moveUndo.moveCount;
-
-  if (moveUndo.other & 0x80)
-  {
-    moveUndo.squareTo /= 8;
-
-    if (moveUndo.squareTo == 0 || moveUndo.squareTo == 7)
-      board[moveUndo.squareFrom] = SCL_pieceIsWhite(board[moveUndo.squareFrom]) 
-        ? 'P' : 'p';
-      // ^ was promotion
-    else
-      board[(moveUndo.squareFrom / 8) * 8 + (moveUndo.enPassantCastle & 0x0f)] =
-        (board[moveUndo.squareFrom] == 'P') ? 'p' : 'P'; // was en passant
-  }
-#if !SCL_960_CASTLING
-  else if (board[moveUndo.squareFrom] == 'k' && // black castling
-    moveUndo.squareFrom == 60)
-  {
-    if (moveUndo.squareTo == 58)
-    {
-      board[59] = '.';
-      board[56] = 'r';
-    } 
-    else if (moveUndo.squareTo == 62)
-    {
-      board[61] = '.';
-      board[63] = 'r';
-    } 
-  }
-  else if (board[moveUndo.squareFrom] == 'K' && // white castling
-    moveUndo.squareFrom == 4)
-  {
-    if (moveUndo.squareTo == 2)
-    {
-      board[3] = '.';
-      board[0] = 'R';
-    } 
-    else if (moveUndo.squareTo == 6)
-    {
-      board[5] = '.';
-      board[7] = 'R';
-    } 
-  }
-#else // 960 castling
-  else if (((moveUndo.other & 0x7f) == 'r') && // black castling
-    (squareToNow == '.' || !SCL_pieceIsWhite(squareToNow)))
-  {
-    board[moveUndo.squareTo < moveUndo.squareFrom ? 59 : 61] = '.';
-    board[moveUndo.squareTo < moveUndo.squareFrom ? 58 : 62] = '.';
-
-    board[moveUndo.squareFrom] = 'k';
-    board[moveUndo.squareTo] = 'r';
-  }
-  else if (((moveUndo.other & 0x7f) == 'R') && // white castling
-    (squareToNow == '.' || SCL_pieceIsWhite(squareToNow)))
-  {
-    board[moveUndo.squareTo < moveUndo.squareFrom ? 3 : 5] = '.';
-    board[moveUndo.squareTo < moveUndo.squareFrom ? 2 : 6] = '.';
-
-    board[moveUndo.squareFrom] = 'K';
-    board[moveUndo.squareTo] = 'R';
-  }
-#endif
-}
-
-/**
-  Potentially disables castling rights according to whether something moved from
-  or to a square with a rook.
-*/
-void _SCL_handleRookActivity(SCL_Board board, uint8_t rookSquare)
-{
-#if !SCL_960_CASTLING
-  switch (rookSquare)
-  {
-    case 0:  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x20; break;
-    case 7:  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x10; break;
-    case 56: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x80; break;
-    case 63: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x40; break;
-    default: break;
-  }
-#else // 960 castling
-  if (rookSquare == (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
-    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x20;
-  else if (rookSquare == (board[SCL_BOARD_EXTRA_BYTE] >> 3))
-    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x10;
-  else if (rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
-    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x80;
-  else if (rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] >> 3))
-    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x40;
-#endif
-}
-
-SCL_MoveUndo SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo, 
-  char promotePiece)
-{
-  char s = board[squareFrom];
-
-  SCL_MoveUndo moveUndo;
-
-  moveUndo.squareFrom = squareFrom;
-  moveUndo.squareTo = squareTo; 
-  moveUndo.moveCount = board[SCL_BOARD_MOVE_COUNT_BYTE];
-  moveUndo.enPassantCastle = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE];
-  moveUndo.other = board[squareTo];
-
-  // reset the en-passant state
-  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] |= 0x0f;
-
-  if (SCL_boardMoveResetsCount(board,squareFrom,squareTo))
-    board[SCL_BOARD_MOVE_COUNT_BYTE] = 0;
-  else
-    board[SCL_BOARD_MOVE_COUNT_BYTE]++;
-
-#if SCL_960_CASTLING
-  uint8_t castled = 0;
-#endif
-
-  if ((s == 'k') || (s == 'K'))
-  {
-#if !SCL_960_CASTLING
-    if ((squareFrom == 4) || (squareFrom == 60)) // check castling
-    {
-      int8_t difference = squareTo - squareFrom;
-
-      char rook = SCL_pieceToColor('r',SCL_pieceIsWhite(s));
-
-      if (difference == 2) // short
-      {
-        board[squareTo - 1] = rook;
-        board[squareTo + 1] = '.';
-      }
-      else if (difference == -2) // long
-      {
-        board[squareTo - 2] = '.';
-        board[squareTo + 1] = rook;
-      }
-    }
-#else // 960 castling
-    uint8_t isWhite = SCL_pieceIsWhite(s);
-    char rook = SCL_pieceToColor('r',isWhite);
-
-    if (board[squareTo] == rook)
-    {
-      castled = 1;
-        
-      board[squareFrom] = '.';
-      board[squareTo] = '.';
-
-      if (squareTo > squareFrom) // short
-      {
-        board[isWhite ? 6 : (56 + 6)] = s;
-        board[isWhite ? 5 : (56 + 5)] = rook;
-      }
-      else // long
-      {
-        board[isWhite ? 2 : (56 + 2)] = s;
-        board[isWhite ? 3 : (56 + 3)] = rook;
-      }
-    }
-#endif
-
-    // after king move disable castling
-    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= ~(0x03 << ((s == 'K') ? 4 : 6));
-  }
-  else if ((s == 'p') || (s == 'P'))
-  {
-    uint8_t row = squareTo / 8;
-
-    int8_t rowDiff = squareFrom / 8 - row;
-
-    if (rowDiff == 2 || rowDiff == -2) // record en passant column
-    {
-      board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = 
-        (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0) | (squareFrom % 8);
-    }
-
-    if (row == 0 || row == 7)
-    {
-      // promotion
-      s = SCL_pieceToColor(promotePiece,SCL_pieceIsWhite(s));
-
-      moveUndo.other |= 0x80;
-    }
-    else
-    { 
-      // check en passant move
-
-      int8_t columnDiff = (squareTo % 8) - (squareFrom % 8);
-
-      if ((columnDiff != 0) && (board[squareTo] == '.'))
-      {
-        board[squareFrom + columnDiff] = '.'; 
-        moveUndo.other |= 0x80;
-      }
-    }
-  }
-  else if ((s == 'r') || (s == 'R'))
-    _SCL_handleRookActivity(board,squareFrom);
-
-  char taken = board[squareTo];
-
-  // taking a rook may also disable castling:
-
-  if (taken == 'R' || taken == 'r')
-    _SCL_handleRookActivity(board,squareTo);
-
-#if SCL_960_CASTLING
-  if (!castled)
-#endif
-  {
-    board[squareTo] = s;
-    board[squareFrom] = '.';
-  }
-
-  board[SCL_BOARD_PLY_BYTE]++; // increase ply count
-
-  return moveUndo;
-}
-
-void SCL_boardSetPosition(SCL_Board board, const char *pieces,
-  uint8_t castlingEnPassant, uint8_t moveCount, uint8_t ply)
-{
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, pieces++)
-    if (*pieces != 0)
-      board[i] = *pieces;
-    else
-      break;
-
-  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castlingEnPassant;
-  board[SCL_BOARD_PLY_BYTE] = ply;
-  board[SCL_BOARD_MOVE_COUNT_BYTE] = moveCount;
-  board[SCL_BOARD_STATE_SIZE - 1] = 0;
-}
-
-void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square)
-{
-  squareSet[square / 8] |= 0x01 << (square % 8);
-}
-
-uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square)
-{
-  return squareSet[square / 8] & (0x01 << (square % 8));
-}
-
-uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet)
-{
-  uint8_t result = 0;
-
-  for (uint8_t i = 0; i < 8; ++i)
-  {
-    uint8_t byte = squareSet[i];
-
-    for (uint8_t j = 0; j < 8; ++j)
-    {
-      result += byte & 0x01;
-      byte >>= 1;
-    }
-  }
-
-  return result;
-}
-
-uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet)
-{
-  for (uint8_t i = 0; i < 8; ++i)
-    if (squareSet[i] != 0)
-      return 0;  
-
-  return 1;
-}
-
-uint8_t SCL_squareSetGetRandom(
-  const SCL_SquareSet squareSet, SCL_RandomFunction randFunc)
-{
-  uint8_t size = SCL_squareSetSize(squareSet);
-
-  if (size == 0)
-    return 0;
-
-  uint8_t n = (randFunc() % size) + 1;
-  uint8_t i = 0;
-
-  while (i < SCL_BOARD_SQUARES)
-  {
-    if (SCL_squareSetContains(squareSet,i))
-    {
-      n--;
-
-      if (n == 0)
-        break;
-    }
-
-    ++i;
-  }
-
-  return i;
-}
-
-void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo)
-{
-  for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
-    boardTo[i] = boardFrom[i]; 
-}
-
-uint8_t SCL_boardSquareAttacked(
-  SCL_Board board,
-  uint8_t square,
-  uint8_t byWhite)
-{
-  const char *currentSquare = board;
-
-  /* We need to place a temporary piece on the tested square in order to test if
-     the square is attacked (consider testing if attacked by a pawn). */
-
-  char previous = board[square];
-
-  board[square] = SCL_pieceToColor('r',!byWhite);
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++currentSquare)
-  {
-    char s = *currentSquare;
-    if ((s == '.') || (SCL_pieceIsWhite(s) != byWhite))
-      continue;
-
-    SCL_SquareSet moves;
-    SCL_boardGetPseudoMoves(board,i,0,moves);
-
-    if (SCL_squareSetContains(moves,square))
-    {
-      board[square] = previous;
-      return 1;
-    }
-  }
-      
-  board[square] = previous;    
-  return 0;
-}
-
-uint8_t SCL_boardCheck(SCL_Board board,uint8_t white)
-{
-  const char *square = board;
-  char kingChar = white ? 'K' : 'k';
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++square)
-    if ((*square == kingChar && 
-      SCL_boardSquareAttacked(board,i,!white)))
-        return 1;
-
-  return 0;
-}
-
-uint8_t SCL_boardGameOver(SCL_Board board)
-{
-  uint8_t position = SCL_boardGetPosition(board);
-
-  return (position == SCL_POSITION_MATE) ||
-         (position == SCL_POSITION_STALEMATE) ||
-         (position == SCL_POSITION_DEAD);
-}
-
-uint8_t SCL_boardMovePossible(SCL_Board board)
-{
-  uint8_t white = SCL_boardWhitesTurn(board);
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
-  {
-    char s = board[i];
-
-    if ((s != '.') && (SCL_pieceIsWhite(s) == white))
-    {
-      SCL_SquareSet moves;
-
-      SCL_boardGetMoves(board,i,moves);
-
-      if (SCL_squareSetSize(moves) != 0)
-        return 1;
-    }
-  }
-
-  return 0;
-}
-
-uint8_t SCL_boardMate(SCL_Board board)
-{
-  return SCL_boardGetPosition(board) == SCL_POSITION_MATE;
-}
-
-void SCL_boardGetPseudoMoves(
-  SCL_Board board,
-  uint8_t pieceSquare,
-  uint8_t checkCastling,
-  SCL_SquareSet result)
-{
-  char piece = board[pieceSquare];
-
-  SCL_squareSetClear(result);
-
-  uint8_t isWhite = SCL_pieceIsWhite(piece);
-  int8_t horizontalPosition = pieceSquare % 8;
-  int8_t pawnOffset = -8;
-
-  switch (piece)
-  {
-    case 'P':
-      pawnOffset = 8;
-      // intentional fallthrough
-    case 'p':
-    {
-      uint8_t square = pieceSquare + pawnOffset;
-      uint8_t verticalPosition = pieceSquare / 8;
-
-      if (board[square] == '.') // forward move
-      {
-        SCL_squareSetAdd(result,square);
-
-        if (verticalPosition == (1 + (piece == 'p') * 5)) // start position?
-        {
-          uint8_t square2 = square + pawnOffset;
-
-          if (board[square2] == '.')
-            SCL_squareSetAdd(result,square2);
-        }
-      }
-
-      #define checkDiagonal(hor,add) \
-        if (horizontalPosition != hor) \
-        { \
-          uint8_t square2 = square + add; \
-          char c = board[square2]; \
-          if (c != '.' && SCL_pieceIsWhite(c) != isWhite) \
-            SCL_squareSetAdd(result,square2); \
-        }
-
-      // diagonal moves
-      checkDiagonal(0,-1)
-      checkDiagonal(7,1)
-
-      uint8_t enPassantRow = 4;
-      uint8_t enemyPawn = 'p';
-
-      if (piece == 'p')
-      {
-        enPassantRow = 3;
-        enemyPawn = 'P';
-      }
-
-      // en-passant moves
-      if (verticalPosition == enPassantRow)
-      {
-        uint8_t enPassantColumn = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
-        uint8_t column = pieceSquare % 8;
-
-        for (int8_t offset = -1; offset < 2; offset += 2)
-          if ((enPassantColumn == column + offset) &&
-              (board[pieceSquare + offset] == enemyPawn))
-          {
-            SCL_squareSetAdd(result,pieceSquare + pawnOffset + offset);
-            break;
-          }
-      } 
-
-      #undef checkDiagonal
-    }
-      break;
-
-    case 'r': // rook
-    case 'R':
-    case 'b': // bishop
-    case 'B':
-    case 'q': // queen
-    case 'Q':
-    {
-      const int8_t offsets[8] =    {-8, 1, 8, -1, -7, 9, -9, 7};
-      const int8_t columnDirs[8] = { 0, 1, 0, -1,  1, 1, -1,-1};
-
-      uint8_t from = (piece == 'b' || piece == 'B') * 4;
-      uint8_t to = 4 + (piece != 'r' && piece != 'R') * 4;
-
-      for (uint8_t i = from; i < to; ++i)
-      {
-        int8_t offset = offsets[i];
-        int8_t columnDir = columnDirs[i];
-        int8_t square = pieceSquare;
-        int8_t col = horizontalPosition;
-
-        while (1)
-        {
-          square += offset;
-          col += columnDir;
-
-          if (square < 0 || square > 63 || col < 0 || col > 7)
-            break;
-
-          char squareC = board[square];
-
-          if (squareC == '.')
-            SCL_squareSetAdd(result,square);
-          else
-          {
-            if (SCL_pieceIsWhite(squareC) != isWhite)
-              SCL_squareSetAdd(result,square);
-
-            break;
-          }
-        }
-      }
-    }
-      break;
-
-    case 'n': // knight
-    case 'N':
-    {
-      const int8_t offsets[4] = {6, 10, 15, 17};
-      const int8_t columnsMinus[4] = {2,-2,1,-1};
-      const int8_t columnsPlus[4] = {-2,2,-1,1};
-      const int8_t *off, *col;
-
-      #define checkOffsets(op,comp,limit,dir)\
-        off = offsets;\
-        col = columns ## dir;\
-        for (uint8_t i = 0; i < 4; ++i, ++off, ++col)\
-        {\
-          int8_t square = pieceSquare op (*off);\
-          if (square comp limit) /* out of board? */\
-            break;\
-          int8_t horizontalCheck = horizontalPosition + (*col);\
-          if (horizontalCheck < 0 || horizontalCheck >= 8)\
-            continue;\
-          char squareC = board[square];\
-          if ((squareC == '.') || (SCL_pieceIsWhite(squareC) != isWhite))\
-            SCL_squareSetAdd(result,square);\
-        }
-
-      checkOffsets(-,<,0,Minus)
-      checkOffsets(+,>=,SCL_BOARD_SQUARES,Plus)
-
-      #undef checkOffsets
-    } 
-      break; 
-
-    case 'k': // king
-    case 'K':
-    {
-      uint8_t verticalPosition = pieceSquare / 8;
-
-      uint8_t
-        u = verticalPosition != 0,
-        d = verticalPosition != 7,
-        l = horizontalPosition != 0,
-        r = horizontalPosition != 7;
-
-      uint8_t square2 = pieceSquare - 9;
-
-      #define checkSquare(cond,add) \
-        if (cond && ((board[square2] == '.') || \
-            (SCL_pieceIsWhite(board[square2])) != isWhite))\
-          SCL_squareSetAdd(result,square2);\
-        square2 += add;
-
-      checkSquare(l && u,1)
-      checkSquare(u,1)
-      checkSquare(r && u,6)
-      checkSquare(l,2)
-      checkSquare(r,6)
-      checkSquare(l && d,1)
-      checkSquare(d,1)
-      checkSquare(r && d,0)
-
-      #undef checkSquare
-
-      // castling:
-
-      if (checkCastling)
-      {
-        uint8_t bitShift = 4 + 2 * (!isWhite);
-
-        if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x03 << bitShift)) &&
-          !SCL_boardSquareAttacked(board,pieceSquare,!isWhite)) // no check?
-        { 
-#if !SCL_960_CASTLING
-          // short castle:
-          pieceSquare++;
-            
-          if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x01 << bitShift)) &&
-              (board[pieceSquare] == '.') &&
-              (board[pieceSquare + 1] == '.') &&
-              (board[pieceSquare + 2] == SCL_pieceToColor('r',isWhite)) &&
-              !SCL_boardSquareAttacked(board,pieceSquare,!isWhite))
-            SCL_squareSetAdd(result,pieceSquare + 1);
-
-          /* note: don't check the final square for check, it will potentially
-             be removed later (can't end up in check) */
-
-          // long castle:
-          pieceSquare -= 2;
-
-          if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x02 << bitShift)) &&
-              (board[pieceSquare] == '.') &&
-              (board[pieceSquare - 1] == '.') &&
-              (board[pieceSquare - 2] == '.') &&
-              (board[pieceSquare - 3] == SCL_pieceToColor('r',isWhite)) &&
-              !SCL_boardSquareAttacked(board,pieceSquare,!isWhite))
-            SCL_squareSetAdd(result,pieceSquare - 1);
-#else // 960 castling
-          for (int i = 0; i < 2; ++i) // short and long
-            if (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & ((i + 1) << bitShift))
-            {
-              uint8_t 
-                rookPos = board[SCL_BOARD_EXTRA_BYTE] >> 3,
-                targetPos = 5;
-
-              if (i == 1)
-              {
-                rookPos = board[SCL_BOARD_EXTRA_BYTE] & 0x07,
-                targetPos = 3;
-              }
-
-              if (!isWhite)
-              {
-                rookPos += 56;
-                targetPos += 56;
-              }
-
-              uint8_t ok = board[rookPos] == SCL_pieceToColor('r',isWhite);
-
-              if (!ok)
-                continue;
-
-              int8_t inc = 1 - 2 * (targetPos > rookPos);
-
-              while (targetPos != rookPos) // check vacant squares for the rook
-              {
-                if (board[targetPos] != '.' && 
-                    targetPos != pieceSquare)
-                {
-                  ok = 0;
-                  break;
-                }
-
-                targetPos += inc;
-              }
-
-              if (!ok) 
-                continue;
-
-              targetPos = i == 0 ? 6 : 2;
-
-              if (!isWhite)
-                targetPos += 56;
-
-              inc = 1 - 2 * (targetPos > pieceSquare);
-
-              while (targetPos != pieceSquare) // check squares for the king
-              {
-                if ((board[targetPos] != '.' && 
-                     targetPos != rookPos) ||
-                     SCL_boardSquareAttacked(board,targetPos,!isWhite))
-                {
-                  ok = 0;
-                  break;
-                }
-
-                targetPos += inc;
-              }
-
-              if (ok)
-                SCL_squareSetAdd(result,rookPos);
-            }
-#endif
-        }
-      }
-    }
-      break;
-
-    default:
-      break;
-  }
-}
-
-void SCL_printSquareSet(SCL_SquareSet set, SCL_PutCharFunction putCharFunc)
-{
-  uint8_t first = 1;
-
-  putCharFunc('(');
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
-  {
-    if (!SCL_squareSetContains(set, i))
-      continue;
-
-    if (!first)
-      putCharFunc(',');
-    else
-      first = 0;
-
-    putCharFunc('A' + i % 8);
-    putCharFunc('1' + i / 8);
-  }
-
-  putCharFunc(')');
-}
-
-void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc)
-{
-  uint32_t val = 0;
-
-  switch (square)
-  {
-    case 'r': val = 0x9c99e200; break;
-    case 'n': val = 0x9e99e200; break;
-    case 'b': val = 0x9d99e200; break;
-    case 'q': val = 0x9b99e200; break;
-    case 'k': val = 0x9a99e200; break;
-    case 'p': val = 0x9f99e200; break;
-    case 'R': val = 0x9699e200; break;
-    case 'N': val = 0x9899e200; break;
-    case 'B': val = 0x9799e200; break;
-    case 'Q': val = 0x9599e200; break;
-    case 'K': val = 0x9499e200; break;
-    case 'P': val = 0x9999e200; break;
-    case '.': val = 0x9296e200; break;
-    case ',': val = 0x9196e200; break;
-    default:  putCharFunc(square); return; break;
-  }
-
-  uint8_t count = 4;
-
-  while ((val % 256 == 0) && (count > 0))
-  {
-    val /= 256;
-    count--;
-  }
-
-  while (count > 0)
-  {
-    putCharFunc(val % 256);
-    val /= 256;
-    count--;
-  }
-}
-
-void SCL_boardGetMoves(
-  SCL_Board board,
-  uint8_t pieceSquare,
-  SCL_SquareSet result)
-{
-  SCL_SquareSet allMoves;
-
-  SCL_squareSetClear(allMoves);
-
-  for (uint8_t i = 0; i < 8; ++i)
-    result[i] = 0;
-
-  SCL_boardGetPseudoMoves(board,pieceSquare,1,allMoves);
-
-  // Now only keep moves that don't lead to one's check:
-
-  SCL_SQUARE_SET_ITERATE_BEGIN(allMoves)
-
-    SCL_MoveUndo undo = SCL_boardMakeMove(board,pieceSquare,iteratedSquare,'q');
-
-    if (!SCL_boardCheck(board,!SCL_boardWhitesTurn(board)))
-      SCL_squareSetAdd(result,iteratedSquare);
-
-    SCL_boardUndoMove(board,undo);
-
-  SCL_SQUARE_SET_ITERATE_END
-}
-
-uint8_t SCL_boardDead(SCL_Board board)
-{
-  /*
-    This byte represents material by bits:
-
-    MSB _ _ _ _ _ _ _ _ LSB
-          | | |   | | \_ white knight
-          | | |   |  \__ white bishop on white
-          | | |    \____ white bishop on black
-          | |  \________ black knight
-          |  \__________ black bishop on white
-           \____________ black bishop on black
-  */
-  uint8_t material = 0;
-
-  const char *p = board;
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
-  {
-    char c = *p;
-
-    switch (c)
-    {
-      case 'n': material |= 0x01; break;
-      case 'N': material |= 0x10; break;
-      case 'b': material |= (0x02 << (!SCL_squareIsWhite(i))); break;
-      case 'B': material |= (0x20 << (!SCL_squareIsWhite(i))); break;
-      case 'p':
-      case 'P': 
-      case 'r':
-      case 'R': 
-      case 'q':
-      case 'Q': 
-        return 0; // REMOVE later if more complex check are performed
-        break;
-
-      default: break;
-    }
-
-    p++;
-  }
-
-  // TODO: add other checks than only insufficient material
-
-  // possible combinations of insufficient material:
-
-  return
-    (material == 0x00) || // king vs king
-    (material == 0x01) || // king and knight vs king 
-    (material == 0x10) || // king and knight vs king
-    (material == 0x02) || // king and bishop vs king
-    (material == 0x20) || // king and bishop vs king
-    (material == 0x04) || // king and bishop vs king
-    (material == 0x40) || // king and bishop vs king
-    (material == 0x22) || // king and bishop vs king and bishop (same color)
-    (material == 0x44);   // king and bishop vs king and bishop (same color)
-}
-
-uint8_t SCL_boardGetPosition(SCL_Board board)
-{
-  uint8_t check = SCL_boardCheck(board,SCL_boardWhitesTurn(board)); 
-  uint8_t moves = SCL_boardMovePossible(board);
-
-  if (check)
-    return moves ? SCL_POSITION_CHECK : SCL_POSITION_MATE;
-  else if (!moves)
-    return SCL_POSITION_STALEMATE;
-
-  if (SCL_boardDead(board))
-    return SCL_POSITION_DEAD;
-
-  return SCL_POSITION_NORMAL;
-}
-
-uint8_t SCL_stringToMove(const char *moveString, uint8_t *resultFrom, 
-  uint8_t *resultTo, char *resultPromotion)
-{
-  char c;
-
-  uint8_t *dst = resultFrom;
-
-  for (uint8_t i = 0; i < 2; ++i)
-  {
-    c = *moveString;
-  
-    *dst = (c >= 'a') ? (c - 'a') : (c - 'A');
-
-    if (*dst > 7)
-      return 0;
-
-    moveString++;    
-    c = *moveString;
-  
-    *dst += 8 * (c - '1');
-  
-    if (*dst > 63)
-      return 0;
-  
-    moveString++;    
-
-    dst = resultTo;
-  }
-
-  c = *moveString;
-
-  if (c < 'A')
-    c = c - 'A' + 'a';
-
-  switch (c)
-  {
-    case 'N': case 'n': *resultPromotion = 'n'; break;
-    case 'B': case 'b': *resultPromotion = 'b'; break;
-    case 'R': case 'r': *resultPromotion = 'r'; break;
-    case 'Q': case 'q':
-    default: *resultPromotion = 'q'; break;
-  }
-  
-  return 1;
-}
-
-void SCL_printBoard(
-  SCL_Board board,
-  SCL_PutCharFunction putCharFunc,
-  SCL_SquareSet highlightSquares,
-  uint8_t selectSquare,
-  uint8_t format,
-  uint8_t offset,
-  uint8_t labels,
-  uint8_t blackDown)
-{
-  if (labels)
-  {
-    for (uint8_t i = 0; i < offset + 2; ++i)
-      putCharFunc(' ');
-
-    for (uint8_t i = 0; i < 8; ++i)
-    {
-      if ((format != SCL_PRINT_FORMAT_COMPACT) &&
-          (format != SCL_PRINT_FORMAT_COMPACT_UTF8))
-        putCharFunc(' ');
-
-      putCharFunc(blackDown ? ('H' - i) : ('A' + i));
-    }     
-
-    putCharFunc('\n');
-  }   
-  int8_t i = 7;
-  int8_t add = 1;
-
-  if (!blackDown)
-  {
-    i = 56;
-    add = -1;
-  }
-
-  for (int8_t row = 0; row < 8; ++row)
-  {
-    for (uint8_t j = 0; j < offset; ++j)
-      putCharFunc(' ');
-
-    if (labels)
-    {
-      putCharFunc(!blackDown ? ('8' - row) : ('1' + row));
-      putCharFunc(' ');
-    }
-
-    const char *square = board + i;
-
-    for (int8_t col = 0; col < 8; ++col)
-    {
-      switch (format)
-      {
-        case SCL_PRINT_FORMAT_COMPACT:
-          putCharFunc(
-            (*square == '.') ? (
-            ((i != selectSquare) ?
-              (!SCL_squareSetContains(highlightSquares,i) ? *square : '*')
-              : '#')) : *square);
-          break;
-
-        case SCL_PRINT_FORMAT_UTF8:
-        {
-          char squareChar = SCL_squareIsWhite(i) ? '.' : ',';
-          char pieceChar = (*square == '.') ? squareChar : *square;
-
-          if (i == selectSquare)
-          {
-            putCharFunc('(');
-
-            if (*square == '.')
-              putCharFunc(')');
-            else
-              SCL_printSquareUTF8(pieceChar,putCharFunc);
-          }
-          else if (!SCL_squareSetContains(highlightSquares,i))
-          {
-            SCL_printSquareUTF8(squareChar,putCharFunc);
-            SCL_printSquareUTF8(pieceChar,putCharFunc);
-          }
-          else
-          {
-            putCharFunc('[');
-
-            if (*square == '.')
-              putCharFunc(']');
-            else
-              SCL_printSquareUTF8(*square,putCharFunc);
-          }
-
-          break;
-        }
-
-        case SCL_PRINT_FORMAT_COMPACT_UTF8:
-          SCL_printSquareUTF8(
-            (*square == '.') ? (
-              SCL_squareSetContains(highlightSquares,i) ? '*' :
-              (i == selectSquare ? '#' : ((SCL_squareIsWhite(i) ? '.' : ',')))
-            ) : *square,putCharFunc);
-          break;
-
-        case SCL_PRINT_FORMAT_NORMAL:
-        default:
-        {
-          uint8_t c = *square;
-
-          char squareColor = SCL_squareIsWhite(i) ? ' ' : ':';
-
-          putCharFunc((i != selectSquare) ?
-            (!SCL_squareSetContains(highlightSquares,i) ?
-            squareColor : '#') : '@');
-
-          putCharFunc(c == '.' ? squareColor : *square);
-          break;
-        }
-      }
-
-      i -= add;
-      square -= add;
-    }
-        
-    putCharFunc('\n');
-
-    i += add * 16;  
-  } // for rows
-}
-
-int16_t SCL_pieceValuePositive(char piece)
-{
-  switch (piece)
-  {
-    case 'p':
-    case 'P': return SCL_VALUE_PAWN; break;
-    case 'n':
-    case 'N': return SCL_VALUE_KNIGHT; break;
-    case 'b':
-    case 'B': return SCL_VALUE_BISHOP; break;
-    case 'r':
-    case 'R': return SCL_VALUE_ROOK; break;
-    case 'q':
-    case 'Q': return SCL_VALUE_QUEEN; break;
-    case 'k':
-    case 'K': return SCL_VALUE_KING; break;
-    default: break;
-  }
-
-  return 0;
-}
-
-int16_t SCL_pieceValue(char piece)
-{
-  switch (piece)
-  {
-    case 'P': return SCL_VALUE_PAWN; break;
-    case 'N': return SCL_VALUE_KNIGHT; break;
-    case 'B': return SCL_VALUE_BISHOP; break;
-    case 'R': return SCL_VALUE_ROOK; break;
-    case 'Q': return SCL_VALUE_QUEEN; break;
-    case 'K': return SCL_VALUE_KING; break;
-    case 'p': return -1 * SCL_VALUE_PAWN; break;
-    case 'n': return -1 * SCL_VALUE_KNIGHT; break;
-    case 'b': return -1 * SCL_VALUE_BISHOP; break;
-    case 'r': return -1 * SCL_VALUE_ROOK; break;
-    case 'q': return -1 * SCL_VALUE_QUEEN; break;
-    case 'k': return -1 * SCL_VALUE_KING; break;
-    default: break;
-  }
-
-  return 0;
-}
-
-#define ATTACK_BONUS 3
-#define MOBILITY_BONUS 10
-#define CENTER_BONUS 7
-#define CHECK_BONUS 5
-#define KING_CASTLED_BONUS 30
-#define KING_BACK_BONUS 15
-#define KING_NOT_CENTER_BONUS 15
-#define PAWN_NON_DOUBLE_BONUS 3
-#define PAWN_PAIR_BONUS 3
-#define KING_CENTERNESS 10
-
-int16_t _SCL_rateKingEndgamePosition(uint8_t position)
-{
-  int16_t result = 0;
-  uint8_t rank = position / 8;
-  position %= 8;
-
-  if (position > 1 && position < 6)
-    result += KING_CENTERNESS;
-
-  if (rank > 1 && rank < 6)
-    result += KING_CENTERNESS;
-
-  return result;
-}
-
-int16_t SCL_boardEvaluateStatic(SCL_Board board)
-{
-  uint8_t position = SCL_boardGetPosition(board);
-  
-  int16_t total = 0;                        
-
-  switch (position)
-  {
-    case SCL_POSITION_MATE:
-      return SCL_boardWhitesTurn(board) ? 
-        -1 * SCL_EVALUATION_MAX_SCORE : SCL_EVALUATION_MAX_SCORE;
-      break;
-
-    case SCL_POSITION_STALEMATE:
-    case SCL_POSITION_DEAD:
-      return 0;
-      break;
-
-    /*
-      main points are assigned as follows:
-      - points for material as a sum of all material on board
-      - for playing side: if a piece attacks piece of greater value, a fraction
-        of the value difference is gained (we suppose exchange), this is only
-        gained once per every attacking piece (maximum gain is taken), we only
-        take fraction so that actually taking the piece is favored
-      - ATTACK_BONUS points for any attacked piece
-
-      other points are assigned as follows (in total these shouldn't be more
-      than the value of one pawn)
-      - mobility: MOBILITY_BONUS points for each piece with at least 4 possible
-        moves
-      - center control: CENTER_BONUS points for a piece on a center square
-      - CHECK_BONUS points for check
-      - king:
-        - safety (non endgame): KING_BACK_BONUS points for king on staring rank,
-          additional KING_CASTLED_BONUS if the kind if on castled square or
-          closer to the edge, additional KING_NOT_CENTER_BONUS for king not on
-          its start neighbouring center square
-        - center closeness (endgame): up to 2 * KING_CENTERNESS points for
-          being closer to center
-      - non-doubled pawns: PAWN_NON_DOUBLE_BONUS points for each pawn without
-        same color pawn directly in front of it 
-      - pawn structure: PAWN_PAIR_BONUS points for each pawn guarding own pawn
-      - advancing pawns: 1 point for each pawn's rank in its move
-        direction
-    */
-
-    case SCL_POSITION_CHECK:
-      total += SCL_boardWhitesTurn(board) ? -1 * CHECK_BONUS : CHECK_BONUS;
-      // intentional fallthrough
-    case SCL_POSITION_NORMAL:
-    default:
-    {
-      SCL_SquareSet moves;
-
-      const char *p = board;
-
-      int16_t positiveMaterial = 0;
-      uint8_t endgame = 0;
-
-      // first count material to see if this is endgame or not
-      for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p)
-      {
-        char s = *p;
-
-        if (s != '.')
-        {
-          positiveMaterial += SCL_pieceValuePositive(s);
-          total += SCL_pieceValue(s);
-        }
-      }
-
-      endgame = positiveMaterial <= SCL_ENDGAME_MATERIAL_LIMIT;
-
-      p = board;
-
-      for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p)
-      {
-        char s = *p;
-
-        if (s != '.')
-        {
-          uint8_t white = SCL_pieceIsWhite(s);
-
-          switch (s)
-          {
-            case 'k': // king safety
-              if (endgame)
-                total -= _SCL_rateKingEndgamePosition(i);
-              else if (i >= 56)
-              {
-                total -= KING_BACK_BONUS;
-
-                if (i != 59)
-                {
-                  total -= KING_NOT_CENTER_BONUS;
-
-                  if (i >= 62 || i <= 58)
-                    total -= KING_CASTLED_BONUS;
-                }
-              }
-            break;
-
-            case 'K':
-              if (endgame)
-                total += _SCL_rateKingEndgamePosition(i);
-              else if (i <= 7)
-              {
-                total += KING_BACK_BONUS;
-
-                if (i != 3)
-                {
-                  total += KING_NOT_CENTER_BONUS;
-
-                  if (i <= 2 || i >= 6)
-                    total += KING_CASTLED_BONUS;
-                }
-              }
-            break;
-
-            case 'P': // pawns
-            case 'p':
-            {
-              int8_t rank = i / 8;
-
-              if (rank != 0 && rank != 7)
-              {
-                if (s == 'P')
-                {
-                  total += rank;
-
-                  char *tmp = board + i + 8;
-
-                  if (*tmp != 'P')
-                    total += PAWN_NON_DOUBLE_BONUS;
-
-                  if (i % 8 != 7)
-                  {
-                    tmp++;
-
-                    if (*tmp == 'P')
-                      total += PAWN_PAIR_BONUS;
-
-                    if (*(tmp - 16) == 'P')
-                      total += PAWN_PAIR_BONUS;
-                  }
-                }
-                else
-                {
-                  total -= 7 - rank;
-
-                  char *tmp = board + i - 8;
-
-                  if (*tmp != 'p')
-                    total -= PAWN_NON_DOUBLE_BONUS;
-
-                  if (i % 8 != 7)
-                  {
-                    tmp += 17;
-
-                    if (*tmp == 'p')
-                      total -= PAWN_PAIR_BONUS;
-
-                    if (*(tmp - 16) == 'p')
-                      total -= PAWN_PAIR_BONUS;
-                  }
-                }
-              }
-           
-              break;
-            }
-
-            default: break;
-          }
-
-          if (i >= 27 && i <= 36 && (i >= 35 || i <= 28)) // center control
-            total += white ? CENTER_BONUS : (-1 * CENTER_BONUS);
-
-          // for performance we only take pseudo moves
-          SCL_boardGetPseudoMoves(board,i,0,moves);
-
-          if (SCL_squareSetSize(moves) >= 4) // mobility
-            total += white ? 
-              MOBILITY_BONUS : (-1 * MOBILITY_BONUS);
-
-          int16_t exchangeBonus = 0;
-
-          SCL_SQUARE_SET_ITERATE_BEGIN(moves)
-
-            if (board[iteratedSquare] != '.')
-            {
-              total += white ? 
-                ATTACK_BONUS : (- 1 * ATTACK_BONUS);
-
-              if (SCL_boardWhitesTurn(board) == white)
-              {
-                int16_t valueDiff = 
-                  SCL_pieceValuePositive(board[iteratedSquare]) - 
-                  SCL_pieceValuePositive(s);
-
-                valueDiff /= 4; // only take a fraction to favor taking
-
-                if (valueDiff > exchangeBonus)
-                  exchangeBonus = valueDiff;
-              }
-            }
-
-          SCL_SQUARE_SET_ITERATE_END
-
-          if (exchangeBonus != 0)
-            total += white ? exchangeBonus : -1 * exchangeBonus;
-        }
-      } // for each square
-
-      return total;
-
-      break;
-
-    } // normal position
-  } // switch
-
-  return 0;
-}
-
-#undef ATTACK_BONUS
-#undef MOBILITY_BONUS
-#undef CENTER_BONUS
-#undef CHECK_BONUS
-#undef KING_CASTLED_BONUS
-#undef KING_BACK_BONUS
-#undef PAWN_NON_DOUBLE_BONUS
-#undef PAWN_PAIR_BONUS
-#undef KING_CENTERNESS
-
-SCL_StaticEvaluationFunction _SCL_staticEvaluationFunction;
-int16_t _SCL_currentEval;
-int8_t _SCL_depthHardLimit;
-
-/**
-  Inner recursive function for SCL_boardEvaluateDynamic. It is passed a square
-  (or -1) at which last capture happened, to implement capture extension.
-*/
-int16_t _SCL_boardEvaluateDynamic(SCL_Board board, int8_t depth,
-  int16_t alphaBeta, int8_t takenSquare)
-{
-#if SCL_COUNT_EVALUATED_POSITIONS
-  SCL_positionsEvaluated++;
-#endif
-
-#if SCL_CALL_WDT_RESET
-  wdt_reset();
-#endif
-
-  uint8_t whitesTurn = SCL_boardWhitesTurn(board);
-  int8_t valueMultiply = whitesTurn ? 1 : -1;
-  int16_t bestMoveValue = -1 * SCL_EVALUATION_MAX_SCORE;
-  uint8_t shouldCompute = depth > 0;
-  uint8_t extended = 0;
-  uint8_t positionType = SCL_boardGetPosition(board);
-
-  if (!shouldCompute)
-  {
-    /* here we do two extensions (deeper search): taking on a same square 
-      (exchanges) and checks (good for mating and preventing mates): */
-    extended =
-      (depth > _SCL_depthHardLimit) &&
-      (takenSquare >= 0 ||
-      (SCL_boardGetPosition(board) == SCL_POSITION_CHECK));
-
-    shouldCompute = extended;
-  }
-
-#if SCL_DEBUG_AI
-  char moveStr[8];
-  uint8_t debugFirst = 1;
-#endif
-
-  if (shouldCompute &&
-    (positionType == SCL_POSITION_NORMAL || positionType == SCL_POSITION_CHECK))
-  {
-#if SCL_DEBUG_AI
-    putchar('(');
-#endif
-
-    alphaBeta *= valueMultiply;
-    uint8_t end = 0;
-    const char *b = board;
-
-    depth--;
-
-    for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b)
-    {
-      char s = *b;
-
-      if (s != '.' && SCL_pieceIsWhite(s) == whitesTurn)
-      {
-        SCL_SquareSet moves;
-
-        SCL_squareSetClear(moves);
-
-        SCL_boardGetMoves(board,i,moves);
-
-        if (!SCL_squareSetEmpty(moves))
-        {
-          SCL_SQUARE_SET_ITERATE_BEGIN(moves)
-
-            int8_t captureExtension = -1;
-            
-            if (board[iteratedSquare] != '.' &&   // takes a piece
-              (takenSquare == -1 ||               // extend on first taken sq. 
-              (extended && takenSquare != -1) ||  // ignore check extension
-              (iteratedSquare == takenSquare)))   // extend on same sq. taken
-              captureExtension = iteratedSquare;
-
-            SCL_MoveUndo undo = SCL_boardMakeMove(board,i,iteratedSquare,'q');
-
-            uint8_t s0Dummy, s1Dummy;
-            char pDummy;
-
-            SCL_UNUSED(s0Dummy);
-            SCL_UNUSED(s1Dummy);
-            SCL_UNUSED(pDummy);
-
-#if SCL_DEBUG_AI
-            if (debugFirst)
-              debugFirst = 0;
-            else
-              putchar(',');
-
-            if (extended)
-              putchar('*');
-
-            printf("%s ",SCL_moveToString(board,i,iteratedSquare,'q',moveStr));
-#endif
-
-            int16_t value = _SCL_boardEvaluateDynamic(
-              board,
-              depth, // this is depth - 1, we decremented it
-#if SCL_ALPHA_BETA
-              valueMultiply * bestMoveValue,
-#else
-              0,
-#endif      
-              captureExtension
-              ) * valueMultiply;
-
-            SCL_boardUndoMove(board,undo);
-
-            if (value > bestMoveValue) 
-            {
-              bestMoveValue = value;
-
-#if SCL_ALPHA_BETA
-              // alpha-beta pruning:
-
-              if (value > alphaBeta) // no, >= can't be here
-              {
-                end = 1;
-                iterationEnd = 1;
-              }
-#endif
-            }
-
-          SCL_SQUARE_SET_ITERATE_END
-        } // !squre set empty?
-      } // valid piece?
-
-      if (end)
-        break;
-
-    } // for each square
-
-#if SCL_DEBUG_AI
-  putchar(')');
-#endif
-  }
-  else // don't dive recursively, evaluate statically
-  {
-    bestMoveValue = valueMultiply *
-  #ifndef SCL_EVALUATION_FUNCTION
-      _SCL_staticEvaluationFunction(board);
-  #else
-      SCL_EVALUATION_FUNCTION(board);
-  #endif
-
-    /* For stalemate return the opposite value of the board, i.e. if the
-       position is good for white, then stalemate is good for black and vice
-       versa. */
-    if (positionType == SCL_POSITION_STALEMATE)
-      bestMoveValue *= -1;
-  }
-
-  /* Here we either improve (if the move worsens the situation) or devalve (if
-     it improves the situation) the result: this needs to be done so that good
-     moves far away are seen as worse compared to equally good moves achieved
-     in fewer moves. Without this an AI in winning situation may just repeat
-     random moves and draw by repetition even if it has mate in 1 (it sees all
-     moves as leading to mate). */
-  bestMoveValue += bestMoveValue > _SCL_currentEval * valueMultiply ? -1 : 1;
-
-#if SCL_DEBUG_AI
-  printf("%d",bestMoveValue * valueMultiply);
-#endif
-
-  return bestMoveValue * valueMultiply;
-}
-
-int16_t SCL_boardEvaluateDynamic(SCL_Board board, uint8_t baseDepth,
-  uint8_t extensionExtraDepth, SCL_StaticEvaluationFunction evalFunction)
-{
-  _SCL_staticEvaluationFunction = evalFunction;
-  _SCL_currentEval = evalFunction(board);
-  _SCL_depthHardLimit = 0;
-  _SCL_depthHardLimit -= extensionExtraDepth;
-
-  return _SCL_boardEvaluateDynamic(
-    board,
-    baseDepth,
-    SCL_boardWhitesTurn(board) ? 
-      SCL_EVALUATION_MAX_SCORE : (-1 * SCL_EVALUATION_MAX_SCORE),-1); 
-}
-
-void SCL_boardRandomMove(SCL_Board board, SCL_RandomFunction randFunc,
-  uint8_t *squareFrom, uint8_t *squareTo, char *resultProm)
-{
-  *resultProm = (randFunc() < 128) ? 
-    ((randFunc() < 128) ? 'r' : 'n') : 
-    ((randFunc() < 128) ? 'b' : 'q');
-
-  SCL_SquareSet set;
-  uint8_t white = SCL_boardWhitesTurn(board);
-  const char *s = board;
-
-  SCL_squareSetClear(set);
-
-  // find squares with pieces that have legal moves
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++s)
-  {
-    char c = *s;
-
-    if (c != '.' && SCL_pieceIsWhite(c) == white)
-    {
-      SCL_SquareSet moves;
-
-      SCL_boardGetMoves(board,i,moves);
-
-      if (SCL_squareSetSize(moves) != 0)
-        SCL_squareSetAdd(set,i);
-    }
-  }
-  *squareFrom = SCL_squareSetGetRandom(set,randFunc);
-
-  SCL_boardGetMoves(board,*squareFrom,set);
-
-  *squareTo = SCL_squareSetGetRandom(set,randFunc);
-}
-
-void SCL_printBoardSimple(
-  SCL_Board board,
-  SCL_PutCharFunction putCharFunc,
-  uint8_t selectSquare,
-  uint8_t format)
-{
-  SCL_SquareSet s;
-
-  SCL_squareSetClear(s);
-
-  SCL_printBoard(board,putCharFunc,s,selectSquare,format,1,1,0);
-}
-
-int16_t SCL_getAIMove(
-  SCL_Board board,
-  uint8_t baseDepth,
-  uint8_t extensionExtraDepth,
-  uint8_t endgameExtraDepth,
-  SCL_StaticEvaluationFunction evalFunc,
-  SCL_RandomFunction randFunc,
-  uint8_t randomness,
-  uint8_t repetitionMoveFrom,
-  uint8_t repetitionMoveTo,
-  uint8_t *resultFrom,
-  uint8_t *resultTo,
-  char *resultProm)
-{
-#if SCL_DEBUG_AI
-  puts("===== AI debug =====");
-  putchar('(');
-  unsigned char debugFirst = 1;
-  char moveStr[8];
-#endif 
-
-  if (baseDepth == 0)
-  {
-    SCL_boardRandomMove(board,randFunc,resultFrom,resultTo,resultProm);
-#ifndef SCL_EVALUATION_FUNCTION
-    return evalFunc(board);
-#else
-    return SCL_EVALUATION_FUNCTION(board);
-#endif
-  }
-
-  if (SCL_boardEstimatePhase(board) == SCL_PHASE_ENDGAME)
-    baseDepth += endgameExtraDepth;
-
-  *resultFrom = 0;
-  *resultTo = 0;
-  *resultProm = 'q';
-
-  int16_t bestScore =
-    SCL_boardWhitesTurn(board) ?
-    -1 * SCL_EVALUATION_MAX_SCORE - 1 : (SCL_EVALUATION_MAX_SCORE + 1);
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
-    if (board[i] != '.' && 
-      SCL_boardWhitesTurn(board) == SCL_pieceIsWhite(board[i]))
-    {
-      SCL_SquareSet moves;
-
-      SCL_squareSetClear(moves);
-
-      SCL_boardGetMoves(board,i,moves);
-
-      SCL_SQUARE_SET_ITERATE_BEGIN(moves)
-
-        int16_t score = 0;
-
-#if SCL_DEBUG_AI
-      if (debugFirst)
-        debugFirst = 0;
-      else
-        putchar(',');
-
-printf("%s ",SCL_moveToString(
-board,i,iteratedSquare,'q',moveStr));
-
-#endif
-
-        if (i != repetitionMoveFrom || iteratedSquare != repetitionMoveTo)
-        {
-          SCL_MoveUndo undo = SCL_boardMakeMove(board,i,iteratedSquare,'q');
-
-          score = SCL_boardEvaluateDynamic(board,baseDepth - 1,
-            extensionExtraDepth,evalFunc);
-
-          SCL_boardUndoMove(board,undo);
-        }
-
-        if (randFunc != 0 &&
-          randomness > 1 &&
-          score < 16000 && 
-          score > -16000)
-        {
-          /*^ We limit randomizing by about half the max score for two reasons:
-            to prevent over/under flows and secondly we don't want to alter
-            the highest values for checkmate -- these are modified by tiny
-            values depending on their depth so as to prevent endless loops in
-            which most moves are winning, biasing such values would completely
-            kill that algorithm */
-
-          int16_t bias = randFunc();
-          bias = (bias - 128) / 2;
-          bias *= randomness - 1;
-          score += bias;
-        }
-
-        uint8_t comparison =
-          score == bestScore;
-
-        if ((comparison != 1) &&
-          (
-            (SCL_boardWhitesTurn(board) && score > bestScore) ||
-            (!SCL_boardWhitesTurn(board) && score < bestScore)
-          ))
-          comparison = 2;
-
-        uint8_t replace = 0;
-
-        if (randFunc == 0)
-          replace = comparison == 2;
-        else
-          replace = (comparison == 2) ||
-          ((comparison == 1) && (randFunc() < 160)); // not uniform distr. but simple
-
-        if (replace)
-        {
-          *resultFrom = i;
-          *resultTo = iteratedSquare;
-          bestScore = score;
-        }
-
-      SCL_SQUARE_SET_ITERATE_END
-    }
-
-#if SCL_DEBUG_AI
-  printf(")%d %s\n",bestScore,SCL_moveToString(board,*resultFrom,*resultTo,'q',moveStr));
-  puts("===== AI debug end ===== ");
-#endif 
-
-  return bestScore;
-}
-
-uint8_t SCL_boardToFEN(SCL_Board board, char *string)
-{
-  uint8_t square = 56;
-  uint8_t spaces = 0;
-  uint8_t result = 0;
-  #define put(c) { *string = (c); string++; result++; }
-
-  while (1) // pieces
-  {
-    char s = board[square];
-
-    if (s == '.')
-    {
-      spaces++;
-    }
-    else
-    {
-      if (spaces != 0)
-      {
-        put('0' + spaces)
-        spaces = 0; 
-      }
-
-      put(s)
-    }
-
-    square++;
-
-    if (square % 8 == 0)
-    {
-      if (spaces != 0)
-      {
-        put('0' + spaces)
-        spaces = 0; 
-      }
-
-      if (square == 8)
-        break;
-   
-      put('/'); 
-
-      square -= 16;
-    }
-  }
-
-  put(' ');
-  put(SCL_boardWhitesTurn(board) ? 'w' : 'b');
-  put(' ');
-
-  uint8_t b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0;
-
-  if (b != 0) // castling
-  {
-    if (b & 0x10) put('K');
-    if (b & 0x20) put('Q');
-    if (b & 0x40) put('k');
-    if (b & 0x80) put('q');
-  }
-  else
-    put('-');
-
-  put(' ');
-
-  b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
-
-  if (b < 8)
-  {
-    put('a' + b);
-    put(SCL_boardWhitesTurn(board) ? '6' : '3');
-  }
-  else
-    put('-');
-
-  for (uint8_t i = 0; i < 2; ++i)
-  {
-    put(' ');
-
-    uint8_t moves = i == 0 ?
-      ((uint8_t) board[SCL_BOARD_MOVE_COUNT_BYTE]) :
-      (((uint8_t) board[SCL_BOARD_PLY_BYTE]) / 2 + 1);
-
-    uint8_t hundreds = moves / 100;
-    uint8_t tens = (moves % 100) / 10;
-
-    if (hundreds != 0)
-    {
-      put('0' + hundreds);
-      put('0' + tens);
-    }
-    else if (tens != 0)
-      put('0' + tens);
-
-    put('0' + moves % 10);
-    
-  }
-
-  *string = 0; // terminate the string
-
-  return result + 1;
-
-  #undef put
-}
-
-uint8_t SCL_boardFromFEN(SCL_Board board, const char *string)
-{
-  uint8_t square = 56;
-
-  while (1)
-  {
-    char c = *string;
-
-    if (c == 0)
-      return 0;
-
-    if (c != '/' && c != ' ') // ignore line separators
-    {
-      if (c < '9') // empty square sequence
-      {
-        while (c > '0')
-        {
-          board[square] = '.';
-          square++;
-          c--;
-        }
-      }
-      else // piece
-      {
-        board[square] = c;
-        square++;
-      }
-    }
-    else
-    {
-      if (square == 8)
-        break;
-
-      square -= 16;
-    }
-
-    string++;
-  }
-
-#define nextChar string++; if (*string == 0) return 0;
-
-  nextChar // space
-
-  board[SCL_BOARD_PLY_BYTE] = *string == 'b';
-  nextChar
-
-  nextChar // space
-
-  uint8_t castleEnPassant = 0x0;
-
-  while (*string != ' ')
-  {
-    switch (*string)
-    {
-      case 'K': castleEnPassant |= 0x10; break;
-      case 'Q': castleEnPassant |= 0x20; break;
-      case 'k': castleEnPassant |= 0x40; break;
-      case 'q': castleEnPassant |= 0x80; break;
-      default: castleEnPassant |= 0xf0; break;  // for partial XFEN compat.
-    }
-
-    nextChar
-  }
-
-  nextChar // space
-
-  if (*string != '-')
-  {
-    castleEnPassant |= *string - 'a';
-    nextChar
-  }
-  else
-    castleEnPassant |= 0x0f;
-
-  nextChar
-
-  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castleEnPassant;
-
-  for (uint8_t i = 0; i < 2; ++i)
-  {
-    nextChar // space
-
-    uint8_t ply = 0;
-
-    while (1)
-    {
-      char c = *string;
-
-      if (c < '0' || c > '9')
-        break;
-
-      ply = ply * 10 + (c - '0'); 
-
-      string++;
-    }
-
-    if (i == 0 && *string == 0)
-      return 0;
-
-    if (i == 0)
-      board[SCL_BOARD_MOVE_COUNT_BYTE] = ply;
-    else
-      board[SCL_BOARD_PLY_BYTE] += (ply - 1) * 2;
-  }
-
-#if SCL_960_CASTLING
-  _SCL_board960RememberRookPositions(board);
-#endif
-
-  return 1;
-#undef nextChar
-}
-
-uint8_t SCL_boardEstimatePhase(SCL_Board board)
-{
-  uint16_t totalMaterial = 0;
-
-  uint8_t ply = board[SCL_BOARD_PLY_BYTE];
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
-  {
-    char s = *board;
-
-    if (s != '.')
-    {
-      int16_t v = SCL_pieceValue(s);
-
-      if (!SCL_pieceIsWhite(s))
-        v *= -1;
-
-      totalMaterial += v;
-    }
-
-    board++;
-  }
-
-  if (totalMaterial < SCL_ENDGAME_MATERIAL_LIMIT)
-    return SCL_PHASE_ENDGAME;
-
-  if (ply <= 10 && (totalMaterial >= SCL_START_MATERIAL - 3 * SCL_VALUE_PAWN))
-    return SCL_PHASE_OPENING;    
-
-  return SCL_PHASE_MIDGAME;
-}
-
-#define SCL_IMAGE_COUNT 12
-
-static const uint8_t SCL_images[8 * SCL_IMAGE_COUNT] =
-{
-  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-  0xff,0x81,0xff,0xff,0xff,0xff,0xff,0x81,0xff,0xff,0xff,0xff,
-  0xff,0x81,0xe7,0xf7,0xf7,0xaa,0xff,0xbd,0xe7,0xf7,0xf7,0xaa,
-  0xff,0xc3,0xc3,0xe3,0xc1,0x80,0xff,0x99,0xdb,0xeb,0xc9,0x94,
-  0xe7,0xc3,0x81,0xc1,0x94,0x80,0xe7,0xdb,0xbd,0xdd,0xbe,0xbe,
-  0xc3,0xc3,0x91,0xe3,0x80,0x80,0xdb,0x99,0x8d,0xeb,0xaa,0xbe,
-  0xc3,0x81,0xe1,0xc1,0xc1,0xc1,0xdb,0xbd,0xdd,0xe3,0xdd,0xdd,
-  0x81,0x81,0xc1,0x9c,0xc1,0xc1,0x81,0x81,0xc1,0x9c,0xc1,0xc1
-};
-
-void SCL_drawBoard(
-  SCL_Board board,
-  SCL_PutPixelFunction putPixel,
-  uint8_t selectedSquare,
-  SCL_SquareSet highlightSquares,
-  uint8_t blackDown)
-{
-  uint8_t row = 0;
-  uint8_t col = 0;
-  uint8_t x = 0;
-  uint8_t y = 0;
-  uint16_t n = 0;
-  uint8_t s = 0;
-
-  uint8_t pictureLine = 0;
-  uint8_t loadLine = 1;
-
-  while (row < 8)
-  {
-    if (loadLine)
-    {
-      s = blackDown ? (row * 8 + (7 - col)) : ((7 - row) * 8 + col);
-
-      char piece = board[s];
-
-      if (piece == '.')
-        pictureLine = (y == 4) ? 0xef : 0xff;
-      else
-      {
-        uint8_t offset = SCL_pieceIsWhite(piece) ? 6 : 0;
-        piece = SCL_pieceToColor(piece,1);
-
-        switch (piece)
-        {
-          case 'R': offset += 1; break;
-          case 'N': offset += 2; break;
-          case 'B': offset += 3; break;
-          case 'K': offset += 4; break;
-          case 'Q': offset += 5; break;
-          default: break;
-        }
-      
-        pictureLine = SCL_images[y * SCL_IMAGE_COUNT + offset];
-      }
-
-      if (SCL_squareSetContains(highlightSquares,s))
-        pictureLine &= (y % 2) ? 0xaa : 0x55;
-      
-      if (s == selectedSquare)
-        pictureLine &= (y == 0 || y == 7) ? 0x00 : ~0x81;
-
-      loadLine = 0;
-    }
-
-    putPixel(pictureLine & 0x80,n);
-    pictureLine <<= 1;
-
-    n++;
-    x++;
-
-    if (x == 8)
-    {
-      col++;
-      loadLine = 1;
-      x = 0;
-    }
-
-    if (col == 8)
-    {
-      y++;
-      col = 0;
-      x = 0;
-    }
-
-    if (y == 8)
-    {
-      row++;
-      y = 0;
-    }
-  }
-}
-
-uint32_t SCL_boardHash32(const SCL_Board board)
-{
-  uint32_t result = (board[SCL_BOARD_PLY_BYTE] & 0x01) +
-    (((uint32_t) ((uint8_t) board[SCL_BOARD_ENPASSANT_CASTLE_BYTE])) << 24) +
-    board[SCL_BOARD_MOVE_COUNT_BYTE];
-
-  const char *b = board;
-
-  for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b)
-  {
-    switch (*b)
-    {
-#define C(p,n) case p: result ^= (i + 1) * n; break;
-      // the below number are primes
-      C('P',4003)
-      C('R',84673)
-      C('N',93911)
-      C('B',999331)
-      C('Q',909091)
-      C('K',2796203)
-      C('p',4793)
-      C('r',19391)
-      C('n',391939)
-      C('b',108301)
-      C('q',174763)
-      C('k',2474431)
-#undef C
-      default: break;
-    }
-  }
-
-  // for extra spread of values we swap the low/high parts:
-  result = (result >> 16) | (result << 16);
-
-  return result;
-}
-
-void SCL_boardDisableCastling(SCL_Board board)
-{
-  board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= 0x0f;
-}
-
-uint8_t SCL_boardMoveResetsCount(SCL_Board board,
-  uint8_t squareFrom, uint8_t squareTo)
-{
-  return board[squareFrom] == 'P' || board[squareFrom] == 'p' ||
-    board[squareTo] != '.';
-}
-
-void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc,
-  SCL_Board initialState)
-{
-  if (SCL_recordLength(r) == 0)
-    return;
-
-  uint16_t pos = 0;
-
-  SCL_Board board;
-
-  if (initialState != 0)
-    for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
-      board[i] = initialState[i];
-  else
-    SCL_boardInit(board);
-
-  while (1)
-  {
-    uint8_t s0, s1;
-    char p;
-
-    uint8_t state = SCL_recordGetMove(r,pos,&s0,&s1,&p);
-
-    pos++;
-
-    if (pos % 2)
-    {
-      uint8_t move = pos / 2 + 1;
-
-      if (move / 100 != 0)
-        putCharFunc('0' + move / 100);
-
-      if (move / 10 != 0 || move / 100 != 0)
-        putCharFunc('0' + (move % 100) / 10);
-
-      putCharFunc('0' + move % 10);
-
-      putCharFunc('.');
-      putCharFunc(' ');
-    }
-
-#if !SCL_960_CASTLING
-    if ((board[s0] == 'K' && s0 == 4 && (s1 == 2 || s1 == 6)) ||
-      (board[s0] == 'k' && s0 == 60 && (s1 == 62 || s1 == 58)))
-#else
-    if ((board[s0] == 'K' && board[s1] == 'R') ||
-        (board[s0] == 'k' && board[s1] == 'r'))
-#endif
-    {
-      putCharFunc('O');
-      putCharFunc('-');
-      putCharFunc('O');
-
-#if !SCL_960_CASTLING
-      if (s1 == 58 || s1 == 2)
-#else
-      if ((s1 == (board[SCL_BOARD_EXTRA_BYTE] & 0x07)) ||
-          (s1 == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07)))
-#endif
-      {
-        putCharFunc('-');
-        putCharFunc('O');
-      }
-    }
-    else
-    {
-      uint8_t pawn = board[s0] == 'P' || board[s0] == 'p';
-
-      if (!pawn)
-      {
-        putCharFunc(SCL_pieceToColor(board[s0],1));
-
-        // disambiguation:
-
-        uint8_t specify = 0;
-
-        for (int i = 0; i < SCL_BOARD_SQUARES; ++i)
-          if (i != s0 && board[i] == board[s0])
-          {
-            SCL_SquareSet s;
-            
-            SCL_squareSetClear(s);
-
-            SCL_boardGetMoves(board,i,s);
-
-            if (SCL_squareSetContains(s,s1))
-              specify |= (s0 % 8 != s1 % 8) ? 1 : 2;
-          }
-
-        if (specify & 0x01)
-          putCharFunc('a' + s0 % 8);
-
-        if (specify & 0x02)
-          putCharFunc('1' + s0 / 8);
-      }
-
-      if (board[s1] != '.' ||
-       (pawn && s0 % 8 != s1 % 8 && board[s1] == '.')) // capture?
-      {
-        if (pawn)
-          putCharFunc('a' + s0 % 8);
-          
-        putCharFunc('x');
-      }
-      
-      putCharFunc('a' + s1 % 8);
-      putCharFunc('1' + s1 / 8);
-
-      if (pawn && (s1 >= 56 || s1 <= 7)) // promotion?
-      {
-        putCharFunc('=');
-        putCharFunc(SCL_pieceToColor(p,1));
-      }
-    }
-
-    SCL_boardMakeMove(board,s0,s1,p);
-
-    uint8_t position = SCL_boardGetPosition(board);
-    if (position == SCL_POSITION_CHECK)
-      putCharFunc('+');
-
-    if (position == SCL_POSITION_MATE)
-    {
-      putCharFunc('#');
-      break;
-    }
-    else if (state != SCL_RECORD_CONT)
-    {
-      putCharFunc('*');
-      break;
-    }
-      
-    putCharFunc(' ');
-  }
-}
-
-void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo)
-{
-  for (uint16_t i = 0; i < SCL_RECORD_MAX_SIZE; ++i)
-    recordTo[i] = recordFrom[i];
-}
-
-void SCL_gameInit(SCL_Game *game, const SCL_Board startState)
-{
-  game->startState = startState;
-
-  if (startState != 0)
-    SCL_boardCopy(startState,game->board);
-  else
-    SCL_boardInit(game->board);
-
-  SCL_recordInit(game->record);
-
-  for (uint8_t i = 0; i < 14; ++i)
-    game->prevMoves[i] = 0;
-  game->state = SCL_GAME_STATE_PLAYING;
-  game->ply = 0;
-
-  SCL_recordInit(game->record);
-}
-
-uint8_t SCL_gameGetRepetiotionMove(SCL_Game *game,
-  uint8_t *squareFrom, uint8_t *squareTo)
-{
-  if (squareFrom != 0 && squareTo != 0)
-  {
-      *squareFrom = 0;
-      *squareTo = 0;
-  }
-
-  /*  pos. 1st         2nd         3rd
-            |           |           |
-            v           v           v       
-             01 23 45 67 89 AB CD EF
-     move    ab cd ba dc ab cd ba dc */
-
-  if (game->ply >= 7 &&
-    game->prevMoves[0] == game->prevMoves[5] &&
-    game->prevMoves[0] == game->prevMoves[8] &&
-    game->prevMoves[0] == game->prevMoves[13] &&
-
-    game->prevMoves[1] == game->prevMoves[4] &&
-    game->prevMoves[1] == game->prevMoves[9] &&
-    game->prevMoves[1] == game->prevMoves[12] &&
-
-    game->prevMoves[2] == game->prevMoves[7] &&
-    game->prevMoves[2] == game->prevMoves[10] &&
-
-    game->prevMoves[3] == game->prevMoves[6] &&
-    game->prevMoves[3] == game->prevMoves[11]
-    )
-  {
-    if (squareFrom != 0 && squareTo != 0)
-    {
-      *squareFrom = game->prevMoves[3];
-      *squareTo = game->prevMoves[2];
-    }
-
-    return 1;
-  } 
-
-  return 0;
-}
-
-void SCL_gameMakeMove(SCL_Game *game, uint8_t squareFrom, uint8_t squareTo,
-  char promoteTo)
-{
-  uint8_t repetitionS0, repetitionS1;
-
-  SCL_gameGetRepetiotionMove(game,&repetitionS0,&repetitionS1);
-  SCL_boardMakeMove(game->board,squareFrom,squareTo,promoteTo);
-  SCL_recordAdd(game->record,squareFrom,squareTo,promoteTo,SCL_RECORD_CONT);
-  // ^ TODO: SCL_RECORD_CONT
-
-  game->ply++;
-
-  for (uint8_t i = 0; i < 14 - 2; ++i)
-    game->prevMoves[i] = game->prevMoves[i + 2];
-
-  game->prevMoves[12] = squareFrom;
-  game->prevMoves[13] = squareTo;
-
-  if (squareFrom == repetitionS0 && squareTo == repetitionS1)
-    game->state = SCL_GAME_STATE_DRAW_REPETITION;
-  else if (game->board[SCL_BOARD_MOVE_COUNT_BYTE] >= 50)
-    game->state = SCL_GAME_STATE_DRAW_50;
-  else
-  {
-    uint8_t position = SCL_boardGetPosition(game->board);
-
-    switch (position)
-    {
-      case SCL_POSITION_MATE:
-        game->state = SCL_boardWhitesTurn(game->board) ?
-          SCL_GAME_STATE_BLACK_WIN : SCL_GAME_STATE_WHITE_WIN;
-        break;
-
-      case SCL_POSITION_STALEMATE:
-        game->state = SCL_GAME_STATE_DRAW_STALEMATE;
-        break;
-
-      case SCL_POSITION_DEAD:
-        game->state = SCL_GAME_STATE_DRAW_DEAD;
-        break;
-
-      default: break;
-    }
-  }
-}
-
-uint8_t SCL_gameUndoMove(SCL_Game *game)
-{
-  if (game->ply == 0)
-    return 0;
-
-  if ((game->ply - 1) > SCL_recordLength(game->record))
-    return 0; // can't undo, lacking record
-
-  SCL_Record r;
-
-  SCL_recordCopy(game->record,r);
-
-  uint16_t applyMoves = game->ply - 1;
-
-  SCL_gameInit(game,game->startState);
-
-  for (uint16_t i = 0; i < applyMoves; ++i)
-  {
-    uint8_t s0, s1;
-    char p;
-
-    SCL_recordGetMove(r,i,&s0,&s1,&p);
-    SCL_gameMakeMove(game,s0,s1,p);
-  }
-    
-  return 1;
-}
-
-uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom,
-  uint8_t squareTo)
-{
-  if (squareFrom >= SCL_BOARD_SQUARES || squareTo >= SCL_BOARD_SQUARES)
-    return 0;
-
-  char piece = board[squareFrom];
-
-  if ((piece == '.') ||
-    (SCL_boardWhitesTurn(board) != SCL_pieceIsWhite(piece)))
-    return 0;
-
-  SCL_SquareSet moves;
-
-  SCL_boardGetMoves(board,squareFrom,moves);
-
-  return SCL_squareSetContains(moves,squareTo);
-}
-
-#endif // guard
diff --git a/movement/lib/sunriset/sunriset.c b/movement/lib/sunriset/sunriset.c
deleted file mode 100644 (file)
index 178f2ff..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
-
-SUNRISET.C - computes Sun rise/set times, start/end of twilight, and
-             the length of the day at any date and latitude
-
-Written as DAYLEN.C, 1989-08-16
-
-Modified to SUNRISET.C, 1992-12-01
-
-(c) Paul Schlyter, 1989, 1992
-
-Released to the public domain by Paul Schlyter, December 1992
-
-*/
-
-#include <stdio.h>
-#include <math.h>
-#include "sunriset.h"
-
-static void sunpos( double d, double *lon, double *r );
-
-/* A macro to compute the number of days elapsed since 2000 Jan 0.0 */
-/* (which is equal to 1999 Dec 31, 0h UT)                           */
-
-#define days_since_2000_Jan_0(y,m,d) \
-    (367L*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530L)
-
-/* Some conversion factors between radians and degrees */
-
-#ifndef PI
- #define PI        3.1415926535897932384
-#endif
-
-#define RADEG     ( 180.0 / PI )
-#define DEGRAD    ( PI / 180.0 )
-
-/* The trigonometric functions in degrees */
-
-#define sind(x)  sin((x)*DEGRAD)
-#define cosd(x)  cos((x)*DEGRAD)
-#define tand(x)  tan((x)*DEGRAD)
-
-#define atand(x)    (RADEG*atan(x))
-#define asind(x)    (RADEG*asin(x))
-#define acosd(x)    (RADEG*acos(x))
-#define atan2d(y,x) (RADEG*atan2(y,x))
-
-/* The "workhorse" function for sun rise/set times */
-
-int __sunriset__( int year, int month, int day, double lon, double lat,
-                  double altit, int upper_limb, double *trise, double *tset )
-/***************************************************************************/
-/* Note: year,month,date = calendar date, 1801-2099 only.             */
-/*       Eastern longitude positive, Western longitude negative       */
-/*       Northern latitude positive, Southern latitude negative       */
-/*       The longitude value IS critical in this function!            */
-/*       altit = the altitude which the Sun should cross              */
-/*               Set to -35/60 degrees for rise/set, -6 degrees       */
-/*               for civil, -12 degrees for nautical and -18          */
-/*               degrees for astronomical twilight.                   */
-/*         upper_limb: non-zero -> upper limb, zero -> center         */
-/*               Set to non-zero (e.g. 1) when computing rise/set     */
-/*               times, and to zero when computing start/end of       */
-/*               twilight.                                            */
-/*        *rise = where to store the rise time                        */
-/*        *set  = where to store the set  time                        */
-/*                Both times are relative to the specified altitude,  */
-/*                and thus this function can be used to compute       */
-/*                various twilight times, as well as rise/set times   */
-/* Return value:  0 = sun rises/sets this day, times stored at        */
-/*                    *trise and *tset.                               */
-/*               +1 = sun above the specified "horizon" 24 hours.     */
-/*                    *trise set to time when the sun is at south,    */
-/*                    minus 12 hours while *tset is set to the south  */
-/*                    time plus 12 hours. "Day" length = 24 hours     */
-/*               -1 = sun is below the specified "horizon" 24 hours   */
-/*                    "Day" length = 0 hours, *trise and *tset are    */
-/*                    both set to the time when the sun is at south.  */
-/*                                                                    */
-/**********************************************************************/
-{
-      double  d,  /* Days since 2000 Jan 0.0 (negative before) */
-      sr,         /* Solar distance, astronomical units */
-      sRA,        /* Sun's Right Ascension */
-      sdec,       /* Sun's declination */
-      sradius,    /* Sun's apparent radius */
-      t,          /* Diurnal arc */
-      tsouth,     /* Time when Sun is at south */
-      sidtime;    /* Local sidereal time */
-
-      int rc = 0; /* Return cde from function - usually 0 */
-
-      /* Compute d of 12h local mean solar time */
-      d = days_since_2000_Jan_0(year,month,day) + 0.5 - lon/360.0;
-
-      /* Compute the local sidereal time of this moment */
-      sidtime = revolution( GMST0(d) + 180.0 + lon );
-
-      /* Compute Sun's RA, Decl and distance at this moment */
-      sun_RA_dec( d, &sRA, &sdec, &sr );
-
-      /* Compute time when Sun is at south - in hours UT */
-      tsouth = 12.0 - rev180(sidtime - sRA)/15.0;
-
-      /* Compute the Sun's apparent radius in degrees */
-      sradius = 0.2666 / sr;
-
-      /* Do correction to upper limb, if necessary */
-      if ( upper_limb )
-            altit -= sradius;
-
-      /* Compute the diurnal arc that the Sun traverses to reach */
-      /* the specified altitude altit: */
-      {
-            double cost;
-            cost = ( sind(altit) - sind(lat) * sind(sdec) ) /
-                  ( cosd(lat) * cosd(sdec) );
-            if ( cost >= 1.0 )
-                  rc = -1, t = 0.0;       /* Sun always below altit */
-            else if ( cost <= -1.0 )
-                  rc = +1, t = 12.0;      /* Sun always above altit */
-            else
-                  t = acosd(cost)/15.0;   /* The diurnal arc, hours */
-      }
-
-      /* Store rise and set times - in hours UT */
-      *trise = tsouth - t;
-      *tset  = tsouth + t;
-
-      return rc;
-}  /* __sunriset__ */
-
-
-
-/* The "workhorse" function */
-
-
-double __daylen__( int year, int month, int day, double lon, double lat,
-                   double altit, int upper_limb )
-/**********************************************************************/
-/* Note: year,month,date = calendar date, 1801-2099 only.             */
-/*       Eastern longitude positive, Western longitude negative       */
-/*       Northern latitude positive, Southern latitude negative       */
-/*       The longitude value is not critical. Set it to the correct   */
-/*       longitude if you're picky, otherwise set to to, say, 0.0     */
-/*       The latitude however IS critical - be sure to get it correct */
-/*       altit = the altitude which the Sun should cross              */
-/*               Set to -35/60 degrees for rise/set, -6 degrees       */
-/*               for civil, -12 degrees for nautical and -18          */
-/*               degrees for astronomical twilight.                   */
-/*         upper_limb: non-zero -> upper limb, zero -> center         */
-/*               Set to non-zero (e.g. 1) when computing day length   */
-/*               and to zero when computing day+twilight length.      */
-/**********************************************************************/
-{
-      double  d,  /* Days since 2000 Jan 0.0 (negative before) */
-      obl_ecl,    /* Obliquity (inclination) of Earth's axis */
-      sr,         /* Solar distance, astronomical units */
-      slon,       /* True solar longitude */
-      sin_sdecl,  /* Sine of Sun's declination */
-      cos_sdecl,  /* Cosine of Sun's declination */
-      sradius,    /* Sun's apparent radius */
-      t;          /* Diurnal arc */
-
-      /* Compute d of 12h local mean solar time */
-      d = days_since_2000_Jan_0(year,month,day) + 0.5 - lon/360.0;
-
-      /* Compute obliquity of ecliptic (inclination of Earth's axis) */
-      obl_ecl = 23.4393 - 3.563E-7 * d;
-
-      /* Compute Sun's ecliptic longitude and distance */
-      sunpos( d, &slon, &sr );
-
-      /* Compute sine and cosine of Sun's declination */
-      sin_sdecl = sind(obl_ecl) * sind(slon);
-      cos_sdecl = sqrt( 1.0 - sin_sdecl * sin_sdecl );
-
-      /* Compute the Sun's apparent radius, degrees */
-      sradius = 0.2666 / sr;
-
-      /* Do correction to upper limb, if necessary */
-      if ( upper_limb )
-            altit -= sradius;
-
-      /* Compute the diurnal arc that the Sun traverses to reach */
-      /* the specified altitude altit: */
-      {
-            double cost;
-            cost = ( sind(altit) - sind(lat) * sin_sdecl ) /
-                  ( cosd(lat) * cos_sdecl );
-            if ( cost >= 1.0 )
-                  t = 0.0;                      /* Sun always below altit */
-            else if ( cost <= -1.0 )
-                  t = 24.0;                     /* Sun always above altit */
-            else  t = (2.0/15.0) * acosd(cost); /* The diurnal arc, hours */
-      }
-      return t;
-}  /* __daylen__ */
-
-
-/* This function computes the Sun's position at any instant */
-
-static void sunpos( double d, double *lon, double *r )
-/******************************************************/
-/* Computes the Sun's ecliptic longitude and distance */
-/* at an instant given in d, number of days since     */
-/* 2000 Jan 0.0.  The Sun's ecliptic latitude is not  */
-/* computed, since it's always very near 0.           */
-/******************************************************/
-{
-      double M,         /* Mean anomaly of the Sun */
-             w,         /* Mean longitude of perihelion */
-                        /* Note: Sun's mean longitude = M + w */
-             e,         /* Eccentricity of Earth's orbit */
-             E,         /* Eccentric anomaly */
-             x, y,      /* x, y coordinates in orbit */
-             v;         /* True anomaly */
-
-      /* Compute mean elements */
-      M = revolution( 356.0470 + 0.9856002585 * d );
-      w = 282.9404 + 4.70935E-5 * d;
-      e = 0.016709 - 1.151E-9 * d;
-
-      /* Compute true longitude and radius vector */
-      E = M + e * RADEG * sind(M) * ( 1.0 + e * cosd(M) );
-            x = cosd(E) - e;
-      y = sqrt( 1.0 - e*e ) * sind(E);
-      *r = sqrt( x*x + y*y );              /* Solar distance */
-      v = atan2d( y, x );                  /* True anomaly */
-      *lon = v + w;                        /* True solar longitude */
-      if ( *lon >= 360.0 )
-            *lon -= 360.0;                   /* Make it 0..360 degrees */
-}
-
-void sun_RA_dec( double d, double *RA, double *dec, double *r )
-/******************************************************/
-/* Computes the Sun's equatorial coordinates RA, Decl */
-/* and also its distance, at an instant given in d,   */
-/* the number of days since 2000 Jan 0.0.             */
-/******************************************************/
-{
-      double lon, obl_ecl, x, y, z;
-
-      /* Compute Sun's ecliptical coordinates */
-      sunpos( d, &lon, r );
-
-      /* Compute ecliptic rectangular coordinates (z=0) */
-      x = *r * cosd(lon);
-      y = *r * sind(lon);
-
-      /* Compute obliquity of ecliptic (inclination of Earth's axis) */
-      obl_ecl = 23.4393 - 3.563E-7 * d;
-
-      /* Convert to equatorial rectangular coordinates - x is unchanged */
-      z = y * sind(obl_ecl);
-      y = y * cosd(obl_ecl);
-
-      /* Convert to spherical coordinates */
-      *RA = atan2d( y, x );
-      *dec = atan2d( z, sqrt(x*x + y*y) );
-
-}  /* sun_RA_dec */
-
-
-/******************************************************************/
-/* This function reduces any angle to within the first revolution */
-/* by subtracting or adding even multiples of 360.0 until the     */
-/* result is >= 0.0 and < 360.0                                   */
-/******************************************************************/
-
-#define INV360    ( 1.0 / 360.0 )
-
-double revolution( double x )
-/*****************************************/
-/* Reduce angle to within 0..360 degrees */
-/*****************************************/
-{
-      return( x - 360.0 * floor( x * INV360 ) );
-}  /* revolution */
-
-double rev180( double x )
-/*********************************************/
-/* Reduce angle to within +180..+180 degrees */
-/*********************************************/
-{
-      return( x - 360.0 * floor( x * INV360 + 0.5 ) );
-}  /* revolution */
-
-
-/*******************************************************************/
-/* This function computes GMST0, the Greenwich Mean Sidereal Time  */
-/* at 0h UT (i.e. the sidereal time at the Greenwhich meridian at  */
-/* 0h UT).  GMST is then the sidereal time at Greenwich at any     */
-/* time of the day.  I've generalized GMST0 as well, and define it */
-/* as:  GMST0 = GMST - UT  --  this allows GMST0 to be computed at */
-/* other times than 0h UT as well.  While this sounds somewhat     */
-/* contradictory, it is very practical:  instead of computing      */
-/* GMST like:                                                      */
-/*                                                                 */
-/*  GMST = (GMST0) + UT * (366.2422/365.2422)                      */
-/*                                                                 */
-/* where (GMST0) is the GMST last time UT was 0 hours, one simply  */
-/* computes:                                                       */
-/*                                                                 */
-/*  GMST = GMST0 + UT                                              */
-/*                                                                 */
-/* where GMST0 is the GMST "at 0h UT" but at the current moment!   */
-/* Defined in this way, GMST0 will increase with about 4 min a     */
-/* day.  It also happens that GMST0 (in degrees, 1 hr = 15 degr)   */
-/* is equal to the Sun's mean longitude plus/minus 180 degrees!    */
-/* (if we neglect aberration, which amounts to 20 seconds of arc   */
-/* or 1.33 seconds of time)                                        */
-/*                                                                 */
-/*******************************************************************/
-
-double GMST0( double d )
-{
-      double sidtim0;
-      /* Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr  */
-      /* L = M + w, as defined in sunpos().  Since I'm too lazy to */
-      /* add these numbers, I'll let the C compiler do it for me.  */
-      /* Any decent C compiler will add the constants at compile   */
-      /* time, imposing no runtime or code overhead.               */
-      sidtim0 = revolution( ( 180.0 + 356.0470 + 282.9404 ) +
-                          ( 0.9856002585 + 4.70935E-5 ) * d );
-      return sidtim0;
-}  /* GMST0 */
\ No newline at end of file
diff --git a/movement/lib/sunriset/sunriset.h b/movement/lib/sunriset/sunriset.h
deleted file mode 100644 (file)
index 7b2b6c8..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
-
-SUNRISET.H - computes Sun rise/set times, start/end of twilight, and
-             the length of the day at any date and latitude
-
-Written as DAYLEN.C, 1989-08-16
-
-Modified to SUNRISET.C, 1992-12-01
-
-(c) Paul Schlyter, 1989, 1992
-
-Released to the public domain by Paul Schlyter, December 1992
-
-*/
-
-#ifndef SUNRISET_H_
-#define SUNRISET_H_
-
-/* Function prototypes */
-
-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 );
-
-void sun_RA_dec( double d, double *RA, double *dec, double *r );
-
-double revolution( double x );
-
-double rev180( double x );
-
-double GMST0( double d );
-
-
-/* Following are some macros around the "workhorse" function __daylen__ */
-/* They mainly fill in the desired values for the reference altitude    */
-/* below the horizon, and also selects whether this altitude should     */
-/* refer to the Sun's center or its upper limb.                         */
-
-
-/* This macro computes the length of the day, from sunrise to 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 */
-/* of the Earth's atmosphere).                                        */
-#define day_length(year,month,day,lon,lat)  \
-        __daylen__( year, month, day, lon, lat, -35.0/60.0, 1 )
-
-/* This macro computes the length of the day, including civil twilight. */
-/* Civil twilight starts/ends when the Sun's center is 6 degrees below  */
-/* the horizon.                                                         */
-#define day_civil_twilight_length(year,month,day,lon,lat)  \
-        __daylen__( year, month, day, lon, lat, -6.0, 0 )
-
-/* This macro computes the length of the day, incl. nautical twilight.  */
-/* Nautical twilight starts/ends when the Sun's center is 12 degrees    */
-/* below the horizon.                                                   */
-#define day_nautical_twilight_length(year,month,day,lon,lat)  \
-        __daylen__( year, month, day, lon, lat, -12.0, 0 )
-
-/* This macro computes the length of the day, incl. astronomical twilight. */
-/* Astronomical twilight starts/ends when the Sun's center is 18 degrees   */
-/* below the horizon.                                                      */
-#define day_astronomical_twilight_length(year,month,day,lon,lat)  \
-        __daylen__( year, month, day, lon, lat, -18.0, 0 )
-
-
-/* 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 */
-/* of the Earth's atmosphere).                                        */
-#define sun_rise_set(year,month,day,lon,lat,rise,set)  \
-        __sunriset__( year, month, day, lon, lat, -35.0/60.0, 1, rise, set )
-
-/* This macro computes the start and end times of civil twilight.       */
-/* Civil twilight starts/ends when the Sun's center is 6 degrees below  */
-/* the horizon.                                                         */
-#define civil_twilight(year,month,day,lon,lat,start,end)  \
-        __sunriset__( year, month, day, lon, lat, -6.0, 0, start, end )
-
-/* This macro computes the start and end times of nautical twilight.    */
-/* Nautical twilight starts/ends when the Sun's center is 12 degrees    */
-/* below the horizon.                                                   */
-#define nautical_twilight(year,month,day,lon,lat,start,end)  \
-        __sunriset__( year, month, day, lon, lat, -12.0, 0, start, end )
-
-/* This macro computes the start and end times of astronomical twilight.   */
-/* Astronomical twilight starts/ends when the Sun's center is 18 degrees   */
-/* below the horizon.                                                      */
-#define astronomical_twilight(year,month,day,lon,lat,start,end)  \
-        __sunriset__( year, month, day, lon, lat, -18.0, 0, start, end )
-
-#endif // SUNRISET_H_
diff --git a/movement/lib/vsop87/LICENSE.txt b/movement/lib/vsop87/LICENSE.txt
deleted file mode 100755 (executable)
index cc8be27..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-Author: Greg A Miller (gmiller@gregmiller.net)\r
-I release all of my work in this project to the public domain.\r
-\r
-Note, though, not everything here can be considered my work.  The VSOP87 theory is the work of\r
-Bureau des Longitudes.
\ No newline at end of file
diff --git a/movement/lib/vsop87/README.md b/movement/lib/vsop87/README.md
deleted file mode 100755 (executable)
index 6ec7cbb..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-# Example\r
-\r
-An example in JavaScript showing computations is available at http://www.celestialprogramming.com/vsop87-multilang/\r
-\r
-### Current Stauts\r
-Versions for many different languages and environments have been created.  These have passed all test cases provided by the\r
-VSOP87 authors, a validation program is also included for each language.  They are ready for use. See the "Languages" directory for the status\r
-of each language.\r
-\r
-Still to come: more languages, documentation, and examples.\r
-\r
-# About this Project\r
-The purpose of this project is to create versions of VSOP87 in many different languages.  The plan is to generate a initial version in C# truncated to different\r
-precisions, then convert the resulting files into other languages.  This project was inspired by the [Neoprogrammics Source Code Generator Tool](http://www.neoprogrammics.com/vsop87/source_code_generator_tool/).\r
-The goal of this project is to provide easier to use (readymade source files), include all data (e.g. the Moon and velocities), are truncated to different accuracy levels,\r
-, for more languages, and have a more permissive license (public domain).  It will provide tests against the original VSOP87 test data for each programming language to validate correctness, as well as examples to show common useage.\r
-\r
-# Language Status\r
-\r
-Below is a list of the planned languages and their current status.\r
-\r
-\r
-Language     |Inline|JSON|CSV|CSV Low Mem|Validation Tests|Alt Az Reduction Example\r
--------------|------|----|---|-----------|----------------|--------------\r
-Java         |Yes   |    |Yes|           |Pass            |\r
-C            |Yes   |    |   |Yes        |Pass            |\r
-C#           |Yes   |    |Yes|           |Pass            |\r
-Python       |Yes   |Yes |Yes|           |Pass            |\r
-C++          |Yes   |    |   |           |Pass            |\r
-VB.Net       |Yes   |    |   |           |Pass            |\r
-JavaScript   |Yes   |Yes |   |           |Pass            |Yes\r
-PHP          |Yes   |    |   |           |Pass            |\r
-Ruby         |Yes   |    |   |           |Pass            |\r
-Swift        |Yes   |    |   |           |Pass            |\r
-Matlab/Octave|Yes   |    |   |           |Pass            |\r
-Groovy       |      |    |Yes|           |Pass            |\r
-Go           |Yes   |    |   |           |Pass            |\r
-Pascal       |Yes   |    |   |           |0.000009au      |\r
-Perl         |Yes   |    |   |Yes        |Pass            |\r
-R            |Fail! |    |   |           |Fail!           |\r
-Cobol        |      |    |   |           |                |\r
-Fortran      |      |    |   |           |                |\r
-Rust         |Yes   |    |   |           |Pass            |\r
-Arduino      |Yes   |    |   |           |0.000009au      |\r
-\r
-# What is VSOP87? Why use it?\r
-VSOP87 is one of many solutions available for predicting the positions of the planets (plus the Sun and the Moon) in our solar system.  The actual name is\r
-Variations S�culaires des Orbites Plan�taires, and it was published in 1987 by the Bureau des Longitudes.  Even though there have been many other methods\r
-developed before and after VSOP87, it remains one of the most popular methods among amatuers.  It provides better accuracy than most amatuers require (.1 arcseconds)\r
-over a period of a few thousand years before and after the year 2000.\r
-\r
-# Implementation Types\r
-There are a few different types of implementations: Inline, JSON, CSV, and CSV Low Memory.  The inline versions are generally the easiest to use as they will have no\r
-external requirements, they are also the easiest to generate, so they're available for more languages.  The JSON versions require a JSON file (located in the \r
-languages/JSON folder) which is loaded into memory.  The advantages of the JSON versions are you can compute the velocities with the same data the positions\r
-are generated from, and you can load and dispose of the memory used by the data when you need it.  The CSV implementations are similar to the JSON implementations, \r
-but, obviously, read from a CSV file (located in the languages/CSV folder). And the Low Memory CSV implementations read the same CSV files, but the\r
-data is not retained in memory.  The JSON versions are located in the Languages/JSON directory, as well as the required JSON files, and the CSV implementations\r
-are in the languages/CSV folder.\r
-\r
-# Which Version Should I Use?\r
-For the overwhelming majority of users, the VSOP87A_full version will be all that you need.  This is the full version, but should still be fast enough and small enough\r
-for most use cases.  Using the full version eliminates any questions of whether it will be accurate enough.  If, after trying the full version, the computation isn't\r
-fast enough, from there you should experiment with truncated versions.  The VSOP87A versions are the only versions which include both the Earth and Moon.  VSOP87A doesn't include the moon directly, but does include the Earth and the Earth-Moon Barrycenter, and all provided code for the VSOP87A versions include a function to compute the Moon's position from the Earth and EMB.  Using the versions that provide the velocities is necessary if you want to account for relativistic effects do to the motion of the observer.\r
-\r
-There are several versions of the main theory.  The first is just called VSOP87, the remainder of them are appended with the letters A, B, C, D, E.  Each version\r
-provides the data in a slightly different form.\r
-\r
-Version|Mercury|Venus|Earth|EMB|Mars|Jupiter|Saturn|Uranus|Neptune|Sun|Coordinates\r
--------|-------|-----|-----|---|----|-------|------|------|-------|---|-----------\r
-VSOP87|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|Yes|No|Keperian Orbital Elements\r
-VSOP87A|Yes|Yes|Yes|Yes|Yes|Yes|Yes|Yes|Yes|No|Heliocentric J2000 Ecliptic Rectangular XYZ\r
-VSOP87B|Yes|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|No|Heliocentric J2000 Ecliptic Spherical LBR\r
-VSOP87C|Yes|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|No|Heliocentric Ecliptic of date Rectangular XYZ\r
-VSOP87D|Yes|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|No|Heliocentric Ecliptic of date Spherical LBR\r
-VSOP87E|Yes|Yes|Yes|No|Yes|Yes|Yes|Yes|Yes|Yes|Barycentric J2000 Ecliptic Rectangular XYZ\r
-\r
-# Truncated versions\r
-Since the full VSOP87 provides more accuracy than most amateurs require, the algorithm can be shortened by eliminating terms.  This speeds up the computations, and\r
-reduces the overall size of the code at the cost of accuracy.  For each programming language, this project supplies VSOP87 truncated at ten different levels.  The\r
-effects of accuracy are detailed in the graphs below.  Each level of truncation eliminates any terms with a coefficient 1/10 the previous truncation level.\r
-\r
-Trunaction Level|Total Terms|Skipped Terms|Percent Skipped\r
-----------------|-----------|-------------|---------------\r
-full    |269949|0     |0 %\r
-xx large |269949|20998 |7.7 %\r
-x large  |269949|67848 |25.1 %\r
-large   |269949|145031|53.7 %\r
-small   |269949|218559|80.9 %\r
-x small  |269949|250204|92.6 %\r
-milli   |269949|262369|97.1 %\r
-micro   |269949|266975|98.8 %\r
-nano    |269949|268686|99.5 %\r
-pico    |269949|269464|99.8 %\r
-\r
-# Accuracy\r
-\r
-Accuracy graphs are below.  They show the error in degrees of each body as viewed from Earth.  Each graph shows the error for one body for all truncated versions of\r
-VSOP87.  The error is vs. the full version of VSOP87, so the inherent error in VSOP87 also has to be added.  Some bodies appear twice, to zoom in on the lower portion\r
-of the graph, as the error of the pico version makes it difficult to see errors amongst the larger versions.  The Python script and data to reproduce the graphs is\r
-in the Accuracy folder, by regenerating them you can use the Matplotlib interface to explore the graphs further.\r
-\r
-Since the error is computed from the geocenter, the Earth does not appear in the graphs below, nor does the Sun.  Graphs are also not present for the Moon, but graphs\r
-are available for the Earth-Moon Barrycenter (EMB), the error for the Moon will be a linear function of the EMB error.\r
-\r
-The full VSOP87 accuracy is .1 arcseconds for Saturn, and better for all others.  For more details on accuracy of the full theory consult\r
-[Planetary theories in rectangular and spherical variables - VSOP 87 solutions](http://articles.adsabs.harvard.edu/full/1988A%26A...202..309B).\r
-\r
-### Mercury\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/mercury.png)\r
-\r
-### Venus\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/venus.png)\r
-\r
-### Earth-Moon Barrycenter\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/emb.png)\r
-\r
-### Earth-Moon Barrycenter (zoomed)\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/emb2.png)\r
-\r
-### Mars\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/mars.png)\r
-\r
-### Jupiter\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/jupiter.png)\r
-\r
-### Saturn\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/saturn.png)\r
-\r
-### Saturn (zoomed)\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/saturn2.png)\r
-\r
-### Uranus\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/uranus.png)\r
-\r
-### Uranus (zoomed)\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/uranus2.png)\r
-\r
-### Neptune\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/neptune.png)\r
-\r
-### Neptune (zoomed)\r
-![Mercury](https://raw.githubusercontent.com/gmiller123456/vsop87-multilang/master/utility/Accuracy/neptune2.png)\r
diff --git a/movement/lib/vsop87/vsop87a_micro.c b/movement/lib/vsop87/vsop87a_micro.c
deleted file mode 100755 (executable)
index 7e69fea..0000000
+++ /dev/null
@@ -1,1019 +0,0 @@
-//VSOP87-Multilang http://www.astrogreg.com/vsop87-multilang/index.html\r
-//Greg Miller (gmiller@gregmiller.net) 2019.  Released as Public Domain\r
-\r
-#include <math.h>\r
-#include "vsop87a_micro.h"\r
-double vsop87a_micro_earth_x(double t);\r
-double vsop87a_micro_earth_y(double t);\r
-double vsop87a_micro_earth_z(double t);\r
-double vsop87a_micro_emb_x(double t);\r
-double vsop87a_micro_emb_y(double t);\r
-double vsop87a_micro_emb_z(double t);\r
-double vsop87a_micro_jupiter_x(double t);\r
-double vsop87a_micro_jupiter_y(double t);\r
-double vsop87a_micro_jupiter_z(double t);\r
-double vsop87a_micro_mars_x(double t);\r
-double vsop87a_micro_mars_y(double t);\r
-double vsop87a_micro_mars_z(double t);\r
-double vsop87a_micro_mercury_x(double t);\r
-double vsop87a_micro_mercury_y(double t);\r
-double vsop87a_micro_mercury_z(double t);\r
-double vsop87a_micro_neptune_x(double t);\r
-double vsop87a_micro_neptune_y(double t);\r
-double vsop87a_micro_neptune_z(double t);\r
-double vsop87a_micro_saturn_x(double t);\r
-double vsop87a_micro_saturn_y(double t);\r
-double vsop87a_micro_saturn_z(double t);\r
-double vsop87a_micro_uranus_x(double t);\r
-double vsop87a_micro_uranus_y(double t);\r
-double vsop87a_micro_uranus_z(double t);\r
-double vsop87a_micro_venus_x(double t);\r
-double vsop87a_micro_venus_y(double t);\r
-double vsop87a_micro_venus_z(double t);\r
-\r
-\r
-void vsop87a_micro_getEarth(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_earth_x(t);\r
-   temp[1]=vsop87a_micro_earth_y(t);\r
-   temp[2]=vsop87a_micro_earth_z(t);\r
-}\r
-\r
-void vsop87a_micro_getEmb(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_emb_x(t);\r
-   temp[1]=vsop87a_micro_emb_y(t);\r
-   temp[2]=vsop87a_micro_emb_z(t);\r
-}\r
-\r
-void vsop87a_micro_getJupiter(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_jupiter_x(t);\r
-   temp[1]=vsop87a_micro_jupiter_y(t);\r
-   temp[2]=vsop87a_micro_jupiter_z(t);\r
-}\r
-\r
-void vsop87a_micro_getMars(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_mars_x(t);\r
-   temp[1]=vsop87a_micro_mars_y(t);\r
-   temp[2]=vsop87a_micro_mars_z(t);\r
-}\r
-\r
-void vsop87a_micro_getMercury(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_mercury_x(t);\r
-   temp[1]=vsop87a_micro_mercury_y(t);\r
-   temp[2]=vsop87a_micro_mercury_z(t);\r
-}\r
-\r
-void vsop87a_micro_getNeptune(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_neptune_x(t);\r
-   temp[1]=vsop87a_micro_neptune_y(t);\r
-   temp[2]=vsop87a_micro_neptune_z(t);\r
-}\r
-\r
-void vsop87a_micro_getSaturn(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_saturn_x(t);\r
-   temp[1]=vsop87a_micro_saturn_y(t);\r
-   temp[2]=vsop87a_micro_saturn_z(t);\r
-}\r
-\r
-void vsop87a_micro_getUranus(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_uranus_x(t);\r
-   temp[1]=vsop87a_micro_uranus_y(t);\r
-   temp[2]=vsop87a_micro_uranus_z(t);\r
-}\r
-\r
-void vsop87a_micro_getVenus(double t,double temp[]){\r
-   temp[0]=vsop87a_micro_venus_x(t);\r
-   temp[1]=vsop87a_micro_venus_y(t);\r
-   temp[2]=vsop87a_micro_venus_z(t);\r
-}\r
-\r
-void vsop87a_micro_getMoon(double earth[], double emb[],double temp[]){\r
-   temp[0]=(emb[0]-earth[0])*(1 + 1 / 0.01230073677);\r
-   temp[1]=(emb[1]-earth[1])*(1 + 1 / 0.01230073677);\r
-   temp[2]=(emb[2]-earth[2])*(1 + 1 / 0.01230073677);\r
-   temp[0]=temp[0]+earth[0];\r
-   temp[1]=temp[1]+earth[1];\r
-   temp[2]=temp[2]+earth[2];\r
-}\r
-\r
-double vsop87a_micro_earth_x(double t){\r
-   double earth_x_0=0.0;\r
-   earth_x_0+=     0.99982928844 * cos( 1.75348568475 +     6283.07584999140*t);\r
-   earth_x_0+=     0.00835257300 * cos( 1.71034539450 +    12566.15169998280*t);\r
-   earth_x_0+=     0.00561144206 * cos( 0.00000000000 +        0.00000000000*t);\r
-   earth_x_0+=     0.00010466628 * cos( 1.66722645223 +    18849.22754997420*t);\r
-\r
-   double earth_x_1=0.0;\r
-   earth_x_1+=     0.00123403056 * cos( 0.00000000000 +        0.00000000000*t);\r
-   earth_x_1+=     0.00051500156 * cos( 6.00266267204 +    12566.15169998280*t);\r
-   earth_x_1=earth_x_1 * t;\r
-\r
-   return earth_x_0+earth_x_1;\r
-}\r
-\r
-double vsop87a_micro_earth_y(double t){\r
-   double earth_y_0=0.0;\r
-   earth_y_0+=     0.99989211030 * cos( 0.18265890456 +     6283.07584999140*t);\r
-   earth_y_0+=     0.02442699036 * cos( 3.14159265359 +        0.00000000000*t);\r
-   earth_y_0+=     0.00835292314 * cos( 0.13952878991 +    12566.15169998280*t);\r
-   earth_y_0+=     0.00010466965 * cos( 0.09641690558 +    18849.22754997420*t);\r
-\r
-   double earth_y_1=0.0;\r
-   earth_y_1+=     0.00093046324 * cos( 0.00000000000 +        0.00000000000*t);\r
-   earth_y_1+=     0.00051506609 * cos( 4.43180499286 +    12566.15169998280*t);\r
-   earth_y_1=earth_y_1 * t;\r
-\r
-   return earth_y_0+earth_y_1;\r
-}\r
-\r
-double vsop87a_micro_earth_z(double t){\r
-   double earth_z_1=0.0;\r
-   earth_z_1+=     0.00227822442 * cos( 3.41372504278 +     6283.07584999140*t);\r
-   earth_z_1=earth_z_1 * t;\r
-\r
-   return earth_z_1;\r
-}\r
-\r
-double vsop87a_micro_emb_x(double t){\r
-   double emb_x_0=0.0;\r
-   emb_x_0+=     0.99982927460 * cos( 1.75348568475 +     6283.07584999140*t);\r
-   emb_x_0+=     0.00835257300 * cos( 1.71034539450 +    12566.15169998280*t);\r
-   emb_x_0+=     0.00561144161 * cos( 0.00000000000 +        0.00000000000*t);\r
-   emb_x_0+=     0.00010466628 * cos( 1.66722645223 +    18849.22754997420*t);\r
-\r
-   double emb_x_1=0.0;\r
-   emb_x_1+=     0.00123403046 * cos( 0.00000000000 +        0.00000000000*t);\r
-   emb_x_1+=     0.00051500156 * cos( 6.00266267204 +    12566.15169998280*t);\r
-   emb_x_1=emb_x_1 * t;\r
-\r
-   return emb_x_0+emb_x_1;\r
-}\r
-\r
-double vsop87a_micro_emb_y(double t){\r
-   double emb_y_0=0.0;\r
-   emb_y_0+=     0.99989209645 * cos( 0.18265890456 +     6283.07584999140*t);\r
-   emb_y_0+=     0.02442698841 * cos( 3.14159265359 +        0.00000000000*t);\r
-   emb_y_0+=     0.00835292314 * cos( 0.13952878991 +    12566.15169998280*t);\r
-   emb_y_0+=     0.00010466965 * cos( 0.09641690558 +    18849.22754997420*t);\r
-\r
-   double emb_y_1=0.0;\r
-   emb_y_1+=     0.00093046317 * cos( 0.00000000000 +        0.00000000000*t);\r
-   emb_y_1+=     0.00051506609 * cos( 4.43180499286 +    12566.15169998280*t);\r
-   emb_y_1=emb_y_1 * t;\r
-\r
-   return emb_y_0+emb_y_1;\r
-}\r
-\r
-double vsop87a_micro_emb_z(double t){\r
-   double emb_z_1=0.0;\r
-   emb_z_1+=     0.00227822442 * cos( 3.41372504278 +     6283.07584999140*t);\r
-   emb_z_1=emb_z_1 * t;\r
-\r
-   return emb_z_1;\r
-}\r
-\r
-double vsop87a_micro_jupiter_x(double t){\r
-   double jupiter_x_0=0.0;\r
-   jupiter_x_0+=     5.19663470114 * cos( 0.59945082355 +      529.69096509460*t);\r
-   jupiter_x_0+=     0.36662642320 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_x_0+=     0.12593937922 * cos( 0.94911583701 +     1059.38193018920*t);\r
-   jupiter_x_0+=     0.01500672056 * cos( 0.73175134610 +      522.57741809380*t);\r
-   jupiter_x_0+=     0.01476224578 * cos( 3.61736921122 +      536.80451209540*t);\r
-   jupiter_x_0+=     0.00457752736 * cos( 1.29883700755 +     1589.07289528380*t);\r
-   jupiter_x_0+=     0.00301689798 * cos( 5.17372551148 +        7.11354700080*t);\r
-   jupiter_x_0+=     0.00385975375 * cos( 2.01229910687 +      103.09277421860*t);\r
-   jupiter_x_0+=     0.00194025405 * cos( 5.02580363996 +      426.59819087600*t);\r
-   jupiter_x_0+=     0.00150678793 * cos( 6.12003027739 +      110.20632121940*t);\r
-   jupiter_x_0+=     0.00144867641 * cos( 5.55980577080 +      632.78373931320*t);\r
-   jupiter_x_0+=     0.00134226996 * cos( 0.87648567011 +      213.29909543800*t);\r
-   jupiter_x_0+=     0.00103494641 * cos( 6.19324769120 +     1052.26838318840*t);\r
-   jupiter_x_0+=     0.00114201562 * cos( 0.01567084269 +     1162.47470440780*t);\r
-   jupiter_x_0+=     0.00072095575 * cos( 3.96117430643 +     1066.49547719000*t);\r
-   jupiter_x_0+=     0.00059486083 * cos( 4.45769374358 +      949.17560896980*t);\r
-   jupiter_x_0+=     0.00068284021 * cos( 3.44051122631 +      846.08283475120*t);\r
-   jupiter_x_0+=     0.00047092251 * cos( 1.44612636451 +      419.48464387520*t);\r
-   jupiter_x_0+=     0.00030623417 * cos( 2.99132321427 +      206.18554843720*t);\r
-   jupiter_x_0+=     0.00026613459 * cos( 4.85169906494 +      323.50541665740*t);\r
-   jupiter_x_0+=     0.00019727457 * cos( 1.64891626213 +     2118.76386037840*t);\r
-   jupiter_x_0+=     0.00016481594 * cos( 1.95150056568 +      316.39186965660*t);\r
-   jupiter_x_0+=     0.00016101974 * cos( 0.87973155980 +      515.46387109300*t);\r
-   jupiter_x_0+=     0.00014209487 * cos( 2.07769621413 +      742.99006053260*t);\r
-   jupiter_x_0+=     0.00015192516 * cos( 6.25820127906 +      735.87651353180*t);\r
-   jupiter_x_0+=     0.00011423199 * cos( 3.48146108929 +      543.91805909620*t);\r
-   jupiter_x_0+=     0.00012155285 * cos( 3.75229924999 +      525.75881183150*t);\r
-   jupiter_x_0+=     0.00011996271 * cos( 0.58568573729 +      533.62311835770*t);\r
-\r
-   double jupiter_x_1=0.0;\r
-   jupiter_x_1+=     0.00882389251 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_x_1+=     0.00635297172 * cos( 0.10662156868 +     1059.38193018920*t);\r
-   jupiter_x_1+=     0.00599720482 * cos( 2.42996678275 +      522.57741809380*t);\r
-   jupiter_x_1+=     0.00589157060 * cos( 1.91556314637 +      536.80451209540*t);\r
-   jupiter_x_1+=     0.00081697204 * cos( 3.46668108797 +        7.11354700080*t);\r
-   jupiter_x_1+=     0.00046201898 * cos( 0.45714214032 +     1589.07289528380*t);\r
-   jupiter_x_1+=     0.00032508590 * cos( 1.74648849928 +     1052.26838318840*t);\r
-   jupiter_x_1+=     0.00033891193 * cos( 4.10113482752 +      529.69096509460*t);\r
-   jupiter_x_1+=     0.00031234303 * cos( 2.34698051502 +     1066.49547719000*t);\r
-   jupiter_x_1+=     0.00021244363 * cos( 4.36576178953 +      110.20632121940*t);\r
-   jupiter_x_1+=     0.00018156701 * cos( 4.00572238779 +      426.59819087600*t);\r
-   jupiter_x_1+=     0.00013577576 * cos( 0.30008010246 +      632.78373931320*t);\r
-   jupiter_x_1+=     0.00012889505 * cos( 2.57489294062 +      515.46387109300*t);\r
-   jupiter_x_1=jupiter_x_1 * t;\r
-\r
-   double jupiter_x_2=0.0;\r
-   jupiter_x_2+=     0.00123864644 * cos( 4.13563277513 +      522.57741809380*t);\r
-   jupiter_x_2+=     0.00121521296 * cos( 0.21155109275 +      536.80451209540*t);\r
-   jupiter_x_2+=     0.00085355503 * cos( 0.00000000000 +        0.00000000000*t);\r
-   jupiter_x_2+=     0.00077685547 * cos( 5.29776154458 +      529.69096509460*t);\r
-   jupiter_x_2+=     0.00041410887 * cos( 5.12291589939 +     1059.38193018920*t);\r
-   jupiter_x_2+=     0.00011423070 * cos( 1.72917878238 +        7.11354700080*t);\r
-   jupiter_x_2=jupiter_x_2 * t * t;\r
-\r
-   double jupiter_x_3=0.0;\r
-   jupiter_x_3+=     0.00017071323 * cos( 5.86133022278 +      522.57741809380*t);\r
-   jupiter_x_3+=     0.00016713548 * cos( 4.77458794485 +      536.80451209540*t);\r
-   jupiter_x_3=jupiter_x_3 * t * t * t;\r
-\r
-   return jupiter_x_0+jupiter_x_1+jupiter_x_2+jupiter_x_3;\r
-}\r
-\r
-double vsop87a_micro_jupiter_y(double t){\r
-   double jupiter_y_0=0.0;\r
-   jupiter_y_0+=     5.19520046589 * cos( 5.31203162731 +      529.69096509460*t);\r
-   jupiter_y_0+=     0.12592862602 * cos( 5.66160227728 +     1059.38193018920*t);\r
-   jupiter_y_0+=     0.09363670616 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_y_0+=     0.01508275299 * cos( 5.43934968102 +      522.57741809380*t);\r
-   jupiter_y_0+=     0.01475809370 * cos( 2.04679566495 +      536.80451209540*t);\r
-   jupiter_y_0+=     0.00457750806 * cos( 6.01129093501 +     1589.07289528380*t);\r
-   jupiter_y_0+=     0.00300686679 * cos( 3.60948050740 +        7.11354700080*t);\r
-   jupiter_y_0+=     0.00378285578 * cos( 3.53006782383 +      103.09277421860*t);\r
-   jupiter_y_0+=     0.00192333128 * cos( 3.45690564771 +      426.59819087600*t);\r
-   jupiter_y_0+=     0.00146104656 * cos( 4.62267224431 +      110.20632121940*t);\r
-   jupiter_y_0+=     0.00139480058 * cos( 4.00075307706 +      632.78373931320*t);\r
-   jupiter_y_0+=     0.00132696764 * cos( 5.62184581859 +      213.29909543800*t);\r
-   jupiter_y_0+=     0.00101999807 * cos( 4.57594598884 +     1052.26838318840*t);\r
-   jupiter_y_0+=     0.00114043110 * cos( 4.72982262969 +     1162.47470440780*t);\r
-   jupiter_y_0+=     0.00072091178 * cos( 2.39048659148 +     1066.49547719000*t);\r
-   jupiter_y_0+=     0.00059051769 * cos( 2.89529070968 +      949.17560896980*t);\r
-   jupiter_y_0+=     0.00068374489 * cos( 1.86537074374 +      846.08283475120*t);\r
-   jupiter_y_0+=     0.00029807369 * cos( 4.52105772740 +      206.18554843720*t);\r
-   jupiter_y_0+=     0.00026933579 * cos( 3.86233956827 +      419.48464387520*t);\r
-   jupiter_y_0+=     0.00026619714 * cos( 3.28203174951 +      323.50541665740*t);\r
-   jupiter_y_0+=     0.00020873780 * cos( 3.79369881757 +      735.87651353180*t);\r
-   jupiter_y_0+=     0.00019727397 * cos( 0.07818534532 +     2118.76386037840*t);\r
-   jupiter_y_0+=     0.00018639846 * cos( 0.38751972138 +      316.39186965660*t);\r
-   jupiter_y_0+=     0.00016355726 * cos( 5.56997881604 +      515.46387109300*t);\r
-   jupiter_y_0+=     0.00014606858 * cos( 0.47759399145 +      742.99006053260*t);\r
-   jupiter_y_0+=     0.00011419853 * cos( 1.91089341468 +      543.91805909620*t);\r
-   jupiter_y_0+=     0.00012153427 * cos( 2.18151972499 +      525.75881183150*t);\r
-   jupiter_y_0+=     0.00011988875 * cos( 5.29687602089 +      533.62311835770*t);\r
-\r
-   double jupiter_y_1=0.0;\r
-   jupiter_y_1+=     0.01694798253 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_y_1+=     0.00634859798 * cos( 4.81903199650 +     1059.38193018920*t);\r
-   jupiter_y_1+=     0.00601160431 * cos( 0.85811249940 +      522.57741809380*t);\r
-   jupiter_y_1+=     0.00588928504 * cos( 0.34491576890 +      536.80451209540*t);\r
-   jupiter_y_1+=     0.00081187145 * cos( 1.90914316532 +        7.11354700080*t);\r
-   jupiter_y_1+=     0.00046888090 * cos( 1.91294535618 +      529.69096509460*t);\r
-   jupiter_y_1+=     0.00046194129 * cos( 5.16955994561 +     1589.07289528380*t);\r
-   jupiter_y_1+=     0.00032503453 * cos( 0.17640743623 +     1052.26838318840*t);\r
-   jupiter_y_1+=     0.00031231694 * cos( 0.77623645597 +     1066.49547719000*t);\r
-   jupiter_y_1+=     0.00019462096 * cos( 3.00957119470 +      110.20632121940*t);\r
-   jupiter_y_1+=     0.00017738615 * cos( 2.46531787101 +      426.59819087600*t);\r
-   jupiter_y_1+=     0.00013701692 * cos( 5.02070197804 +      632.78373931320*t);\r
-   jupiter_y_1+=     0.00013034616 * cos( 0.98979834442 +      515.46387109300*t);\r
-   jupiter_y_1=jupiter_y_1 * t;\r
-\r
-   double jupiter_y_2=0.0;\r
-   jupiter_y_2+=     0.00124032509 * cos( 2.56495576833 +      522.57741809380*t);\r
-   jupiter_y_2+=     0.00121455991 * cos( 4.92398766380 +      536.80451209540*t);\r
-   jupiter_y_2+=     0.00076523263 * cos( 3.75913371793 +      529.69096509460*t);\r
-   jupiter_y_2+=     0.00076943042 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_y_2+=     0.00041357600 * cos( 3.55228440457 +     1059.38193018920*t);\r
-   jupiter_y_2+=     0.00011277667 * cos( 0.18559902389 +        7.11354700080*t);\r
-   jupiter_y_2=jupiter_y_2 * t * t;\r
-\r
-   double jupiter_y_3=0.0;\r
-   jupiter_y_3+=     0.00017085516 * cos( 4.29096904063 +      522.57741809380*t);\r
-   jupiter_y_3+=     0.00016701353 * cos( 3.20365737109 +      536.80451209540*t);\r
-   jupiter_y_3=jupiter_y_3 * t * t * t;\r
-\r
-   return jupiter_y_0+jupiter_y_1+jupiter_y_2+jupiter_y_3;\r
-}\r
-\r
-double vsop87a_micro_jupiter_z(double t){\r
-   double jupiter_z_0=0.0;\r
-   jupiter_z_0+=     0.11823100489 * cos( 3.55844646343 +      529.69096509460*t);\r
-   jupiter_z_0+=     0.00859031952 * cos( 0.00000000000 +        0.00000000000*t);\r
-   jupiter_z_0+=     0.00286562094 * cos( 3.90812238338 +     1059.38193018920*t);\r
-   jupiter_z_0+=     0.00042388592 * cos( 3.60144191032 +      522.57741809380*t);\r
-   jupiter_z_0+=     0.00033295491 * cos( 0.30297050585 +      536.80451209540*t);\r
-   jupiter_z_0+=     0.00010416160 * cos( 4.25764593061 +     1589.07289528380*t);\r
-\r
-   double jupiter_z_1=0.0;\r
-   jupiter_z_1+=     0.00407072175 * cos( 1.52699353482 +      529.69096509460*t);\r
-   jupiter_z_1+=     0.00020307341 * cos( 2.59878269248 +     1059.38193018920*t);\r
-   jupiter_z_1+=     0.00014424953 * cos( 4.85400155025 +      536.80451209540*t);\r
-   jupiter_z_1+=     0.00015474611 * cos( 0.00000000000 +        0.00000000000*t);\r
-   jupiter_z_1+=     0.00012730364 * cos( 5.45536715732 +      522.57741809380*t);\r
-   jupiter_z_1=jupiter_z_1 * t;\r
-\r
-   double jupiter_z_2=0.0;\r
-   jupiter_z_2+=     0.00028635326 * cos( 3.01374166973 +      529.69096509460*t);\r
-   jupiter_z_2=jupiter_z_2 * t * t;\r
-\r
-   return jupiter_z_0+jupiter_z_1+jupiter_z_2;\r
-}\r
-\r
-double vsop87a_micro_mars_x(double t){\r
-   double mars_x_0=0.0;\r
-   mars_x_0+=     1.51769936383 * cos( 6.20403346548 +     3340.61242669980*t);\r
-   mars_x_0+=     0.19502945246 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_x_0+=     0.07070919655 * cos( 0.25870338558 +     6681.22485339960*t);\r
-   mars_x_0+=     0.00494196914 * cos( 0.59669127768 +    10021.83728009940*t);\r
-   mars_x_0+=     0.00040938237 * cos( 0.93473307419 +    13362.44970679920*t);\r
-   mars_x_0+=     0.00021067199 * cos( 1.80435656154 +     3337.08930835080*t);\r
-   mars_x_0+=     0.00021041626 * cos( 1.17895619474 +     3344.13554504880*t);\r
-   mars_x_0+=     0.00011370375 * cos( 4.83265211109 +     1059.38193018920*t);\r
-   mars_x_0+=     0.00013527976 * cos( 0.63010765169 +      529.69096509460*t);\r
-\r
-   double mars_x_1=0.0;\r
-   mars_x_1+=     0.00861441374 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_x_1+=     0.00552437949 * cos( 5.09565872891 +     6681.22485339960*t);\r
-   mars_x_1+=     0.00077184977 * cos( 5.43315636209 +    10021.83728009940*t);\r
-   mars_x_1+=     0.00020467294 * cos( 5.57051812369 +     3340.61242669980*t);\r
-   mars_x_1=mars_x_1 * t;\r
-\r
-   double mars_x_2=0.0;\r
-   mars_x_2+=     0.00056323939 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mars_x_2+=     0.00022122528 * cos( 3.54372113272 +     6681.22485339960*t);\r
-   mars_x_2=mars_x_2 * t * t;\r
-\r
-   return mars_x_0+mars_x_1+mars_x_2;\r
-}\r
-\r
-double vsop87a_micro_mars_y(double t){\r
-   double mars_y_0=0.0;\r
-   mars_y_0+=     1.51558976277 * cos( 4.63212206588 +     3340.61242669980*t);\r
-   mars_y_0+=     0.07064550239 * cos( 4.97051892902 +     6681.22485339960*t);\r
-   mars_y_0+=     0.08655481102 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mars_y_0+=     0.00493872848 * cos( 5.30877806694 +    10021.83728009940*t);\r
-   mars_y_0+=     0.00040917422 * cos( 5.64698263703 +    13362.44970679920*t);\r
-   mars_y_0+=     0.00021036784 * cos( 0.23240270955 +     3337.08930835080*t);\r
-   mars_y_0+=     0.00021012921 * cos( 5.89022773653 +     3344.13554504880*t);\r
-   mars_y_0+=     0.00011370034 * cos( 3.26131408801 +     1059.38193018920*t);\r
-   mars_y_0+=     0.00013324177 * cos( 5.34259389724 +      529.69096509460*t);\r
-\r
-   double mars_y_1=0.0;\r
-   mars_y_1+=     0.01427324210 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_y_1+=     0.00551063753 * cos( 3.52128320402 +     6681.22485339960*t);\r
-   mars_y_1+=     0.00077091913 * cos( 3.86082685753 +    10021.83728009940*t);\r
-   mars_y_1+=     0.00037310491 * cos( 1.16016958445 +     3340.61242669980*t);\r
-   mars_y_1=mars_y_1 * t;\r
-\r
-   double mars_y_2=0.0;\r
-   mars_y_2+=     0.00035396765 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_y_2+=     0.00021950759 * cos( 1.96291594946 +     6681.22485339960*t);\r
-   mars_y_2=mars_y_2 * t * t;\r
-\r
-   return mars_y_0+mars_y_1+mars_y_2;\r
-}\r
-\r
-double vsop87a_micro_mars_z(double t){\r
-   double mars_z_0=0.0;\r
-   mars_z_0+=     0.04901207220 * cos( 3.76712324286 +     3340.61242669980*t);\r
-   mars_z_0+=     0.00660669541 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mars_z_0+=     0.00228333904 * cos( 4.10544022266 +     6681.22485339960*t);\r
-   mars_z_0+=     0.00015958402 * cos( 4.44367058261 +    10021.83728009940*t);\r
-\r
-   double mars_z_1=0.0;\r
-   mars_z_1+=     0.00331842958 * cos( 6.05027773492 +     3340.61242669980*t);\r
-   mars_z_1+=     0.00047930411 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_z_1=mars_z_1 * t;\r
-\r
-   double mars_z_2=0.0;\r
-   mars_z_2+=     0.00013705360 * cos( 1.04212852598 +     3340.61242669980*t);\r
-   mars_z_2=mars_z_2 * t * t;\r
-\r
-   return mars_z_0+mars_z_1+mars_z_2;\r
-}\r
-\r
-double vsop87a_micro_mercury_x(double t){\r
-   double mercury_x_0=0.0;\r
-   mercury_x_0+=     0.37546291728 * cos( 4.39651506942 +    26087.90314157420*t);\r
-   mercury_x_0+=     0.03825746672 * cos( 1.16485604339 +    52175.80628314840*t);\r
-   mercury_x_0+=     0.02625615963 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_x_0+=     0.00584261333 * cos( 4.21599394757 +    78263.70942472259*t);\r
-   mercury_x_0+=     0.00105716695 * cos( 0.98379033182 +   104351.61256629678*t);\r
-   mercury_x_0+=     0.00021011730 * cos( 4.03469353923 +   130439.51570787099*t);\r
-\r
-   double mercury_x_1=0.0;\r
-   mercury_x_1+=     0.00318848034 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mercury_x_1+=     0.00105289019 * cos( 5.91600475006 +    52175.80628314840*t);\r
-   mercury_x_1+=     0.00032316001 * cos( 2.68247273347 +    78263.70942472259*t);\r
-   mercury_x_1+=     0.00011992889 * cos( 5.81575112963 +    26087.90314157420*t);\r
-   mercury_x_1=mercury_x_1 * t;\r
-\r
-   return mercury_x_0+mercury_x_1;\r
-}\r
-\r
-double vsop87a_micro_mercury_y(double t){\r
-   double mercury_y_0=0.0;\r
-   mercury_y_0+=     0.37953642888 * cos( 2.83780617820 +    26087.90314157420*t);\r
-   mercury_y_0+=     0.11626131831 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_y_0+=     0.03854668215 * cos( 5.88780608966 +    52175.80628314840*t);\r
-   mercury_y_0+=     0.00587711268 * cos( 2.65498896201 +    78263.70942472259*t);\r
-   mercury_y_0+=     0.00106235493 * cos( 5.70550616735 +   104351.61256629678*t);\r
-   mercury_y_0+=     0.00021100828 * cos( 2.47291315849 +   130439.51570787099*t);\r
-\r
-   double mercury_y_1=0.0;\r
-   mercury_y_1+=     0.00107803852 * cos( 4.34964793883 +    52175.80628314840*t);\r
-   mercury_y_1+=     0.00080651544 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_y_1+=     0.00032715354 * cos( 1.11763734425 +    78263.70942472259*t);\r
-   mercury_y_1+=     0.00011914709 * cos( 1.22139986340 +    26087.90314157420*t);\r
-   mercury_y_1=mercury_y_1 * t;\r
-\r
-   return mercury_y_0+mercury_y_1;\r
-}\r
-\r
-double vsop87a_micro_mercury_z(double t){\r
-   double mercury_z_0=0.0;\r
-   mercury_z_0+=     0.04607665326 * cos( 1.99295081967 +    26087.90314157420*t);\r
-   mercury_z_0+=     0.00708734365 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_z_0+=     0.00469171617 * cos( 5.04215742764 +    52175.80628314840*t);\r
-   mercury_z_0+=     0.00071626395 * cos( 1.80894256071 +    78263.70942472259*t);\r
-   mercury_z_0+=     0.00012957446 * cos( 4.85922032010 +   104351.61256629678*t);\r
-\r
-   double mercury_z_1=0.0;\r
-   mercury_z_1+=     0.00108722177 * cos( 3.91134750825 +    26087.90314157420*t);\r
-   mercury_z_1+=     0.00057826621 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_z_1=mercury_z_1 * t;\r
-\r
-   return mercury_z_0+mercury_z_1;\r
-}\r
-\r
-double vsop87a_micro_neptune_x(double t){\r
-   double neptune_x_0=0.0;\r
-   neptune_x_0+=    30.05890004476 * cos( 5.31211340029 +       38.13303563780*t);\r
-   neptune_x_0+=     0.27080164222 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_x_0+=     0.13505661755 * cos( 3.50078975634 +       76.26607127560*t);\r
-   neptune_x_0+=     0.15726094556 * cos( 0.11319072675 +       36.64856292950*t);\r
-   neptune_x_0+=     0.14935120126 * cos( 1.08499403018 +       39.61750834610*t);\r
-   neptune_x_0+=     0.02597313814 * cos( 1.99590301412 +        1.48447270830*t);\r
-   neptune_x_0+=     0.01074040708 * cos( 5.38502938672 +       74.78159856730*t);\r
-   neptune_x_0+=     0.00823793287 * cos( 1.43221581862 +       35.16409022120*t);\r
-   neptune_x_0+=     0.00817588813 * cos( 0.78180174031 +        2.96894541660*t);\r
-   neptune_x_0+=     0.00565534918 * cos( 5.98964907613 +       41.10198105440*t);\r
-   neptune_x_0+=     0.00495719107 * cos( 0.59948143567 +      529.69096509460*t);\r
-   neptune_x_0+=     0.00307525907 * cos( 0.40023311011 +       73.29712585900*t);\r
-   neptune_x_0+=     0.00272253551 * cos( 0.87443494387 +      213.29909543800*t);\r
-   neptune_x_0+=     0.00135887219 * cos( 5.54676577816 +       77.75054398390*t);\r
-   neptune_x_0+=     0.00090965704 * cos( 1.68910246115 +      114.39910691340*t);\r
-   neptune_x_0+=     0.00069040539 * cos( 5.83469123520 +        4.45341812490*t);\r
-   neptune_x_0+=     0.00060813556 * cos( 2.62589958380 +       33.67961751290*t);\r
-   neptune_x_0+=     0.00054690827 * cos( 1.55799996661 +       71.81265315070*t);\r
-   neptune_x_0+=     0.00028889260 * cos( 4.78966826027 +       42.58645376270*t);\r
-   neptune_x_0+=     0.00012614732 * cos( 3.57002516434 +      112.91463420510*t);\r
-   neptune_x_0+=     0.00012749153 * cos( 2.73719269645 +      111.43016149680*t);\r
-   neptune_x_0+=     0.00012013994 * cos( 0.94912933496 +     1059.38193018920*t);\r
-\r
-   double neptune_x_1=0.0;\r
-   neptune_x_1+=     0.00255840261 * cos( 2.01935686795 +       36.64856292950*t);\r
-   neptune_x_1+=     0.00243125299 * cos( 5.46214902873 +       39.61750834610*t);\r
-   neptune_x_1+=     0.00118398168 * cos( 2.88251845061 +       76.26607127560*t);\r
-   neptune_x_1+=     0.00037965449 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_x_1+=     0.00021924705 * cos( 3.20156164152 +       35.16409022120*t);\r
-   neptune_x_1+=     0.00017459808 * cos( 4.26349398817 +       41.10198105440*t);\r
-   neptune_x_1+=     0.00013130617 * cos( 5.36424961848 +        2.96894541660*t);\r
-   neptune_x_1=neptune_x_1 * t;\r
-\r
-   return neptune_x_0+neptune_x_1;\r
-}\r
-\r
-double vsop87a_micro_neptune_y(double t){\r
-   double neptune_y_0=0.0;\r
-   neptune_y_0+=    30.06056351665 * cos( 3.74086294714 +       38.13303563780*t);\r
-   neptune_y_0+=     0.30205857683 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_y_0+=     0.13506391797 * cos( 1.92953034883 +       76.26607127560*t);\r
-   neptune_y_0+=     0.15706589373 * cos( 4.82539970129 +       36.64856292950*t);\r
-   neptune_y_0+=     0.14936165806 * cos( 5.79694900665 +       39.61750834610*t);\r
-   neptune_y_0+=     0.02584250749 * cos( 0.42549700754 +        1.48447270830*t);\r
-   neptune_y_0+=     0.01073739772 * cos( 3.81371728533 +       74.78159856730*t);\r
-   neptune_y_0+=     0.00815187583 * cos( 5.49429775826 +        2.96894541660*t);\r
-   neptune_y_0+=     0.00582199295 * cos( 6.19633718936 +       35.16409022120*t);\r
-   neptune_y_0+=     0.00565576412 * cos( 4.41843009015 +       41.10198105440*t);\r
-   neptune_y_0+=     0.00495581047 * cos( 5.31205825784 +      529.69096509460*t);\r
-   neptune_y_0+=     0.00304525203 * cos( 5.11048113661 +       73.29712585900*t);\r
-   neptune_y_0+=     0.00272640298 * cos( 5.58603690785 +      213.29909543800*t);\r
-   neptune_y_0+=     0.00135897385 * cos( 3.97553750964 +       77.75054398390*t);\r
-   neptune_y_0+=     0.00090970871 * cos( 0.11783619888 +      114.39910691340*t);\r
-   neptune_y_0+=     0.00068790261 * cos( 4.26391997151 +        4.45341812490*t);\r
-   neptune_y_0+=     0.00028893355 * cos( 3.21848975032 +       42.58645376270*t);\r
-   neptune_y_0+=     0.00020081559 * cos( 1.19787916085 +       33.67961751290*t);\r
-   neptune_y_0+=     0.00012613583 * cos( 1.99777332934 +      112.91463420510*t);\r
-   neptune_y_0+=     0.00012828708 * cos( 1.16740053443 +      111.43016149680*t);\r
-   neptune_y_0+=     0.00012012961 * cos( 5.66157563804 +     1059.38193018920*t);\r
-\r
-   double neptune_y_1=0.0;\r
-   neptune_y_1+=     0.00352947493 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_y_1+=     0.00256125493 * cos( 0.44757496817 +       36.64856292950*t);\r
-   neptune_y_1+=     0.00243147725 * cos( 3.89099798696 +       39.61750834610*t);\r
-   neptune_y_1+=     0.00118427205 * cos( 1.31128027037 +       76.26607127560*t);\r
-   neptune_y_1+=     0.00021936702 * cos( 1.63124087591 +       35.16409022120*t);\r
-   neptune_y_1+=     0.00017462332 * cos( 2.69229902966 +       41.10198105440*t);\r
-   neptune_y_1+=     0.00012992380 * cos( 3.79578633002 +        2.96894541660*t);\r
-   neptune_y_1=neptune_y_1 * t;\r
-\r
-   return neptune_y_0+neptune_y_1;\r
-}\r
-\r
-double vsop87a_micro_neptune_z(double t){\r
-   double neptune_z_0=0.0;\r
-   neptune_z_0+=     0.92866054405 * cos( 1.44103930278 +       38.13303563780*t);\r
-   neptune_z_0+=     0.01245978462 * cos( 0.00000000000 +        0.00000000000*t);\r
-   neptune_z_0+=     0.00474333567 * cos( 2.52218774238 +       36.64856292950*t);\r
-   neptune_z_0+=     0.00451987936 * cos( 3.50949720541 +       39.61750834610*t);\r
-   neptune_z_0+=     0.00417558068 * cos( 5.91310695421 +       76.26607127560*t);\r
-   neptune_z_0+=     0.00084104329 * cos( 4.38928900096 +        1.48447270830*t);\r
-   neptune_z_0+=     0.00032704958 * cos( 1.52048692001 +       74.78159856730*t);\r
-   neptune_z_0+=     0.00030873335 * cos( 3.29017611456 +       35.16409022120*t);\r
-   neptune_z_0+=     0.00025812584 * cos( 3.19303128782 +        2.96894541660*t);\r
-   neptune_z_0+=     0.00016865319 * cos( 2.13251104425 +       41.10198105440*t);\r
-   neptune_z_0+=     0.00011789909 * cos( 3.60001877675 +      213.29909543800*t);\r
-   neptune_z_0+=     0.00011279680 * cos( 3.55816676334 +      529.69096509460*t);\r
-\r
-   double neptune_z_1=0.0;\r
-   neptune_z_1+=     0.00154885971 * cos( 2.14239039664 +       38.13303563780*t);\r
-   neptune_z_1=neptune_z_1 * t;\r
-\r
-   return neptune_z_0+neptune_z_1;\r
-}\r
-\r
-double vsop87a_micro_saturn_x(double t){\r
-   double saturn_x_0=0.0;\r
-   saturn_x_0+=     9.51638335797 * cos( 0.87441380794 +      213.29909543800*t);\r
-   saturn_x_0+=     0.26412374238 * cos( 0.12390892620 +      426.59819087600*t);\r
-   saturn_x_0+=     0.06760430339 * cos( 4.16767145778 +      206.18554843720*t);\r
-   saturn_x_0+=     0.06624260115 * cos( 0.75094737780 +      220.41264243880*t);\r
-   saturn_x_0+=     0.04244797817 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_x_0+=     0.02336340488 * cos( 2.02227784673 +        7.11354700080*t);\r
-   saturn_x_0+=     0.01255372247 * cos( 2.17338917731 +      110.20632121940*t);\r
-   saturn_x_0+=     0.01115684467 * cos( 3.15686878377 +      419.48464387520*t);\r
-   saturn_x_0+=     0.01097683232 * cos( 5.65753337256 +      639.89728631400*t);\r
-   saturn_x_0+=     0.00716328481 * cos( 2.71149993708 +      316.39186965660*t);\r
-   saturn_x_0+=     0.00509313365 * cos( 4.95865624780 +      103.09277421860*t);\r
-   saturn_x_0+=     0.00433994439 * cos( 0.72012820974 +      529.69096509460*t);\r
-   saturn_x_0+=     0.00372894461 * cos( 0.00137195497 +      433.71173787680*t);\r
-   saturn_x_0+=     0.00097843523 * cos( 1.01485750417 +      323.50541665740*t);\r
-   saturn_x_0+=     0.00080600536 * cos( 5.62103979796 +       11.04570026390*t);\r
-   saturn_x_0+=     0.00083782316 * cos( 0.62038893702 +      227.52618943960*t);\r
-   saturn_x_0+=     0.00074150224 * cos( 2.38206066655 +      632.78373931320*t);\r
-   saturn_x_0+=     0.00070219382 * cos( 0.88789752415 +      209.36694217490*t);\r
-   saturn_x_0+=     0.00068855792 * cos( 4.01788097627 +      217.23124870110*t);\r
-   saturn_x_0+=     0.00065620467 * cos( 2.69728593339 +      202.25339517410*t);\r
-   saturn_x_0+=     0.00058297911 * cos( 2.16155251399 +      224.34479570190*t);\r
-   saturn_x_0+=     0.00054022837 * cos( 4.90928184374 +      853.19638175200*t);\r
-   saturn_x_0+=     0.00045550446 * cos( 1.88235037830 +       14.22709400160*t);\r
-   saturn_x_0+=     0.00038345667 * cos( 4.39815501478 +      199.07200143640*t);\r
-   saturn_x_0+=     0.00044551703 * cos( 5.60763553535 +       63.73589830340*t);\r
-   saturn_x_0+=     0.00025165185 * cos( 0.37800582257 +      216.48048917570*t);\r
-   saturn_x_0+=     0.00024554499 * cos( 4.53150598095 +      210.11770170030*t);\r
-   saturn_x_0+=     0.00024673219 * cos( 5.90891573850 +      522.57741809380*t);\r
-   saturn_x_0+=     0.00024677050 * cos( 5.60389382420 +      415.55249061210*t);\r
-   saturn_x_0+=     0.00025491374 * cos( 1.63922423181 +      117.31986822020*t);\r
-   saturn_x_0+=     0.00031253049 * cos( 4.62976601833 +      735.87651353180*t);\r
-   saturn_x_0+=     0.00023372467 * cos( 5.53491987276 +      647.01083331480*t);\r
-   saturn_x_0+=     0.00023355468 * cos( 0.18791490124 +      149.56319713460*t);\r
-   saturn_x_0+=     0.00024805815 * cos( 5.50327676733 +       74.78159856730*t);\r
-   saturn_x_0+=     0.00014731703 * cos( 4.67981909838 +      277.03499374140*t);\r
-   saturn_x_0+=     0.00012427525 * cos( 1.02995545746 +     1059.38193018920*t);\r
-   saturn_x_0+=     0.00012393514 * cos( 4.19747622821 +      490.33408917940*t);\r
-   saturn_x_0+=     0.00012026472 * cos( 5.66372282839 +      351.81659230870*t);\r
-\r
-   double saturn_x_1=0.0;\r
-   saturn_x_1+=     0.07575103962 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_x_1+=     0.03085041716 * cos( 4.27565749128 +      426.59819087600*t);\r
-   saturn_x_1+=     0.02714918399 * cos( 5.85229412397 +      206.18554843720*t);\r
-   saturn_x_1+=     0.02643100909 * cos( 5.33291950584 +      220.41264243880*t);\r
-   saturn_x_1+=     0.00627104520 * cos( 0.32898307969 +        7.11354700080*t);\r
-   saturn_x_1+=     0.00256560953 * cos( 3.52478934343 +      639.89728631400*t);\r
-   saturn_x_1+=     0.00312356512 * cos( 4.83001724941 +      419.48464387520*t);\r
-   saturn_x_1+=     0.00189196274 * cos( 4.48642453552 +      433.71173787680*t);\r
-   saturn_x_1+=     0.00203646570 * cos( 1.10998681782 +      213.29909543800*t);\r
-   saturn_x_1+=     0.00119531145 * cos( 1.14735096078 +      110.20632121940*t);\r
-   saturn_x_1+=     0.00066764238 * cos( 3.72346596928 +      316.39186965660*t);\r
-   saturn_x_1+=     0.00066901225 * cos( 5.20257500380 +      227.52618943960*t);\r
-   saturn_x_1+=     0.00031000840 * cos( 6.06067919437 +      199.07200143640*t);\r
-   saturn_x_1+=     0.00030418100 * cos( 0.18746903351 +       14.22709400160*t);\r
-   saturn_x_1+=     0.00022275210 * cos( 6.19530878014 +      103.09277421860*t);\r
-   saturn_x_1+=     0.00018939377 * cos( 2.77618306725 +      853.19638175200*t);\r
-   saturn_x_1+=     0.00018093009 * cos( 5.09162723865 +      209.36694217490*t);\r
-   saturn_x_1+=     0.00017777854 * cos( 6.10381593351 +      217.23124870110*t);\r
-   saturn_x_1+=     0.00016296201 * cos( 4.86945681437 +      216.48048917570*t);\r
-   saturn_x_1+=     0.00017120250 * cos( 4.59611664188 +      632.78373931320*t);\r
-   saturn_x_1+=     0.00015894491 * cos( 0.03653502304 +      210.11770170030*t);\r
-   saturn_x_1+=     0.00016192653 * cos( 5.60798014450 +      323.50541665740*t);\r
-   saturn_x_1+=     0.00014466010 * cos( 3.67449380090 +      647.01083331480*t);\r
-   saturn_x_1+=     0.00011061528 * cos( 0.03163071461 +      117.31986822020*t);\r
-   saturn_x_1=saturn_x_1 * t;\r
-\r
-   double saturn_x_2=0.0;\r
-   saturn_x_2+=     0.00560746334 * cos( 1.26401632282 +      206.18554843720*t);\r
-   saturn_x_2+=     0.00545834518 * cos( 3.62343709657 +      220.41264243880*t);\r
-   saturn_x_2+=     0.00443342186 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_x_2+=     0.00336109713 * cos( 2.42547432460 +      213.29909543800*t);\r
-   saturn_x_2+=     0.00224302269 * cos( 2.49151203519 +      426.59819087600*t);\r
-   saturn_x_2+=     0.00087170924 * cos( 4.89048951691 +        7.11354700080*t);\r
-   saturn_x_2+=     0.00050028094 * cos( 2.70119046081 +      433.71173787680*t);\r
-   saturn_x_2+=     0.00045122590 * cos( 0.36735068943 +      419.48464387520*t);\r
-   saturn_x_2+=     0.00032847824 * cos( 1.59210153669 +      639.89728631400*t);\r
-   saturn_x_2+=     0.00027153555 * cos( 3.49804002218 +      227.52618943960*t);\r
-   saturn_x_2+=     0.00012676167 * cos( 1.45465729530 +      199.07200143640*t);\r
-   saturn_x_2+=     0.00010330738 * cos( 4.76949531290 +       14.22709400160*t);\r
-   saturn_x_2=saturn_x_2 * t * t;\r
-\r
-   double saturn_x_3=0.0;\r
-   saturn_x_3+=     0.00077115952 * cos( 2.97714385362 +      206.18554843720*t);\r
-   saturn_x_3+=     0.00075340436 * cos( 1.89208005248 +      220.41264243880*t);\r
-   saturn_x_3+=     0.00018450895 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_x_3+=     0.00010527244 * cos( 0.66368256891 +      426.59819087600*t);\r
-   saturn_x_3=saturn_x_3 * t * t * t;\r
-\r
-   return saturn_x_0+saturn_x_1+saturn_x_2+saturn_x_3;\r
-}\r
-\r
-double vsop87a_micro_saturn_y(double t){\r
-   double saturn_y_0=0.0;\r
-   saturn_y_0+=     9.52986882699 * cos( 5.58600556665 +      213.29909543800*t);\r
-   saturn_y_0+=     0.79387988806 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_y_0+=     0.26441781302 * cos( 4.83528061849 +      426.59819087600*t);\r
-   saturn_y_0+=     0.06916653915 * cos( 2.55279408706 +      206.18554843720*t);\r
-   saturn_y_0+=     0.06633570703 * cos( 5.46258848288 +      220.41264243880*t);\r
-   saturn_y_0+=     0.02345609742 * cos( 0.44652132519 +        7.11354700080*t);\r
-   saturn_y_0+=     0.01183874652 * cos( 1.34638298371 +      419.48464387520*t);\r
-   saturn_y_0+=     0.01245790434 * cos( 0.60367177975 +      110.20632121940*t);\r
-   saturn_y_0+=     0.01098751131 * cos( 4.08608782813 +      639.89728631400*t);\r
-   saturn_y_0+=     0.00700849336 * cos( 1.13611298025 +      316.39186965660*t);\r
-   saturn_y_0+=     0.00434466176 * cos( 5.42474696262 +      529.69096509460*t);\r
-   saturn_y_0+=     0.00373327342 * cos( 4.71308726958 +      433.71173787680*t);\r
-   saturn_y_0+=     0.00335162363 * cos( 0.66422253983 +      103.09277421860*t);\r
-   saturn_y_0+=     0.00097837745 * cos( 5.72844290173 +      323.50541665740*t);\r
-   saturn_y_0+=     0.00080571808 * cos( 4.05295449910 +       11.04570026390*t);\r
-   saturn_y_0+=     0.00083899691 * cos( 5.33204070267 +      227.52618943960*t);\r
-   saturn_y_0+=     0.00070158491 * cos( 5.59777963629 +      209.36694217490*t);\r
-   saturn_y_0+=     0.00065937657 * cos( 1.25969608208 +      202.25339517410*t);\r
-   saturn_y_0+=     0.00070957225 * cos( 0.88888207567 +      632.78373931320*t);\r
-   saturn_y_0+=     0.00068985859 * cos( 2.44460312617 +      217.23124870110*t);\r
-   saturn_y_0+=     0.00058382264 * cos( 0.58978766922 +      224.34479570190*t);\r
-   saturn_y_0+=     0.00054049836 * cos( 3.33757904879 +      853.19638175200*t);\r
-   saturn_y_0+=     0.00045790930 * cos( 0.30331527632 +       14.22709400160*t);\r
-   saturn_y_0+=     0.00041976402 * cos( 2.62591355948 +      199.07200143640*t);\r
-   saturn_y_0+=     0.00044697175 * cos( 0.90661238256 +       63.73589830340*t);\r
-   saturn_y_0+=     0.00025199575 * cos( 5.08963506006 +      216.48048917570*t);\r
-   saturn_y_0+=     0.00024640836 * cos( 2.95445247282 +      210.11770170030*t);\r
-   saturn_y_0+=     0.00024835151 * cos( 4.02630190571 +      415.55249061210*t);\r
-   saturn_y_0+=     0.00025545907 * cos( 0.06626229252 +      117.31986822020*t);\r
-   saturn_y_0+=     0.00029666833 * cos( 6.09910638345 +      735.87651353180*t);\r
-   saturn_y_0+=     0.00023396742 * cos( 3.96337393635 +      647.01083331480*t);\r
-   saturn_y_0+=     0.00023380691 * cos( 4.90051072276 +      149.56319713460*t);\r
-   saturn_y_0+=     0.00020272215 * cos( 2.34319548198 +      309.27832265580*t);\r
-   saturn_y_0+=     0.00020099552 * cos( 0.98365186365 +      522.57741809380*t);\r
-   saturn_y_0+=     0.00024827950 * cos( 3.92681428900 +       74.78159856730*t);\r
-   saturn_y_0+=     0.00015383927 * cos( 3.10227822627 +      277.03499374140*t);\r
-   saturn_y_0+=     0.00011629210 * cos( 5.74108283772 +     1059.38193018920*t);\r
-   saturn_y_0+=     0.00012422966 * cos( 2.62557865743 +      490.33408917940*t);\r
-   saturn_y_0+=     0.00012048048 * cos( 4.09265980116 +      351.81659230870*t);\r
-\r
-   double saturn_y_1=0.0;\r
-   saturn_y_1+=     0.05373889135 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_y_1+=     0.03090575152 * cos( 2.70346890906 +      426.59819087600*t);\r
-   saturn_y_1+=     0.02741594312 * cos( 4.26667636015 +      206.18554843720*t);\r
-   saturn_y_1+=     0.02647489677 * cos( 3.76132298889 +      220.41264243880*t);\r
-   saturn_y_1+=     0.00631520527 * cos( 5.03245505280 +        7.11354700080*t);\r
-   saturn_y_1+=     0.00256799701 * cos( 1.95351819758 +      639.89728631400*t);\r
-   saturn_y_1+=     0.00312271930 * cos( 3.25850205023 +      419.48464387520*t);\r
-   saturn_y_1+=     0.00189433319 * cos( 2.91501840819 +      433.71173787680*t);\r
-   saturn_y_1+=     0.00164133553 * cos( 5.29239290066 +      213.29909543800*t);\r
-   saturn_y_1+=     0.00116791227 * cos( 5.89146675760 +      110.20632121940*t);\r
-   saturn_y_1+=     0.00067210919 * cos( 2.17042636344 +      316.39186965660*t);\r
-   saturn_y_1+=     0.00067003292 * cos( 3.63101075514 +      227.52618943960*t);\r
-   saturn_y_1+=     0.00033002406 * cos( 4.35527405801 +      199.07200143640*t);\r
-   saturn_y_1+=     0.00030628998 * cos( 4.88861760772 +       14.22709400160*t);\r
-   saturn_y_1+=     0.00022234714 * cos( 4.62212779231 +      103.09277421860*t);\r
-   saturn_y_1+=     0.00018945004 * cos( 1.20412493845 +      853.19638175200*t);\r
-   saturn_y_1+=     0.00018079959 * cos( 3.51566153251 +      209.36694217490*t);\r
-   saturn_y_1+=     0.00017791543 * cos( 4.53214140649 +      217.23124870110*t);\r
-   saturn_y_1+=     0.00016320701 * cos( 3.29784030970 +      216.48048917570*t);\r
-   saturn_y_1+=     0.00015944258 * cos( 4.74503265169 +      210.11770170030*t);\r
-   saturn_y_1+=     0.00016717122 * cos( 3.00270792752 +      632.78373931320*t);\r
-   saturn_y_1+=     0.00016149947 * cos( 4.04186432517 +      323.50541665740*t);\r
-   saturn_y_1+=     0.00014481431 * cos( 2.10298298650 +      647.01083331480*t);\r
-   saturn_y_1+=     0.00011084040 * cos( 4.74073871754 +      117.31986822020*t);\r
-   saturn_y_1=saturn_y_1 * t;\r
-\r
-   double saturn_y_2=0.0;\r
-   saturn_y_2+=     0.00563706537 * cos( 5.97115878242 +      206.18554843720*t);\r
-   saturn_y_2+=     0.00547012116 * cos( 2.05154973426 +      220.41264243880*t);\r
-   saturn_y_2+=     0.00458518613 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_y_2+=     0.00362294249 * cos( 0.89540100509 +      213.29909543800*t);\r
-   saturn_y_2+=     0.00225521642 * cos( 0.91699821445 +      426.59819087600*t);\r
-   saturn_y_2+=     0.00088390611 * cos( 3.30289449917 +        7.11354700080*t);\r
-   saturn_y_2+=     0.00050101314 * cos( 1.12976163835 +      433.71173787680*t);\r
-   saturn_y_2+=     0.00045516403 * cos( 5.07669466539 +      419.48464387520*t);\r
-   saturn_y_2+=     0.00032896745 * cos( 0.02089057938 +      639.89728631400*t);\r
-   saturn_y_2+=     0.00027199743 * cos( 1.92638417640 +      227.52618943960*t);\r
-   saturn_y_2+=     0.00013251505 * cos( 6.07693099404 +      199.07200143640*t);\r
-   saturn_y_2+=     0.00010425984 * cos( 3.18246869028 +       14.22709400160*t);\r
-   saturn_y_2=saturn_y_2 * t * t;\r
-\r
-   double saturn_y_3=0.0;\r
-   saturn_y_3+=     0.00077376615 * cos( 1.40391048961 +      206.18554843720*t);\r
-   saturn_y_3+=     0.00075564351 * cos( 0.31962896379 +      220.41264243880*t);\r
-   saturn_y_3+=     0.00022843837 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_y_3+=     0.00010672263 * cos( 5.36495663820 +      426.59819087600*t);\r
-   saturn_y_3=saturn_y_3 * t * t * t;\r
-\r
-   return saturn_y_0+saturn_y_1+saturn_y_2+saturn_y_3;\r
-}\r
-\r
-double vsop87a_micro_saturn_z(double t){\r
-   double saturn_z_0=0.0;\r
-   saturn_z_0+=     0.41356950940 * cos( 3.60234142982 +      213.29909543800*t);\r
-   saturn_z_0+=     0.01148283576 * cos( 2.85128367469 +      426.59819087600*t);\r
-   saturn_z_0+=     0.01214249867 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_z_0+=     0.00329280791 * cos( 0.57121407104 +      206.18554843720*t);\r
-   saturn_z_0+=     0.00286934048 * cos( 3.48073526693 +      220.41264243880*t);\r
-   saturn_z_0+=     0.00099076584 * cos( 4.73369511264 +        7.11354700080*t);\r
-   saturn_z_0+=     0.00057361820 * cos( 4.92611225093 +      110.20632121940*t);\r
-   saturn_z_0+=     0.00047738127 * cos( 2.10039779728 +      639.89728631400*t);\r
-   saturn_z_0+=     0.00043458803 * cos( 5.84904978051 +      419.48464387520*t);\r
-   saturn_z_0+=     0.00034565673 * cos( 5.42614229590 +      316.39186965660*t);\r
-   saturn_z_0+=     0.00016185391 * cos( 2.72987173675 +      433.71173787680*t);\r
-   saturn_z_0+=     0.00011433574 * cos( 3.71662021072 +      529.69096509460*t);\r
-\r
-   double saturn_z_1=0.0;\r
-   saturn_z_1+=     0.01906503283 * cos( 4.94544746116 +      213.29909543800*t);\r
-   saturn_z_1+=     0.00528301265 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_z_1+=     0.00130262284 * cos( 2.26140980879 +      206.18554843720*t);\r
-   saturn_z_1+=     0.00101466332 * cos( 1.79095829545 +      220.41264243880*t);\r
-   saturn_z_1+=     0.00085947578 * cos( 0.51612788497 +      426.59819087600*t);\r
-   saturn_z_1+=     0.00022257446 * cos( 3.07684015656 +        7.11354700080*t);\r
-   saturn_z_1+=     0.00016179946 * cos( 1.19987517506 +      419.48464387520*t);\r
-   saturn_z_1=saturn_z_1 * t;\r
-\r
-   double saturn_z_2=0.0;\r
-   saturn_z_2+=     0.00131275155 * cos( 0.08868998101 +      213.29909543800*t);\r
-   saturn_z_2+=     0.00030147649 * cos( 3.91396203887 +      206.18554843720*t);\r
-   saturn_z_2+=     0.00019322173 * cos( 0.09228748624 +      220.41264243880*t);\r
-   saturn_z_2=saturn_z_2 * t * t;\r
-\r
-   return saturn_z_0+saturn_z_1+saturn_z_2;\r
-}\r
-\r
-double vsop87a_micro_uranus_x(double t){\r
-   double uranus_x_0=0.0;\r
-   uranus_x_0+=    19.17370730359 * cos( 5.48133416489 +       74.78159856730*t);\r
-   uranus_x_0+=     1.32272523872 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_x_0+=     0.44402496796 * cos( 1.65967519586 +      149.56319713460*t);\r
-   uranus_x_0+=     0.14668209481 * cos( 3.42395862804 +       73.29712585900*t);\r
-   uranus_x_0+=     0.14130269479 * cos( 4.39572927934 +       76.26607127560*t);\r
-   uranus_x_0+=     0.06201106178 * cos( 5.14043574125 +        1.48447270830*t);\r
-   uranus_x_0+=     0.01542951343 * cos( 4.12121838072 +      224.34479570190*t);\r
-   uranus_x_0+=     0.01444216660 * cos( 2.65117115201 +      148.07872442630*t);\r
-   uranus_x_0+=     0.00944995563 * cos( 1.65869338757 +       11.04570026390*t);\r
-   uranus_x_0+=     0.00657524815 * cos( 0.57595170636 +      151.04766984290*t);\r
-   uranus_x_0+=     0.00621624676 * cos( 3.05882246638 +       77.75054398390*t);\r
-   uranus_x_0+=     0.00585182542 * cos( 4.79934779678 +       71.81265315070*t);\r
-   uranus_x_0+=     0.00634000270 * cos( 4.09556589724 +       63.73589830340*t);\r
-   uranus_x_0+=     0.00547699056 * cos( 3.63127725056 +       85.82729883120*t);\r
-   uranus_x_0+=     0.00458219984 * cos( 3.90788284112 +        2.96894541660*t);\r
-   uranus_x_0+=     0.00496087649 * cos( 0.59947400861 +      529.69096509460*t);\r
-   uranus_x_0+=     0.00383625535 * cos( 6.18762010576 +      138.51749687070*t);\r
-   uranus_x_0+=     0.00267938156 * cos( 0.96885660137 +      213.29909543800*t);\r
-   uranus_x_0+=     0.00215368005 * cos( 5.30877641428 +       38.13303563780*t);\r
-   uranus_x_0+=     0.00145505389 * cos( 2.31759757085 +       70.84944530420*t);\r
-   uranus_x_0+=     0.00135340032 * cos( 5.51062460816 +       78.71375183040*t);\r
-   uranus_x_0+=     0.00119593859 * cos( 4.10138544267 +       39.61750834610*t);\r
-   uranus_x_0+=     0.00125105686 * cos( 2.51455273063 +      111.43016149680*t);\r
-   uranus_x_0+=     0.00111260244 * cos( 5.12252784325 +      222.86032299360*t);\r
-   uranus_x_0+=     0.00104619827 * cos( 3.90538916334 +      146.59425171800*t);\r
-   uranus_x_0+=     0.00110125387 * cos( 4.45473528724 +       35.16409022120*t);\r
-   uranus_x_0+=     0.00063584588 * cos( 0.29966233158 +      299.12639426920*t);\r
-   uranus_x_0+=     0.00053904041 * cos( 3.92590422507 +        3.93215326310*t);\r
-   uranus_x_0+=     0.00065066905 * cos( 3.73008452906 +      109.94568878850*t);\r
-   uranus_x_0+=     0.00039181662 * cos( 2.68841280769 +        4.45341812490*t);\r
-   uranus_x_0+=     0.00034341683 * cos( 3.03781661928 +      225.82926841020*t);\r
-   uranus_x_0+=     0.00033134636 * cos( 2.54201591218 +       65.22037101170*t);\r
-   uranus_x_0+=     0.00034555652 * cos( 1.84699329257 +       79.23501669220*t);\r
-   uranus_x_0+=     0.00033867050 * cos( 5.98418436103 +       70.32818044240*t);\r
-   uranus_x_0+=     0.00028371614 * cos( 2.58026657123 +      127.47179660680*t);\r
-   uranus_x_0+=     0.00035943348 * cos( 4.08754543016 +      202.25339517410*t);\r
-   uranus_x_0+=     0.00025208833 * cos( 5.30272144657 +        9.56122755560*t);\r
-   uranus_x_0+=     0.00023467802 * cos( 4.09729860322 +      145.63104387150*t);\r
-   uranus_x_0+=     0.00022963939 * cos( 5.51475073655 +       84.34282612290*t);\r
-   uranus_x_0+=     0.00031823951 * cos( 5.53948583244 +      152.53214255120*t);\r
-   uranus_x_0+=     0.00028384953 * cos( 6.01785430306 +      184.72728735580*t);\r
-   uranus_x_0+=     0.00026657176 * cos( 6.11027939727 +      160.60889739850*t);\r
-   uranus_x_0+=     0.00019676762 * cos( 5.53431398332 +       74.66972398270*t);\r
-   uranus_x_0+=     0.00019653873 * cos( 2.28660913421 +       74.89347315190*t);\r
-   uranus_x_0+=     0.00019954280 * cos( 0.57450958037 +       12.53017297220*t);\r
-   uranus_x_0+=     0.00018565067 * cos( 0.62225019017 +       52.69019803950*t);\r
-   uranus_x_0+=     0.00020084756 * cos( 4.47297488471 +       22.09140052780*t);\r
-   uranus_x_0+=     0.00019926329 * cos( 1.39878194708 +      112.91463420510*t);\r
-   uranus_x_0+=     0.00018575632 * cos( 5.70217475790 +       33.67961751290*t);\r
-   uranus_x_0+=     0.00016587870 * cos( 4.86920309163 +      108.46121608020*t);\r
-   uranus_x_0+=     0.00015171194 * cos( 2.88415453399 +       41.10198105440*t);\r
-   uranus_x_0+=     0.00011245800 * cos( 6.11597016146 +       71.60020482960*t);\r
-   uranus_x_0+=     0.00013948521 * cos( 6.27545694160 +      221.37585028530*t);\r
-   uranus_x_0+=     0.00010798350 * cos( 1.70031857078 +       77.96299230500*t);\r
-   uranus_x_0+=     0.00013593955 * cos( 2.55407820633 +       87.31177153950*t);\r
-   uranus_x_0+=     0.00011997848 * cos( 0.94875212305 +     1059.38193018920*t);\r
-   uranus_x_0+=     0.00012884351 * cos( 5.08737999470 +      145.10977900970*t);\r
-   uranus_x_0+=     0.00012394786 * cos( 6.21892878850 +       72.33391801250*t);\r
-   uranus_x_0+=     0.00012253318 * cos( 0.19452856525 +       36.64856292950*t);\r
-   uranus_x_0+=     0.00011538642 * cos( 1.77241794539 +       77.22927912210*t);\r
-\r
-   double uranus_x_1=0.0;\r
-   uranus_x_1+=     0.00739730021 * cos( 6.01067825116 +      149.56319713460*t);\r
-   uranus_x_1+=     0.00526878306 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_x_1+=     0.00239840801 * cos( 5.33657762707 +       73.29712585900*t);\r
-   uranus_x_1+=     0.00229676787 * cos( 2.48204455775 +       76.26607127560*t);\r
-   uranus_x_1+=     0.00111045158 * cos( 5.57157235960 +       11.04570026390*t);\r
-   uranus_x_1+=     0.00096352822 * cos( 0.35070389084 +       63.73589830340*t);\r
-   uranus_x_1+=     0.00081511870 * cos( 1.21058618039 +       85.82729883120*t);\r
-   uranus_x_1+=     0.00045687564 * cos( 2.29216583843 +      138.51749687070*t);\r
-   uranus_x_1+=     0.00051382501 * cos( 2.18935125260 +      224.34479570190*t);\r
-   uranus_x_1+=     0.00038844330 * cos( 0.30724575951 +       70.84944530420*t);\r
-   uranus_x_1+=     0.00036158493 * cos( 1.23634798757 +       78.71375183040*t);\r
-   uranus_x_1+=     0.00032333094 * cos( 5.06666556704 +       74.78159856730*t);\r
-   uranus_x_1+=     0.00021685656 * cos( 4.93710968392 +      151.04766984290*t);\r
-   uranus_x_1+=     0.00019441970 * cos( 1.30617490304 +       77.75054398390*t);\r
-   uranus_x_1+=     0.00017376241 * cos( 0.24607221230 +       71.81265315070*t);\r
-   uranus_x_1+=     0.00015211071 * cos( 5.53141633140 +        3.93215326310*t);\r
-   uranus_x_1=uranus_x_1 * t;\r
-\r
-   double uranus_x_2=0.0;\r
-   uranus_x_2+=     0.00016015732 * cos( 3.83700026619 +       74.78159856730*t);\r
-   uranus_x_2+=     0.00010915299 * cos( 3.02987776270 +      149.56319713460*t);\r
-   uranus_x_2=uranus_x_2 * t * t;\r
-\r
-   return uranus_x_0+uranus_x_1+uranus_x_2;\r
-}\r
-\r
-double vsop87a_micro_uranus_y(double t){\r
-   double uranus_y_0=0.0;\r
-   uranus_y_0+=    19.16518231584 * cos( 3.91045677002 +       74.78159856730*t);\r
-   uranus_y_0+=     0.44390465203 * cos( 0.08884111329 +      149.56319713460*t);\r
-   uranus_y_0+=     0.16256125476 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_y_0+=     0.14755940186 * cos( 1.85423280679 +       73.29712585900*t);\r
-   uranus_y_0+=     0.14123958128 * cos( 2.82486076549 +       76.26607127560*t);\r
-   uranus_y_0+=     0.06250078231 * cos( 3.56960243857 +        1.48447270830*t);\r
-   uranus_y_0+=     0.01542668264 * cos( 2.55040539213 +      224.34479570190*t);\r
-   uranus_y_0+=     0.01442356575 * cos( 1.08004542712 +      148.07872442630*t);\r
-   uranus_y_0+=     0.00938975501 * cos( 0.09275714761 +       11.04570026390*t);\r
-   uranus_y_0+=     0.00650331846 * cos( 2.76142680222 +       63.73589830340*t);\r
-   uranus_y_0+=     0.00657343120 * cos( 5.28830704469 +      151.04766984290*t);\r
-   uranus_y_0+=     0.00621326770 * cos( 1.48795811387 +       77.75054398390*t);\r
-   uranus_y_0+=     0.00541961958 * cos( 3.24476486661 +       71.81265315070*t);\r
-   uranus_y_0+=     0.00547472694 * cos( 2.06037924573 +       85.82729883120*t);\r
-   uranus_y_0+=     0.00459589120 * cos( 2.33745536070 +        2.96894541660*t);\r
-   uranus_y_0+=     0.00495936105 * cos( 5.31205753740 +      529.69096509460*t);\r
-   uranus_y_0+=     0.00387922853 * cos( 4.62026923885 +      138.51749687070*t);\r
-   uranus_y_0+=     0.00268363417 * cos( 5.68085299020 +      213.29909543800*t);\r
-   uranus_y_0+=     0.00216239629 * cos( 3.73800767580 +       38.13303563780*t);\r
-   uranus_y_0+=     0.00144032475 * cos( 0.75015700920 +       70.84944530420*t);\r
-   uranus_y_0+=     0.00135290820 * cos( 3.93970260616 +       78.71375183040*t);\r
-   uranus_y_0+=     0.00119670613 * cos( 2.53058783780 +       39.61750834610*t);\r
-   uranus_y_0+=     0.00124868545 * cos( 0.94315917319 +      111.43016149680*t);\r
-   uranus_y_0+=     0.00111204860 * cos( 3.55163219419 +      222.86032299360*t);\r
-   uranus_y_0+=     0.00104507929 * cos( 2.33345675603 +      146.59425171800*t);\r
-   uranus_y_0+=     0.00108584454 * cos( 6.02234848388 +       35.16409022120*t);\r
-   uranus_y_0+=     0.00063573747 * cos( 5.01204967920 +      299.12639426920*t);\r
-   uranus_y_0+=     0.00053289771 * cos( 2.38437587876 +        3.93215326310*t);\r
-   uranus_y_0+=     0.00063774261 * cos( 2.15607602904 +      109.94568878850*t);\r
-   uranus_y_0+=     0.00039218598 * cos( 1.11841109252 +        4.45341812490*t);\r
-   uranus_y_0+=     0.00034205426 * cos( 0.92405922576 +       65.22037101170*t);\r
-   uranus_y_0+=     0.00034334377 * cos( 1.46696169843 +      225.82926841020*t);\r
-   uranus_y_0+=     0.00034538316 * cos( 0.27613780697 +       79.23501669220*t);\r
-   uranus_y_0+=     0.00039256771 * cos( 5.75956853703 +      202.25339517410*t);\r
-   uranus_y_0+=     0.00026157754 * cos( 3.74097610798 +        9.56122755560*t);\r
-   uranus_y_0+=     0.00023427328 * cos( 2.52740125551 +      145.63104387150*t);\r
-   uranus_y_0+=     0.00022933138 * cos( 3.94455540350 +       84.34282612290*t);\r
-   uranus_y_0+=     0.00031816303 * cos( 3.96860170484 +      152.53214255120*t);\r
-   uranus_y_0+=     0.00025237176 * cos( 4.45141413666 +       70.32818044240*t);\r
-   uranus_y_0+=     0.00028372491 * cos( 4.44714627097 +      184.72728735580*t);\r
-   uranus_y_0+=     0.00026652859 * cos( 4.53944395347 +      160.60889739850*t);\r
-   uranus_y_0+=     0.00019666208 * cos( 3.96350065335 +       74.66972398270*t);\r
-   uranus_y_0+=     0.00019643845 * cos( 0.71577796385 +       74.89347315190*t);\r
-   uranus_y_0+=     0.00019838981 * cos( 5.29113397354 +       12.53017297220*t);\r
-   uranus_y_0+=     0.00021523908 * cos( 4.93565132068 +       36.64856292950*t);\r
-   uranus_y_0+=     0.00015537967 * cos( 1.87863275460 +       52.69019803950*t);\r
-   uranus_y_0+=     0.00020115100 * cos( 3.45473780762 +      127.47179660680*t);\r
-   uranus_y_0+=     0.00020051641 * cos( 2.90386352937 +       22.09140052780*t);\r
-   uranus_y_0+=     0.00019901477 * cos( 6.11075402434 +      112.91463420510*t);\r
-   uranus_y_0+=     0.00018126776 * cos( 0.98478853787 +       33.67961751290*t);\r
-   uranus_y_0+=     0.00015174962 * cos( 1.31314034959 +       41.10198105440*t);\r
-   uranus_y_0+=     0.00011239020 * cos( 4.54508334011 +       71.60020482960*t);\r
-   uranus_y_0+=     0.00013948849 * cos( 4.70474945682 +      221.37585028530*t);\r
-   uranus_y_0+=     0.00010819728 * cos( 0.12807029856 +       77.96299230500*t);\r
-   uranus_y_0+=     0.00013589665 * cos( 0.98313719930 +       87.31177153950*t);\r
-   uranus_y_0+=     0.00011996772 * cos( 5.66129275335 +     1059.38193018920*t);\r
-   uranus_y_0+=     0.00012407787 * cos( 4.64945783340 +       72.33391801250*t);\r
-   uranus_y_0+=     0.00011531140 * cos( 0.20190074645 +       77.22927912210*t);\r
-\r
-   double uranus_y_1=0.0;\r
-   uranus_y_1+=     0.02157896385 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_y_1+=     0.00739227349 * cos( 4.43963890935 +      149.56319713460*t);\r
-   uranus_y_1+=     0.00238545685 * cos( 3.76882493145 +       73.29712585900*t);\r
-   uranus_y_1+=     0.00229396424 * cos( 0.91090183978 +       76.26607127560*t);\r
-   uranus_y_1+=     0.00110137111 * cos( 4.00844441616 +       11.04570026390*t);\r
-   uranus_y_1+=     0.00094979054 * cos( 5.07141537066 +       63.73589830340*t);\r
-   uranus_y_1+=     0.00081474163 * cos( 5.92275367106 +       85.82729883120*t);\r
-   uranus_y_1+=     0.00045457174 * cos( 0.73292241207 +      138.51749687070*t);\r
-   uranus_y_1+=     0.00051366974 * cos( 0.61844114994 +      224.34479570190*t);\r
-   uranus_y_1+=     0.00038296005 * cos( 5.01873578671 +       70.84944530420*t);\r
-   uranus_y_1+=     0.00036146116 * cos( 5.94859452787 +       78.71375183040*t);\r
-   uranus_y_1+=     0.00032420558 * cos( 4.32617271732 +       74.78159856730*t);\r
-   uranus_y_1+=     0.00021673269 * cos( 3.36607263522 +      151.04766984290*t);\r
-   uranus_y_1+=     0.00019425087 * cos( 6.01842187783 +       77.75054398390*t);\r
-   uranus_y_1+=     0.00017393206 * cos( 4.96098895488 +       71.81265315070*t);\r
-   uranus_y_1+=     0.00014991169 * cos( 3.97176856758 +        3.93215326310*t);\r
-   uranus_y_1=uranus_y_1 * t;\r
-\r
-   double uranus_y_2=0.0;\r
-   uranus_y_2+=     0.00034812647 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_y_2+=     0.00016589194 * cos( 2.29556740620 +       74.78159856730*t);\r
-   uranus_y_2+=     0.00010905147 * cos( 1.45737963668 +      149.56319713460*t);\r
-   uranus_y_2=uranus_y_2 * t * t;\r
-\r
-   return uranus_y_0+uranus_y_1+uranus_y_2;\r
-}\r
-\r
-double vsop87a_micro_uranus_z(double t){\r
-   double uranus_z_0=0.0;\r
-   uranus_z_0+=     0.25878127698 * cos( 2.61861272578 +       74.78159856730*t);\r
-   uranus_z_0+=     0.01774318778 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_z_0+=     0.00599316131 * cos( 5.08119500585 +      149.56319713460*t);\r
-   uranus_z_0+=     0.00190281890 * cos( 1.61643841193 +       76.26607127560*t);\r
-   uranus_z_0+=     0.00190881685 * cos( 0.57869575952 +       73.29712585900*t);\r
-   uranus_z_0+=     0.00084626761 * cos( 2.26030150166 +        1.48447270830*t);\r
-   uranus_z_0+=     0.00030734257 * cos( 0.23571721555 +       63.73589830340*t);\r
-   uranus_z_0+=     0.00020842052 * cos( 1.26054208091 +      224.34479570190*t);\r
-   uranus_z_0+=     0.00019734273 * cos( 6.04314677688 +      148.07872442630*t);\r
-   uranus_z_0+=     0.00012537530 * cos( 5.17169051466 +       11.04570026390*t);\r
-   uranus_z_0+=     0.00014582864 * cos( 6.14852037212 +       71.81265315070*t);\r
-   uranus_z_0+=     0.00010407529 * cos( 3.65320417038 +      213.29909543800*t);\r
-   uranus_z_0+=     0.00011261541 * cos( 3.55973769686 +      529.69096509460*t);\r
-\r
-   double uranus_z_1=0.0;\r
-   uranus_z_1+=     0.00655916626 * cos( 0.01271947660 +       74.78159856730*t);\r
-   uranus_z_1+=     0.00049648951 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_z_1+=     0.00023874178 * cos( 2.73870491220 +      149.56319713460*t);\r
-   uranus_z_1=uranus_z_1 * t;\r
-\r
-   double uranus_z_2=0.0;\r
-   uranus_z_2+=     0.00014697858 * cos( 1.75149165003 +       74.78159856730*t);\r
-   uranus_z_2=uranus_z_2 * t * t;\r
-\r
-   return uranus_z_0+uranus_z_1+uranus_z_2;\r
-}\r
-\r
-double vsop87a_micro_venus_x(double t){\r
-   double venus_x_0=0.0;\r
-   venus_x_0+=     0.72211281391 * cos( 3.17575836361 +    10213.28554621100*t);\r
-   venus_x_0+=     0.00486448018 * cos( 0.00000000000 +        0.00000000000*t);\r
-   venus_x_0+=     0.00244500474 * cos( 4.05566613861 +    20426.57109242200*t);\r
-\r
-   double venus_x_1=0.0;\r
-   venus_x_1+=     0.00033862636 * cos( 3.14159265359 +        0.00000000000*t);\r
-   venus_x_1+=     0.00017234992 * cos( 0.92721124604 +    20426.57109242200*t);\r
-   venus_x_1=venus_x_1 * t;\r
-\r
-   return venus_x_0+venus_x_1;\r
-}\r
-\r
-double vsop87a_micro_venus_y(double t){\r
-   double venus_y_0=0.0;\r
-   venus_y_0+=     0.72324820731 * cos( 1.60573808356 +    10213.28554621100*t);\r
-   venus_y_0+=     0.00549506273 * cos( 3.14159265359 +        0.00000000000*t);\r
-   venus_y_0+=     0.00244884790 * cos( 2.48564954004 +    20426.57109242200*t);\r
-\r
-   double venus_y_1=0.0;\r
-   venus_y_1+=     0.00039231430 * cos( 0.00000000000 +        0.00000000000*t);\r
-   venus_y_1+=     0.00017282326 * cos( 5.63824735900 +    20426.57109242200*t);\r
-   venus_y_1=venus_y_1 * t;\r
-\r
-   return venus_y_0+venus_y_1;\r
-}\r
-\r
-double vsop87a_micro_venus_z(double t){\r
-   double venus_z_0=0.0;\r
-   venus_z_0+=     0.04282990302 * cos( 0.26703856476 +    10213.28554621100*t);\r
-   venus_z_0+=     0.00035588343 * cos( 3.14159265359 +        0.00000000000*t);\r
-   venus_z_0+=     0.00014501879 * cos( 1.14696911390 +    20426.57109242200*t);\r
-\r
-   double venus_z_1=0.0;\r
-   venus_z_1+=     0.00208096402 * cos( 1.88967278742 +    10213.28554621100*t);\r
-   venus_z_1=venus_z_1 * t;\r
-\r
-   return venus_z_0+venus_z_1;\r
-}\r
-\r
diff --git a/movement/lib/vsop87/vsop87a_micro.h b/movement/lib/vsop87/vsop87a_micro.h
deleted file mode 100755 (executable)
index 87515f5..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-//VSOP87-Multilang http://www.astrogreg.com/vsop87-multilang/index.html\r
-//Greg Miller (gmiller@gregmiller.net) 2019.  Released as Public Domain\r
-\r
-#ifndef VSOP87A_MICRO\r
-#define VSOP87A_MICRO\r
-\r
-   void vsop87a_micro_getEarth(double t,double temp[]);\r
-   void vsop87a_micro_getEmb(double t,double temp[]);\r
-   void vsop87a_micro_getJupiter(double t,double temp[]);\r
-   void vsop87a_micro_getMars(double t,double temp[]);\r
-   void vsop87a_micro_getMercury(double t,double temp[]);\r
-   void vsop87a_micro_getNeptune(double t,double temp[]);\r
-   void vsop87a_micro_getSaturn(double t,double temp[]);\r
-   void vsop87a_micro_getUranus(double t,double temp[]);\r
-   void vsop87a_micro_getVenus(double t,double temp[]);\r
-   void vsop87a_micro_getMoon(double earth[], double emb[],double temp[]);\r
-#endif\r
diff --git a/movement/lib/vsop87/vsop87a_milli.c b/movement/lib/vsop87/vsop87a_milli.c
deleted file mode 100755 (executable)
index b187572..0000000
+++ /dev/null
@@ -1,2048 +0,0 @@
-//VSOP87-Multilang http://www.astrogreg.com/vsop87-multilang/index.html\r
-//Greg Miller (gmiller@gregmiller.net) 2019.  Released as Public Domain\r
-\r
-#include <math.h>\r
-#include "vsop87a_milli.h"\r
-double vsop87a_milli_earth_x(double t);\r
-double vsop87a_milli_earth_y(double t);\r
-double vsop87a_milli_earth_z(double t);\r
-double vsop87a_milli_emb_x(double t);\r
-double vsop87a_milli_emb_y(double t);\r
-double vsop87a_milli_emb_z(double t);\r
-double vsop87a_milli_jupiter_x(double t);\r
-double vsop87a_milli_jupiter_y(double t);\r
-double vsop87a_milli_jupiter_z(double t);\r
-double vsop87a_milli_mars_x(double t);\r
-double vsop87a_milli_mars_y(double t);\r
-double vsop87a_milli_mars_z(double t);\r
-double vsop87a_milli_mercury_x(double t);\r
-double vsop87a_milli_mercury_y(double t);\r
-double vsop87a_milli_mercury_z(double t);\r
-double vsop87a_milli_neptune_x(double t);\r
-double vsop87a_milli_neptune_y(double t);\r
-double vsop87a_milli_neptune_z(double t);\r
-double vsop87a_milli_saturn_x(double t);\r
-double vsop87a_milli_saturn_y(double t);\r
-double vsop87a_milli_saturn_z(double t);\r
-double vsop87a_milli_uranus_x(double t);\r
-double vsop87a_milli_uranus_y(double t);\r
-double vsop87a_milli_uranus_z(double t);\r
-double vsop87a_milli_venus_x(double t);\r
-double vsop87a_milli_venus_y(double t);\r
-double vsop87a_milli_venus_z(double t);\r
-\r
-\r
-void vsop87a_milli_getEarth(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_earth_x(t);\r
-   temp[1]=vsop87a_milli_earth_y(t);\r
-   temp[2]=vsop87a_milli_earth_z(t);\r
-}\r
-\r
-void vsop87a_milli_getEmb(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_emb_x(t);\r
-   temp[1]=vsop87a_milli_emb_y(t);\r
-   temp[2]=vsop87a_milli_emb_z(t);\r
-}\r
-\r
-void vsop87a_milli_getJupiter(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_jupiter_x(t);\r
-   temp[1]=vsop87a_milli_jupiter_y(t);\r
-   temp[2]=vsop87a_milli_jupiter_z(t);\r
-}\r
-\r
-void vsop87a_milli_getMars(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_mars_x(t);\r
-   temp[1]=vsop87a_milli_mars_y(t);\r
-   temp[2]=vsop87a_milli_mars_z(t);\r
-}\r
-\r
-void vsop87a_milli_getMercury(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_mercury_x(t);\r
-   temp[1]=vsop87a_milli_mercury_y(t);\r
-   temp[2]=vsop87a_milli_mercury_z(t);\r
-}\r
-\r
-void vsop87a_milli_getNeptune(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_neptune_x(t);\r
-   temp[1]=vsop87a_milli_neptune_y(t);\r
-   temp[2]=vsop87a_milli_neptune_z(t);\r
-}\r
-\r
-void vsop87a_milli_getSaturn(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_saturn_x(t);\r
-   temp[1]=vsop87a_milli_saturn_y(t);\r
-   temp[2]=vsop87a_milli_saturn_z(t);\r
-}\r
-\r
-void vsop87a_milli_getUranus(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_uranus_x(t);\r
-   temp[1]=vsop87a_milli_uranus_y(t);\r
-   temp[2]=vsop87a_milli_uranus_z(t);\r
-}\r
-\r
-void vsop87a_milli_getVenus(double t,double temp[]){\r
-   temp[0]=vsop87a_milli_venus_x(t);\r
-   temp[1]=vsop87a_milli_venus_y(t);\r
-   temp[2]=vsop87a_milli_venus_z(t);\r
-}\r
-\r
-void vsop87a_milli_getMoon(double earth[], double emb[],double temp[]){\r
-   temp[0]=(emb[0]-earth[0])*(1 + 1 / 0.01230073677);\r
-   temp[1]=(emb[1]-earth[1])*(1 + 1 / 0.01230073677);\r
-   temp[2]=(emb[2]-earth[2])*(1 + 1 / 0.01230073677);\r
-   temp[0]=temp[0]+earth[0];\r
-   temp[1]=temp[1]+earth[1];\r
-   temp[2]=temp[2]+earth[2];\r
-}\r
-\r
-double vsop87a_milli_earth_x(double t){\r
-   double earth_x_0=0.0;\r
-   earth_x_0+=     0.99982928844 * cos( 1.75348568475 +     6283.07584999140*t);\r
-   earth_x_0+=     0.00835257300 * cos( 1.71034539450 +    12566.15169998280*t);\r
-   earth_x_0+=     0.00561144206 * cos( 0.00000000000 +        0.00000000000*t);\r
-   earth_x_0+=     0.00010466628 * cos( 1.66722645223 +    18849.22754997420*t);\r
-   earth_x_0+=     0.00003110838 * cos( 0.66875185215 +    83996.84731811189*t);\r
-   earth_x_0+=     0.00002552498 * cos( 0.58310207301 +      529.69096509460*t);\r
-   earth_x_0+=     0.00002137256 * cos( 1.09235189672 +     1577.34354244780*t);\r
-   earth_x_0+=     0.00001709103 * cos( 0.49540223397 +     6279.55273164240*t);\r
-   earth_x_0+=     0.00001707882 * cos( 6.15315547484 +     6286.59896834040*t);\r
-   earth_x_0+=     0.00001445242 * cos( 3.47272783760 +     2352.86615377180*t);\r
-   earth_x_0+=     0.00001091006 * cos( 3.68984782465 +     5223.69391980220*t);\r
-\r
-   double earth_x_1=0.0;\r
-   earth_x_1+=     0.00123403056 * cos( 0.00000000000 +        0.00000000000*t);\r
-   earth_x_1+=     0.00051500156 * cos( 6.00266267204 +    12566.15169998280*t);\r
-   earth_x_1+=     0.00001290726 * cos( 5.95943124583 +    18849.22754997420*t);\r
-   earth_x_1+=     0.00001068627 * cos( 2.01554176551 +     6283.07584999140*t);\r
-   earth_x_1=earth_x_1 * t;\r
-\r
-   double earth_x_2=0.0;\r
-   earth_x_2+=     0.00004143217 * cos( 3.14159265359 +        0.00000000000*t);\r
-   earth_x_2+=     0.00002175695 * cos( 4.39999849572 +    12566.15169998280*t);\r
-   earth_x_2=earth_x_2 * t * t;\r
-\r
-   return earth_x_0+earth_x_1+earth_x_2;\r
-}\r
-\r
-double vsop87a_milli_earth_y(double t){\r
-   double earth_y_0=0.0;\r
-   earth_y_0+=     0.99989211030 * cos( 0.18265890456 +     6283.07584999140*t);\r
-   earth_y_0+=     0.02442699036 * cos( 3.14159265359 +        0.00000000000*t);\r
-   earth_y_0+=     0.00835292314 * cos( 0.13952878991 +    12566.15169998280*t);\r
-   earth_y_0+=     0.00010466965 * cos( 0.09641690558 +    18849.22754997420*t);\r
-   earth_y_0+=     0.00003110838 * cos( 5.38114091484 +    83996.84731811189*t);\r
-   earth_y_0+=     0.00002570338 * cos( 5.30103973360 +      529.69096509460*t);\r
-   earth_y_0+=     0.00002147473 * cos( 2.66253538905 +     1577.34354244780*t);\r
-   earth_y_0+=     0.00001709219 * cos( 5.20780401071 +     6279.55273164240*t);\r
-   earth_y_0+=     0.00001707987 * cos( 4.58232858766 +     6286.59896834040*t);\r
-   earth_y_0+=     0.00001440265 * cos( 1.90068164664 +     2352.86615377180*t);\r
-   earth_y_0+=     0.00001135092 * cos( 5.27313415220 +     5223.69391980220*t);\r
-\r
-   double earth_y_1=0.0;\r
-   earth_y_1+=     0.00093046324 * cos( 0.00000000000 +        0.00000000000*t);\r
-   earth_y_1+=     0.00051506609 * cos( 4.43180499286 +    12566.15169998280*t);\r
-   earth_y_1+=     0.00001290800 * cos( 4.38860548540 +    18849.22754997420*t);\r
-   earth_y_1=earth_y_1 * t;\r
-\r
-   double earth_y_2=0.0;\r
-   earth_y_2+=     0.00005080208 * cos( 0.00000000000 +        0.00000000000*t);\r
-   earth_y_2+=     0.00002178016 * cos( 2.82957544235 +    12566.15169998280*t);\r
-   earth_y_2+=     0.00001020487 * cos( 4.63746718598 +     6283.07584999140*t);\r
-   earth_y_2=earth_y_2 * t * t;\r
-\r
-   return earth_y_0+earth_y_1+earth_y_2;\r
-}\r
-\r
-double vsop87a_milli_earth_z(double t){\r
-   double earth_z_1=0.0;\r
-   earth_z_1+=     0.00227822442 * cos( 3.41372504278 +     6283.07584999140*t);\r
-   earth_z_1+=     0.00005429282 * cos( 0.00000000000 +        0.00000000000*t);\r
-   earth_z_1+=     0.00001903183 * cos( 3.37061270964 +    12566.15169998280*t);\r
-   earth_z_1=earth_z_1 * t;\r
-\r
-   double earth_z_2=0.0;\r
-   earth_z_2+=     0.00009721989 * cos( 5.15233725915 +     6283.07584999140*t);\r
-   earth_z_2=earth_z_2 * t * t;\r
-\r
-   return earth_z_1+earth_z_2;\r
-}\r
-\r
-double vsop87a_milli_emb_x(double t){\r
-   double emb_x_0=0.0;\r
-   emb_x_0+=     0.99982927460 * cos( 1.75348568475 +     6283.07584999140*t);\r
-   emb_x_0+=     0.00835257300 * cos( 1.71034539450 +    12566.15169998280*t);\r
-   emb_x_0+=     0.00561144161 * cos( 0.00000000000 +        0.00000000000*t);\r
-   emb_x_0+=     0.00010466628 * cos( 1.66722645223 +    18849.22754997420*t);\r
-   emb_x_0+=     0.00002552498 * cos( 0.58310207301 +      529.69096509460*t);\r
-   emb_x_0+=     0.00002137256 * cos( 1.09235189672 +     1577.34354244780*t);\r
-   emb_x_0+=     0.00001709103 * cos( 0.49540223397 +     6279.55273164240*t);\r
-   emb_x_0+=     0.00001707882 * cos( 6.15315547484 +     6286.59896834040*t);\r
-   emb_x_0+=     0.00001445242 * cos( 3.47272783760 +     2352.86615377180*t);\r
-   emb_x_0+=     0.00001091006 * cos( 3.68984782465 +     5223.69391980220*t);\r
-\r
-   double emb_x_1=0.0;\r
-   emb_x_1+=     0.00123403046 * cos( 0.00000000000 +        0.00000000000*t);\r
-   emb_x_1+=     0.00051500156 * cos( 6.00266267204 +    12566.15169998280*t);\r
-   emb_x_1+=     0.00001290726 * cos( 5.95943124583 +    18849.22754997420*t);\r
-   emb_x_1+=     0.00001068627 * cos( 2.01554176551 +     6283.07584999140*t);\r
-   emb_x_1=emb_x_1 * t;\r
-\r
-   double emb_x_2=0.0;\r
-   emb_x_2+=     0.00004143217 * cos( 3.14159265359 +        0.00000000000*t);\r
-   emb_x_2+=     0.00002175695 * cos( 4.39999849572 +    12566.15169998280*t);\r
-   emb_x_2=emb_x_2 * t * t;\r
-\r
-   return emb_x_0+emb_x_1+emb_x_2;\r
-}\r
-\r
-double vsop87a_milli_emb_y(double t){\r
-   double emb_y_0=0.0;\r
-   emb_y_0+=     0.99989209645 * cos( 0.18265890456 +     6283.07584999140*t);\r
-   emb_y_0+=     0.02442698841 * cos( 3.14159265359 +        0.00000000000*t);\r
-   emb_y_0+=     0.00835292314 * cos( 0.13952878991 +    12566.15169998280*t);\r
-   emb_y_0+=     0.00010466965 * cos( 0.09641690558 +    18849.22754997420*t);\r
-   emb_y_0+=     0.00002570338 * cos( 5.30103973360 +      529.69096509460*t);\r
-   emb_y_0+=     0.00002147473 * cos( 2.66253538905 +     1577.34354244780*t);\r
-   emb_y_0+=     0.00001709219 * cos( 5.20780401071 +     6279.55273164240*t);\r
-   emb_y_0+=     0.00001707987 * cos( 4.58232858766 +     6286.59896834040*t);\r
-   emb_y_0+=     0.00001440265 * cos( 1.90068164664 +     2352.86615377180*t);\r
-   emb_y_0+=     0.00001135092 * cos( 5.27313415220 +     5223.69391980220*t);\r
-\r
-   double emb_y_1=0.0;\r
-   emb_y_1+=     0.00093046317 * cos( 0.00000000000 +        0.00000000000*t);\r
-   emb_y_1+=     0.00051506609 * cos( 4.43180499286 +    12566.15169998280*t);\r
-   emb_y_1+=     0.00001290800 * cos( 4.38860548540 +    18849.22754997420*t);\r
-   emb_y_1=emb_y_1 * t;\r
-\r
-   double emb_y_2=0.0;\r
-   emb_y_2+=     0.00005080208 * cos( 0.00000000000 +        0.00000000000*t);\r
-   emb_y_2+=     0.00002178016 * cos( 2.82957544235 +    12566.15169998280*t);\r
-   emb_y_2+=     0.00001020487 * cos( 4.63746718598 +     6283.07584999140*t);\r
-   emb_y_2=emb_y_2 * t * t;\r
-\r
-   return emb_y_0+emb_y_1+emb_y_2;\r
-}\r
-\r
-double vsop87a_milli_emb_z(double t){\r
-   double emb_z_1=0.0;\r
-   emb_z_1+=     0.00227822442 * cos( 3.41372504278 +     6283.07584999140*t);\r
-   emb_z_1+=     0.00005429282 * cos( 0.00000000000 +        0.00000000000*t);\r
-   emb_z_1+=     0.00001903183 * cos( 3.37061270964 +    12566.15169998280*t);\r
-   emb_z_1=emb_z_1 * t;\r
-\r
-   double emb_z_2=0.0;\r
-   emb_z_2+=     0.00009721989 * cos( 5.15233725915 +     6283.07584999140*t);\r
-   emb_z_2=emb_z_2 * t * t;\r
-\r
-   return emb_z_1+emb_z_2;\r
-}\r
-\r
-double vsop87a_milli_jupiter_x(double t){\r
-   double jupiter_x_0=0.0;\r
-   jupiter_x_0+=     5.19663470114 * cos( 0.59945082355 +      529.69096509460*t);\r
-   jupiter_x_0+=     0.36662642320 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_x_0+=     0.12593937922 * cos( 0.94911583701 +     1059.38193018920*t);\r
-   jupiter_x_0+=     0.01500672056 * cos( 0.73175134610 +      522.57741809380*t);\r
-   jupiter_x_0+=     0.01476224578 * cos( 3.61736921122 +      536.80451209540*t);\r
-   jupiter_x_0+=     0.00457752736 * cos( 1.29883700755 +     1589.07289528380*t);\r
-   jupiter_x_0+=     0.00301689798 * cos( 5.17372551148 +        7.11354700080*t);\r
-   jupiter_x_0+=     0.00385975375 * cos( 2.01229910687 +      103.09277421860*t);\r
-   jupiter_x_0+=     0.00194025405 * cos( 5.02580363996 +      426.59819087600*t);\r
-   jupiter_x_0+=     0.00150678793 * cos( 6.12003027739 +      110.20632121940*t);\r
-   jupiter_x_0+=     0.00144867641 * cos( 5.55980577080 +      632.78373931320*t);\r
-   jupiter_x_0+=     0.00134226996 * cos( 0.87648567011 +      213.29909543800*t);\r
-   jupiter_x_0+=     0.00103494641 * cos( 6.19324769120 +     1052.26838318840*t);\r
-   jupiter_x_0+=     0.00114201562 * cos( 0.01567084269 +     1162.47470440780*t);\r
-   jupiter_x_0+=     0.00072095575 * cos( 3.96117430643 +     1066.49547719000*t);\r
-   jupiter_x_0+=     0.00059486083 * cos( 4.45769374358 +      949.17560896980*t);\r
-   jupiter_x_0+=     0.00068284021 * cos( 3.44051122631 +      846.08283475120*t);\r
-   jupiter_x_0+=     0.00047092251 * cos( 1.44612636451 +      419.48464387520*t);\r
-   jupiter_x_0+=     0.00030623417 * cos( 2.99132321427 +      206.18554843720*t);\r
-   jupiter_x_0+=     0.00026613459 * cos( 4.85169906494 +      323.50541665740*t);\r
-   jupiter_x_0+=     0.00019727457 * cos( 1.64891626213 +     2118.76386037840*t);\r
-   jupiter_x_0+=     0.00016481594 * cos( 1.95150056568 +      316.39186965660*t);\r
-   jupiter_x_0+=     0.00016101974 * cos( 0.87973155980 +      515.46387109300*t);\r
-   jupiter_x_0+=     0.00014209487 * cos( 2.07769621413 +      742.99006053260*t);\r
-   jupiter_x_0+=     0.00015192516 * cos( 6.25820127906 +      735.87651353180*t);\r
-   jupiter_x_0+=     0.00011423199 * cos( 3.48146108929 +      543.91805909620*t);\r
-   jupiter_x_0+=     0.00012155285 * cos( 3.75229924999 +      525.75881183150*t);\r
-   jupiter_x_0+=     0.00011996271 * cos( 0.58568573729 +      533.62311835770*t);\r
-   jupiter_x_0+=     0.00008468556 * cos( 3.47248751739 +      639.89728631400*t);\r
-   jupiter_x_0+=     0.00008223302 * cos( 5.56680447143 +     1478.86657406440*t);\r
-   jupiter_x_0+=     0.00008694124 * cos( 0.38262009411 +     1692.16566950240*t);\r
-   jupiter_x_0+=     0.00007427517 * cos( 5.98380751196 +      956.28915597060*t);\r
-   jupiter_x_0+=     0.00007516470 * cos( 0.92896448412 +     1265.56747862640*t);\r
-   jupiter_x_0+=     0.00007655867 * cos( 0.14178789086 +     1581.95934828300*t);\r
-   jupiter_x_0+=     0.00005318791 * cos( 1.10494016349 +      526.50957135690*t);\r
-   jupiter_x_0+=     0.00005218492 * cos( 3.23235129224 +      532.87235883230*t);\r
-   jupiter_x_0+=     0.00005777311 * cos( 5.03726165628 +       14.22709400160*t);\r
-   jupiter_x_0+=     0.00004622685 * cos( 3.75817086099 +     1375.77379984580*t);\r
-   jupiter_x_0+=     0.00003939864 * cos( 4.30892687511 +     1596.18644228460*t);\r
-   jupiter_x_0+=     0.00004569444 * cos( 2.15087281710 +       95.97922721780*t);\r
-   jupiter_x_0+=     0.00002952712 * cos( 3.85988483947 +      309.27832265580*t);\r
-   jupiter_x_0+=     0.00002857935 * cos( 6.01118473739 +      117.31986822020*t);\r
-   jupiter_x_0+=     0.00002440094 * cos( 4.23995765702 +      433.71173787680*t);\r
-   jupiter_x_0+=     0.00002438257 * cos( 3.88808463822 +      220.41264243880*t);\r
-   jupiter_x_0+=     0.00002675112 * cos( 3.18723449094 +     1169.58825140860*t);\r
-   jupiter_x_0+=     0.00002386425 * cos( 5.96354994324 +     1045.15483618760*t);\r
-   jupiter_x_0+=     0.00001870097 * cos( 0.52019313301 +     1155.36115740700*t);\r
-   jupiter_x_0+=     0.00001939060 * cos( 5.91883412864 +      625.67019231240*t);\r
-   jupiter_x_0+=     0.00001631500 * cos( 4.41910383466 +      942.06206196900*t);\r
-   jupiter_x_0+=     0.00001451667 * cos( 5.76112706040 +      853.19638175200*t);\r
-   jupiter_x_0+=     0.00001361286 * cos( 1.34792748837 +     1368.66025284500*t);\r
-   jupiter_x_0+=     0.00001663331 * cos( 1.94010629194 +      838.96928775040*t);\r
-   jupiter_x_0+=     0.00001611229 * cos( 5.49324974845 +       74.78159856730*t);\r
-   jupiter_x_0+=     0.00001033570 * cos( 0.08907208789 +     1795.25844372100*t);\r
-   jupiter_x_0+=     0.00001088284 * cos( 1.13406104190 +      527.24328453980*t);\r
-   jupiter_x_0+=     0.00001080643 * cos( 3.20528362573 +      532.13864564940*t);\r
-\r
-   double jupiter_x_1=0.0;\r
-   jupiter_x_1+=     0.00882389251 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_x_1+=     0.00635297172 * cos( 0.10662156868 +     1059.38193018920*t);\r
-   jupiter_x_1+=     0.00599720482 * cos( 2.42996678275 +      522.57741809380*t);\r
-   jupiter_x_1+=     0.00589157060 * cos( 1.91556314637 +      536.80451209540*t);\r
-   jupiter_x_1+=     0.00081697204 * cos( 3.46668108797 +        7.11354700080*t);\r
-   jupiter_x_1+=     0.00046201898 * cos( 0.45714214032 +     1589.07289528380*t);\r
-   jupiter_x_1+=     0.00032508590 * cos( 1.74648849928 +     1052.26838318840*t);\r
-   jupiter_x_1+=     0.00033891193 * cos( 4.10113482752 +      529.69096509460*t);\r
-   jupiter_x_1+=     0.00031234303 * cos( 2.34698051502 +     1066.49547719000*t);\r
-   jupiter_x_1+=     0.00021244363 * cos( 4.36576178953 +      110.20632121940*t);\r
-   jupiter_x_1+=     0.00018156701 * cos( 4.00572238779 +      426.59819087600*t);\r
-   jupiter_x_1+=     0.00013577576 * cos( 0.30008010246 +      632.78373931320*t);\r
-   jupiter_x_1+=     0.00012889505 * cos( 2.57489294062 +      515.46387109300*t);\r
-   jupiter_x_1+=     0.00009125875 * cos( 1.78082469962 +      543.91805909620*t);\r
-   jupiter_x_1+=     0.00008085991 * cos( 6.16136518902 +      949.17560896980*t);\r
-   jupiter_x_1+=     0.00007142547 * cos( 3.17267801203 +      323.50541665740*t);\r
-   jupiter_x_1+=     0.00004292240 * cos( 4.74970626655 +      206.18554843720*t);\r
-   jupiter_x_1+=     0.00004393977 * cos( 1.14770788063 +      735.87651353180*t);\r
-   jupiter_x_1+=     0.00003399164 * cos( 2.90091450747 +      526.50957135690*t);\r
-   jupiter_x_1+=     0.00003333344 * cos( 1.43691652967 +      532.87235883230*t);\r
-   jupiter_x_1+=     0.00003873467 * cos( 3.33648870101 +       14.22709400160*t);\r
-   jupiter_x_1+=     0.00003044408 * cos( 1.65428048669 +      525.75881183150*t);\r
-   jupiter_x_1+=     0.00003001874 * cos( 2.68376982746 +      533.62311835770*t);\r
-   jupiter_x_1+=     0.00002933359 * cos( 2.61899855005 +      419.48464387520*t);\r
-   jupiter_x_1+=     0.00002438199 * cos( 3.60655644537 +      316.39186965660*t);\r
-   jupiter_x_1+=     0.00002804218 * cos( 4.89742591320 +      103.09277421860*t);\r
-   jupiter_x_1+=     0.00002990245 * cos( 0.80692155639 +     2118.76386037840*t);\r
-   jupiter_x_1+=     0.00001977572 * cos( 5.08915489088 +      956.28915597060*t);\r
-   jupiter_x_1+=     0.00001853679 * cos( 2.76941001747 +     1596.18644228460*t);\r
-   jupiter_x_1+=     0.00001772800 * cos( 0.72631739446 +      742.99006053260*t);\r
-   jupiter_x_1+=     0.00001812965 * cos( 3.84602148747 +       95.97922721780*t);\r
-   jupiter_x_1+=     0.00001532945 * cos( 4.31556714501 +      117.31986822020*t);\r
-   jupiter_x_1+=     0.00001904067 * cos( 1.85937873703 +     1581.95934828300*t);\r
-   jupiter_x_1+=     0.00001539212 * cos( 1.47899172821 +      639.89728631400*t);\r
-   jupiter_x_1+=     0.00001632362 * cos( 1.41504212408 +     1045.15483618760*t);\r
-   jupiter_x_1+=     0.00001023812 * cos( 2.57182697715 +      433.71173787680*t);\r
-   jupiter_x_1+=     0.00001055422 * cos( 2.50844222977 +     1265.56747862640*t);\r
-   jupiter_x_1=jupiter_x_1 * t;\r
-\r
-   double jupiter_x_2=0.0;\r
-   jupiter_x_2+=     0.00123864644 * cos( 4.13563277513 +      522.57741809380*t);\r
-   jupiter_x_2+=     0.00121521296 * cos( 0.21155109275 +      536.80451209540*t);\r
-   jupiter_x_2+=     0.00085355503 * cos( 0.00000000000 +        0.00000000000*t);\r
-   jupiter_x_2+=     0.00077685547 * cos( 5.29776154458 +      529.69096509460*t);\r
-   jupiter_x_2+=     0.00041410887 * cos( 5.12291589939 +     1059.38193018920*t);\r
-   jupiter_x_2+=     0.00011423070 * cos( 1.72917878238 +        7.11354700080*t);\r
-   jupiter_x_2+=     0.00007051587 * cos( 0.74163703419 +     1066.49547719000*t);\r
-   jupiter_x_2+=     0.00005711029 * cos( 3.63172846494 +     1052.26838318840*t);\r
-   jupiter_x_2+=     0.00005242644 * cos( 4.27482379441 +      515.46387109300*t);\r
-   jupiter_x_2+=     0.00004039540 * cos( 5.58417732117 +     1589.07289528380*t);\r
-   jupiter_x_2+=     0.00003706457 * cos( 0.07769981349 +      543.91805909620*t);\r
-   jupiter_x_2+=     0.00001698817 * cos( 2.44284418066 +      110.20632121940*t);\r
-   jupiter_x_2+=     0.00001134598 * cos( 2.35807061809 +      426.59819087600*t);\r
-   jupiter_x_2+=     0.00001322673 * cos( 1.63142549980 +       14.22709400160*t);\r
-   jupiter_x_2=jupiter_x_2 * t * t;\r
-\r
-   double jupiter_x_3=0.0;\r
-   jupiter_x_3+=     0.00017071323 * cos( 5.86133022278 +      522.57741809380*t);\r
-   jupiter_x_3+=     0.00016713548 * cos( 4.77458794485 +      536.80451209540*t);\r
-   jupiter_x_3+=     0.00003348610 * cos( 0.00000000000 +        0.00000000000*t);\r
-   jupiter_x_3+=     0.00001787838 * cos( 3.56550298031 +     1059.38193018920*t);\r
-   jupiter_x_3+=     0.00001435449 * cos( 5.98502036587 +      515.46387109300*t);\r
-   jupiter_x_3+=     0.00001080194 * cos( 5.42530305914 +     1066.49547719000*t);\r
-   jupiter_x_3+=     0.00001014206 * cos( 4.64773902077 +      543.91805909620*t);\r
-   jupiter_x_3+=     0.00001073175 * cos( 6.22314467964 +        7.11354700080*t);\r
-   jupiter_x_3=jupiter_x_3 * t * t * t;\r
-\r
-   double jupiter_x_4=0.0;\r
-   jupiter_x_4+=     0.00001762402 * cos( 1.32863039757 +      522.57741809380*t);\r
-   jupiter_x_4+=     0.00001717846 * cos( 3.03331531843 +      536.80451209540*t);\r
-   jupiter_x_4=jupiter_x_4 * t * t * t * t;\r
-\r
-   return jupiter_x_0+jupiter_x_1+jupiter_x_2+jupiter_x_3+jupiter_x_4;\r
-}\r
-\r
-double vsop87a_milli_jupiter_y(double t){\r
-   double jupiter_y_0=0.0;\r
-   jupiter_y_0+=     5.19520046589 * cos( 5.31203162731 +      529.69096509460*t);\r
-   jupiter_y_0+=     0.12592862602 * cos( 5.66160227728 +     1059.38193018920*t);\r
-   jupiter_y_0+=     0.09363670616 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_y_0+=     0.01508275299 * cos( 5.43934968102 +      522.57741809380*t);\r
-   jupiter_y_0+=     0.01475809370 * cos( 2.04679566495 +      536.80451209540*t);\r
-   jupiter_y_0+=     0.00457750806 * cos( 6.01129093501 +     1589.07289528380*t);\r
-   jupiter_y_0+=     0.00300686679 * cos( 3.60948050740 +        7.11354700080*t);\r
-   jupiter_y_0+=     0.00378285578 * cos( 3.53006782383 +      103.09277421860*t);\r
-   jupiter_y_0+=     0.00192333128 * cos( 3.45690564771 +      426.59819087600*t);\r
-   jupiter_y_0+=     0.00146104656 * cos( 4.62267224431 +      110.20632121940*t);\r
-   jupiter_y_0+=     0.00139480058 * cos( 4.00075307706 +      632.78373931320*t);\r
-   jupiter_y_0+=     0.00132696764 * cos( 5.62184581859 +      213.29909543800*t);\r
-   jupiter_y_0+=     0.00101999807 * cos( 4.57594598884 +     1052.26838318840*t);\r
-   jupiter_y_0+=     0.00114043110 * cos( 4.72982262969 +     1162.47470440780*t);\r
-   jupiter_y_0+=     0.00072091178 * cos( 2.39048659148 +     1066.49547719000*t);\r
-   jupiter_y_0+=     0.00059051769 * cos( 2.89529070968 +      949.17560896980*t);\r
-   jupiter_y_0+=     0.00068374489 * cos( 1.86537074374 +      846.08283475120*t);\r
-   jupiter_y_0+=     0.00029807369 * cos( 4.52105772740 +      206.18554843720*t);\r
-   jupiter_y_0+=     0.00026933579 * cos( 3.86233956827 +      419.48464387520*t);\r
-   jupiter_y_0+=     0.00026619714 * cos( 3.28203174951 +      323.50541665740*t);\r
-   jupiter_y_0+=     0.00020873780 * cos( 3.79369881757 +      735.87651353180*t);\r
-   jupiter_y_0+=     0.00019727397 * cos( 0.07818534532 +     2118.76386037840*t);\r
-   jupiter_y_0+=     0.00018639846 * cos( 0.38751972138 +      316.39186965660*t);\r
-   jupiter_y_0+=     0.00016355726 * cos( 5.56997881604 +      515.46387109300*t);\r
-   jupiter_y_0+=     0.00014606858 * cos( 0.47759399145 +      742.99006053260*t);\r
-   jupiter_y_0+=     0.00011419853 * cos( 1.91089341468 +      543.91805909620*t);\r
-   jupiter_y_0+=     0.00012153427 * cos( 2.18151972499 +      525.75881183150*t);\r
-   jupiter_y_0+=     0.00011988875 * cos( 5.29687602089 +      533.62311835770*t);\r
-   jupiter_y_0+=     0.00008443107 * cos( 1.91435801697 +      639.89728631400*t);\r
-   jupiter_y_0+=     0.00008163163 * cos( 4.00303742375 +     1478.86657406440*t);\r
-   jupiter_y_0+=     0.00008732789 * cos( 5.09607066097 +     1692.16566950240*t);\r
-   jupiter_y_0+=     0.00007414115 * cos( 4.41141990461 +      956.28915597060*t);\r
-   jupiter_y_0+=     0.00007619486 * cos( 5.59554151997 +     1265.56747862640*t);\r
-   jupiter_y_0+=     0.00007779184 * cos( 4.83346300662 +     1581.95934828300*t);\r
-   jupiter_y_0+=     0.00005322882 * cos( 5.81740472645 +      526.50957135690*t);\r
-   jupiter_y_0+=     0.00005217025 * cos( 1.66178643542 +      532.87235883230*t);\r
-   jupiter_y_0+=     0.00005772132 * cos( 3.46915716927 +       14.22709400160*t);\r
-   jupiter_y_0+=     0.00004528355 * cos( 2.18377558038 +     1375.77379984580*t);\r
-   jupiter_y_0+=     0.00003939875 * cos( 2.73830531054 +     1596.18644228460*t);\r
-   jupiter_y_0+=     0.00004567181 * cos( 3.71300776935 +       95.97922721780*t);\r
-   jupiter_y_0+=     0.00003235419 * cos( 4.76600347062 +      625.67019231240*t);\r
-   jupiter_y_0+=     0.00003140740 * cos( 5.59566796922 +      309.27832265580*t);\r
-   jupiter_y_0+=     0.00002855423 * cos( 4.44478286006 +      117.31986822020*t);\r
-   jupiter_y_0+=     0.00002445625 * cos( 2.67036952230 +      433.71173787680*t);\r
-   jupiter_y_0+=     0.00002253545 * cos( 4.28462825722 +      838.96928775040*t);\r
-   jupiter_y_0+=     0.00002672262 * cos( 1.61857897069 +     1169.58825140860*t);\r
-   jupiter_y_0+=     0.00002423639 * cos( 2.32942339839 +      220.41264243880*t);\r
-   jupiter_y_0+=     0.00002362662 * cos( 4.60417580207 +     1155.36115740700*t);\r
-   jupiter_y_0+=     0.00002409581 * cos( 4.33196301609 +     1045.15483618760*t);\r
-   jupiter_y_0+=     0.00001458169 * cos( 4.18761881277 +      853.19638175200*t);\r
-   jupiter_y_0+=     0.00001432195 * cos( 3.24824554500 +      942.06206196900*t);\r
-   jupiter_y_0+=     0.00001646568 * cos( 3.91965876562 +       74.78159856730*t);\r
-   jupiter_y_0+=     0.00001050270 * cos( 4.83706014327 +     1795.25844372100*t);\r
-   jupiter_y_0+=     0.00001002355 * cos( 1.50931939870 +     1272.68102562720*t);\r
-   jupiter_y_0+=     0.00001087727 * cos( 5.84673086939 +      527.24328453980*t);\r
-   jupiter_y_0+=     0.00001079512 * cos( 1.63448507346 +      532.13864564940*t);\r
-\r
-   double jupiter_y_1=0.0;\r
-   jupiter_y_1+=     0.01694798253 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_y_1+=     0.00634859798 * cos( 4.81903199650 +     1059.38193018920*t);\r
-   jupiter_y_1+=     0.00601160431 * cos( 0.85811249940 +      522.57741809380*t);\r
-   jupiter_y_1+=     0.00588928504 * cos( 0.34491576890 +      536.80451209540*t);\r
-   jupiter_y_1+=     0.00081187145 * cos( 1.90914316532 +        7.11354700080*t);\r
-   jupiter_y_1+=     0.00046888090 * cos( 1.91294535618 +      529.69096509460*t);\r
-   jupiter_y_1+=     0.00046194129 * cos( 5.16955994561 +     1589.07289528380*t);\r
-   jupiter_y_1+=     0.00032503453 * cos( 0.17640743623 +     1052.26838318840*t);\r
-   jupiter_y_1+=     0.00031231694 * cos( 0.77623645597 +     1066.49547719000*t);\r
-   jupiter_y_1+=     0.00019462096 * cos( 3.00957119470 +      110.20632121940*t);\r
-   jupiter_y_1+=     0.00017738615 * cos( 2.46531787101 +      426.59819087600*t);\r
-   jupiter_y_1+=     0.00013701692 * cos( 5.02070197804 +      632.78373931320*t);\r
-   jupiter_y_1+=     0.00013034616 * cos( 0.98979834442 +      515.46387109300*t);\r
-   jupiter_y_1+=     0.00009122660 * cos( 0.21022587969 +      543.91805909620*t);\r
-   jupiter_y_1+=     0.00008109050 * cos( 4.58123811601 +      949.17560896980*t);\r
-   jupiter_y_1+=     0.00007145229 * cos( 1.60381236094 +      323.50541665740*t);\r
-   jupiter_y_1+=     0.00003957592 * cos( 6.18550697817 +      206.18554843720*t);\r
-   jupiter_y_1+=     0.00004347346 * cos( 5.85522835488 +      735.87651353180*t);\r
-   jupiter_y_1+=     0.00003401735 * cos( 1.33033225252 +      526.50957135690*t);\r
-   jupiter_y_1+=     0.00003331887 * cos( 6.14951835712 +      532.87235883230*t);\r
-   jupiter_y_1+=     0.00003866147 * cos( 1.76877582038 +       14.22709400160*t);\r
-   jupiter_y_1+=     0.00003094257 * cos( 1.00670454701 +      419.48464387520*t);\r
-   jupiter_y_1+=     0.00003044205 * cos( 0.08329779827 +      525.75881183150*t);\r
-   jupiter_y_1+=     0.00003001484 * cos( 1.11280606283 +      533.62311835770*t);\r
-   jupiter_y_1+=     0.00002977284 * cos( 3.35507028507 +      103.09277421860*t);\r
-   jupiter_y_1+=     0.00002347100 * cos( 2.06781775390 +      316.39186965660*t);\r
-   jupiter_y_1+=     0.00002990192 * cos( 5.51944830506 +     2118.76386037840*t);\r
-   jupiter_y_1+=     0.00001875464 * cos( 5.32657356489 +      742.99006053260*t);\r
-   jupiter_y_1+=     0.00001854067 * cos( 1.19908734197 +     1596.18644228460*t);\r
-   jupiter_y_1+=     0.00001968401 * cos( 3.51896739844 +      956.28915597060*t);\r
-   jupiter_y_1+=     0.00001808627 * cos( 5.40287543026 +       95.97922721780*t);\r
-   jupiter_y_1+=     0.00001530472 * cos( 2.75094722237 +      117.31986822020*t);\r
-   jupiter_y_1+=     0.00001885393 * cos( 0.29905973710 +     1581.95934828300*t);\r
-   jupiter_y_1+=     0.00001516541 * cos( 6.21684203571 +      639.89728631400*t);\r
-   jupiter_y_1+=     0.00001636913 * cos( 6.09270756447 +     1045.15483618760*t);\r
-   jupiter_y_1+=     0.00001260123 * cos( 0.07143173954 +      625.67019231240*t);\r
-   jupiter_y_1+=     0.00001028165 * cos( 1.00301485824 +      433.71173787680*t);\r
-   jupiter_y_1+=     0.00001035933 * cos( 0.98273794152 +     1265.56747862640*t);\r
-   jupiter_y_1=jupiter_y_1 * t;\r
-\r
-   double jupiter_y_2=0.0;\r
-   jupiter_y_2+=     0.00124032509 * cos( 2.56495576833 +      522.57741809380*t);\r
-   jupiter_y_2+=     0.00121455991 * cos( 4.92398766380 +      536.80451209540*t);\r
-   jupiter_y_2+=     0.00076523263 * cos( 3.75913371793 +      529.69096509460*t);\r
-   jupiter_y_2+=     0.00076943042 * cos( 3.14159265359 +        0.00000000000*t);\r
-   jupiter_y_2+=     0.00041357600 * cos( 3.55228440457 +     1059.38193018920*t);\r
-   jupiter_y_2+=     0.00011277667 * cos( 0.18559902389 +        7.11354700080*t);\r
-   jupiter_y_2+=     0.00007051103 * cos( 5.45404368570 +     1066.49547719000*t);\r
-   jupiter_y_2+=     0.00005719440 * cos( 2.05970000230 +     1052.26838318840*t);\r
-   jupiter_y_2+=     0.00005286157 * cos( 2.69490465064 +      515.46387109300*t);\r
-   jupiter_y_2+=     0.00004039038 * cos( 4.01341034637 +     1589.07289528380*t);\r
-   jupiter_y_2+=     0.00003704528 * cos( 4.79029292271 +      543.91805909620*t);\r
-   jupiter_y_2+=     0.00001280283 * cos( 1.47574006861 +      110.20632121940*t);\r
-   jupiter_y_2+=     0.00001059783 * cos( 0.89610748176 +      426.59819087600*t);\r
-   jupiter_y_2+=     0.00001320627 * cos( 0.05786048417 +       14.22709400160*t);\r
-   jupiter_y_2=jupiter_y_2 * t * t;\r
-\r
-   double jupiter_y_3=0.0;\r
-   jupiter_y_3+=     0.00017085516 * cos( 4.29096904063 +      522.57741809380*t);\r
-   jupiter_y_3+=     0.00016701353 * cos( 3.20365737109 +      536.80451209540*t);\r
-   jupiter_y_3+=     0.00004006038 * cos( 0.00000000000 +        0.00000000000*t);\r
-   jupiter_y_3+=     0.00001782451 * cos( 1.99283071153 +     1059.38193018920*t);\r
-   jupiter_y_3+=     0.00001443816 * cos( 4.40866555269 +      515.46387109300*t);\r
-   jupiter_y_3+=     0.00001079405 * cos( 3.85450799252 +     1066.49547719000*t);\r
-   jupiter_y_3+=     0.00001013157 * cos( 3.07729621279 +      543.91805909620*t);\r
-   jupiter_y_3+=     0.00001055565 * cos( 4.70184773789 +        7.11354700080*t);\r
-   jupiter_y_3=jupiter_y_3 * t * t * t;\r
-\r
-   double jupiter_y_4=0.0;\r
-   jupiter_y_4+=     0.00001762645 * cos( 6.04159386554 +      522.57741809380*t);\r
-   jupiter_y_4+=     0.00001716045 * cos( 1.46206285710 +      536.80451209540*t);\r
-   jupiter_y_4=jupiter_y_4 * t * t * t * t;\r
-\r
-   return jupiter_y_0+jupiter_y_1+jupiter_y_2+jupiter_y_3+jupiter_y_4;\r
-}\r
-\r
-double vsop87a_milli_jupiter_z(double t){\r
-   double jupiter_z_0=0.0;\r
-   jupiter_z_0+=     0.11823100489 * cos( 3.55844646343 +      529.69096509460*t);\r
-   jupiter_z_0+=     0.00859031952 * cos( 0.00000000000 +        0.00000000000*t);\r
-   jupiter_z_0+=     0.00286562094 * cos( 3.90812238338 +     1059.38193018920*t);\r
-   jupiter_z_0+=     0.00042388592 * cos( 3.60144191032 +      522.57741809380*t);\r
-   jupiter_z_0+=     0.00033295491 * cos( 0.30297050585 +      536.80451209540*t);\r
-   jupiter_z_0+=     0.00010416160 * cos( 4.25764593061 +     1589.07289528380*t);\r
-   jupiter_z_0+=     0.00007449294 * cos( 5.24213104150 +      103.09277421860*t);\r
-   jupiter_z_0+=     0.00006910102 * cos( 1.75032945752 +        7.11354700080*t);\r
-   jupiter_z_0+=     0.00005292012 * cos( 1.68231447192 +      426.59819087600*t);\r
-   jupiter_z_0+=     0.00004313598 * cos( 3.70673689841 +      213.29909543800*t);\r
-   jupiter_z_0+=     0.00003784265 * cos( 2.71522544491 +      110.20632121940*t);\r
-   jupiter_z_0+=     0.00003798016 * cos( 2.16715743175 +      632.78373931320*t);\r
-   jupiter_z_0+=     0.00002455385 * cos( 2.96904135659 +     1052.26838318840*t);\r
-   jupiter_z_0+=     0.00002461547 * cos( 2.99889460411 +     1162.47470440780*t);\r
-   jupiter_z_0+=     0.00002001451 * cos( 2.68535838309 +      419.48464387520*t);\r
-   jupiter_z_0+=     0.00002163471 * cos( 6.26718259854 +      846.08283475120*t);\r
-   jupiter_z_0+=     0.00001633653 * cos( 0.64194743493 +     1066.49547719000*t);\r
-   jupiter_z_0+=     0.00001450672 * cos( 1.17108416193 +      949.17560896980*t);\r
-\r
-   double jupiter_z_1=0.0;\r
-   jupiter_z_1+=     0.00407072175 * cos( 1.52699353482 +      529.69096509460*t);\r
-   jupiter_z_1+=     0.00020307341 * cos( 2.59878269248 +     1059.38193018920*t);\r
-   jupiter_z_1+=     0.00014424953 * cos( 4.85400155025 +      536.80451209540*t);\r
-   jupiter_z_1+=     0.00015474611 * cos( 0.00000000000 +        0.00000000000*t);\r
-   jupiter_z_1+=     0.00012730364 * cos( 5.45536715732 +      522.57741809380*t);\r
-   jupiter_z_1+=     0.00002100882 * cos( 0.09538864287 +        7.11354700080*t);\r
-   jupiter_z_1+=     0.00001230425 * cos( 3.14222500244 +     1589.07289528380*t);\r
-   jupiter_z_1=jupiter_z_1 * t;\r
-\r
-   double jupiter_z_2=0.0;\r
-   jupiter_z_2+=     0.00028635326 * cos( 3.01374166973 +      529.69096509460*t);\r
-   jupiter_z_2+=     0.00003114752 * cos( 3.13228646176 +      536.80451209540*t);\r
-   jupiter_z_2+=     0.00002379765 * cos( 0.95574345340 +      522.57741809380*t);\r
-   jupiter_z_2+=     0.00001310111 * cos( 2.05263704913 +     1059.38193018920*t);\r
-   jupiter_z_2=jupiter_z_2 * t * t;\r
-\r
-   return jupiter_z_0+jupiter_z_1+jupiter_z_2;\r
-}\r
-\r
-double vsop87a_milli_mars_x(double t){\r
-   double mars_x_0=0.0;\r
-   mars_x_0+=     1.51769936383 * cos( 6.20403346548 +     3340.61242669980*t);\r
-   mars_x_0+=     0.19502945246 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_x_0+=     0.07070919655 * cos( 0.25870338558 +     6681.22485339960*t);\r
-   mars_x_0+=     0.00494196914 * cos( 0.59669127768 +    10021.83728009940*t);\r
-   mars_x_0+=     0.00040938237 * cos( 0.93473307419 +    13362.44970679920*t);\r
-   mars_x_0+=     0.00021067199 * cos( 1.80435656154 +     3337.08930835080*t);\r
-   mars_x_0+=     0.00021041626 * cos( 1.17895619474 +     3344.13554504880*t);\r
-   mars_x_0+=     0.00011370375 * cos( 4.83265211109 +     1059.38193018920*t);\r
-   mars_x_0+=     0.00013527976 * cos( 0.63010765169 +      529.69096509460*t);\r
-   mars_x_0+=     0.00006774107 * cos( 3.61785048282 +     3340.59517304760*t);\r
-   mars_x_0+=     0.00006774060 * cos( 5.64862211431 +     3340.62968035200*t);\r
-   mars_x_0+=     0.00008226069 * cos( 1.86843519535 +     2281.23049651060*t);\r
-   mars_x_0+=     0.00005469046 * cos( 1.13324429003 +     2942.46342329160*t);\r
-   mars_x_0+=     0.00004817134 * cos( 1.85091045536 +     3738.76143010800*t);\r
-   mars_x_0+=     0.00004937579 * cos( 4.43241440654 +     5621.84292321040*t);\r
-   mars_x_0+=     0.00005276260 * cos( 2.33148083116 +     6151.53388830500*t);\r
-   mars_x_0+=     0.00003636667 * cos( 6.11397592106 +      796.29800681640*t);\r
-   mars_x_0+=     0.00003725823 * cos( 1.27280182943 +    16703.06213349900*t);\r
-   mars_x_0+=     0.00003729746 * cos( 1.21398323637 +      398.14900340820*t);\r
-   mars_x_0+=     0.00002368513 * cos( 2.96841895360 +     2544.31441988340*t);\r
-   mars_x_0+=     0.00002397865 * cos( 0.63553674054 +     3149.16416058820*t);\r
-   mars_x_0+=     0.00002274646 * cos( 2.35708328853 +     3532.06069281140*t);\r
-   mars_x_0+=     0.00001977579 * cos( 2.14087826110 +     6677.70173505060*t);\r
-   mars_x_0+=     0.00002229176 * cos( 1.69588962513 +     3340.54511639700*t);\r
-   mars_x_0+=     0.00002229117 * cos( 1.28739323821 +     3340.67973700260*t);\r
-   mars_x_0+=     0.00002182206 * cos( 1.69655112969 +     6283.07584999140*t);\r
-   mars_x_0+=     0.00002241010 * cos( 4.82218655311 +     8962.45534991020*t);\r
-   mars_x_0+=     0.00001677693 * cos( 3.14442612046 +     5884.92684658320*t);\r
-   mars_x_0+=     0.00001630482 * cos( 0.24117974845 +     4136.91043351620*t);\r
-   mars_x_0+=     0.00001958162 * cos( 1.51914544555 +     6684.74797174860*t);\r
-   mars_x_0+=     0.00001378470 * cos( 2.18011900021 +     1751.53953141600*t);\r
-   mars_x_0+=     0.00001289804 * cos( 4.70970778621 +     1194.44701022460*t);\r
-   mars_x_0+=     0.00001468124 * cos( 1.87869730543 +     3870.30339179440*t);\r
-   mars_x_0+=     0.00001290170 * cos( 0.43596325296 +     2810.92146160520*t);\r
-   mars_x_0+=     0.00001572540 * cos( 4.84809921789 +     1589.07289528380*t);\r
-\r
-   double mars_x_1=0.0;\r
-   mars_x_1+=     0.00861441374 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_x_1+=     0.00552437949 * cos( 5.09565872891 +     6681.22485339960*t);\r
-   mars_x_1+=     0.00077184977 * cos( 5.43315636209 +    10021.83728009940*t);\r
-   mars_x_1+=     0.00020467294 * cos( 5.57051812369 +     3340.61242669980*t);\r
-   mars_x_1+=     0.00009589581 * cos( 5.77107234791 +    13362.44970679920*t);\r
-   mars_x_1+=     0.00002620610 * cos( 6.22441295122 +     3344.13554504880*t);\r
-   mars_x_1+=     0.00002620537 * cos( 3.04172154436 +     3337.08930835080*t);\r
-   mars_x_1+=     0.00001163612 * cos( 6.10909257097 +    16703.06213349900*t);\r
-   mars_x_1=mars_x_1 * t;\r
-\r
-   double mars_x_2=0.0;\r
-   mars_x_2+=     0.00056323939 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mars_x_2+=     0.00022122528 * cos( 3.54372113272 +     6681.22485339960*t);\r
-   mars_x_2+=     0.00006091409 * cos( 3.93272649649 +    10021.83728009940*t);\r
-   mars_x_2+=     0.00001451998 * cos( 3.64655666460 +     3340.61242669980*t);\r
-   mars_x_2+=     0.00001130613 * cos( 4.28827023222 +    13362.44970679920*t);\r
-   mars_x_2=mars_x_2 * t * t;\r
-\r
-   return mars_x_0+mars_x_1+mars_x_2;\r
-}\r
-\r
-double vsop87a_milli_mars_y(double t){\r
-   double mars_y_0=0.0;\r
-   mars_y_0+=     1.51558976277 * cos( 4.63212206588 +     3340.61242669980*t);\r
-   mars_y_0+=     0.07064550239 * cos( 4.97051892902 +     6681.22485339960*t);\r
-   mars_y_0+=     0.08655481102 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mars_y_0+=     0.00493872848 * cos( 5.30877806694 +    10021.83728009940*t);\r
-   mars_y_0+=     0.00040917422 * cos( 5.64698263703 +    13362.44970679920*t);\r
-   mars_y_0+=     0.00021036784 * cos( 0.23240270955 +     3337.08930835080*t);\r
-   mars_y_0+=     0.00021012921 * cos( 5.89022773653 +     3344.13554504880*t);\r
-   mars_y_0+=     0.00011370034 * cos( 3.26131408801 +     1059.38193018920*t);\r
-   mars_y_0+=     0.00013324177 * cos( 5.34259389724 +      529.69096509460*t);\r
-   mars_y_0+=     0.00006764653 * cos( 4.07671230062 +     3340.62968035200*t);\r
-   mars_y_0+=     0.00006764700 * cos( 2.04594066912 +     3340.59517304760*t);\r
-   mars_y_0+=     0.00008346220 * cos( 3.42464704002 +     2281.23049651060*t);\r
-   mars_y_0+=     0.00005400042 * cos( 5.81507793194 +     2942.46342329160*t);\r
-   mars_y_0+=     0.00004809528 * cos( 0.27875310553 +     3738.76143010800*t);\r
-   mars_y_0+=     0.00004849523 * cos( 2.85190987550 +     5621.84292321040*t);\r
-   mars_y_0+=     0.00005263268 * cos( 0.75811089992 +     6151.53388830500*t);\r
-   mars_y_0+=     0.00003609527 * cos( 4.53244488294 +      796.29800681640*t);\r
-   mars_y_0+=     0.00003724293 * cos( 5.98516013322 +    16703.06213349900*t);\r
-   mars_y_0+=     0.00003805073 * cos( 5.94234296399 +      398.14900340820*t);\r
-   mars_y_0+=     0.00002394490 * cos( 5.34678816191 +     3149.16416058820*t);\r
-   mars_y_0+=     0.00002251027 * cos( 0.76938193892 +     3532.06069281140*t);\r
-   mars_y_0+=     0.00001975769 * cos( 0.56949816579 +     6677.70173505060*t);\r
-   mars_y_0+=     0.00002226030 * cos( 5.99867316288 +     3340.67973700260*t);\r
-   mars_y_0+=     0.00002226089 * cos( 0.12398424247 +     3340.54511639700*t);\r
-   mars_y_0+=     0.00002177591 * cos( 0.12334436516 +     6283.07584999140*t);\r
-   mars_y_0+=     0.00001690439 * cos( 1.58331163985 +     5884.92684658320*t);\r
-   mars_y_0+=     0.00002234121 * cos( 3.24909113765 +     8962.45534991020*t);\r
-   mars_y_0+=     0.00001628395 * cos( 4.95250906888 +     4136.91043351620*t);\r
-   mars_y_0+=     0.00001956411 * cos( 6.23095843554 +     6684.74797174860*t);\r
-   mars_y_0+=     0.00001697214 * cos( 0.81869636263 +     2544.31441988340*t);\r
-   mars_y_0+=     0.00001385946 * cos( 3.73437191158 +     1751.53953141600*t);\r
-   mars_y_0+=     0.00001439619 * cos( 5.19505958438 +     2810.92146160520*t);\r
-   mars_y_0+=     0.00001281890 * cos( 3.13035275682 +     1194.44701022460*t);\r
-   mars_y_0+=     0.00001469783 * cos( 0.30415060688 +     3870.30339179440*t);\r
-   mars_y_0+=     0.00001571880 * cos( 3.27679498650 +     1589.07289528380*t);\r
-   mars_y_0+=     0.00001575854 * cos( 2.78266835243 +     5092.15195811580*t);\r
-\r
-   double mars_y_1=0.0;\r
-   mars_y_1+=     0.01427324210 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_y_1+=     0.00551063753 * cos( 3.52128320402 +     6681.22485339960*t);\r
-   mars_y_1+=     0.00077091913 * cos( 3.86082685753 +    10021.83728009940*t);\r
-   mars_y_1+=     0.00037310491 * cos( 1.16016958445 +     3340.61242669980*t);\r
-   mars_y_1+=     0.00009582122 * cos( 4.19942354479 +    13362.44970679920*t);\r
-   mars_y_1+=     0.00002617695 * cos( 1.47284555520 +     3337.08930835080*t);\r
-   mars_y_1+=     0.00002611572 * cos( 4.65030772498 +     3344.13554504880*t);\r
-   mars_y_1+=     0.00001162955 * cos( 4.53778503576 +    16703.06213349900*t);\r
-   mars_y_1=mars_y_1 * t;\r
-\r
-   double mars_y_2=0.0;\r
-   mars_y_2+=     0.00035396765 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_y_2+=     0.00021950759 * cos( 1.96291594946 +     6681.22485339960*t);\r
-   mars_y_2+=     0.00006075990 * cos( 2.35864321001 +    10021.83728009940*t);\r
-   mars_y_2+=     0.00002571425 * cos( 5.64795745327 +     3340.61242669980*t);\r
-   mars_y_2+=     0.00001129099 * cos( 2.71576248963 +    13362.44970679920*t);\r
-   mars_y_2=mars_y_2 * t * t;\r
-\r
-   double mars_y_3=0.0;\r
-   mars_y_3+=     0.00001448778 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mars_y_3=mars_y_3 * t * t * t;\r
-\r
-   return mars_y_0+mars_y_1+mars_y_2+mars_y_3;\r
-}\r
-\r
-double vsop87a_milli_mars_z(double t){\r
-   double mars_z_0=0.0;\r
-   mars_z_0+=     0.04901207220 * cos( 3.76712324286 +     3340.61242669980*t);\r
-   mars_z_0+=     0.00660669541 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mars_z_0+=     0.00228333904 * cos( 4.10544022266 +     6681.22485339960*t);\r
-   mars_z_0+=     0.00015958402 * cos( 4.44367058261 +    10021.83728009940*t);\r
-   mars_z_0+=     0.00001321976 * cos( 4.78186604114 +    13362.44970679920*t);\r
-\r
-   double mars_z_1=0.0;\r
-   mars_z_1+=     0.00331842958 * cos( 6.05027773492 +     3340.61242669980*t);\r
-   mars_z_1+=     0.00047930411 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_z_1+=     0.00009896501 * cos( 1.61155844715 +     6681.22485339960*t);\r
-   mars_z_1+=     0.00001700147 * cos( 2.63703242065 +    10021.83728009940*t);\r
-   mars_z_1=mars_z_1 * t;\r
-\r
-   double mars_z_2=0.0;\r
-   mars_z_2+=     0.00013705360 * cos( 1.04212852598 +     3340.61242669980*t);\r
-   mars_z_2+=     0.00005931596 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mars_z_2=mars_z_2 * t * t;\r
-\r
-   return mars_z_0+mars_z_1+mars_z_2;\r
-}\r
-\r
-double vsop87a_milli_mercury_x(double t){\r
-   double mercury_x_0=0.0;\r
-   mercury_x_0+=     0.37546291728 * cos( 4.39651506942 +    26087.90314157420*t);\r
-   mercury_x_0+=     0.03825746672 * cos( 1.16485604339 +    52175.80628314840*t);\r
-   mercury_x_0+=     0.02625615963 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_x_0+=     0.00584261333 * cos( 4.21599394757 +    78263.70942472259*t);\r
-   mercury_x_0+=     0.00105716695 * cos( 0.98379033182 +   104351.61256629678*t);\r
-   mercury_x_0+=     0.00021011730 * cos( 4.03469353923 +   130439.51570787099*t);\r
-   mercury_x_0+=     0.00004433373 * cos( 0.80236674527 +   156527.41884944518*t);\r
-\r
-   double mercury_x_1=0.0;\r
-   mercury_x_1+=     0.00318848034 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mercury_x_1+=     0.00105289019 * cos( 5.91600475006 +    52175.80628314840*t);\r
-   mercury_x_1+=     0.00032316001 * cos( 2.68247273347 +    78263.70942472259*t);\r
-   mercury_x_1+=     0.00011992889 * cos( 5.81575112963 +    26087.90314157420*t);\r
-   mercury_x_1+=     0.00008783200 * cos( 5.73285747425 +   104351.61256629678*t);\r
-   mercury_x_1+=     0.00002329042 * cos( 2.50023793407 +   130439.51570787099*t);\r
-   mercury_x_1=mercury_x_1 * t;\r
-\r
-   double mercury_x_2=0.0;\r
-   mercury_x_2+=     0.00001484185 * cos( 4.35401210269 +    52175.80628314840*t);\r
-   mercury_x_2+=     0.00001214995 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mercury_x_2=mercury_x_2 * t * t;\r
-\r
-   return mercury_x_0+mercury_x_1+mercury_x_2;\r
-}\r
-\r
-double vsop87a_milli_mercury_y(double t){\r
-   double mercury_y_0=0.0;\r
-   mercury_y_0+=     0.37953642888 * cos( 2.83780617820 +    26087.90314157420*t);\r
-   mercury_y_0+=     0.11626131831 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_y_0+=     0.03854668215 * cos( 5.88780608966 +    52175.80628314840*t);\r
-   mercury_y_0+=     0.00587711268 * cos( 2.65498896201 +    78263.70942472259*t);\r
-   mercury_y_0+=     0.00106235493 * cos( 5.70550616735 +   104351.61256629678*t);\r
-   mercury_y_0+=     0.00021100828 * cos( 2.47291315849 +   130439.51570787099*t);\r
-   mercury_y_0+=     0.00004450056 * cos( 5.52354907071 +   156527.41884944518*t);\r
-\r
-   double mercury_y_1=0.0;\r
-   mercury_y_1+=     0.00107803852 * cos( 4.34964793883 +    52175.80628314840*t);\r
-   mercury_y_1+=     0.00080651544 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_y_1+=     0.00032715354 * cos( 1.11763734425 +    78263.70942472259*t);\r
-   mercury_y_1+=     0.00008858158 * cos( 4.16852401867 +   104351.61256629678*t);\r
-   mercury_y_1+=     0.00011914709 * cos( 1.22139986340 +    26087.90314157420*t);\r
-   mercury_y_1+=     0.00002344469 * cos( 0.93615372641 +   130439.51570787099*t);\r
-   mercury_y_1=mercury_y_1 * t;\r
-\r
-   double mercury_y_2=0.0;\r
-   mercury_y_2+=     0.00004612157 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mercury_y_2+=     0.00001575670 * cos( 2.81172733349 +    52175.80628314840*t);\r
-   mercury_y_2=mercury_y_2 * t * t;\r
-\r
-   return mercury_y_0+mercury_y_1+mercury_y_2;\r
-}\r
-\r
-double vsop87a_milli_mercury_z(double t){\r
-   double mercury_z_0=0.0;\r
-   mercury_z_0+=     0.04607665326 * cos( 1.99295081967 +    26087.90314157420*t);\r
-   mercury_z_0+=     0.00708734365 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_z_0+=     0.00469171617 * cos( 5.04215742764 +    52175.80628314840*t);\r
-   mercury_z_0+=     0.00071626395 * cos( 1.80894256071 +    78263.70942472259*t);\r
-   mercury_z_0+=     0.00012957446 * cos( 4.85922032010 +   104351.61256629678*t);\r
-   mercury_z_0+=     0.00002575002 * cos( 1.62646731545 +   130439.51570787099*t);\r
-\r
-   double mercury_z_1=0.0;\r
-   mercury_z_1+=     0.00108722177 * cos( 3.91134750825 +    26087.90314157420*t);\r
-   mercury_z_1+=     0.00057826621 * cos( 3.14159265359 +        0.00000000000*t);\r
-   mercury_z_1+=     0.00004297352 * cos( 2.56373047177 +    52175.80628314840*t);\r
-   mercury_z_1+=     0.00002435833 * cos( 0.05112640506 +    78263.70942472259*t);\r
-   mercury_z_1=mercury_z_1 * t;\r
-\r
-   double mercury_z_2=0.0;\r
-   mercury_z_2+=     0.00001053118 * cos( 5.37979214357 +    26087.90314157420*t);\r
-   mercury_z_2+=     0.00001185024 * cos( 0.00000000000 +        0.00000000000*t);\r
-   mercury_z_2=mercury_z_2 * t * t;\r
-\r
-   return mercury_z_0+mercury_z_1+mercury_z_2;\r
-}\r
-\r
-double vsop87a_milli_neptune_x(double t){\r
-   double neptune_x_0=0.0;\r
-   neptune_x_0+=    30.05890004476 * cos( 5.31211340029 +       38.13303563780*t);\r
-   neptune_x_0+=     0.27080164222 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_x_0+=     0.13505661755 * cos( 3.50078975634 +       76.26607127560*t);\r
-   neptune_x_0+=     0.15726094556 * cos( 0.11319072675 +       36.64856292950*t);\r
-   neptune_x_0+=     0.14935120126 * cos( 1.08499403018 +       39.61750834610*t);\r
-   neptune_x_0+=     0.02597313814 * cos( 1.99590301412 +        1.48447270830*t);\r
-   neptune_x_0+=     0.01074040708 * cos( 5.38502938672 +       74.78159856730*t);\r
-   neptune_x_0+=     0.00823793287 * cos( 1.43221581862 +       35.16409022120*t);\r
-   neptune_x_0+=     0.00817588813 * cos( 0.78180174031 +        2.96894541660*t);\r
-   neptune_x_0+=     0.00565534918 * cos( 5.98964907613 +       41.10198105440*t);\r
-   neptune_x_0+=     0.00495719107 * cos( 0.59948143567 +      529.69096509460*t);\r
-   neptune_x_0+=     0.00307525907 * cos( 0.40023311011 +       73.29712585900*t);\r
-   neptune_x_0+=     0.00272253551 * cos( 0.87443494387 +      213.29909543800*t);\r
-   neptune_x_0+=     0.00135887219 * cos( 5.54676577816 +       77.75054398390*t);\r
-   neptune_x_0+=     0.00090965704 * cos( 1.68910246115 +      114.39910691340*t);\r
-   neptune_x_0+=     0.00069040539 * cos( 5.83469123520 +        4.45341812490*t);\r
-   neptune_x_0+=     0.00060813556 * cos( 2.62589958380 +       33.67961751290*t);\r
-   neptune_x_0+=     0.00054690827 * cos( 1.55799996661 +       71.81265315070*t);\r
-   neptune_x_0+=     0.00028889260 * cos( 4.78966826027 +       42.58645376270*t);\r
-   neptune_x_0+=     0.00012614732 * cos( 3.57002516434 +      112.91463420510*t);\r
-   neptune_x_0+=     0.00012749153 * cos( 2.73719269645 +      111.43016149680*t);\r
-   neptune_x_0+=     0.00012013994 * cos( 0.94912933496 +     1059.38193018920*t);\r
-   neptune_x_0+=     0.00007540650 * cos( 2.77783477855 +       70.32818044240*t);\r
-   neptune_x_0+=     0.00007573383 * cos( 0.10011329853 +      426.59819087600*t);\r
-   neptune_x_0+=     0.00008004318 * cos( 1.63965626260 +      108.46121608020*t);\r
-   neptune_x_0+=     0.00006464842 * cos( 4.62580066013 +        5.93789083320*t);\r
-   neptune_x_0+=     0.00005565860 * cos( 3.82502185953 +       32.19514480460*t);\r
-   neptune_x_0+=     0.00004654361 * cos( 0.10385887980 +       37.61177077600*t);\r
-   neptune_x_0+=     0.00004732434 * cos( 4.09723977191 +       79.23501669220*t);\r
-   neptune_x_0+=     0.00004557247 * cos( 1.09712669317 +       38.65430049960*t);\r
-   neptune_x_0+=     0.00004322550 * cos( 2.37744780188 +       38.08485152800*t);\r
-   neptune_x_0+=     0.00004315539 * cos( 5.10473142056 +       38.18121974760*t);\r
-   neptune_x_0+=     0.00004089036 * cos( 1.99429048244 +       37.16982779130*t);\r
-   neptune_x_0+=     0.00004249674 * cos( 5.63324475823 +       28.57180808220*t);\r
-   neptune_x_0+=     0.00003920412 * cos( 5.49263784865 +       39.09624348430*t);\r
-   neptune_x_0+=     0.00003951848 * cos( 2.29996934110 +       98.89998852460*t);\r
-   neptune_x_0+=     0.00003322735 * cos( 4.68798591938 +        4.19278569400*t);\r
-   neptune_x_0+=     0.00003108292 * cos( 1.84434543409 +       47.69426319340*t);\r
-   neptune_x_0+=     0.00003260095 * cos( 1.81839652878 +      145.10977900970*t);\r
-   neptune_x_0+=     0.00002723442 * cos( 3.82296285941 +      109.94568878850*t);\r
-   neptune_x_0+=     0.00002522938 * cos( 4.66296126912 +      312.19908396260*t);\r
-   neptune_x_0+=     0.00001887430 * cos( 3.20485417792 +       35.68535508300*t);\r
-   neptune_x_0+=     0.00001648985 * cos( 4.06990666591 +       30.05628079050*t);\r
-   neptune_x_0+=     0.00001826700 * cos( 3.58024318649 +       44.07092647100*t);\r
-   neptune_x_0+=     0.00001945462 * cos( 4.15326825288 +      206.18554843720*t);\r
-   neptune_x_0+=     0.00001681255 * cos( 4.27560304282 +       40.58071619260*t);\r
-   neptune_x_0+=     0.00001533383 * cos( 1.17732211665 +       38.02116105320*t);\r
-   neptune_x_0+=     0.00001891892 * cos( 0.74998855536 +      220.41264243880*t);\r
-   neptune_x_0+=     0.00001527526 * cos( 0.02173640246 +       38.24491022240*t);\r
-   neptune_x_0+=     0.00002084477 * cos( 1.56821671369 +      149.56319713460*t);\r
-   neptune_x_0+=     0.00002083682 * cos( 2.83676961811 +      137.03302416240*t);\r
-   neptune_x_0+=     0.00001615063 * cos( 2.91063835010 +      106.97674337190*t);\r
-   neptune_x_0+=     0.00001265797 * cos( 3.42037275447 +       46.20979048510*t);\r
-   neptune_x_0+=     0.00001560429 * cos( 0.55865739143 +       37.87240320690*t);\r
-   neptune_x_0+=     0.00001545705 * cos( 0.64028780672 +       38.39366806870*t);\r
-   neptune_x_0+=     0.00001434798 * cos( 0.72658718863 +      522.57741809380*t);\r
-   neptune_x_0+=     0.00001271543 * cos( 2.74412981229 +       33.94024994380*t);\r
-   neptune_x_0+=     0.00001407422 * cos( 3.61743288666 +      536.80451209540*t);\r
-   neptune_x_0+=     0.00001387922 * cos( 3.71814330952 +      115.88357962170*t);\r
-   neptune_x_0+=     0.00001228939 * cos( 2.78878211792 +       72.07328558160*t);\r
-   neptune_x_0+=     0.00001448439 * cos( 1.98814317259 +      181.75834193920*t);\r
-   neptune_x_0+=     0.00001170078 * cos( 3.98594689041 +        8.07675484730*t);\r
-   neptune_x_0+=     0.00001080795 * cos( 4.75485636019 +       42.32582133180*t);\r
-   neptune_x_0+=     0.00001220341 * cos( 2.64791449584 +      148.07872442630*t);\r
-\r
-   double neptune_x_1=0.0;\r
-   neptune_x_1+=     0.00255840261 * cos( 2.01935686795 +       36.64856292950*t);\r
-   neptune_x_1+=     0.00243125299 * cos( 5.46214902873 +       39.61750834610*t);\r
-   neptune_x_1+=     0.00118398168 * cos( 2.88251845061 +       76.26607127560*t);\r
-   neptune_x_1+=     0.00037965449 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_x_1+=     0.00021924705 * cos( 3.20156164152 +       35.16409022120*t);\r
-   neptune_x_1+=     0.00017459808 * cos( 4.26349398817 +       41.10198105440*t);\r
-   neptune_x_1+=     0.00013130617 * cos( 5.36424961848 +        2.96894541660*t);\r
-   neptune_x_1+=     0.00005086527 * cos( 1.92377354729 +       38.13303563780*t);\r
-   neptune_x_1+=     0.00004899718 * cos( 2.09349497813 +       73.29712585900*t);\r
-   neptune_x_1+=     0.00002745267 * cos( 4.06252818667 +       77.75054398390*t);\r
-   neptune_x_1+=     0.00002204414 * cos( 4.38855639521 +       33.67961751290*t);\r
-   neptune_x_1+=     0.00002168719 * cos( 4.11768012563 +        4.45341812490*t);\r
-   neptune_x_1+=     0.00001572202 * cos( 1.07606611589 +      114.39910691340*t);\r
-   neptune_x_1+=     0.00001344022 * cos( 3.03802059051 +       42.58645376270*t);\r
-   neptune_x_1+=     0.00001285542 * cos( 6.02367554997 +       74.78159856730*t);\r
-   neptune_x_1=neptune_x_1 * t;\r
-\r
-   double neptune_x_2=0.0;\r
-   neptune_x_2+=     0.00005371138 * cos( 0.00000000000 +        0.00000000000*t);\r
-   neptune_x_2+=     0.00004536283 * cos( 5.02700751836 +       36.64856292950*t);\r
-   neptune_x_2+=     0.00004350766 * cos( 2.45420254304 +       39.61750834610*t);\r
-   neptune_x_2+=     0.00003092965 * cos( 0.62250463031 +       38.13303563780*t);\r
-   neptune_x_2+=     0.00002163703 * cos( 1.79218168368 +       76.26607127560*t);\r
-   neptune_x_2=neptune_x_2 * t * t;\r
-\r
-   return neptune_x_0+neptune_x_1+neptune_x_2;\r
-}\r
-\r
-double vsop87a_milli_neptune_y(double t){\r
-   double neptune_y_0=0.0;\r
-   neptune_y_0+=    30.06056351665 * cos( 3.74086294714 +       38.13303563780*t);\r
-   neptune_y_0+=     0.30205857683 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_y_0+=     0.13506391797 * cos( 1.92953034883 +       76.26607127560*t);\r
-   neptune_y_0+=     0.15706589373 * cos( 4.82539970129 +       36.64856292950*t);\r
-   neptune_y_0+=     0.14936165806 * cos( 5.79694900665 +       39.61750834610*t);\r
-   neptune_y_0+=     0.02584250749 * cos( 0.42549700754 +        1.48447270830*t);\r
-   neptune_y_0+=     0.01073739772 * cos( 3.81371728533 +       74.78159856730*t);\r
-   neptune_y_0+=     0.00815187583 * cos( 5.49429775826 +        2.96894541660*t);\r
-   neptune_y_0+=     0.00582199295 * cos( 6.19633718936 +       35.16409022120*t);\r
-   neptune_y_0+=     0.00565576412 * cos( 4.41843009015 +       41.10198105440*t);\r
-   neptune_y_0+=     0.00495581047 * cos( 5.31205825784 +      529.69096509460*t);\r
-   neptune_y_0+=     0.00304525203 * cos( 5.11048113661 +       73.29712585900*t);\r
-   neptune_y_0+=     0.00272640298 * cos( 5.58603690785 +      213.29909543800*t);\r
-   neptune_y_0+=     0.00135897385 * cos( 3.97553750964 +       77.75054398390*t);\r
-   neptune_y_0+=     0.00090970871 * cos( 0.11783619888 +      114.39910691340*t);\r
-   neptune_y_0+=     0.00068790261 * cos( 4.26391997151 +        4.45341812490*t);\r
-   neptune_y_0+=     0.00028893355 * cos( 3.21848975032 +       42.58645376270*t);\r
-   neptune_y_0+=     0.00020081559 * cos( 1.19787916085 +       33.67961751290*t);\r
-   neptune_y_0+=     0.00012613583 * cos( 1.99777332934 +      112.91463420510*t);\r
-   neptune_y_0+=     0.00012828708 * cos( 1.16740053443 +      111.43016149680*t);\r
-   neptune_y_0+=     0.00012012961 * cos( 5.66157563804 +     1059.38193018920*t);\r
-   neptune_y_0+=     0.00008768580 * cos( 3.23487156950 +      108.46121608020*t);\r
-   neptune_y_0+=     0.00007581788 * cos( 4.81169168396 +      426.59819087600*t);\r
-   neptune_y_0+=     0.00006439265 * cos( 3.05453259951 +        5.93789083320*t);\r
-   neptune_y_0+=     0.00005297978 * cos( 0.79002313990 +       71.81265315070*t);\r
-   neptune_y_0+=     0.00004650708 * cos( 4.81540983294 +       37.61177077600*t);\r
-   neptune_y_0+=     0.00004733483 * cos( 2.52620194642 +       79.23501669220*t);\r
-   neptune_y_0+=     0.00004557247 * cos( 5.80951552318 +       38.65430049960*t);\r
-   neptune_y_0+=     0.00004322550 * cos( 0.80665145881 +       38.08485152800*t);\r
-   neptune_y_0+=     0.00004315539 * cos( 3.53393506841 +       38.18121974760*t);\r
-   neptune_y_0+=     0.00004089036 * cos( 0.42349446479 +       37.16982779130*t);\r
-   neptune_y_0+=     0.00004247643 * cos( 4.06355336504 +       28.57180808220*t);\r
-   neptune_y_0+=     0.00003932515 * cos( 3.91607592815 +       39.09624348430*t);\r
-   neptune_y_0+=     0.00003930135 * cos( 3.86614178174 +       98.89998852460*t);\r
-   neptune_y_0+=     0.00003323991 * cos( 3.11674274385 +        4.19278569400*t);\r
-   neptune_y_0+=     0.00003112636 * cos( 0.27319642944 +       47.69426319340*t);\r
-   neptune_y_0+=     0.00003373281 * cos( 3.39616255650 +      145.10977900970*t);\r
-   neptune_y_0+=     0.00002670944 * cos( 2.31235275416 +      109.94568878850*t);\r
-   neptune_y_0+=     0.00002523042 * cos( 6.23400745185 +      312.19908396260*t);\r
-   neptune_y_0+=     0.00001888827 * cos( 1.63364331324 +       35.68535508300*t);\r
-   neptune_y_0+=     0.00001647474 * cos( 2.50010254963 +       30.05628079050*t);\r
-   neptune_y_0+=     0.00001826390 * cos( 2.00938305966 +       44.07092647100*t);\r
-   neptune_y_0+=     0.00001967147 * cos( 2.56634772532 +      206.18554843720*t);\r
-   neptune_y_0+=     0.00001681258 * cos( 2.70480318579 +       40.58071619260*t);\r
-   neptune_y_0+=     0.00001533383 * cos( 5.88971113590 +       38.02116105320*t);\r
-   neptune_y_0+=     0.00001894261 * cos( 5.46274825258 +      220.41264243880*t);\r
-   neptune_y_0+=     0.00001527526 * cos( 4.73412534395 +       38.24491022240*t);\r
-   neptune_y_0+=     0.00002086907 * cos( 6.28313624461 +      149.56319713460*t);\r
-   neptune_y_0+=     0.00002057794 * cos( 4.38552505781 +      137.03302416240*t);\r
-   neptune_y_0+=     0.00001720954 * cos( 4.49400805134 +      106.97674337190*t);\r
-   neptune_y_0+=     0.00001314116 * cos( 1.80386443362 +       46.20979048510*t);\r
-   neptune_y_0+=     0.00001732739 * cos( 4.14518500834 +       70.32818044240*t);\r
-   neptune_y_0+=     0.00001559193 * cos( 5.27114846878 +       37.87240320690*t);\r
-   neptune_y_0+=     0.00001545705 * cos( 5.35267669439 +       38.39366806870*t);\r
-   neptune_y_0+=     0.00001435274 * cos( 5.44292013172 +      522.57741809380*t);\r
-   neptune_y_0+=     0.00001404991 * cos( 2.04611088339 +      536.80451209540*t);\r
-   neptune_y_0+=     0.00001242929 * cos( 1.10242173566 +       33.94024994380*t);\r
-   neptune_y_0+=     0.00001388024 * cos( 2.14792830412 +      115.88357962170*t);\r
-   neptune_y_0+=     0.00001467042 * cos( 3.56226463770 +      181.75834193920*t);\r
-   neptune_y_0+=     0.00001227926 * cos( 1.21334651843 +       72.07328558160*t);\r
-   neptune_y_0+=     0.00001080807 * cos( 3.18401661435 +       42.32582133180*t);\r
-   neptune_y_0+=     0.00001111708 * cos( 5.51669920239 +        8.07675484730*t);\r
-   neptune_y_0+=     0.00001237027 * cos( 1.08622199668 +      148.07872442630*t);\r
-\r
-   double neptune_y_1=0.0;\r
-   neptune_y_1+=     0.00352947493 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_y_1+=     0.00256125493 * cos( 0.44757496817 +       36.64856292950*t);\r
-   neptune_y_1+=     0.00243147725 * cos( 3.89099798696 +       39.61750834610*t);\r
-   neptune_y_1+=     0.00118427205 * cos( 1.31128027037 +       76.26607127560*t);\r
-   neptune_y_1+=     0.00021936702 * cos( 1.63124087591 +       35.16409022120*t);\r
-   neptune_y_1+=     0.00017462332 * cos( 2.69229902966 +       41.10198105440*t);\r
-   neptune_y_1+=     0.00012992380 * cos( 3.79578633002 +        2.96894541660*t);\r
-   neptune_y_1+=     0.00004945117 * cos( 0.51727080684 +       73.29712585900*t);\r
-   neptune_y_1+=     0.00002745921 * cos( 2.49178311082 +       77.75054398390*t);\r
-   neptune_y_1+=     0.00002145481 * cos( 2.54768447291 +        4.45341812490*t);\r
-   neptune_y_1+=     0.00001572289 * cos( 5.78853350711 +      114.39910691340*t);\r
-   neptune_y_1+=     0.00001565725 * cos( 2.89846266272 +       33.67961751290*t);\r
-   neptune_y_1+=     0.00001458269 * cos( 1.61835542699 +       38.13303563780*t);\r
-   neptune_y_1+=     0.00001293459 * cos( 4.45868061082 +       74.78159856730*t);\r
-   neptune_y_1+=     0.00001343731 * cos( 1.46712622109 +       42.58645376270*t);\r
-   neptune_y_1=neptune_y_1 * t;\r
-\r
-   double neptune_y_2=0.0;\r
-   neptune_y_2+=     0.00004539421 * cos( 3.45613207922 +       36.64856292950*t);\r
-   neptune_y_2+=     0.00004347956 * cos( 0.88317230351 +       39.61750834610*t);\r
-   neptune_y_2+=     0.00003595394 * cos( 3.14159265359 +        0.00000000000*t);\r
-   neptune_y_2+=     0.00003058647 * cos( 5.31956613665 +       38.13303563780*t);\r
-   neptune_y_2+=     0.00002163809 * cos( 0.22086532214 +       76.26607127560*t);\r
-   neptune_y_2=neptune_y_2 * t * t;\r
-\r
-   return neptune_y_0+neptune_y_1+neptune_y_2;\r
-}\r
-\r
-double vsop87a_milli_neptune_z(double t){\r
-   double neptune_z_0=0.0;\r
-   neptune_z_0+=     0.92866054405 * cos( 1.44103930278 +       38.13303563780*t);\r
-   neptune_z_0+=     0.01245978462 * cos( 0.00000000000 +        0.00000000000*t);\r
-   neptune_z_0+=     0.00474333567 * cos( 2.52218774238 +       36.64856292950*t);\r
-   neptune_z_0+=     0.00451987936 * cos( 3.50949720541 +       39.61750834610*t);\r
-   neptune_z_0+=     0.00417558068 * cos( 5.91310695421 +       76.26607127560*t);\r
-   neptune_z_0+=     0.00084104329 * cos( 4.38928900096 +        1.48447270830*t);\r
-   neptune_z_0+=     0.00032704958 * cos( 1.52048692001 +       74.78159856730*t);\r
-   neptune_z_0+=     0.00030873335 * cos( 3.29017611456 +       35.16409022120*t);\r
-   neptune_z_0+=     0.00025812584 * cos( 3.19303128782 +        2.96894541660*t);\r
-   neptune_z_0+=     0.00016865319 * cos( 2.13251104425 +       41.10198105440*t);\r
-   neptune_z_0+=     0.00011789909 * cos( 3.60001877675 +      213.29909543800*t);\r
-   neptune_z_0+=     0.00009770125 * cos( 2.80133971586 +       73.29712585900*t);\r
-   neptune_z_0+=     0.00011279680 * cos( 3.55816676334 +      529.69096509460*t);\r
-   neptune_z_0+=     0.00004119873 * cos( 1.67934316836 +       77.75054398390*t);\r
-   neptune_z_0+=     0.00002818034 * cos( 4.10661077794 +      114.39910691340*t);\r
-   neptune_z_0+=     0.00002868677 * cos( 4.27011526203 +       33.67961751290*t);\r
-   neptune_z_0+=     0.00002213464 * cos( 1.96045135168 +        4.45341812490*t);\r
-   neptune_z_0+=     0.00001865650 * cos( 5.05540709577 +       71.81265315070*t);\r
-\r
-   double neptune_z_1=0.0;\r
-   neptune_z_1+=     0.00154885971 * cos( 2.14239039664 +       38.13303563780*t);\r
-   neptune_z_1+=     0.00007783708 * cos( 4.40146905905 +       36.64856292950*t);\r
-   neptune_z_1+=     0.00006862414 * cos( 1.65930160610 +       39.61750834610*t);\r
-   neptune_z_1+=     0.00009464276 * cos( 0.00000000000 +        0.00000000000*t);\r
-   neptune_z_1+=     0.00003891873 * cos( 5.46761139427 +       76.26607127560*t);\r
-   neptune_z_1=neptune_z_1 * t;\r
-\r
-   double neptune_z_2=0.0;\r
-   neptune_z_2+=     0.00001264840 * cos( 1.91401498992 +       38.13303563780*t);\r
-   neptune_z_2=neptune_z_2 * t * t;\r
-\r
-   return neptune_z_0+neptune_z_1+neptune_z_2;\r
-}\r
-\r
-double vsop87a_milli_saturn_x(double t){\r
-   double saturn_x_0=0.0;\r
-   saturn_x_0+=     9.51638335797 * cos( 0.87441380794 +      213.29909543800*t);\r
-   saturn_x_0+=     0.26412374238 * cos( 0.12390892620 +      426.59819087600*t);\r
-   saturn_x_0+=     0.06760430339 * cos( 4.16767145778 +      206.18554843720*t);\r
-   saturn_x_0+=     0.06624260115 * cos( 0.75094737780 +      220.41264243880*t);\r
-   saturn_x_0+=     0.04244797817 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_x_0+=     0.02336340488 * cos( 2.02227784673 +        7.11354700080*t);\r
-   saturn_x_0+=     0.01255372247 * cos( 2.17338917731 +      110.20632121940*t);\r
-   saturn_x_0+=     0.01115684467 * cos( 3.15686878377 +      419.48464387520*t);\r
-   saturn_x_0+=     0.01097683232 * cos( 5.65753337256 +      639.89728631400*t);\r
-   saturn_x_0+=     0.00716328481 * cos( 2.71149993708 +      316.39186965660*t);\r
-   saturn_x_0+=     0.00509313365 * cos( 4.95865624780 +      103.09277421860*t);\r
-   saturn_x_0+=     0.00433994439 * cos( 0.72012820974 +      529.69096509460*t);\r
-   saturn_x_0+=     0.00372894461 * cos( 0.00137195497 +      433.71173787680*t);\r
-   saturn_x_0+=     0.00097843523 * cos( 1.01485750417 +      323.50541665740*t);\r
-   saturn_x_0+=     0.00080600536 * cos( 5.62103979796 +       11.04570026390*t);\r
-   saturn_x_0+=     0.00083782316 * cos( 0.62038893702 +      227.52618943960*t);\r
-   saturn_x_0+=     0.00074150224 * cos( 2.38206066655 +      632.78373931320*t);\r
-   saturn_x_0+=     0.00070219382 * cos( 0.88789752415 +      209.36694217490*t);\r
-   saturn_x_0+=     0.00068855792 * cos( 4.01788097627 +      217.23124870110*t);\r
-   saturn_x_0+=     0.00065620467 * cos( 2.69728593339 +      202.25339517410*t);\r
-   saturn_x_0+=     0.00058297911 * cos( 2.16155251399 +      224.34479570190*t);\r
-   saturn_x_0+=     0.00054022837 * cos( 4.90928184374 +      853.19638175200*t);\r
-   saturn_x_0+=     0.00045550446 * cos( 1.88235037830 +       14.22709400160*t);\r
-   saturn_x_0+=     0.00038345667 * cos( 4.39815501478 +      199.07200143640*t);\r
-   saturn_x_0+=     0.00044551703 * cos( 5.60763553535 +       63.73589830340*t);\r
-   saturn_x_0+=     0.00025165185 * cos( 0.37800582257 +      216.48048917570*t);\r
-   saturn_x_0+=     0.00024554499 * cos( 4.53150598095 +      210.11770170030*t);\r
-   saturn_x_0+=     0.00024673219 * cos( 5.90891573850 +      522.57741809380*t);\r
-   saturn_x_0+=     0.00024677050 * cos( 5.60389382420 +      415.55249061210*t);\r
-   saturn_x_0+=     0.00025491374 * cos( 1.63922423181 +      117.31986822020*t);\r
-   saturn_x_0+=     0.00031253049 * cos( 4.62976601833 +      735.87651353180*t);\r
-   saturn_x_0+=     0.00023372467 * cos( 5.53491987276 +      647.01083331480*t);\r
-   saturn_x_0+=     0.00023355468 * cos( 0.18791490124 +      149.56319713460*t);\r
-   saturn_x_0+=     0.00024805815 * cos( 5.50327676733 +       74.78159856730*t);\r
-   saturn_x_0+=     0.00014731703 * cos( 4.67981909838 +      277.03499374140*t);\r
-   saturn_x_0+=     0.00012427525 * cos( 1.02995545746 +     1059.38193018920*t);\r
-   saturn_x_0+=     0.00009943329 * cos( 0.84628387596 +        3.93215326310*t);\r
-   saturn_x_0+=     0.00012393514 * cos( 4.19747622821 +      490.33408917940*t);\r
-   saturn_x_0+=     0.00012026472 * cos( 5.66372282839 +      351.81659230870*t);\r
-   saturn_x_0+=     0.00008222014 * cos( 2.47875301104 +      742.99006053260*t);\r
-   saturn_x_0+=     0.00009087093 * cos( 4.33505326762 +     1052.26838318840*t);\r
-   saturn_x_0+=     0.00006717741 * cos( 5.51897460997 +      838.96928775040*t);\r
-   saturn_x_0+=     0.00006232999 * cos( 2.45837758015 +      846.08283475120*t);\r
-   saturn_x_0+=     0.00007161671 * cos( 2.18152751738 +       95.97922721780*t);\r
-   saturn_x_0+=     0.00006321101 * cos( 0.83915408770 +      309.27832265580*t);\r
-   saturn_x_0+=     0.00006074958 * cos( 6.15905897331 +      440.82528487760*t);\r
-   saturn_x_0+=     0.00005343894 * cos( 3.60046273598 +      412.37109687440*t);\r
-   saturn_x_0+=     0.00004860582 * cos( 0.26461045175 +      536.80451209540*t);\r
-   saturn_x_0+=     0.00005775802 * cos( 5.30717695229 +       38.13303563780*t);\r
-   saturn_x_0+=     0.00005194178 * cos( 4.54584467686 +      210.85141488320*t);\r
-   saturn_x_0+=     0.00005152474 * cos( 0.34669517150 +      215.74677599280*t);\r
-   saturn_x_0+=     0.00003792540 * cos( 5.99766568983 +      422.66603761290*t);\r
-   saturn_x_0+=     0.00003762834 * cos( 3.72112920226 +      212.33588759150*t);\r
-   saturn_x_0+=     0.00003747433 * cos( 1.16965137714 +      214.26230328450*t);\r
-   saturn_x_0+=     0.00003114576 * cos( 0.84631897292 +      213.25091132820*t);\r
-   saturn_x_0+=     0.00003113641 * cos( 4.04410367190 +      213.34727954780*t);\r
-   saturn_x_0+=     0.00002990421 * cos( 0.04148806852 +      625.67019231240*t);\r
-   saturn_x_0+=     0.00004111695 * cos( 5.96153153046 +      137.03302416240*t);\r
-   saturn_x_0+=     0.00002966450 * cos( 5.39568820046 +      138.51749687070*t);\r
-   saturn_x_0+=     0.00002827527 * cos( 0.73252555642 +      330.61896365820*t);\r
-   saturn_x_0+=     0.00003363323 * cos( 1.42089586686 +      437.64389113990*t);\r
-   saturn_x_0+=     0.00002886599 * cos( 1.14057922619 +       85.82729883120*t);\r
-   saturn_x_0+=     0.00002634075 * cos( 5.40645201521 +      288.08069400530*t);\r
-   saturn_x_0+=     0.00002713354 * cos( 0.96812639712 +      203.73786788240*t);\r
-   saturn_x_0+=     0.00003169390 * cos( 5.76640408988 +       76.26607127560*t);\r
-   saturn_x_0+=     0.00002618634 * cos( 5.49334837098 +      127.47179660680*t);\r
-   saturn_x_0+=     0.00002527746 * cos( 5.09752068381 +      628.85158605010*t);\r
-   saturn_x_0+=     0.00002989778 * cos( 4.15673836604 +     1066.49547719000*t);\r
-   saturn_x_0+=     0.00002507415 * cos( 1.49447138038 +        9.56122755560*t);\r
-   saturn_x_0+=     0.00002470181 * cos( 5.27435870056 +     1155.36115740700*t);\r
-   saturn_x_0+=     0.00002427626 * cos( 3.97311214231 +      222.86032299360*t);\r
-   saturn_x_0+=     0.00003128325 * cos( 4.05483976553 +     1368.66025284500*t);\r
-   saturn_x_0+=     0.00002309076 * cos( 3.67821438247 +      430.53034413910*t);\r
-   saturn_x_0+=     0.00002162629 * cos( 3.26951119901 +      340.77089204480*t);\r
-   saturn_x_0+=     0.00002912676 * cos( 1.76893577106 +        3.18139373770*t);\r
-   saturn_x_0+=     0.00002095366 * cos( 3.55759089756 +      423.41679713830*t);\r
-   saturn_x_0+=     0.00002335270 * cos( 5.86791072516 +      388.46515523820*t);\r
-   saturn_x_0+=     0.00001634262 * cos( 4.54357767539 +       12.53017297220*t);\r
-   saturn_x_0+=     0.00001635975 * cos( 2.19968869780 +      212.77783057620*t);\r
-   saturn_x_0+=     0.00001632759 * cos( 2.69164822165 +      213.82036029980*t);\r
-   saturn_x_0+=     0.00001498689 * cos( 3.60168057129 +       52.69019803950*t);\r
-   saturn_x_0+=     0.00001461217 * cos( 5.92456743836 +      429.77958461370*t);\r
-   saturn_x_0+=     0.00001963947 * cos( 2.05086487180 +        1.48447270830*t);\r
-   saturn_x_0+=     0.00001538425 * cos( 4.78544077085 +      860.30992875280*t);\r
-   saturn_x_0+=     0.00001485856 * cos( 5.65501463408 +      949.17560896980*t);\r
-   saturn_x_0+=     0.00001418116 * cos( 5.41419993599 +      350.33211960040*t);\r
-   saturn_x_0+=     0.00001147607 * cos( 0.19147238521 +      942.06206196900*t);\r
-   saturn_x_0+=     0.00001111703 * cos( 0.47907488492 +      234.63973644040*t);\r
-   saturn_x_0+=     0.00001019566 * cos( 5.00707811029 +     1471.75302706360*t);\r
-   saturn_x_0+=     0.00001009190 * cos( 1.34289487761 +      265.98929347750*t);\r
-   saturn_x_0+=     0.00001089450 * cos( 5.82690672710 +      362.86229257260*t);\r
-   saturn_x_0+=     0.00001092244 * cos( 1.13561107749 +      173.94221952280*t);\r
-   saturn_x_0+=     0.00001173456 * cos( 3.79591687208 +     1685.05212250160*t);\r
-   saturn_x_0+=     0.00001118369 * cos( 3.46624149583 +      703.63318461740*t);\r
-   saturn_x_0+=     0.00001150595 * cos( 3.74707160019 +      200.76892246580*t);\r
-   saturn_x_0+=     0.00001007761 * cos( 1.08964371328 +      225.82926841020*t);\r
-\r
-   double saturn_x_1=0.0;\r
-   saturn_x_1+=     0.07575103962 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_x_1+=     0.03085041716 * cos( 4.27565749128 +      426.59819087600*t);\r
-   saturn_x_1+=     0.02714918399 * cos( 5.85229412397 +      206.18554843720*t);\r
-   saturn_x_1+=     0.02643100909 * cos( 5.33291950584 +      220.41264243880*t);\r
-   saturn_x_1+=     0.00627104520 * cos( 0.32898307969 +        7.11354700080*t);\r
-   saturn_x_1+=     0.00256560953 * cos( 3.52478934343 +      639.89728631400*t);\r
-   saturn_x_1+=     0.00312356512 * cos( 4.83001724941 +      419.48464387520*t);\r
-   saturn_x_1+=     0.00189196274 * cos( 4.48642453552 +      433.71173787680*t);\r
-   saturn_x_1+=     0.00203646570 * cos( 1.10998681782 +      213.29909543800*t);\r
-   saturn_x_1+=     0.00119531145 * cos( 1.14735096078 +      110.20632121940*t);\r
-   saturn_x_1+=     0.00066764238 * cos( 3.72346596928 +      316.39186965660*t);\r
-   saturn_x_1+=     0.00066901225 * cos( 5.20257500380 +      227.52618943960*t);\r
-   saturn_x_1+=     0.00031000840 * cos( 6.06067919437 +      199.07200143640*t);\r
-   saturn_x_1+=     0.00030418100 * cos( 0.18746903351 +       14.22709400160*t);\r
-   saturn_x_1+=     0.00022275210 * cos( 6.19530878014 +      103.09277421860*t);\r
-   saturn_x_1+=     0.00018939377 * cos( 2.77618306725 +      853.19638175200*t);\r
-   saturn_x_1+=     0.00018093009 * cos( 5.09162723865 +      209.36694217490*t);\r
-   saturn_x_1+=     0.00017777854 * cos( 6.10381593351 +      217.23124870110*t);\r
-   saturn_x_1+=     0.00016296201 * cos( 4.86945681437 +      216.48048917570*t);\r
-   saturn_x_1+=     0.00017120250 * cos( 4.59611664188 +      632.78373931320*t);\r
-   saturn_x_1+=     0.00015894491 * cos( 0.03653502304 +      210.11770170030*t);\r
-   saturn_x_1+=     0.00016192653 * cos( 5.60798014450 +      323.50541665740*t);\r
-   saturn_x_1+=     0.00014466010 * cos( 3.67449380090 +      647.01083331480*t);\r
-   saturn_x_1+=     0.00011061528 * cos( 0.03163071461 +      117.31986822020*t);\r
-   saturn_x_1+=     0.00009873183 * cos( 5.20065307357 +      202.25339517410*t);\r
-   saturn_x_1+=     0.00008707608 * cos( 6.03511731637 +      224.34479570190*t);\r
-   saturn_x_1+=     0.00005499109 * cos( 4.40350603415 +      440.82528487760*t);\r
-   saturn_x_1+=     0.00005512222 * cos( 2.60556642348 +       11.04570026390*t);\r
-   saturn_x_1+=     0.00004008257 * cos( 1.48942966807 +      522.57741809380*t);\r
-   saturn_x_1+=     0.00003571196 * cos( 5.10821908379 +      412.37109687440*t);\r
-   saturn_x_1+=     0.00002731381 * cos( 4.10892223660 +      149.56319713460*t);\r
-   saturn_x_1+=     0.00002763786 * cos( 3.96253590209 +       95.97922721780*t);\r
-   saturn_x_1+=     0.00001875862 * cos( 2.52384080586 +        3.93215326310*t);\r
-   saturn_x_1+=     0.00001765816 * cos( 0.75684544353 +      277.03499374140*t);\r
-   saturn_x_1+=     0.00001688957 * cos( 3.98270950731 +      422.66603761290*t);\r
-   saturn_x_1+=     0.00001544787 * cos( 5.30283923836 +      330.61896365820*t);\r
-   saturn_x_1+=     0.00001449143 * cos( 6.26507179861 +      529.69096509460*t);\r
-   saturn_x_1+=     0.00001395551 * cos( 2.03533642541 +     1066.49547719000*t);\r
-   saturn_x_1+=     0.00001330413 * cos( 5.06312203212 +      234.63973644040*t);\r
-   saturn_x_1+=     0.00001511648 * cos( 6.23274598777 +        3.18139373770*t);\r
-   saturn_x_1+=     0.00001271050 * cos( 2.40338468675 +      415.55249061210*t);\r
-   saturn_x_1+=     0.00001171680 * cos( 4.59341412127 +      536.80451209540*t);\r
-   saturn_x_1+=     0.00001129056 * cos( 5.45794529295 +      423.41679713830*t);\r
-   saturn_x_1+=     0.00001126400 * cos( 2.88173213734 +      860.30992875280*t);\r
-   saturn_x_1+=     0.00001101245 * cos( 4.07698108824 +      429.77958461370*t);\r
-   saturn_x_1+=     0.00001273315 * cos( 0.09572429396 +      742.99006053260*t);\r
-   saturn_x_1+=     0.00001342277 * cos( 2.98929557875 +      210.85141488320*t);\r
-   saturn_x_1+=     0.00001331045 * cos( 1.90899526877 +      215.74677599280*t);\r
-   saturn_x_1=saturn_x_1 * t;\r
-\r
-   double saturn_x_2=0.0;\r
-   saturn_x_2+=     0.00560746334 * cos( 1.26401632282 +      206.18554843720*t);\r
-   saturn_x_2+=     0.00545834518 * cos( 3.62343709657 +      220.41264243880*t);\r
-   saturn_x_2+=     0.00443342186 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_x_2+=     0.00336109713 * cos( 2.42547432460 +      213.29909543800*t);\r
-   saturn_x_2+=     0.00224302269 * cos( 2.49151203519 +      426.59819087600*t);\r
-   saturn_x_2+=     0.00087170924 * cos( 4.89048951691 +        7.11354700080*t);\r
-   saturn_x_2+=     0.00050028094 * cos( 2.70119046081 +      433.71173787680*t);\r
-   saturn_x_2+=     0.00045122590 * cos( 0.36735068943 +      419.48464387520*t);\r
-   saturn_x_2+=     0.00032847824 * cos( 1.59210153669 +      639.89728631400*t);\r
-   saturn_x_2+=     0.00027153555 * cos( 3.49804002218 +      227.52618943960*t);\r
-   saturn_x_2+=     0.00012676167 * cos( 1.45465729530 +      199.07200143640*t);\r
-   saturn_x_2+=     0.00010330738 * cos( 4.76949531290 +       14.22709400160*t);\r
-   saturn_x_2+=     0.00007249149 * cos( 5.70264553247 +      110.20632121940*t);\r
-   saturn_x_2+=     0.00004653214 * cos( 1.83710048213 +      647.01083331480*t);\r
-   saturn_x_2+=     0.00004923585 * cos( 3.08463039042 +      216.48048917570*t);\r
-   saturn_x_2+=     0.00004777358 * cos( 1.81695155349 +      210.11770170030*t);\r
-   saturn_x_2+=     0.00004166633 * cos( 5.32887874226 +      316.39186965660*t);\r
-   saturn_x_2+=     0.00003508385 * cos( 0.78251653369 +      853.19638175200*t);\r
-   saturn_x_2+=     0.00002660470 * cos( 3.16731393212 +      209.36694217490*t);\r
-   saturn_x_2+=     0.00002538027 * cos( 2.65097612407 +      440.82528487760*t);\r
-   saturn_x_2+=     0.00002568114 * cos( 1.74024228572 +      217.23124870110*t);\r
-   saturn_x_2+=     0.00002503277 * cos( 4.69450368911 +      117.31986822020*t);\r
-   saturn_x_2+=     0.00002506986 * cos( 1.74781817701 +      103.09277421860*t);\r
-   saturn_x_2+=     0.00002129256 * cos( 0.28453141367 +      632.78373931320*t);\r
-   saturn_x_2+=     0.00001841989 * cos( 3.99269872894 +      323.50541665740*t);\r
-   saturn_x_2+=     0.00001228511 * cos( 0.42906039519 +      412.37109687440*t);\r
-   saturn_x_2=saturn_x_2 * t * t;\r
-\r
-   double saturn_x_3=0.0;\r
-   saturn_x_3+=     0.00077115952 * cos( 2.97714385362 +      206.18554843720*t);\r
-   saturn_x_3+=     0.00075340436 * cos( 1.89208005248 +      220.41264243880*t);\r
-   saturn_x_3+=     0.00018450895 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_x_3+=     0.00010527244 * cos( 0.66368256891 +      426.59819087600*t);\r
-   saturn_x_3+=     0.00008994946 * cos( 0.91696559755 +      433.71173787680*t);\r
-   saturn_x_3+=     0.00007403594 * cos( 1.78627385870 +      227.52618943960*t);\r
-   saturn_x_3+=     0.00008045160 * cos( 3.12864412887 +        7.11354700080*t);\r
-   saturn_x_3+=     0.00004505149 * cos( 2.24531319187 +      419.48464387520*t);\r
-   saturn_x_3+=     0.00003468010 * cos( 3.14590544446 +      199.07200143640*t);\r
-   saturn_x_3+=     0.00002974601 * cos( 6.00030641555 +      639.89728631400*t);\r
-   saturn_x_3+=     0.00002342089 * cos( 3.06091771643 +       14.22709400160*t);\r
-   saturn_x_3+=     0.00001230715 * cos( 4.38196130069 +      213.29909543800*t);\r
-   saturn_x_3+=     0.00001023888 * cos( 0.01138655869 +      647.01083331480*t);\r
-   saturn_x_3=saturn_x_3 * t * t * t;\r
-\r
-   double saturn_x_4=0.0;\r
-   saturn_x_4+=     0.00007959921 * cos( 4.70523623364 +      206.18554843720*t);\r
-   saturn_x_4+=     0.00007836652 * cos( 0.13981693631 +      220.41264243880*t);\r
-   saturn_x_4+=     0.00001511196 * cos( 0.06561560462 +      227.52618943960*t);\r
-   saturn_x_4+=     0.00001223066 * cos( 5.41618485361 +      433.71173787680*t);\r
-   saturn_x_4=saturn_x_4 * t * t * t * t;\r
-\r
-   return saturn_x_0+saturn_x_1+saturn_x_2+saturn_x_3+saturn_x_4;\r
-}\r
-\r
-double vsop87a_milli_saturn_y(double t){\r
-   double saturn_y_0=0.0;\r
-   saturn_y_0+=     9.52986882699 * cos( 5.58600556665 +      213.29909543800*t);\r
-   saturn_y_0+=     0.79387988806 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_y_0+=     0.26441781302 * cos( 4.83528061849 +      426.59819087600*t);\r
-   saturn_y_0+=     0.06916653915 * cos( 2.55279408706 +      206.18554843720*t);\r
-   saturn_y_0+=     0.06633570703 * cos( 5.46258848288 +      220.41264243880*t);\r
-   saturn_y_0+=     0.02345609742 * cos( 0.44652132519 +        7.11354700080*t);\r
-   saturn_y_0+=     0.01183874652 * cos( 1.34638298371 +      419.48464387520*t);\r
-   saturn_y_0+=     0.01245790434 * cos( 0.60367177975 +      110.20632121940*t);\r
-   saturn_y_0+=     0.01098751131 * cos( 4.08608782813 +      639.89728631400*t);\r
-   saturn_y_0+=     0.00700849336 * cos( 1.13611298025 +      316.39186965660*t);\r
-   saturn_y_0+=     0.00434466176 * cos( 5.42474696262 +      529.69096509460*t);\r
-   saturn_y_0+=     0.00373327342 * cos( 4.71308726958 +      433.71173787680*t);\r
-   saturn_y_0+=     0.00335162363 * cos( 0.66422253983 +      103.09277421860*t);\r
-   saturn_y_0+=     0.00097837745 * cos( 5.72844290173 +      323.50541665740*t);\r
-   saturn_y_0+=     0.00080571808 * cos( 4.05295449910 +       11.04570026390*t);\r
-   saturn_y_0+=     0.00083899691 * cos( 5.33204070267 +      227.52618943960*t);\r
-   saturn_y_0+=     0.00070158491 * cos( 5.59777963629 +      209.36694217490*t);\r
-   saturn_y_0+=     0.00065937657 * cos( 1.25969608208 +      202.25339517410*t);\r
-   saturn_y_0+=     0.00070957225 * cos( 0.88888207567 +      632.78373931320*t);\r
-   saturn_y_0+=     0.00068985859 * cos( 2.44460312617 +      217.23124870110*t);\r
-   saturn_y_0+=     0.00058382264 * cos( 0.58978766922 +      224.34479570190*t);\r
-   saturn_y_0+=     0.00054049836 * cos( 3.33757904879 +      853.19638175200*t);\r
-   saturn_y_0+=     0.00045790930 * cos( 0.30331527632 +       14.22709400160*t);\r
-   saturn_y_0+=     0.00041976402 * cos( 2.62591355948 +      199.07200143640*t);\r
-   saturn_y_0+=     0.00044697175 * cos( 0.90661238256 +       63.73589830340*t);\r
-   saturn_y_0+=     0.00025199575 * cos( 5.08963506006 +      216.48048917570*t);\r
-   saturn_y_0+=     0.00024640836 * cos( 2.95445247282 +      210.11770170030*t);\r
-   saturn_y_0+=     0.00024835151 * cos( 4.02630190571 +      415.55249061210*t);\r
-   saturn_y_0+=     0.00025545907 * cos( 0.06626229252 +      117.31986822020*t);\r
-   saturn_y_0+=     0.00029666833 * cos( 6.09910638345 +      735.87651353180*t);\r
-   saturn_y_0+=     0.00023396742 * cos( 3.96337393635 +      647.01083331480*t);\r
-   saturn_y_0+=     0.00023380691 * cos( 4.90051072276 +      149.56319713460*t);\r
-   saturn_y_0+=     0.00020272215 * cos( 2.34319548198 +      309.27832265580*t);\r
-   saturn_y_0+=     0.00020099552 * cos( 0.98365186365 +      522.57741809380*t);\r
-   saturn_y_0+=     0.00024827950 * cos( 3.92681428900 +       74.78159856730*t);\r
-   saturn_y_0+=     0.00015383927 * cos( 3.10227822627 +      277.03499374140*t);\r
-   saturn_y_0+=     0.00011629210 * cos( 5.74108283772 +     1059.38193018920*t);\r
-   saturn_y_0+=     0.00012422966 * cos( 2.62557865743 +      490.33408917940*t);\r
-   saturn_y_0+=     0.00012048048 * cos( 4.09265980116 +      351.81659230870*t);\r
-   saturn_y_0+=     0.00009551796 * cos( 3.48788042094 +       95.97922721780*t);\r
-   saturn_y_0+=     0.00007670379 * cos( 1.16594276164 +      742.99006053260*t);\r
-   saturn_y_0+=     0.00006919946 * cos( 1.17090063883 +      412.37109687440*t);\r
-   saturn_y_0+=     0.00009034877 * cos( 5.86816144198 +     1052.26838318840*t);\r
-   saturn_y_0+=     0.00006536751 * cos( 0.84246459392 +      838.96928775040*t);\r
-   saturn_y_0+=     0.00006082097 * cos( 4.58758280729 +      440.82528487760*t);\r
-   saturn_y_0+=     0.00005027211 * cos( 0.93213690546 +      846.08283475120*t);\r
-   saturn_y_0+=     0.00004838146 * cos( 4.98563812475 +      536.80451209540*t);\r
-   saturn_y_0+=     0.00005768897 * cos( 3.73776690402 +       38.13303563780*t);\r
-   saturn_y_0+=     0.00005201849 * cos( 2.97482802430 +      210.85141488320*t);\r
-   saturn_y_0+=     0.00005156578 * cos( 5.05796998564 +      215.74677599280*t);\r
-   saturn_y_0+=     0.00003792348 * cos( 4.41806046981 +      422.66603761290*t);\r
-   saturn_y_0+=     0.00003881104 * cos( 3.38026646963 +        3.93215326310*t);\r
-   saturn_y_0+=     0.00003768751 * cos( 2.14954247360 +      212.33588759150*t);\r
-   saturn_y_0+=     0.00003752010 * cos( 5.88125434018 +      214.26230328450*t);\r
-   saturn_y_0+=     0.00003118938 * cos( 5.55799397159 +      213.25091132820*t);\r
-   saturn_y_0+=     0.00003118056 * cos( 2.47259780102 +      213.34727954780*t);\r
-   saturn_y_0+=     0.00003815691 * cos( 2.44166851155 +      625.67019231240*t);\r
-   saturn_y_0+=     0.00004042463 * cos( 1.24471211016 +      137.03302416240*t);\r
-   saturn_y_0+=     0.00002829944 * cos( 5.44434225998 +      330.61896365820*t);\r
-   saturn_y_0+=     0.00003367270 * cos( 6.13298847057 +      437.64389113990*t);\r
-   saturn_y_0+=     0.00002891352 * cos( 5.85313497106 +       85.82729883120*t);\r
-   saturn_y_0+=     0.00002774595 * cos( 5.61594351302 +      203.73786788240*t);\r
-   saturn_y_0+=     0.00002638715 * cos( 3.83678156812 +      288.08069400530*t);\r
-   saturn_y_0+=     0.00003171680 * cos( 4.19553075395 +       76.26607127560*t);\r
-   saturn_y_0+=     0.00002532374 * cos( 3.52629372341 +      628.85158605010*t);\r
-   saturn_y_0+=     0.00002533632 * cos( 3.89788590926 +      138.51749687070*t);\r
-   saturn_y_0+=     0.00002982174 * cos( 2.58535107213 +     1066.49547719000*t);\r
-   saturn_y_0+=     0.00002620642 * cos( 0.69751279148 +      127.47179660680*t);\r
-   saturn_y_0+=     0.00002501775 * cos( 6.19929274396 +        9.56122755560*t);\r
-   saturn_y_0+=     0.00002448467 * cos( 0.54179432209 +     1155.36115740700*t);\r
-   saturn_y_0+=     0.00002431496 * cos( 2.40122451395 +      222.86032299360*t);\r
-   saturn_y_0+=     0.00003138628 * cos( 5.63058455924 +     1368.66025284500*t);\r
-   saturn_y_0+=     0.00002269226 * cos( 2.12401905105 +      430.53034413910*t);\r
-   saturn_y_0+=     0.00002078049 * cos( 1.95682348964 +      423.41679713830*t);\r
-   saturn_y_0+=     0.00002670750 * cos( 0.18165311734 +        3.18139373770*t);\r
-   saturn_y_0+=     0.00002339764 * cos( 4.29619053852 +      388.46515523820*t);\r
-   saturn_y_0+=     0.00001636580 * cos( 2.97440139727 +       12.53017297220*t);\r
-   saturn_y_0+=     0.00001639181 * cos( 0.62823227849 +      212.77783057620*t);\r
-   saturn_y_0+=     0.00001634235 * cos( 1.12043073218 +      213.82036029980*t);\r
-   saturn_y_0+=     0.00001499665 * cos( 5.16865990579 +       52.69019803950*t);\r
-   saturn_y_0+=     0.00001462908 * cos( 4.35285690993 +      429.77958461370*t);\r
-   saturn_y_0+=     0.00001678952 * cos( 2.07211719214 +      949.17560896980*t);\r
-   saturn_y_0+=     0.00001989253 * cos( 0.42496478369 +        1.48447270830*t);\r
-   saturn_y_0+=     0.00001540242 * cos( 3.21449770483 +      860.30992875280*t);\r
-   saturn_y_0+=     0.00001437063 * cos( 3.84293543293 +      350.33211960040*t);\r
-   saturn_y_0+=     0.00001276377 * cos( 2.98728987770 +      340.77089204480*t);\r
-   saturn_y_0+=     0.00001152164 * cos( 1.78736848302 +      942.06206196900*t);\r
-   saturn_y_0+=     0.00001112617 * cos( 5.19114183145 +      234.63973644040*t);\r
-   saturn_y_0+=     0.00001011023 * cos( 0.27242160432 +     1471.75302706360*t);\r
-   saturn_y_0+=     0.00001162807 * cos( 2.35040840317 +      200.76892246580*t);\r
-   saturn_y_0+=     0.00001091025 * cos( 4.25638370205 +      362.86229257260*t);\r
-   saturn_y_0+=     0.00001090678 * cos( 5.85086226218 +      173.94221952280*t);\r
-   saturn_y_0+=     0.00001186647 * cos( 5.38323620554 +     1685.05212250160*t);\r
-   saturn_y_0+=     0.00001120819 * cos( 1.89478696683 +      703.63318461740*t);\r
-   saturn_y_0+=     0.00001008898 * cos( 5.80110302450 +      225.82926841020*t);\r
-\r
-   double saturn_y_1=0.0;\r
-   saturn_y_1+=     0.05373889135 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_y_1+=     0.03090575152 * cos( 2.70346890906 +      426.59819087600*t);\r
-   saturn_y_1+=     0.02741594312 * cos( 4.26667636015 +      206.18554843720*t);\r
-   saturn_y_1+=     0.02647489677 * cos( 3.76132298889 +      220.41264243880*t);\r
-   saturn_y_1+=     0.00631520527 * cos( 5.03245505280 +        7.11354700080*t);\r
-   saturn_y_1+=     0.00256799701 * cos( 1.95351819758 +      639.89728631400*t);\r
-   saturn_y_1+=     0.00312271930 * cos( 3.25850205023 +      419.48464387520*t);\r
-   saturn_y_1+=     0.00189433319 * cos( 2.91501840819 +      433.71173787680*t);\r
-   saturn_y_1+=     0.00164133553 * cos( 5.29239290066 +      213.29909543800*t);\r
-   saturn_y_1+=     0.00116791227 * cos( 5.89146675760 +      110.20632121940*t);\r
-   saturn_y_1+=     0.00067210919 * cos( 2.17042636344 +      316.39186965660*t);\r
-   saturn_y_1+=     0.00067003292 * cos( 3.63101075514 +      227.52618943960*t);\r
-   saturn_y_1+=     0.00033002406 * cos( 4.35527405801 +      199.07200143640*t);\r
-   saturn_y_1+=     0.00030628998 * cos( 4.88861760772 +       14.22709400160*t);\r
-   saturn_y_1+=     0.00022234714 * cos( 4.62212779231 +      103.09277421860*t);\r
-   saturn_y_1+=     0.00018945004 * cos( 1.20412493845 +      853.19638175200*t);\r
-   saturn_y_1+=     0.00018079959 * cos( 3.51566153251 +      209.36694217490*t);\r
-   saturn_y_1+=     0.00017791543 * cos( 4.53214140649 +      217.23124870110*t);\r
-   saturn_y_1+=     0.00016320701 * cos( 3.29784030970 +      216.48048917570*t);\r
-   saturn_y_1+=     0.00015944258 * cos( 4.74503265169 +      210.11770170030*t);\r
-   saturn_y_1+=     0.00016717122 * cos( 3.00270792752 +      632.78373931320*t);\r
-   saturn_y_1+=     0.00016149947 * cos( 4.04186432517 +      323.50541665740*t);\r
-   saturn_y_1+=     0.00014481431 * cos( 2.10298298650 +      647.01083331480*t);\r
-   saturn_y_1+=     0.00011084040 * cos( 4.74073871754 +      117.31986822020*t);\r
-   saturn_y_1+=     0.00009905491 * cos( 3.60258599375 +      202.25339517410*t);\r
-   saturn_y_1+=     0.00008726051 * cos( 4.46341342877 +      224.34479570190*t);\r
-   saturn_y_1+=     0.00006585597 * cos( 4.07326320487 +      309.27832265580*t);\r
-   saturn_y_1+=     0.00005505978 * cos( 2.83207390240 +      440.82528487760*t);\r
-   saturn_y_1+=     0.00005424041 * cos( 1.03197684410 +       11.04570026390*t);\r
-   saturn_y_1+=     0.00004178266 * cos( 3.01038512076 +      412.37109687440*t);\r
-   saturn_y_1+=     0.00004049905 * cos( 5.17488767645 +       95.97922721780*t);\r
-   saturn_y_1+=     0.00002735256 * cos( 2.53975850409 +      149.56319713460*t);\r
-   saturn_y_1+=     0.00002369024 * cos( 2.40497927917 +      522.57741809380*t);\r
-   saturn_y_1+=     0.00001745258 * cos( 5.50576015456 +      277.03499374140*t);\r
-   saturn_y_1+=     0.00001692790 * cos( 2.39926502529 +      422.66603761290*t);\r
-   saturn_y_1+=     0.00001546006 * cos( 3.73156925599 +      330.61896365820*t);\r
-   saturn_y_1+=     0.00001389354 * cos( 0.46207025895 +     1066.49547719000*t);\r
-   saturn_y_1+=     0.00001332553 * cos( 3.49199812296 +      234.63973644040*t);\r
-   saturn_y_1+=     0.00001300934 * cos( 0.83727681906 +      415.55249061210*t);\r
-   saturn_y_1+=     0.00001393622 * cos( 4.62214277175 +        3.18139373770*t);\r
-   saturn_y_1+=     0.00001174319 * cos( 2.72609984335 +      846.08283475120*t);\r
-   saturn_y_1+=     0.00001148010 * cos( 3.04374738882 +      536.80451209540*t);\r
-   saturn_y_1+=     0.00001314125 * cos( 4.38891656600 +      625.67019231240*t);\r
-   saturn_y_1+=     0.00001127952 * cos( 3.87309692307 +      423.41679713830*t);\r
-   saturn_y_1+=     0.00001127646 * cos( 1.31088906213 +      860.30992875280*t);\r
-   saturn_y_1+=     0.00001519732 * cos( 6.12880664637 +        3.93215326310*t);\r
-   saturn_y_1+=     0.00001102361 * cos( 2.50535306014 +      429.77958461370*t);\r
-   saturn_y_1+=     0.00001344891 * cos( 1.41793593685 +      210.85141488320*t);\r
-   saturn_y_1+=     0.00001331786 * cos( 0.33834520814 +      215.74677599280*t);\r
-   saturn_y_1=saturn_y_1 * t;\r
-\r
-   double saturn_y_2=0.0;\r
-   saturn_y_2+=     0.00563706537 * cos( 5.97115878242 +      206.18554843720*t);\r
-   saturn_y_2+=     0.00547012116 * cos( 2.05154973426 +      220.41264243880*t);\r
-   saturn_y_2+=     0.00458518613 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_y_2+=     0.00362294249 * cos( 0.89540100509 +      213.29909543800*t);\r
-   saturn_y_2+=     0.00225521642 * cos( 0.91699821445 +      426.59819087600*t);\r
-   saturn_y_2+=     0.00088390611 * cos( 3.30289449917 +        7.11354700080*t);\r
-   saturn_y_2+=     0.00050101314 * cos( 1.12976163835 +      433.71173787680*t);\r
-   saturn_y_2+=     0.00045516403 * cos( 5.07669466539 +      419.48464387520*t);\r
-   saturn_y_2+=     0.00032896745 * cos( 0.02089057938 +      639.89728631400*t);\r
-   saturn_y_2+=     0.00027199743 * cos( 1.92638417640 +      227.52618943960*t);\r
-   saturn_y_2+=     0.00013251505 * cos( 6.07693099404 +      199.07200143640*t);\r
-   saturn_y_2+=     0.00010425984 * cos( 3.18246869028 +       14.22709400160*t);\r
-   saturn_y_2+=     0.00006673556 * cos( 4.24747633887 +      110.20632121940*t);\r
-   saturn_y_2+=     0.00004658591 * cos( 0.26557833758 +      647.01083331480*t);\r
-   saturn_y_2+=     0.00004934094 * cos( 1.51301179516 +      216.48048917570*t);\r
-   saturn_y_2+=     0.00004789554 * cos( 0.24337901916 +      210.11770170030*t);\r
-   saturn_y_2+=     0.00004167268 * cos( 3.73203671391 +      316.39186965660*t);\r
-   saturn_y_2+=     0.00003509537 * cos( 5.49281440568 +      853.19638175200*t);\r
-   saturn_y_2+=     0.00002743470 * cos( 6.21939083886 +      103.09277421860*t);\r
-   saturn_y_2+=     0.00002661172 * cos( 1.58795412736 +      209.36694217490*t);\r
-   saturn_y_2+=     0.00002541191 * cos( 1.07964653574 +      440.82528487760*t);\r
-   saturn_y_2+=     0.00002568018 * cos( 0.16811216098 +      217.23124870110*t);\r
-   saturn_y_2+=     0.00002507738 * cos( 3.11882746290 +      117.31986822020*t);\r
-   saturn_y_2+=     0.00002159089 * cos( 4.99912567024 +      632.78373931320*t);\r
-   saturn_y_2+=     0.00001828412 * cos( 2.43368650590 +      323.50541665740*t);\r
-   saturn_y_2+=     0.00001351629 * cos( 4.81673889364 +      412.37109687440*t);\r
-   saturn_y_2+=     0.00001177305 * cos( 5.84484412189 +      309.27832265580*t);\r
-   saturn_y_2=saturn_y_2 * t * t;\r
-\r
-   double saturn_y_3=0.0;\r
-   saturn_y_3+=     0.00077376615 * cos( 1.40391048961 +      206.18554843720*t);\r
-   saturn_y_3+=     0.00075564351 * cos( 0.31962896379 +      220.41264243880*t);\r
-   saturn_y_3+=     0.00022843837 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_y_3+=     0.00010672263 * cos( 5.36495663820 +      426.59819087600*t);\r
-   saturn_y_3+=     0.00009010175 * cos( 5.62865146645 +      433.71173787680*t);\r
-   saturn_y_3+=     0.00007418018 * cos( 0.21442310101 +      227.52618943960*t);\r
-   saturn_y_3+=     0.00008298723 * cos( 1.52262563519 +        7.11354700080*t);\r
-   saturn_y_3+=     0.00004507061 * cos( 0.67248969480 +      419.48464387520*t);\r
-   saturn_y_3+=     0.00003581682 * cos( 1.51466786030 +      199.07200143640*t);\r
-   saturn_y_3+=     0.00002981969 * cos( 4.42868951627 +      639.89728631400*t);\r
-   saturn_y_3+=     0.00002376221 * cos( 1.46232779180 +       14.22709400160*t);\r
-   saturn_y_3+=     0.00001024263 * cos( 4.72337917196 +      647.01083331480*t);\r
-   saturn_y_3=saturn_y_3 * t * t * t;\r
-\r
-   double saturn_y_4=0.0;\r
-   saturn_y_4+=     0.00007978886 * cos( 3.13229268011 +      206.18554843720*t);\r
-   saturn_y_4+=     0.00007868379 * cos( 4.84940260021 +      220.41264243880*t);\r
-   saturn_y_4+=     0.00001514835 * cos( 4.77675733867 +      227.52618943960*t);\r
-   saturn_y_4+=     0.00001225569 * cos( 3.84500138574 +      433.71173787680*t);\r
-   saturn_y_4=saturn_y_4 * t * t * t * t;\r
-\r
-   return saturn_y_0+saturn_y_1+saturn_y_2+saturn_y_3+saturn_y_4;\r
-}\r
-\r
-double vsop87a_milli_saturn_z(double t){\r
-   double saturn_z_0=0.0;\r
-   saturn_z_0+=     0.41356950940 * cos( 3.60234142982 +      213.29909543800*t);\r
-   saturn_z_0+=     0.01148283576 * cos( 2.85128367469 +      426.59819087600*t);\r
-   saturn_z_0+=     0.01214249867 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_z_0+=     0.00329280791 * cos( 0.57121407104 +      206.18554843720*t);\r
-   saturn_z_0+=     0.00286934048 * cos( 3.48073526693 +      220.41264243880*t);\r
-   saturn_z_0+=     0.00099076584 * cos( 4.73369511264 +        7.11354700080*t);\r
-   saturn_z_0+=     0.00057361820 * cos( 4.92611225093 +      110.20632121940*t);\r
-   saturn_z_0+=     0.00047738127 * cos( 2.10039779728 +      639.89728631400*t);\r
-   saturn_z_0+=     0.00043458803 * cos( 5.84904978051 +      419.48464387520*t);\r
-   saturn_z_0+=     0.00034565673 * cos( 5.42614229590 +      316.39186965660*t);\r
-   saturn_z_0+=     0.00016185391 * cos( 2.72987173675 +      433.71173787680*t);\r
-   saturn_z_0+=     0.00009001270 * cos( 1.38140102737 +      103.09277421860*t);\r
-   saturn_z_0+=     0.00011433574 * cos( 3.71662021072 +      529.69096509460*t);\r
-   saturn_z_0+=     0.00005398708 * cos( 5.13204892363 +      202.25339517410*t);\r
-   saturn_z_0+=     0.00003902467 * cos( 3.71499738796 +      323.50541665740*t);\r
-   saturn_z_0+=     0.00003709212 * cos( 5.05549348785 +      632.78373931320*t);\r
-   saturn_z_0+=     0.00003614100 * cos( 3.35210451276 +      227.52618943960*t);\r
-   saturn_z_0+=     0.00003379953 * cos( 2.13868919206 +       11.04570026390*t);\r
-   saturn_z_0+=     0.00003089874 * cos( 3.62572857085 +      209.36694217490*t);\r
-   saturn_z_0+=     0.00002683064 * cos( 4.87689555581 +      224.34479570190*t);\r
-   saturn_z_0+=     0.00002963493 * cos( 0.46490184985 +      217.23124870110*t);\r
-   saturn_z_0+=     0.00002343367 * cos( 1.34558278340 +      853.19638175200*t);\r
-   saturn_z_0+=     0.00002423663 * cos( 2.92907094760 +       63.73589830340*t);\r
-   saturn_z_0+=     0.00001701916 * cos( 1.89892525654 +      735.87651353180*t);\r
-   saturn_z_0+=     0.00001941205 * cos( 4.59421314662 +       14.22709400160*t);\r
-   saturn_z_0+=     0.00001990145 * cos( 0.73166053611 +      199.07200143640*t);\r
-   saturn_z_0+=     0.00001460265 * cos( 3.12851339724 +      522.57741809380*t);\r
-   saturn_z_0+=     0.00001148341 * cos( 4.41139213915 +      117.31986822020*t);\r
-   saturn_z_0+=     0.00001092809 * cos( 3.10679381209 +      216.48048917570*t);\r
-   saturn_z_0+=     0.00001015179 * cos( 1.97897195994 +      647.01083331480*t);\r
-   saturn_z_0+=     0.00001098254 * cos( 0.96097709156 +      210.11770170030*t);\r
-   saturn_z_0+=     0.00001028743 * cos( 2.11933059243 +      415.55249061210*t);\r
-\r
-   double saturn_z_1=0.0;\r
-   saturn_z_1+=     0.01906503283 * cos( 4.94544746116 +      213.29909543800*t);\r
-   saturn_z_1+=     0.00528301265 * cos( 3.14159265359 +        0.00000000000*t);\r
-   saturn_z_1+=     0.00130262284 * cos( 2.26140980879 +      206.18554843720*t);\r
-   saturn_z_1+=     0.00101466332 * cos( 1.79095829545 +      220.41264243880*t);\r
-   saturn_z_1+=     0.00085947578 * cos( 0.51612788497 +      426.59819087600*t);\r
-   saturn_z_1+=     0.00022257446 * cos( 3.07684015656 +        7.11354700080*t);\r
-   saturn_z_1+=     0.00016179946 * cos( 1.19987517506 +      419.48464387520*t);\r
-   saturn_z_1+=     0.00009117402 * cos( 6.17205626814 +      639.89728631400*t);\r
-   saturn_z_1+=     0.00007470703 * cos( 0.93135621171 +      433.71173787680*t);\r
-   saturn_z_1+=     0.00004966668 * cos( 0.19044864213 +      316.39186965660*t);\r
-   saturn_z_1+=     0.00003816564 * cos( 4.38284565245 +      110.20632121940*t);\r
-   saturn_z_1+=     0.00002724120 * cos( 1.65580138665 +      227.52618943960*t);\r
-   saturn_z_1+=     0.00001734540 * cos( 3.51628075636 +      103.09277421860*t);\r
-   saturn_z_1+=     0.00001541995 * cos( 2.42323572812 +      199.07200143640*t);\r
-   saturn_z_1+=     0.00001209302 * cos( 2.91140089093 +       14.22709400160*t);\r
-   saturn_z_1=saturn_z_1 * t;\r
-\r
-   double saturn_z_2=0.0;\r
-   saturn_z_2+=     0.00131275155 * cos( 0.08868998101 +      213.29909543800*t);\r
-   saturn_z_2+=     0.00030147649 * cos( 3.91396203887 +      206.18554843720*t);\r
-   saturn_z_2+=     0.00019322173 * cos( 0.09228748624 +      220.41264243880*t);\r
-   saturn_z_2+=     0.00006868926 * cos( 5.48420255395 +      426.59819087600*t);\r
-   saturn_z_2+=     0.00002826107 * cos( 1.36583318555 +        7.11354700080*t);\r
-   saturn_z_2+=     0.00002646332 * cos( 2.94607395955 +      419.48464387520*t);\r
-   saturn_z_2+=     0.00003138233 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_z_2+=     0.00001844798 * cos( 5.43612062856 +      433.71173787680*t);\r
-   saturn_z_2+=     0.00001055383 * cos( 6.23890785179 +      227.52618943960*t);\r
-   saturn_z_2+=     0.00001036435 * cos( 4.33916308552 +      639.89728631400*t);\r
-   saturn_z_2=saturn_z_2 * t * t;\r
-\r
-   double saturn_z_3=0.0;\r
-   saturn_z_3+=     0.00004559419 * cos( 1.70646871501 +      213.29909543800*t);\r
-   saturn_z_3+=     0.00004779074 * cos( 5.57723756330 +      206.18554843720*t);\r
-   saturn_z_3+=     0.00003965402 * cos( 0.00000000000 +        0.00000000000*t);\r
-   saturn_z_3+=     0.00002508242 * cos( 4.64959056313 +      220.41264243880*t);\r
-   saturn_z_3=saturn_z_3 * t * t * t;\r
-\r
-   return saturn_z_0+saturn_z_1+saturn_z_2+saturn_z_3;\r
-}\r
-\r
-double vsop87a_milli_uranus_x(double t){\r
-   double uranus_x_0=0.0;\r
-   uranus_x_0+=    19.17370730359 * cos( 5.48133416489 +       74.78159856730*t);\r
-   uranus_x_0+=     1.32272523872 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_x_0+=     0.44402496796 * cos( 1.65967519586 +      149.56319713460*t);\r
-   uranus_x_0+=     0.14668209481 * cos( 3.42395862804 +       73.29712585900*t);\r
-   uranus_x_0+=     0.14130269479 * cos( 4.39572927934 +       76.26607127560*t);\r
-   uranus_x_0+=     0.06201106178 * cos( 5.14043574125 +        1.48447270830*t);\r
-   uranus_x_0+=     0.01542951343 * cos( 4.12121838072 +      224.34479570190*t);\r
-   uranus_x_0+=     0.01444216660 * cos( 2.65117115201 +      148.07872442630*t);\r
-   uranus_x_0+=     0.00944995563 * cos( 1.65869338757 +       11.04570026390*t);\r
-   uranus_x_0+=     0.00657524815 * cos( 0.57595170636 +      151.04766984290*t);\r
-   uranus_x_0+=     0.00621624676 * cos( 3.05882246638 +       77.75054398390*t);\r
-   uranus_x_0+=     0.00585182542 * cos( 4.79934779678 +       71.81265315070*t);\r
-   uranus_x_0+=     0.00634000270 * cos( 4.09556589724 +       63.73589830340*t);\r
-   uranus_x_0+=     0.00547699056 * cos( 3.63127725056 +       85.82729883120*t);\r
-   uranus_x_0+=     0.00458219984 * cos( 3.90788284112 +        2.96894541660*t);\r
-   uranus_x_0+=     0.00496087649 * cos( 0.59947400861 +      529.69096509460*t);\r
-   uranus_x_0+=     0.00383625535 * cos( 6.18762010576 +      138.51749687070*t);\r
-   uranus_x_0+=     0.00267938156 * cos( 0.96885660137 +      213.29909543800*t);\r
-   uranus_x_0+=     0.00215368005 * cos( 5.30877641428 +       38.13303563780*t);\r
-   uranus_x_0+=     0.00145505389 * cos( 2.31759757085 +       70.84944530420*t);\r
-   uranus_x_0+=     0.00135340032 * cos( 5.51062460816 +       78.71375183040*t);\r
-   uranus_x_0+=     0.00119593859 * cos( 4.10138544267 +       39.61750834610*t);\r
-   uranus_x_0+=     0.00125105686 * cos( 2.51455273063 +      111.43016149680*t);\r
-   uranus_x_0+=     0.00111260244 * cos( 5.12252784325 +      222.86032299360*t);\r
-   uranus_x_0+=     0.00104619827 * cos( 3.90538916334 +      146.59425171800*t);\r
-   uranus_x_0+=     0.00110125387 * cos( 4.45473528724 +       35.16409022120*t);\r
-   uranus_x_0+=     0.00063584588 * cos( 0.29966233158 +      299.12639426920*t);\r
-   uranus_x_0+=     0.00053904041 * cos( 3.92590422507 +        3.93215326310*t);\r
-   uranus_x_0+=     0.00065066905 * cos( 3.73008452906 +      109.94568878850*t);\r
-   uranus_x_0+=     0.00039181662 * cos( 2.68841280769 +        4.45341812490*t);\r
-   uranus_x_0+=     0.00034341683 * cos( 3.03781661928 +      225.82926841020*t);\r
-   uranus_x_0+=     0.00033134636 * cos( 2.54201591218 +       65.22037101170*t);\r
-   uranus_x_0+=     0.00034555652 * cos( 1.84699329257 +       79.23501669220*t);\r
-   uranus_x_0+=     0.00033867050 * cos( 5.98418436103 +       70.32818044240*t);\r
-   uranus_x_0+=     0.00028371614 * cos( 2.58026657123 +      127.47179660680*t);\r
-   uranus_x_0+=     0.00035943348 * cos( 4.08754543016 +      202.25339517410*t);\r
-   uranus_x_0+=     0.00025208833 * cos( 5.30272144657 +        9.56122755560*t);\r
-   uranus_x_0+=     0.00023467802 * cos( 4.09729860322 +      145.63104387150*t);\r
-   uranus_x_0+=     0.00022963939 * cos( 5.51475073655 +       84.34282612290*t);\r
-   uranus_x_0+=     0.00031823951 * cos( 5.53948583244 +      152.53214255120*t);\r
-   uranus_x_0+=     0.00028384953 * cos( 6.01785430306 +      184.72728735580*t);\r
-   uranus_x_0+=     0.00026657176 * cos( 6.11027939727 +      160.60889739850*t);\r
-   uranus_x_0+=     0.00019676762 * cos( 5.53431398332 +       74.66972398270*t);\r
-   uranus_x_0+=     0.00019653873 * cos( 2.28660913421 +       74.89347315190*t);\r
-   uranus_x_0+=     0.00019954280 * cos( 0.57450958037 +       12.53017297220*t);\r
-   uranus_x_0+=     0.00018565067 * cos( 0.62225019017 +       52.69019803950*t);\r
-   uranus_x_0+=     0.00020084756 * cos( 4.47297488471 +       22.09140052780*t);\r
-   uranus_x_0+=     0.00019926329 * cos( 1.39878194708 +      112.91463420510*t);\r
-   uranus_x_0+=     0.00018575632 * cos( 5.70217475790 +       33.67961751290*t);\r
-   uranus_x_0+=     0.00016587870 * cos( 4.86920309163 +      108.46121608020*t);\r
-   uranus_x_0+=     0.00015171194 * cos( 2.88415453399 +       41.10198105440*t);\r
-   uranus_x_0+=     0.00011245800 * cos( 6.11597016146 +       71.60020482960*t);\r
-   uranus_x_0+=     0.00013948521 * cos( 6.27545694160 +      221.37585028530*t);\r
-   uranus_x_0+=     0.00010798350 * cos( 1.70031857078 +       77.96299230500*t);\r
-   uranus_x_0+=     0.00013593955 * cos( 2.55407820633 +       87.31177153950*t);\r
-   uranus_x_0+=     0.00011997848 * cos( 0.94875212305 +     1059.38193018920*t);\r
-   uranus_x_0+=     0.00012884351 * cos( 5.08737999470 +      145.10977900970*t);\r
-   uranus_x_0+=     0.00012394786 * cos( 6.21892878850 +       72.33391801250*t);\r
-   uranus_x_0+=     0.00012253318 * cos( 0.19452856525 +       36.64856292950*t);\r
-   uranus_x_0+=     0.00011538642 * cos( 1.77241794539 +       77.22927912210*t);\r
-   uranus_x_0+=     0.00008738409 * cos( 4.96956808452 +      186.21176006410*t);\r
-   uranus_x_0+=     0.00007095608 * cos( 1.30384750044 +      297.64192156090*t);\r
-   uranus_x_0+=     0.00006262602 * cos( 1.71385983783 +      153.49535039770*t);\r
-   uranus_x_0+=     0.00007487302 * cos( 0.11408470667 +      426.59819087600*t);\r
-   uranus_x_0+=     0.00007798974 * cos( 5.82410372587 +      340.77089204480*t);\r
-   uranus_x_0+=     0.00006669249 * cos( 5.08626589612 +       62.25142559510*t);\r
-   uranus_x_0+=     0.00005505358 * cos( 3.31282108025 +      140.00196957900*t);\r
-   uranus_x_0+=     0.00005372927 * cos( 4.12498282863 +       75.30286342910*t);\r
-   uranus_x_0+=     0.00005354242 * cos( 3.69263973447 +       74.26033370550*t);\r
-   uranus_x_0+=     0.00004478123 * cos( 1.11838191479 +       66.70484372000*t);\r
-   uranus_x_0+=     0.00004233075 * cos( 3.94913608184 +      265.98929347750*t);\r
-   uranus_x_0+=     0.00005038353 * cos( 4.68664376918 +       18.15924726470*t);\r
-   uranus_x_0+=     0.00004570470 * cos( 0.97536665751 +      183.24281464750*t);\r
-   uranus_x_0+=     0.00004751325 * cos( 4.95762395337 +       73.81839072080*t);\r
-   uranus_x_0+=     0.00004448651 * cos( 0.29436142982 +      114.39910691340*t);\r
-   uranus_x_0+=     0.00003312340 * cos( 0.52418923788 +       82.85835341460*t);\r
-   uranus_x_0+=     0.00004515952 * cos( 2.88576303120 +       75.74480641380*t);\r
-   uranus_x_0+=     0.00003559276 * cos( 1.47627607503 +        5.93789083320*t);\r
-   uranus_x_0+=     0.00003268117 * cos( 0.51827231333 +      220.41264243880*t);\r
-   uranus_x_0+=     0.00003578235 * cos( 1.11528903208 +      137.03302416240*t);\r
-   uranus_x_0+=     0.00003004737 * cos( 5.12122132051 +        7.11354700080*t);\r
-   uranus_x_0+=     0.00002882392 * cos( 2.76136583899 +      373.90799283650*t);\r
-   uranus_x_0+=     0.00002579454 * cos( 3.84784330333 +      277.03499374140*t);\r
-   uranus_x_0+=     0.00002597765 * cos( 0.22409539936 +       96.87299909510*t);\r
-   uranus_x_0+=     0.00002560744 * cos( 4.44236223450 +       80.19822453870*t);\r
-   uranus_x_0+=     0.00002722745 * cos( 6.09456175016 +      106.97674337190*t);\r
-   uranus_x_0+=     0.00002528025 * cos( 0.89508396542 +       68.84370773410*t);\r
-   uranus_x_0+=     0.00002631138 * cos( 0.04831552531 +      305.34616939270*t);\r
-   uranus_x_0+=     0.00002541716 * cos( 0.64495056482 +       32.19514480460*t);\r
-   uranus_x_0+=     0.00002241129 * cos( 5.22377697501 +        3.18139373770*t);\r
-   uranus_x_0+=     0.00001965145 * cos( 0.09207526632 +       20.60692781950*t);\r
-   uranus_x_0+=     0.00002232022 * cos( 0.63571664756 +       80.71948940050*t);\r
-   uranus_x_0+=     0.00001933814 * cos( 5.75490033864 +       74.73341445750*t);\r
-   uranus_x_0+=     0.00001933817 * cos( 2.06557585395 +       74.82978267710*t);\r
-   uranus_x_0+=     0.00002138391 * cos( 4.20897429922 +       74.52096613640*t);\r
-   uranus_x_0+=     0.00002126427 * cos( 3.61171465436 +       75.04223099820*t);\r
-   uranus_x_0+=     0.00002215516 * cos( 2.18613112875 +      259.50888592310*t);\r
-   uranus_x_0+=     0.00001891213 * cos( 5.49941424248 +      300.61086697750*t);\r
-   uranus_x_0+=     0.00001927679 * cos( 1.29228021932 +      159.12442469020*t);\r
-   uranus_x_0+=     0.00001796558 * cos( 5.73271543335 +       74.62153987290*t);\r
-   uranus_x_0+=     0.00001792522 * cos( 2.08789166984 +       74.94165726170*t);\r
-   uranus_x_0+=     0.00001873542 * cos( 4.23391867169 +      206.18554843720*t);\r
-   uranus_x_0+=     0.00002182901 * cos( 1.23755478345 +      479.28838891550*t);\r
-   uranus_x_0+=     0.00001860591 * cos( 1.67536711716 +       42.58645376270*t);\r
-   uranus_x_0+=     0.00002075591 * cos( 3.15586933464 +      131.40394986990*t);\r
-   uranus_x_0+=     0.00001912582 * cos( 5.83091918696 +       14.97785352700*t);\r
-   uranus_x_0+=     0.00001529180 * cos( 2.05204104820 +      191.20769491020*t);\r
-   uranus_x_0+=     0.00002064173 * cos( 3.60208606410 +      835.03713448730*t);\r
-   uranus_x_0+=     0.00001768763 * cos( 1.19254481620 +      219.89137757700*t);\r
-   uranus_x_0+=     0.00001892359 * cos( 4.32128621847 +      154.01661525950*t);\r
-   uranus_x_0+=     0.00001797047 * cos( 1.73417465594 +      227.31374111850*t);\r
-   uranus_x_0+=     0.00001558489 * cos( 6.16891070489 +       59.80374504030*t);\r
-   uranus_x_0+=     0.00001672893 * cos( 0.01232646186 +      143.62530630140*t);\r
-   uranus_x_0+=     0.00001467268 * cos( 2.10975578758 +        2.44768055480*t);\r
-   uranus_x_0+=     0.00001677659 * cos( 0.42525121334 +        8.07675484730*t);\r
-   uranus_x_0+=     0.00001347303 * cos( 5.46763140224 +      288.08069400530*t);\r
-   uranus_x_0+=     0.00001744555 * cos( 0.82022450313 +       56.62235130260*t);\r
-   uranus_x_0+=     0.00001427180 * cos( 0.38786175669 +       92.94084583200*t);\r
-   uranus_x_0+=     0.00001263032 * cos( 5.63689596853 +      404.50679034820*t);\r
-   uranus_x_0+=     0.00001221506 * cos( 5.20012455894 +       54.17467074780*t);\r
-   uranus_x_0+=     0.00001574905 * cos( 5.72297800263 +       39.35687591520*t);\r
-   uranus_x_0+=     0.00001269686 * cos( 2.66330104031 +      142.44965013380*t);\r
-   uranus_x_0+=     0.00001438869 * cos( 0.72633739717 +      522.57741809380*t);\r
-   uranus_x_0+=     0.00001408997 * cos( 3.61751904356 +      536.80451209540*t);\r
-   uranus_x_0+=     0.00001418300 * cos( 2.29718712012 +      235.39049596580*t);\r
-   uranus_x_0+=     0.00001040948 * cos( 2.74644165501 +        5.41662597140*t);\r
-   uranus_x_0+=     0.00001256867 * cos( 5.61684736425 +       67.66805156650*t);\r
-   uranus_x_0+=     0.00001164218 * cos( 2.08302637541 +       81.89514556810*t);\r
-   uranus_x_0+=     0.00001009252 * cos( 2.02320517037 +       74.03083904190*t);\r
-   uranus_x_0+=     0.00001077810 * cos( 1.05685900920 +      128.95626931510*t);\r
-   uranus_x_0+=     0.00001212025 * cos( 3.41577832660 +      211.81462272970*t);\r
-   uranus_x_0+=     0.00001223648 * cos( 3.84373514640 +      187.69623277240*t);\r
-   uranus_x_0+=     0.00001334371 * cos( 2.17621743689 +      380.12776796000*t);\r
-   uranus_x_0+=     0.00001144565 * cos( 2.42148845239 +      296.15744885260*t);\r
-   uranus_x_0+=     0.00001241589 * cos( 1.81282962357 +      134.58534360760*t);\r
-   uranus_x_0+=     0.00001192274 * cos( 5.58661990233 +       50.40257617910*t);\r
-   uranus_x_0+=     0.00001093678 * cos( 3.94451812233 +      230.56457082540*t);\r
-   uranus_x_0+=     0.00001077916 * cos( 5.01417740021 +      181.75834193920*t);\r
-   uranus_x_0+=     0.00001166898 * cos( 2.02955848543 +      110.20632121940*t);\r
-   uranus_x_0+=     0.00001056848 * cos( 5.53440854164 +       14.01464568050*t);\r
-\r
-   double uranus_x_1=0.0;\r
-   uranus_x_1+=     0.00739730021 * cos( 6.01067825116 +      149.56319713460*t);\r
-   uranus_x_1+=     0.00526878306 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_x_1+=     0.00239840801 * cos( 5.33657762707 +       73.29712585900*t);\r
-   uranus_x_1+=     0.00229676787 * cos( 2.48204455775 +       76.26607127560*t);\r
-   uranus_x_1+=     0.00111045158 * cos( 5.57157235960 +       11.04570026390*t);\r
-   uranus_x_1+=     0.00096352822 * cos( 0.35070389084 +       63.73589830340*t);\r
-   uranus_x_1+=     0.00081511870 * cos( 1.21058618039 +       85.82729883120*t);\r
-   uranus_x_1+=     0.00045687564 * cos( 2.29216583843 +      138.51749687070*t);\r
-   uranus_x_1+=     0.00051382501 * cos( 2.18935125260 +      224.34479570190*t);\r
-   uranus_x_1+=     0.00038844330 * cos( 0.30724575951 +       70.84944530420*t);\r
-   uranus_x_1+=     0.00036158493 * cos( 1.23634798757 +       78.71375183040*t);\r
-   uranus_x_1+=     0.00032333094 * cos( 5.06666556704 +       74.78159856730*t);\r
-   uranus_x_1+=     0.00021685656 * cos( 4.93710968392 +      151.04766984290*t);\r
-   uranus_x_1+=     0.00019441970 * cos( 1.30617490304 +       77.75054398390*t);\r
-   uranus_x_1+=     0.00017376241 * cos( 0.24607221230 +       71.81265315070*t);\r
-   uranus_x_1+=     0.00015211071 * cos( 5.53141633140 +        3.93215326310*t);\r
-   uranus_x_1+=     0.00007735984 * cos( 1.61349552789 +       71.60020482960*t);\r
-   uranus_x_1+=     0.00007425078 * cos( 6.20357977116 +       77.96299230500*t);\r
-   uranus_x_1+=     0.00006995857 * cos( 2.40633283814 +      145.63104387150*t);\r
-   uranus_x_1+=     0.00007291691 * cos( 2.23597571444 +        2.96894541660*t);\r
-   uranus_x_1+=     0.00007585264 * cos( 2.76074218330 +      148.07872442630*t);\r
-   uranus_x_1+=     0.00004378335 * cos( 3.74296322240 +      160.60889739850*t);\r
-   uranus_x_1+=     0.00004127713 * cos( 1.48475181305 +       22.09140052780*t);\r
-   uranus_x_1+=     0.00003933541 * cos( 4.73864204208 +       65.22037101170*t);\r
-   uranus_x_1+=     0.00002910312 * cos( 5.91941333050 +      127.47179660680*t);\r
-   uranus_x_1+=     0.00002788434 * cos( 4.90117297196 +      213.29909543800*t);\r
-   uranus_x_1+=     0.00002802392 * cos( 3.76505436434 +       52.69019803950*t);\r
-   uranus_x_1+=     0.00002545264 * cos( 3.36768337628 +        9.56122755560*t);\r
-   uranus_x_1+=     0.00002647073 * cos( 4.53813176345 +       12.53017297220*t);\r
-   uranus_x_1+=     0.00003177614 * cos( 4.65226634926 +      299.12639426920*t);\r
-   uranus_x_1+=     0.00002228396 * cos( 0.18087986338 +       87.31177153950*t);\r
-   uranus_x_1+=     0.00002824668 * cos( 3.40143685673 +       84.34282612290*t);\r
-   uranus_x_1+=     0.00002525203 * cos( 2.83821144961 +       18.15924726470*t);\r
-   uranus_x_1+=     0.00002216987 * cos( 4.78338909951 +       72.33391801250*t);\r
-   uranus_x_1+=     0.00001624493 * cos( 3.75817281127 +      153.49535039770*t);\r
-   uranus_x_1+=     0.00001928881 * cos( 2.39940180311 +       39.61750834610*t);\r
-   uranus_x_1+=     0.00001555444 * cos( 4.13741667297 +       73.81839072080*t);\r
-   uranus_x_1+=     0.00001600865 * cos( 0.08376247543 +       79.23501669220*t);\r
-   uranus_x_1+=     0.00001476317 * cos( 3.67283851029 +       75.74480641380*t);\r
-   uranus_x_1+=     0.00001427088 * cos( 1.45690759014 +       70.32818044240*t);\r
-   uranus_x_1+=     0.00001533469 * cos( 3.71776498048 +      152.53214255120*t);\r
-   uranus_x_1+=     0.00001747266 * cos( 3.24870046809 +       77.22927912210*t);\r
-   uranus_x_1+=     0.00001716831 * cos( 3.39415662657 +      222.86032299360*t);\r
-   uranus_x_1+=     0.00001707504 * cos( 1.11296012106 +      225.82926841020*t);\r
-   uranus_x_1+=     0.00001734228 * cos( 5.39619902298 +      146.59425171800*t);\r
-   uranus_x_1+=     0.00001476617 * cos( 3.53047075439 +        3.18139373770*t);\r
-   uranus_x_1+=     0.00001179645 * cos( 5.13953276367 +      220.41264243880*t);\r
-   uranus_x_1+=     0.00001239315 * cos( 0.98221206501 +        4.45341812490*t);\r
-   uranus_x_1+=     0.00001099691 * cos( 1.35138854505 +       62.25142559510*t);\r
-   uranus_x_1+=     0.00001061312 * cos( 5.00125105380 +      131.40394986990*t);\r
-   uranus_x_1+=     0.00001029261 * cos( 5.42434597865 +      109.94568878850*t);\r
-   uranus_x_1=uranus_x_1 * t;\r
-\r
-   double uranus_x_2=0.0;\r
-   uranus_x_2+=     0.00016015732 * cos( 3.83700026619 +       74.78159856730*t);\r
-   uranus_x_2+=     0.00010915299 * cos( 3.02987776270 +      149.56319713460*t);\r
-   uranus_x_2+=     0.00007497619 * cos( 3.83429136661 +       11.04570026390*t);\r
-   uranus_x_2+=     0.00008053623 * cos( 2.54646146122 +       63.73589830340*t);\r
-   uranus_x_2+=     0.00005408033 * cos( 4.78033642303 +       70.84944530420*t);\r
-   uranus_x_2+=     0.00005021971 * cos( 3.04632772928 +       78.71375183040*t);\r
-   uranus_x_2+=     0.00006717313 * cos( 5.31264214501 +       85.82729883120*t);\r
-   uranus_x_2+=     0.00005284684 * cos( 2.11901942097 +       73.29712585900*t);\r
-   uranus_x_2+=     0.00004874936 * cos( 5.68616132176 +       76.26607127560*t);\r
-   uranus_x_2+=     0.00003002124 * cos( 4.07944398452 +      138.51749687070*t);\r
-   uranus_x_2+=     0.00002521797 * cos( 3.36028253173 +       71.60020482960*t);\r
-   uranus_x_2+=     0.00002413832 * cos( 4.45865225690 +       77.96299230500*t);\r
-   uranus_x_2+=     0.00002221373 * cos( 0.87427485235 +        3.93215326310*t);\r
-   uranus_x_2+=     0.00002291767 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_x_2+=     0.00001040250 * cos( 0.73133408837 +      145.63104387150*t);\r
-   uranus_x_2+=     0.00001046100 * cos( 5.85311910228 +      224.34479570190*t);\r
-   uranus_x_2=uranus_x_2 * t * t;\r
-\r
-   double uranus_x_3=0.0;\r
-   uranus_x_3+=     0.00001307049 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_x_3=uranus_x_3 * t * t * t;\r
-\r
-   return uranus_x_0+uranus_x_1+uranus_x_2+uranus_x_3;\r
-}\r
-\r
-double vsop87a_milli_uranus_y(double t){\r
-   double uranus_y_0=0.0;\r
-   uranus_y_0+=    19.16518231584 * cos( 3.91045677002 +       74.78159856730*t);\r
-   uranus_y_0+=     0.44390465203 * cos( 0.08884111329 +      149.56319713460*t);\r
-   uranus_y_0+=     0.16256125476 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_y_0+=     0.14755940186 * cos( 1.85423280679 +       73.29712585900*t);\r
-   uranus_y_0+=     0.14123958128 * cos( 2.82486076549 +       76.26607127560*t);\r
-   uranus_y_0+=     0.06250078231 * cos( 3.56960243857 +        1.48447270830*t);\r
-   uranus_y_0+=     0.01542668264 * cos( 2.55040539213 +      224.34479570190*t);\r
-   uranus_y_0+=     0.01442356575 * cos( 1.08004542712 +      148.07872442630*t);\r
-   uranus_y_0+=     0.00938975501 * cos( 0.09275714761 +       11.04570026390*t);\r
-   uranus_y_0+=     0.00650331846 * cos( 2.76142680222 +       63.73589830340*t);\r
-   uranus_y_0+=     0.00657343120 * cos( 5.28830704469 +      151.04766984290*t);\r
-   uranus_y_0+=     0.00621326770 * cos( 1.48795811387 +       77.75054398390*t);\r
-   uranus_y_0+=     0.00541961958 * cos( 3.24476486661 +       71.81265315070*t);\r
-   uranus_y_0+=     0.00547472694 * cos( 2.06037924573 +       85.82729883120*t);\r
-   uranus_y_0+=     0.00459589120 * cos( 2.33745536070 +        2.96894541660*t);\r
-   uranus_y_0+=     0.00495936105 * cos( 5.31205753740 +      529.69096509460*t);\r
-   uranus_y_0+=     0.00387922853 * cos( 4.62026923885 +      138.51749687070*t);\r
-   uranus_y_0+=     0.00268363417 * cos( 5.68085299020 +      213.29909543800*t);\r
-   uranus_y_0+=     0.00216239629 * cos( 3.73800767580 +       38.13303563780*t);\r
-   uranus_y_0+=     0.00144032475 * cos( 0.75015700920 +       70.84944530420*t);\r
-   uranus_y_0+=     0.00135290820 * cos( 3.93970260616 +       78.71375183040*t);\r
-   uranus_y_0+=     0.00119670613 * cos( 2.53058783780 +       39.61750834610*t);\r
-   uranus_y_0+=     0.00124868545 * cos( 0.94315917319 +      111.43016149680*t);\r
-   uranus_y_0+=     0.00111204860 * cos( 3.55163219419 +      222.86032299360*t);\r
-   uranus_y_0+=     0.00104507929 * cos( 2.33345675603 +      146.59425171800*t);\r
-   uranus_y_0+=     0.00108584454 * cos( 6.02234848388 +       35.16409022120*t);\r
-   uranus_y_0+=     0.00063573747 * cos( 5.01204967920 +      299.12639426920*t);\r
-   uranus_y_0+=     0.00053289771 * cos( 2.38437587876 +        3.93215326310*t);\r
-   uranus_y_0+=     0.00063774261 * cos( 2.15607602904 +      109.94568878850*t);\r
-   uranus_y_0+=     0.00039218598 * cos( 1.11841109252 +        4.45341812490*t);\r
-   uranus_y_0+=     0.00034205426 * cos( 0.92405922576 +       65.22037101170*t);\r
-   uranus_y_0+=     0.00034334377 * cos( 1.46696169843 +      225.82926841020*t);\r
-   uranus_y_0+=     0.00034538316 * cos( 0.27613780697 +       79.23501669220*t);\r
-   uranus_y_0+=     0.00039256771 * cos( 5.75956853703 +      202.25339517410*t);\r
-   uranus_y_0+=     0.00026157754 * cos( 3.74097610798 +        9.56122755560*t);\r
-   uranus_y_0+=     0.00023427328 * cos( 2.52740125551 +      145.63104387150*t);\r
-   uranus_y_0+=     0.00022933138 * cos( 3.94455540350 +       84.34282612290*t);\r
-   uranus_y_0+=     0.00031816303 * cos( 3.96860170484 +      152.53214255120*t);\r
-   uranus_y_0+=     0.00025237176 * cos( 4.45141413666 +       70.32818044240*t);\r
-   uranus_y_0+=     0.00028372491 * cos( 4.44714627097 +      184.72728735580*t);\r
-   uranus_y_0+=     0.00026652859 * cos( 4.53944395347 +      160.60889739850*t);\r
-   uranus_y_0+=     0.00019666208 * cos( 3.96350065335 +       74.66972398270*t);\r
-   uranus_y_0+=     0.00019643845 * cos( 0.71577796385 +       74.89347315190*t);\r
-   uranus_y_0+=     0.00019838981 * cos( 5.29113397354 +       12.53017297220*t);\r
-   uranus_y_0+=     0.00021523908 * cos( 4.93565132068 +       36.64856292950*t);\r
-   uranus_y_0+=     0.00015537967 * cos( 1.87863275460 +       52.69019803950*t);\r
-   uranus_y_0+=     0.00020115100 * cos( 3.45473780762 +      127.47179660680*t);\r
-   uranus_y_0+=     0.00020051641 * cos( 2.90386352937 +       22.09140052780*t);\r
-   uranus_y_0+=     0.00019901477 * cos( 6.11075402434 +      112.91463420510*t);\r
-   uranus_y_0+=     0.00018126776 * cos( 0.98478853787 +       33.67961751290*t);\r
-   uranus_y_0+=     0.00015174962 * cos( 1.31314034959 +       41.10198105440*t);\r
-   uranus_y_0+=     0.00011239020 * cos( 4.54508334011 +       71.60020482960*t);\r
-   uranus_y_0+=     0.00013948849 * cos( 4.70474945682 +      221.37585028530*t);\r
-   uranus_y_0+=     0.00010819728 * cos( 0.12807029856 +       77.96299230500*t);\r
-   uranus_y_0+=     0.00013589665 * cos( 0.98313719930 +       87.31177153950*t);\r
-   uranus_y_0+=     0.00011996772 * cos( 5.66129275335 +     1059.38193018920*t);\r
-   uranus_y_0+=     0.00012407787 * cos( 4.64945783340 +       72.33391801250*t);\r
-   uranus_y_0+=     0.00011531140 * cos( 0.20190074645 +       77.22927912210*t);\r
-   uranus_y_0+=     0.00008736150 * cos( 3.39874828293 +      186.21176006410*t);\r
-   uranus_y_0+=     0.00007093587 * cos( 6.01613487245 +      297.64192156090*t);\r
-   uranus_y_0+=     0.00006408245 * cos( 3.93246367895 +       62.25142559510*t);\r
-   uranus_y_0+=     0.00006261153 * cos( 0.14258542752 +      153.49535039770*t);\r
-   uranus_y_0+=     0.00007494000 * cos( 4.82565771386 +      426.59819087600*t);\r
-   uranus_y_0+=     0.00007856014 * cos( 1.12354254831 +      340.77089204480*t);\r
-   uranus_y_0+=     0.00005516018 * cos( 1.73758326119 +      140.00196957900*t);\r
-   uranus_y_0+=     0.00005556643 * cos( 3.68095215063 +      145.10977900970*t);\r
-   uranus_y_0+=     0.00005368405 * cos( 2.55422957958 +       75.30286342910*t);\r
-   uranus_y_0+=     0.00005350948 * cos( 2.12171493922 +       74.26033370550*t);\r
-   uranus_y_0+=     0.00004508794 * cos( 5.82224064821 +       66.70484372000*t);\r
-   uranus_y_0+=     0.00004290374 * cos( 5.54490766551 +      265.98929347750*t);\r
-   uranus_y_0+=     0.00005013871 * cos( 3.11907749268 +       18.15924726470*t);\r
-   uranus_y_0+=     0.00004326138 * cos( 5.70135056853 +      183.24281464750*t);\r
-   uranus_y_0+=     0.00004750018 * cos( 3.38678300054 +       73.81839072080*t);\r
-   uranus_y_0+=     0.00004445347 * cos( 5.00638490308 +      114.39910691340*t);\r
-   uranus_y_0+=     0.00003314154 * cos( 5.23054574329 +       82.85835341460*t);\r
-   uranus_y_0+=     0.00004509054 * cos( 1.31254342829 +       75.74480641380*t);\r
-   uranus_y_0+=     0.00003553107 * cos( 6.18906516846 +        5.93789083320*t);\r
-   uranus_y_0+=     0.00003265634 * cos( 5.23063560176 +      220.41264243880*t);\r
-   uranus_y_0+=     0.00003575435 * cos( 5.83994849224 +      137.03302416240*t);\r
-   uranus_y_0+=     0.00002880734 * cos( 1.19038424330 +      373.90799283650*t);\r
-   uranus_y_0+=     0.00002885443 * cos( 3.50279993038 +        7.11354700080*t);\r
-   uranus_y_0+=     0.00002594155 * cos( 4.93691413537 +       96.87299909510*t);\r
-   uranus_y_0+=     0.00002559357 * cos( 2.87184237678 +       80.19822453870*t);\r
-   uranus_y_0+=     0.00002676616 * cos( 1.61805362044 +      305.34616939270*t);\r
-   uranus_y_0+=     0.00002246530 * cos( 5.80081898763 +      108.46121608020*t);\r
-   uranus_y_0+=     0.00002474483 * cos( 2.21173751117 +       32.19514480460*t);\r
-   uranus_y_0+=     0.00002060991 * cos( 6.24178596384 +       56.62235130260*t);\r
-   uranus_y_0+=     0.00001958711 * cos( 4.80807045815 +       20.60692781950*t);\r
-   uranus_y_0+=     0.00002227451 * cos( 5.34765264557 +       80.71948940050*t);\r
-   uranus_y_0+=     0.00001937874 * cos( 0.49529839431 +       74.82978267710*t);\r
-   uranus_y_0+=     0.00001937871 * cos( 4.18462288684 +       74.73341445750*t);\r
-   uranus_y_0+=     0.00002164382 * cos( 0.47581392325 +        3.18139373770*t);\r
-   uranus_y_0+=     0.00002138407 * cos( 2.63817804331 +       74.52096613640*t);\r
-   uranus_y_0+=     0.00002130909 * cos( 2.04143912495 +       75.04223099820*t);\r
-   uranus_y_0+=     0.00001787737 * cos( 0.32096699926 +        2.44768055480*t);\r
-   uranus_y_0+=     0.00002212861 * cos( 0.61491281306 +      259.50888592310*t);\r
-   uranus_y_0+=     0.00001889369 * cos( 3.92852240171 +      300.61086697750*t);\r
-   uranus_y_0+=     0.00002275258 * cos( 1.55666401505 +      131.40394986990*t);\r
-   uranus_y_0+=     0.00001925946 * cos( 6.00527473515 +      159.12442469020*t);\r
-   uranus_y_0+=     0.00001802494 * cos( 4.16218259902 +       74.62153987290*t);\r
-   uranus_y_0+=     0.00001796292 * cos( 0.51761494342 +       74.94165726170*t);\r
-   uranus_y_0+=     0.00002240648 * cos( 0.47739127862 +      181.75834193920*t);\r
-   uranus_y_0+=     0.00001924499 * cos( 2.64284880495 +      206.18554843720*t);\r
-   uranus_y_0+=     0.00001626134 * cos( 3.70023731184 +      191.20769491020*t);\r
-   uranus_y_0+=     0.00001860824 * cos( 0.10445996392 +       42.58645376270*t);\r
-   uranus_y_0+=     0.00002177437 * cos( 2.80437422101 +      479.28838891550*t);\r
-   uranus_y_0+=     0.00001896184 * cos( 4.26975898003 +       14.97785352700*t);\r
-   uranus_y_0+=     0.00002045249 * cos( 5.17400788104 +      835.03713448730*t);\r
-   uranus_y_0+=     0.00001887812 * cos( 2.75000237791 +      154.01661525950*t);\r
-   uranus_y_0+=     0.00001794754 * cos( 0.16290844853 +      227.31374111850*t);\r
-   uranus_y_0+=     0.00001347410 * cos( 3.89237011696 +      288.08069400530*t);\r
-   uranus_y_0+=     0.00001572826 * cos( 5.93367812903 +      219.89137757700*t);\r
-   uranus_y_0+=     0.00001424804 * cos( 5.10004758033 +       92.94084583200*t);\r
-   uranus_y_0+=     0.00001267766 * cos( 0.92771324396 +      404.50679034820*t);\r
-   uranus_y_0+=     0.00001588897 * cos( 4.15115668974 +       39.35687591520*t);\r
-   uranus_y_0+=     0.00001269786 * cos( 1.09685727529 +      142.44965013380*t);\r
-   uranus_y_0+=     0.00001291065 * cos( 5.67425699047 +       68.84370773410*t);\r
-   uranus_y_0+=     0.00001436850 * cos( 5.44312198350 +      522.57741809380*t);\r
-   uranus_y_0+=     0.00001405564 * cos( 2.04677392527 +      536.80451209540*t);\r
-   uranus_y_0+=     0.00001416917 * cos( 0.72597245494 +      235.39049596580*t);\r
-   uranus_y_0+=     0.00001165315 * cos( 0.51071041452 +       81.89514556810*t);\r
-   uranus_y_0+=     0.00001035262 * cos( 1.20639876458 +        5.41662597140*t);\r
-   uranus_y_0+=     0.00001009454 * cos( 0.45375065997 +       74.03083904190*t);\r
-   uranus_y_0+=     0.00001220696 * cos( 1.84988185963 +      211.81462272970*t);\r
-   uranus_y_0+=     0.00001222886 * cos( 2.27306099902 +      187.69623277240*t);\r
-   uranus_y_0+=     0.00001336792 * cos( 3.74888989756 +      380.12776796000*t);\r
-   uranus_y_0+=     0.00001151803 * cos( 0.46579056125 +      128.95626931510*t);\r
-   uranus_y_0+=     0.00001149114 * cos( 0.85101218281 +      296.15744885260*t);\r
-   uranus_y_0+=     0.00001163762 * cos( 5.51157783762 +      230.56457082540*t);\r
-   uranus_y_0+=     0.00001192292 * cos( 0.68084398426 +       99.16062095550*t);\r
-   uranus_y_0+=     0.00001151286 * cos( 4.01147735438 +       67.66805156650*t);\r
-   uranus_y_0+=     0.00001189801 * cos( 4.01778306134 +       50.40257617910*t);\r
-   uranus_y_0+=     0.00001015998 * cos( 1.00290501307 +       35.42472265210*t);\r
-   uranus_y_0+=     0.00001174953 * cos( 0.45683512473 +      110.20632121940*t);\r
-   uranus_y_0+=     0.00001051606 * cos( 3.96907647535 +       14.01464568050*t);\r
-\r
-   double uranus_y_1=0.0;\r
-   uranus_y_1+=     0.02157896385 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_y_1+=     0.00739227349 * cos( 4.43963890935 +      149.56319713460*t);\r
-   uranus_y_1+=     0.00238545685 * cos( 3.76882493145 +       73.29712585900*t);\r
-   uranus_y_1+=     0.00229396424 * cos( 0.91090183978 +       76.26607127560*t);\r
-   uranus_y_1+=     0.00110137111 * cos( 4.00844441616 +       11.04570026390*t);\r
-   uranus_y_1+=     0.00094979054 * cos( 5.07141537066 +       63.73589830340*t);\r
-   uranus_y_1+=     0.00081474163 * cos( 5.92275367106 +       85.82729883120*t);\r
-   uranus_y_1+=     0.00045457174 * cos( 0.73292241207 +      138.51749687070*t);\r
-   uranus_y_1+=     0.00051366974 * cos( 0.61844114994 +      224.34479570190*t);\r
-   uranus_y_1+=     0.00038296005 * cos( 5.01873578671 +       70.84944530420*t);\r
-   uranus_y_1+=     0.00036146116 * cos( 5.94859452787 +       78.71375183040*t);\r
-   uranus_y_1+=     0.00032420558 * cos( 4.32617271732 +       74.78159856730*t);\r
-   uranus_y_1+=     0.00021673269 * cos( 3.36607263522 +      151.04766984290*t);\r
-   uranus_y_1+=     0.00019425087 * cos( 6.01842187783 +       77.75054398390*t);\r
-   uranus_y_1+=     0.00017393206 * cos( 4.96098895488 +       71.81265315070*t);\r
-   uranus_y_1+=     0.00014991169 * cos( 3.97176856758 +        3.93215326310*t);\r
-   uranus_y_1+=     0.00007732367 * cos( 0.04256630122 +       71.60020482960*t);\r
-   uranus_y_1+=     0.00007438492 * cos( 4.63165436478 +       77.96299230500*t);\r
-   uranus_y_1+=     0.00006979238 * cos( 0.83723520791 +      145.63104387150*t);\r
-   uranus_y_1+=     0.00007321559 * cos( 0.66348425538 +        2.96894541660*t);\r
-   uranus_y_1+=     0.00007595636 * cos( 1.19807643487 +      148.07872442630*t);\r
-   uranus_y_1+=     0.00004376824 * cos( 2.17182724016 +      160.60889739850*t);\r
-   uranus_y_1+=     0.00003962568 * cos( 3.18042711824 +       65.22037101170*t);\r
-   uranus_y_1+=     0.00004117202 * cos( 6.19931612790 +       22.09140052780*t);\r
-   uranus_y_1+=     0.00002830313 * cos( 3.34365222278 +      213.29909543800*t);\r
-   uranus_y_1+=     0.00002690065 * cos( 1.78946471198 +        9.56122755560*t);\r
-   uranus_y_1+=     0.00002628159 * cos( 2.97459067399 +       12.53017297220*t);\r
-   uranus_y_1+=     0.00003174617 * cos( 3.08131638838 +      299.12639426920*t);\r
-   uranus_y_1+=     0.00002227510 * cos( 4.89407437055 +       87.31177153950*t);\r
-   uranus_y_1+=     0.00002821206 * cos( 1.83130010947 +       84.34282612290*t);\r
-   uranus_y_1+=     0.00002510693 * cos( 1.27166561854 +       18.15924726470*t);\r
-   uranus_y_1+=     0.00002224655 * cos( 3.21541108798 +       72.33391801250*t);\r
-   uranus_y_1+=     0.00001929662 * cos( 4.68383962079 +       52.69019803950*t);\r
-   uranus_y_1+=     0.00001707606 * cos( 5.60415260609 +      127.47179660680*t);\r
-   uranus_y_1+=     0.00001620819 * cos( 2.18676505386 +      153.49535039770*t);\r
-   uranus_y_1+=     0.00001926926 * cos( 0.82821252139 +       39.61750834610*t);\r
-   uranus_y_1+=     0.00001555611 * cos( 2.56681954823 +       73.81839072080*t);\r
-   uranus_y_1+=     0.00001595703 * cos( 4.79558057860 +       79.23501669220*t);\r
-   uranus_y_1+=     0.00001476550 * cos( 2.10111459539 +       75.74480641380*t);\r
-   uranus_y_1+=     0.00001531355 * cos( 2.14701519407 +      152.53214255120*t);\r
-   uranus_y_1+=     0.00001744564 * cos( 1.67897185084 +       77.22927912210*t);\r
-   uranus_y_1+=     0.00001713945 * cos( 1.82334975258 +      222.86032299360*t);\r
-   uranus_y_1+=     0.00001705184 * cos( 5.82532917611 +      225.82926841020*t);\r
-   uranus_y_1+=     0.00001739019 * cos( 3.82452086292 +      146.59425171800*t);\r
-   uranus_y_1+=     0.00001291753 * cos( 6.19666243545 +       70.32818044240*t);\r
-   uranus_y_1+=     0.00001458526 * cos( 5.10147126404 +        3.18139373770*t);\r
-   uranus_y_1+=     0.00001179966 * cos( 3.56807126055 +      220.41264243880*t);\r
-   uranus_y_1+=     0.00001234914 * cos( 5.69239889831 +        4.45341812490*t);\r
-   uranus_y_1+=     0.00001075108 * cos( 1.76286452034 +       56.62235130260*t);\r
-   uranus_y_1+=     0.00001035661 * cos( 6.12642568708 +       62.25142559510*t);\r
-   uranus_y_1+=     0.00001114523 * cos( 3.41304662369 +      131.40394986990*t);\r
-   uranus_y_1+=     0.00001050191 * cos( 3.84176879347 +      109.94568878850*t);\r
-   uranus_y_1=uranus_y_1 * t;\r
-\r
-   double uranus_y_2=0.0;\r
-   uranus_y_2+=     0.00034812647 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_y_2+=     0.00016589194 * cos( 2.29556740620 +       74.78159856730*t);\r
-   uranus_y_2+=     0.00010905147 * cos( 1.45737963668 +      149.56319713460*t);\r
-   uranus_y_2+=     0.00007484633 * cos( 2.27968076918 +       11.04570026390*t);\r
-   uranus_y_2+=     0.00007964298 * cos( 0.97230247087 +       63.73589830340*t);\r
-   uranus_y_2+=     0.00005307100 * cos( 3.20519221878 +       70.84944530420*t);\r
-   uranus_y_2+=     0.00005018595 * cos( 1.47518527303 +       78.71375183040*t);\r
-   uranus_y_2+=     0.00006713255 * cos( 3.74148881189 +       85.82729883120*t);\r
-   uranus_y_2+=     0.00005265170 * cos( 0.54901216905 +       73.29712585900*t);\r
-   uranus_y_2+=     0.00004864822 * cos( 4.11367426823 +       76.26607127560*t);\r
-   uranus_y_2+=     0.00002995853 * cos( 2.49432193549 +      138.51749687070*t);\r
-   uranus_y_2+=     0.00002519021 * cos( 1.78896824345 +       71.60020482960*t);\r
-   uranus_y_2+=     0.00002418371 * cos( 2.88675006488 +       77.96299230500*t);\r
-   uranus_y_2+=     0.00002185856 * cos( 5.58862614977 +        3.93215326310*t);\r
-   uranus_y_2+=     0.00001035578 * cos( 5.44752448275 +      145.63104387150*t);\r
-   uranus_y_2+=     0.00001044459 * cos( 4.27972239360 +      224.34479570190*t);\r
-   uranus_y_2=uranus_y_2 * t * t;\r
-\r
-   double uranus_y_3=0.0;\r
-   uranus_y_3+=     0.00001211380 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_y_3=uranus_y_3 * t * t * t;\r
-\r
-   return uranus_y_0+uranus_y_1+uranus_y_2+uranus_y_3;\r
-}\r
-\r
-double vsop87a_milli_uranus_z(double t){\r
-   double uranus_z_0=0.0;\r
-   uranus_z_0+=     0.25878127698 * cos( 2.61861272578 +       74.78159856730*t);\r
-   uranus_z_0+=     0.01774318778 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_z_0+=     0.00599316131 * cos( 5.08119500585 +      149.56319713460*t);\r
-   uranus_z_0+=     0.00190281890 * cos( 1.61643841193 +       76.26607127560*t);\r
-   uranus_z_0+=     0.00190881685 * cos( 0.57869575952 +       73.29712585900*t);\r
-   uranus_z_0+=     0.00084626761 * cos( 2.26030150166 +        1.48447270830*t);\r
-   uranus_z_0+=     0.00030734257 * cos( 0.23571721555 +       63.73589830340*t);\r
-   uranus_z_0+=     0.00020842052 * cos( 1.26054208091 +      224.34479570190*t);\r
-   uranus_z_0+=     0.00019734273 * cos( 6.04314677688 +      148.07872442630*t);\r
-   uranus_z_0+=     0.00012537530 * cos( 5.17169051466 +       11.04570026390*t);\r
-   uranus_z_0+=     0.00014582864 * cos( 6.14852037212 +       71.81265315070*t);\r
-   uranus_z_0+=     0.00010407529 * cos( 3.65320417038 +      213.29909543800*t);\r
-   uranus_z_0+=     0.00011261541 * cos( 3.55973769686 +      529.69096509460*t);\r
-   uranus_z_0+=     0.00008855669 * cos( 4.03774505739 +      151.04766984290*t);\r
-   uranus_z_0+=     0.00008239460 * cos( 0.34225652715 +       77.75054398390*t);\r
-   uranus_z_0+=     0.00007950169 * cos( 0.72564903051 +       85.82729883120*t);\r
-   uranus_z_0+=     0.00006867469 * cos( 0.81417174224 +        2.96894541660*t);\r
-   uranus_z_0+=     0.00005648720 * cos( 3.45324719543 +      138.51749687070*t);\r
-   uranus_z_0+=     0.00004581938 * cos( 1.69668682344 +       38.13303563780*t);\r
-   uranus_z_0+=     0.00002578399 * cos( 5.19696447390 +      111.43016149680*t);\r
-   uranus_z_0+=     0.00002964070 * cos( 6.14338802239 +       35.16409022120*t);\r
-   uranus_z_0+=     0.00001884104 * cos( 2.61192472648 +       78.71375183040*t);\r
-   uranus_z_0+=     0.00002330304 * cos( 5.72640226150 +       70.84944530420*t);\r
-   uranus_z_0+=     0.00001985215 * cos( 0.76408839812 +       39.61750834610*t);\r
-   uranus_z_0+=     0.00001743154 * cos( 1.20586281789 +      146.59425171800*t);\r
-   uranus_z_0+=     0.00002037011 * cos( 0.95353587037 +       70.32818044240*t);\r
-   uranus_z_0+=     0.00001508924 * cos( 2.26195448553 +      222.86032299360*t);\r
-   uranus_z_0+=     0.00001082736 * cos( 2.62872874057 +      108.46121608020*t);\r
-   uranus_z_0+=     0.00001051968 * cos( 0.75560753840 +      109.94568878850*t);\r
-\r
-   double uranus_z_1=0.0;\r
-   uranus_z_1+=     0.00655916626 * cos( 0.01271947660 +       74.78159856730*t);\r
-   uranus_z_1+=     0.00049648951 * cos( 0.00000000000 +        0.00000000000*t);\r
-   uranus_z_1+=     0.00023874178 * cos( 2.73870491220 +      149.56319713460*t);\r
-   uranus_z_1+=     0.00007552177 * cos( 5.49304207700 +       76.26607127560*t);\r
-   uranus_z_1+=     0.00005941304 * cos( 3.61254073304 +       73.29712585900*t);\r
-   uranus_z_1+=     0.00002868429 * cos( 4.17954157878 +       63.73589830340*t);\r
-   uranus_z_1+=     0.00002087455 * cos( 5.97858625817 +        1.48447270830*t);\r
-   uranus_z_1+=     0.00001827697 * cos( 2.71810813335 +       11.04570026390*t);\r
-   uranus_z_1+=     0.00001305063 * cos( 4.52337002195 +       85.82729883120*t);\r
-   uranus_z_1+=     0.00001158250 * cos( 5.31913504112 +      224.34479570190*t);\r
-   uranus_z_1=uranus_z_1 * t;\r
-\r
-   double uranus_z_2=0.0;\r
-   uranus_z_2+=     0.00014697858 * cos( 1.75149165003 +       74.78159856730*t);\r
-   uranus_z_2+=     0.00001600044 * cos( 3.14159265359 +        0.00000000000*t);\r
-   uranus_z_2=uranus_z_2 * t * t;\r
-\r
-   return uranus_z_0+uranus_z_1+uranus_z_2;\r
-}\r
-\r
-double vsop87a_milli_venus_x(double t){\r
-   double venus_x_0=0.0;\r
-   venus_x_0+=     0.72211281391 * cos( 3.17575836361 +    10213.28554621100*t);\r
-   venus_x_0+=     0.00486448018 * cos( 0.00000000000 +        0.00000000000*t);\r
-   venus_x_0+=     0.00244500474 * cos( 4.05566613861 +    20426.57109242200*t);\r
-   venus_x_0+=     0.00002800281 * cos( 0.33147492492 +     2352.86615377180*t);\r
-   venus_x_0+=     0.00001949669 * cos( 4.23196016801 +     1577.34354244780*t);\r
-   venus_x_0+=     0.00001241717 * cos( 4.93573787058 +    30639.85663863300*t);\r
-   venus_x_0+=     0.00001162258 * cos( 2.87958246189 +    18073.70493865020*t);\r
-   venus_x_0+=     0.00001046690 * cos( 1.75434920413 +     6283.07584999140*t);\r
-\r
-   double venus_x_1=0.0;\r
-   venus_x_1+=     0.00033862636 * cos( 3.14159265359 +        0.00000000000*t);\r
-   venus_x_1+=     0.00017234992 * cos( 0.92721124604 +    20426.57109242200*t);\r
-   venus_x_1+=     0.00006510416 * cos( 2.19289889733 +    10213.28554621100*t);\r
-   venus_x_1=venus_x_1 * t;\r
-\r
-   return venus_x_0+venus_x_1;\r
-}\r
-\r
-double vsop87a_milli_venus_y(double t){\r
-   double venus_y_0=0.0;\r
-   venus_y_0+=     0.72324820731 * cos( 1.60573808356 +    10213.28554621100*t);\r
-   venus_y_0+=     0.00549506273 * cos( 3.14159265359 +        0.00000000000*t);\r
-   venus_y_0+=     0.00244884790 * cos( 2.48564954004 +    20426.57109242200*t);\r
-   venus_y_0+=     0.00002789807 * cos( 5.04214523606 +     2352.86615377180*t);\r
-   venus_y_0+=     0.00001933868 * cos( 5.80597990261 +     1577.34354244780*t);\r
-   venus_y_0+=     0.00001243658 * cos( 3.36573697344 +    30639.85663863300*t);\r
-   venus_y_0+=     0.00001164480 * cos( 1.30970620277 +    18073.70493865020*t);\r
-   venus_y_0+=     0.00001041872 * cos( 0.18129136925 +     6283.07584999140*t);\r
-\r
-   double venus_y_1=0.0;\r
-   venus_y_1+=     0.00039231430 * cos( 0.00000000000 +        0.00000000000*t);\r
-   venus_y_1+=     0.00017282326 * cos( 5.63824735900 +    20426.57109242200*t);\r
-   venus_y_1+=     0.00005968075 * cos( 3.60854944086 +    10213.28554621100*t);\r
-   venus_y_1=venus_y_1 * t;\r
-\r
-   double venus_y_2=0.0;\r
-   venus_y_2+=     0.00002007155 * cos( 3.14159265359 +        0.00000000000*t);\r
-   venus_y_2=venus_y_2 * t * t;\r
-\r
-   return venus_y_0+venus_y_1+venus_y_2;\r
-}\r
-\r
-double vsop87a_milli_venus_z(double t){\r
-   double venus_z_0=0.0;\r
-   venus_z_0+=     0.04282990302 * cos( 0.26703856476 +    10213.28554621100*t);\r
-   venus_z_0+=     0.00035588343 * cos( 3.14159265359 +        0.00000000000*t);\r
-   venus_z_0+=     0.00014501879 * cos( 1.14696911390 +    20426.57109242200*t);\r
-\r
-   double venus_z_1=0.0;\r
-   venus_z_1+=     0.00208096402 * cos( 1.88967278742 +    10213.28554621100*t);\r
-   venus_z_1+=     0.00001264989 * cos( 3.71037501321 +    20426.57109242200*t);\r
-   venus_z_1+=     0.00001364144 * cos( 0.00000000000 +        0.00000000000*t);\r
-   venus_z_1=venus_z_1 * t;\r
-\r
-   double venus_z_2=0.0;\r
-   venus_z_2+=     0.00009148044 * cos( 3.34791005272 +    10213.28554621100*t);\r
-   venus_z_2=venus_z_2 * t * t;\r
-\r
-   return venus_z_0+venus_z_1+venus_z_2;\r
-}\r
-\r
diff --git a/movement/lib/vsop87/vsop87a_milli.h b/movement/lib/vsop87/vsop87a_milli.h
deleted file mode 100755 (executable)
index 4c820e8..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-//VSOP87-Multilang http://www.astrogreg.com/vsop87-multilang/index.html\r
-//Greg Miller (gmiller@gregmiller.net) 2019.  Released as Public Domain\r
-\r
-#ifndef VSOP87A_MILLI\r
-#define VSOP87A_MILLI\r
-\r
-   void vsop87a_milli_getEarth(double t,double temp[]);\r
-   void vsop87a_milli_getEmb(double t,double temp[]);\r
-   void vsop87a_milli_getJupiter(double t,double temp[]);\r
-   void vsop87a_milli_getMars(double t,double temp[]);\r
-   void vsop87a_milli_getMercury(double t,double temp[]);\r
-   void vsop87a_milli_getNeptune(double t,double temp[]);\r
-   void vsop87a_milli_getSaturn(double t,double temp[]);\r
-   void vsop87a_milli_getUranus(double t,double temp[]);\r
-   void vsop87a_milli_getVenus(double t,double temp[]);\r
-   void vsop87a_milli_getMoon(double earth[], double emb[],double temp[]);\r
-#endif\r
diff --git a/movement/movement_faces.h b/movement/movement_faces.h
deleted file mode 100644 (file)
index b273204..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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 MOVEMENT_FACES_H_
-#define MOVEMENT_FACES_H_
-
-#include "simple_clock_face.h"
-#include "close_enough_clock_face.h"
-#include "clock_face.h"
-#include "world_clock_face.h"
-#include "preferences_face.h"
-#include "set_time_face.h"
-#include "set_time_hackwatch_face.h"
-#include "pulsometer_face.h"
-#include "temperature_display_face.h"
-#include "temperature_logging_face.h"
-#include "thermistor_testing_face.h"
-#include "character_set_face.h"
-#include "beats_face.h"
-#include "day_one_face.h"
-#include "voltage_face.h"
-#include "stopwatch_face.h"
-#include "totp_face.h"
-#include "totp_face_lfs.h"
-#include "lis2dw_logging_face.h"
-#include "demo_face.h"
-#include "hello_there_face.h"
-#include "sunrise_sunset_face.h"
-#include "countdown_face.h"
-#include "sailing_face.h"
-#include "counter_face.h"
-#include "blinky_face.h"
-#include "moon_phase_face.h"
-#include "accelerometer_data_acquisition_face.h"
-#include "mars_time_face.h"
-#include "orrery_face.h"
-#include "astronomy_face.h"
-#include "tomato_face.h"
-#include "probability_face.h"
-#include "wake_face.h"
-#include "frequency_correction_face.h"
-#include "alarm_face.h"
-#include "ratemeter_face.h"
-#include "rpn_calculator_alt_face.h"
-#include "weeknumber_clock_face.h"
-#include "fast_stopwatch_face.h"
-#include "tachymeter_face.h"
-#include "nanosec_face.h"
-#include "finetune_face.h"
-#include "databank_face.h"
-#include "tempchart_face.h"
-#include "tally_face.h"
-#include "tarot_face.h"
-#include "interval_face.h"
-#include "morsecalc_face.h"
-#include "rpn_calculator_face.h"
-#include "activity_face.h"
-#include "chirpy_demo_face.h"
-#include "ships_bell_face.h"
-#include "lightmeter_face.h"
-#include "discgolf_face.h"
-#include "habit_face.h"
-#include "planetary_time_face.h"
-#include "planetary_hours_face.h"
-#include "breathing_face.h"
-#include "repetition_minute_face.h"
-#include "timer_face.h"
-#include "invaders_face.h"
-#include "world_clock2_face.h"
-#include "time_left_face.h"
-#include "randonaut_face.h"
-#include "toss_up_face.h"
-#include "geomancy_face.h"
-#include "dual_timer_face.h"
-#include "simple_clock_bin_led_face.h"
-#include "menstrual_cycle_face.h"
-#include "flashlight_face.h"
-#include "decimal_time_face.h"
-#include "wyoscan_face.h"
-#include "save_load_face.h"
-#include "day_night_percentage_face.h"
-#include "simple_coin_flip_face.h"
-#include "solstice_face.h"
-#include "couch_to_5k_face.h"
-#include "minute_repeater_decimal_face.h"
-#include "tuning_tones_face.h"
-#include "minmax_face.h"
-#include "kitchen_conversions_face.h"
-#include "butterfly_game_face.h"
-#include "wareki_face.h"
-#include "wordle_face.h"
-#include "endless_runner_face.h"
-#include "periodic_face.h"
-#include "deadline_face.h"
-#include "higher_lower_game_face.h"
-#include "french_revolutionary_face.h"
-#include "minimal_clock_face.h"
-#include "simon_face.h"
-#include "simple_calculator_face.h"
-#include "alarm_thermometer_face.h"
-#include "beeps_face.h"
-#include "accel_interrupt_count_face.h"
-#include "metronome_face.h"
-#include "smallchess_face.h"
-// New includes go above this line.
-
-#endif // MOVEMENT_FACES_H_
diff --git a/movement/watch_faces/clock/close_enough_clock_face.c b/movement/watch_faces/clock/close_enough_clock_face.c
deleted file mode 100644 (file)
index 98a8e70..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 Ruben Nic
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "close_enough_clock_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-const char *words[12] = {
-    "  ",
-    " 5",
-    "10",
-    "15",
-    "20",
-    "25",
-    "30",
-    "35",
-    "40",
-    "45",
-    "50",
-    "55",
-};
-
-static const char *past_word = " P";
-static const char *to_word = " 2";
-static const char *oclock_word = "OC";
-
-// sets when in the five minute period we switch
-// from "X past HH" to  "X to HH+1"
-static const int hour_switch_index = 8;
-
-static void _update_alarm_indicator(bool settings_alarm_enabled, close_enough_clock_state_t *state) {
-    state->alarm_enabled = settings_alarm_enabled;
-    if (state->alarm_enabled) {
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    } else {
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-    };
-}
-
-void close_enough_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(close_enough_clock_state_t));
-    }
-}
-
-void close_enough_clock_face_activate(void *context) {
-    close_enough_clock_state_t *state = (close_enough_clock_state_t *)context;
-
-    if (watch_sleep_animation_is_running()) {
-        watch_stop_sleep_animation();
-    }
-
-    if (movement_clock_mode_24h()) {
-        watch_set_indicator(WATCH_INDICATOR_24H);
-    }
-
-    // show alarm indicator if there is an active alarm
-    _update_alarm_indicator(movement_alarm_enabled(), state);
-
-    // this ensures that none of the five_minute_periods will match, so we always rerender when the face activates
-    state->prev_five_minute_period = -1;
-    state->prev_min_checked = -1;
-}
-
-bool close_enough_clock_face_loop(movement_event_t event, void *context) {
-    close_enough_clock_state_t *state = (close_enough_clock_state_t *)context;
-
-    char buf[11];
-    watch_date_time_t date_time;
-    bool show_next_hour = false;
-    int prev_five_minute_period;
-    int prev_min_checked;
-    int close_enough_hour;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-        case EVENT_LOW_ENERGY_UPDATE:
-            date_time = watch_rtc_get_date_time();
-            prev_five_minute_period = state->prev_five_minute_period;
-            prev_min_checked = state->prev_min_checked;
-
-            // check the battery voltage once a day...
-            if (date_time.unit.day != state->last_battery_check) {
-                state->last_battery_check = date_time.unit.day;
-                watch_enable_adc();
-                uint16_t voltage = watch_get_vcc_voltage();
-                watch_disable_adc();
-                // 2.2 volts will happen when the battery has maybe 5-10% remaining?
-                // we can refine this later.
-                state->battery_low = (voltage < 2200);
-            }
-
-            // ...and set the LAP indicator if low.
-            if (state->battery_low) {
-                watch_set_indicator(WATCH_INDICATOR_LAP);
-            }
-
-            // same minute, skip update
-            if (date_time.unit.minute == prev_min_checked) {
-                break;
-            } else {
-                state->prev_min_checked = date_time.unit.minute;
-            }
-
-            int five_minute_period = (date_time.unit.minute / 5) % 12;
-
-            // If we are 60% to the next 5 interval, move up to the next period
-            if (fmodf(date_time.unit.minute / 5.0f, 1.0f) > 0.5f) {
-                // If we are on the last 5 interval and moving to the next period we need to display the next hour because we are wrapping around
-                if (five_minute_period == 11) {
-                    show_next_hour = true;
-                }
-
-                five_minute_period = (five_minute_period + 1) % 12;
-            }
-
-            // same five_minute_period, skip update
-            if (five_minute_period == prev_five_minute_period) {
-                break;
-            }
-
-            // we don't want to modify date_time.unit.hour just in case other watch faces use it
-            close_enough_hour = date_time.unit.hour;
-
-            // move from "MM(mins) P HH" to "MM(mins) 2 HH+1"
-            if (five_minute_period >= hour_switch_index || show_next_hour) {
-                close_enough_hour = (close_enough_hour + 1) % 24;
-            }
-
-            if (!movement_clock_mode_24h()) {
-                // if we are in 12 hour mode, do some cleanup.
-                if (close_enough_hour < 12) {
-                    watch_clear_indicator(WATCH_INDICATOR_PM);
-                } else {
-                    watch_set_indicator(WATCH_INDICATOR_PM);
-                }
-
-                close_enough_hour %= 12;
-                if (close_enough_hour == 0) {
-                    close_enough_hour = 12;
-                }
-
-                date_time.unit.hour %= 12;
-                if (date_time.unit.hour == 0) {
-                    date_time.unit.hour = 12;
-                }
-            }
-
-            char first_word[3];
-            char second_word[3];
-            char third_word[3];
-            if (five_minute_period == 0) { // "HH  OC",
-                sprintf(first_word, "%2d", close_enough_hour);
-                strncpy(second_word, words[five_minute_period], 3);
-                strncpy(third_word, oclock_word, 3);
-            } else {
-                int words_length = sizeof(words) / sizeof(words[0]);
-
-                strncpy(
-                    first_word,
-                    five_minute_period >= hour_switch_index ?
-                        words[words_length - five_minute_period] :
-                        words[five_minute_period],
-                    3
-                );
-                strncpy(
-                    second_word,
-                    five_minute_period >= hour_switch_index ?
-                        to_word : past_word,
-                    3
-                );
-                sprintf(third_word, "%2d", close_enough_hour);
-            }
-
-            sprintf(
-                buf,
-                "%s%2d%s%s%s",
-                watch_utility_get_weekday(date_time),
-                date_time.unit.day,
-                first_word,
-                second_word,
-                third_word
-            );
-
-            watch_display_string(buf, 0);
-            state->prev_five_minute_period = five_minute_period;
-
-            // handle alarm indicator
-            if (state->alarm_enabled != movement_alarm_enabled()) {
-                _update_alarm_indicator(movement_alarm_enabled(), state);
-            }
-
-            break;
-
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void close_enough_clock_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/clock/close_enough_clock_face.h b/movement/watch_faces/clock/close_enough_clock_face.h
deleted file mode 100644 (file)
index e693043..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 Ruben Nic
- *
- * 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 CLOSE_ENOUGH_CLOCK_FACE_H_
-#define CLOSE_ENOUGH_CLOCK_FACE_H_
-
-/*
- * CLOSE ENOUGH CLOCK FACE
- *
- * Displays the current time; but only in periods of 5.
- * Just in the in the formats of:
- * - "10 past 5"
- * - "15 to 7"
- * - "6 o'clock"
- *
- */
-
-#include "movement.h"
-
-typedef struct {
-    int prev_five_minute_period;
-    int prev_min_checked;
-    uint8_t last_battery_check;
-    bool battery_low;
-    bool alarm_enabled;
-} close_enough_clock_state_t;
-
-void close_enough_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void close_enough_clock_face_activate(void *context);
-bool close_enough_clock_face_loop(movement_event_t event, void *context);
-void close_enough_clock_face_resign(void *context);
-
-#define close_enough_clock_face ((const watch_face_t){ \
-    close_enough_clock_face_setup, \
-    close_enough_clock_face_activate, \
-    close_enough_clock_face_loop, \
-    close_enough_clock_face_resign, \
-    NULL, \
-})
-
-#endif // CLOSE_ENOUGH_CLOCK_FACE_H_
diff --git a/movement/watch_faces/clock/day_night_percentage_face.c b/movement/watch_faces/clock/day_night_percentage_face.c
deleted file mode 100644 (file)
index fbd3d45..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Wesley Aptekar-Cassels
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "day_night_percentage_face.h"
-#include "watch_utility.h"
-#include "sunriset.h"
-
-// fmod but handle negatives right
-static double better_fmod(double x, double y) {
-    return fmod(fmod(x, y) + y, y);
-}
-
-static void recalculate(watch_date_time_t utc_now, day_night_percentage_state_t *state) {
-    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
-
-    if (movement_location.reg == 0) {
-        state->result = -2;
-        return;
-    }
-
-    // Weird quirky unsigned things were happening when I tried to cast these directly to doubles below.
-    // it looks redundant, but extracting them to local int16's seemed to fix it.
-    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
-    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
-
-    double lat = (double)lat_centi / 100.0;
-    double lon = (double)lon_centi / 100.0;
-
-    state->daylen = day_length(utc_now.unit.year + WATCH_RTC_REFERENCE_YEAR, utc_now.unit.month, utc_now.unit.day, lon, lat);
-
-    state->result = sun_rise_set(utc_now.unit.year + WATCH_RTC_REFERENCE_YEAR, utc_now.unit.month, utc_now.unit.day, lon, lat, &state->rise, &state->set);
-}
-
-void day_night_percentage_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(day_night_percentage_state_t));
-        day_night_percentage_state_t *state = (day_night_percentage_state_t *)*context_ptr;
-        watch_date_time_t utc_now = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_get_current_timezone_offset(), 0);
-        recalculate(utc_now, state);
-    }
-}
-
-void day_night_percentage_face_activate(void *context) {
-    (void) context;
-}
-
-bool day_night_percentage_face_loop(movement_event_t event, void *context) {
-    day_night_percentage_state_t *state = (day_night_percentage_state_t *)context;
-
-    char buf[12];
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0);
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-        case EVENT_LOW_ENERGY_UPDATE:
-            if ((utc_now.unit.hour == 0 && utc_now.unit.minute == 0 && utc_now.unit.second == 0) || state->result == -2) {
-                recalculate(utc_now, state);
-            }
-
-            if (state->result == -2) {
-                watch_display_string("    no Loc", 0);
-                break;
-            }
-
-            const char* weekday = watch_utility_get_weekday(date_time);
-            if (state->result != 0) {
-                if (state->result == 1) {
-                    watch_clear_indicator(WATCH_INDICATOR_PM);
-                } else {
-                    watch_set_indicator(WATCH_INDICATOR_PM);
-                }
-                sprintf(buf, "%s%2dEtrnal", weekday, date_time.unit.day);
-                watch_display_string(buf, 0);
-            } else {
-                double day_hours_decimal = utc_now.unit.hour + (utc_now.unit.minute + (utc_now.unit.second / 60.0)) / 60.0;
-
-                double day_percentage = (24.0 - better_fmod(state->rise - day_hours_decimal, 24.0)) / state->daylen;
-                double night_percentage = (24.0 - better_fmod(state->set - day_hours_decimal, 24.0)) / (24 - state->daylen);
-
-                uint16_t percentage;
-                if (day_percentage > 0.0 && day_percentage < 1.0) {
-                    percentage = day_percentage * 10000;
-                    watch_clear_indicator(WATCH_INDICATOR_PM);
-                } else {
-                    percentage = night_percentage * 10000;
-                    watch_set_indicator(WATCH_INDICATOR_PM);
-                }
-                if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
-                    if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
-                    sprintf(buf, "%s%2d  %02d  ", weekday, date_time.unit.day, percentage / 100);
-                } else {
-                    sprintf(buf, "%s%2d  %04d", weekday, date_time.unit.day, percentage);
-                }
-                watch_display_string(buf, 0);
-            }
-
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void day_night_percentage_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/clock/day_night_percentage_face.h b/movement/watch_faces/clock/day_night_percentage_face.h
deleted file mode 100644 (file)
index 01c94de..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Wesley Aptekar-Cassels
- *
- * 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 DAY_NIGHT_PERCENTAGE_FACE_H_
-#define DAY_NIGHT_PERCENTAGE_FACE_H_
-
-#include "movement.h"
-
-/*
- * Day/night percentage face
- *
- * Shows the percentage of the way through the day/night the current time is.
- *
- * The time digits show the percentage of the way through the day/night it is,
- * with decimals in the smaller seconds digits. If the day or night will last
- * for a full 24 hours, the text "Etrnal" is displayed instead of a percentage.
- * The "PM" indicator is set when it is currently nighttime. The weekday and
- * day digits display the weekday and day, as one would expect.
- *
- * This face does not currently offer any configuration. You must set the
- * location register with some other face.
- */
-
-typedef struct {
-    int result; // -1, 0, 1: result from sun_rise_set, -2: no location set
-    double rise;
-    double set;
-    double daylen;
-} day_night_percentage_state_t;
-
-void day_night_percentage_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void day_night_percentage_face_activate(void *context);
-bool day_night_percentage_face_loop(movement_event_t event, void *context);
-void day_night_percentage_face_resign(void *context);
-
-#define day_night_percentage_face ((const watch_face_t){ \
-    day_night_percentage_face_setup, \
-    day_night_percentage_face_activate, \
-    day_night_percentage_face_loop, \
-    day_night_percentage_face_resign, \
-    NULL, \
-})
-
-#endif // DAY_NIGHT_PERCENTAGE_FACE_H_
-
diff --git a/movement/watch_faces/clock/decimal_time_face.c b/movement/watch_faces/clock/decimal_time_face.c
deleted file mode 100644 (file)
index 5056302..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Curtis J. Brown <mrbrown8@juno.com>
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "decimal_time_face.h"
-#include "watch.h"
-
-
-
-void decimal_time_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    // These next two lines just silence the compiler warnings associated with unused parameters.
-    // We have no use for the settings or the watch_face_index, so we make that explicit here.
-    (void) watch_face_index;
-    (void) context_ptr;
-    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
-    if (*context_ptr == NULL) {
-        // in this case, we allocate an area of memory sufficient to store the stuff we need to track.
-        *context_ptr = malloc(sizeof(decimal_time_face_state_t));
-        decimal_time_face_state_t *state = (decimal_time_face_state_t *)*context_ptr;
-        state->chime_enabled = false;
-        state->features_to_show = 0 ;
-    }
-
-
-}
-
-
-void decimal_time_face_activate(void *context) {
-
-    // same as above: silence the warning, we don't need to check the settings.
-
-    // we do however need to set some things in our context. Here we cast it to the correct type...
-    decimal_time_face_state_t *state = (decimal_time_face_state_t *)context;
-
-    watch_set_indicator(WATCH_INDICATOR_24H);  // This face is always 24h, so just set the indicators
-    watch_clear_indicator(WATCH_INDICATOR_PM);
-
-    if (state->chime_enabled) {
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    }
-
-
-}
-
-
-
-bool decimal_time_face_loop(movement_event_t event, void *context) {
-
-    decimal_time_face_state_t *state = (decimal_time_face_state_t *)context;
-
-    char buf[16];
-    uint8_t centihours, decimal_seconds;
-    watch_date_time_t date_time;
-    
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            // on activate and tick
-            
-            date_time = watch_rtc_get_date_time();
-            
-            centihours = (( date_time.unit.minute * 60 + date_time.unit.second ) * 100 ) / 3600;  // Integer division, fractions get dropped, no need for abs() (bonus)
-
-            decimal_seconds = ( date_time.unit.minute * 60 + date_time.unit.second ) % 36 ;
-            
-            switch (state->features_to_show) {
-                case 0:
-                    sprintf( buf, "dT  %02d%02d  ",                       date_time.unit.hour, centihours );
-                    break;
-                case 1:
-                    sprintf( buf, "dT  %02d%02d%2d",                      date_time.unit.hour, centihours, decimal_seconds );
-                    break;
-                case 2:
-                    sprintf( buf, "dT%2d%02d%02d  ",  date_time.unit.day, date_time.unit.hour, centihours );
-                    break;
-                case 3:
-                    sprintf( buf, "dT%2d%02d%02d%2d", date_time.unit.day, date_time.unit.hour, centihours, decimal_seconds );
-                    break;
-            }
-
-            watch_display_string(buf, 0);  // display calculated time
-            
-            // at the top of every hour...
-            if (date_time.unit.minute == 0 && date_time.unit.second == 0 && state->chime_enabled) {
-                watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-                watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
-                watch_buzzer_play_note(BUZZER_NOTE_REST, 15);
-                watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
-                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-            }
-            
-            break;
-
-        case EVENT_ALARM_LONG_PRESS:   
-            state->chime_enabled = !state->chime_enabled;   // just like from simple_watch_face
-            if (state->chime_enabled) 
-                watch_set_indicator(WATCH_INDICATOR_BELL);
-            else
-                watch_clear_indicator(WATCH_INDICATOR_BELL);
-            break;
-
-        case EVENT_ALARM_BUTTON_UP:
-            state->features_to_show += 1 ;  // cycle thru what's to be shown
-            break;
-
-
-        case EVENT_LOW_ENERGY_UPDATE:
-            // This low energy mode update occurs once a minute, if the watch face is in the
-            // foreground when Movement enters low energy mode. We have the option of supporting
-            // this mode, but since our watch face animates once a second, the "Hello there" face
-            // isn't very useful in this mode. So we choose not to support it. (continued below)
-            break;
-
-        case EVENT_TIMEOUT:
-            // ... Instead, we respond to the timeout event. This event happens after a configurable
-            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
-            // resign control if we want to, and in this case, we do.
-            // This function will return the watch to the first screen (usually a simple clock),
-            // and it will do it long before the watch enters low energy mode. This ensures we
-            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
-            //movement_move_to_face(0);
-            break;
-
-        default:
-            movement_default_loop_handler(event);
-            break;
-
-    }
-
-    return true;
-}
-
-void decimal_time_face_resign(void *context) {
-    // our watch face, like most watch faces, has nothing special to do when resigning.
-    // there are no peripherals or sensors here to worry about turning off.
-    (void) context;
-
-}
-
-
-// void decimal_time_face_advise() {
-// 
-// }
-
-
-
-
-
-
-
-
-
-
-
diff --git a/movement/watch_faces/clock/decimal_time_face.h b/movement/watch_faces/clock/decimal_time_face.h
deleted file mode 100644 (file)
index eb1a41e..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Curtis J. Brown <mrbrown8@juno.com>
- *
- * 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 DECIMAL_TIME_FACE_H_
-#define DECIMAL_TIME_FACE_H_
-
-/*
- * DECIMAL TIME face
- * 
- * This face presents the current time as hours and hundredths of an hour. Every hundreth of an hour, or "centihour", 
- * occurs every 36 seconds. Because they range from 0 to 99, centihours, in the seventies range, will be displayed with a lowercase 7.
- * 
- * See https://en.wikipedia.org/wiki/Decimal_time#Decimal_hours
- *  
- * This method of timekeeping is used by the United States Postal Service.
- * http://www.branch38nalc.com/sitebuildercontent/sitebuilderfiles/CONVERSION_TABLE_TIME.pdf
- * https://postalemployeenetwork.com/time-conversion-print.htm
- * 
- * This method may be used by other organizations as well
- * https://www.labor.nc.gov/workplace-rights/employer-responsibilities/time-conversion-chart-minutes-decimal-hours
- * https://uh.edu/office-of-finance/payroll/time_conversion_chart_minutes_to_decimalhours.pdf
- * https://www.placer.ca.gov/DocumentCenter/View/3860/Decimals-to-Minutes-Conversion-Table-PDF
- * https://hr.colostate.edu/minute-to-decimal-conversion-chart/
- * 
- * Many thanks go to Joey Castillo for making this project happen.
- */
-
-#include "movement.h"
-
-typedef struct {
-    bool chime_enabled;            // did the user enable hourly chime for this face? 
-    uint8_t features_to_show : 2 ; // what features are to be displayed?
-} decimal_time_face_state_t;
-
-void decimal_time_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void decimal_time_face_activate(void *context);
-bool decimal_time_face_loop(movement_event_t event, void *context);
-void decimal_time_face_resign(void *context);
-// void decimal_time_face_advise();
-
-
-#define decimal_time_face ((const watch_face_t){ \
-    decimal_time_face_setup, \
-    decimal_time_face_activate, \
-    decimal_time_face_loop, \
-    decimal_time_face_resign, \
-    NULL, \
-})
-
-#endif // DECIMAL_TIME_FACE_H_
diff --git a/movement/watch_faces/clock/french_revolutionary_face.c b/movement/watch_faces/clock/french_revolutionary_face.c
deleted file mode 100644 (file)
index 83564a3..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 CarpeNoctem
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "french_revolutionary_face.h"
-
-void french_revolutionary_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(french_revolutionary_state_t));
-        memset(*context_ptr, 0, sizeof(french_revolutionary_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-        french_revolutionary_state_t *state = (french_revolutionary_state_t *)*context_ptr;
-        state->use_am_pm = false;
-        state->show_seconds = true;
-        state->display_type = 0;
-        state->colon_set_after_splash = false;
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void french_revolutionary_face_activate(void *context) {
-    french_revolutionary_state_t *state = (french_revolutionary_state_t *)context;
-
-    // Handle any tasks related to your watch face coming on screen.
-    state->colon_set_after_splash = false;
-}
-
-bool french_revolutionary_face_loop(movement_event_t event, void *context) {
-    french_revolutionary_state_t *state = (french_revolutionary_state_t *)context;
-
-    char buf[11];
-    watch_date_time_t date_time;
-    fr_decimal_time decimal_time;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Initial UI - Show a quick "splash screen"
-            watch_clear_display();
-            watch_display_string("FR  dECimL", 0);
-            break;
-        case EVENT_TICK:
-        case EVENT_LOW_ENERGY_UPDATE:
-
-            date_time = watch_rtc_get_date_time();
-
-            decimal_time = get_decimal_time(&date_time);
-
-            set_display_buffer(buf, state, &decimal_time, &date_time);
-
-            // If we're in low-energy mode, don't write out the seconds. Also start the LE tick animation if it's not already going.
-            if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
-                buf[8] = ' ';
-                buf[9] = ' ';
-                if (!watch_sleep_animation_is_running()) { watch_start_sleep_animation(500); }
-            }
-
-            // Update the display with our decimal time
-            watch_display_string(buf, 0);
-
-            // Oh, and a one-off to set the colon after the "splash screen"
-            if (!state->colon_set_after_splash) {
-                watch_set_colon();
-                state->colon_set_after_splash = true;
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            state->display_type += 1 ; // cycle through the display types
-            if (state->display_type > 2) { state->display_type = 0; } // but return to 0 after 2
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            // I originally had chiming on the decimal-hour enabled, and this would enable/disable that chime, just like on
-            // the simple clock and decimal time faces. But because decimal seconds don't always line up with normal seconds,
-            // I assume the (decimal-)hourly chime could sometimes be missed. Additionally, I need this button for other purposes,
-            // now that I added seconds on/off toggle and upper normal-time with the ability to toggle that between 12/24hr format.
-            state->show_seconds = !state->show_seconds;
-            if (!state->show_seconds) { watch_display_string("  ", 8); }
-            else { watch_display_string("--", 8); }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            // In case anyone really wants that upper time in 12-hour format. I thought about using the global setting (movement_clock_mode_24h())
-            // for this preference, but thought someone who prefers 12-hour format normally, might prefer 24hr when compared to a 10hr decimal day,
-            // so this is separate for now.
-            state->use_am_pm = !state->use_am_pm;
-            if (state->use_am_pm) {
-                watch_clear_indicator(WATCH_INDICATOR_24H);
-                date_time = watch_rtc_get_date_time();
-                if (date_time.unit.hour < 12) { watch_clear_indicator(WATCH_INDICATOR_PM); }
-                else { watch_set_indicator(WATCH_INDICATOR_PM); }
-            } else {
-                watch_clear_indicator(WATCH_INDICATOR_PM);
-                watch_set_indicator(WATCH_INDICATOR_24H);
-            }
-            break;
-        default:
-            // Movement's default loop handler will step in for any cases you don't handle above:
-            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
-            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
-            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
-            // You can override any of these behaviors by adding a case for these events to this switch statement.
-            return movement_default_loop_handler(event);
-    }
-
-    // return true if the watch can enter standby mode. Generally speaking, you should always return true.
-    // Exceptions:
-    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
-    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
-    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
-    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
-    return true;
-}
-
-void french_revolutionary_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
-
-// Calculate decimal time from normal (24hr) time
-fr_decimal_time get_decimal_time(watch_date_time_t *date_time) {
-    uint32_t current_24hr_secs, current_decimal_seconds;
-    fr_decimal_time decimal_time;
-    // Current 24-hr time in seconds (There are 86400 of these in a day.)
-    current_24hr_secs = date_time->unit.hour * 3600 + date_time->unit.minute * 60 + date_time->unit.second;
-
-    // Current Decimal Time in seconds. There are 100000 seconds in a 10-hr decimal-time day.
-    // current_decimal_seconds = current_24hr_seconds * 100000 / 86400, or = current_24_seconds * 1000 / 864;
-    // By chopping the extra zeros off the end, we can use uint32 instead of uint64.
-    current_decimal_seconds = current_24hr_secs * 1000 / 864;
-
-    decimal_time.hour = current_decimal_seconds / 10000;
-    // Remove the hours from total seconds and keep the remainder for below.
-    current_decimal_seconds = current_decimal_seconds - decimal_time.hour * 10000;
-
-    decimal_time.minute = current_decimal_seconds / 100;
-    // Remove the minutes from total seconds and keep the remaining seconds
-    // Note: I think I used an extra seconds variable here because sprintf or movement weren't liking a uint32...
-    decimal_time.second = current_decimal_seconds - decimal_time.minute * 100;
-    return decimal_time;
-}
-
-// Fills in the display buffer, depending on the currently-selected display option (and sub-options):
-// - Decimal-time only
-// - Decimal-time with date in top-right
-// - Decimal-time with normal time in the top (minutes first, then hours, due to display limitations)
-// TODO: There is some power-saving stuff that simple clock does here around not redrawing characters that haven't changed, but we're not doing that here.
-//       I'll try to add that optimization could be added in a future commit.
-void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time_t *date_time) {
-    switch (state->display_type) {
-                // Decimal time only
-                case 0:
-                    // Originally I had the day slot set to "FR" (French Revolutionary time), but my brain kept thinking "Friday" whenever I saw it,
-                    // so I changed it to dT (Decimal Time) to avoid that confusion. Apologies to anyone who has the other decimal_time face and this one
-                    // installed concurrently. Maybe the splash screen will help a little.
-                    sprintf( buf, "dT  %2d%02d%02d", decimal_time->hour, decimal_time->minute, decimal_time->second );
-                    watch_clear_indicator(WATCH_INDICATOR_PM);
-                    watch_clear_indicator(WATCH_INDICATOR_24H);
-                    break;
-                // Decimal time and date
-                case 1:
-                    sprintf( buf, "dT%2d%2d%02d%02d",  date_time->unit.day, decimal_time->hour, decimal_time->minute, decimal_time->second );
-                    watch_clear_indicator(WATCH_INDICATOR_PM);
-                    watch_clear_indicator(WATCH_INDICATOR_24H);
-                    break;
-                // Decimal time on bottom, normal time above
-                case 2:
-                    if (state->use_am_pm) {
-                        // if we are in 12 hour mode, do some cleanup.
-                        watch_clear_indicator(WATCH_INDICATOR_24H);
-                        if (date_time->unit.hour < 12) {
-                            watch_clear_indicator(WATCH_INDICATOR_PM);
-                        } else {
-                            watch_set_indicator(WATCH_INDICATOR_PM);
-                        }
-                        date_time->unit.hour %= 12;
-                        if (date_time->unit.hour == 0) date_time->unit.hour = 12;
-                    } else {
-                        watch_clear_indicator(WATCH_INDICATOR_PM);
-                        watch_set_indicator(WATCH_INDICATOR_24H);
-                    }
-                    // Note, the date digits don't display a leading zero well, so we don't use it.
-                    sprintf( buf, "%02d%2d%2d%02d%02d", date_time->unit.minute, date_time->unit.hour, decimal_time->hour, decimal_time->minute, decimal_time->second );
-                    
-                    // Make the second character of the Day area more readable
-                    buf[1] = fix_character_one(buf[1]);
-                    break;
-            }
-            // Finally, if show_seconds is disabled, trim those off.
-            if (!state->show_seconds) {
-                buf[8] = ' ';
-                buf[9] = ' ';
-            }
-}
-
-// Sadly, the second character of the Day field cannot show all numbers, so we make some replacements.
-// See https://www.sensorwatch.net/docs/wig/display/#limitations-of-the-weekday-digits
-char fix_character_one(char digit) {
-    char return_char = digit; // We don't need to update this for 0, 1, 3, 7 and 8.
-    switch(digit) {
-        case '2':
-            // Roman numeral / tally representation of 2
-            return_char = '|'; // Thanks, Joey, for already having this in the character set.
-            break;
-        case '4':
-            // Looks almost like a 4 - just missing the top-left segment.
-            // 0b01000110
-            return_char = '&'; // Slight hack - I want 0b01000110, but 0b01000100 is already in the character set and will do, since B and C segments are linked in this position.
-            break;
-        case '5':
-            return_char = 'F'; // F for Five
-            break;
-        case '6':
-            return_char = 'E'; // Looks almost like a 6 - just missing the bottom-right segment. Not super happy with it, but liked it best of the options I tried.
-            break;
-        case '9':
-            return_char = 'N'; // N for Nine
-            break;
-    }
-    return return_char;
-}
diff --git a/movement/watch_faces/clock/french_revolutionary_face.h b/movement/watch_faces/clock/french_revolutionary_face.h
deleted file mode 100644 (file)
index 554bcea..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 CarpeNoctem
- *
- * 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 FRENCH_REVOLUTIONARY_FACE_H_
-#define FRENCH_REVOLUTIONARY_FACE_H_
-
-#include "movement.h"
-
-/*
- * French Revolutionary Decimal Time
- *
- * Similar to the Decimal Time face, but with the day divided into ten hours instead of twenty four.
- * Each hour is divided into one hundred minutes, and those minutes are divided into 100 seconds.
- * I came across this one the Svalbard watch site here: https://svalbard.watch/pages/about_decimal_time.html
- * More info here as well: https://en.wikipedia.org/wiki/Decimal_time
- * 
- * By default, the face just displays the current decimal time. Pressing the alarm button will toggle through other display options:
- * 1) Just decimal time (with dT indicator at top)
- * 2) Decimal time, with dT indicator and date above.
- * 3) Decimal time, with 24-hr time above (where Day and Date would normally be displayed), BUT minutes first then hours.
- *    Sadly, the first character of the date area only goes up to 3 (see https://www.sensorwatch.net/docs/wig/display/#the-day-digits)
- *    I was going to begrudgindly leave this display option out when I realized that, but thought it would be better to have this backwards
- *    representation of the "normal" time than not at all.
- * 
- * A long-press of the light button will toggle the upper time between 12-hr AM/PM and 24-hr mode. I thought of reading the main setting for this,
- * but thought that a person could normally prefer 12hr time, but next to a 10hr day want to see the normal time in the 24hr format.
- * 
- * A long-press of the alarm button will toggle the seconds off and on.
- *
- */
-
-typedef struct {
-    bool use_am_pm; // Use 12-hr AM/PM for upper display instead of 24-hr? (Default is 24-hr)
-    bool show_seconds;
-    bool colon_set_after_splash;
-    uint8_t display_type : 2;
-} french_revolutionary_state_t;
-
-typedef struct {
-    uint8_t second : 8;    // 0-99
-    uint8_t minute : 8;    // 0-99
-    uint8_t hour : 5;      // 0-10
-} fr_decimal_time;
-
-void french_revolutionary_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void french_revolutionary_face_activate(void *context);
-bool french_revolutionary_face_loop(movement_event_t event, void *context);
-void french_revolutionary_face_resign(void *context);
-char fix_character_one(char digit);
-fr_decimal_time get_decimal_time(watch_date_time_t *date_time);
-void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time_t *date_time);
-
-
-#define french_revolutionary_face ((const watch_face_t){ \
-    french_revolutionary_face_setup, \
-    french_revolutionary_face_activate, \
-    french_revolutionary_face_loop, \
-    french_revolutionary_face_resign, \
-    NULL, \
-})
-
-#endif // FRENCH_REVOLUTIONARY_FACE_H_
-
diff --git a/movement/watch_faces/clock/mars_time_face.c b/movement/watch_faces/clock/mars_time_face.c
deleted file mode 100644 (file)
index 5c55cd1..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "watch_utility.h"
-#include "mars_time_face.h"
-
-// note: lander coordinates come from Mars24's `marslandmarks.xml` file
-static double site_longitudes[MARS_TIME_NUM_SITES] = {
-    0,                      // Mars Coordinated Time, at the meridian
-    360.0 - 109.9,          // Zhurong lander site
-    360.0 - 77.45088572,    // Perseverance lander site
-    360.0 - 135.623447,     // InSight lander site
-    360.0 - 137.441635,     // Curiosity lander site
-};
-
-static char site_names[MARS_TIME_NUM_SITES][3] = {
-    "MC",
-    "ZH",
-    "PE",
-    "IN",
-    "CU",
-};
-
-static uint16_t landing_sols[MARS_TIME_NUM_SITES] = {
-    0,
-    52387,
-    52304,
-    51511,
-    49269,
-};
-
-typedef struct {
-    uint8_t hour;
-    uint8_t minute;
-    uint8_t second;
-} mars_clock_hms_t;
-
-static void _h_to_hms(mars_clock_hms_t *date_time, double h) {
-       unsigned int seconds = (unsigned int)(h * 3600.0);
-       date_time->hour = seconds / 3600;
-       seconds = seconds % 3600;
-       date_time->minute = floor(seconds / 60);
-       date_time->second = round(seconds % 60);
-}
-
-static void _update(mars_time_state_t *state) {
-    char buf[11];
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
-    // TODO: I'm skipping over some steps here.
-    // https://www.giss.nasa.gov/tools/mars24/help/algorithm.html
-    double jdut = 2440587.5 + ((double)now / 86400.0);
-    double jdtt = jdut + ((37.0 + 32.184) / 86400.0);
-    double jd2k = jdtt - 2451545.0;
-    double msd = ((jd2k - 4.5) / 1.0274912517) + 44796.0 - 0.0009626;
-    double mtc = fmod(24 * msd, 24);
-    double lmt;
-
-    if (state->current_site == 0) {
-        lmt = mtc;
-    } else {
-        double longitude = site_longitudes[state->current_site];
-        double lmst = mtc - ((longitude * 24.0) / 360.0);
-        lmt = fmod(lmst + 24, 24);
-    }
-
-    if (state->displaying_sol) {
-        // TODO: this is not right, mission sol should turn over at midnight local time?
-        uint16_t sol = floor(msd) - landing_sols[state->current_site];
-        if (sol < 1000) sprintf(&buf[0], "%s  Sol%3d", site_names[state->current_site], sol);
-        else sprintf(&buf[0], "%s $%6d", site_names[state->current_site], sol);
-        watch_clear_colon();
-        watch_clear_indicator(WATCH_INDICATOR_24H);
-    } else {
-        mars_clock_hms_t mars_time;
-        _h_to_hms(&mars_time, lmt);
-        sprintf(&buf[0], "%s  %02d%02d%02d", site_names[state->current_site], mars_time.hour, mars_time.minute, mars_time.second);
-        watch_set_colon();
-        watch_set_indicator(WATCH_INDICATOR_24H);
-    }
-
-    watch_display_string(buf, 0);
-}
-
-void mars_time_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(mars_time_state_t));
-        memset(*context_ptr, 0, sizeof(mars_time_state_t));
-    }
-}
-
-void mars_time_face_activate(void *context) {
-    mars_time_state_t *state = (mars_time_state_t *)context;
-    (void) state;
-}
-
-bool mars_time_face_loop(movement_event_t event, void *context) {
-    mars_time_state_t *state = (mars_time_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            _update(state);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            state->displaying_sol = !state->displaying_sol;
-            _update(state);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            movement_illuminate_led();
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            state->current_site = (state->current_site + 1) % MARS_TIME_NUM_SITES;
-            _update(state);
-            break;
-        case EVENT_TIMEOUT:
-            // TODO: make this lower power so we can avoid timeout
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // TODO: low energy update
-            // watch_start_sleep_animation(500);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void mars_time_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/clock/mars_time_face.h b/movement/watch_faces/clock/mars_time_face.h
deleted file mode 100644 (file)
index ddb43e6..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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 MARS_TIME_FACE_H_
-#define MARS_TIME_FACE_H_
-
-/*
- * MARS TIME face
- *
- * This watch face is dedicated to Martian timekeeping.
- * It has several modes, and can display either a time or a date.
- *
- * Pressing the ALARM button cycles through different time zones on Mars:
- *   MC - Mars Coordinated Time, the time at Airy-0 Crater on the Martian prime meridian
- *   ZH - Local mean solar time for the Zhurong rover
- *   PE - LMST for the Perseverance rover
- *   IN - LMST for the Insight lander
- *   CU - LMST for the Curiosity rover
- *
- * Press the LIGHT button to toggle between displaying time and date:
- *   MC S - the Mars Sol Date, Martian days since December 29, 1873
- *   ZH Sol - Mission sol for the Zhurong rover
- *   PE Sol - Mission sol for the Perseverance rover
- *   IN S - Mission sol for the InSight lander
- *   CU S - Mission sol for the Curiosity rover
- *
- * Note that where the mission sol is below 1000, this watch face displays
- * the word “Sol” on the bottom line. When the mission sol is over 1000, the
- * word “Sol” will not fit and so it displays a stylized letter S at the top
- * right.
- */
-
-#include "movement.h"
-
-typedef enum {
-    MARS_TIME_MERIDIAN,
-    MARS_TIME_ZHURONG_SITE,
-    MARS_TIME_PERSEVERANCE_SITE,
-    MARS_TIME_INSIGHT_SITE,
-    MARS_TIME_CURIOSITY_SITE,
-    MARS_TIME_NUM_SITES,
-} mars_time_site_t;
-
-typedef struct {
-    mars_time_site_t current_site;
-    bool displaying_sol;
-} mars_time_state_t;
-
-void mars_time_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void mars_time_face_activate(void *context);
-bool mars_time_face_loop(movement_event_t event, void *context);
-void mars_time_face_resign(void *context);
-
-#define mars_time_face ((const watch_face_t){ \
-    mars_time_face_setup, \
-    mars_time_face_activate, \
-    mars_time_face_loop, \
-    mars_time_face_resign, \
-    NULL, \
-})
-
-#endif // MARS_TIME_FACE_H_
-
diff --git a/movement/watch_faces/clock/minimal_clock_face.c b/movement/watch_faces/clock/minimal_clock_face.c
deleted file mode 100644 (file)
index cb66323..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Dennisman219
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "minimal_clock_face.h"
-
-static void _minimal_clock_face_update_display() {
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    char buffer[11];
-
-    if (!movement_clock_mode_24h()) {
-        date_time.unit.hour %= 12;
-        sprintf(buffer, "%2d%02d  ", date_time.unit.hour, date_time.unit.minute);
-    } else {
-        sprintf(buffer, "%02d%02d  ", date_time.unit.hour, date_time.unit.minute);
-    }
-
-    watch_display_string(buffer, 4);
-}
-
-void minimal_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(minimal_clock_state_t));
-        memset(*context_ptr, 0, sizeof(minimal_clock_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void minimal_clock_face_activate(void *context) {
-    (void) context;
-    // Handle any tasks related to your watch face coming on screen.
-    watch_set_colon();
-}
-
-bool minimal_clock_face_loop(movement_event_t event, void *context) {
-    (void) context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Show your initial UI here.
-            _minimal_clock_face_update_display();
-            break;
-        case EVENT_TICK:
-            // If needed, update your display here.
-            _minimal_clock_face_update_display();
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            // You can use the Light button for your own purposes. Note that by default, Movement will also
-            // illuminate the LED in response to EVENT_LIGHT_BUTTON_DOWN; to suppress that behavior, add an
-            // empty case for EVENT_LIGHT_BUTTON_DOWN.
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // Just in case you have need for another button.
-            break;
-        case EVENT_TIMEOUT:
-            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
-            // you may uncomment this line to move back to the first watch face in the list:
-            // movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
-            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
-            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
-            // watch_start_sleep_animation(500);
-            _minimal_clock_face_update_display();
-            break;
-        default:
-            // Movement's default loop handler will step in for any cases you don't handle above:
-            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
-            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
-            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
-            // You can override any of these behaviors by adding a case for these events to this switch statement.
-            return movement_default_loop_handler(event);
-    }
-
-    // return true if the watch can enter standby mode. Generally speaking, you should always return true.
-    // Exceptions:
-    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
-    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
-    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
-    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
-    return true;
-}
-
-void minimal_clock_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
-
diff --git a/movement/watch_faces/clock/minimal_clock_face.h b/movement/watch_faces/clock/minimal_clock_face.h
deleted file mode 100644 (file)
index 7c345e3..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Dennisman219
- *
- * 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 MINIMAL_CLOCK_FACE_H_
-#define MINIMAL_CLOCK_FACE_H_
-
-#include "movement.h"
-
-/*
- * MINIMAL CLOCK FACE
- *
- * A minimal clock face that just shows hours and minutes.
- * There is nothing to configure. The face follows the 12h/24h setting
- *
- */
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint8_t unused;
-} minimal_clock_state_t;
-
-void minimal_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void minimal_clock_face_activate(void *context);
-bool minimal_clock_face_loop(movement_event_t event, void *context);
-void minimal_clock_face_resign(void *context);
-
-#define minimal_clock_face ((const watch_face_t){ \
-    minimal_clock_face_setup, \
-    minimal_clock_face_activate, \
-    minimal_clock_face_loop, \
-    minimal_clock_face_resign, \
-    NULL, \
-})
-
-#endif // MINIMAL_CLOCK_FACE_H_
-
diff --git a/movement/watch_faces/clock/minute_repeater_decimal_face.c b/movement/watch_faces/clock/minute_repeater_decimal_face.c
deleted file mode 100644 (file)
index 9f5eb43..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Jonas Termeau - original repetition_minute_face
- * Copyright (c) 2023 Brian Blakley - modified minute_repeater_decimal_face
- *
- * 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.
- */
-
-/*
- * This face, minute_repeater_decimal_face, is a modification of the original 
- * repetition_minute_face by Jonas Termeau.
- * 
- * This version was created by BrianBinFL to use a decimal minute repeater pattern 
- * (hours, tens, and minutes) instead of the traditional pattern (hours, quarters, 
- * minutes).
- *
- * Also 500ms delays were added after the hours segment and after the tens segment
- * to make it easier for the user to realize that the counting for the current 
- * segment has ended.
- *
- */
-#include <stdlib.h>
-#include "minute_repeater_decimal_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "watch_private_display.h"
-
-void mrd_play_hour_chime(void) {
-        watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
-}
-
-void mrd_play_tens_chime(void) {
-        watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 150);
-        watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 750);
-}
-
-void mrd_play_minute_chime(void) {
-        watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
-}
-
-static void _update_alarm_indicator(bool settings_alarm_enabled, minute_repeater_decimal_state_t *state) {
-    state->alarm_enabled = settings_alarm_enabled;
-    if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-}
-
-void minute_repeater_decimal_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(minute_repeater_decimal_state_t));
-        minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)*context_ptr;
-        state->signal_enabled = false;
-        state->watch_face_index = watch_face_index;
-    }
-}
-
-void minute_repeater_decimal_face_activate(void *context) {
-    minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
-
-    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
-
-    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
-
-    // handle chime indicator
-    if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-    else watch_clear_indicator(WATCH_INDICATOR_BELL);
-
-    // show alarm indicator if there is an active alarm
-    _update_alarm_indicator(movement_alarm_enabled(), state);
-
-    watch_set_colon();
-
-    // this ensures that none of the timestamp fields will match, so we can re-render them all.
-    state->previous_date_time = 0xFFFFFFFF;
-}
-
-bool minute_repeater_decimal_face_loop(movement_event_t event, void *context) {
-    minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
-    char buf[11];
-    uint8_t pos;
-
-    watch_date_time_t date_time;
-    uint32_t previous_date_time;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-        case EVENT_LOW_ENERGY_UPDATE:
-            date_time = watch_rtc_get_date_time();
-            previous_date_time = state->previous_date_time;
-            state->previous_date_time = date_time.reg;
-
-            // check the battery voltage once a day...
-            if (date_time.unit.day != state->last_battery_check) {
-                state->last_battery_check = date_time.unit.day;
-                watch_enable_adc();
-                uint16_t voltage = watch_get_vcc_voltage();
-                watch_disable_adc();
-                // 2.2 volts will happen when the battery has maybe 5-10% remaining?
-                // we can refine this later.
-                state->battery_low = (voltage < 2200);
-            }
-
-            // ...and set the LAP indicator if low.
-            if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
-
-            if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-                // everything before seconds is the same, don't waste cycles setting those segments.
-                watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
-                watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
-                break;
-            } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-                // everything before minutes is the same.
-                pos = 6;
-                sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
-            } else {
-                // other stuff changed; let's do it all.
-                if (!movement_clock_mode_24h()) {
-                    // if we are in 12 hour mode, do some cleanup.
-                    if (date_time.unit.hour < 12) {
-                        watch_clear_indicator(WATCH_INDICATOR_PM);
-                    } else {
-                        watch_set_indicator(WATCH_INDICATOR_PM);
-                    }
-                    date_time.unit.hour %= 12;
-                    if (date_time.unit.hour == 0) date_time.unit.hour = 12;
-                }
-                pos = 0;
-                if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
-                    if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
-                    sprintf(buf, "%s%2d%2d%02d  ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
-                } else {
-                    sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
-                }
-            }
-            watch_display_string(buf, pos);
-            // handle alarm indicator
-            if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->signal_enabled = !state->signal_enabled;
-            if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-            else watch_clear_indicator(WATCH_INDICATOR_BELL);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            movement_play_signal();
-            break;
-        case EVENT_LIGHT_LONG_UP:
-            /*
-             * Howdy neighbors, this is the actual complication. Like an actual
-             * (very expensive) watch with a repetition minute complication it's
-             * boring at 00:00 or 1:00 and very quite musical at 23:59 or 12:59.
-             */
-
-            date_time = watch_rtc_get_date_time();
-            
-            
-            int hours = date_time.unit.hour;
-            int tens = date_time.unit.minute / 10;
-            int minutes = date_time.unit.minute % 10;
-
-            // chiming hours
-            if (!movement_clock_mode_24h()) {
-                hours = date_time.unit.hour % 12;                
-                if (hours == 0) hours = 12;
-            }
-            if (hours > 0) {
-                int count = 0;
-                for(count = hours; count > 0; --count) {          
-                    mrd_play_hour_chime();
-                }
-                // do a little pause before proceeding to tens
-                watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
-            }
-
-            // chiming tens (if needed)
-            if (tens > 0) {
-                int count = 0;
-                for(count = tens; count > 0; --count) {          
-                    mrd_play_tens_chime();
-                }
-                // do a little pause before proceeding to minutes
-                watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
-            }
-
-            // chiming minutes (if needed)
-            if (minutes > 0) {
-                int count = 0;
-                for(count = minutes; count > 0; --count) {          
-                    mrd_play_minute_chime();
-                }
-            }
-           
-            break; 
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void minute_repeater_decimal_face_resign(void *context) {
-    (void) context;
-}
-
-movement_watch_face_advisory_t minute_repeater_decimal_face_advise(void *context) {
-    minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
-    movement_watch_face_advisory_t retval = { 0 };
-
-    if (state->signal_enabled) {
-        watch_date_time_t date_time = watch_rtc_get_date_time();
-        retval.wants_background_task = date_time.unit.minute == 0;
-    }
-
-    return retval;
-}
diff --git a/movement/watch_faces/clock/minute_repeater_decimal_face.h b/movement/watch_faces/clock/minute_repeater_decimal_face.h
deleted file mode 100644 (file)
index 738b34d..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Jonas Termeau - original repetition_minute_face
- * Copyright (c) 2023 Brian Blakley - modified minute_repeater_decimal_face
- *
- * 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 MINUTE_REPEATER_DECIMAL_FACE_H_
-#define MINUTE_REPEATER_DECIMAL_FACE_H_
-
-#include "movement.h"
-
-/*
- * A hopefully useful complication for friendly neighbors in the dark
- *
- * Originating from 1676 from reverend and mechanician Edward Barlow, and
- * perfected in 1820 by neighbor Abraham Breguet, a minute repeater or 
- * "repetition minute" is a complication in a mechanical watch or clock that
- * chimes the hours and often minutes at the press of a button. There are many
- * types of repeater, from the simple repeater which merely strikes the number
- * of hours, to the minute repeater which chimes the time down to the minute,
- * using separate tones for hours, decimal hours, and minutes. They originated
- * before widespread artificial illumination, to allow the time to be determined
- * in the dark, and were also used by the visually impaired. 
- *
- *
- * How to use it :
- * 
- * Long press the light button to get an auditive reading of the time like so :
- * 0..23 (1..12 if 24-hours format isn't enabled) low beep(s) for the hours
- * 0..9 low-high couple pitched beeps for the tens of minutes
- * 0..9 high pitched beep(s) for the remaining minutes (ones of minutes)
- *
- * Prerequisite : a watch with a working buzzer
- * 
- * ~ Only in the darkness can you see the stars. - Martin Luther King ~
- *
- */
-
-typedef struct {
-    uint32_t previous_date_time;
-    uint8_t last_battery_check;
-    uint8_t watch_face_index;
-    bool signal_enabled;
-    bool battery_low;
-    bool alarm_enabled;
-} minute_repeater_decimal_state_t;
-
-void mrd_play_hour_chime(void);
-void mrd_play_tens_chime(void);
-void mrd_play_minute_chime(void);
-void minute_repeater_decimal_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void minute_repeater_decimal_face_activate(void *context);
-bool minute_repeater_decimal_face_loop(movement_event_t event, void *context);
-void minute_repeater_decimal_face_resign(void *context);
-movement_watch_face_advisory_t minute_repeater_decimal_face_advise(void *context);
-
-#define minute_repeater_decimal_face ((const watch_face_t){ \
-    minute_repeater_decimal_face_setup, \
-    minute_repeater_decimal_face_activate, \
-    minute_repeater_decimal_face_loop, \
-    minute_repeater_decimal_face_resign, \
-    minute_repeater_decimal_face_advise, \
-})
-
-#endif // MINUTE_REPEATER_DECIMAL_FACE_H_
diff --git a/movement/watch_faces/clock/repetition_minute_face.c b/movement/watch_faces/clock/repetition_minute_face.c
deleted file mode 100644 (file)
index b475b45..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Jonas Termeau
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include "repetition_minute_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "watch_private_display.h"
-
-void play_hour_chime(void) {
-        watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
-}
-
-void play_quarter_chime(void) {
-        watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 150);
-        watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 750);
-}
-
-void play_minute_chime(void) {
-        watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
-}
-
-static void _update_alarm_indicator(bool settings_alarm_enabled, repetition_minute_state_t *state) {
-    state->alarm_enabled = settings_alarm_enabled;
-    if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-}
-
-void repetition_minute_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(repetition_minute_state_t));
-        repetition_minute_state_t *state = (repetition_minute_state_t *)*context_ptr;
-        state->signal_enabled = false;
-        state->watch_face_index = watch_face_index;
-    }
-}
-
-void repetition_minute_face_activate(void *context) {
-    repetition_minute_state_t *state = (repetition_minute_state_t *)context;
-
-    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
-
-    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
-
-    // handle chime indicator
-    if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-    else watch_clear_indicator(WATCH_INDICATOR_BELL);
-
-    // show alarm indicator if there is an active alarm
-    _update_alarm_indicator(movement_alarm_enabled(), state);
-
-    watch_set_colon();
-
-    // this ensures that none of the timestamp fields will match, so we can re-render them all.
-    state->previous_date_time = 0xFFFFFFFF;
-}
-
-bool repetition_minute_face_loop(movement_event_t event, void *context) {
-    repetition_minute_state_t *state = (repetition_minute_state_t *)context;
-    char buf[11];
-    uint8_t pos;
-
-    watch_date_time_t date_time;
-    uint32_t previous_date_time;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-        case EVENT_LOW_ENERGY_UPDATE:
-            date_time = watch_rtc_get_date_time();
-            previous_date_time = state->previous_date_time;
-            state->previous_date_time = date_time.reg;
-
-            // check the battery voltage once a day...
-            if (date_time.unit.day != state->last_battery_check) {
-                state->last_battery_check = date_time.unit.day;
-                watch_enable_adc();
-                uint16_t voltage = watch_get_vcc_voltage();
-                watch_disable_adc();
-                // 2.2 volts will happen when the battery has maybe 5-10% remaining?
-                // we can refine this later.
-                state->battery_low = (voltage < 2200);
-            }
-
-            // ...and set the LAP indicator if low.
-            if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
-
-            if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-                // everything before seconds is the same, don't waste cycles setting those segments.
-                watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
-                watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
-                break;
-            } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-                // everything before minutes is the same.
-                pos = 6;
-                sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
-            } else {
-                // other stuff changed; let's do it all.
-                if (!movement_clock_mode_24h()) {
-                    // if we are in 12 hour mode, do some cleanup.
-                    if (date_time.unit.hour < 12) {
-                        watch_clear_indicator(WATCH_INDICATOR_PM);
-                    } else {
-                        watch_set_indicator(WATCH_INDICATOR_PM);
-                    }
-                    date_time.unit.hour %= 12;
-                    if (date_time.unit.hour == 0) date_time.unit.hour = 12;
-                }
-                pos = 0;
-                if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
-                    if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
-                    sprintf(buf, "%s%2d%2d%02d  ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
-                } else {
-                    sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
-                }
-            }
-            watch_display_string(buf, pos);
-            // handle alarm indicator
-            if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->signal_enabled = !state->signal_enabled;
-            if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-            else watch_clear_indicator(WATCH_INDICATOR_BELL);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            // uncomment this line to snap back to the clock face when the hour signal sounds:
-            // movement_move_to_face(state->watch_face_index);
-            movement_play_signal();
-            break;
-        case EVENT_LIGHT_LONG_UP:
-            /*
-             * Howdy neighbors, this is the actual complication. Like an actual
-             * (very expensive) watch with a repetition minute complication it's
-             * boring at 00:00 or 1:00 and very quite musical at 23:59 or 12:59.
-             */
-
-            date_time = watch_rtc_get_date_time();
-            
-            
-            int hours = date_time.unit.hour;
-            int quarters = date_time.unit.minute / 15;
-            int minutes = date_time.unit.minute % 15;
-
-            // chiming hours
-            if (!movement_clock_mode_24h()) {
-                hours = date_time.unit.hour % 12;                
-                if (hours == 0) hours = 12;
-            }
-            if (hours > 0) {
-                int count = 0;
-                for(count = hours; count > 0; --count) {          
-                    play_hour_chime();
-                }
-            }
-
-            // chiming quarters (if needed)
-            if (quarters > 0) {
-                int count = 0;
-                for(count = quarters; count > 0; --count) {          
-                    play_quarter_chime();
-                }
-            }
-
-            // chiming minutes (if needed)
-            if (minutes > 0) {
-                int count = 0;
-                for(count = minutes; count > 0; --count) {          
-                    play_minute_chime();
-                }
-            }
-           
-            break; 
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void repetition_minute_face_resign(void *context) {
-    (void) context;
-}
-
-movement_watch_face_advisory_t repetition_minute_face_advise(void *context) {
-    repetition_minute_state_t *state = (repetition_minute_state_t *)context;
-    movement_watch_face_advisory_t retval = { 0 };
-
-    if (state->signal_enabled) {
-        watch_date_time_t date_time = watch_rtc_get_date_time();
-        retval.wants_background_task = date_time.unit.minute == 0;
-    }
-
-    return retval;
-}
diff --git a/movement/watch_faces/clock/repetition_minute_face.h b/movement/watch_faces/clock/repetition_minute_face.h
deleted file mode 100644 (file)
index 1e5d10a..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Jonas Termeau
- *
- * 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 REPETITION_MINUTE_FACE_H_
-#define REPETITION_MINUTE_FACE_H_
-
-/*
- * REPETITION MINUTE face
- *
- * A hopefully useful complication for friendly neighbors in the dark
- *
- * Originating from 1676 from reverend and mechanician Edward Barlow, and
- * perfected in 1820 by neighbor Abraham Breguet, a minute repeater or 
- * "repetition minute" is a complication in a mechanical watch or clock that
- * chimes the hours and often minutes at the press of a button. There are many
- * types of repeater, from the simple repeater which merely strikes the number
- * of hours, to the minute repeater which chimes the time down to the minute,
- * using separate tones for hours, quarter hours, and minutes. They originated
- * before widespread artificial illumination, to allow the time to be determined
- * in the dark, and were also used by the visually impaired. 
- *
- * How to use it :
- * 
- * Long press the light button to get an auditive reading of the time like so :
- * 0..23 (1..12 if 24-hours format isn't enabled) low beep(s) for the hours
- * 0..3 low-high couple pitched beeps for the quarters
- * 0..14 high pitched beep(s) for the remaining minutes
- *
- * Prerequisite : a watch with a working buzzer
- * 
- * ~ Only in the darkness can you see the stars. - Martin Luther King ~
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint32_t previous_date_time;
-    uint8_t last_battery_check;
-    uint8_t watch_face_index;
-    bool signal_enabled;
-    bool battery_low;
-    bool alarm_enabled;
-} repetition_minute_state_t;
-
-void play_hour_chime(void);
-void play_quarter_chime(void);
-void play_minute_chime(void);
-void repetition_minute_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void repetition_minute_face_activate(void *context);
-bool repetition_minute_face_loop(movement_event_t event, void *context);
-void repetition_minute_face_resign(void *context);
-movement_watch_face_advisory_t repetition_minute_face_advise(void *context);
-
-#define repetition_minute_face ((const watch_face_t){ \
-    repetition_minute_face_setup, \
-    repetition_minute_face_activate, \
-    repetition_minute_face_loop, \
-    repetition_minute_face_resign, \
-    repetition_minute_face_advise, \
-})
-
-#endif // REPETITION_MINUTE_FACE_H_
diff --git a/movement/watch_faces/clock/simple_clock_bin_led_face.c b/movement/watch_faces/clock/simple_clock_bin_led_face.c
deleted file mode 100644 (file)
index 527b1cd..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Andreas Nebinger, based on Joey Castillo's simple clock face
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "simple_clock_bin_led_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "watch_private_display.h"
-
-static void _update_alarm_indicator(bool settings_alarm_enabled, simple_clock_bin_led_state_t *state) {
-    state->alarm_enabled = settings_alarm_enabled;
-    if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-}
-
-static void _display_left_aligned(uint8_t value) {
-    if (value >= 10) {
-        watch_display_character('0' + value / 10, 4);
-        watch_display_character('0' + value % 10, 5);
-    } else {
-        watch_display_character('0' + value, 4);
-        watch_display_character(' ', 5);
-    }
-}
-
-void simple_clock_bin_led_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(simple_clock_bin_led_state_t));
-        memset(*context_ptr, 0, sizeof(simple_clock_bin_led_state_t));
-        simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)*context_ptr;
-        state->watch_face_index = watch_face_index;
-    }
-}
-
-void simple_clock_bin_led_face_activate(void *context) {
-    simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
-
-    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
-
-    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
-
-    // handle chime indicator
-    if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-    else watch_clear_indicator(WATCH_INDICATOR_BELL);
-
-    // show alarm indicator if there is an active alarm
-    _update_alarm_indicator(movement_alarm_enabled(), state);
-
-    watch_set_colon();
-
-    // this ensures that none of the timestamp fields will match, so we can re-render them all.
-    state->previous_date_time = 0xFFFFFFFF;
-}
-
-bool simple_clock_bin_led_face_loop(movement_event_t event, void *context) {
-    simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
-    char buf[11];
-    uint8_t pos;
-
-    watch_date_time_t date_time;
-    uint32_t previous_date_time;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-        case EVENT_LOW_ENERGY_UPDATE:
-            date_time = watch_rtc_get_date_time();
-            if (state->flashing_state > 0) {
-                if (state->ticks) {
-                    state->ticks--;
-                } else {
-                    
-                    if (state->flashing_state & 64) {
-                        // start led on for current bit
-                        state->flashing_state &= 63;
-                        state->ticks = (state->flashing_value & 1 ? 7 : 1);
-                        movement_illuminate_led();
-                    } else {
-                        // indicate first or switch to next bit
-                        watch_set_led_off();
-                        if ((state->flashing_state & 128) == 0) state->flashing_value = state->flashing_value >> 1;
-                        if (state->flashing_value || (state->flashing_state & 128)) {
-                            state->flashing_state &= 127;
-                            state->flashing_state |= 64;
-                            state->ticks = 6;
-                        } else if (state->flashing_state & 1) {
-                            // transition to minutes
-                            state->flashing_state = 2 + 128;
-                            state->flashing_value = date_time.unit.minute;
-                            _display_left_aligned(state->flashing_value);
-                            state->ticks = 9;
-                        } else {
-                            // end flashing
-                            state->flashing_state = 0;
-                            state->previous_date_time = 0xFFFFFFFF;
-                            movement_request_tick_frequency(1);
-                            watch_set_colon();
-                        }
-                    }
-                }
-            } else {
-                previous_date_time = state->previous_date_time;
-                state->previous_date_time = date_time.reg;
-
-                // check the battery voltage once a day...
-                if (date_time.unit.day != state->last_battery_check) {
-                    state->last_battery_check = date_time.unit.day;
-                    watch_enable_adc();
-                    uint16_t voltage = watch_get_vcc_voltage();
-                    watch_disable_adc();
-                    // 2.2 volts will happen when the battery has maybe 5-10% remaining?
-                    // we can refine this later.
-                    state->battery_low = (voltage < 2200);
-                }
-
-                // ...and set the LAP indicator if low.
-                if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
-
-                if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-                    // everything before seconds is the same, don't waste cycles setting those segments.
-                    watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
-                    watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
-                    break;
-                } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-                    // everything before minutes is the same.
-                    pos = 6;
-                    sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
-                } else {
-                    // other stuff changed; let's do it all.
-                    if (!movement_clock_mode_24h()) {
-                        // if we are in 12 hour mode, do some cleanup.
-                        if (date_time.unit.hour < 12) {
-                            watch_clear_indicator(WATCH_INDICATOR_PM);
-                        } else {
-                            watch_set_indicator(WATCH_INDICATOR_PM);
-                        }
-                        date_time.unit.hour %= 12;
-                        if (date_time.unit.hour == 0) date_time.unit.hour = 12;
-                    }
-                    pos = 0;
-                    if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
-                        if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
-                        sprintf(buf, "%s%2d%2d%02d  ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
-                    } else {
-                        sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
-                    }
-                }
-                watch_display_string(buf, pos);
-                // handle alarm indicator
-                if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->signal_enabled = !state->signal_enabled;
-            if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-            else watch_clear_indicator(WATCH_INDICATOR_BELL);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            // uncomment this line to snap back to the clock face when the hour signal sounds:
-            // movement_move_to_face(state->watch_face_index);
-            movement_play_signal();
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (state->flashing_state == 0) {
-                date_time = watch_rtc_get_date_time();
-                state->flashing_state = 1 + 128;
-                state->ticks = 4;
-                if (!movement_clock_mode_24h()) {
-                    date_time.unit.hour %= 12;
-                    if (date_time.unit.hour == 0) date_time.unit.hour = 12;
-                }
-                watch_display_string("      ", 4);
-                _display_left_aligned(date_time.unit.hour);
-                state->flashing_value = date_time.unit.hour > 12 ? date_time.unit.hour - 12 : date_time.unit.hour;
-                watch_set_led_off();
-                watch_clear_colon();
-                movement_request_tick_frequency(8);
-            }
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void simple_clock_bin_led_face_resign(void *context) {
-    (void) context;
-}
-
-movement_watch_face_advisory_t simple_clock_bin_led_face_advise(void *context) {
-    simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
-    movement_watch_face_advisory_t retval = { 0 };
-
-    if (state->signal_enabled) {
-        watch_date_time_t date_time = watch_rtc_get_date_time();
-        retval.wants_background_task = date_time.unit.minute == 0;
-    }
-
-    return retval;
-}
diff --git a/movement/watch_faces/clock/simple_clock_bin_led_face.h b/movement/watch_faces/clock/simple_clock_bin_led_face.h
deleted file mode 100644 (file)
index ed9d3ae..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Andreas Nebinger, based on Joey Castillo's simple clock face
- *
- * 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 SIIMPLE_CLOCK_BIN_LED_FACE_H_
-#define SIIMPLE_CLOCK_BIN_LED_FACE_H_
-
-/*
- * BINARY LED CLOCK FACE
- *
- * A "fork" of the simple clock face, which provides the functionality of showing 
- * the current time by flashing the LED using binary representation.
- *
- * This feature serves as a practical solution to compensate for the admittedly 
- * subpar backlight of the F91w watch and is especially useful if your eyesight
- * is not the best. By pressing and holding the light button long enough, the
- * watch will illuminate the LED to showcase the current time.
- *
- * How to interpret the flashing led:
- * - Firstly, the hour is presented as a binary number, with the lowest bit displayed
- *   first. A short flash signifies 0, while a longer flash represents 1. If you use
- *   the watch in 24h mode, please note that the indicated value may be decreased 
- *   by 12, to keep things simple and short. For example, 22h would be translated
- *   to 10h.
- * - After showing the hour, a lengthier  pause indicates that minutes will be shown 
- *   next.
- * - Similar to the hour representation, minutes are displayed in binary format, 
- *   starting with the lowest bits. A short flash denotes 0, a longer flash 
- *   represents 1.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint32_t previous_date_time;
-    uint8_t last_battery_check;
-    uint8_t watch_face_index;
-    bool signal_enabled;
-    bool battery_low;
-    bool alarm_enabled;
-    uint8_t flashing_state; // bitmap representing the flashing state. Bit 0 = hours showing, bit 1 = minutes showing,
-                            // bit 6 = short break between flashing bits, bit 7 = long break between hours and minutes
-    uint8_t flashing_value;
-    uint8_t ticks;
-} simple_clock_bin_led_state_t;
-
-void simple_clock_bin_led_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void simple_clock_bin_led_face_activate(void *context);
-bool simple_clock_bin_led_face_loop(movement_event_t event, void *context);
-void simple_clock_bin_led_face_resign(void *context);
-movement_watch_face_advisory_t simple_clock_bin_led_face_advise(void *context);
-
-#define simple_clock_bin_led_face ((const watch_face_t){ \
-    simple_clock_bin_led_face_setup, \
-    simple_clock_bin_led_face_activate, \
-    simple_clock_bin_led_face_loop, \
-    simple_clock_bin_led_face_resign, \
-    simple_clock_bin_led_face_advise, \
-})
-
-#endif // SIIMPLE_CLOCK_BIN_LED_FACE_H_
-
diff --git a/movement/watch_faces/clock/weeknumber_clock_face.c b/movement/watch_faces/clock/weeknumber_clock_face.c
deleted file mode 100644 (file)
index 3a153e1..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include "weeknumber_clock_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-static void _update_alarm_indicator(bool settings_alarm_enabled, weeknumber_clock_state_t *state) {
-    state->alarm_enabled = settings_alarm_enabled;
-    if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-}
-
-void weeknumber_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(weeknumber_clock_state_t));
-        weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)*context_ptr;
-        state->signal_enabled = false;
-        state->watch_face_index = watch_face_index;
-    }
-}
-
-void weeknumber_clock_face_activate(void *context) {
-    weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
-
-    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
-
-    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
-
-    // handle chime indicator
-    if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-    else watch_clear_indicator(WATCH_INDICATOR_BELL);
-
-    // show alarm indicator if there is an active alarm
-    _update_alarm_indicator(movement_alarm_enabled(), state);
-
-    watch_set_colon();
-
-    // this ensures that none of the timestamp fields will match, so we can re-render them all.
-    state->previous_date_time = 0xFFFFFFFF;
-}
-
-bool weeknumber_clock_face_loop(movement_event_t event, void *context) {
-    weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
-    char buf[11];
-    uint8_t pos;
-
-    watch_date_time_t date_time;
-    uint32_t previous_date_time;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-        case EVENT_LOW_ENERGY_UPDATE:
-            date_time = watch_rtc_get_date_time();
-            previous_date_time = state->previous_date_time;
-            state->previous_date_time = date_time.reg;
-
-            // check the battery voltage once a day...
-            if (date_time.unit.day != state->last_battery_check) {
-                state->last_battery_check = date_time.unit.day;
-                watch_enable_adc();
-                uint16_t voltage = watch_get_vcc_voltage();
-                watch_disable_adc();
-                // 2.2 volts will happen when the battery has maybe 5-10% remaining?
-                // we can refine this later.
-                state->battery_low = (voltage < 2200);
-            }
-
-            // ...and set the LAP indicator if low.
-            if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
-
-            if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-                // everything before minutes is the same.
-                pos = 6;
-                sprintf(buf, "%02d%02d", date_time.unit.minute, watch_utility_get_weeknumber(date_time.unit.year, date_time.unit.month, date_time.unit.day));
-            } else {
-                // other stuff changed; let's do it all.
-                if (!movement_clock_mode_24h()) {
-                    // if we are in 12 hour mode, do some cleanup.
-                    if (date_time.unit.hour < 12) {
-                        watch_clear_indicator(WATCH_INDICATOR_PM);
-                    } else {
-                        watch_set_indicator(WATCH_INDICATOR_PM);
-                    }
-                    date_time.unit.hour %= 12;
-                    if (date_time.unit.hour == 0) date_time.unit.hour = 12;
-                }
-                pos = 0;
-                if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
-                    if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
-                    sprintf(buf, "%s%2d%2d%02d  ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
-                } else {
-                    sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, watch_utility_get_weeknumber(date_time.unit.year, date_time.unit.month, date_time.unit.day));
-                }
-            }
-            watch_display_string(buf, pos);
-            // handle alarm indicator
-            if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->signal_enabled = !state->signal_enabled;
-            if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-            else watch_clear_indicator(WATCH_INDICATOR_BELL);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            // uncomment this line to snap back to the clock face when the hour signal sounds:
-            // movement_move_to_face(state->watch_face_index);
-            movement_play_signal();
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void weeknumber_clock_face_resign(void *context) {
-    (void) context;
-}
-
-movement_watch_face_advisory_t weeknumber_clock_face_advise(void *context) {
-    weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
-    movement_watch_face_advisory_t retval = { 0 };
-
-    if (state->signal_enabled) {
-        watch_date_time_t date_time = watch_rtc_get_date_time();
-        retval.wants_background_task = date_time.unit.minute == 0;
-    }
-
-    return retval;
-}
diff --git a/movement/watch_faces/clock/weeknumber_clock_face.h b/movement/watch_faces/clock/weeknumber_clock_face.h
deleted file mode 100644 (file)
index 9ba1270..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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 WEEKNUMBER_CLOCK_FACE_H_
-#define WEEKNUMBER_CLOCK_FACE_H_
-
-/*
- * WEEK-NUMBER WATCH FACE
- *
- * Same as simple clock, but has iso 8601 week number instead of seconds counter.
- *
- * Long-press ALARM to toggle the hourly chime.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint32_t previous_date_time;
-    uint8_t last_battery_check;
-    uint8_t watch_face_index;
-    bool signal_enabled;
-    bool battery_low;
-    bool alarm_enabled;
-} weeknumber_clock_state_t;
-
-void weeknumber_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void weeknumber_clock_face_activate(void *context);
-bool weeknumber_clock_face_loop(movement_event_t event, void *context);
-void weeknumber_clock_face_resign(void *context);
-movement_watch_face_advisory_t weeknumber_clock_face_advise(void *context);
-
-#define weeknumber_clock_face ((const watch_face_t){ \
-    weeknumber_clock_face_setup, \
-    weeknumber_clock_face_activate, \
-    weeknumber_clock_face_loop, \
-    weeknumber_clock_face_resign, \
-    weeknumber_clock_face_advise, \
-})
-
-#endif // SIMPLE_CLOCK_FACE_H_
diff --git a/movement/watch_faces/clock/world_clock2_face.c b/movement/watch_faces/clock/world_clock2_face.c
deleted file mode 100644 (file)
index e79f2df..0000000
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Konrad Rieck
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "world_clock2_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "watch_utility.h"
-
-static bool refresh_face;
-
-/* Simple macros for navigation */
-#define FORWARD             +1
-#define BACKWARD            -1
-
-/* Activate refresh of time */
-#define REFRESH_TIME        0xffffffff
-
-/* List of all time zone names */
-const char *zone_names[] = {
-    "UTC",     //  0 :   0:00:00 (UTC)
-    "CET",     //  1 :   1:00:00 (Central European Time)
-    "SAST",    //  2 :   2:00:00 (South African Standard Time)
-    "ARST",    //  3 :   3:00:00 (Arabia Standard Time)
-    "IRST",    //  4 :   3:30:00 (Iran Standard Time)
-    "GET",     //  5 :   4:00:00 (Georgia Standard Time)
-    "AFT",     //  6 :   4:30:00 (Afghanistan Time)
-    "PKT",     //  7 :   5:00:00 (Pakistan Standard Time)
-    "IST",     //  8 :   5:30:00 (Indian Standard Time)
-    "NPT",     //  9 :   5:45:00 (Nepal Time)
-    "KGT",     // 10 :   6:00:00 (Kyrgyzstan time)
-    "MYST",    // 11 :   6:30:00 (Myanmar Time)
-    "THA",     // 12 :   7:00:00 (Thailand Standard Time)
-    "CST",     // 13 :   8:00:00 (China Standard Time, Australian Western Standard Time)
-    "ACWS",    // 14 :   8:45:00 (Australian Central Western Standard Time)
-    "JST",     // 15 :   9:00:00 (Japan Standard Time, Korea Standard Time)
-    "ACST",    // 16 :   9:30:00 (Australian Central Standard Time)
-    "AEST",    // 17 :  10:00:00 (Australian Eastern Standard Time)
-    "LHST",    // 18 :  10:30:00 (Lord Howe Standard Time)
-    "SBT",     // 19 :  11:00:00 (Solomon Islands Time)
-    "NZST",    // 20 :  12:00:00 (New Zealand Standard Time)
-    "CHAS",    // 21 :  12:45:00 (Chatham Standard Time)
-    "TOT",     // 22 :  13:00:00 (Tonga Time)
-    "CHAD",    // 23 :  13:45:00 (Chatham Daylight Time)
-    "LINT",    // 24 :  14:00:00 (Line Islands Time)
-    "BIT",     // 25 : -12:00:00 (Baker Island Time)
-    "NUT",     // 26 : -11:00:00 (Niue Time)
-    "HST",     // 27 : -10:00:00 (Hawaii-Aleutian Standard Time)
-    "MART",    // 28 :  -9:30:00 (Marquesas Islands Time)
-    "AKST",    // 29 :  -9:00:00 (Alaska Standard Time)
-    "PST",     // 30 :  -8:00:00 (Pacific Standard Time)
-    "MST",     // 31 :  -7:00:00 (Mountain Standard Time)
-    "CST",     // 32 :  -6:00:00 (Central Standard Time)
-    "EST",     // 33 :  -5:00:00 (Eastern Standard Time)
-    "VET",     // 34 :  -4:30:00 (Venezuelan Standard Time)
-    "AST",     // 35 :  -4:00:00 (Atlantic Standard Time)
-    "NST",     // 36 :  -3:30:00 (Newfoundland Standard Time)
-    "BRT",     // 37 :  -3:00:00 (Brasilia Time)
-    "NDT",     // 38 :  -2:30:00 (Newfoundland Daylight Time)
-    "FNT",     // 39 :  -2:00:00 (Fernando de Noronha Time)
-    "AZOT",    // 40 :  -1:00:00 (Azores Standard Time)
-};
-
-/* Modulo function */
-static inline unsigned int mod(int a, int b)
-{
-    int r = a % b;
-    return r < 0 ? r + b : r;
-}
-
-/* Find the next selected time zone */
-static inline uint8_t find_selected_zone(world_clock2_state_t *state, int direction)
-{
-    uint8_t i = state->current_zone;
-
-    do {
-       i = mod(i + direction, NUM_TIME_ZONES);
-       /* Could not find a selected zone. Return UTC */
-       if (i == state->current_zone) {
-           return 0;
-       }
-    } while (!state->zones[i].selected);
-
-    return i;
-}
-
-/* Beep when zone is enabled. An octave up */
-static void beep_enable() {
-    watch_buzzer_play_note(BUZZER_NOTE_G7, 50);
-    watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
-    watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
-}
-
-/* Beep when zone id disable. An octave down */
-static void beep_disable() {
-    watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-    watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
-    watch_buzzer_play_note(BUZZER_NOTE_G7, 75);
-}
-
-void world_clock2_face_setup(uint8_t watch_face_index, void **context_ptr)
-{
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(world_clock2_state_t));
-        memset(*context_ptr, 0, sizeof(world_clock2_state_t));
-
-        /* Start in settings mode */
-        world_clock2_state_t *state = (world_clock2_state_t *) * context_ptr;
-        state->current_mode = WORLD_CLOCK2_MODE_SETTINGS;
-    }
-}
-
-void world_clock2_face_activate(void *context)
-{
-    world_clock2_state_t *state = (world_clock2_state_t *) context;
-
-    if (watch_sleep_animation_is_running())
-        watch_stop_sleep_animation();
-
-    switch (state->current_mode) {
-        case WORLD_CLOCK2_MODE_DISPLAY:
-            /* Normal tick frequency */
-            movement_request_tick_frequency(1);
-            break;
-        case WORLD_CLOCK2_MODE_SETTINGS:
-            /* Faster frequency for blinking effect */
-            movement_request_tick_frequency(4);
-            break;
-    }
-    refresh_face = true;
-}
-
-static bool mode_display(movement_event_t event, world_clock2_state_t *state)
-{
-    char buf[11];
-    uint8_t pos;
-
-    uint32_t timestamp;
-    uint32_t previous_date_time;
-    watch_date_time_t date_time;
-
-    switch (event.event_type) {
-       case EVENT_ACTIVATE:
-       case EVENT_TICK:
-       case EVENT_LOW_ENERGY_UPDATE:
-           /* Update indicators and colon on refresh */
-           if (refresh_face) {
-               watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-               watch_set_colon();
-                if (movement_clock_mode_24h())
-                    watch_set_indicator(WATCH_INDICATOR_24H);
-
-                state->previous_date_time = REFRESH_TIME;
-                refresh_face = false;
-            }
-
-            /* Determine current time at time zone and store date/time */
-           date_time = watch_rtc_get_date_time();
-           timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
-           date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->current_zone] * 60);
-           previous_date_time = state->previous_date_time;
-           state->previous_date_time = date_time.reg;
-
-           if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-                /* Everything before seconds is the same, don't waste cycles setting those segments. */
-               pos = 8;
-               sprintf(buf, "%02d", date_time.unit.second);
-           } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
-               /* Everything before minutes is the same. */
-               pos = 6;
-               sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
-           } else {
-               /* Other stuff changed; Let's do it all. */
-               if (!movement_clock_mode_24h()) {
-                   /* If we are in 12 hour mode, do some cleanup. */
-                   if (date_time.unit.hour < 12) {
-                       watch_clear_indicator(WATCH_INDICATOR_PM);
-                   } else {
-                       watch_set_indicator(WATCH_INDICATOR_PM);
-                   }
-                   date_time.unit.hour %= 12;
-                   if (date_time.unit.hour == 0)
-                       date_time.unit.hour = 12;
-               }
-
-               pos = 0;
-               if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
-                   if (!watch_sleep_animation_is_running())
-                       watch_start_sleep_animation(500);
-
-                   sprintf(buf, "%.2s%2d%2d%02d  ",
-                            zone_names[state->current_zone],
-                            date_time.unit.day,
-                            date_time.unit.hour,
-                            date_time.unit.minute);
-               } else {
-                   sprintf(buf, "%.2s%2d%2d%02d%02d",
-                           zone_names[state->current_zone],
-                            date_time.unit.day,
-                            date_time.unit.hour,
-                            date_time.unit.minute,
-                            date_time.unit.second);
-               }
-           }
-           watch_display_string(buf, pos);
-           break;
-       case EVENT_ALARM_BUTTON_UP:
-           state->current_zone = find_selected_zone(state, FORWARD);
-            state->previous_date_time = REFRESH_TIME;
-           break;
-       case EVENT_LIGHT_BUTTON_DOWN:
-           /* Do nothing. */
-           break;
-       case EVENT_LIGHT_BUTTON_UP:
-           state->current_zone = find_selected_zone(state, BACKWARD);
-            state->previous_date_time = REFRESH_TIME;
-           break;
-       case EVENT_LIGHT_LONG_PRESS:
-           movement_illuminate_led();
-           break;
-       case EVENT_ALARM_LONG_PRESS:
-           /* Switch to settings mode */
-           state->current_mode = WORLD_CLOCK2_MODE_SETTINGS;
-           refresh_face = true;
-            movement_request_tick_frequency(1);
-
-            if (movement_button_should_sound())
-                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-           break;
-        case EVENT_MODE_BUTTON_UP:
-            /* Reset frequency and move to next face */
-            movement_request_tick_frequency(1);
-            movement_move_to_next_face();
-            break;
-       default:
-           return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-static bool mode_settings(movement_event_t event, world_clock2_state_t *state)
-{
-    char buf[11];
-    int8_t hours, minutes;
-    uint8_t zone;
-    div_t result;
-
-    switch (event.event_type) {
-       case EVENT_ACTIVATE:
-       case EVENT_TICK:
-       case EVENT_LOW_ENERGY_UPDATE:
-            /* Update indicator and colon on refresh */
-            if (refresh_face) {
-                watch_clear_colon();
-                watch_clear_indicator(WATCH_INDICATOR_24H);
-                watch_clear_indicator(WATCH_INDICATOR_PM);
-                refresh_face = false;
-            }
-           result = div(movement_timezone_offsets[state->current_zone], 60);
-           hours = result.quot;
-           minutes = result.rem;
-
-           /*
-            * Display time zone. The range of the parameters is reduced
-            * to avoid accidentally overflowing the buffer and to suppress
-            * corresponding compiler warnings.
-            */
-           sprintf(buf, "%.2s%2d %c%02d%02d",
-                    zone_names[state->current_zone],
-                    state->current_zone % 100,
-                    hours < 0 ? '-' : '+',
-                    abs(hours) % 24,
-                   abs(minutes) % 60);
-
-            /* Let the zone number blink */
-            if (event.subsecond % 2)
-                buf[2] = buf[3] = ' ';
-
-           if (state->zones[state->current_zone].selected)
-               watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-           else
-               watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-
-           watch_display_string(buf, 0);
-           break;
-       case EVENT_ALARM_BUTTON_UP:
-           state->current_zone = mod(state->current_zone + FORWARD, NUM_TIME_ZONES);
-           break;
-       case EVENT_LIGHT_BUTTON_UP:
-           state->current_zone = mod(state->current_zone + BACKWARD, NUM_TIME_ZONES);
-           break;
-       case EVENT_LIGHT_BUTTON_DOWN:
-           /* Do nothing */
-           break;
-       case EVENT_ALARM_LONG_PRESS:
-           /* Find next selected zone */
-           if (!state->zones[state->current_zone].selected)
-               state->current_zone = find_selected_zone(state, FORWARD);
-
-           /* Switch to display mode */
-           state->current_mode = WORLD_CLOCK2_MODE_DISPLAY;
-           refresh_face = true;
-            movement_request_tick_frequency(1);
-
-            if (movement_button_should_sound())
-                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-           break;
-       case EVENT_LIGHT_LONG_PRESS:
-           /* Toggle selection of current zone */
-           zone = state->current_zone;
-           state->zones[zone].selected = !state->zones[zone].selected;
-
-            if (movement_button_should_sound()) {
-                if (state->zones[zone].selected) {
-                    beep_enable();
-                } else {
-                    beep_disable();
-                }
-            }
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            /* Reset frequency and move to next face */
-            movement_request_tick_frequency(1);
-            movement_move_to_next_face();
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-bool world_clock2_face_loop(movement_event_t event, void *context)
-{
-    world_clock2_state_t *state = (world_clock2_state_t *) context;
-    switch (state->current_mode) {
-       case WORLD_CLOCK2_MODE_DISPLAY:
-           return mode_display(event, state);
-       case WORLD_CLOCK2_MODE_SETTINGS:
-           return mode_settings(event, state);
-    }
-    return false;
-}
-
-void world_clock2_face_resign(void *context)
-{
-    (void) context;
-}
diff --git a/movement/watch_faces/clock/world_clock2_face.h b/movement/watch_faces/clock/world_clock2_face.h
deleted file mode 100644 (file)
index bd1ac69..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Konrad Rieck
- * 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 WORLD_CLOCK2_FACE_H_
-#define WORLD_CLOCK2_FACE_H_
-
-/*
- * WORLD CLOCK 2
- *
- * This is an alternative world clock face that allows the user to cycle
- * through a list of selected time zones. It extends the original
- * implementation by Joey Castillo. The face has two modes: display mode
- * and settings mode.
- *
- * Settings mode
- *
- * When the clock face is activated for the first time, it enters settings
- * mode. Here, the user can select the time zones they want to display. The
- * face shows a summary of the current time zone:
- *  * The top of the face displays the first two letters of the time zone
- *    abbreviation, such as "PS" for Pacific Standard Time or CE for
- *    "Central European Time".
- *  * The upper-right corner shows the index number of the time zone. This
- *    helps avoid confusion when multiple time zones have the same two-letter
- *    abbreviation.
- *  * The main display shows the offset from UTC, with a "+" indicating a
- *    positive offset and a "-" indicating a negative offset. For example,
- *    the offset for Japanese Standard Time is displayed as "+9:00".
- *
- * The user can navigate through the time zones and select them using the
- * following buttons:
- *  * The ALARM button moves forward to the next time zone, while the LIGHT
- *    button moves backward to the previous zone. This way, the user can
- *    cycle through all 41 supported time zones.
- *  * A long press on the LIGHT button selects the current time zone, and
- *    the signal indicator appears at the top left. Another long press of
- *    the LIGHT button deselects the time zone.
- *  * A long press on the ALARM button exits settings mode and returns to
- *    display mode.
- *
- * Display mode
- *
- * In the display mode, the face shows the time of the currently selected
- * time zone. The face includes the following components:
- *  * The top of the face displays the first two letters of the time zone
- *    abbreviation, such as "PS" for Pacific Standard Time or "CE" for
- *    Central European Time.
- *  * The upper-right corner shows the current day of the month, which helps
- *    indicate time zones that cross the international date line with respect
- *    to the local time.
- *  * The main display shows the time in the selected time zone in either
- *    12-hour or 24-hour form. There is no timeout, allowing users to keep
- *    the chosen time zone displayed for as long as they wish.
- *
- * The user can navigate through the selected time zones using the following
- * buttons:
- *  * The ALARM button moves to the next selected time zone, while the LIGHT
- *    button moves to the previous zone. If no time zone is selected, the
- *    face simply shows UTC.
- *  * A long press on the ALARM button enters settings mode and enables the
- *    user to re-configure the selected time zones.
- *  * A long press on the LIGHT button activates the LED illumination of the
- *    watch.
- */
-
-/* Number of zones. See movement_timezone_offsets. */
-#define NUM_TIME_ZONES  41
-
-#include "movement.h"
-
-typedef enum {
-    WORLD_CLOCK2_MODE_DISPLAY,
-    WORLD_CLOCK2_MODE_SETTINGS
-} world_clock2_mode_t;
-
-typedef struct {
-    bool selected;
-} world_clock2_zone_t;
-
-typedef struct {
-    world_clock2_zone_t zones[NUM_TIME_ZONES];
-    world_clock2_mode_t current_mode;
-    uint8_t current_zone;
-    uint32_t previous_date_time;
-} world_clock2_state_t;
-
-void world_clock2_face_setup(uint8_t watch_face_index, void **context_ptr);
-void world_clock2_face_activate(void *context);
-bool world_clock2_face_loop(movement_event_t event, void *context);
-void world_clock2_face_resign(void *context);
-
-#define world_clock2_face ((const watch_face_t){ \
-    world_clock2_face_setup, \
-    world_clock2_face_activate, \
-    world_clock2_face_loop, \
-    world_clock2_face_resign, \
-    NULL, \
-})
-
-#endif /* WORLD_CLOCK2_FACE_H_ */
diff --git a/movement/watch_faces/clock/wyoscan_face.c b/movement/watch_faces/clock/wyoscan_face.c
deleted file mode 100644 (file)
index bf7b5e1..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 <#author_name#>
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "wyoscan_face.h"
-#include "watch_private_display.h"
-
-/*
-Slowly render the current time from left to right, 
-scanning across its liquid crystal face, completing 1 cycle every 2 seconds.
-
-Created to mimic the wyoscan watch that was produced by Halmos and designed by Dexter Sinister
-It looks like this https://www.o-r-g.com/apps/wyoscan
-
-You’ll notice that reading this watch requires more attention than usual, 
-as the seven segments of each digit are lit one by one across its display. 
-This speed may be adjusted until it reaches the limits of your perception. 
-You and your watch are now in tune.
-
-This is a relatively generic way of animating a time display.
-If you want to modify the animation, you can change the segment_map
-the A-F are corresponding to the segments on the watch face
-  A  
-F   B
-  G
-E   C
-  D
-the X's are the frames that will be skipped in the animation
-This particular segment_map allocates 8 frames to display each number
-this is to achieve the 2 second cycle time.
-8 frames per number * 6 numbers + the trailing 16 frames = 64 frames
-at 32 frames per second, this is a 2 second cycle time.
-
-I tried to make the animation of each number display similar to if you were 
-to draw the number on the watch face with a pen, pausing with 'X'
-when your pen might turn a corner or when you might cross over 
-a line you've already drawn. It is vaguely top to bottom and counter,
-clockwise when possible.
-*/
-static char *segment_map[] = {
-    "AXFBDEXC",  // 0
-    "BXXXCXXX",  // 1
-    "ABGEXXXD",  // 2
-    "ABGXXXCD",  // 3
-    "FXGBXXXC",  // 4
-    "AXFXGXCD",  // 5
-    "AXFEDCXG",  // 6
-    "AXXBXXCX",  // 7
-    "AFGCDEXB",  // 8
-    "AFGBXXCD"   // 9
-};
-
-/*
-This is the mapping of input to the watch_set_pixel() function
-for each position in hhmmss it defines the 2 dimention input at each of A-F*/
-static const int32_t clock_mapping[6][7][2] = {
-    // hour 1
-    {{1,18}, {2,19}, {0,19}, {1,18}, {0,18}, {2,18}, {1,19}},
-    // hour 2
-    {{2,20}, {2,21}, {1,21}, {0,21}, {0,20}, {1,17}, {1,20}},
-    // minute 1
-    {{0,22}, {2,23}, {0,23}, {0,22}, {1,22}, {2,22}, {1,23}},
-    // minute 2
-    {{2,1}, {2,10}, {0,1}, {0,0}, {1,0}, {2,0}, {1,1}},
-    // second 1
-    {{2,2}, {2,3}, {0,4}, {0,3}, {0,2}, {1,2}, {1,3}},
-    // second 2
-    {{2,4}, {2,5}, {1,6}, {0,6}, {0,5}, {1,4}, {1,5}},
-};
-
-void wyoscan_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(wyoscan_state_t));
-        memset(*context_ptr, 0, sizeof(wyoscan_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void wyoscan_face_activate(void *context) {
-    wyoscan_state_t *state = (wyoscan_state_t *)context;
-    movement_request_tick_frequency(32);
-    state->total_frames = 64;
-}
-
-bool wyoscan_face_loop(movement_event_t event, void *context) {
-    wyoscan_state_t *state = (wyoscan_state_t *)context;
-
-    watch_date_time_t date_time;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            break;
-        case EVENT_TICK:            
-            if (!state->animate) {
-                date_time = watch_rtc_get_date_time();
-                state->start = 0; 
-                state->end = 0;
-                state->animation = 0;
-                state->animate = true;
-                state->time_digits[0] = date_time.unit.hour / 10;
-                state->time_digits[1] = date_time.unit.hour % 10;
-                state->time_digits[2] = date_time.unit.minute / 10;
-                state->time_digits[3] = date_time.unit.minute % 10;
-                state->time_digits[4] = date_time.unit.second / 10;
-                state->time_digits[5] = date_time.unit.second % 10;
-            }
-            if ( state->animate ) {
-                // if we have reached the max number of illuminated segments, we clear the oldest one
-                if ((state->end + 1) % MAX_ILLUMINATED_SEGMENTS == state->start) {
-                    // clear the oldest pixel if it's not 'X'
-                    if (state->illuminated_segments[state->start][0] != 99 && state->illuminated_segments[state->start][1] != 99) {
-                        watch_clear_pixel(state->illuminated_segments[state->start][0], state->illuminated_segments[state->start][1]);
-                    }
-                    // increment the start index to point to the next oldest pixel
-                    state->start = (state->start + 1) % MAX_ILLUMINATED_SEGMENTS;
-                }
-                if (state->animation < state->total_frames - MAX_ILLUMINATED_SEGMENTS) {
-                    if (state->animation % 32 == 0) {
-                        if (state->colon) {
-                            watch_set_colon();
-                        } else {
-                            watch_clear_colon();
-                        }
-                        state->colon = !state->colon;
-                    }
-                    
-                    // calculate the start position for the current frame
-                    state->position = (state->animation / 8) % 6;
-                    // calculate the current segment for the current digit
-                    state->segment = state->animation % strlen(segment_map[state->time_digits[state->position]]);
-                    // get the segments for the current digit
-                    state->segments = segment_map[state->time_digits[state->position]];
-                    
-                    if (state->segments[state->segment] == 'X') {
-                        // if 'X', skip this frame
-                        state->illuminated_segments[state->end][0] = 99;
-                        state->illuminated_segments[state->end][1] = 99;
-                        state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
-                        state->animation = (state->animation + 1);
-                        break;
-                    }
-
-                    // calculate the animation frame
-                    state->x = clock_mapping[state->position][state->segments[state->segment]-'A'][0];
-                    state->y = clock_mapping[state->position][state->segments[state->segment]-'A'][1];
-                    
-                    // set the new pixel
-                    watch_set_pixel(state->x, state->y);
-                    
-                    // store this pixel in the buffer
-                    state->illuminated_segments[state->end][0] = state->x;
-                    state->illuminated_segments[state->end][1] = state->y;
-                    // increment the end index to the next position
-                    state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
-                } 
-                else if (state->animation >= state->total_frames - MAX_ILLUMINATED_SEGMENTS && state->animation < state->total_frames) {
-                    state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
-                }
-                else {
-                    // reset the animation state
-                    state->animate = false;
-                }
-                state->animation = (state->animation + 1);
-            } 
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            break;
-        case EVENT_BACKGROUND_TASK:
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void wyoscan_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
-
diff --git a/movement/watch_faces/clock/wyoscan_face.h b/movement/watch_faces/clock/wyoscan_face.h
deleted file mode 100644 (file)
index ee41200..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 <#author_name#>
- *
- * 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 WYOSCAN_FACE_H_
-#define WYOSCAN_FACE_H_
-
-/*
- * WYOSCAN .5 hz watchface
- *
- * This is a recreation of the Wyoscan watch, which was a $175 watch in 2014.
- * It was an f-91w pcb replacement.
- * 
- * Video: https://user-images.githubusercontent.com/1795778/252550124-e07f0ed1-e328-4337-a654-fa1ee65d883f.mp4
- * Background information: https://artmetropole.com/shop/11460
- * Demo of what it looks like: https://www.o-r-g.com/apps/wyoscan
- *
- * 8 frames per number * 6 numbers + the trailing 16 frames = 64 frames
- * at 32 frames per second, this is a 2-second cycle time or 0.5 Hz.
- *
- * It is giving me a stack overflow after about 2.5 cycles of the time display
- * in the emulator, but it works fine on the watch.
- *
- * I'd like to make something for the low energy mode, but I haven't thought
- * about how that might work, right now it just freezes in low energy mode
- * until you press the 12-24HR button.
- *
- * There are no controls; it simply animates as long as the page is active.
- *
- */
-
-#include "movement.h"
-
-#define MAX_ILLUMINATED_SEGMENTS 16
-
-typedef struct {
-    uint32_t previous_date_time;
-    uint8_t last_battery_check;
-    uint8_t watch_face_index;
-    bool signal_enabled;
-    bool battery_low;
-    bool alarm_enabled;
-    uint8_t animation;
-    bool animate;
-    uint32_t start;
-    uint32_t end;
-    uint32_t total_frames;
-    bool colon;
-    uint8_t position, segment;
-    char *segments;
-    uint8_t x, y;
-    uint32_t time_digits[6];
-    uint32_t illuminated_segments[MAX_ILLUMINATED_SEGMENTS][2]; 
-} wyoscan_state_t;
-
-void wyoscan_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void wyoscan_face_activate(void *context);
-bool wyoscan_face_loop(movement_event_t event, void *context);
-void wyoscan_face_resign(void *context);
-movement_watch_face_advisory_t wyoscan_face_advise(void *context);
-
-#define wyoscan_face ((const watch_face_t){ \
-    wyoscan_face_setup, \
-    wyoscan_face_activate, \
-    wyoscan_face_loop, \
-    wyoscan_face_resign, \
-    NULL, \
-})
-
-#endif // WYOSCAN_FACE_H_
-
diff --git a/movement/watch_faces/complication/activity_face.c b/movement/watch_faces/complication/activity_face.c
deleted file mode 100644 (file)
index 5810749..0000000
+++ /dev/null
@@ -1,722 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Gabor L Ugray
- *
- * 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.
- */
-
-/* ** TODO
- * ===================
- * -- Additional power-saving optimizations
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "activity_face.h"
-#include "chirpy_tx.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-// ===========================================================================
-// This part is configurable: you can edit values here to customize you activity face
-// In particular, with num_enabled_activities and enabled_activities you can choose a subset of the
-//   activities that you want to see in your watch.
-// You can also add new items to activity_names, but don't redefine or remove existing ones.
-
-// If a logged activity is shorter than this, then it won't be added to log when it ends.
-// This way scarce log slots are not taken up by aborted events that weren't real activities.
-static const uint16_t activity_min_length_sec = 60;
-
-// Supported activities. ID of activity is index in this buffer
-// W e should never change order or redefine items, only add new items when needed.
-static const char activity_names[][7] = {
-    " bIKE ",
-    "uuaLK ",
-    "  rUn ",
-    "DAnCE ",
-    " yOgA ",
-    "CrOSS ",
-    "Suuinn",
-    "ELLIP ",
-    "  gYnn",
-    "  rOuu",
-    "SOCCEr",
-    " FOOTb",
-    " bALL ",
-    "  SKI ",
-};
-
-// Currently enabled activities. This makes picking on first subface easier: why show activities you personally never do.
-static const uint8_t enabled_activities[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
-
-// Number of currently enabled activities (size of enabled_activities).
-static const uint8_t num_enabled_activities = sizeof(enabled_activities) / sizeof(uint8_t);
-
-// End configurable section
-// ===========================================================================
-
-// One logged activity
-typedef struct __attribute__((__packed__)) {
-    // Activity's start time
-    watch_date_time_t start_time;
-
-    // Total duration of activity, including time spend in paus
-    uint16_t total_sec;
-
-    // Number of seconds the activity was paused
-    uint16_t pause_sec;
-
-    // Type of activity (index in activity_names)
-    uint8_t activity_type;
-
-} activity_item_t;
-
-#define MAX_ACTIVITY_SECONDS 28800 // 8 hours = 28800 sec
-
-// Size of (fixed) buffer to log activites. Takes up x9 bytes in SRAM if face is installed.
-#define ACTIVITY_LOG_SZ 99
-
-// Number of activities in buffer.
-static uint8_t activity_log_count = 0;
-
-// Buffer with all logged activities.
-static activity_item_t activity_log_buffer[ACTIVITY_LOG_SZ];
-
-#define CHIRPY_PREFIX_LEN 2
-// First two bytes chirped out, to identify transmission as from the activity face
-static const uint8_t activity_chirpy_prefix[CHIRPY_PREFIX_LEN] = {0x27, 0x00};
-
-// The face's different UI modes (views).
-typedef enum {
-    ACTM_CHOOSE = 0,
-    ACTM_LOGGING,
-    ACTM_PAUSED,
-    ACTM_DONE,
-    ACTM_LOGSIZE,
-    ACTM_CHIRP,
-    ACTM_CHIRPING,
-    ACTM_CLEAR,
-    ACTM_CLEAR_CONFIRM,
-    ACTM_CLEAR_DONE,
-} activity_mode_t;
-
-// The full state of the activity face
-typedef struct {
-    // Current mode (which secondary face, or ongoing operation like logging)
-    activity_mode_t mode;
-
-    // Index of currently selected activity in enabled_activities
-    uint8_t type_ix;
-
-    // Used for different things depending on mode
-    // In ACTM_DONE: countdown for animation, before returning to start face
-    // In ACTM_LOGGING and ACTM_PAUSED: drives blinking colon and alternating time display
-    // In ACTM_LOGSIZE, ACTM_CLEAR: enables timeout return to choose screen
-    uint16_t counter;
-
-    // Start of currently logged activity, if any
-    watch_date_time_t start_time;
-
-    // Total seconds elapsed since logging started
-    uint16_t curr_total_sec;
-
-    // Total paused seconds in current log
-    uint16_t curr_pause_sec;
-
-    // Helps us handle 1/64 ticks during transmission; including countdown timer
-    chirpy_tick_state_t chirpy_tick_state;
-
-    // Used by chirpy encoder during transmission
-    chirpy_encoder_state_t chirpy_encoder_state;
-
-    // 0: Running normally
-    // 1: In LE mode
-    // 2: Just woke up from LE mode. Will go to 0 after ignoring ALARM_BUTTON_UP.
-    uint8_t le_state;
-
-} activity_state_t;
-
-#define ACTIVITY_BUF_SZ 14
-
-// Temp buffer used for sprintf'ing content for the display.
-char activity_buf[ACTIVITY_BUF_SZ];
-
-// Needed by _activity_get_next_byte to keep track of where we are in transmission
-uint16_t *activity_seq_pos;
-
-static void _activity_clear_buffers() {
-    // Clear activity buffer; 0xcd is good for diagnostics
-    memset(activity_log_buffer, 0xcd, ACTIVITY_LOG_SZ * sizeof(activity_item_t));
-    // Clear display buffer
-    memset(activity_buf, 0, ACTIVITY_BUF_SZ);
-}
-
-static void _activity_display_choice(activity_state_t *state);
-static void _activity_update_logging_screen(activity_state_t *state);
-static uint8_t _activity_get_next_byte(uint8_t *next_byte);
-
-void activity_face_setup(uint8_t watch_face_index, void **context_ptr) {
-    (void)watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(activity_state_t));
-        memset(*context_ptr, 0, sizeof(activity_state_t));
-        // This happens only at boot
-        _activity_clear_buffers();
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void activity_face_activate(void *context) {
-    (void)context;
-
-    // Not using this function. Calling _activity_activate from the event handler.
-    // That is what we get both when the face is shown upon navigation by MODE,
-    // and when waking from low energy state.
-}
-
-// Called from the ACTIVATE event handler in the loop
-static void _activity_activate(activity_state_t *state) {
-    // If waking from low-energy state and currently logging: update seconds values
-    // Those are not up-to-date because ticks have not been coming
-    if (state->le_state != 0 && state->mode == ACTM_LOGGING) {
-        state->le_state = 2;
-        watch_date_time_t now = watch_rtc_get_date_time();
-        uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
-        uint32_t start_timestamp = watch_utility_date_time_to_unix_time(state->start_time, 0);
-        uint32_t total_seconds = now_timestamp - start_timestamp;
-        state->curr_total_sec = total_seconds;
-        _activity_update_logging_screen(state);
-    }
-    // Regular activation: start from defaults
-    else {
-        state->le_state = 0;
-        state->mode = 0;
-        state->type_ix = 0;
-        _activity_display_choice(state);
-    }
-}
-
-static void _activity_display_choice(activity_state_t *state) {
-    watch_display_string("AC", 0);
-    // If buffer is full: We say "FULL"
-    if (activity_log_count >= ACTIVITY_LOG_SZ) {
-        watch_display_string(" FULL ", 4);
-    }
-    // Otherwise, we show currently activity
-    else {
-        uint8_t activity_ix = enabled_activities[state->type_ix];
-        const char *name = activity_names[activity_ix];
-        watch_display_string((char *)name, 4);
-    }
-}
-
-const uint8_t activity_anim_pixels[][2] = {
-    {1, 4},  // TL
-    {0, 5},  // BL
-    {0, 6},  // BOT
-    {1, 6},  // BR
-    {2, 5},  // TR
-    {2, 4},  // TOP
-    // {2, 4}, // MID
-};
-
-static void _activity_update_logging_screen(activity_state_t *state) {
-    watch_duration_t duration;
-
-    watch_display_string("AC  ", 0);
-
-    // If we're in LE state: per-minute update is special
-    if (state->le_state == 1) {
-        watch_date_time_t now = watch_rtc_get_date_time();
-        uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
-        uint32_t start_timestamp = watch_utility_date_time_to_unix_time(state->start_time, 0);
-        uint32_t total_seconds = now_timestamp - start_timestamp;
-        duration = watch_utility_seconds_to_duration(total_seconds);
-        sprintf(activity_buf, " %d%02d  ", duration.hours, duration.minutes);
-        watch_display_string(activity_buf, 4);
-        watch_set_colon();
-        watch_set_indicator(WATCH_INDICATOR_LAP);
-        watch_clear_indicator(WATCH_INDICATOR_PM);
-        watch_clear_indicator(WATCH_INDICATOR_24H);
-        return;
-    }
-
-    // Show elapsed time, or PAUSE
-    if ((state->counter % 5) < 3) {
-        watch_set_indicator(WATCH_INDICATOR_LAP);
-        watch_clear_indicator(WATCH_INDICATOR_PM);
-        watch_clear_indicator(WATCH_INDICATOR_24H);
-        if (state->mode == ACTM_PAUSED) {
-            watch_display_string(" PAUSE", 4);
-            watch_clear_colon();
-        } else {
-            duration = watch_utility_seconds_to_duration(state->curr_total_sec);
-            // Under 10 minutes: M:SS
-            if (state->curr_total_sec < 600) {
-                sprintf(activity_buf, "   %01d%02d", duration.minutes, duration.seconds);
-                watch_display_string(activity_buf, 4);
-                watch_clear_colon();
-            }
-            // Under an hour: MM:SS
-            else if (state->curr_total_sec < 3600) {
-                sprintf(activity_buf, "  %02d%02d", duration.minutes, duration.seconds);
-                watch_display_string(activity_buf, 4);
-                watch_clear_colon();
-            }
-            // Over an hour: H:MM:SS
-            // (We never go to two-digit hours; stop at 8)
-            else {
-                sprintf(activity_buf, " %d%02d%02d", duration.hours, duration.minutes, duration.seconds);
-                watch_display_string(activity_buf, 4);
-                watch_set_colon();
-            }
-        }
-    }
-    // Briefly, show time without seconds
-    else {
-        watch_clear_indicator(WATCH_INDICATOR_LAP);
-        watch_date_time_t now = watch_rtc_get_date_time();
-        uint8_t hour = now.unit.hour;
-        if (!movement_clock_mode_24h()) {
-            watch_clear_indicator(WATCH_INDICATOR_24H);
-            if (hour < 12)
-                watch_clear_indicator(WATCH_INDICATOR_PM);
-            else
-                watch_set_indicator(WATCH_INDICATOR_PM);
-            hour %= 12;
-            if (hour == 0) hour = 12;
-        }
-        else {
-            watch_set_indicator(WATCH_INDICATOR_24H);
-            watch_clear_indicator(WATCH_INDICATOR_PM);
-        }
-        sprintf(activity_buf, "%2d%02d  ", hour, now.unit.minute);
-        watch_set_colon();
-        watch_display_string(activity_buf, 4);
-    }
-}
-
-static void _activity_quit_chirping() {
-    watch_clear_indicator(WATCH_INDICATOR_BELL);
-    watch_set_buzzer_off();
-    movement_request_tick_frequency(1);
-}
-
-static void _activity_chirp_tick_transmit(void *context) {
-    activity_state_t *state = (activity_state_t *)context;
-
-    uint8_t tone = chirpy_get_next_tone(&state->chirpy_encoder_state);
-    // Transmission over?
-    if (tone == 255) {
-        _activity_quit_chirping();
-        state->mode = ACTM_CHIRP;
-        state->counter = 0;
-        watch_display_string("AC  CHIRP ", 0);
-        return;
-    }
-    uint16_t period = chirpy_get_tone_period(tone);
-    watch_set_buzzer_period_and_duty_cycle(period, 25);
-    watch_set_buzzer_on();
-}
-
-static void _activity_chirp_tick_countdown(void *context) {
-    activity_state_t *state = (activity_state_t *)context;
-
-    // Countdown over: start actual broadcast
-    if (state->chirpy_tick_state.seq_pos == 8 * 3) {
-        state->chirpy_tick_state.tick_compare = 3;
-        state->chirpy_tick_state.tick_count = 2;  // tick_compare - 1, so it starts immediately
-        state->chirpy_tick_state.seq_pos = 0;
-        state->chirpy_tick_state.tick_fun = _activity_chirp_tick_transmit;
-        return;
-    }
-    // Sound or turn off buzzer
-    if ((state->chirpy_tick_state.seq_pos % 8) == 0) {
-        watch_set_buzzer_period_and_duty_cycle(NotePeriods[BUZZER_NOTE_A5], 25);
-        watch_set_buzzer_on();
-        if (state->chirpy_tick_state.seq_pos == 0) {
-            watch_display_string(" ---  ", 4);
-        } else if (state->chirpy_tick_state.seq_pos == 8) {
-            watch_display_string(" --", 5);
-        } else if (state->chirpy_tick_state.seq_pos == 16) {
-            watch_display_string("  -", 5);
-        }
-    } else if ((state->chirpy_tick_state.seq_pos % 8) == 1) {
-        watch_set_buzzer_off();
-    }
-    ++state->chirpy_tick_state.seq_pos;
-}
-
-static uint8_t _activity_get_next_byte(uint8_t *next_byte) {
-    uint16_t num_bytes = 2 + activity_log_count * sizeof(activity_item_t);
-    uint16_t pos = *activity_seq_pos;
-
-    // Init counter
-    if (pos == 0) {
-        sprintf(activity_buf, "%3d", activity_log_count);
-        watch_display_string(activity_buf, 5);
-    }
-
-    if (pos == num_bytes) {
-        return 0;
-    }
-    // Two-byte prefix
-    if (pos < 2) {
-        (*next_byte) = activity_chirpy_prefix[pos];
-    }
-    // Data
-    else {
-        pos -= 2;
-        uint16_t ix = pos / sizeof(activity_item_t);
-        const activity_item_t *itm = &activity_log_buffer[ix];
-        uint16_t ofs = pos % sizeof(activity_item_t);
-
-        // Update counter when starting new item
-        if (ofs == 0) {
-            sprintf(activity_buf, "%3d", activity_log_count - ix);
-            watch_display_string(activity_buf, 5);
-        }
-
-        // Do this the hard way, byte by byte, to avoid high/low endedness issues
-        // Higher order bytes first, is our serialization format
-        uint8_t val;
-        // watch_date_time_t start_time;
-        // uint16_t total_sec;
-        // uint16_t pause_sec;
-        // uint8_t activity_type;
-        if (ofs == 0)
-            val = (itm->start_time.reg & 0xff000000) >> 24;
-        else if (ofs == 1)
-            val = (itm->start_time.reg & 0x00ff0000) >> 16;
-        else if (ofs == 2)
-            val = (itm->start_time.reg & 0x0000ff00) >> 8;
-        else if (ofs == 3)
-            val = (itm->start_time.reg & 0x000000ff);
-        else if (ofs == 4)
-            val = (itm->total_sec & 0xff00) >> 8;
-        else if (ofs == 5)
-            val = (itm->total_sec & 0x00ff);
-        else if (ofs == 6)
-            val = (itm->pause_sec & 0xff00) >> 8;
-        else if (ofs == 7)
-            val = (itm->pause_sec & 0x00ff);
-        else
-            val = itm->activity_type;
-        (*next_byte) = val;
-    }
-    ++(*activity_seq_pos);
-    return 1;
-}
-
-static void _activity_finish_logging(activity_state_t *state) {
-    // Save this activity
-    // If shorter than minimum for log: don't save
-    // Sanity check about buffer length. This should never happen, but also we never want to overrun by error
-    if (state->curr_total_sec >= activity_min_length_sec && activity_log_count + 1 < ACTIVITY_LOG_SZ) {
-        activity_item_t *itm = &activity_log_buffer[activity_log_count];
-        itm->start_time = state->start_time;
-        itm->total_sec = state->curr_total_sec;
-        itm->pause_sec = state->curr_pause_sec;
-        itm->activity_type = state->type_ix;
-        ++activity_log_count;
-    }
-
-    // Go to DONE animation
-    // TODO: Not in LE mode
-    state->mode = ACTM_DONE;
-    watch_clear_indicator(WATCH_INDICATOR_LAP);
-    movement_request_tick_frequency(2);
-    state->counter = 6 * 1;
-    watch_clear_display();
-    watch_display_string("AC   dONE ", 0);
-}
-
-static void _activity_handle_tick(activity_state_t *state) {
-    // Display stopwatch-like duration while logging, alternating with time
-    if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
-        ++state->counter;
-        ++state->curr_total_sec;
-        if (state->mode == ACTM_PAUSED)
-            ++state->curr_pause_sec;
-        // If we've reached max activity length: finish logging
-        if (state->curr_total_sec == MAX_ACTIVITY_SECONDS) {
-            _activity_finish_logging(state);
-        }
-        // Still logging: refresh display
-        else {
-            _activity_update_logging_screen(state);
-        }
-    }
-    // Display countown animation, and exit face when down
-    else if (state->mode == ACTM_DONE) {
-        if (state->counter == 0) {
-            movement_move_to_face(0);
-            movement_request_tick_frequency(1);
-        }
-        else {
-            uint8_t cd = state->counter % 6;
-            watch_clear_pixel(activity_anim_pixels[cd][0], activity_anim_pixels[cd][1]);
-            --state->counter;
-            cd = state->counter % 6;
-            watch_set_pixel(activity_anim_pixels[cd][0], activity_anim_pixels[cd][1]);
-        }
-    }
-    // Log size, chirp, clear: return to choose after some time
-    else if (state->mode == ACTM_LOGSIZE || state->mode == ACTM_CHIRP || state->mode == ACTM_CLEAR) {
-        ++state->counter;
-        // Leave Log Size after 20 seconds
-        // Leave Clear after only 10: this is danger zone, we don't like hanging around here
-        // Leave Chirp after 2 minutes: most likely need to the time to fiddle with mic & Chirpy RX on the computer
-        uint16_t timeout = 20;
-        if (state->mode == ACTM_CLEAR) timeout = 10;
-        else if (state->mode == ACTM_CHIRP) timeout = 120;
-        if (state->counter > timeout) {
-            state->mode = ACTM_CHOOSE;
-            _activity_display_choice(state);
-        }
-    }
-    // Chirping
-    else if (state->mode == ACTM_CHIRPING) {
-        ++state->chirpy_tick_state.tick_count;
-        if (state->chirpy_tick_state.tick_count == state->chirpy_tick_state.tick_compare) {
-            state->chirpy_tick_state.tick_count = 0;
-            state->chirpy_tick_state.tick_fun(state);
-        }
-    }
-    // Clear confirm: blink CLEAR
-    else if (state->mode == ACTM_CLEAR_CONFIRM) {
-        ++state->counter;
-        if ((state->counter % 2) == 0)
-            watch_display_string("CLEAR ", 4);
-        else
-            watch_display_string("      ", 4);
-        if (state->counter > 12) {
-            state->mode = ACTM_CHOOSE;
-            _activity_display_choice(state);
-            movement_request_tick_frequency(1);
-        }
-    }
-    // Clear done: fill up zeroes, then return to choose screen
-    else if (state->mode == ACTM_CLEAR_DONE) {
-        ++state->counter;
-        // Animation done
-        if (state->counter == 7) {
-            state->mode = ACTM_CHOOSE;
-            _activity_display_choice(state);
-            movement_request_tick_frequency(1);
-            return;
-        }
-        // Display current state of animation
-        sprintf(activity_buf, "      ");
-        uint8_t nZeros = state->counter + 1;
-        if (nZeros > 6) nZeros = 6;
-        for (uint8_t i = 0; i < nZeros; ++i) {
-            activity_buf[i] = '0';
-        }
-        watch_display_string(activity_buf, 4);
-    }
-}
-
-static void _activity_alarm_long(activity_state_t *state) {
-    // On choose face: start logging activity
-    if (state->mode == ACTM_CHOOSE) {
-        // If buffer is full: Ignore this long press
-        if (activity_log_count >= ACTIVITY_LOG_SZ)
-            return;
-        // OK, we go ahead and start logging
-        state->start_time = watch_rtc_get_date_time();
-        state->curr_total_sec = 0;
-        state->curr_pause_sec = 0;
-        state->counter = -1;
-        state->mode = ACTM_LOGGING;
-        watch_set_indicator(WATCH_INDICATOR_LAP);
-        _activity_update_logging_screen(state);
-    }
-    // If logging or paused: end logging
-    else if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
-        _activity_finish_logging(state);
-    }
-    // If chirp: kick off chirping
-    else if (state->mode == ACTM_CHIRP) {
-        // Set up our tick handling for countdown beeps
-        activity_seq_pos = &state->chirpy_tick_state.seq_pos;
-        state->chirpy_tick_state.tick_compare = 8;
-        state->chirpy_tick_state.tick_count = 7;  // tick_compare - 1, so it starts immediately
-        state->chirpy_tick_state.seq_pos = 0;
-        state->chirpy_tick_state.tick_fun = _activity_chirp_tick_countdown;
-        // Set up chirpy encoder
-        chirpy_init_encoder(&state->chirpy_encoder_state, _activity_get_next_byte);
-        // Show bell; switch to 64/sec ticks
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-        movement_request_tick_frequency(64);
-        state->mode = ACTM_CHIRPING;
-    }
-    // If clear: confirm (unless empty)
-    else if (state->mode == ACTM_CLEAR) {
-        if (activity_log_count == 0)
-            return;
-        state->mode = ACTM_CLEAR_CONFIRM;
-        state->counter = -1;
-        movement_request_tick_frequency(4);
-    }
-    // If clear confirm: do clear.
-    else if (state->mode == ACTM_CLEAR_CONFIRM) {
-        _activity_clear_buffers();
-        activity_log_count = 0;
-        state->mode = ACTM_CLEAR_DONE;
-        state->counter = -1;
-        watch_display_string("0     ", 4);
-        movement_request_tick_frequency(2);
-    }
-}
-
-static void _activity_alarm_short(activity_state_t *state) {
-    // In the choose face, short ALARM cycles through activities
-    if (state->mode == ACTM_CHOOSE) {
-        state->type_ix = (state->type_ix + 1) % num_enabled_activities;
-        _activity_display_choice(state);
-    }
-    // If logging: pause
-    else if (state->mode == ACTM_LOGGING) {
-        state->mode = ACTM_PAUSED;
-        state->counter = 0;
-        _activity_update_logging_screen(state);
-    }
-    // If paused: Update paused seconds count and return to logging
-    else if (state->mode == ACTM_PAUSED) {
-        state->mode = ACTM_LOGGING;
-        state->counter = 0;
-        _activity_update_logging_screen(state);
-    }
-    // If chirping: stoppit
-    else if (state->mode == ACTM_CHIRPING) {
-        _activity_quit_chirping();
-        state->mode = ACTM_CHIRP;
-        state->counter = 0;
-        watch_display_string("AC  CHIRP ", 0);
-    }
-}
-
-static void _activity_light_short(activity_state_t *state) {
-    // If choose face: move to log size
-    if (state->mode == ACTM_CHOOSE) {
-        state->mode = ACTM_LOGSIZE;
-        state->counter = 0;
-        sprintf(activity_buf, "AC  L#g%3d", activity_log_count);
-        watch_display_string(activity_buf, 0);
-    }
-    // If log size face: move to chirp
-    else if (state->mode == ACTM_LOGSIZE) {
-        state->mode = ACTM_CHIRP;
-        state->counter = 0;
-        watch_display_string("AC  CHIRP ", 0);
-    }
-    // If chirp face: move to clear
-    else if (state->mode == ACTM_CHIRP) {
-        state->mode = ACTM_CLEAR;
-        state->counter = 0;
-        watch_display_string("AC  CLEAR ", 0);
-    }
-    // If clear face: return to choose face
-    else if (state->mode == ACTM_CLEAR || state->mode == ACTM_CLEAR_CONFIRM) {
-        state->mode = ACTM_CHOOSE;
-        _activity_display_choice(state);
-        movement_request_tick_frequency(1);
-    }
-    // While logging or paused, light is light
-    else if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
-        movement_illuminate_led();
-    }
-    // Otherwise, we don't do light.
-}
-
-bool activity_face_loop(movement_event_t event, void *context) {
-    activity_state_t *state = (activity_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _activity_activate(state);
-            break;
-        case EVENT_TICK:
-            _activity_handle_tick(state);
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            if (state->mode != ACTM_LOGGING && state->mode != ACTM_PAUSED && state->mode != ACTM_CHIRPING) {
-                movement_request_tick_frequency(1);
-                movement_move_to_next_face();
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            _activity_light_short(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // We also receive ALARM press that woke us up from LE state
-            // Don't want to act on that as if it were a real button press for us
-            if (state->le_state != 2)
-                _activity_alarm_short(state);
-            else
-                state->le_state = 0;
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            _activity_alarm_long(state);
-            break;
-        case EVENT_TIMEOUT:
-            if (state->mode != ACTM_LOGGING && state->mode != ACTM_PAUSED &&
-                state->mode != ACTM_CHIRP && state->mode != ACTM_CHIRPING) {
-                movement_request_tick_frequency(1);
-                movement_move_to_face(0);
-            }
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            state->le_state = 1;
-            // If we're in paused logging mode: let's lose this activity. Pause is not meant for over an hour.
-            if (state->mode == ACTM_PAUSED) {
-                // When waking, face will revert to default screen
-                state->mode = ACTM_CHOOSE;
-                watch_display_string("AC  SLEEP ", 0);
-                watch_clear_colon();
-                watch_clear_indicator(WATCH_INDICATOR_LAP);
-                watch_clear_indicator(WATCH_INDICATOR_PM);
-            }
-            else {
-                _activity_update_logging_screen(state);
-                watch_start_sleep_animation(500);
-            }
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    // Return true if the watch can enter standby mode. False needed when chirping.
-    if (state->mode == ACTM_CHIRPING)
-        return false;
-    else
-        return true;
-}
-
-void activity_face_resign(void *context) {
-    (void)context;
-
-    // Face should only ever temporarily request a higher frequency, so by the time we're resigning,
-    // this should not be needed. But we don't want an error to create a situation that drains the battery.
-    // Rather do this defensively here.
-    movement_request_tick_frequency(1);
-}
diff --git a/movement/watch_faces/complication/activity_face.h b/movement/watch_faces/complication/activity_face.h
deleted file mode 100644 (file)
index 48f9ee8..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Gabor L Ugray
- *
- * 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 ACTIVITY_FACE_H_
-#define ACTIVITY_FACE_H_
-
-/*
- * ACTIVITY watch face
- *
- * The Activity face lets you record activities like you would do with a fitness watch.
- * It supports different activities like running, biking, rowing etc., and for each recorded activity
- * it stores when it started and how long it was.
- * 
- * You can save up to 99 activities this way. Every once in a while you can chirp them out
- * using the watch's piezo buzzer as a modem, then clear the log in the watch.
- * To record and decode a chirpy transmission on your computer, you can use the web app here:
- * https://jealousmarkup.xyz/off/chirpy/rx/
- * 
- * Using the face
- * 
- * When you activate the face, it starts with the first screen to select the activity you want to log.
- * ALARM cycles through the list of activities.
- * LONG ALARM starts logging.
- * While logging is in progress, the face alternates between the elapsed time and the current time.
- * You can press ALARM to pause (e.g., while you hop in to the baker's for a croissant during your jog).
- * Pressing ALARM again resumes the activity.
- * LONG ALARM stops logging and saves the activity.
- * 
- * When you're not loggin, you can press LIGHT to access the secondary faces.
- * LIGHT #1 => Shows the size of the log (how many activities have been recorded).
- * LIGHT #2 => The screen to chirp out the data. Press LONG ALARM to start chirping.
- * LIGHT #3 => The screen to clear the log in the watch. Press LONG ALARM twice to clear data.
- *
- * Quirky details
- * 
- * The face will discard short activities (less than a minute) when you press LONG ALARM to finish logging.
- * These were probably logged by mistake, and it's better to save slots and chirping battery power for
- * stuff that really matters.
- * 
- * The face will continue to record an activity past the normal one-hour mark, when the watch
- * enters low energy mode. However, it will always stop at 8 hours. If an activity takes that long,
- * you probably just forgot to stop logging.
- * 
- * The log is stored in regular memory. It will be lost when you remove the battery, so make
- * sure you chirp it out before taking the watch apart.
- * 
- * See the top of activity_face.c for some customization options. What you most likely want to do
- * is reduce the list of activities shown on the first screen to the ones you are regularly doing.
- */
-
-#include "movement.h"
-
-void activity_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void activity_face_activate(void *context);
-bool activity_face_loop(movement_event_t event, void *context);
-void activity_face_resign(void *context);
-
-#define activity_face ((const watch_face_t){ \
-    activity_face_setup, \
-    activity_face_activate, \
-    activity_face_loop, \
-    activity_face_resign, \
-    NULL, \
-})
-
-#endif // ACTIVITY_FACE_H_
-
diff --git a/movement/watch_faces/complication/astronomy_face.c b/movement/watch_faces/complication/astronomy_face.c
deleted file mode 100644 (file)
index 61df67b..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include "astronomy_face.h"
-#include "watch_utility.h"
-
-#if __EMSCRIPTEN__
-#include <emscripten.h>
-#endif
-
-#define NUM_AVAILABLE_BODIES 9
-
-static const char astronomy_available_celestial_bodies[NUM_AVAILABLE_BODIES] = {
-    ASTRO_BODY_SUN,
-    ASTRO_BODY_MERCURY,
-    ASTRO_BODY_VENUS,
-    ASTRO_BODY_MOON,
-    ASTRO_BODY_MARS,
-    ASTRO_BODY_JUPITER,
-    ASTRO_BODY_SATURN,
-    ASTRO_BODY_URANUS,
-    ASTRO_BODY_NEPTUNE
-};
-
-static const char astronomy_celestial_body_names[NUM_AVAILABLE_BODIES][3] = {
-    "SO",   // Sol
-    "ME",   // Mercury
-    "VE",   // Venus
-    "LU",   // Moon (Luna)
-    "MA",   // Mars
-    "JU",   // Jupiter
-    "SA",   // Saturn
-    "UR",   // Uranus
-    "NE"    // Neptune
-};
-
-static void _astronomy_face_recalculate(astronomy_state_t *state) {
-#if __EMSCRIPTEN__
-    int16_t browser_lat = EM_ASM_INT({
-        return lat;
-    });
-    int16_t browser_lon = EM_ASM_INT({
-        return lon;
-    });
-    if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
-        movement_location_t browser_loc;
-        browser_loc.bit.latitude = browser_lat;
-        browser_loc.bit.longitude = browser_lon;
-        watch_store_backup_data(browser_loc.reg, 1);
-        double lat = (double)browser_lat / 100.0;
-        double lon = (double)browser_lon / 100.0;
-        state->latitude_radians = astro_degrees_to_radians(lat);
-        state->longitude_radians = astro_degrees_to_radians(lon);
-    }
-#endif
-
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
-    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);
-
-    astro_equatorial_coordinates_t radec_precession = astro_get_ra_dec(jd, astronomy_available_celestial_bodies[state->active_body_index], state->latitude_radians, state->longitude_radians, true);
-    printf("\nParams to convert: %f %f %f %f %f\n",
-            jd,
-            astro_radians_to_degrees(state->latitude_radians),
-            astro_radians_to_degrees(state->longitude_radians),
-            astro_radians_to_degrees(radec_precession.right_ascension),
-            astro_radians_to_degrees(radec_precession.declination));
-
-    astro_horizontal_coordinates_t horiz = astro_ra_dec_to_alt_az(jd, state->latitude_radians, state->longitude_radians, radec_precession.right_ascension, radec_precession.declination);
-    astro_equatorial_coordinates_t radec = astro_get_ra_dec(jd, astronomy_available_celestial_bodies[state->active_body_index], state->latitude_radians, state->longitude_radians, false);
-    state->altitude = astro_radians_to_degrees(horiz.altitude);
-    state->azimuth = astro_radians_to_degrees(horiz.azimuth);
-    state->right_ascension = astro_radians_to_hms(radec.right_ascension);
-    state->declination = astro_radians_to_dms(radec.declination);
-    state->distance = radec.distance;
-
-    printf("Calculated coordinates for %s on %f: \n\tRA  = %f / %2dh %2dm %2ds\n\tDec = %f / %3d° %3d' %3d\"\n\tAzi = %f\n\tAlt = %f\n\tDst = %f AU\n",
-            astronomy_celestial_body_names[state->active_body_index],
-            jd,
-            astro_radians_to_degrees(radec.right_ascension),
-            state->right_ascension.hours,
-            state->right_ascension.minutes,
-            state->right_ascension.seconds,
-            astro_radians_to_degrees(radec.declination),
-            state->declination.degrees,
-            state->declination.minutes,
-            state->declination.seconds,
-            state->altitude,
-            state->azimuth,
-            state->distance);
-}
-
-static void _astronomy_face_update(movement_event_t event, astronomy_state_t *state) {
-    char buf[16];
-    switch (state->mode) {
-        case ASTRONOMY_MODE_SELECTING_BODY:
-            watch_clear_colon();
-            watch_display_string(" Astro", 4);
-            if (event.subsecond % 2) {
-                watch_display_string((char *)astronomy_celestial_body_names[state->active_body_index], 0);
-            } else {
-                watch_display_string("  ", 0);
-            }
-            if (event.subsecond == 0) {
-                watch_display_string("  ", 2);
-                switch (state->animation_state) {
-                    case 0:
-                        watch_set_pixel(0, 7);
-                        watch_set_pixel(2, 6);
-                        break;
-                    case 1:
-                        watch_set_pixel(1, 7);
-                        watch_set_pixel(2, 9);
-                        break;
-                    case 2:
-                        watch_set_pixel(2, 7);
-                        watch_set_pixel(0, 9);
-                        break;
-                }
-                state->animation_state = (state->animation_state + 1) % 3;
-            }
-            break;
-        case ASTRONOMY_MODE_CALCULATING:
-            watch_clear_display();
-             // this takes a moment and locks the UI, flash C for "Calculating"
-            watch_start_character_blink('C', 100);
-            _astronomy_face_recalculate(state);
-            watch_stop_blink();
-            state->mode = ASTRONOMY_MODE_DISPLAYING_ALT;
-            // fall through
-        case ASTRONOMY_MODE_DISPLAYING_ALT:
-            sprintf(buf, "%saL%6d", astronomy_celestial_body_names[state->active_body_index], (int16_t)round(state->altitude * 100));
-            watch_display_string(buf, 0);
-            break;
-        case ASTRONOMY_MODE_DISPLAYING_AZI:
-            sprintf(buf, "%saZ%6d", astronomy_celestial_body_names[state->active_body_index], (int16_t)round(state->azimuth * 100));
-            watch_display_string(buf, 0);
-            break;
-        case ASTRONOMY_MODE_DISPLAYING_RA:
-            watch_set_colon();
-            sprintf(buf, "ra H%02d%02d%02d", state->right_ascension.hours, state->right_ascension.minutes, state->right_ascension.seconds);
-            watch_display_string(buf, 0);
-            break;
-        case ASTRONOMY_MODE_DISPLAYING_DEC:
-            watch_clear_colon();
-            sprintf(buf, "de %3d%2d%2d", state->declination.degrees, state->declination.minutes, state->declination.seconds);
-            watch_display_string(buf, 0);
-            break;
-        case ASTRONOMY_MODE_DISPLAYING_DIST:
-            if (state->distance >= 0.00668456) {
-                // if >= 1,000,000 kilometers (all planets), we display distance in AU.
-                sprintf(buf, "diAU%6d", (uint16_t)round(state->distance * 100));
-            } else {
-                // otherwise distance in kilometers fits in 6 digits. This mode will only happen for Luna.
-                sprintf(buf, "di K%6ld", (uint32_t)round(state->distance * 149597871.0));
-            }
-            watch_display_string(buf, 0);
-            break;
-        case ASTRONOMY_MODE_NUM_MODES:
-            // this case does not happen, but we need it to silence a warning.
-            break;
-    }
-}
-
-void astronomy_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(astronomy_state_t));
-        memset(*context_ptr, 0, sizeof(astronomy_state_t));
-    }
-}
-
-void astronomy_face_activate(void *context) {
-    astronomy_state_t *state = (astronomy_state_t *)context;
-    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
-    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
-    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
-    double lat = (double)lat_centi / 100.0;
-    double lon = (double)lon_centi / 100.0;
-    state->latitude_radians = astro_degrees_to_radians(lat);
-    state->longitude_radians = astro_degrees_to_radians(lon);
-
-    movement_request_tick_frequency(4);
-}
-
-bool astronomy_face_loop(movement_event_t event, void *context) {
-    astronomy_state_t *state = (astronomy_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            _astronomy_face_update(event, state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch (state->mode) {
-                case ASTRONOMY_MODE_SELECTING_BODY:
-                    // advance to next celestial body (move to calculations with a long press)
-                    state->active_body_index = (state->active_body_index + 1) % NUM_AVAILABLE_BODIES;
-                    break;
-                case ASTRONOMY_MODE_CALCULATING:
-                    // ignore button press during calculations
-                    break;
-                case ASTRONOMY_MODE_DISPLAYING_DIST:
-                    // at last mode, wrap around
-                    state->mode = ASTRONOMY_MODE_DISPLAYING_ALT;
-                    break;
-                default:
-                    // otherwise, advance to next mode
-                    state->mode++;
-                    break;
-            }
-            _astronomy_face_update(event, state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->mode == ASTRONOMY_MODE_SELECTING_BODY) {
-                // celestial body selected! this triggers a calculation in the update method.
-                state->mode = ASTRONOMY_MODE_CALCULATING;
-                movement_request_tick_frequency(1);
-                _astronomy_face_update(event, state);
-            } else if (state->mode != ASTRONOMY_MODE_CALCULATING) {
-                // in all modes except "doing a calculation", return to the selection screen.
-                state->mode = ASTRONOMY_MODE_SELECTING_BODY;
-                movement_request_tick_frequency(4);
-            _astronomy_face_update(event, state);
-            }
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // TODO?
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void astronomy_face_resign(void *context) {
-    astronomy_state_t *state = (astronomy_state_t *)context;
-    state->mode = ASTRONOMY_MODE_SELECTING_BODY;
-}
diff --git a/movement/watch_faces/complication/astronomy_face.h b/movement/watch_faces/complication/astronomy_face.h
deleted file mode 100644 (file)
index e0b33f5..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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 ASTRONOMY_FACE_H_
-#define ASTRONOMY_FACE_H_
-
-/*
- * ASTRONOMY face
- *
- * The Astronomy watch face is among the most complex watch faces in the
- * Movement collection. It allows you to calculate the locations of celestial
- * bodies in the sky, as well as distance in astronomical units (or, in the
- * case of the Moon, distance in kilometers).
- * 
- * When you arrive at the Astronomy watch face, you’ll see its name (“Astro”)
- * and an animation of two objects orbiting each other. You will also see “SO”
- * (for Sol) flashing in the top left. The flashing letters indicate the
- * currently selected celestial body. Short press Alarm to advance through
- * the available celestial bodies:
- * 
- *     SO - Sol, the sun
- *     ME - Mercury
- *     VE - Venus
- *     LU - Luna, the Earth’s moon
- *     MA - Mars
- *     JU - Jupiter
- *     SA - Saturn
- *     UR - Uranus
- *     NE - Neptune
- * 
- * Once you’ve selected the celestial body whose parameters you wish to
- * calculate, long press the Alarm button and release it. The letter “C” will
- * flash while the calculation is performed.
- * 
- * When the calculation is complete, the screen will display the altitude
- * (“aL”) of the celestial body. You can cycle through the available parameters
- * with repeated short presses on the Alarm button:
- * 
- *     aL - Altitude (in degrees), the elevation over the horizon. If negative, it is below the horizon.
- *     aZ - Azimuth (in degrees), the cardinal direction relative to true north.
- *     rA - Right Ascension (in hours/minutes/seconds)
- *     dE - Declination (in degrees/minutes/seconds)
- *     di - Distance (the digits in the top right will display either aU for astronomical units, or K for kilometers)
- * 
- * Long press on the Alarm button to select another celestial body.
- */
-
-#include "movement.h"
-#include "astrolib.h"
-
-typedef enum {
-    ASTRONOMY_MODE_SELECTING_BODY = 0,
-    ASTRONOMY_MODE_CALCULATING,
-    ASTRONOMY_MODE_DISPLAYING_ALT,
-    ASTRONOMY_MODE_DISPLAYING_AZI,
-    ASTRONOMY_MODE_DISPLAYING_RA,
-    ASTRONOMY_MODE_DISPLAYING_DEC,
-    ASTRONOMY_MODE_DISPLAYING_DIST,
-    ASTRONOMY_MODE_NUM_MODES
-} astronomy_mode_t;
-
-typedef struct {
-    astronomy_mode_t mode;
-    uint8_t active_body_index;
-    uint8_t animation_state;
-    double latitude_radians;    // this is the user location
-    double longitude_radians;   // but in radians
-    astro_angle_hms_t right_ascension;
-    astro_angle_dms_t declination;
-    double altitude;    // in decimal degrees
-    double azimuth;     // in decimal degrees
-    double distance;    // in AU
-} astronomy_state_t;
-
-void astronomy_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void astronomy_face_activate(void *context);
-bool astronomy_face_loop(movement_event_t event, void *context);
-void astronomy_face_resign(void *context);
-
-#define astronomy_face ((const watch_face_t){ \
-    astronomy_face_setup, \
-    astronomy_face_activate, \
-    astronomy_face_loop, \
-    astronomy_face_resign, \
-    NULL, \
-})
-
-#endif // ASTRONOMY_FACE_H_
diff --git a/movement/watch_faces/complication/blinky_face.c b/movement/watch_faces/complication/blinky_face.c
deleted file mode 100644 (file)
index aa43466..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "blinky_face.h"
-#include "watch.h"
-
-void blinky_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(blinky_face_state_t));
-        memset(*context_ptr, 0, sizeof(blinky_face_state_t));
-    }
-}
-
-void blinky_face_activate(void *context) {
-    blinky_face_state_t *state = (blinky_face_state_t *)context;
-    state->active = false;
-}
-
-static void _blinky_face_update_lcd(blinky_face_state_t *state) {
-    char buf[11];
-    const char colors[][7] = {" red  ", " Green", " Yello"};
-    sprintf(buf, "BL %c%s", state->fast ? 'F' : 'S', colors[state->color]);
-    watch_display_string(buf, 0);
-}
-
-bool blinky_face_loop(movement_event_t event, void *context) {
-    blinky_face_state_t *state = (blinky_face_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _blinky_face_update_lcd(state);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            if (!state->active) {
-                state->color = (state->color + 1) % 3;
-                _blinky_face_update_lcd(state);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if (!state->active) {
-                state->active = true;
-                watch_clear_display();
-                movement_request_tick_frequency(state->fast ? 8 : 2);            
-            } else {
-                state->active = false;
-                watch_set_led_off();
-                _blinky_face_update_lcd(state);
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (!state->active) {
-                state->fast = !state->fast;
-                _blinky_face_update_lcd(state);
-            }
-            break;
-        case EVENT_TICK:
-            if (state->active) {
-                if (event.subsecond % 2 == 0) watch_set_led_off();
-                else if (state->color == 0) watch_set_led_red();
-                else if (state->color == 1) watch_set_led_green();
-                else watch_set_led_yellow();
-            }
-            break;
-        case EVENT_TIMEOUT:
-            if (!state->active) movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void blinky_face_resign(void *context) {
-    (void) context;
-    watch_set_led_off();
-}
diff --git a/movement/watch_faces/complication/blinky_face.h b/movement/watch_faces/complication/blinky_face.h
deleted file mode 100644 (file)
index cf6f265..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 BLINKY_FACE_H_
-#define BLINKY_FACE_H_
-
-/*
- * BLINKY LIGHT face
- *
- * The blinky light watch face was designed as a tutorial for making a watch
- * face in Movement, but it actually might be useful to have a blinking light
- * in a pinch.
- *
- * The screen displays the name of the watch face (”BL”), as well as an S at
- * the top right for slow blink or an F for fast blink. The bottom line selects
- * the color: green, red or yellow. You can change the speed of the blinking
- * light by pressing the Alarm button, and change the color with the Light
- * button. A long press on the Alarm button starts the blinking light, and
- * another long press stops it.
- *
- * Note that this will chew through your battery! The green LED uses about
- * 450µA at full brightness, which is 45 times the normal power consumption of
- * the watch. The red LED is an order of magnitude less efficient (4500 µA),
- * and the yellow setting lights both LEDs, which chews through nearly
- * 5 milliamperes. This means that one hour of yellow blinking is likely to
- * eat up between 2 and 3 percent of the battery’s usable life!
- *
- * Still, if you need to signal your location to someone in a dark forest,
- * this watch face could come in handy. Just try to use the green LED as much
- * as you can.
- */
-
-#include "movement.h"
-
-typedef struct {
-    bool active;
-    bool fast;
-    uint8_t color;
-} blinky_face_state_t;
-
-void blinky_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void blinky_face_activate(void *context);
-bool blinky_face_loop(movement_event_t event, void *context);
-void blinky_face_resign(void *context);
-
-#define blinky_face ((const watch_face_t){ \
-    blinky_face_setup, \
-    blinky_face_activate, \
-    blinky_face_loop, \
-    blinky_face_resign, \
-    NULL, \
-})
-
-#endif // BLINKY_FACE_H_
diff --git a/movement/watch_faces/complication/breathing_face.c b/movement/watch_faces/complication/breathing_face.c
deleted file mode 100644 (file)
index c9ea67a..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Bernd Plontsch
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "breathing_face.h"
-#include "watch.h"
-
-typedef struct {
-    uint8_t current_stage;
-    bool sound_on;
-} breathing_state_t;
-
-static void beep_in (void);
-static void beep_in_hold (void);
-static void beep_out (void);
-static void beep_out_hold (void);
-
-void breathing_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    // These next two lines just silence the compiler warnings associated with unused parameters.
-    // We have no use for the settings or the watch_face_index, so we make that explicit here.
-    (void) watch_face_index;
-    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
-    if (*context_ptr == NULL) {
-        // in this case, we allocate an area of memory sufficient to store the stuff we need to track.
-        *context_ptr = malloc(sizeof(breathing_state_t));
-    }
-}
-
-void breathing_face_activate(void *context) {
-    // same as above: silence the warning, we don't need to check the settings.
-    // we do however need to set some things in our context. Here we cast it to the correct type...
-    breathing_state_t *state = (breathing_state_t *)context;
-    // ...and set the initial state of our watch face.
-    state->current_stage = 0;
-    state->sound_on = true;
-}
-
-const int NOTE_LENGTH = 80;
-
-void beep_in (void) {
-        const watch_buzzer_note_t notes[] = {
-            BUZZER_NOTE_C4,
-            BUZZER_NOTE_D4,
-            BUZZER_NOTE_E4,
-        };
-        const uint16_t durations[] = {
-            NOTE_LENGTH,
-            NOTE_LENGTH,
-            NOTE_LENGTH
-        };
-        for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
-            watch_buzzer_play_note(notes[i], durations[i]);
-        }
-}
-
-void beep_in_hold (void) {
-        const watch_buzzer_note_t notes[] = {
-            BUZZER_NOTE_E4,
-            BUZZER_NOTE_REST,
-            BUZZER_NOTE_E4,
-        };
-        const uint16_t durations[] = {
-            NOTE_LENGTH,
-            NOTE_LENGTH * 2,
-            NOTE_LENGTH,
-        };
-        for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
-            watch_buzzer_play_note(notes[i], durations[i]);
-        }
-}
-
-void beep_out (void) {
-        const watch_buzzer_note_t notes[] = {
-            BUZZER_NOTE_E4,
-            BUZZER_NOTE_D4,
-            BUZZER_NOTE_C4,
-        };
-        const uint16_t durations[] = {
-            NOTE_LENGTH,
-            NOTE_LENGTH,
-            NOTE_LENGTH,
-        };
-        for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
-            watch_buzzer_play_note(notes[i], durations[i]);
-        }
-}
-
-void beep_out_hold (void) {
-        const watch_buzzer_note_t notes[] = {
-            BUZZER_NOTE_C4,
-            BUZZER_NOTE_REST * 2,
-            BUZZER_NOTE_C4,
-        };
-        const uint16_t durations[] = {
-            NOTE_LENGTH,
-            NOTE_LENGTH,
-            NOTE_LENGTH,
-        };
-        for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
-            watch_buzzer_play_note(notes[i], durations[i]);
-        }
-}
-
-bool breathing_face_loop(movement_event_t event, void *context) {
-    breathing_state_t *state = (breathing_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-
-            if (state->sound_on == true) {
-                watch_set_indicator(WATCH_INDICATOR_BELL); 
-            } else {
-                watch_clear_indicator(WATCH_INDICATOR_BELL); 
-            }
-
-            switch (state->current_stage)
-            {
-            case 0: { watch_display_string("Breath", 4); if (state->sound_on) beep_in(); } break;
-            case 1: watch_display_string("In   3", 4); break;
-            case 2: watch_display_string("In   2", 4); break;
-            case 3: watch_display_string("In   1", 4); break;
-            
-            case 4: { watch_display_string("Hold 4", 4); if (state->sound_on) beep_in_hold(); } break;
-            case 5: watch_display_string("Hold 3", 4); break;
-            case 6: watch_display_string("Hold 2", 4); break;               
-            case 7:  watch_display_string("Hold 1", 4); break;
-
-            case 8: { watch_display_string("Ou t 4", 4); if (state->sound_on) beep_out(); } break;
-            case 9: watch_display_string("Ou t 3", 4); break;
-            case 10: watch_display_string("Ou t 2", 4); break;
-            case 11: watch_display_string("Ou t 1", 4); break;         
-            
-            case 12: { watch_display_string("Hold 4", 4); if (state->sound_on) beep_out_hold(); } break;
-            case 13: watch_display_string("Hold 3", 4); break;
-            case 14: watch_display_string("Hold 2", 4); break;     
-            case 15: watch_display_string("Hold 1", 4); break;     
-            default:
-                break;
-            }
-
-            // and increment it so that it will update on the next tick.
-            state->current_stage = (state->current_stage + 1) % 16;
-
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            state->sound_on = !state->sound_on;            
-             if (state->sound_on == true) {
-                watch_set_indicator(WATCH_INDICATOR_BELL); 
-            } else {
-                watch_clear_indicator(WATCH_INDICATOR_BELL); 
-            }
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // This low energy mode update occurs once a minute, if the watch face is in the
-            // foreground when Movement enters low energy mode. We have the option of supporting
-            // this mode, but since our watch face animates once a second, the "Hello there" face
-            // isn't very useful in this mode. So we choose not to support it. (continued below)
-            break;
-        case EVENT_TIMEOUT:
-            // ... Instead, we respond to the timeout event. This event happens after a configurable
-            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
-            // resign control if we want to, and in this case, we do.
-            // This function will return the watch to the first screen (usually a simple clock),
-            // and it will do it long before the watch enters low energy mode. This ensures we
-            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
-            
-            // movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void breathing_face_resign(void *context) {
-    // our watch face, like most watch faces, has nothing special to do when resigning.
-    // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/breathing_face.h b/movement/watch_faces/complication/breathing_face.h
deleted file mode 100644 (file)
index 91ef2a6..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Bernd Plontsch
- *
- * 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 BREATHING_FACE_H_
-#define BREATHING_FACE_H_
-
-/*
- * BOXED BREATHING face
- *
- * Breathing is a complication for guiding boxed breathing sessions.
- * Boxed breathing is a technique to help you stay calm and improve
- * concentration in stressful situations.
- *
- * Usage: Timed messages will cycle as long as this face is active.
- * Press ALARM to toggle sound.
- */
-
-#include "movement.h"
-
-void breathing_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void breathing_face_activate(void *context);
-bool breathing_face_loop(movement_event_t event, void *context);
-void breathing_face_resign(void *context);
-
-#define breathing_face ((const watch_face_t){ \
-    breathing_face_setup, \
-    breathing_face_activate, \
-    breathing_face_loop, \
-    breathing_face_resign, \
-    NULL, \
-})
-
-#endif // BREATHING_FACE_H_
diff --git a/movement/watch_faces/complication/butterfly_game_face.c b/movement/watch_faces/complication/butterfly_game_face.c
deleted file mode 100644 (file)
index b8f5434..0000000
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Hugo Chargois
- *
- * 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.
- */
-
-// Emulator only: need time() to seed the random number generator
-#if __EMSCRIPTEN__
-#include <time.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include "butterfly_game_face.h"
-
-static char butterfly_shapes[][3] = {
-    "[]", "][", "25", "52", "9e", "e9", "6a", "a6", "3E", "E3", "00", "HH", "88"
-};
-
-static int8_t single_beep[] = {BUZZER_NOTE_A7, 4, 0};
-static int8_t round_win_melody[] = {
-    BUZZER_NOTE_C6, 4,
-    BUZZER_NOTE_E6, 4,
-    BUZZER_NOTE_G6, 4,
-    BUZZER_NOTE_C7, 12,
-    0};
-static int8_t round_lose_melody[] = {
-    BUZZER_NOTE_E6, 4,
-    BUZZER_NOTE_F6, 4,
-    BUZZER_NOTE_D6SHARP_E6FLAT, 4,
-    BUZZER_NOTE_C6, 12,
-    0};
-static int8_t game_win_melody[] = {
-    BUZZER_NOTE_G6, 4,
-    BUZZER_NOTE_A6, 4,
-    BUZZER_NOTE_B6, 4,
-    BUZZER_NOTE_C7, 12,
-    BUZZER_NOTE_D7, 4,
-    BUZZER_NOTE_E7, 4,
-    BUZZER_NOTE_D7, 4,
-    BUZZER_NOTE_C7, 12,
-    BUZZER_NOTE_B6, 4,
-    BUZZER_NOTE_C7, 4,
-    BUZZER_NOTE_D7, 4,
-    BUZZER_NOTE_G7, 24,
-    0};
-
-#define NUM_SHAPES (sizeof(butterfly_shapes) / sizeof(butterfly_shapes[0]))
-
-#define POS_LEFT   4
-#define POS_CENTER 6
-#define POS_RIGHT  8
-
-#define TICK_FREQ 8
-#define TICKS_PER_SHAPE 8
-
-#define PLAYER_1 0
-#define PLAYER_2 1
-
-
-// returns a random integer r with 0 <= r < max
-static inline uint8_t _get_rand(uint8_t max) {
-#if __EMSCRIPTEN__
-    return rand() % max;
-#else
-    return arc4random_uniform(max);
-#endif
-}
-
-/*
- * The game is built with a simple state machine where each state is called a
- * "screen". Each screen can draw on the display and handles events, including
- * the "activate" event, which is repurposed and sent whenever we move from one
- * screen to another via the _transition_to function. Basically it's a mini
- * movement inside movement.
- */
-typedef bool (*screen_fn_t)(movement_event_t, butterfly_game_state_t*);
-
-static screen_fn_t cur_screen_fn;
-
-static bool _transition_to(screen_fn_t sf, butterfly_game_state_t *state) {
-    movement_event_t ev = {EVENT_ACTIVATE, 0};
-    cur_screen_fn = sf;
-    return sf(ev, state);
-}
-
-static uint8_t _pick_wrong_shape(butterfly_game_state_t *state, bool skip_wrong_shape) {
-    if (!skip_wrong_shape) {
-        // easy case, we only need to skip over 1 shape: the correct shape
-        uint8_t r = _get_rand(NUM_SHAPES-1);
-        if (r >= state->correct_shape) {
-            r++;
-        }
-        return r;
-    } else {
-        // a bit more complex, we need to skip over 2 shapes: the correct one
-        // and the current wrong one
-        uint8_t r = _get_rand(NUM_SHAPES-2);
-        uint8_t i1, i2; // the 2 indices to skip over, with i1 < i2
-        if (state->correct_shape < state->current_shape) {
-            i1 = state->correct_shape;
-            i2 = state->current_shape;
-        } else {
-            i1 = state->current_shape;
-            i2 = state->correct_shape;
-        }
-
-        if (r >= i1) {
-            r++;
-        }
-        if (r >= i2) {
-            r++;
-        }
-
-        return r;
-    }
-}
-
-static void _display_shape(uint8_t shape, uint8_t pos) {
-    watch_display_string(butterfly_shapes[shape], pos);
-}
-
-static void _display_scores(butterfly_game_state_t *state) {
-    char buf[] = " ";
-    buf[0] = '0' + state->score_p1;
-    watch_display_string(buf, 0);
-    buf[0] = '0' + state->score_p2;
-    watch_display_string(buf, 3);
-}
-
-static void _play_sound(butterfly_game_state_t *state, int8_t *seq) {
-    if (state->sound) watch_buzzer_play_sequence(seq, NULL);
-}
-
-static bool _round_start_screen(movement_event_t event, butterfly_game_state_t *state);
-static bool _reset_screen(movement_event_t event, butterfly_game_state_t *state);
-
-static bool _game_win_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            state->ctr = 4 * TICK_FREQ;
-            watch_clear_display();
-
-            if (state->score_p1 >= state->goal_score) {
-                watch_display_string("pl1  wins", 0);
-            } else {
-                watch_display_string("pl2  wins", 0);
-            }
-            _play_sound(state, game_win_melody);
-
-            break;
-        case EVENT_TICK:
-            state->ctr--;
-            if (state->ctr == 0) {
-                return _transition_to(_reset_screen, state);
-            }
-            break;
-    }
-    return true;
-}
-
-static bool _round_win_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            state->ctr = TICK_FREQ;
-
-            if (state->round_winner == PLAYER_1) {
-                state->score_p1++;
-            } else {
-                state->score_p2++;
-            }
-
-            watch_clear_display();
-            _display_scores(state);
-            _display_shape(state->correct_shape, state->round_winner == PLAYER_1 ? POS_LEFT : POS_RIGHT);
-            _play_sound(state, round_win_melody);
-            break;
-        case EVENT_TICK:
-            state->ctr--;
-            if (state->ctr == 0) {
-                if (state->score_p1 >= state->goal_score || state->score_p2 >= state->goal_score) {
-                    return _transition_to(_game_win_screen, state);
-                }
-                return _transition_to(_round_start_screen, state);
-            }
-            break;
-    }
-    return true;
-}
-
-static bool _round_lose_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            state->ctr = TICK_FREQ;
-
-            if (state->round_winner == PLAYER_1) {
-                if (state->score_p2 > 0) state->score_p2--;
-            } else {
-                if (state->score_p1 > 0) state->score_p1--;
-            }
-
-            _display_shape(state->correct_shape, POS_CENTER);
-            _play_sound(state, round_lose_melody);
-            break;
-        case EVENT_TICK:
-            if (--state->ctr == 0) {
-                return _transition_to(_round_start_screen, state);
-            }
-            _display_shape(state->ctr%2 ? state->correct_shape : state->current_shape, POS_CENTER);
-            break;
-
-    }
-    return true;
-}
-
-static bool _correct_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _display_shape(state->correct_shape, POS_CENTER);
-            _play_sound(state, single_beep);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            state->round_winner = PLAYER_1;
-            return _transition_to(_round_win_screen, state);
-        case EVENT_ALARM_BUTTON_DOWN:
-            state->round_winner = PLAYER_2;
-            return _transition_to(_round_win_screen, state);
-    }
-    return true;
-}
-
-static bool _wrong_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            state->ctr = TICKS_PER_SHAPE;
-            state->current_shape = _pick_wrong_shape(state, true);
-            _display_shape(state->current_shape, POS_CENTER);
-            _play_sound(state, single_beep);
-            break;
-        case EVENT_TICK:
-            if (--state->ctr == 0) {
-                if (--state->show_correct_shape_after == 0) {
-                    return _transition_to(_correct_shape_screen, state);
-                }
-                return _transition_to(_wrong_shape_screen, state);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            state->round_winner = PLAYER_2;
-            return _transition_to(_round_lose_screen, state);
-        case EVENT_ALARM_BUTTON_DOWN:
-            state->round_winner = PLAYER_1;
-            return _transition_to(_round_lose_screen, state);
-    }
-    return true;
-}
-
-static bool _first_wrong_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
-    // the first of the wrong shape screens is a bit different than the next
-    // ones, for 2 reasons:
-    // * we can pick any shape except one (the correct shape); whereas in the
-    //   subsequent wrong shape screens, we also must not pick the same wrong
-    //   shape as the last
-    // * we don't act on the light/alarm button events; they would normally be
-    //   a fail in a wrong shape screen, but in this case it may just be that
-    //   the 2 players acknowledge the picked shape (in the previous screen) in
-    //   quick succession, and we don't want the second player to immediately
-    //   fail.
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            state->ctr = TICKS_PER_SHAPE;
-            state->current_shape = _pick_wrong_shape(state, false);
-            _display_shape(state->current_shape, POS_CENTER);
-            _play_sound(state, single_beep);
-            break;
-        case EVENT_TICK:
-            if (--state->ctr == 0) {
-                return _transition_to(_wrong_shape_screen, state);
-            }
-            break;
-    }
-    return true;
-}
-
-static bool _round_start_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            state->correct_shape = _get_rand(NUM_SHAPES);
-            state->show_correct_shape_after = _get_rand(10) + 1;
-            watch_display_string("    -    -", 0);
-            _display_scores(state);
-            _display_shape(state->correct_shape, POS_CENTER);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-        case EVENT_ALARM_BUTTON_DOWN:
-            watch_display_string("      ", 4);
-            return _transition_to(_first_wrong_shape_screen, state);
-    }
-    return true;
-}
-
-static bool _goal_select_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_clear_display();
-            state->goal_score = 6;
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            return _transition_to(_round_start_screen, state);
-        case EVENT_ALARM_BUTTON_DOWN:
-            state->goal_score += 3;
-            if (state->goal_score > 9) state->goal_score = 3;
-            break;
-    }
-
-    char buf[] = "GOaL  ";
-    buf[5] = '0' + state->goal_score;
-    watch_display_string(buf, 4);
-    return true;
-}
-
-static bool _reset_screen(movement_event_t event, butterfly_game_state_t *state) {
-    (void) event;
-
-    state->score_p1 = 0;
-    state->score_p2 = 0;
-
-    return _transition_to(_goal_select_screen, state);
-}
-
-static bool _continue_select_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_clear_display();
-
-            // no game in progress, start a new game
-            if (state->score_p1 == 0 && state->score_p2 == 0) {
-                return _transition_to(_goal_select_screen, state);
-            }
-
-            state->cont = false;
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            if (state->cont) {
-                return _transition_to(_round_start_screen, state);
-            }
-            return _transition_to(_reset_screen, state);
-        case EVENT_ALARM_BUTTON_DOWN:
-            state->cont = !state->cont;
-            break;
-    }
-
-    if (state->cont) {
-        watch_display_string("Cont y", 4);
-    } else {
-        watch_display_string("Cont n", 4);
-    }
-    return true;
-}
-
-static bool _sound_select_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_clear_display();
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            return _transition_to(_continue_select_screen, state);
-        case EVENT_ALARM_BUTTON_DOWN:
-            state->sound = !state->sound;
-            break;
-    }
-
-    if (state->sound) {
-        watch_display_string("snd y", 5);
-    } else {
-        watch_display_string("snd n", 5);
-    }
-    return true;
-}
-
-static bool _splash_screen(movement_event_t event, butterfly_game_state_t *state) {
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            state->ctr = TICK_FREQ;
-
-            watch_clear_display();
-            watch_display_string("Btrfly", 4);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-        case EVENT_ALARM_BUTTON_DOWN:
-            return _transition_to(_sound_select_screen, state);
-        case EVENT_TICK:
-            if (--state->ctr == 0) {
-                return _transition_to(_sound_select_screen, state);
-            }
-            break;
-    }
-    return true;
-}
-
-void butterfly_game_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(butterfly_game_state_t));
-        memset(*context_ptr, 0, sizeof(butterfly_game_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-#if __EMSCRIPTEN__
-    // simulator only: seed the random number generator
-    time_t t;
-    srand((unsigned) time(&t));
-#endif
-}
-
-void butterfly_game_face_activate(void *context) {
-    (void) context;
-
-    movement_request_tick_frequency(TICK_FREQ);
-}
-
-bool butterfly_game_face_loop(movement_event_t event, void *context) {
-    butterfly_game_state_t *state = (butterfly_game_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            return _transition_to(_splash_screen, state);
-        case EVENT_TICK:
-        case EVENT_LIGHT_BUTTON_DOWN:
-        case EVENT_ALARM_BUTTON_DOWN:
-            return (*cur_screen_fn)(event, state);
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            return true;
-        default:
-            return movement_default_loop_handler(event);
-    }
-}
-
-void butterfly_game_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
-
diff --git a/movement/watch_faces/complication/butterfly_game_face.h b/movement/watch_faces/complication/butterfly_game_face.h
deleted file mode 100644 (file)
index 5922247..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Hugo Chargois
- *
- * 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 BUTTERFLY_GAME_FACE_H_
-#define BUTTERFLY_GAME_FACE_H_
-
-#include "movement.h"
-
-/*
- * BUTTERFLY
- *
- * A GAME OF SHAPE RECOGNITION AND QUICK REFLEXES FOR 2 PLAYERS
- *
- * Setup
- * =====
- * 
- * The game is played by 2 players, each using a distinct button:
- * - player 1 plays with the LIGHT (upper left) button
- * - player 2 plays with the ALARM (lower right) button
- * 
- * To play, both players need a firm grip on the watch. A suggested method is to
- * face each other, remove the watch from the wrist, and position it sideways
- * between you. Hold one side of the strap in your preferred hand (right or
- * left) and use your thumb to play.
- * 
- * Start of the game
- * =================
- * 
- * After the splash screen (BtrFly) is shown, the game proceeds through a couple
- * configuration screens. Use ALARM to cycle through the possible values, and
- * LIGHT to validate and move to the next screen.
- * 
- * The configuration options are:
- * 
- * - snd  y/n     Toggle sound effects on or off
- * - goal 3/6/9   Choose to play a game of 3, 6 or 9 points
- * - cont y/n     Decide to continue an unfinished game or start a new one
- *                (this option appears only if a game is in progress)
- * 
- * Rules
- * =====
- * 
- * Prior to each round, a symmetrical shape composed of 2 characters is shown in
- * the center of the screen. This shape, representing a butterfly's wings, is
- * randomly chosen from a set of a dozen or so possible shapes. For example:
- * 
- *     ][
- * 
- * Memorize this shape! Your objective in the round will be to "catch" this 
- * "butterfly" by pressing your button before your opponent does.
- * 
- * Once you believe you've memorized the shape, press your button. The round
- * officially begins as soon as either player presses their button.
- * 
- * Various "butterflies" will then appear on the screen, one after the other.
- * The fastest player to press their button when the correct butterfly is shown
- * wins the round. However, if a player presses their button when an incorrect
- * butterfly is shown, they immediately lose the round.
- * 
- * Scoring
- * =======
- * 
- * The scores are displayed at the top of the screen at all times.
- * 
- * When a round is won by a player, their score increases by one. When a round
- * is lost by a player, their score decreases by one; unless they have a score
- * of 0, in which case it remains unchanged.
- * 
- * The game ends when a player reaches the set point goal (3, 6 or 9 points).
- *
- */
-
-typedef struct {
-    bool cont : 1; // continue
-    bool sound : 1;
-    uint8_t goal_score : 4;
-    
-    // a generic ctr used by multiple states to display themselves for multiple frames
-    uint8_t ctr : 6;
-
-    uint8_t correct_shape : 5;
-    uint8_t current_shape : 5;
-    uint8_t show_correct_shape_after : 5;
-    uint8_t round_winner : 1;
-
-    uint8_t score_p1 : 5;
-    uint8_t score_p2 : 5;
-} butterfly_game_state_t;
-
-void butterfly_game_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void butterfly_game_face_activate(void *context);
-bool butterfly_game_face_loop(movement_event_t event, void *context);
-void butterfly_game_face_resign(void *context);
-
-#define butterfly_game_face ((const watch_face_t){ \
-    butterfly_game_face_setup, \
-    butterfly_game_face_activate, \
-    butterfly_game_face_loop, \
-    butterfly_game_face_resign, \
-    NULL, \
-})
-
-#endif // BUTTERFLY_GAME_FACE_H_
-
diff --git a/movement/watch_faces/complication/couch_to_5k_face.c b/movement/watch_faces/complication/couch_to_5k_face.c
deleted file mode 100644 (file)
index 37efa80..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "couch_to_5k_face.h"
-
-// They go: Warmup, Run, Walk, Run, Walk, Run, Walk ... , End (0)
-// Time is defined in seconds
-// Maybe do /10 to reduce memory usage?
-// (i don't want to use floats)
-
-// uint16_t C25K_WEEK_TEST[] = {10, 10, 10, 0};
-uint16_t C25K_WEEK_1[]   = {300, 60, 90, 60, 90, 60, 90, 60, 90, 60, 90, 60,
-    90, 60, 90, 60, 90, 0};
-uint16_t C25K_WEEK_2[]   = {300, 90, 120, 90, 120, 90, 120, 90, 120, 90, 120,
-    90, 120, 0};
-uint16_t C25K_WEEK_3[]   = {300, 90, 90, 180, 180, 90, 90, 180, 180, 0};
-uint16_t C25K_WEEK_4[]   = {300, 180, 90, 300, 150, 180, 90, 300, 0};
-uint16_t C25K_WEEK_5_1[] = {300, 300, 180, 300, 180, 300, 0 };
-uint16_t C25K_WEEK_5_2[] = {300, 480, 300, 480 , 0};
-uint16_t C25K_WEEK_5_3[] = {300, 1200, 0};
-uint16_t C25K_WEEK_6_1[] = {300, 300, 180, 480, 180, 300, 0 };
-uint16_t C25K_WEEK_6_2[] = {300, 600, 180, 600 , 0};
-uint16_t C25K_WEEK_6_3[] = {300, 1500, 0};
-uint16_t C25K_WEEK_7[]   = {300, 1500, 0};
-uint16_t C25K_WEEK_8[]   = {300, 1680, 0};
-uint16_t C25K_WEEK_9[]   = {300, 1800, 0};
-
-
-#define C25K_SESSIONS_LENGTH 3*9
-uint16_t *C25K_SESSIONS[C25K_SESSIONS_LENGTH];
-
-static inline bool _finished(couch_to_5k_state_t *state){
-    return state->exercise_type == C25K_FINISHED;
-}
-static inline bool _cleared(couch_to_5k_state_t *state){
-    return state->timer == C25K_SESSIONS[state->session][0]
-        && state->exercise == 0;
-}
-static inline void _next_session(couch_to_5k_state_t *state){
-    if (++state->session >= C25K_SESSIONS_LENGTH){
-        state->session = 0;
-    }
-}
-
-static inline void _assign_exercise_type(couch_to_5k_state_t *state){
-    if (state->exercise == 0){
-        state->exercise_type = C25K_WARMUP;
-    } else if (state->exercise % 2 == 1){
-        state->exercise_type = C25K_RUN;
-    } else {
-        state->exercise_type = C25K_WALK;
-    }
-}
-
-static void _next_exercise(couch_to_5k_state_t *state){
-    state->exercise++;
-    state->timer = C25K_SESSIONS[state->session][state->exercise];
-    // If the new timer starts in zero, it's finished
-    if (state->timer == 0){
-        movement_play_alarm_beeps(7, BUZZER_NOTE_C8);
-        state->exercise_type = C25K_FINISHED;
-        return;
-    }
-    movement_play_alarm_beeps(4, BUZZER_NOTE_A7);
-    _assign_exercise_type(state);
-}
-
-static void _init_session(couch_to_5k_state_t *state){
-    state->exercise = 0; // Restart exercise counter
-    state->timer = C25K_SESSIONS[state->session][state->exercise];
-    _assign_exercise_type(state);
-}
-
-static char *_exercise_type_to_str(exercise_type_t t){
-    switch (t){
-        case C25K_WARMUP:
-            return "WU";
-        case C25K_RUN:
-            return "RU";
-        case C25K_WALK:
-            return "WA";
-        case C25K_FINISHED:
-            return "--";
-        default:
-            return "  ";
-    }
-}
-static void _display(couch_to_5k_state_t *state, char *buf){
-    // TODO only repaint needed parts
-    uint8_t seconds = state->timer % 60;
-    sprintf(buf, "%s%2d%2d%02d%02d",
-            _exercise_type_to_str(state->exercise_type),
-            (state->session + 1) % 100,
-            ((state->timer - seconds) / 60) % 100,
-            seconds,
-            (state->exercise + 1) % 100);
-    watch_display_string(buf, 0);
-}
-
-
-void couch_to_5k_face_setup(uint8_t
-                          watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(couch_to_5k_state_t));
-        memset(*context_ptr, 0, sizeof(couch_to_5k_state_t));
-        // Do any one-time tasks in here; the inside of this conditional
-        // happens only at boot.
-        // C25K_SESSIONS[0]  = C25K_WEEK_TEST;
-        C25K_SESSIONS[0]  = C25K_WEEK_1;
-        C25K_SESSIONS[1]  = C25K_WEEK_1;
-        C25K_SESSIONS[2]  = C25K_WEEK_1;
-        C25K_SESSIONS[3]  = C25K_WEEK_2;
-        C25K_SESSIONS[4]  = C25K_WEEK_2;
-        C25K_SESSIONS[5]  = C25K_WEEK_2;
-        C25K_SESSIONS[6]  = C25K_WEEK_3;
-        C25K_SESSIONS[7]  = C25K_WEEK_3;
-        C25K_SESSIONS[8]  = C25K_WEEK_3;
-        C25K_SESSIONS[9]  = C25K_WEEK_4;
-        C25K_SESSIONS[10] = C25K_WEEK_4;
-        C25K_SESSIONS[11] = C25K_WEEK_4;
-        C25K_SESSIONS[12] = C25K_WEEK_5_1;
-        C25K_SESSIONS[13] = C25K_WEEK_5_2;
-        C25K_SESSIONS[14] = C25K_WEEK_5_3;
-        C25K_SESSIONS[15] = C25K_WEEK_6_1;
-        C25K_SESSIONS[16] = C25K_WEEK_6_2;
-        C25K_SESSIONS[17] = C25K_WEEK_6_3;
-        C25K_SESSIONS[18] = C25K_WEEK_7;
-        C25K_SESSIONS[19] = C25K_WEEK_7;
-        C25K_SESSIONS[20] = C25K_WEEK_7;
-        C25K_SESSIONS[21] = C25K_WEEK_8;
-        C25K_SESSIONS[22] = C25K_WEEK_8;
-        C25K_SESSIONS[23] = C25K_WEEK_8;
-        C25K_SESSIONS[24] = C25K_WEEK_9;
-        C25K_SESSIONS[25] = C25K_WEEK_9;
-        C25K_SESSIONS[26] = C25K_WEEK_9;
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the
-    // watch wakes from deep sleep.
-}
-
-void couch_to_5k_face_activate(void *context) {
-    (void) context;
-    // Handle any tasks related to your watch face coming on screen.
-    watch_set_colon();
-}
-
-
-bool couch_to_5k_face_loop(movement_event_t event,
-                         void *context) {
-    couch_to_5k_state_t *state = (couch_to_5k_state_t *)context;
-    static char buf[11];
-    static bool paused = true;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Show your initial UI here.
-            movement_request_tick_frequency(1);
-            _init_session(state);
-            paused = true;
-            _display(state, buf);
-            break;
-        case EVENT_TICK:
-            if ( !paused && !_finished(state) ) {
-                if (state->timer == 0){
-                    _next_exercise(state);
-                } else {
-                    state->timer--;
-                }
-            }
-            _display(state, buf);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            // This is the next-exercise / reset button.
-
-            // When finished move to the next session and leave it paused
-            if ( _finished(state) ){
-                _next_session(state);
-                _init_session(state);
-                paused = true;
-                break;
-            }
-            // When paused and cleared move to next, when only paused, clear
-            if ( paused ) {
-                if ( _cleared(state) ){
-                    _next_session(state);
-                }
-                _init_session(state);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if (movement_button_should_sound()) {
-                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-            }
-            paused = !paused;
-            break;
-        case EVENT_TIMEOUT:
-            // Your watch face will receive this event after a period of
-            // inactivity. If it makes sense to resign,
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // If you did not resign in EVENT_TIMEOUT, you can use this event
-            // to update the display once a minute. Avoid displaying
-            // fast-updating values like seconds, since the display won't
-            // update again for 60 seconds. You should also consider starting
-            // the tick animation, to show the wearer that this is sleep mode:
-            // watch_start_sleep_animation(500);
-            break;
-        default:
-            // Movement's default loop handler will step in for any cases you
-            // don't handle above:
-            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
-            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
-            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or
-            // skips to the secondary watch face, if configured)
-            // You can override any of these behaviors by adding a case for
-            // these events to this switch statement.
-            return movement_default_loop_handler(event);
-    }
-
-    // return true if the watch can enter standby mode. Generally speaking, you
-    // should always return true.
-    // Exceptions:
-    //  * If you are displaying a color using the low-level watch_set_led_color
-    //  function, you should return false.
-    //  * If you are sounding the buzzer using the low-level
-    //  watch_set_buzzer_on function, you should return false.
-    // Note that if you are driving the LED or buzzer using Movement functions
-    // like movement_illuminate_led or movement_play_alarm, you can still
-    // return true. This guidance only applies to the low-level watch_
-    // functions.
-    return true;
-}
-
-void couch_to_5k_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
-
diff --git a/movement/watch_faces/complication/couch_to_5k_face.h b/movement/watch_faces/complication/couch_to_5k_face.h
deleted file mode 100644 (file)
index 73d7b8f..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
- *
- * 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 COUCHTO5K_FACE_H_
-#define COUCHTO5K_FACE_H_
-
-#include "movement.h"
-
-/*
- * Couch To 5k;
- *
- *
- * The program is designed to train 3 times a week. Each training is a
- * *session*. Each of the rounds you have in the training is an *exercise*.
- *
- * The training goes like this:
- * 5min warm-up walk -> Run X minutes -> Walk Y minutes -> ... -> Stop
- *
- * The watch face shows it like this: The weekday indicator shows if you need
- * to Warm Up (`WU`), run (`rU`), walk (`WA`) or stop (`--`).
- *
- * The month-day indicator shows the session you are in (from 1 to 27).
- *
- * The timer shows the time you have left in the exercise and the exercise you
- * are doing (MM:SS:ee). When an exercise finishes you are notified with an
- * alarm. When the whole session finishes, a different tone is played for a
- * longer period.
- *
- * Pressing the ALARM button pauses/resumes the clock.
- *
- * Pressing the LIGHT button does nothing if the timer is not paused. When it
- * is paused it clears the current session (it restarts it to the beginning)
- * and if it was already cleared or the current session was finished moves to
- * the next session.
- */
-
-typedef enum {
-    C25K_WARMUP,
-    C25K_RUN,
-    C25K_WALK,
-    C25K_FINISHED
-} exercise_type_t;
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint8_t session;
-    uint8_t exercise;
-    exercise_type_t exercise_type;
-    uint16_t timer;
-} couch_to_5k_state_t;
-
-void couch_to_5k_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void couch_to_5k_face_activate(void *context);
-bool couch_to_5k_face_loop(movement_event_t event, void *context);
-void couch_to_5k_face_resign(void *context);
-
-#define couch_to_5k_face ((const watch_face_t){ \
-    couch_to_5k_face_setup, \
-    couch_to_5k_face_activate, \
-    couch_to_5k_face_loop, \
-    couch_to_5k_face_resign, \
-    NULL, \
-})
-
-#endif // COUCHTO5K_FACE_H_
-
diff --git a/movement/watch_faces/complication/counter_face.c b/movement/watch_faces/complication/counter_face.c
deleted file mode 100644 (file)
index 3542d78..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Shogo Okamoto
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "counter_face.h"
-#include "watch.h"
-
-void counter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(counter_state_t));
-        memset(*context_ptr, 0, sizeof(counter_state_t));
-        counter_state_t *state = (counter_state_t *)*context_ptr;
-        state->beep_on = true;
-    }
-}
-
-void counter_face_activate(void *context) {
-    counter_state_t *state = (counter_state_t *)context;
-    if (state->beep_on) {
-        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    }
-}
-
-bool counter_face_loop(movement_event_t event, void *context) {
-
-    counter_state_t *state = (counter_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_UP:
-            watch_buzzer_abort_sequence(); //abort running buzzer sequence when counting fast
-            state->counter_idx++; // increment counter index
-            if (state->counter_idx>99) { //0-99
-                state->counter_idx=0;//reset counter index
-            }
-            print_counter(state);
-            if (state->beep_on) {
-                beep_counter(state);
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            watch_buzzer_abort_sequence();
-            state->beep_on = !state->beep_on;
-            if (state->beep_on) {
-                watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-            } else {
-                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->counter_idx=0; // reset counter index
-            print_counter(state);
-            break;
-        case EVENT_ACTIVATE:
-            print_counter(state);
-            break;
-        case EVENT_TIMEOUT:
-            // ignore timeout
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-// beep counter index times
-void beep_counter(counter_state_t *state) {
-    int low_count = state->counter_idx/5;
-    int high_count = state->counter_idx - low_count * 5;
-    static int8_t sound_seq[15];
-    memset(sound_seq, 0, 15);
-    int i = 0;
-    if (low_count > 0) {
-        sound_seq[i] = BUZZER_NOTE_A6;
-        i++;
-        sound_seq[i] = 3;
-        i++;
-        sound_seq[i] = BUZZER_NOTE_REST;
-        i++;
-        sound_seq[i] = 6;
-        i++;
-        if (low_count > 1) {
-            sound_seq[i] = -2;
-            i++;
-            sound_seq[i] = low_count-1;
-            i++;
-        }
-        sound_seq[i] = BUZZER_NOTE_REST;
-        i++;
-        sound_seq[i] = 6;
-        i++;
-    }
-    if (high_count > 0) {
-        sound_seq[i] = BUZZER_NOTE_B6;
-        i++;
-        sound_seq[i] = 3;
-        i++;
-        sound_seq[i] = BUZZER_NOTE_REST;
-        i++;
-        sound_seq[i] = 6;
-        i++;
-    }
-    if (high_count > 1) {
-        sound_seq[i] = -2;
-        i++;
-        sound_seq[i] = high_count-1;
-    }
-    watch_buzzer_play_sequence((int8_t *)sound_seq, NULL);
-}
-
-
-// print counter index at the center of display.
-void print_counter(counter_state_t *state) {
-    char buf[14];
-    sprintf(buf, "CO    %02d", state->counter_idx); // center of LCD display
-    watch_display_string(buf, 0);
-}
-
-void counter_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/counter_face.h b/movement/watch_faces/complication/counter_face.h
deleted file mode 100644 (file)
index 2ec6812..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Shogo Okamoto
- *
- * 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 COUNTER_FACE_H_
-#define COUNTER_FACE_H_
-
-/*
- * COUNTER face
- *
- * Counter face is designed to count the number of running laps during exercises.
- *
- * Usage:
- * Short-press ALARM to increment the counter (loops at 99)
- * Long-press ALARM to reset the counter.
- * Long-press LIGHT to toggle sound.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint8_t counter_idx;
-    bool beep_on;
-} counter_state_t;
-
-
-void counter_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void counter_face_activate(void *context);
-bool counter_face_loop(movement_event_t event, void *context);
-void counter_face_resign(void *context);
-
-void print_counter(counter_state_t *state);
-void beep_counter(counter_state_t *state);
-
-#define counter_face ((const watch_face_t){ \
-    counter_face_setup, \
-    counter_face_activate, \
-    counter_face_loop, \
-    counter_face_resign, \
-    NULL, \
-})
-
-#endif // COUNTER_FACE_H_
diff --git a/movement/watch_faces/complication/databank_face.c b/movement/watch_faces/complication/databank_face.c
deleted file mode 100644 (file)
index 34cc4b4..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Mikhail Svarichevsky
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "databank_face.h"
-#include "watch.h"
-#include "watch_private_display.h"
-
-const char *pi_data[] = {
-    "PI", "314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442",
-    "S ", "9192631770",
-    "31", "2147483648",
-    "32", "4294967296",
-    "63", "9223372036854775808",
-    "64", "18446744073709551616",
-};
-//we show 6 characters per screen
-
-const int databank_num_pages = (sizeof(pi_data) / sizeof(char*) / 2);
-
-struct {
-    uint8_t current_word;
-    uint8_t databank_page;
-    bool animating;
-} databank_state;
-
-void databank_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    // These next two lines just silence the compiler warnings associated with unused parameters.
-    // We have no use for the settings or the watch_face_index, so we make that explicit here.
-    (void) context_ptr;
-    (void) watch_face_index;
-    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
-}
-
-void databank_face_activate(void *context) {
-    // same as above: silence the warning, we don't need to check the settings.
-    (void) context;
-    // we do however need to set some things in our context. Here we cast it to the correct type...
-    databank_state.current_word = 0;
-    databank_state.animating = true;
-}
-
-static void display()
-{
-    char buf[14];
-    int page = databank_state.current_word;
-    sprintf(buf, "%s%2d", pi_data[databank_state.databank_page * 2 + 0], page);
-    watch_display_string(buf, 0);
-    bool data_ended = false;
-    for (int i = 0; i < 6; i++) {
-        if (pi_data[databank_state.databank_page * 2 + 1][page * 6 + i] == 0) {
-            data_ended = true;
-        }
-
-        if (!data_ended) {
-            watch_display_character(pi_data[databank_state.databank_page * 2 + 1][page * 6 + i], 4 + i);
-        } else {
-            // only 6 digits per page
-            watch_display_character(' ', 4 + i);
-        }
-    }
-}
-
-bool databank_face_loop(movement_event_t event, void *context) {
-    (void) context;
-    int max_words = (strlen(pi_data[databank_state.databank_page * 2 + 1]) - 1) / 6 + 1;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-             display();
-        case EVENT_TICK:
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            databank_state.current_word = (databank_state.current_word + max_words - 1) % max_words;
-            display();
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            databank_state.databank_page = (databank_state.databank_page + databank_num_pages - 1) % databank_num_pages;
-            databank_state.current_word = 0;
-            display();
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            databank_state.databank_page = (databank_state.databank_page + 1) % databank_num_pages;
-            databank_state.current_word = 0;
-            display();
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            databank_state.current_word = (databank_state.current_word + 1) % max_words;
-            display();
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // This low energy mode update occurs once a minute, if the watch face is in the
-            // foreground when Movement enters low energy mode. We have the option of supporting
-            // this mode, but since our watch face animates once a second, the "Hello there" face
-            // isn't very useful in this mode. So we choose not to support it. (continued below)
-            break;
-        case EVENT_TIMEOUT:
-            // ... Instead, we respond to the timeout event. This event happens after a configurable
-            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
-            // resign control if we want to, and in this case, we do.
-            // This function will return the watch to the first screen (usually a simple clock),
-            // and it will do it long before the watch enters low energy mode. This ensures we
-            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
-            movement_move_to_face(0);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void databank_face_resign(void *context) {
-    // our watch face, like most watch faces, has nothing special to do when resigning.
-    // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/databank_face.h b/movement/watch_faces/complication/databank_face.h
deleted file mode 100644 (file)
index a9a4a32..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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 DATABANK_FACE_H_
-#define DATABANK_FACE_H_
-
-/*
- * DATABANK face
- *
- * Displays some pre-defined data that you might want to remember
- * Math constants, birthdays, phone numbers...
- *
- * Usage: Edit the global variable `pi_data` in "databank_face.c"
- * to the define the data that will be displayed. Each "item" contains
- * a two-letter label (using the day-of-week display), then a longer
- * string that will be displayed one "word" (six characters) at a time.
- *
- * Short-press ALARM to display the next word.
- * Short-press LIGHT to display the previous word.
- * Long-press ALARM to display the next item.
- * Long-press LIGHT to display the previous item.
- */
-
-#include "movement.h"
-
-void databank_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void databank_face_activate(void *context);
-bool databank_face_loop(movement_event_t event, void *context);
-void databank_face_resign(void *context);
-
-#define databank_face ((const watch_face_t){ \
-    databank_face_setup, \
-    databank_face_activate, \
-    databank_face_loop, \
-    databank_face_resign, \
-    NULL, \
-})
-
-#endif // DATABANK_FACE_H_
diff --git a/movement/watch_faces/complication/day_one_face.c b/movement/watch_faces/complication/day_one_face.c
deleted file mode 100644 (file)
index 2315233..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "day_one_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-static uint32_t _day_one_face_juliandaynum(uint16_t year, uint16_t month, uint16_t day) {
-    // from here: https://en.wikipedia.org/wiki/Julian_day#Julian_day_number_calculation
-    return (1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100))/4 + day - 32075;
-}
-
-static void _day_one_face_update(day_one_state_t *state) {
-    char buf[15];
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    uint32_t julian_date = _day_one_face_juliandaynum(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day);
-    uint32_t julian_birthdate = _day_one_face_juliandaynum(state->birth_year, state->birth_month, state->birth_day);
-    if (julian_date < julian_birthdate) {
-        sprintf(buf, "DA  %6lu", julian_birthdate - julian_date);
-    } else {
-        sprintf(buf, "DA  %6lu", julian_date - julian_birthdate);
-    }
-    watch_display_string(buf, 0);
-}
-
-static void _day_one_face_abort_quick_cycle(day_one_state_t *state) {
-    if (state->quick_cycle) {
-        state->quick_cycle = false;
-        movement_request_tick_frequency(4);
-    }
-}
-
-static void _day_one_face_increment(day_one_state_t *state) {
-    state->birthday_changed = true;
-    switch (state->current_page) {
-        case PAGE_YEAR:
-            state->birth_year = state->birth_year + 1;
-            if (state->birth_year > 2080) state->birth_year = 1900;
-            break;
-        case PAGE_MONTH:
-            state->birth_month = (state->birth_month % 12) + 1;
-            break;
-        case PAGE_DAY:
-            state->birth_day = (state->birth_day % watch_utility_days_in_month(state->birth_month, state->birth_year)) + 1;
-            break;
-        default:
-            break;
-    }
-}
-
-void day_one_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(day_one_state_t));
-        memset(*context_ptr, 0, sizeof(day_one_state_t));
-        movement_birthdate_t movement_birthdate = (movement_birthdate_t) watch_get_backup_data(2);
-        if (movement_birthdate.reg == 0) {
-            // if birth date is totally blank, set a reasonable starting date. this works well for anyone under 63, but
-            // you can keep pressing to go back to 1900; just pass the year 2080. also picked this date because if you
-            // set it to 1959-01-02, it counts up from the launch of Luna-1, the first spacecraft to leave the well.
-            movement_birthdate.bit.year = 1959;
-            movement_birthdate.bit.month = 1;
-            movement_birthdate.bit.day = 1;
-            watch_store_backup_data(movement_birthdate.reg, 2);
-        }
-    }
-}
-
-void day_one_face_activate(void *context) {
-    day_one_state_t *state = (day_one_state_t *)context;
-
-    state->current_page = PAGE_DISPLAY;
-    state->quick_cycle = false;
-    state->ticks = 0;
-
-    // fetch the user's birth date from the birthday register.
-    movement_birthdate_t movement_birthdate = (movement_birthdate_t) watch_get_backup_data(2);
-    state->birth_year = movement_birthdate.bit.year;
-    state->birth_month = movement_birthdate.bit.month;
-    state->birth_day = movement_birthdate.bit.day;
-}
-
-bool day_one_face_loop(movement_event_t event, void *context) {
-    day_one_state_t *state = (day_one_state_t *)context;
-
-    char buf[9];
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _day_one_face_update(state);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-        case EVENT_TICK:
-            if (state->quick_cycle) {
-                if (HAL_GPIO_BTN_ALARM_read()) {
-                    _day_one_face_increment(state);
-                } else {
-                    _day_one_face_abort_quick_cycle(state);
-                }
-            }
-            switch (state->current_page) {
-                // if in settings mode, update whatever the current page is
-                case PAGE_YEAR:
-                    watch_display_string("YR        ", 0);
-                    if (event.subsecond % 2) {
-                        sprintf(buf, "%4d", state->birth_year);
-                        watch_display_string(buf, 4);
-                    }
-                    break;
-                case PAGE_MONTH:
-                    watch_display_string("MO        ", 0);
-                    if (event.subsecond % 2) {
-                        sprintf(buf, "%2d", state->birth_month);
-                        watch_display_string(buf, 4);
-                    }
-                    break;
-                case PAGE_DAY:
-                    watch_display_string("DA        ", 0);
-                    if (event.subsecond % 2) {
-                        sprintf(buf, "%2d", state->birth_day);
-                        watch_display_string(buf, 6);
-                    }
-                    break;
-                // otherwise, check if we have to update. the display only needs to change at midnight!
-                case PAGE_DISPLAY: {
-                    watch_date_time_t date_time = watch_rtc_get_date_time();
-                    if (date_time.unit.hour == 0 &&  date_time.unit.minute == 0 && date_time.unit.second == 0) {
-                        _day_one_face_update(state);
-                    }
-                    break;}
-                case PAGE_DATE:
-                    if (state->ticks > 0) {
-                        state->ticks--;
-                    } else {
-                        state->current_page = PAGE_DISPLAY;
-                        _day_one_face_update(state);
-                    }
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // only illuminate if we're in display mode
-            switch (state->current_page) {
-                case PAGE_DISPLAY:
-                    // fall through
-                case PAGE_DATE:
-                    movement_illuminate_led();
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            // otherwise use the light button to advance settings pages.
-            switch (state->current_page) {
-                case PAGE_YEAR:
-                    // fall through
-                case PAGE_MONTH:
-                    // fall through
-                case PAGE_DAY:
-                    // go to next setting page...
-                    state->current_page = (state->current_page + 1) % 4;
-                    if (state->current_page == PAGE_DISPLAY) {
-                        // ...unless we've been pushed back to display mode.
-                        movement_request_tick_frequency(1);
-                        // force display since it normally won't update til midnight.
-                        _day_one_face_update(state);
-                    }
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // if we are on a settings page, increment whatever value we're setting.
-            switch (state->current_page) {
-                case PAGE_YEAR:
-                    // fall through
-                case PAGE_MONTH:
-                    // fall through
-                case PAGE_DAY:
-                    _day_one_face_abort_quick_cycle(state);
-                    _day_one_face_increment(state);
-                    break;
-                case PAGE_DISPLAY:
-                    state->current_page = PAGE_DATE;
-                    sprintf(buf, "%04d%02d%02d", state->birth_year % 10000, state->birth_month % 100, state->birth_day % 100);
-                    watch_display_string(buf, 2);
-                    state->ticks = 2;
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            // if we aren't already in settings mode, put us there.
-            switch (state->current_page) {
-                case PAGE_DISPLAY:
-                    state->current_page++;
-                    movement_request_tick_frequency(4);
-                    break;
-                case PAGE_YEAR:
-                    // fall through
-                case PAGE_MONTH:
-                    // fall through
-                case PAGE_DAY:
-                    state->quick_cycle = true;
-                    movement_request_tick_frequency(8);
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_ALARM_LONG_UP:
-            _day_one_face_abort_quick_cycle(state);
-            break;
-        case EVENT_TIMEOUT:
-            _day_one_face_abort_quick_cycle(state);
-            // return home if we're on a settings page (this saves our changes when we resign).
-            if (state->current_page != PAGE_DISPLAY) {
-                movement_move_to_face(0);
-            }
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void day_one_face_resign(void *context) {
-    day_one_state_t *state = (day_one_state_t *)context;
-
-    // if the user changed their birth date, store it to the birth date register
-    if (state->birthday_changed) {
-        day_one_state_t *state = (day_one_state_t *)context;
-        movement_birthdate_t movement_birthdate = (movement_birthdate_t) watch_get_backup_data(2);
-        movement_birthdate.bit.year = state->birth_year;
-        movement_birthdate.bit.month = state->birth_month;
-        movement_birthdate.bit.day = state->birth_day;
-        watch_store_backup_data(movement_birthdate.reg, 2);
-        state->birthday_changed = false;
-    }
-}
diff --git a/movement/watch_faces/complication/day_one_face.h b/movement/watch_faces/complication/day_one_face.h
deleted file mode 100644 (file)
index ab6a3ce..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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 DAY_ONE_FACE_H_
-#define DAY_ONE_FACE_H_
-
-/*
- * DAY ONE face
- *
- * This watch face displays the number of days since or until a given date.
- * It was originally designed to display the number of days you’ve been alive,
- * but technically it can count up from any date in the 20th century or the
- * 21st century, so far.
- *
- * Long press on the Alarm button to enter customization mode. The text “YR”
- * will appear, and will allow you to set the year starting from 1959. Press
- * Alarm repeatedly to advance the year. If your birthday is before 1959,
- * advance beyond the current year and it will wrap around to 1900.
- *
- * Once you have set the year, press Light to set the month (“MO”) and
- * day (“DA”), advancing the value by pressing Alarm repeatedly.
- *
- * Note that at this time, the Day One face does not display the sleep
- * indicator in sleep mode, which may make the watch appear to be
- * unresponsive in sleep mode. You can still press the Alarm button to
- * wake the watch. This UI quirk will be addressed in a future update.
- */
-
-#include "movement.h"
-
-typedef enum {
-    PAGE_DISPLAY,
-    PAGE_YEAR,
-    PAGE_MONTH,
-    PAGE_DAY,
-    PAGE_DATE
-} day_one_page_t;
-
-typedef struct {
-    day_one_page_t current_page;
-    uint16_t birth_year;
-    uint8_t birth_month;
-    uint8_t birth_day;
-    bool birthday_changed;
-    bool quick_cycle;
-    uint8_t ticks;
-} day_one_state_t;
-
-void day_one_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void day_one_face_activate(void *context);
-bool day_one_face_loop(movement_event_t event, void *context);
-void day_one_face_resign(void *context);
-
-#define day_one_face ((const watch_face_t){ \
-    day_one_face_setup, \
-    day_one_face_activate, \
-    day_one_face_loop, \
-    day_one_face_resign, \
-    NULL, \
-})
-
-#endif // DAY_ONE_FACE_H_
diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c
deleted file mode 100644 (file)
index 66d6378..0000000
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023-2024 Konrad Rieck
- *
- * 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.
- */
-
-/*
- * # Deadline Face
- * 
- * This is a watch face for tracking deadlines.  It draws inspiration from
- * other watch faces of the project but focuses on keeping track of
- * deadlines.  You can enter and monitor up to four different deadlines by
- * providing their respective date and time.  The face has two modes:
- * *running mode* and *settings mode*.
- *
- * ## Running Mode
- * 
- * When the watch face is activated, it defaults to running mode.  The top
- * right corner shows the current deadline number, and the main display
- * presents the time left until the deadline.  The format of the display
- * varies depending on the remaining time.
- *
- * - When less than a day is left, the display shows the remaining hours,
- *   minutes, and seconds in the form `HH:MM:SS`.
- * 
- * - When less than a month is left, the display shows the remaining days
- *   and hours in the form `DD:HH` with the unit `dy` for days.
- * 
- * - When less than a year is left, the display shows the remaining months
- *   and days in the form `MM:DD` with the unit `mo` for months.
- * 
- * - When more than a year is left, the years and months are displayed in
- *   the form `YY:MM` with the unit `yr` for years.
- * 
- * - When a deadline has passed in the last 24 hours, the display shows
- *   `over` to indicate that the deadline has just recently been reached.
- * 
- * - When no deadline is set for a particular slot, or if a deadline has
- *   already passed by more than 24 hours, `--:--` is displayed.
- * 
- * The user can navigate in running mode using the following buttons:
- * 
- * - The *alarm button* moves the next deadline.  There are currently four
- *   slots available for deadlines.  When the last slot has been reached,
- *   pressing the button moves to the first slot.
- *
- * - A *long press* on the *alarm button* activates settings mode and
- *   enables configuring the currently selected deadline.
- * 
- * - A *long press* on the *light button* activates a deadline alarm.  The 
- *   bell icon is displayed, and the alarm will ring upon reaching any of
- *   the deadlines set.  It is important to note that the watch will not 
- *   enter low-energy sleep mode while the alarm is enabled.
- *
- *
- * ## Settings Mode
- * 
- * In settings mode, the currently selected slot for a deadline can be
- * configured by providing the date and the time.  Like running mode, the
- * top right corner of the display indicates the current deadline number. 
- * The main display shows the date and, on the next page, the time to be
- * configured.
- *
- * The user can use the following buttons in settings mode.
- *
- * - The *light button* navigates through the different date and time
- *   settings, going from year, month, day, hour, to minute.  The selected
- *   position is blinking.
- *
- * - A *long press* on the light button resets the date and time to the next
- *   day at midnight.  This is the default deadline.
- *
- * - The *alarm button* increments the currently selected position.  A *long
- *   press* on the *alarm button* changes the value faster.
- *
- * - The *mode button* exists setting mode and returns to *running mode*. 
- *   Here the selected deadline slot can be changed.
- *
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "deadline_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-#define SETTINGS_NUM (5)
-const char settings_titles[SETTINGS_NUM][3] = { "YR", "MO", "DA", "HR", "M1" };
-
-/* Local functions */
-static void _running_init(deadline_state_t *state);
-static bool _running_loop(movement_event_t event, void *context);
-static void _running_display(movement_event_t event, deadline_state_t *state);
-static void _setting_init(deadline_state_t *state);
-static bool _setting_loop(movement_event_t event, void *context);
-static void _setting_display(movement_event_t event, deadline_state_t *state, watch_date_time_t date);
-
-/* Utility functions */
-static void _background_alarm_play(deadline_state_t *state);
-static void _background_alarm_schedule(deadline_state_t *state);
-static void _background_alarm_cancel(deadline_state_t *state);
-static void _increment_date(deadline_state_t *state, watch_date_time_t date_time);
-static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state);
-static inline bool _is_leap(int16_t y);
-static inline int _days_in_month(int16_t mpnth, int16_t y);
-static inline unsigned int _mod(int a, int b);
-static inline void _beep_button();
-static inline void _beep_enable();
-static inline void _beep_disable();
-static inline void _reset_deadline(deadline_state_t *state);
-
-/* Check for leap year */
-static inline bool _is_leap(int16_t y)
-{
-    y += 1900;
-    return !(y % 4) && ((y % 100) || !(y % 400));
-}
-
-/* Modulo function */
-static inline unsigned int _mod(int a, int b)
-{
-    int r = a % b;
-    return r < 0 ? r + b : r;
-}
-
-/* Return days in month */
-static inline int _days_in_month(int16_t month, int16_t year)
-{
-    uint8_t days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-    month = _mod(month - 1, 12);
-
-    if (month == 1 && _is_leap(year)) {
-        return days[month] + 1;
-    } else {
-        return days[month];
-    }
-}
-
-/* Beep for a button press*/
-static inline void _beep_button()
-{
-    // Play a beep as confirmation for a button press (if applicable)
-    if (!movement_button_should_sound())
-        return;
-
-    watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
-}
-
-/* Beep for entering settings */
-static inline void _beep_enable()
-{
-    if (!movement_button_should_sound())
-        return;
-
-    watch_buzzer_play_note(BUZZER_NOTE_G7, 50);
-    watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
-    watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
-}
-
-/* Beep for leaving settings */
-static inline void _beep_disable()
-{
-    if (!movement_button_should_sound())
-        return;
-
-    watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-    watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
-    watch_buzzer_play_note(BUZZER_NOTE_G7, 75);
-}
-
-/* Change tick frequency */
-static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state)
-{
-    if (state->tick_freq != freq) {
-        movement_request_tick_frequency(freq);
-        state->tick_freq = freq;
-    }
-}
-
-/* Determine index of closest deadline */
-static uint8_t _closest_deadline(deadline_state_t *state)
-{
-    watch_date_time_t now = watch_rtc_get_date_time();
-    uint32_t now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
-    uint32_t min_ts = UINT32_MAX;
-    uint8_t min_index = 0;
-
-    for (uint8_t i = 0; i < DEADLINE_FACE_DATES; i++) {
-        if (state->deadlines[i] < now_ts || state->deadlines[i] > min_ts)
-            continue;
-        min_ts = state->deadlines[i];
-        min_index = i;
-    }
-
-    return min_index;
-}
-
-/* Play background alarm */
-static void _background_alarm_play(deadline_state_t *state)
-{
-
-    /* Use the default alarm from movement and move to foreground */
-    if (state->alarm_enabled) {
-        movement_play_alarm();
-        movement_move_to_face(state->face_idx);
-    }
-}
-
-/* Schedule background alarm */
-static void _background_alarm_schedule(deadline_state_t *state)
-{
-    /* We simply re-use the scheduling in the background task */
-    deadline_face_advise(state);
-}
-
-/* Cancel background alarm */
-static void _background_alarm_cancel(deadline_state_t *state)
-{
-
-    movement_cancel_background_task_for_face(state->face_idx);
-}
-
-/* Reset deadline to tomorrow */
-static inline void _reset_deadline(deadline_state_t *state)
-{
-    /* Get current time and reset hours/minutes/seconds */
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    date_time.unit.second = 0;
-    date_time.unit.minute = 0;
-    date_time.unit.hour = 0;
-
-    /* Add 24 hours to obtain first second of tomorrow */
-    uint32_t ts = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
-    ts += 24 * 60 * 60;
-
-    state->deadlines[state->current_index] = ts;
-}
-
-/* Increment date in settings mode. Function taken from `set_time_face.c` */
-static void _increment_date(deadline_state_t *state, watch_date_time_t date_time)
-{
-    const uint8_t days_in_month[12] = { 31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31 };
-
-    switch (state->current_page) {
-        case 0:
-            /* Only 10 years covered. Fix this sometime next decade */
-            date_time.unit.year = ((date_time.unit.year % 10) + 1);
-            break;
-        case 1:
-            date_time.unit.month = (date_time.unit.month % 12) + 1;
-            break;
-        case 2:
-            date_time.unit.day = (date_time.unit.day % watch_utility_days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR)) + 1;
-            break;
-        case 3:
-            date_time.unit.hour = (date_time.unit.hour + 1) % 24;
-            break;
-        case 4:
-            date_time.unit.minute = (date_time.unit.minute + 1) % 60;
-            break;
-    }
-
-    uint32_t ts = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
-    state->deadlines[state->current_index] = ts;
-}
-
-/* Update display in running mode */
-static void _running_display(movement_event_t event, deadline_state_t *state)
-{
-    (void) event;
-
-    /* Seconds, minutes, hours, days, months, years */
-    int16_t unit[] = { 0, 0, 0, 0, 0, 0 };
-    uint8_t i, range[] = { 60, 60, 24, 30, 12, 0 };
-    char buf[16];
-
-    /* Display indicators */
-    if (state->alarm_enabled)
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    else
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-
-    watch_date_time_t now = watch_rtc_get_date_time();
-    uint32_t now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
-
-    /* Deadline expired */
-    if (state->deadlines[state->current_index] < now_ts) {
-        if (state->deadlines[state->current_index] + 24 * 60 * 60 > now_ts)
-            sprintf(buf, "DL%2dOVER  ", state->current_index + 1);
-        else
-            sprintf(buf, "DL%2d----  ", state->current_index + 1);
-
-        //watch_clear_indicator(WATCH_INDICATOR_BELL);
-        watch_display_string(buf, 0);
-        return;
-    }
-
-    /* Get date time structs */
-    watch_date_time_t deadline = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], movement_get_current_timezone_offset()
-        );
-
-    /* Calculate naive difference of dates */
-    unit[0] = deadline.unit.second - now.unit.second;
-    unit[1] = deadline.unit.minute - now.unit.minute;
-    unit[2] = deadline.unit.hour - now.unit.hour;
-    unit[3] = deadline.unit.day - now.unit.day;
-    unit[4] = deadline.unit.month - now.unit.month;
-    unit[5] = deadline.unit.year - now.unit.year;
-
-    /* Correct errors of naive difference */
-    for (i = 0; i < 6; i++) {
-        if (unit[i] < 0) {
-            /* Correct remaining units */
-            if (i == 3)
-                unit[i] += _days_in_month(deadline.unit.month - 1, deadline.unit.year);
-            else
-                unit[i] += range[i];
-
-            /* Carry over change to next unit if non-zero */
-            if (i < 5 && unit[i + 1] != 0)
-                unit[i + 1] -= 1;
-        }
-    }
-
-    /* Set range */
-    i = state->current_index + 1;
-    if (unit[5] > 0) {
-        /* years:months */
-        sprintf(buf, "DL%2d%02d%02dYR", i, unit[5] % 100, unit[4] % 12);
-    } else if (unit[4] > 0) {
-        /* months:days */
-        sprintf(buf, "DL%2d%02d%02dMO", i, (unit[5] * 12 + unit[4]) % 100, unit[3] % 32);
-    } else if (unit[3] > 0) {
-        /* days:hours */
-        sprintf(buf, "DL%2d%02d%02ddY", i, unit[3] % 32, unit[2] % 24);
-    } else {
-        /* hours:minutes:seconds */
-        sprintf(buf, "DL%2d%02d%02d%02d", i, unit[2] % 24, unit[1] % 60, unit[0] % 60);
-    }
-    watch_display_string(buf, 0);
-}
-
-/* Init running mode */
-static void _running_init(deadline_state_t *state)
-{
-    (void) state;
-
-    watch_clear_indicator(WATCH_INDICATOR_24H);
-    watch_clear_indicator(WATCH_INDICATOR_PM);
-    watch_set_colon();
-
-    /* Ensure 1Hz updates only */
-    _change_tick_freq(1, state);
-}
-
-/* Loop of running mode */
-static bool _running_loop(movement_event_t event, void *context)
-{
-    deadline_state_t *state = (deadline_state_t *) context;
-
-    if (event.event_type != EVENT_BACKGROUND_TASK)
-        _running_display(event, state);
-
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_UP:
-            _beep_button();
-            state->current_index = (state->current_index + 1) % DEADLINE_FACE_DATES;
-            _running_display(event, state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            _beep_enable();
-            _setting_init(state);
-            state->mode = DEADLINE_FACE_SETTING;
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            movement_move_to_next_face();
-            return false;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            _beep_button();
-            state->alarm_enabled = !state->alarm_enabled;
-            if (state->alarm_enabled) {
-                _background_alarm_schedule(settings, context);
-            } else {
-                _background_alarm_cancel(settings, context);
-            }
-            _running_display(event, state);
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            _background_alarm_play(state);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-/* Update display in settings mode */
-static void _setting_display(movement_event_t event, deadline_state_t *state, watch_date_time_t date_time)
-{
-    char buf[11];
-
-    int i = state->current_index + 1;
-    if (state->current_page > 2) {
-        watch_set_colon();
-        if (movement_clock_mode_24h()) {
-            watch_set_indicator(WATCH_INDICATOR_24H);
-            sprintf(buf, "%s%2d%2d%02d  ", settings_titles[state->current_page], i, date_time.unit.hour, date_time.unit.minute);
-        } else {
-            sprintf(buf, "%s%2d%2d%02d  ", settings_titles[state->current_page], i, (date_time.unit.hour % 12) ? (date_time.unit.hour % 12) : 12,
-                    date_time.unit.minute);
-            if (date_time.unit.hour < 12)
-                watch_clear_indicator(WATCH_INDICATOR_PM);
-            else
-                watch_set_indicator(WATCH_INDICATOR_PM);
-        }
-    } else {
-        watch_clear_colon();
-        watch_clear_indicator(WATCH_INDICATOR_24H);
-        watch_clear_indicator(WATCH_INDICATOR_PM);
-        sprintf(buf, "%s%2d%2d%02d%02d", settings_titles[state->current_page], i, date_time.unit.year + 20, date_time.unit.month, date_time.unit.day);
-    }
-
-    /* Blink up the parameter we are setting */
-    if (event.subsecond % 2) {
-        switch (state->current_page) {
-            case 0:
-            case 3:
-                buf[4] = buf[5] = ' ';
-                break;
-            case 1:
-            case 4:
-                buf[6] = buf[7] = ' ';
-                break;
-            case 2:
-                buf[8] = buf[9] = ' ';
-                break;
-        }
-    }
-
-    watch_display_string(buf, 0);
-}
-
-/* Init setting mode */
-static void _setting_init(deadline_state_t *state)
-{
-    state->current_page = 0;
-
-    /* Init fresh deadline to next day */
-    if (state->deadlines[state->current_index] == 0) {
-        _reset_deadline(state);
-    }
-
-    /* Ensure 1Hz updates only */
-    _change_tick_freq(1, state);
-}
-
-/* Loop of setting mode */
-static bool _setting_loop(movement_event_t event, void *context)
-{
-    deadline_state_t *state = (deadline_state_t *) context;
-    watch_date_time_t date_time;
-    date_time = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], movement_get_current_timezone_offset());
-
-    if (event.event_type != EVENT_BACKGROUND_TASK)
-        _setting_display(event, state, date_time);
-
-    switch (event.event_type) {
-        case EVENT_TICK:
-            if (state->tick_freq == 8) {
-                if (HAL_GPIO_BTN_ALARM_read()) {
-                    _increment_date(state, date_time);
-                    _setting_display(event, state, date_time);
-                } else {
-                    _change_tick_freq(4, state);
-                }
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            _change_tick_freq(8, state);
-            break;
-        case EVENT_ALARM_LONG_UP:
-            _change_tick_freq(4, state);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            _beep_button();
-            _reset_deadline(state);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            state->current_page = (state->current_page + 1) % SETTINGS_NUM;
-            _setting_display(event, state, date_time);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            _change_tick_freq(4, state);
-            _increment_date(state, date_time);
-            _setting_display(event, state, date_time);
-            break;
-        case EVENT_TIMEOUT:
-            _beep_button();
-            _background_alarm_schedule(settings, context);
-            _change_tick_freq(1, state);
-            state->mode = DEADLINE_FACE_RUNNING;
-            movement_move_to_face(0);
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            _beep_disable();
-            _background_alarm_schedule(settings, context);
-            _running_init(state);
-            _running_display(event, state);
-            state->mode = DEADLINE_FACE_RUNNING;
-            break;
-        case EVENT_BACKGROUND_TASK:
-            _background_alarm_play(state);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-/* Setup face */
-void deadline_face_setup(uint8_t watch_face_index, void **context_ptr)
-{
-    (void) watch_face_index;
-    if (*context_ptr != NULL)
-        return; /* Skip setup if context available */
-
-    /* Allocate state */
-    *context_ptr = malloc(sizeof(deadline_state_t));
-    memset(*context_ptr, 0, sizeof(deadline_state_t));
-
-    /* Store face index for background tasks */
-    deadline_state_t *state = (deadline_state_t *) *context_ptr;
-    state->face_idx = watch_face_index;
-}
-
-/* Activate face */
-void deadline_face_activate(void *context)
-{
-    deadline_state_t *state = (deadline_state_t *) context;
-
-    /* Set display options */
-    _running_init(state);
-    state->mode = DEADLINE_FACE_RUNNING;
-    state->current_index = _closest_deadline(state);
-}
-
-/* Loop face */
-bool deadline_face_loop(movement_event_t event, void *context)
-{
-    deadline_state_t *state = (deadline_state_t *) context;
-    switch (state->mode) {
-        case DEADLINE_FACE_SETTING:
-            _setting_loop(event, settings, context);
-            break;
-        default:
-        case DEADLINE_FACE_RUNNING:
-            _running_loop(event, settings, context);
-            break;
-    }
-
-    return true;
-}
-
-/* Resign face */
-void deadline_face_resign(void *context)
-{
-    (void) context;
-}
-
-/* Want background task */
-movement_watch_face_advisory_t deadline_face_advise(void *context)
-{
-    deadline_state_t *state = (deadline_state_t *) context;
-
-    if (!state->alarm_enabled)
-        return false;
-
-    /* Determine closest deadline */
-    watch_date_time_t now = watch_rtc_get_date_time();
-    uint32_t now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
-    uint32_t next_ts = state->deadlines[_closest_deadline(state)];
-
-    /* No active deadline */
-    if (next_ts < now_ts)
-        return false;
-
-    /* No deadline within next 60 seconds */
-    if (next_ts >= now_ts + 60)
-        return false;
-
-    /* Deadline within next minute. Let's set up an alarm */
-    watch_date_time_t next = watch_utility_date_time_from_unix_time(next_ts, movement_get_current_timezone_offset());
-    movement_request_wake();
-    movement_schedule_background_task_for_face(state->face_idx, next);
-    return false;
-}
diff --git a/movement/watch_faces/complication/deadline_face.h b/movement/watch_faces/complication/deadline_face.h
deleted file mode 100644 (file)
index 3cfca55..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023-2024 Konrad Rieck
- *
- * 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 DEADLINE_FACE_H_
-#define DEADLINE_FACE_H_
-
-#include "movement.h"
-
-/* Modes of face */
-typedef enum {
-    DEADLINE_FACE_RUNNING = 0,
-    DEADLINE_FACE_SETTING
-} deadline_mode_t;
-
-/* Number of deadline dates */
-#define DEADLINE_FACE_DATES (4)
-
-/* Deadline configuration */
-typedef struct {
-    deadline_mode_t mode:1;
-    uint8_t current_page:3;
-    uint8_t current_index:2;
-    uint8_t alarm_enabled:1;
-    uint8_t tick_freq;
-    uint8_t face_idx;
-    uint32_t deadlines[DEADLINE_FACE_DATES];
-} deadline_state_t;
-
-void deadline_face_setup(uint8_t watch_face_index, void **context_ptr);
-void deadline_face_activate(void *context);
-bool deadline_face_loop(movement_event_t event, void *context);
-void deadline_face_resign(void *context);
-movement_watch_face_advisory_t deadline_face_advise(void *context);
-
-#define deadline_face ((const watch_face_t){ \
-    deadline_face_setup, \
-    deadline_face_activate, \
-    deadline_face_loop, \
-    deadline_face_resign, \
-    deadline_face_advise \
-})
-
-#endif                          // DEADLINE_FACE_H_
diff --git a/movement/watch_faces/complication/discgolf_face.c b/movement/watch_faces/complication/discgolf_face.c
deleted file mode 100644 (file)
index 8d49992..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include "discgolf_face.h"
-#include "watch.h"          // Remember to change number of courses in this file
-#include "watch_utility.h"
-
-/* 
- * Keep track of scores in discgolf or golf!
- * The watch face operates in three different modes:
- *
- *  - dg_setting: Select a course
- *      Enter this mode by holding down the light button. The screen will display
- *      the label for the hole and the lowest score since last boot. 
- *      Press alarm to loop through the holes. Press the light button to make a 
- *      selection. This will reset all scores and start a new game in dg_idle mode.
- *
- *  -dg_idle: We're playing a hole
- *      This either shows your current score relative to par, or the score for a
- *      particular hole. 
- *      At the start of a game, press alarm to loop through the holes and leave it
- *      your starting hole. For optimal experience, play the course linearly after that
- *      If you're viewing the hole you're supposed to be playing, the watch face will
- *      display your score relative to par.
- *      Use the alarm button to view other holes than the one you're playing, in which
- *      case the input score for that hole will be displayed, in case it needs changing.
- *      Long press the alarm button to snap back to currently playing hole.
- *      To input scores for a hole in this mode, press the light button.
- *
- *  -dg_scoring: Input score for a hole
- *      In this mode, if the score is 0 (hasn't been entered during this round),
- *      it will blink, indicating we're in scoring mode. Press the alarm button
- *      to increment the score up until 15, in which case it loops back to 0. 
- *      Press the light button to save the score for that hole, advance one hole
- *      if you're not editing an already input score, and returning to idle mode.
- *
- *  When all scores have been entered, the LAP indicator turns on. At that point, if we enter 
- *  dg_setting to select a course, the score for that round is evaluated against the current 
- *  lowest score for that course, and saved if it is better.
-*/
-///////////////////////////////////////////////////////////////////////////////////////////////
-// Enter course data
-/* Initialize lowest scores with a high number */
-int8_t best[courses];
-
-static const uint8_t pars[][18] = {
-   { 3, 3, 4, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },    // Grafarholt
-   { 3, 4, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3 },    // Gufunes
-   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 },    // Vífilsstaðir
-   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 },    // Dalvegur
-   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 },    // Laugardalur
-   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 },    // Guðmundarlundur
-   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 },    // Víðistaðatún
-   { 3, 3, 3, 4, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 },    // Fossvogur
-   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 },    // Klambratún
-   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 },    // Seljahverfi
-   { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }     // Fella- og Hóla
-};
-static const uint8_t holes[] = {                                // Number of holes on each course
-    18,
-    18,
-    10,
-    10,
-    10,
-    10,
-    9,
-    9,
-    9,
-    9,
-    9
-};
-/* Two-letter descriptive labels, second field can only be A, B, C, D, E, F, H, I, J, L, N, O, R, T, U and X. */
-static const char labels[][2] = {
-    { 'G', 'H' },
-    { 'G', 'N' },
-    { 'V', 'I' },
-    { 'D', 'V' },
-    { 'L', 'A' },
-    { 'G', 'L' },
-    { 'V', 'T' },
-    { 'F', 'V' },
-    { 'K', 'T' },
-    { 'S', 'E' },
-    { 'F', 'H' }
-};
-
-// End of course data
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-/* Beep function */
-static inline void beep() {
-    if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
-}
-
-/* Prep for a new round */
-static inline void reset(discgolf_state_t *state) {
-    for (int i = 0; i < holes[state->course]; i++) {
-        state->scores[i] = 0;
-    }
-    state->hole = 1;
-    watch_clear_indicator(WATCH_INDICATOR_LAP);
-    return;
-}
-
-/* Total number of throws so far */
-static inline uint8_t score_sum(discgolf_state_t *state) {
-    uint8_t sum = 0;
-    for (int i = 0; i < holes[state->course]; i++) {
-        sum = sum + state->scores[i];
-    }
-    return sum;
-}
-
-/* Count how many holes have been played */
-static inline uint8_t count_played(discgolf_state_t *state) {
-    uint8_t holes_played = 0;
-    for (int i = 0; i < holes[state->course]; i++) {
-        if (state->scores[i] > 0) holes_played++;
-    }
-    return holes_played;
-}
-
-
-/* Calculate the current score relative to par */
-static inline int8_t calculate_score(discgolf_state_t *state) {
-    uint8_t par_sum = 0;
-    uint8_t score_sum = 0;
-    
-    for (int i = 0; i < holes[state->course]; i++) {
-        if (state->scores[i] > 0) {
-            par_sum = par_sum + pars[state->course][i];
-            score_sum = score_sum + state->scores[i];
-        }
-    }
-    return score_sum - par_sum;
-}
-
-/* Store score if it's the best so far */
-static inline void store_best(discgolf_state_t *state) {
-    uint8_t played = count_played(state);
-    if ( played == holes[state->course] ) {
-        int8_t high_score = calculate_score(state);
-        if (high_score < best[state->course] ) {
-            best[state->course] = high_score;
-        }
-    }
-}
-
-/* Configuration at boot, the high score array can be initialized with your high scores if they're known */
-void discgolf_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-       *context_ptr = malloc(sizeof(discgolf_state_t));
-       discgolf_state_t *state = (discgolf_state_t *)*context_ptr;
-       memset(*context_ptr, 0, sizeof(discgolf_state_t));
-       state->hole = 1;
-       state->course = 0;
-       state->playing = holes[state->course] + 1;
-       for (int i = 0; i < courses; i++) best[i] = 99;
-       state->mode = dg_setting;
-    }
-}
-
-void discgolf_face_activate(void *context) {
-    watch_clear_colon();
-    discgolf_state_t *state = (discgolf_state_t *)context;
-
-    /* If we were playing, go to current hole */
-    if (state->playing <= holes[0]) {
-        state->hole = state->playing;
-    }
-    /* Set LAP if round finished */
-    if (count_played(state) == holes[state->course] ) {
-        watch_set_indicator(WATCH_INDICATOR_LAP);
-    }
-    movement_request_tick_frequency(4);
-}
-
-bool discgolf_face_loop(movement_event_t event, void *context) {
-    
-    discgolf_state_t *state = (discgolf_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_TIMEOUT:
-            /* Snap to first screen if we're not playing*/
-            if ( count_played(state) == holes[state->course] || state->mode == dg_setting) {
-                movement_move_to_face(0);
-            }
-            /* Cancel scoring if timed out */
-            if (state->mode == dg_scoring) {
-                state->scores[state->hole] = 0;
-                state->mode = dg_idle;
-            }
-            break;
-        /* Advance if not scoring */
-        case EVENT_MODE_BUTTON_UP:
-            if ( state->mode != dg_scoring ) {
-                               movement_move_to_next_face();
-            }
-            break;
-        /* Go to default face if not scoring */
-        case EVENT_MODE_LONG_PRESS:
-            if ( state->mode != dg_scoring ) {
-                movement_move_to_face(0);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            switch ( state->mode ) {
-                case dg_idle:
-                    /* Check if selected hole is the first one */
-                    if ( score_sum(state) == 0 ) {
-                        state->playing = state->hole;
-                    }
-                    /* Enter scoring mode */
-                    state->mode = dg_scoring;
-                    break;
-                case dg_scoring:
-                    /* Set the LAP indicator if all scores are entered */
-                    if (count_played(state) == holes[state->course] ) {
-                        watch_set_indicator(WATCH_INDICATOR_LAP);
-                    }
-                    /* Advance to next hole if not editing previously set score */
-                    if ( state->hole == state->playing ) {
-                        if (state->hole < holes[state->course]) state->hole++;
-                        else state->hole = 1;
-                        if (state->playing < holes[state->course]) state->playing++;
-                        else state->playing = 1;
-                    }
-                    /* Return to idle */
-                    state->mode = dg_idle;
-                    break;
-                case dg_setting:
-                    /* Return to idle */
-                    state->playing = holes[state->course] + 1;
-                    state->mode = dg_idle;
-                    break;
-            }
-            beep();
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch (state->mode) {
-                /* Setting, loop through courses */
-                case dg_setting:
-                    state->course = (state->course + 1) % courses;
-                    break;
-                /* Scoring, increment score for current hole */
-                case dg_scoring:
-                    state->scores[state->hole - 1] = (state->scores[state->hole - 1] + 1) % 16;   // Loop around at 15
-                    break;
-                /* Idle, loop through holes */
-                case dg_idle:
-                    if (state->hole < holes[state->course]) {
-                        state->hole++;
-                    } else { state->hole = 1; }
-                    break;
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            /* Enter setting mode, reset state */
-            if ( state->mode == dg_idle ) {
-                state->mode = dg_setting;
-                store_best(state);
-                reset(state);
-                beep();
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            /* Snap back to currently playing hole if we've established one*/
-            if ( (state->mode == dg_idle) && (state->hole != state->playing) && (state->playing <= holes[state->course]) ) {
-                state->hole = state->playing;
-                beep();
-            }
-            break;
-        default:
-            break;
-    }
-
-    char buf[21];
-    char prefix;
-    int8_t diff;
-
-    switch (state->mode) {
-        /* Setting mode, display course label and high score */
-        case dg_setting:
-            if ( best[state->course] < 0 ) { 
-                prefix = '-';
-            } else { prefix = ' '; }
-            sprintf(buf, "%c%c   %c%2d  ", labels[state->course][0], labels[state->course][1], prefix, abs(best[state->course]));
-            break;
-        /* Idle, show relative or input score */
-        case dg_idle:
-            if (state->hole == state->playing) {
-                diff = calculate_score(state);
-                if ( diff < 0 ) {
-                    prefix = '-';
-                } else { prefix = ' '; }
-                sprintf(buf, "%c%c%2d %c%2d  ", labels[state->course][0], labels[state->course][1], state->hole, prefix, abs(diff));
-            } else {
-                sprintf(buf, "%c%c%2d  %2d  ", labels[state->course][0], labels[state->course][1], state->hole, state->scores[state->hole - 1]);
-            }
-            break;
-        /* Scoring, show set score */
-        case dg_scoring:
-            sprintf(buf, "%c%c%2d  %2d  ", labels[state->course][0], labels[state->course][1], state->hole, state->scores[state->hole - 1]);
-            break;
-    }
-
-    /* Blink during scoring */
-    if (event.subsecond % 2 && state->mode == dg_scoring) {
-        buf[6] = buf[7] = ' ';
-    }
-
-    /* Draw screen */
-    watch_display_string(buf, 0);
-
-    return true;
-}
-
-void discgolf_face_resign(void *context) {
-    (void) context;
-    watch_clear_indicator(WATCH_INDICATOR_LAP);
-}
diff --git a/movement/watch_faces/complication/discgolf_face.h b/movement/watch_faces/complication/discgolf_face.h
deleted file mode 100644 (file)
index bfdd532..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Þorsteinn Jón Gautason
- *
- * 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 DISCGOLF_FACE_H_
-#define DISCGOLF_FACE_H_
-
-/*
- * DISC GOLF face
- *
- * Keep track of scores in discgolf or golf!
- * The watch face operates in three different modes:
- *
- *  - dg_setting: Select a course
- *      Enter this mode by holding down the light button. The screen will display
- *      the label for the hole and the lowest score since last boot.
- *      Press alarm to loop through the holes. Press the light button to make a
- *      selection. This will reset all scores and start a new game in dg_idle mode.
- *
- *  -dg_idle: We're playing a hole
- *      This either shows your current score relative to par, or the score for a
- *      particular hole.
- *      At the start of a game, press alarm to loop through the holes and leave it
- *      your starting hole. For optimal experience, play the course linearly after that
- *      If you're viewing the hole you're supposed to be playing, the watch face will
- *      display your score relative to par.
- *      Use the alarm button to view other holes than the one you're playing, in which
- *      case the input score for that hole will be displayed, in case it needs changing.
- *      Long press the alarm button to snap back to currently playing hole.
- *      To input scores for a hole in this mode, press the light button.
- *
- *  -dg_scoring: Input score for a hole
- *      In this mode, if the score is 0 (hasn't been entered during this round),
- *      it will blink, indicating we're in scoring mode. Press the alarm button
- *      to increment the score up until 15, in which case it loops back to 0.
- *      Press the light button to save the score for that hole, advance one hole
- *      if you're not editing an already input score, and returning to idle mode.
- *
- *  When all scores have been entered, the LAP indicator turns on. At that point, if we enter
- *  dg_setting to select a course, the score for that round is evaluated against the current
- *  lowest score for that course, and saved if it is better.
-*/
-
-#include "movement.h"
-#define courses 11
-
-typedef enum {
-  dg_setting,                   // We are selecting a course
-  dg_scoring,                   // We are inputting our score
-  dg_idle,                      // We have input our score and are playing a hole
-} discgolf_mode_t;
-
-typedef struct {
-    uint8_t course;             // Index for course selection, from 0
-    uint8_t hole;               // Index for current hole, from 1
-    uint8_t playing;            // Current hole
-    int scores[18];             // Scores for each played hole
-    discgolf_mode_t mode;       // Watch face mode
-} discgolf_state_t;
-
-void discgolf_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void discgolf_face_activate(void *context);
-bool discgolf_face_loop(movement_event_t event, void *context);
-void discgolf_face_resign(void *context);
-
-#define discgolf_face ((const watch_face_t){ \
-    discgolf_face_setup, \
-    discgolf_face_activate, \
-    discgolf_face_loop, \
-    discgolf_face_resign, \
-    NULL, \
-})
-
-#endif // DISCGOLF_FACE_H_
diff --git a/movement/watch_faces/complication/dual_timer_face.c b/movement/watch_faces/complication/dual_timer_face.c
deleted file mode 100644 (file)
index e819d59..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- * Copyright (c) 2022 Andreas Nebinger
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "dual_timer_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "watch_rtc.h"
-
-/*
- * IMPORTANT: This watch face uses the same TC2 callback counter as the Stock Stopwatch
- * watch-face. It works through calling a global handler function. The two watch-faces
- * therefore can't coexist within the same firmware. If you want to compile this watch-face
- * then you need to remove the line <../watch_faces/complication/fast_stopwatch_face.c \>
- * from the Makefile.
- */
-
-// FROM fast_stopwatch_face.c ////////////////////////////////////////////////
-// Copyright (c) 2022 Andreas Nebinger
-
-#if __EMSCRIPTEN__
-#include <emscripten.h>
-#include <emscripten/html5.h>
-#else
-#include "../../../watch-library/hardware/include/saml22j18a.h"
-#include "../../../watch-library/hardware/include/component/tc.h"
-#include "../../../watch-library/hardware/hri/hri_tc_l22.h"
-#endif
-
-static const watch_date_time_t distant_future = {.unit = {0, 0, 0, 1, 1, 63}};
-static bool _is_running;
-static uint32_t _ticks;
-
-#if __EMSCRIPTEN__
-
-static long _em_interval_id = 0;
-
-void em_dual_timer_cb_handler(void *userData) {
-    // interrupt handler for emscripten 128 Hz callbacks
-    (void) userData;
-    _ticks++;
-}
-
-static void _dual_timer_cb_initialize() { }
-
-static inline void _dual_timer_cb_stop() {
-    emscripten_clear_interval(_em_interval_id);
-    _em_interval_id = 0;
-    _is_running = false;
-}
-
-static inline void _dual_timer_cb_start() {
-    // initiate 128 hz callback
-    _em_interval_id = emscripten_set_interval(em_dual_timer_cb_handler, (double)(1000/128), (void *)NULL);
-}
-
-#else
-
-static inline void _dual_timer_cb_start() {
-    // start the TC2 timer
-    hri_tc_set_CTRLA_ENABLE_bit(TC2);
-    _is_running = true;
-}
-
-static inline void _dual_timer_cb_stop() {
-    // stop the TC2 timer
-    hri_tc_clear_CTRLA_ENABLE_bit(TC2);
-    _is_running = false;
-}
-
-static void _dual_timer_cb_initialize() {
-    // setup and initialize TC2 for a 64 Hz interrupt
-    hri_mclk_set_APBCMASK_TC2_bit(MCLK);
-    hri_gclk_write_PCHCTRL_reg(GCLK, TC2_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN);
-    _dual_timer_cb_stop();
-    hri_tc_write_CTRLA_reg(TC2, TC_CTRLA_SWRST);
-    hri_tc_wait_for_sync(TC2, TC_SYNCBUSY_SWRST);
-    hri_tc_write_CTRLA_reg(TC2, TC_CTRLA_PRESCALER_DIV64 | // 32 Khz divided by 64 divided by 4 results in a 128 Hz interrupt
-                           TC_CTRLA_MODE_COUNT8 | 
-                           TC_CTRLA_RUNSTDBY);
-    hri_tccount8_write_PER_reg(TC2, 3);
-    hri_tc_set_INTEN_OVF_bit(TC2);
-    NVIC_ClearPendingIRQ(TC2_IRQn);
-    NVIC_EnableIRQ (TC2_IRQn);
-}
-
-// you need to take fast_stopwatch.c out of the Makefile or this will create a conflict
-// you have to choose between one of the stopwatches
- void TC2_Handler(void) {
-    // interrupt handler for TC2 (globally!)
-    _ticks++;
-    TC2->COUNT8.INTFLAG.reg |= TC_INTFLAG_OVF;
-}
-
-#endif
-
-// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
-
-/** @brief converts tick counts to duration struct for time display 
- */
-static dual_timer_duration_t ticks_to_duration(uint32_t ticks) {
-    dual_timer_duration_t duration;
-    uint8_t hours = 0;
-    uint8_t days = 0;
-
-    // count hours and days
-    while (ticks >= (128 * 60 * 60)) {
-        ticks -= (128 * 60 * 60);
-        hours++;
-        if (hours >= 24) {
-            hours -= 24;
-            days++;
-        }
-    }
-
-    // convert minutes, seconds, centiseconds
-    duration.centiseconds = (ticks & 0x7F) * 100 / 128;
-    duration.seconds = (ticks >> 7) % 60;
-    duration.minutes = (ticks >> 7) / 60;
-    duration.hours = hours;
-    duration.days = days;
-
-    return duration;
-}
-
-/** @brief starts one of the dual timers
- *  @details Starts a dual timer. If no previous timer is running it starts the global
- *  tick counter. If a previous timer is already running it registers the current tick. 
- */
-static void start_timer(dual_timer_state_t *state, bool timer) {
-    // if it is not running yet, run it
-    if ( !_is_running ) {
-        _is_running = true;
-        movement_request_tick_frequency(16);
-        state->start_ticks[timer] = 0;
-        state->stop_ticks[timer] = 0;
-        _ticks = 0;
-        _dual_timer_cb_start();
-        movement_schedule_background_task(distant_future);
-    } else {
-        // if another timer is already running save the current tick
-        state->start_ticks[timer] = _ticks;
-        state->stop_ticks[timer] = _ticks;
-    }
-    state->running[timer] = true;
-}
-
-/** @brief stops one of the dual timers
- *  @details Stops a dual timer. If no other timer is running it stops the global
- *  tick counter. If another timer is already running it registers the current stop tick. 
- */
-static void stop_timer(dual_timer_state_t *state, bool timer) {
-    // stop timer and save duration
-    state->stop_ticks[timer] = _ticks;
-    state->duration[timer] = ticks_to_duration(state->stop_ticks[timer] - state->start_ticks[timer]);
-    state->running[timer] = false;
-    // if the other timer is not running, stop callback
-    if ( state->running[!timer] == false ) {
-        _is_running = false;
-        _dual_timer_cb_stop();
-        movement_request_tick_frequency(1);
-        movement_cancel_background_task();
-    }
-}
-
-/** @brief displays the measured time for each of the dual timers
- *  @details displays the dual timer. Below 1 hour it displays the timed minutes, seconds,
- *  and centiseconds. Above that it shows the timed hours, minutes, and seconds. If it 
- *  has run for more than a day it shows the days, hours, and minutes.
- *  When the timer is running, the colon blinks every half second.
- *  It also indicates at the top if another counter is running and for how long.
- */
-static void dual_timer_display(dual_timer_state_t *state) {
-    char buf[11];
-    char oi[3];
-    // get the current time count of the selected counter
-    dual_timer_duration_t timer = state->running[state->show] ? ticks_to_duration(state->stop_ticks[state->show] - state->start_ticks[state->show]) : state->duration[state->show];
-    // get the current time count of the other counter
-    dual_timer_duration_t other = ticks_to_duration(state->stop_ticks[!state->show] - state->start_ticks[!state->show]);
-    
-    if ( timer.days > 0 )
-        sprintf(buf, "%02u%02u%02u", timer.days, timer.hours, timer.minutes);
-    else if ( timer.hours > 0 )
-        sprintf(buf, "%02u%02u%02u", timer.hours, timer.minutes, timer.seconds);
-    else
-        sprintf(buf, "%02u%02u%02u", timer.minutes, timer.seconds, timer.centiseconds);
-    watch_display_string(buf, 4);
-    
-    // which counter is displayed
-    watch_display_string(state->show ? "B" : "A", 0);
-    
-    // indicate whether other counter is running
-    watch_display_string(state->running[!state->show] && (_ticks % 100) < 50 ? "+" : " ", 1);
-    
-    // indicate for how long the other counter has been running
-    sprintf(oi, "%2u", other.days > 0 ? other.days : (other.hours > 0 ? other.hours : (other.minutes > 0 ? other.minutes : (other.seconds > 0 ? other.seconds : other.centiseconds))));
-    watch_display_string( (state->stop_ticks[!state->show] - state->start_ticks[!state->show]) > 0 ? oi : "  ", 2);
-    
-    // blink colon when running
-    if ( timer.centiseconds > 50 || !state->running[state->show] ) watch_set_colon();
-    else watch_clear_colon();
-}
-
-// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
-
-void dual_timer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(dual_timer_state_t));
-        memset(*context_ptr, 0, sizeof(dual_timer_state_t));
-        _ticks = 0;
-    }
-    if (!_is_running) {
-        _dual_timer_cb_initialize();
-    }
-}
-
-void dual_timer_face_activate(void *context) {
-    (void) context;
-    if (_is_running) {
-        movement_schedule_background_task(distant_future);
-    }
-}
-
-bool dual_timer_face_loop(movement_event_t event, void *context) {
-    dual_timer_state_t *state = (dual_timer_state_t *)context;
-
-    // timers stop at 99:23:59:59:99
-    if ( (_ticks - state->start_ticks[0]) >= 1105919999 )
-        stop_timer(state, 0);
-
-    if ( (_ticks - state->start_ticks[1]) >= 1105919999 )
-        stop_timer(state, 1);
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_set_colon();
-            if (_is_running) {
-                movement_request_tick_frequency(16);
-                if ( state->running[0] )
-                    state->show = 0;
-                else state->show = 1;
-            } else {
-                if (state->stop_ticks[0] > 0 || state->stop_ticks[1] > 0)
-                    dual_timer_display(state);
-                else watch_display_string("A   000000", 0);
-            }
-            break;
-        case EVENT_TICK:
-            if ( _is_running ) {
-                // update stop ticks
-                if ( state->running[0] )
-                    state->stop_ticks[0] = _ticks;
-                if ( state->running[1] )
-                    state->stop_ticks[1] = _ticks;
-                dual_timer_display(state);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // start/stop timer B
-            state->running[1] = !state->running[1];
-            if ( state->running[1] ) {
-                start_timer(state, 1);
-            } else {
-                stop_timer(state, 1);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-            // start/stop timer A
-            state->running[0] = !state->running[0];
-            if ( state->running[0] ) {
-                start_timer(state, 0);
-            } else {
-                stop_timer(state, 0);
-            }
-            break;
-        case EVENT_MODE_BUTTON_DOWN:
-            // switch between the timers
-            state->show = !state->show;
-            dual_timer_display(state);
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            // don't switch to next face...
-            break;
-        case EVENT_MODE_LONG_PRESS:
-            // ...but do it on long press MODE!
-            movement_move_to_next_face();
-            break;
-        case EVENT_TIMEOUT:
-            // go back to 
-            if (!_is_running) movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            dual_timer_display(state);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void dual_timer_face_resign(void *context) {
-    (void) context;
-    movement_cancel_background_task();
-    // handle any cleanup before your watch face goes off-screen.
-}
-
diff --git a/movement/watch_faces/complication/dual_timer_face.h b/movement/watch_faces/complication/dual_timer_face.h
deleted file mode 100644 (file)
index ea8d3c4..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- * Copyright (c) 2022 Andreas Nebinger
- *
- * 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 DUAL_TIMER_FACE_H_
-#define DUAL_TIMER_FACE_H_
-
-/*
- * DUAL TIMER
- * ==========
- * 
- * Inspired by special ops and tactical trope targeted watches like the Nixon Regulus
- * that feature two chronographs for timing two simultaneous events, here is a watch
- * face that expands upon Andreas Nebinger's Stock Stopwatch Face code to implement this 
- * functionality.
- * 
- * ALARM starts/stops timer A, resets on the next start
- * LIGHT starts/stops timer B, resets on the next start
- * 
- * When a timer is running, tapping MODE toggles between displaying timers A or B, as
- * indicated at the top of the display.
- * 
- * The currently selected timer shows minutes, seconds, and 100ths of seconds until the
- * timed event passes the hour mark. Then it shows hours, minutes, and seconds. Once
- * it runs for more than a day it shows days, hours, and minutes. The blinking colon
- * indicates that the timer is running.
- * 
- * The longest time span the timers are able to track as 99 days and 23:59:59:99 hours and
- * they will simply stop.
- * 
- * If the other timer that is not currently selected is also running then a plus sign is
- * blinking next to the A or B indicator. Its progress is indicated by showing the timer's
- * currently highest time unit ( 100ths of seconds if less than a second, seconds if less
- * than a minute, minutes if less than an hour, hours if less than a day, days if more than
- * a day).
- * 
- * Please Note: If at least one timer is running then the default function of the MODE
- * button to move to the next watch face is disabled to be able to use it to toggle between
- * the timers. In this case LONG PRESSING MODE will move to the next face instead of moving
- * back to the default watch face.
- *
- * IMPORTANT: This watch face uses the same TC2 callback counter as the Stock Stopwatch
- * watch-face. It works through calling a global handler function. The two watch-faces
- * therefore can't coexist within the same firmware. If you want to compile this watch-face
- * then you need to remove the line <../watch_faces/complication/fast_stopwatch_face.c \>
- * from the Makefile.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint8_t centiseconds : 7;  // 0-59
-    uint8_t seconds : 6;  // 0-59
-    uint8_t minutes : 6;  // 0-59
-    uint8_t hours : 5;    // 0-23
-    uint8_t days : 7;    // 0-99
-} dual_timer_duration_t;
-
-typedef struct {
-    uint32_t start_ticks[2];
-    uint32_t stop_ticks[2];
-    dual_timer_duration_t duration[2];
-    bool running[2];
-    bool show;
-} dual_timer_state_t;
-
-void dual_timer_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void dual_timer_face_activate(void *context);
-bool dual_timer_face_loop(movement_event_t event, void *context);
-void dual_timer_face_resign(void *context);
-
-#if __EMSCRIPTEN__
-void em_dual_timer_cb_handler(void *userData);
-#else
-void TC2_Handler(void);
-#endif
-
-#define dual_timer_face ((const watch_face_t){ \
-    dual_timer_face_setup, \
-    dual_timer_face_activate, \
-    dual_timer_face_loop, \
-    dual_timer_face_resign, \
-    NULL, \
-})
-
-#endif // DUAL_TIMER_FACE_H_
-
diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c
deleted file mode 100644 (file)
index 1a17329..0000000
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 <David Volovskiy>
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "endless_runner_face.h"
-
-typedef enum {
-    JUMPING_FINAL_FRAME = 0,
-    NOT_JUMPING,
-    JUMPING_START,
-} RunnerJumpState;
-
-typedef enum {
-    SCREEN_TITLE = 0,
-    SCREEN_PLAYING,
-    SCREEN_LOSE,
-    SCREEN_TIME,
-    SCREEN_COUNT
-} RunnerCurrScreen;
-
-typedef enum {
-    DIFF_BABY = 0,  // FREQ_SLOW FPS;       MIN_ZEROES 0's min;  Jump is JUMP_FRAMES_EASY frames
-    DIFF_EASY,      //      FREQ FPS;       MIN_ZEROES 0's min;  Jump is JUMP_FRAMES_EASY frames
-    DIFF_NORM,      //      FREQ FPS;       MIN_ZEROES 0's min;  Jump is JUMP_FRAMES frames
-    DIFF_HARD,      //      FREQ FPS;  MIN_ZEROES_HARD 0's min;  jump is JUMP_FRAMES frames
-    DIFF_FUEL,      // Mode where the top-right displays the amoount of fuel that you can be above the ground for, dodging obstacles. When on the ground, your fuel recharges.
-    DIFF_FUEL_1,    // Same as DIFF_FUEL, but if your fuel is 0, then you won't recharge
-    DIFF_COUNT
-} RunnerDifficulty;
-
-#define NUM_GRID 12  // This the length that the obstacle track can be on
-#define FREQ 8  // Frequency request for the game
-#define FREQ_SLOW 4  // Frequency request for baby mode
-#define JUMP_FRAMES 2  // Wait this many frames on difficulties above EASY before coming down from the jump button pressed
-#define JUMP_FRAMES_EASY 3 // Wait this many frames on difficulties at or below EASY before coming down from the jump button pressed
-#define MIN_ZEROES 4  // At minimum, we'll have this many spaces between obstacles
-#define MIN_ZEROES_HARD 3 // At minimum, we'll have this many spaces between obstacles on hard mode
-#define MAX_HI_SCORE 9999  // Max hi score to store and display on the title screen.
-#define MAX_DISP_SCORE 39  // The top-right digits can't properly display above 39
-#define JUMP_FRAMES_FUEL 30  // The max fuel that fuel that the fuel mode game will hold
-#define JUMP_FRAMES_FUEL_RECHARGE 3 // How much fuel each frame on the ground adds
-#define MAX_DISP_SCORE_FUEL 9  // Since the fuel mode displays the score in the weekday slot, two digits will display wonky data
-
-typedef struct {
-    uint32_t obst_pattern;
-    uint16_t obst_indx : 8;
-    uint16_t jump_state : 5;
-    uint16_t sec_before_moves : 3;
-    uint16_t curr_score : 10;
-    uint16_t curr_screen : 4;
-    bool loc_2_on;
-    bool loc_3_on;
-    bool success_jump;
-    bool fuel_mode;
-    uint8_t fuel;
-} game_state_t;
-
-static game_state_t game_state;
-static const uint8_t _num_bits_obst_pattern = sizeof(game_state.obst_pattern) * 8;
-
-static void print_binary(uint32_t value, int bits) {
-#if __EMSCRIPTEN__
-    for (int i = bits - 1; i >= 0; i--) {
-        // Print each bit
-        printf("%lu", (value >> i) & 1);
-        // Optional: add a space every 4 bits for readability
-        if (i % 4 == 0 && i != 0) {
-            printf(" ");
-        }
-    }
-    printf("\r\n");
-#else
-    (void) value;
-    (void) bits;
-#endif
-    return;
-}
-
-static uint32_t get_random(uint32_t max) {
-    #if __EMSCRIPTEN__
-    return rand() % max;
-    #else
-    return arc4random_uniform(max);
-    #endif
-}
-
-static uint32_t get_random_nonzero(uint32_t max) {
-    uint32_t random;
-    do
-    {
-        random = get_random(max);
-    } while (random == 0);
-    return random;
-}
-
-static uint32_t get_random_kinda_nonzero(uint32_t max) {
-    // Returns a number that's between 1 and max, unless max is 0 or 1, then it returns 0 to max.
-    if (max == 0) return 0;
-    else if (max == 1) return get_random(max);
-    return get_random_nonzero(max);
-}
-
-static uint32_t get_random_fuel(uint32_t prev_val) {
-    static uint8_t prev_rand_subset = 0;
-    uint32_t rand;
-    uint8_t max_ones, subset;
-    uint32_t rand_legal = 0;
-    prev_val = prev_val & ~0xFFFF;
-
-    for (int i = 0; i < 2; i++) {
-        subset = 0;
-        max_ones = 8;
-        if (prev_rand_subset > 4)
-            max_ones -= prev_rand_subset;
-        rand = get_random_kinda_nonzero(max_ones);
-        if (rand > 5 && prev_rand_subset) rand = 5;  // The gap of one or two is awkward
-        for (uint32_t j = 0; j < rand; j++) {
-            subset |= (1 << j);
-        }
-        if (prev_rand_subset >= 7)
-            subset = subset << 1;
-        subset &= 0xFF;
-        rand_legal |= subset << (8 * i);
-        prev_rand_subset = rand;
-    }
-
-    rand_legal = prev_val | rand_legal;
-    print_binary(rand_legal, 32);
-    return rand_legal;
-}
-
-static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) {
-/** @brief A legal random number starts with the previous number (which should be the 12 bits on the screen).
-  * @param prev_val The previous value to tack onto. The return will have its first NUM_GRID MSBs be the same as prev_val, and the rest be new
-  * @param difficulty To dictate how spread apart the obsticles must be
-  * @return the new random value, where it's first NUM_GRID MSBs are the same as prev_val
-  */
-    uint8_t min_zeros = (difficulty == DIFF_HARD) ? MIN_ZEROES_HARD : MIN_ZEROES;
-    uint32_t max = (1 << (_num_bits_obst_pattern - NUM_GRID)) - 1;
-    uint32_t rand = get_random_nonzero(max);
-    uint32_t rand_legal = 0;
-    prev_val = prev_val & ~max;
-
-    for (int i = (NUM_GRID + 1); i <= _num_bits_obst_pattern; i++) {
-        uint32_t mask = 1 << (_num_bits_obst_pattern - i);
-        bool msb = (rand & mask) >> (_num_bits_obst_pattern - i);
-        if (msb) {
-            rand_legal = rand_legal << min_zeros;
-            i+=min_zeros;
-        }
-        rand_legal |= msb;
-        rand_legal = rand_legal << 1;
-    }
-
-    rand_legal = rand_legal & max;
-    for (int i = 0; i <= min_zeros; i++) {
-        if (prev_val & (1 << (i + _num_bits_obst_pattern - NUM_GRID))){
-            rand_legal = rand_legal >> (min_zeros - i);
-            break;
-        }
-    }
-    rand_legal = prev_val | rand_legal;
-    print_binary(rand_legal, 32);
-    return rand_legal;
-}
-
-static void display_ball(bool jumping) {
-    if (!jumping) {
-        watch_set_pixel(0, 21);
-        watch_set_pixel(1, 21);
-        watch_set_pixel(0, 20);
-        watch_set_pixel(1, 20);
-        watch_clear_pixel(1, 17);
-        watch_clear_pixel(2, 20);
-        watch_clear_pixel(2, 21);     
-    }
-    else {
-        watch_clear_pixel(0, 21);
-        watch_clear_pixel(1, 21);
-        watch_clear_pixel(0, 20);
-        watch_set_pixel(1, 20);
-        watch_set_pixel(1, 17);
-        watch_set_pixel(2, 20);
-        watch_set_pixel(2, 21);
-    }
-}
-
-static void display_score(uint8_t score) {
-    char buf[3];
-    if (game_state.fuel_mode) {
-        score %= (MAX_DISP_SCORE_FUEL + 1);
-        sprintf(buf, "%1d", score);
-        watch_display_string(buf, 0);
-    }
-    else {
-        score %= (MAX_DISP_SCORE + 1);
-        sprintf(buf, "%2d", score);
-        watch_display_string(buf, 2);
-    }
-}
-
-static void add_to_score(endless_runner_state_t *state) {
-    if (game_state.curr_score <= MAX_HI_SCORE) {
-        game_state.curr_score++;
-        if (game_state.curr_score > state -> hi_score)
-            state -> hi_score = game_state.curr_score;
-    }
-    game_state.success_jump = true;
-    display_score(game_state.curr_score);
-}
-
-static void display_fuel(uint8_t subsecond, uint8_t difficulty) {
-    char buf[4];
-    if (difficulty == DIFF_FUEL_1 && game_state.fuel == 0 && subsecond % (FREQ/2) == 0) {
-        watch_display_string("  ", 2);  // Blink the 0 fuel to show it cannot be refilled.
-        return;
-    }
-    sprintf(buf, "%2d", game_state.fuel);
-    watch_display_string(buf, 2);
-}
-
-static void check_and_reset_hi_score(endless_runner_state_t *state) {
-    // Resets the hi score at the beginning of each month.
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    if ((state -> year_last_hi_score != date_time.unit.year) || 
-        (state -> month_last_hi_score != date_time.unit.month))
-    {
-        // The high score resets itself every new month.
-        state -> hi_score = 0;
-        state -> year_last_hi_score = date_time.unit.year;
-        state -> month_last_hi_score = date_time.unit.month;
-    }
-}
-
-static void display_difficulty(uint16_t difficulty) {
-    switch (difficulty)
-    {
-    case DIFF_BABY:
-        watch_display_string(" b", 2);
-        break;
-    case DIFF_EASY:
-        watch_display_string(" E", 2);
-        break;
-    case DIFF_HARD:
-        watch_display_string(" H", 2);
-        break;
-    case DIFF_FUEL:
-        watch_display_string(" F", 2);
-        break;
-    case DIFF_FUEL_1:
-        watch_display_string("1F", 2);
-        break;
-    case DIFF_NORM:
-    default:
-        watch_display_string(" N", 2);
-        break;
-    }
-    game_state.fuel_mode = difficulty >= DIFF_FUEL && difficulty <= DIFF_FUEL_1;
-}
-
-static void change_difficulty(endless_runner_state_t *state) {
-    state -> difficulty = (state -> difficulty + 1) % DIFF_COUNT;
-    display_difficulty(state -> difficulty);
-    if (state -> soundOn) {
-        if (state -> difficulty == 0) watch_buzzer_play_note(BUZZER_NOTE_B4, 30);
-        else  watch_buzzer_play_note(BUZZER_NOTE_C5, 30);
-    }
-}
-
-static void toggle_sound(endless_runner_state_t *state) {
-    state -> soundOn = !state -> soundOn;
-    if (state -> soundOn){
-        watch_buzzer_play_note(BUZZER_NOTE_C5, 30);
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    }
-    else {
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-    }
-}
-
-static void display_title(endless_runner_state_t *state) {
-    uint16_t hi_score = state -> hi_score;
-    uint8_t difficulty = state -> difficulty;
-    bool sound_on = state -> soundOn;
-    game_state.curr_screen = SCREEN_TITLE;
-    memset(&game_state, 0, sizeof(game_state));
-    game_state.sec_before_moves = 1; // The first obstacles will all be 0s, which is about an extra second of delay.
-    if (sound_on) game_state.sec_before_moves--; // Start chime is about 1 second
-    watch_set_colon();
-    if (hi_score > MAX_HI_SCORE) {
-        watch_display_string("ER  HS  --", 0);
-    }
-    else {
-        char buf[14];
-        sprintf(buf, "ER  HS%4d", hi_score);
-        watch_display_string(buf, 0);
-    }
-    display_difficulty(difficulty);
-}
-
-static void display_time(watch_date_time_t date_time, bool clock_mode_24h) {
-    static watch_date_time_t previous_date_time;
-    char buf[6 + 1];
-
-    // If the hour needs updating or it's the first time displaying the time
-    if ((game_state.curr_screen != SCREEN_TIME) || (date_time.unit.hour != previous_date_time.unit.hour)) {
-        uint8_t hour = date_time.unit.hour;
-        game_state.curr_screen = SCREEN_TIME;
-
-        if (clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
-        else {
-            if (hour >= 12) watch_set_indicator(WATCH_INDICATOR_PM);
-            hour %= 12;
-            if (hour == 0) hour = 12;
-        }
-        watch_set_colon();
-        sprintf( buf, "%2d%02d  ", hour, date_time.unit.minute);
-        watch_display_string(buf, 4);
-    }
-    // If both digits of the minute need updating
-    else if ((date_time.unit.minute / 10) != (previous_date_time.unit.minute / 10)) {
-        sprintf( buf, "%02d  ", date_time.unit.minute);
-        watch_display_string(buf, 6);
-    }
-    // If only the ones-place of the minute needs updating.
-    else if (date_time.unit.minute != previous_date_time.unit.minute) {
-        sprintf( buf, "%d  ", date_time.unit.minute % 10);
-        watch_display_string(buf, 7);
-    }
-    previous_date_time.reg = date_time.reg;
-}
-
-static void begin_playing(endless_runner_state_t *state) {
-    uint8_t difficulty = state -> difficulty;
-    game_state.curr_screen = SCREEN_PLAYING;
-    watch_clear_colon();
-    movement_request_tick_frequency((state -> difficulty == DIFF_BABY) ? FREQ_SLOW : FREQ);
-    if (game_state.fuel_mode) {
-        watch_display_string("           ", 0);
-        game_state.obst_pattern = get_random_fuel(0);
-        if ((16 * JUMP_FRAMES_FUEL_RECHARGE) < JUMP_FRAMES_FUEL) // 16 frames of zeros at the start of a level
-            game_state.fuel = JUMP_FRAMES_FUEL - (16 * JUMP_FRAMES_FUEL_RECHARGE); // Have it below its max to show it counting up when starting.
-        if (game_state.fuel < JUMP_FRAMES_FUEL_RECHARGE) game_state.fuel = JUMP_FRAMES_FUEL_RECHARGE;
-    }
-    else {
-        watch_display_string("         ", 2);
-        game_state.obst_pattern = get_random_legal(0, difficulty);
-    }
-    game_state.jump_state = NOT_JUMPING;
-    display_ball(game_state.jump_state != NOT_JUMPING);
-    display_score( game_state.curr_score);
-    if (state -> soundOn){
-        watch_buzzer_play_note(BUZZER_NOTE_C5, 200);
-        watch_buzzer_play_note(BUZZER_NOTE_E5, 200);
-        watch_buzzer_play_note(BUZZER_NOTE_G5, 200);
-    }
-}
-
-static void display_lose_screen(endless_runner_state_t *state) {
-    game_state.curr_screen = SCREEN_LOSE;
-    game_state.curr_score = 0;
-    watch_display_string("     LOSE ", 0);
-    if (state -> soundOn)
-        watch_buzzer_play_note(BUZZER_NOTE_A1, 600);
-    else
-        delay_ms(600);
-}
-
-static void display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t *state) {
-    static bool prev_obst_pos_two = 0;
-    switch (grid_loc)
-    {
-    case 2:
-        game_state.loc_2_on = obstacle;
-        if (obstacle)
-            watch_set_pixel(0, 20);
-        else if (game_state.jump_state != NOT_JUMPING) {
-            watch_clear_pixel(0, 20);
-            if (game_state.fuel_mode && prev_obst_pos_two)
-                add_to_score(state);
-        }
-        prev_obst_pos_two = obstacle;
-        break;
-    case 3:
-        game_state.loc_3_on = obstacle;
-        if (obstacle)
-            watch_set_pixel(1, 21);
-        else if (game_state.jump_state != NOT_JUMPING)
-            watch_clear_pixel(1, 21);
-        break;
-    
-    case 1:
-        if (!game_state.fuel_mode && obstacle)  // If an obstacle is here, it means the ball cleared it
-            add_to_score(state);
-        //fall through
-    case 0:
-    case 5:
-        if (obstacle)
-            watch_set_pixel(0, 18 + grid_loc);
-        else
-            watch_clear_pixel(0, 18 + grid_loc);
-        break;
-    case 4:
-        if (obstacle)
-            watch_set_pixel(1, 22);
-        else
-            watch_clear_pixel(1, 22);
-        break;
-    case 6:
-        if (obstacle)
-            watch_set_pixel(1, 0);
-        else
-            watch_clear_pixel(1, 0);
-        break;
-    case 7:
-    case 8:
-        if (obstacle)
-            watch_set_pixel(0, grid_loc - 6);
-        else
-            watch_clear_pixel(0, grid_loc - 6);
-        break;
-    case 9:
-    case 10:
-        if (obstacle)
-            watch_set_pixel(0, grid_loc - 5);
-        else
-            watch_clear_pixel(0, grid_loc - 5);
-        break;
-    case 11:
-        if (obstacle)
-            watch_set_pixel(1, 6);
-        else
-            watch_clear_pixel(1, 6);
-        break;
-    default:
-        break;
-    }
-}
-
-static void stop_jumping(endless_runner_state_t *state) {
-    game_state.jump_state = NOT_JUMPING;
-    display_ball(game_state.jump_state != NOT_JUMPING);
-    if (state -> soundOn){
-        if (game_state.success_jump)
-            watch_buzzer_play_note(BUZZER_NOTE_C5, 60);
-        else
-            watch_buzzer_play_note(BUZZER_NOTE_C3, 60);
-    }
-    game_state.success_jump = false;
-}
-
-static void display_obstacles(endless_runner_state_t *state) {
-    for (int i = 0; i < NUM_GRID; i++) {
-        // Use a bitmask to isolate each bit and shift it to the least significant position
-        uint32_t mask = 1 << ((_num_bits_obst_pattern - 1) - i);
-        bool obstacle = (game_state.obst_pattern & mask) >> ((_num_bits_obst_pattern - 1) - i);
-        display_obstacle(obstacle, i, state);
-    }
-    game_state.obst_pattern = game_state.obst_pattern << 1;
-    game_state.obst_indx++;
-    if (game_state.fuel_mode) {
-        if (game_state.obst_indx >= (_num_bits_obst_pattern / 2)) {
-                game_state.obst_indx = 0;
-                game_state.obst_pattern = get_random_fuel(game_state.obst_pattern);
-        }
-    }
-    else if (game_state.obst_indx >= _num_bits_obst_pattern - NUM_GRID) {
-        game_state.obst_indx = 0;
-        game_state.obst_pattern = get_random_legal(game_state.obst_pattern, state -> difficulty);
-    }
-}
-
-static void update_game(endless_runner_state_t *state, uint8_t subsecond) {
-    uint8_t curr_jump_frame = 0;
-    if (game_state.sec_before_moves != 0) {
-        if (subsecond == 0) --game_state.sec_before_moves;
-        return;
-    }
-    display_obstacles(state);
-    switch (game_state.jump_state)
-    {
-    case NOT_JUMPING:
-        if (game_state.fuel_mode) {
-            for (int i = 0; i < JUMP_FRAMES_FUEL_RECHARGE; i++)
-            {
-                if(game_state.fuel >= JUMP_FRAMES_FUEL || (state -> difficulty == DIFF_FUEL_1 && !game_state.fuel))
-                    break;
-                game_state.fuel++;
-            }
-        }
-        break;
-    case JUMPING_FINAL_FRAME:
-        stop_jumping(state);
-        break;
-    default:
-        if (game_state.fuel_mode) {
-            if (!game_state.fuel)
-                game_state.jump_state = JUMPING_FINAL_FRAME;
-            else
-                game_state.fuel--;
-            if (!HAL_GPIO_BTN_ALARM_read() && !HAL_GPIO_BTN_LIGHT_read()) stop_jumping(state);
-        }
-        else {
-            curr_jump_frame = game_state.jump_state - NOT_JUMPING;
-            if (curr_jump_frame >= JUMP_FRAMES_EASY || (state -> difficulty >= DIFF_NORM && curr_jump_frame >= JUMP_FRAMES))
-                game_state.jump_state = JUMPING_FINAL_FRAME;
-            else
-                game_state.jump_state++;
-        }
-        break;
-    }
-    if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) {
-        delay_ms(200);  // To show the player jumping onto the obstacle before displaying the lose screen.
-        display_lose_screen(state);
-    }
-    else if (game_state.fuel_mode)
-        display_fuel(subsecond, state -> difficulty);
-}
-
-void endless_runner_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(endless_runner_state_t));
-        memset(*context_ptr, 0, sizeof(endless_runner_state_t));
-        endless_runner_state_t *state = (endless_runner_state_t *)*context_ptr;
-        state->difficulty = DIFF_NORM;
-    }
-}
-
-void endless_runner_face_activate(void *context) {
-    (void) context;
-}
-
-bool endless_runner_face_loop(movement_event_t event, void *context) {
-    endless_runner_state_t *state = (endless_runner_state_t *)context;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            check_and_reset_hi_score(state);
-            if (state -> soundOn) watch_set_indicator(WATCH_INDICATOR_BELL);
-            display_title(state);
-            break;
-        case EVENT_TICK:
-            switch (game_state.curr_screen)
-            {
-            case SCREEN_TITLE:
-            case SCREEN_LOSE:
-                break;
-            default:
-                update_game(state, event.subsecond);
-                break;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-        case EVENT_ALARM_BUTTON_UP:
-            if (game_state.curr_screen == SCREEN_TITLE)
-                begin_playing(state);
-            else if (game_state.curr_screen == SCREEN_LOSE)
-                display_title(state);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (game_state.curr_screen == SCREEN_TITLE)
-                change_difficulty(state);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-        case EVENT_ALARM_BUTTON_DOWN:
-            if (game_state.curr_screen == SCREEN_PLAYING && game_state.jump_state == NOT_JUMPING){
-                if (game_state.fuel_mode && !game_state.fuel) break;
-                game_state.jump_state = JUMPING_START;
-                display_ball(game_state.jump_state != NOT_JUMPING);
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (game_state.curr_screen != SCREEN_PLAYING)
-                toggle_sound(state);
-            break;
-        case EVENT_TIMEOUT:
-            if (game_state.curr_screen != SCREEN_TITLE)
-                display_title(state);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            display_time(watch_rtc_get_date_time(), movement_clock_mode_24h());
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-    return true;
-}
-
-void endless_runner_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/complication/endless_runner_face.h b/movement/watch_faces/complication/endless_runner_face.h
deleted file mode 100644 (file)
index 3cfa681..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 <David Volovskiy>
- *
- * 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 ENDLESS_RUNNER_FACE_H_
-#define ENDLESS_RUNNER_FACE_H_
-
-#include "movement.h"
-
-/*
-    ENDLESS_RUNNER face
-
-    This is a basic endless-runner, like the [Chrome Dino game](https://en.wikipedia.org/wiki/Dinosaur_Game).
-    On the title screen, you can select a difficulty by long-pressing LIGHT or toggle sound by long-pressing ALARM.
-    LED or ALARM are used to jump.
-    High-score is displayed on the top-right on the title screen. During a game, the current score is displayed.
-*/
-
-typedef struct {
-    uint16_t hi_score : 10;
-    uint8_t difficulty : 3;
-    uint8_t month_last_hi_score : 4;
-    uint8_t year_last_hi_score : 6;
-    uint8_t soundOn : 1;
-    /* 24 bits, likely aligned to 32 bits = 4 bytes */
-} endless_runner_state_t;
-
-void endless_runner_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void endless_runner_face_activate(void *context);
-bool endless_runner_face_loop(movement_event_t event, void *context);
-void endless_runner_face_resign(void *context);
-
-#define endless_runner_face ((const watch_face_t){ \
-    endless_runner_face_setup, \
-    endless_runner_face_activate, \
-    endless_runner_face_loop, \
-    endless_runner_face_resign, \
-    NULL, \
-})
-
-#endif // ENDLESS_RUNNER_FACE_H_
-
diff --git a/movement/watch_faces/complication/flashlight_face.c b/movement/watch_faces/complication/flashlight_face.c
deleted file mode 100644 (file)
index 10794de..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "flashlight_face.h"
-
-void flashlight_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(flashlight_state_t));
-        memset(*context_ptr, 0, sizeof(flashlight_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void flashlight_face_activate(void *context) {
-    (void) context;
-
-    HAL_GPIO_A2_out();
-    HAL_GPIO_A2_clr();
-}
-
-bool flashlight_face_loop(movement_event_t event, void *context) {
-    (void) context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_display_string("FL", 0);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            HAL_GPIO_A2_toggle();
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void flashlight_face_resign(void *context) {
-    (void) context;
-
-    HAL_GPIO_A2_clr();
-    HAL_GPIO_A2_off();
-}
diff --git a/movement/watch_faces/complication/flashlight_face.h b/movement/watch_faces/complication/flashlight_face.h
deleted file mode 100644 (file)
index 6fc7f8e..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 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 FLASHLIGHT_FACE_H_
-#define FLASHLIGHT_FACE_H_
-
-/*
- * FLASHLIGHT face
- *
- * A flashlight for use with the Flashlight sensor board.
- *
- * When the watch face appears, the display will show "FL" in the top two positions.
- * Pressing the Light button will toggle the flashlight on and off.
- *
- */
-
-#include "movement.h"
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint8_t unused;
-} flashlight_state_t;
-
-void flashlight_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void flashlight_face_activate(void *context);
-bool flashlight_face_loop(movement_event_t event, void *context);
-void flashlight_face_resign(void *context);
-
-#define flashlight_face ((const watch_face_t){ \
-    flashlight_face_setup, \
-    flashlight_face_activate, \
-    flashlight_face_loop, \
-    flashlight_face_resign, \
-    NULL, \
-})
-
-#endif // FLASHLIGHT_FACE_H_
-
diff --git a/movement/watch_faces/complication/geomancy_face.c b/movement/watch_faces/complication/geomancy_face.c
deleted file mode 100644 (file)
index d507601..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "toss_up_face.h"
-#include "geomancy_face.h"
-
-// CONSTANTS //////////////////////////////////////////////////////////////////
-
-// The Bagua 八卦 Trigrams encoded as 3bit tribbles, represented as binary integer
-static const uint32_t bagua = 0b00000101001110010111011100000000;
-
-// The King Wen Sequence 文王卦序 of the I Ching 易經 Hexagrams 卦 encoded as an array
-// of decimal integers in the order of two combined Trigram tribbles from 0b000000 to 
-// 0b111111 
-static const uint8_t wen_order[] = {
-     1, 22,  7, 19, 15, 34, 44, 11, 
-    14, 51, 38, 52, 61, 55, 30, 32, 
-     6,  3, 28, 58, 39, 63, 46,  5, 
-    45, 17, 47, 56, 31, 49, 27, 43, 
-    23, 26,  2, 41, 50, 20, 16, 24, 
-    35, 21, 62, 36, 54, 29, 48, 12, 
-    18, 40, 59, 60, 53, 37, 57,  9, 
-    10, 25,  4,  8, 33, 13, 42,  0
-};
-
-// The geomantic figures encoded as 4 bit nibbles, represented as hexadecimal integer
-static const uint64_t geomantic = 0x4ABF39D25E76C180;
-
-// Abbreviations of the Names of the Geomantic Figures in the order of the 4 bit nibbles
-// from 0b0000 to 0b1111
-static const char figures[16][2] = {
-    "VI" /* Via */, "Hd" /* Head of the Dragon */, "PA" /* Puella */, "GF" /* Greater Fortune*/, 
-    "PR" /* Puer */, "AQ" /* Acquisitio */, "CA" /* Carcer */, "TR" /* Tristitia */,
-    "Td" /* Tail of the Dragon */, "CO" /* Conjunctio */, "AM" /* Amissio */, "AL" /* Albus */,
-    "LF" /* Lesser Fortune */, "RU" /* Rubeus */, "LA" /* Laetitia */, "PO" /* Populus */
-};
-
-// DECLARATIONS ///////////////////////////////////////////////////////////////
-
-static void geomancy_face_display(geomancy_state_t *state);
-static nibble_t _geomancy_pick_figure(void);
-static tribble_t _iching_pick_trigram(void);
-static uint8_t _iching_form_hexagram(void);
-static void _geomancy_display(nibble_t code);
-static void _display_hexagram(uint8_t hexagram, char* str);
-static void _fix_broken_line(uint8_t hexagram);
-static void _throw_animation(geomancy_state_t *state);
-
-// WATCH FACE FUNCTIONS ///////////////////////////////////////////////////////
-
-void geomancy_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(geomancy_state_t));
-        memset(*context_ptr, 0, sizeof(geomancy_state_t));
-    }
-}
-
-void geomancy_face_activate(void *context) {
-    (void) context;
-}
-
-bool geomancy_face_loop(movement_event_t event, void *context) {
-    geomancy_state_t *state = (geomancy_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            state->animate = false;
-            state->animation = 0;
-            watch_display_string("    IChing", 0);
-            break;
-        case EVENT_TICK:
-            if ( state->animate ) {
-                state->animation = (state->animation + 1) % 39;
-                geomancy_face_display(state);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if ( state->animate ) break;
-            if ( state->mode <= 1 ) state->mode = 2;
-            else if ( state->mode >= 2 ) state->mode = 0;
-            geomancy_face_display(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if ( state->animate ) break;
-            switch ( state->mode ) {
-                case 0:
-                    state->mode++;
-                    // fall through
-                case 1:
-                    state->animate = true;
-                    state->i_ching_hexagram = _iching_form_hexagram();
-                    break;
-                case 2:
-                    state->mode++;
-                    // fall through
-                case 3:
-                    state->animate = true;
-                    state->geomantic_figure = _geomancy_pick_figure().bits;
-                    break;
-                default:
-                    break;
-            }
-            geomancy_face_display(state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if ( state->animate ) break;
-            state->caption = !state->caption;
-            watch_display_string("    ", 0);
-            geomancy_face_display(state);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-    return true;
-}
-
-void geomancy_face_resign(void *context) {
-    (void) context;
-}
-
-// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
-
-/** @brief display handler */
-static void geomancy_face_display(geomancy_state_t *state) {
-    char token[7] = {0};
-    nibble_t figure = *((nibble_t*) &state->geomantic_figure);
-    switch ( state->mode ) {
-        case 0:
-            watch_display_string("    IChing", 0);
-            break;
-        case 1:
-            _throw_animation(state);
-            if ( !state->animate ) {
-                _display_hexagram(state->i_ching_hexagram, token);
-                watch_display_string(token, 4);
-                _fix_broken_line(state->i_ching_hexagram);
-                if (state->caption) {
-                    sprintf(token, "%2d", wen_order[state->i_ching_hexagram] + 1);
-                    watch_display_string(token, 2);
-                }
-            }
-            break;
-        case 2:
-            watch_display_string("    GeomCy", 0);
-            break;
-        case 3:
-            _throw_animation(state);
-            if ( !state->animate ) {
-                if ( state->caption ) {
-                    sprintf(token, "%c%c", figures[state->geomantic_figure][0], figures[state->geomantic_figure][1]);
-                    watch_display_string(token, 0);
-                }
-                _geomancy_display(figure);
-            }
-            break;
-        default:
-            break;
-    }
-}
-
-/** @brief screen clearing animation between castings */
-static void _throw_animation(geomancy_state_t *state) {
-    movement_request_tick_frequency(16);
-    switch ( state->animation ) {
-        case 0:
-            watch_set_pixel(0, 22);
-            break;
-        case 1:
-            watch_set_pixel(2, 22);
-            watch_set_pixel(2, 23);
-            watch_clear_pixel(0, 22);
-            break;
-        case 2:
-            watch_set_pixel(1, 22);
-            watch_set_pixel(0, 23);
-            break;
-        case 3:
-            watch_set_pixel(2, 0);
-            watch_set_pixel(1, 0);
-            watch_set_pixel(2, 21);
-            watch_set_pixel(1, 21);
-            watch_clear_pixel(2, 22);
-            watch_clear_pixel(1, 22);
-            watch_clear_pixel(2, 23);
-            watch_clear_pixel(0, 23);
-            watch_clear_pixel(1, 23);
-            break;
-        case 4:
-            watch_set_pixel(1, 17);
-            watch_set_pixel(0, 20);
-            watch_set_pixel(2, 10);
-            watch_set_pixel(0, 1);
-            break;
-        case 5:
-            watch_clear_pixel(2, 21);
-            watch_clear_pixel(1, 21);
-            watch_clear_pixel(2, 0);
-            watch_clear_pixel(1, 0);
-            watch_clear_pixel(1, 20);
-            watch_clear_pixel(2, 20);
-            watch_clear_pixel(0, 21);
-            watch_clear_pixel(1, 1);
-            watch_clear_pixel(0, 0);
-            watch_clear_pixel(2, 1);
-            watch_set_pixel(2, 19);
-            watch_set_pixel(0, 19);
-            watch_set_pixel(1, 2);
-            watch_set_pixel(0, 2);
-            break;
-        case 6:
-            watch_clear_pixel(1, 17);
-            watch_clear_pixel(0, 20);
-            watch_clear_pixel(2, 10);
-            watch_clear_pixel(0, 1);
-            watch_set_pixel(2, 18);
-            watch_set_pixel(0, 18);
-            watch_set_pixel(2, 3);
-            watch_set_pixel(0, 4);
-            break;
-        case 7:
-            watch_clear_pixel(2, 19);
-            watch_clear_pixel(0, 19);
-            watch_clear_pixel(1, 18);
-            watch_clear_pixel(1, 19);
-            watch_clear_pixel(1, 2);
-            watch_clear_pixel(0, 2);
-            watch_clear_pixel(1, 3);
-            watch_clear_pixel(0, 3);
-            watch_clear_pixel(2, 2);
-            watch_set_pixel(1, 4);
-            watch_set_pixel(0, 5);
-            break;
-        case 8:
-            watch_clear_pixel(2, 18);
-            watch_clear_pixel(0, 18);
-            watch_clear_pixel(2, 3);
-            watch_clear_pixel(0, 4);
-            watch_set_pixel(2, 5);
-            watch_set_pixel(1, 6);
-            break;
-        case 9:
-            watch_clear_pixel(1, 4);
-            watch_clear_pixel(0, 5);
-            watch_clear_pixel(1, 5);
-            watch_clear_pixel(2, 4);
-            watch_clear_pixel(0, 6);
-            break;
-        case 10:
-            watch_clear_pixel(2, 5);
-            watch_clear_pixel(1, 6);
-            break;
-        case 11:
-            state->animate = false;
-            state->animation = 0;
-            movement_request_tick_frequency(1);
-            break;
-    }
-}
-
-// I CHING FUNCTIONS //////////////////////////////////////////////////////////
-
-/** @brief form a trigram from three random bit picks
- */
-static tribble_t _iching_pick_trigram(void) {
-    uint8_t index = (divine_bit() << 2) | (divine_bit() << 1) | divine_bit();
-    tribble_t trigram = {(bagua >> (3 * index)) & 0b111};
-    return trigram;
-}
-
-/** @brief form a hexagram from two trigrams
- */
-static uint8_t _iching_form_hexagram(void);
-static uint8_t _iching_form_hexagram(void) {
-    tribble_t inner = _iching_pick_trigram();
-    tribble_t outer = _iching_pick_trigram();
-    uint8_t hexagram = (inner.bits << 3) | outer.bits;
-    return hexagram;
-}
-
-/** @brief display hexagram
- *  @details | for unbroken lines and Ξ for broken lines, left of display is bottom
- */
-static void _display_hexagram(uint8_t hexagram, char* str);
-static void _display_hexagram(uint8_t hexagram, char* str) {
-    str[6] = '\0';  // Null-terminate the string
-    for (uint8_t i = 0; i < 6; i++) {
-        if (hexagram & (1 << (5 - i))) {
-            str[i] = '1';
-        } else {
-            str[i] = '=';
-        }
-    }
-}
-
-/** @brief when Ξ digits show as = then manually add a line on top
- */
-static void _fix_broken_line(uint8_t hexagram) {
-    for (uint8_t i = 0; i < 6; i++) {
-        if (!(hexagram & (1 << (5 - i)))) {
-            if ( i == 1 ) watch_set_pixel(2, 20);
-            if ( i == 3 ) watch_set_pixel(2, 1);
-            if ( i == 4 ) watch_set_pixel(2, 2);
-            if ( i == 5 ) watch_set_pixel(2, 4);
-        }
-    }
-}
-
-// GEOMANCY FUNCTIONS /////////////////////////////////////////////////////////
-
-/** @brief choose a geomantic figure from four random bits
- *  @details 0 represents · and 1 represents : counting from the bottom
- */
-static nibble_t _geomancy_pick_figure(void);
-static nibble_t _geomancy_pick_figure(void) {
-    uint8_t index = (divine_bit() << 3) | (divine_bit() << 2) | (divine_bit() << 1) | divine_bit();
-    nibble_t figure = {(geomantic >> (4 * (15 - index))) & 0xF};
-    return figure;
-}
-
-/** @brief display the geomantic figure, left of display is bottom
- */
-static void _geomancy_display(nibble_t code) {
-    // draw geomantic figures
-    bool row1 = (code.bits >> 3) & 1;
-    bool row2 = (code.bits >> 2) & 1;
-    bool row3 = (code.bits >> 1) & 1;
-    bool row4 = code.bits & 1;
-
-    if ( row1 ) watch_set_pixel(1, 18); else watch_set_pixel(1, 19);
-    if ( row2 ) { watch_set_pixel(2, 20); watch_set_pixel(0, 21);} else watch_set_pixel(1, 20);
-    if ( row3 ) watch_set_pixel(0, 22); else watch_set_pixel(1, 23);
-    if ( row4 ) { watch_set_pixel(2, 1); watch_set_pixel(0, 0);} else watch_set_pixel(1, 1);
-}
\ No newline at end of file
diff --git a/movement/watch_faces/complication/geomancy_face.h b/movement/watch_faces/complication/geomancy_face.h
deleted file mode 100644 (file)
index c622bfc..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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 GEOMANCY_FACE_H_
-#define GEOMANCY_FACE_H_
-
-/*
- * GEOMANCY watch face
- *
- * A simple and straightforward watch face for the ancient Eastern geomantic divination system
- * of I Ching and the western system of "Geomancy". It is an optional addition to the Toss Up
- * Face.
- * 
- * The LIGHT button toggles between the two systems of geomancy.
- * 
- * The ALARM button casts an I Ching hexagram or Geomantic figure based on drawing virtual
- * stalks from the True Random Number Generator in the Sensor Watch.
- * 
- * The figures are flipped 90 degrees clockwise, so the left side is the bottom and the
- * right side the top.
- * 
- * LONG PRESSING ALARM toggles the display of the King Wen sequence index for the cast I Ching
- * Hexagram (https://en.wikipedia.org/wiki/King_Wen_sequence )or the abbreviated name for the
- * cast Geomantic Figure:
- * 
- * GF - Greater Fortune (Fortuna Major)
- * LF - Lesser Fortune (Fortuna Minor)
- * PO - Populus
- * VI - Via
- * AL - Albus
- * CO - Conjunctio
- * PA - Puella
- * AM - Amissio
- * PR - Puer
- * RU - Rubeus
- * AQ - Acquisitio
- * LA - Laetitia
- * TR - Tristitia
- * CA - Carcer
- * HD - Head of the Dragon (Caput Draconis)
- * TD - Tail of the Dragon (Cauda Draconis)
- * 
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint8_t bits : 4;
-} nibble_t;
-
-typedef struct {
-    uint8_t bits : 3;
-} tribble_t;
-
-typedef struct {
-    uint8_t mode : 3;
-    uint8_t geomantic_figure;
-    uint8_t i_ching_hexagram : 6;
-    bool caption;
-    uint8_t animation;
-    bool animate;
-} geomancy_state_t;
-
-void geomancy_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void geomancy_face_activate(void *context);
-bool geomancy_face_loop(movement_event_t event, void *context);
-void geomancy_face_resign(void *context);
-
-#define geomancy_face ((const watch_face_t){ \
-    geomancy_face_setup, \
-    geomancy_face_activate, \
-    geomancy_face_loop, \
-    geomancy_face_resign, \
-    NULL, \
-})
-
-#endif // GEOMANCY_FACE_H_
-
diff --git a/movement/watch_faces/complication/habit_face.c b/movement/watch_faces/complication/habit_face.c
deleted file mode 100644 (file)
index 1045032..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 tslil clingman
- *
- * 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.
- */
-
-#include "habit_face.h"
-#include "watch_private_display.h"
-#include "watch_rtc.h"
-#include "watch_slcd.h"
-#include "watch_utility.h"
-#include <stdlib.h>
-#include <string.h>
-
-static inline uint32_t today_unix(const uint32_t utc_offset) {
-  const watch_date_time_t dt = watch_rtc_get_date_time();
-  return watch_utility_convert_to_unix_time(dt.unit.year + 2020, dt.unit.month,
-                                            dt.unit.day, 0, 0, 0, utc_offset);
-}
-
-static inline uint32_t days_since_unix(const uint32_t since,
-                                       const uint32_t until) {
-  return (until - since) / (60 * 60 * 24);
-}
-
-typedef struct {
-  uint16_t total_count;
-  uint8_t lookback;
-  uint32_t last_update;
-  bool display_total;
-} habit_state_t;
-
-void habit_face_setup(uint8_t watch_face_index,
-                      void **context_ptr) {
-  (void)watch_face_index;
-  if (*context_ptr == NULL) {
-    *context_ptr = malloc(sizeof(habit_state_t));
-    memset(*context_ptr, 0, sizeof(habit_state_t));
-    habit_state_t *state = (habit_state_t *)*context_ptr;
-    state->lookback = 0;
-    state->last_update = watch_utility_offset_timestamp(
-        today_unix(movement_get_current_timezone_offset()), -24, 0, 0);
-  }
-}
-
-static inline void display_state(habit_state_t *state) {
-  const bool can_do = (state->lookback & 1) == 0;
-  char buf[16];
-
-  if (can_do) {
-    watch_clear_indicator(WATCH_INDICATOR_LAP);
-  } else {
-    watch_set_indicator(WATCH_INDICATOR_LAP);
-  }
-
-  if (state->display_total) {
-    sprintf(buf, "HA  %03dtot", state->total_count);
-    watch_display_string(buf, 0);
-  } else {
-    sprintf(buf, "HA      d%c", can_do ? 'o' : 'n');
-    uint8_t copy = state->lookback;
-    for (uint8_t c = 0; copy; copy >>= 2, c++) {
-      switch (copy & 3) {
-      case 0:
-        break;
-      case 1:
-        buf[4 + c] = 'I';
-        break;
-      case 2:
-        buf[4 + c] = '1';
-        break;
-      case 3:
-        buf[4 + c] = '|';
-        break;
-      }
-    }
-    watch_display_string(buf, 0);
-    if (can_do) {
-      const uint64_t segmap = Segment_Map[4] >> 48;
-      watch_set_pixel((segmap & 0xFF) >> 6, segmap & 0x3F);
-    }
-  }
-}
-
-void habit_face_activate(void *context) {
-  habit_state_t *state = (habit_state_t *)context;
-  display_state(state);
-}
-
-bool habit_face_loop(movement_event_t event,
-                     void *context) {
-  habit_state_t *state = (habit_state_t *)context;
-
-  const uint32_t today_now_unix = today_unix(movement_get_current_timezone_offset());
-  const bool can_do = (state->lookback & 1) == 0;
-
-  switch (event.event_type) {
-  case EVENT_ACTIVATE:
-  case EVENT_TICK: {
-    display_state(state);
-    if (today_now_unix > state->last_update) {
-      uint8_t num_shifts = days_since_unix(state->last_update, today_now_unix);
-      if (num_shifts > 7)
-        num_shifts = 7;
-      state->lookback <<= num_shifts;
-      state->last_update = today_now_unix;
-    }
-    break;
-  }
-  case EVENT_LIGHT_BUTTON_UP: {
-    state->display_total = !state->display_total;
-    display_state(state);
-    break;
-  }
-  case EVENT_ALARM_BUTTON_UP: {
-    if (can_do) {
-      state->lookback |= 1;
-      state->total_count++;
-      state->last_update = today_now_unix;
-      display_state(state);
-    };
-    break;
-  }
-  case EVENT_TIMEOUT: {
-    movement_move_to_face(0);
-    break;
-  }
-  default:
-    return movement_default_loop_handler(event);
-  }
-  return true;
-}
-
-void habit_face_resign(void *context) {
-  (void)context;
-}
diff --git a/movement/watch_faces/complication/habit_face.h b/movement/watch_faces/complication/habit_face.h
deleted file mode 100644 (file)
index 6170ee8..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 tslil clingman
- *
- * 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 HABIT_FACE_H_
-#define HABIT_FACE_H_
-
-/*
- * Habit tracking face
- *
- * Allows the user to record a single succesful instance of a particular habit
- * occuring per day, and displays history for eight days prior as well as a
- * total counter.
- *
- */
-
-#include "movement.h"
-
-void habit_face_setup(uint8_t watch_face_index,
-                      void **context_ptr);
-void habit_face_activate(void *context);
-bool habit_face_loop(movement_event_t event, void *context);
-void habit_face_resign(void *context);
-
-#define habit_face ((const watch_face_t){ \
-    habit_face_setup, \
-    habit_face_activate, \
-    habit_face_loop, \
-    habit_face_resign, \
-    NULL, \
-})
-
-#endif // HABIT_FACE_H_
diff --git a/movement/watch_faces/complication/higher_lower_game_face.c b/movement/watch_faces/complication/higher_lower_game_face.c
deleted file mode 100755 (executable)
index 3491b5b..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-/*\r
- * MIT License\r
- *\r
- * Copyright (c) 2023 Chris Ellis\r
- *\r
- * Permission is hereby granted, free of charge, to any person obtaining a copy\r
- * of this software and associated documentation files (the "Software"), to deal\r
- * in the Software without restriction, including without limitation the rights\r
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
- * copies of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be included in all\r
- * copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-// Emulator only: need time() to seed the random number generator.\r
-#if __EMSCRIPTEN__\r
-#include <time.h>\r
-#endif\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include "higher_lower_game_face.h"\r
-#include "watch_private_display.h"\r
-\r
-#define TITLE_TEXT "Hi-Lo"\r
-#define GAME_BOARD_SIZE 6\r
-#define MAX_BOARDS 40\r
-#define GUESSES_PER_SCREEN 5\r
-#define WIN_SCORE (MAX_BOARDS * GUESSES_PER_SCREEN)\r
-#define STATUS_DISPLAY_START 0\r
-#define BOARD_SCORE_DISPLAY_START 2\r
-#define BOARD_DISPLAY_START 4\r
-#define BOARD_DISPLAY_END 9\r
-#define MIN_CARD_VALUE 2\r
-#define MAX_CARD_VALUE 14\r
-#define CARD_RANK_COUNT (MAX_CARD_VALUE - MIN_CARD_VALUE + 1)\r
-#define CARD_SUIT_COUNT 4\r
-#define DECK_SIZE (CARD_SUIT_COUNT * CARD_RANK_COUNT)\r
-#define FLIP_BOARD_DIRECTION false\r
-\r
-typedef struct card_t {\r
-    uint8_t value;\r
-    bool revealed;\r
-} card_t;\r
-\r
-typedef enum {\r
-    A, B, C, D, E, F, G\r
-} segment_t;\r
-\r
-typedef enum {\r
-    HL_GUESS_EQUAL,\r
-    HL_GUESS_HIGHER,\r
-    HL_GUESS_LOWER\r
-} guess_t;\r
-\r
-typedef enum {\r
-    HL_GS_TITLE_SCREEN,\r
-    HL_GS_GUESSING,\r
-    HL_GS_WIN,\r
-    HL_GS_LOSE,\r
-    HL_GS_SHOW_SCORE,\r
-} game_state_t;\r
-\r
-static game_state_t game_state = HL_GS_TITLE_SCREEN;\r
-static card_t game_board[GAME_BOARD_SIZE] = {0};\r
-static uint8_t guess_position = 0;\r
-static uint8_t score = 0;\r
-static uint8_t completed_board_count = 0;\r
-static uint8_t deck[DECK_SIZE] = {0};\r
-static uint8_t current_card = 0;\r
-\r
-static uint8_t generate_random_number(uint8_t num_values) {\r
-    // Emulator: use rand. Hardware: use arc4random.\r
-#if __EMSCRIPTEN__\r
-    return rand() % num_values;\r
-#else\r
-    return arc4random_uniform(num_values);\r
-#endif\r
-}\r
-\r
-static void stack_deck(void) {\r
-    for (size_t i = 0; i < CARD_RANK_COUNT; i++) {\r
-        for (size_t j = 0; j < CARD_SUIT_COUNT; j++)\r
-            deck[(i * CARD_SUIT_COUNT) + j] = MIN_CARD_VALUE + i;\r
-    }\r
-}\r
-\r
-static void shuffle_deck(void) {\r
-    // Randomize shuffle with Fisher Yates\r
-    size_t i;\r
-    size_t j;\r
-    uint8_t tmp;\r
-\r
-    for (i = DECK_SIZE - 1; i > 0; i--) {\r
-        j = generate_random_number(0xFF) % (i + 1);\r
-        tmp = deck[j];\r
-        deck[j] = deck[i];\r
-        deck[i] = tmp;\r
-    }\r
-}\r
-\r
-static void reset_deck(void) {\r
-    current_card = 0;\r
-    stack_deck();\r
-    shuffle_deck();\r
-}\r
-\r
-static uint8_t get_next_card(void) {\r
-    if (current_card >= DECK_SIZE)\r
-        reset_deck();\r
-    return deck[current_card++];\r
-}\r
-\r
-static void reset_board(bool first_round) {\r
-    // First card is random on the first board, and carried over from the last position on subsequent boards\r
-    const uint8_t first_card_value = first_round\r
-                                     ? get_next_card()\r
-                                     : game_board[GAME_BOARD_SIZE - 1].value;\r
-\r
-    game_board[0].value = first_card_value;\r
-    game_board[0].revealed = true; // Always reveal first card\r
-\r
-    // Fill remainder of board\r
-    for (size_t i = 1; i < GAME_BOARD_SIZE; ++i) {\r
-        game_board[i] = (card_t) {\r
-                .value = get_next_card(),\r
-                .revealed = false\r
-        };\r
-    }\r
-}\r
-\r
-static void init_game(void) {\r
-    watch_clear_display();\r
-    watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);\r
-    watch_display_string("GA", STATUS_DISPLAY_START);\r
-    reset_deck();\r
-    reset_board(true);\r
-    score = 0;\r
-    completed_board_count = 0;\r
-    guess_position = 1;\r
-}\r
-\r
-static void set_segment_at_position(segment_t segment, uint8_t position) {\r
-    const uint64_t position_segment_data = (Segment_Map[position] >> (8 * (uint8_t) segment)) & 0xFF;\r
-    const uint8_t com_pin = position_segment_data >> 6;\r
-    const uint8_t seg = position_segment_data & 0x3F;\r
-    watch_set_pixel(com_pin, seg);\r
-}\r
-\r
-static void render_board_position(size_t board_position) {\r
-    const size_t display_position = FLIP_BOARD_DIRECTION\r
-                                    ? BOARD_DISPLAY_START + board_position\r
-                                    : BOARD_DISPLAY_END - board_position;\r
-    const bool revealed = game_board[board_position].revealed;\r
-\r
-    //// Current position indicator spot\r
-    //if (board_position == guess_position) {\r
-    //    watch_display_character('-', display_position);\r
-    //    return;\r
-    //}\r
-\r
-    if (!revealed) {\r
-        // Higher or lower indicator (currently just an empty space)\r
-        watch_display_character(' ', display_position);\r
-        //set_segment_at_position(F, display_position);\r
-        return;\r
-    }\r
-\r
-    const uint8_t value = game_board[board_position].value;\r
-    switch (value) {\r
-        case 14: // A (≡)\r
-            watch_display_character(' ', display_position);\r
-            set_segment_at_position(A, display_position);\r
-            set_segment_at_position(D, display_position);\r
-            set_segment_at_position(G, display_position);\r
-            break;\r
-        case 13: // K (=)\r
-            watch_display_character(' ', display_position);\r
-            set_segment_at_position(A, display_position);\r
-            set_segment_at_position(D, display_position);\r
-            break;\r
-        case 12: // Q (-)\r
-            watch_display_character('-', display_position);\r
-            break;\r
-        default: {\r
-            const char display_char = (value - MIN_CARD_VALUE) + '0';\r
-            watch_display_character(display_char, display_position);\r
-        }\r
-    }\r
-}\r
-\r
-static void render_board(void) {\r
-    for (size_t i = 0; i < GAME_BOARD_SIZE; ++i) {\r
-        render_board_position(i);\r
-    }\r
-}\r
-\r
-static void render_board_count(void) {\r
-    // Render completed boards (screens)\r
-    char buf[3] = {0};\r
-    snprintf(buf, sizeof(buf), "%2hhu", completed_board_count);\r
-    watch_display_string(buf, BOARD_SCORE_DISPLAY_START);\r
-}\r
-\r
-static void render_final_score(void) {\r
-    watch_display_string("SC", STATUS_DISPLAY_START);\r
-    char buf[7] = {0};\r
-    const uint8_t complete_boards = score / GUESSES_PER_SCREEN;\r
-    snprintf(buf, sizeof(buf), "%2hu %03hu", complete_boards, score);\r
-    watch_set_colon();\r
-    watch_display_string(buf, BOARD_DISPLAY_START);\r
-}\r
-\r
-static guess_t get_answer(void) {\r
-    if (guess_position < 1 || guess_position > GAME_BOARD_SIZE)\r
-        return HL_GUESS_EQUAL; // Maybe add an error state, shouldn't ever hit this.\r
-\r
-    game_board[guess_position].revealed = true;\r
-    const uint8_t previous_value = game_board[guess_position - 1].value;\r
-    const uint8_t current_value = game_board[guess_position].value;\r
-\r
-    if (current_value > previous_value)\r
-        return HL_GUESS_HIGHER;\r
-    else if (current_value < previous_value)\r
-        return HL_GUESS_LOWER;\r
-    else\r
-        return HL_GUESS_EQUAL;\r
-}\r
-\r
-static void do_game_loop(guess_t user_guess) {\r
-    switch (game_state) {\r
-        case HL_GS_TITLE_SCREEN:\r
-            init_game();\r
-            render_board();\r
-            render_board_count();\r
-            game_state = HL_GS_GUESSING;\r
-            break;\r
-        case HL_GS_GUESSING: {\r
-            const guess_t answer = get_answer();\r
-\r
-            // Render answer indicator\r
-            switch (answer) {\r
-                case HL_GUESS_EQUAL:\r
-                    watch_display_string("==", STATUS_DISPLAY_START);\r
-                    break;\r
-                case HL_GUESS_HIGHER:\r
-                    watch_display_string("HI", STATUS_DISPLAY_START);\r
-                    break;\r
-                case HL_GUESS_LOWER:\r
-                    watch_display_string("LO", STATUS_DISPLAY_START);\r
-                    break;\r
-            }\r
-\r
-            // Scoring\r
-            if (answer == user_guess) {\r
-                score++;\r
-            } else if (answer == HL_GUESS_EQUAL) {\r
-                // No score for two consecutive identical cards\r
-            } else {\r
-                // Incorrect guess, game over\r
-                watch_display_string("GO", STATUS_DISPLAY_START);\r
-                game_board[guess_position].revealed = true;\r
-                render_board_position(guess_position);\r
-                game_state = HL_GS_LOSE;\r
-                return;\r
-            }\r
-\r
-            if (score >= WIN_SCORE) {\r
-                // Win, perhaps some kind of animation sequence?\r
-                watch_display_string("WI", STATUS_DISPLAY_START);\r
-                watch_display_string("  ", BOARD_SCORE_DISPLAY_START);\r
-                watch_display_string("------", BOARD_DISPLAY_START);\r
-                game_state = HL_GS_WIN;\r
-                return;\r
-            }\r
-\r
-            // Next guess position\r
-            const bool final_board_guess = guess_position == GAME_BOARD_SIZE - 1;\r
-            if (final_board_guess) {\r
-                // Seed new board\r
-                completed_board_count++;\r
-                render_board_count();\r
-                guess_position = 1;\r
-                reset_board(false);\r
-                render_board();\r
-            } else {\r
-                guess_position++;\r
-                render_board_position(guess_position - 1);\r
-                render_board_position(guess_position);\r
-            }\r
-            break;\r
-        }\r
-        case HL_GS_WIN:\r
-        case HL_GS_LOSE:\r
-            // Show score screen on button press from either state\r
-            watch_clear_display();\r
-            render_final_score();\r
-            game_state = HL_GS_SHOW_SCORE;\r
-            break;\r
-        case HL_GS_SHOW_SCORE:\r
-            watch_clear_display();\r
-            watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);\r
-            watch_display_string("GA", STATUS_DISPLAY_START);\r
-            game_state = HL_GS_TITLE_SCREEN;\r
-            break;\r
-        default:\r
-            watch_display_string("ERROR", BOARD_DISPLAY_START);\r
-            break;\r
-    }\r
-}\r
-\r
-static void light_button_handler(void) {\r
-    do_game_loop(HL_GUESS_HIGHER);\r
-}\r
-\r
-static void alarm_button_handler(void) {\r
-    do_game_loop(HL_GUESS_LOWER);\r
-}\r
-\r
-void higher_lower_game_face_setup(uint8_t watch_face_index, void **context_ptr) {\r
-    (void) watch_face_index;\r
-\r
-    if (*context_ptr == NULL) {\r
-        *context_ptr = malloc(sizeof(higher_lower_game_face_state_t));\r
-        memset(*context_ptr, 0, sizeof(higher_lower_game_face_state_t));\r
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.\r
-        memset(game_board, 0, sizeof(game_board));\r
-    }\r
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.\r
-}\r
-\r
-void higher_lower_game_face_activate(void *context) {\r
-    higher_lower_game_face_state_t *state = (higher_lower_game_face_state_t *) context;\r
-    (void) state;\r
-    // Handle any tasks related to your watch face coming on screen.\r
-    game_state = HL_GS_TITLE_SCREEN;\r
-}\r
-\r
-bool higher_lower_game_face_loop(movement_event_t event, void *context) {\r
-    higher_lower_game_face_state_t *state = (higher_lower_game_face_state_t *) context;\r
-    (void) state;\r
-\r
-    switch (event.event_type) {\r
-        case EVENT_ACTIVATE:\r
-            // Show your initial UI here.\r
-            watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);\r
-            watch_display_string("GA", STATUS_DISPLAY_START);\r
-            break;\r
-        case EVENT_TICK:\r
-            // If needed, update your display here.\r
-            break;\r
-        case EVENT_LIGHT_BUTTON_UP:\r
-            light_button_handler();\r
-            break;\r
-        case EVENT_LIGHT_BUTTON_DOWN:\r
-            // Don't trigger light\r
-            break;\r
-        case EVENT_ALARM_BUTTON_UP:\r
-            alarm_button_handler();\r
-            break;\r
-        case EVENT_TIMEOUT:\r
-            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,\r
-            // you may uncomment this line to move back to the first watch face in the list:\r
-            // movement_move_to_face(0);\r
-            break;\r
-        default:\r
-            return movement_default_loop_handler(event);\r
-    }\r
-\r
-    // return true if the watch can enter standby mode. Generally speaking, you should always return true.\r
-    // Exceptions:\r
-    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.\r
-    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.\r
-    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or\r
-    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.\r
-    return true;\r
-}\r
-\r
-void higher_lower_game_face_resign(void *context) {\r
-    (void) context;\r
-\r
-    // handle any cleanup before your watch face goes off-screen.\r
-}\r
diff --git a/movement/watch_faces/complication/higher_lower_game_face.h b/movement/watch_faces/complication/higher_lower_game_face.h
deleted file mode 100755 (executable)
index 51689b5..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*\r
- * MIT License\r
- *\r
- * Copyright (c) 2023 Chris Ellis\r
- *\r
- * Permission is hereby granted, free of charge, to any person obtaining a copy\r
- * of this software and associated documentation files (the "Software"), to deal\r
- * in the Software without restriction, including without limitation the rights\r
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
- * copies of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be included in all\r
- * copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-#ifndef HIGHER_LOWER_GAME_FACE_H_\r
-#define HIGHER_LOWER_GAME_FACE_H_\r
-\r
-#include "movement.h"\r
-\r
-/*\r
- * Higher-Lower game face\r
- * ======================\r
- *\r
- * A game face based on the "higher-lower" card game where the objective is to correctly guess if the next card will\r
- * be higher or lower than the last revealed cards.\r
- *\r
- * Game Flow:\r
- * - When the face is selected, the "Hi-Lo" "Title" screen will be displayed, and the status indicator will display "GA" for game\r
- * - Pressing `ALARM` or `LIGHT` will start the game and proceed to the "Guessing" screen\r
- *   - The first card will be revealed and the player must now make a guess\r
- *   - A player can guess `Higher` by pressing the `LIGHT` button, and `Lower` by pressing the `ALARM` button\r
- *   - The status indicator will show the result of the guess: HI (Higher), LO (Lower), or == (Equal)\r
- *   - There are five guesses to make on each game screen, once the end of the screen is reached, a new screen\r
- *     will be started, with the last revealed card carried over\r
- *   - The number of completed screens is displayed in the top right (see Scoring)\r
- * - If the player has guessed correctly, the score is updated and play continues (see Scoring)\r
- * - If the player has guessed incorrectly, the status will change to GO (Game Over)\r
- *   - The current card will be revealed\r
- *   - Pressing `ALARM` or `LIGHT` will transition to the "Score" screen\r
- * - If the game is won, the status indicator will display "WI" and the "Win" screen will be displayed\r
- *   - Pressing `ALARM` or `LIGHT` will transition to the "Score" screen\r
- * - The status indicator will change to "SC" when the final score is displayed\r
- *   - The number of completed game screens will be displayed on using the first two digits\r
- *   - The number of correct guesses will be displayed using the final three digits\r
- *   - E.g. "13: 063" represents 13 completed screens, with 63 correct guesses\r
- * - Pressing `ALARM` or `LIGHT` while on the "Score" screen will transition to back to the "Title" screen\r
- *\r
- * Scoring:\r
- * - If the player guesses correctly (HI/LO) a point is gained\r
- * - If the player guesses incorrectly the game ends\r
- *   - Unless the revealed card is equal (==) to the last card, in which case play continues, but no point is gained\r
- * - If the player completes 40 screens full of cards, the game ends and a win screen is displayed\r
- *\r
- * Misc:\r
- * The face tries to remain true to the spirit of using "cards"; to cope with the display limitations I've arrived at\r
- * the following mapping of card values to screen display, but am open to better suggestions:\r
- *\r
- * Thanks to voloved for adding deck shuffling and drawing!\r
- *\r
- * | Cards   |                          |\r
- * |---------|--------------------------|\r
- * | Value   |2|3|4|5|6|7|8|9|10|J|Q|K|A|\r
- * | Display |0|1|2|3|4|5|6|7|8 |9|-|=|≡|\r
- *\r
- * A previous alternative can be found in the git history:\r
- * | Cards   |                          |\r
- * |---------|--------------------------|\r
- * | Value   |2|3|4|5|6|7|8|9|10|J|Q|K|A|\r
- * | Display |2|3|4|5|6|7|8|9| 0|-|=|≡|H|\r
- *\r
- *\r
- * Future Ideas:\r
- * - Add sounds\r
- * - Save/Display high score\r
- * - Add a "Win" animation\r
- * - Consider using lap indicator for larger score limit\r
- */\r
-\r
-typedef struct {\r
-    // Anything you need to keep track of, put it here!\r
-} higher_lower_game_face_state_t;\r
-\r
-void higher_lower_game_face_setup(uint8_t watch_face_index, void ** context_ptr);\r
-void higher_lower_game_face_activate(void *context);\r
-bool higher_lower_game_face_loop(movement_event_t event, void *context);\r
-void higher_lower_game_face_resign(void *context);\r
-\r
-#define higher_lower_game_face ((const watch_face_t){ \\r
-    higher_lower_game_face_setup, \\r
-    higher_lower_game_face_activate, \\r
-    higher_lower_game_face_loop, \\r
-    higher_lower_game_face_resign, \\r
-    NULL, \\r
-})\r
-\r
-#endif // HIGHER_LOWER_GAME_FACE_H_\r
diff --git a/movement/watch_faces/complication/interval_face.c b/movement/watch_faces/complication/interval_face.c
deleted file mode 100644 (file)
index c354e72..0000000
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Andreas Nebinger
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "interval_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "watch_private_display.h"
-#include "watch_buzzer.h"
-
-typedef enum {
-    interval_setting_0_timer_idx,
-    interval_setting_1_clear_yn,
-    interval_setting_2_warmup_minutes,
-    interval_setting_3_warmup_seconds,
-    interval_setting_4_work_minutes,
-    interval_setting_5_work_seconds,
-    interval_setting_6_work_rounds,
-    interval_setting_7_break_minutes,
-    interval_setting_8_break_seconds,
-    interval_setting_9_full_rounds,
-    interval_setting_10_cooldown_minutes,
-    interval_setting_11_cooldown_seconds,
-    interval_setting_max
-} interval_setting_idx_t;
-
-#define INTERVAL_FACE_STATE_DEFAULT "IT"    // Interval Timer
-#define INTERVAL_FACE_STATE_WARMUP "PR"     // PRepare / warm up
-#define INTERVAL_FACE_STATE_WORK "WO"       // WOrk
-#define INTERVAL_FACE_STATE_BREAK "BR"      // BReak
-#define INTERVAL_FACE_STATE_COOLDOWN "CD"   // CoolDown
-
-// Define some default timer settings. Each timer is described in an array like this: 
-//      1. warm-up seconds,
-//      2. work time (seconds/minutes)
-//      3. break time (seconds/minutes)
-//      4. full rounds (0 = no limit)
-//      5. cooldown seconds
-// Work time and break time: positive number = seconds, negative number = minutes
-static const int8_t _default_timers[6][5] = {{0, 40, 20, 0, 0},
-                                            {0, 45, 15, 0, 0},
-                                            {10, 20, 10, 8, 10},
-                                            {0, 35, 0, 0, 0},
-                                            {0, -25, -5, 0, 0},
-                                            {0, -20, -5, 0, 0}};
-
-static const uint8_t _intro_segdata[4][2] = {{1, 8}, {0, 8}, {0, 7}, {1, 7}};
-static const uint8_t _blink_idx[] = {3, 9, 4, 6, 4, 6, 8, 4, 6, 8, 4, 6};
-static const uint8_t _setting_page_idx[] = {1, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4};
-static const int8_t _sound_seq_warmup[] = {BUZZER_NOTE_F6, 8, BUZZER_NOTE_REST, 1, -2, 3, 0};
-static const int8_t _sound_seq_work[] = {BUZZER_NOTE_F6, 8, BUZZER_NOTE_REST, 1, -2, 2, BUZZER_NOTE_C7, 24, 0};
-static const int8_t _sound_seq_break[] = {BUZZER_NOTE_B6, 15, BUZZER_NOTE_REST, 1, -2, 1, BUZZER_NOTE_B6, 16, 0};
-static const int8_t _sound_seq_cooldown[] = {BUZZER_NOTE_C7, 15, BUZZER_NOTE_REST, 1, -2, 1, BUZZER_NOTE_C7, 24, 0};
-static const int8_t _sound_seq_finish[] = {BUZZER_NOTE_C7, 6, BUZZER_NOTE_E7, 6, BUZZER_NOTE_G7, 6, BUZZER_NOTE_C8, 18, 0};
-
-static interval_setting_idx_t _setting_idx;
-static int8_t _ticks;
-static bool _erase_timer_flag;
-static uint32_t _target_ts;
-static uint32_t _now_ts;
-static uint32_t _paused_ts;
-static uint8_t _timer_work_round;
-static uint8_t _timer_full_round;
-static uint8_t _timer_run_state;
-
-static inline void _inc_uint8(uint8_t *value, uint8_t step, uint8_t max) {
-    *value += step;
-    if (*value >= max) *value = 0;
-}
-
-static uint32_t _get_now_ts() {
-    // returns the current date time as unix timestamp
-    watch_date_time_t now = watch_rtc_get_date_time();
-    return watch_utility_date_time_to_unix_time(now, 0);
-}
-
-static inline void _button_beep() {
-    // play a beep as confirmation for a button press (if applicable)
-    if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
-}
-
-static void _timer_write_info(interval_face_state_t *state, char *buf, uint8_t timer_page) {
-    // fill display string with requested timer information
-    switch (timer_page) {
-    case 0:
-        // clear timer?
-        sprintf(buf, "%2s %1dCLEARn", INTERVAL_FACE_STATE_DEFAULT, state->timer_idx + 1);
-        if (_erase_timer_flag) buf[9] = 'y';
-        watch_clear_colon();
-        break;
-    case 1:
-        // warmup time info
-        sprintf(buf, "%2s %1d%02d%02d  ", INTERVAL_FACE_STATE_WARMUP, state->timer_idx + 1, 
-            state->timer[state->timer_idx].warmup_minutes, 
-            state->timer[state->timer_idx].warmup_seconds);
-        break;
-    case 2:
-        // work interval info
-        sprintf(buf, "%2s %1d%02d%02d%2d", INTERVAL_FACE_STATE_WORK, state->timer_idx + 1, 
-            state->timer[state->timer_idx].work_minutes, 
-            state->timer[state->timer_idx].work_seconds,
-            state->timer[state->timer_idx].work_rounds);
-        break;
-    case 3:
-        // break interval info
-        sprintf(buf, "%2s %1d%02d%02d%2d", INTERVAL_FACE_STATE_BREAK, state->timer_idx + 1, 
-            state->timer[state->timer_idx].break_minutes, 
-            state->timer[state->timer_idx].break_seconds,
-            state->timer[state->timer_idx].full_rounds);
-        if (!state->timer[state->timer_idx].full_rounds) buf[9] = '-';
-        break;
-    case 4:
-        // cooldown time info
-        sprintf(buf, "%2s %1d%02d%02d  ", INTERVAL_FACE_STATE_COOLDOWN ,state->timer_idx + 1, 
-            state->timer[state->timer_idx].cooldown_minutes, 
-            state->timer[state->timer_idx].cooldown_seconds);
-        break;
-    default:
-        break;
-    }
-}
-
-static void _face_draw(interval_face_state_t *state, uint8_t subsecond) {
-    // draws current face state
-    if (!state->is_active) return;
-    char buf[14];
-    buf[0] = 0;
-    uint8_t tmp;
-    if (state->face_state == interval_state_waiting && _ticks >= 0) {
-        // play info slideshow for current timer
-        int8_t ticks = _ticks % 12;
-        if (ticks == 0) {
-            if ((state->timer[state->timer_idx].warmup_minutes + state->timer[state->timer_idx].warmup_seconds) == 0) {
-                // skip warmup info if there is none for this timer
-                ticks = 3;
-                _ticks += 3;
-            }
-        }
-        tmp = ticks / 3 + 1;
-        _timer_write_info(state, buf, tmp);
-        // don't show '1 round' when displaying workout time to avoid detail overload
-        if (tmp == 2 && state->timer[state->timer_idx].work_rounds == 1) buf[9] = ' ';
-        // blink colon
-        if (subsecond % 2 == 0 && _ticks < 24) watch_clear_colon();
-        else watch_set_colon();
-    } else if (state->face_state == interval_state_setting) {
-        if (_setting_idx == interval_setting_0_timer_idx) {
-            if ((state->timer[state->timer_idx].warmup_minutes + state->timer[state->timer_idx].warmup_seconds) == 0)
-                tmp = 1;
-            else
-                tmp = 2;
-        } else {
-            tmp = _setting_page_idx[_setting_idx];
-        }
-        _timer_write_info(state, buf, tmp);
-        // blink at cursor position
-        if (subsecond % 2 && _ticks != -2) {
-            buf[_blink_idx[_setting_idx]] = ' ';
-            if (_blink_idx[_setting_idx] % 2 == 0) buf[_blink_idx[_setting_idx] + 1] = ' ';
-        }
-        // show lap indicator only when rounds are set
-        if (_setting_idx == interval_setting_6_work_rounds || _setting_idx == interval_setting_9_full_rounds)
-            watch_set_indicator(WATCH_INDICATOR_LAP);
-        else 
-            watch_clear_indicator(WATCH_INDICATOR_LAP);
-    } else if (state->face_state == interval_state_running || state->face_state == interval_state_pausing) {
-        tmp = _timer_full_round;
-        switch (_timer_run_state) {
-        case 0:
-            sprintf(buf, INTERVAL_FACE_STATE_WARMUP);
-            break;
-        case 1:
-            sprintf(buf, INTERVAL_FACE_STATE_WORK);
-            if (state->timer[state->timer_idx].work_rounds > 1) tmp = _timer_work_round;
-            break;
-        case 2:
-            sprintf(buf, INTERVAL_FACE_STATE_BREAK);
-            break;
-        case 3:
-            sprintf(buf, INTERVAL_FACE_STATE_COOLDOWN);
-            break;
-        default:
-            break;
-        }
-        div_t delta;
-        
-        if (state->face_state == interval_state_pausing) {
-            // pausing
-            delta = div(_target_ts - _paused_ts, 60);
-            // blink the bell icon
-            if (_now_ts % 2) watch_set_indicator(WATCH_INDICATOR_BELL);
-            else watch_clear_indicator(WATCH_INDICATOR_BELL);
-        } else
-            // running
-            delta = div(_target_ts - _now_ts, 60);
-        sprintf(&buf[2], " %1d%02d%02d%2d", state->timer_idx + 1, delta.quot, delta.rem, tmp + 1);
-    }
-    // write out to lcd
-    if (buf[0]) {
-        watch_display_character(buf[0], 0);
-        watch_display_character(buf[1], 1);
-        // set the bar for the i-like symbol on position 2
-        watch_set_pixel(2, 9);
-        // display the rest of the string
-        watch_display_string(&buf[3], 3);
-    }
-}
-
-static void _initiate_setting(interval_face_state_t *state, uint8_t subsecond) {
-    state->face_state = interval_state_setting;
-    _setting_idx = interval_setting_0_timer_idx;
-    _ticks = 0;
-    _erase_timer_flag = false;
-    watch_set_colon();
-    movement_request_tick_frequency(4);
-    _face_draw(state, subsecond);
-}
-
-static void _resume_setting(interval_face_state_t *state, uint8_t subsecond) {
-    state->face_state = interval_state_waiting;
-    _ticks = 0;
-    _face_draw(state, subsecond);
-    movement_request_tick_frequency(2);
-    watch_clear_indicator(WATCH_INDICATOR_LAP);
-}
-
-static void _abort_quick_ticks() {
-    if (_ticks == -2) {
-        _ticks = -1;
-        movement_request_tick_frequency(4);
-    }
-}
-
-static void _handle_alarm_button(interval_face_state_t *state) {
-    // handles the alarm button press and alters the corresponding timer settings
-    switch (_setting_idx) {
-    case interval_setting_0_timer_idx:
-        _inc_uint8(&state->timer_idx, 1, INTERVAL_TIMERS);
-        _erase_timer_flag = false;
-        break;
-    case interval_setting_1_clear_yn:
-        _erase_timer_flag ^= 1;
-        break;
-    case interval_setting_2_warmup_minutes:
-        _inc_uint8(&state->timer[state->timer_idx].warmup_minutes, 1, 60);
-        break;
-    case interval_setting_3_warmup_seconds:
-        _inc_uint8(&state->timer[state->timer_idx].warmup_seconds, 5, 60);
-        break;
-    case interval_setting_4_work_minutes:
-        _inc_uint8(&state->timer[state->timer_idx].work_minutes, 1, 60);
-        if (state->timer[state->timer_idx].work_rounds == 0) state->timer[state->timer_idx].work_rounds = 1;
-        break;
-    case interval_setting_5_work_seconds:
-        _inc_uint8(&state->timer[state->timer_idx].work_seconds, 5, 60);
-        if (state->timer[state->timer_idx].work_rounds == 0) state->timer[state->timer_idx].work_rounds = 1;
-        break;
-    case interval_setting_6_work_rounds:
-        _inc_uint8(&state->timer[state->timer_idx].work_rounds, 1, 100);
-        break;
-    case interval_setting_7_break_minutes:
-        _inc_uint8(&state->timer[state->timer_idx].break_minutes, 1, 60);
-        break;
-    case interval_setting_8_break_seconds:
-        _inc_uint8(&state->timer[state->timer_idx].break_seconds, 5, 60);
-        break;
-    case interval_setting_9_full_rounds:
-        _inc_uint8(&state->timer[state->timer_idx].full_rounds, 1, 100);
-        break;
-    case interval_setting_10_cooldown_minutes:
-        _inc_uint8(&state->timer[state->timer_idx].cooldown_minutes, 1, 60);
-        break;
-    case interval_setting_11_cooldown_seconds:
-        _inc_uint8(&state->timer[state->timer_idx].cooldown_seconds, 5, 60);
-        break;
-    default:
-        break;
-    }
-}
-
-static void _set_next_timestamp(interval_face_state_t *state) {
-    // set next timestamp for the running timer, set background task and pay sound sequence
-    uint16_t delta = 0;
-    int8_t *sound_seq;
-    interval_timer_setting_t timer = state->timer[state->timer_idx];
-    switch (_timer_run_state) {
-    case 0:
-        delta = timer.warmup_minutes * 60 + timer.warmup_seconds;
-        sound_seq = (int8_t *)_sound_seq_warmup;
-        break;
-    case 1:
-        delta = timer.work_minutes * 60 + timer.work_seconds;
-        sound_seq = (int8_t *)_sound_seq_work;
-        break;
-    case 2:
-        delta = timer.break_minutes * 60 + timer.break_seconds;
-        sound_seq = (int8_t *)_sound_seq_break;
-        break;
-    case 3:
-        delta = timer.cooldown_minutes * 60 + timer.cooldown_seconds;
-        sound_seq = (int8_t *)_sound_seq_cooldown;
-        break;
-    default:
-        sound_seq = NULL;
-        break;
-    }
-    // failsafe
-    if (delta <= 0) delta = 1;
-    _target_ts += delta;
-    // schedule next background task
-    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(_target_ts, 0);
-    movement_schedule_background_task_for_face(state->face_idx, target_dt);
-    // play sound
-    watch_buzzer_play_sequence(sound_seq, NULL);
-}
-
-static inline bool _is_timer_empty(interval_timer_setting_t *timer) {
-    // checks if a timer is empty
-    return (timer->warmup_minutes + timer->warmup_seconds 
-        + timer->work_minutes + timer->work_seconds 
-        + timer->break_minutes + timer->break_seconds
-        + timer->cooldown_minutes + timer->cooldown_seconds == 0);
-}
-
-static void _init_timer_info(interval_face_state_t *state) {
-    state->face_state = interval_state_waiting;
-    _ticks = 0;
-    if (state->is_active) movement_request_tick_frequency(2);
-}
-
-static void _abort_running_timer() {
-    _timer_work_round = _timer_full_round = 0;
-    _timer_run_state = 0;
-    movement_cancel_background_task();
-    watch_clear_indicator(WATCH_INDICATOR_BELL);
-    watch_buzzer_play_note(BUZZER_NOTE_C8, 100);
-}
-
-static void _resume_paused_timer(interval_face_state_t *state) {
-    // resume paused timer
-    _now_ts = _get_now_ts();
-    _target_ts += _now_ts - _paused_ts;
-    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(_target_ts, 0);
-    movement_schedule_background_task_for_face(state->face_idx, target_dt);
-    state->face_state = interval_state_running;
-    watch_set_indicator(WATCH_INDICATOR_BELL);
-}
-
-void interval_face_setup(uint8_t watch_face_index, void **context_ptr) {
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(interval_face_state_t));
-        interval_face_state_t *state = (interval_face_state_t *)*context_ptr;
-        memset(*context_ptr, 0, sizeof(interval_face_state_t));
-        state->face_idx = watch_face_index;
-        // somehow the memset above doesn't to the trick. So set the state explicitly
-        state->face_state = interval_state_waiting;
-        for (uint8_t i = 0; i < INTERVAL_TIMERS; i++) state->timer[i].work_rounds = 1;
-        // set up default timers
-        for (uint8_t i = 0; i < 6; i++) {
-            state->timer[i].warmup_seconds = _default_timers[i][0];
-            if (_default_timers[i][1] < 0) state->timer[i].work_minutes = -_default_timers[i][1];
-            else state->timer[i].work_seconds = _default_timers[i][1];
-            state->timer[i].work_rounds = 1;
-            if (_default_timers[i][2] < 0) state->timer[i].break_minutes = -_default_timers[i][2];
-            else state->timer[i].break_seconds = _default_timers[i][2];
-            state->timer[i].full_rounds = _default_timers[i][3];
-            state->timer[i].cooldown_seconds = _default_timers[i][4];
-        }
-    }
-}
-
-void interval_face_activate(void *context) {
-    interval_face_state_t *state = (interval_face_state_t *)context;
-    _erase_timer_flag = false;
-    state->is_active = true;
-    if (state->face_state <= interval_state_waiting) {
-        // initiate the intro loop
-        state->face_state = interval_state_intro;
-        _ticks = 0;
-        movement_request_tick_frequency(8);
-    } else watch_set_colon();
-}
-
-void interval_face_resign(void *context) {
-    interval_face_state_t *state = (interval_face_state_t *)context;
-    if (state->face_state <= interval_state_setting) state->face_state = interval_state_waiting;
-    watch_set_led_off();
-    movement_request_tick_frequency(1);
-    state->is_active = false;
-}
-
-bool interval_face_loop(movement_event_t event, void *context) {
-    interval_face_state_t *state = (interval_face_state_t *)context;
-    interval_timer_setting_t *timer = &state->timer[state->timer_idx];
-
-    switch (event.event_type) {
-    case EVENT_TICK:
-        if (state->face_state == interval_state_intro) {
-            // play intro animation so the wearer knows the face
-            if (_ticks == 4) {
-                // transition to default view of current interval slot
-                watch_set_colon();
-                _init_timer_info(state);
-                _face_draw(state, event.subsecond);
-                break;
-            } 
-            watch_set_pixel(_intro_segdata[_ticks][0], _intro_segdata[_ticks][1]);
-            _ticks++;
-        } else if (state->face_state == interval_state_waiting && _ticks >= 0) {
-            // play information slideshow for current interval timer
-            _ticks++;
-            if ((_ticks % 12 == 9) && (timer->cooldown_minutes + timer->cooldown_seconds == 0)) _ticks += 3;
-            if (_ticks > 24) _ticks = -1;
-            else _face_draw(state, event.subsecond);
-        } else if (state->face_state == interval_state_setting) {
-            if (_ticks == -2) {
-                // fast counting
-                _handle_alarm_button(state);
-            }
-            _face_draw(state, event.subsecond);
-        } else if (state->face_state == interval_state_running || state->face_state == interval_state_pausing) {
-            _now_ts = _get_now_ts();
-            _face_draw(state, event.subsecond);
-        }
-        break;
-    case EVENT_ACTIVATE:
-        watch_display_string(INTERVAL_FACE_STATE_DEFAULT, 0);
-        if (state->face_state) _face_draw(state, event.subsecond);
-        break;
-    case EVENT_LIGHT_BUTTON_UP:
-        if (state->face_state == interval_state_setting) {
-            if (_setting_idx == interval_setting_0_timer_idx) {
-                // skip clear page if timer is empty
-                if (_is_timer_empty(timer)) _setting_idx = interval_setting_1_clear_yn;
-            } else if (_setting_idx == interval_setting_1_clear_yn) {
-                watch_set_colon();
-                if (_erase_timer_flag) {
-                    // clear the current timer
-                    memset((void *)timer, 0, sizeof(interval_timer_setting_t));
-                    // play a short beep as confirmation
-                    watch_buzzer_play_note(BUZZER_NOTE_C8, 70);
-                }
-            } else if (_setting_idx == interval_setting_9_full_rounds && !timer->full_rounds) {
-                // skip cooldown if full rounds are not limited
-                _setting_idx = interval_setting_11_cooldown_seconds;
-            }
-            _setting_idx += 1;
-            if (_setting_idx == interval_setting_max) {
-                // we have done a full settings circle: resume setting
-                _resume_setting(state, event.subsecond);
-            } else 
-                _face_draw(state, event.subsecond);
-        } else {
-            movement_illuminate_led();
-        }
-        break;
-    case EVENT_LIGHT_LONG_PRESS:
-        _button_beep(settings);
-        if (state->face_state == interval_state_setting) {
-            _resume_setting(state, event.subsecond);
-        } else {
-            if (state->face_state >= interval_state_running ) _abort_running_timer();
-            _initiate_setting(state, event.subsecond);
-        }
-        break;
-    case EVENT_ALARM_BUTTON_UP:
-        switch (state->face_state) {
-        case interval_state_waiting:
-            // cycle through timers
-            _inc_uint8(&state->timer_idx, 1, INTERVAL_TIMERS);
-            _ticks = 0;
-            _face_draw(state, event.subsecond);
-            break;
-        case interval_state_setting:
-            // alter timer settings
-            _abort_quick_ticks();
-            _handle_alarm_button(state);
-            break;
-        case interval_state_running:
-            // pause timer
-            _button_beep(settings);
-            _paused_ts = _get_now_ts();
-            state->face_state = interval_state_pausing;
-            movement_cancel_background_task();
-            _face_draw(state, event.subsecond);
-            break;
-        case interval_state_pausing:
-            // resume paused timer
-            _button_beep(settings);
-            _resume_paused_timer(state);
-            _face_draw(state, event.subsecond);
-            break;
-        default:
-            break;
-        }
-        break;
-    case EVENT_ALARM_LONG_PRESS:
-        if (state->face_state == interval_state_setting && _setting_idx != interval_setting_1_clear_yn) {
-            // initiate quick counting
-            _ticks = -2;
-            movement_request_tick_frequency(8);
-            break;
-        } else if (state->face_state <= interval_state_waiting) {
-            if (_is_timer_empty(timer)) {
-                // jump back to timer #1
-                _button_beep(settings);
-                state->timer_idx = 0;
-                _init_timer_info(state);
-            } else {
-                // set initial state and start timer
-                _timer_work_round = _timer_full_round = 0;
-                if (timer->warmup_minutes + timer->warmup_seconds) _timer_run_state = 0;
-                else if (timer->work_minutes + timer->work_seconds) _timer_run_state = 1;
-                else if (timer->break_minutes + timer->break_seconds) _timer_run_state = 2;
-                else if (timer->cooldown_minutes + timer->cooldown_seconds) _timer_run_state = 3;
-                movement_request_tick_frequency(1);
-                _now_ts = _get_now_ts();
-                _target_ts = _now_ts;
-                _set_next_timestamp(state);
-                state->face_state = interval_state_running;
-                watch_set_indicator(WATCH_INDICATOR_BELL);
-                watch_set_colon();
-            }
-        } else if (state->face_state == interval_state_running) {
-            // stop the timer
-            _abort_running_timer();
-            _init_timer_info(state);
-        } else if (state->face_state == interval_state_pausing) {
-            // resume paused timer
-            _button_beep(settings);
-            _resume_paused_timer(state);
-        }
-        _face_draw(state, event.subsecond);
-        break;
-    case EVENT_ALARM_LONG_UP:
-        _abort_quick_ticks();
-        break;
-    case EVENT_BACKGROUND_TASK:
-        // find the next timestamp or end the timer
-        if (_timer_run_state == 0) {
-            // warmup finished
-            if (timer->work_minutes + timer->work_seconds) _timer_run_state = 1;
-            else if (timer->break_minutes + timer->break_seconds) _timer_run_state = 2;
-            else if (timer->cooldown_minutes + timer->cooldown_seconds) _timer_run_state = 3;
-            else _timer_run_state = 4;
-        } else if (_timer_run_state == 1) {
-            // work finished
-            _timer_work_round++;
-            if (_timer_work_round == timer->work_rounds) {
-                _timer_work_round = 0;
-                if (timer->break_minutes + timer->break_seconds && (timer->full_rounds == 0 
-                    || (timer->full_rounds && _timer_full_round + 1 < timer->full_rounds))) _timer_run_state = 2;
-                else {
-                    _timer_full_round++;
-                    if (timer->full_rounds && _timer_full_round == timer->full_rounds) {
-                        if (timer->cooldown_minutes + timer->cooldown_seconds) _timer_run_state = 3;
-                        else _timer_run_state = 4;
-                    } else _timer_run_state = 1;
-                }
-            }
-        } else if (_timer_run_state == 2) {
-            // break finished
-            _timer_full_round++;
-            _timer_work_round = 0;
-            if (timer->full_rounds && _timer_full_round == timer->full_rounds) {
-                if (timer->cooldown_minutes + timer->cooldown_seconds) _timer_run_state = 3;
-                else _timer_run_state = 4;
-                _timer_full_round--;
-            } else {
-                if (timer->work_minutes + timer->work_seconds) _timer_run_state = 1;
-            }
-        } else if (_timer_run_state == 3)
-            // cooldown finished
-            _timer_run_state = 4;
-        // set next timestamp or play final sound sequence
-        if (_timer_run_state < 4) {
-            // transition to next timer phase
-            _set_next_timestamp(state);
-        } else {
-            // timer has finished
-            state->face_state = interval_state_waiting;
-            _init_timer_info(state);
-            _face_draw(state, event.subsecond);
-            watch_buzzer_play_sequence((int8_t *)_sound_seq_finish, NULL);
-        }
-        break;
-    case EVENT_TIMEOUT:
-        if (state->face_state != interval_state_running) movement_move_to_face(0);
-        break;
-    case EVENT_LIGHT_BUTTON_DOWN:
-        // don't light up every time light is hit
-        break;
-    default:
-        movement_default_loop_handler(event);
-        break;
-    }
-    return true;
-}
diff --git a/movement/watch_faces/complication/interval_face.h b/movement/watch_faces/complication/interval_face.h
deleted file mode 100644 (file)
index def944b..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Andreas Nebinger
- *
- * 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 INTERVAL_FACE_H_
-#define INTERVAL_FACE_H_
-
-/*
- * INTERVAL TIMER face
- *
- * This face brings 9 customizable interval timers to the sensor watch,
- * to be used as hiit training device and/or for time management techniques.
- *
- * - There are 9 interval timer slots, you can cycle through these with the
- *   alarm button (short press). For each timer slot, a short "slideshow"
- *   displaying the relevant details (like length of each phase - see below)
- *   is shown.
- *
- * - To start an interval timer, press and hold the alarm button.
- *
- * - To pause a running timer, press the alarm button (short press).
- *
- * - To completely abort a running timer, press and hold the alarm button.
- *
- * - Press and hold the light button to enter settings mode for each interval
- *   timer slot.
- *
- * - Each interval timer has 1 to 4 phases of customizable length like so:
- *   (1) prepare/warum up --> (2) work --> (3) break --> (4) cool down.
- *   When setting up or running a timer, each of these phases is displayed by
- *   the letters "PR" (prepare), "WO" (work), "BR" (break), "CD" (cool down).
- *
- * - Each of these phases is optional, you can set the corresponding
- *   minutes and seconds to zero. But at least one phase needs to be set, if
- *   you want to use the timer.
- *
- * - You can define the number of rounds either only for the work
- *   phase and/or for the combination of work + break phase. Let's say you
- *   want an interval timer that counts 3 rounds of 30 seconds work, 
- *   followed by 20 seconds rest:
- *         work 30s --> work 30s --> work 30s --> break 20s
- *   You can do this by setting 30s for the "WO"rk phase and setting a 3
- *   in the lower right hand corner of the work page. The "LAP" indicator
- *   lights up at this position, to explain that we are setting laps here.
- *   After that, set the "BR"eak phase to 20s and leave the rest as it is.
- *
- * - If you want to set up a certain number of "full rounds", consisting
- *   of work phase(s) plus breaks, you can do so at the "BR"eak page. The
- *   number in the lower right hand corner determines the number of full
- *   rounds to be counted. A "-" means, that there is no limit and the 
- *   timer keeps alternating between work and break phases.
- *
- * - This watch face comes with several pre-defined interval timers,
- *   suitable for hiit training (timer slots 1 to 4) as well as doing
- *   work according to the pomodoro principle (timer slots 5 to 6).
- *   Feel free to adjust the timer slots to your own needs (or completely 
- *   wipe them ;-)
- */
-
-#include "movement.h"
-
-#define INTERVAL_TIMERS 9     // no of available customizable timers (be aware: only 4 bits reserved for this value in struct below)
-
-typedef struct {
-    uint8_t warmup_minutes;
-    uint8_t warmup_seconds;
-    uint8_t work_minutes;
-    uint8_t work_seconds;
-    uint8_t break_minutes;
-    uint8_t break_seconds;
-    uint8_t cooldown_minutes;
-    uint8_t cooldown_seconds;
-    uint8_t work_rounds;
-    uint8_t full_rounds;
-} interval_timer_setting_t;
-
-typedef enum {
-    interval_state_intro,
-    interval_state_waiting,
-    interval_state_setting,
-    interval_state_running,
-    interval_state_pausing
-} interval_timer_state_t;
-
-typedef struct {
-    bool is_active;
-    uint8_t face_idx;
-    uint8_t timer_idx;
-    uint8_t timer_running_idx;
-    interval_timer_state_t face_state;
-    interval_timer_setting_t timer[INTERVAL_TIMERS];
-} interval_face_state_t;
-
-void interval_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void interval_face_activate(void *context);
-bool interval_face_loop(movement_event_t event, void *context);
-void interval_face_resign(void *context);
-
-#define interval_face ((const watch_face_t) { \
-    interval_face_setup, \
-    interval_face_activate, \
-    interval_face_loop, \
-    interval_face_resign, \
-    NULL \
-})
-
-#endif // INTERVAL_FACE_H_
diff --git a/movement/watch_faces/complication/invaders_face.c b/movement/watch_faces/complication/invaders_face.c
deleted file mode 100644 (file)
index 935926a..0000000
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Andreas Nebinger
- *
- * 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.
- */
-
-// Emulator only: need time() to seed the random number generator
-#if __EMSCRIPTEN__
-#include <time.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include "watch_private_display.h"
-#include "invaders_face.h"
-
-#define INVADERS_FACE_WAVES_PER_STAGE 9 // number of waves per stage (there are two stages)
-#define INVADERS_FACE_WAVE_INVADERS 16  // number of invaders attacking per wave
-
-static const uint8_t _defense_lines_segdata[3][2] = {{2, 12}, {2, 11}, {0, 11}};
-static const uint8_t _bonus_points_segdata[4][2] = {{2, 7}, {2, 8}, {2, 9}, {0, 10}};
-static const uint8_t _bonus_points_helper[] = {1, 5, 9, 11, 15, 19, 21, 25, 29};
-
-static const int8_t _sound_seq_game_start[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 1, BUZZER_NOTE_REST, 10, BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 1, 0};
-static const int8_t _sound_seq_shot_hit[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 2, 0};
-static const int8_t _sound_seq_shot_miss[] = {BUZZER_NOTE_A7, 1, 0};
-static const int8_t _sound_seq_ufo_hit[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 2, -2, 1, 0};
-static const int8_t _sound_seq_def_gone[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 3, BUZZER_NOTE_REST, 40, BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 4, 0};
-static const int8_t _sound_seq_next_wave[] = {BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, BUZZER_NOTE_REST, 8, BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, -2, 1,
-                                              BUZZER_NOTE_REST, 32, 
-                                              BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, BUZZER_NOTE_REST, 8, BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, -2, 1, 0};
-static const int8_t _sound_seq_game_over[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 11, 0};
-
-typedef enum {
-    invaders_state_activated,
-    invaders_state_pre_game,
-    invaders_state_playing,
-    invaders_state_in_wave_break,
-    invaders_state_pre_next_wave,
-    invaders_state_next_wave,
-    invaders_state_game_over
-} invaders_current_state_t;
-
-typedef struct {
-    bool ufo_next : 1;                  // indicates whether next invader is a ufo
-    bool inv_checking : 1;              // flag to indicate whether we are currently moving invaders (to prevent race conditions)
-    bool suspend_buttons : 1;           // used while playing the game over sequence to prevent involuntary immediate restarts
-} invaders_signals_t;
-
-static int8_t _invaders[6];             // array of current invaders values (-1 = empty, 10 = ufo)
-static uint8_t _wave_invaders[INVADERS_FACE_WAVE_INVADERS]; // all invaders for the current wave. (Predefined to save cpu cycles when playing.)
-static invaders_current_state_t _current_state;
-static uint8_t _defense_lines;          // number of defense lines which have been broken in the current wave
-static uint8_t _aim;                    // current "aim" digit
-static uint8_t _invader_idx;            // index of next invader attacking in current wave (0 to 15)
-static uint8_t _wave_position;          // current position of first invader. When > 6 the defense is broken
-static uint8_t _wave_tick_freq;         // number of ticks passing until the next invader is inserted
-static uint8_t _ticks;                  // counts the ticks
-static uint8_t _bonus_countdown;        // ticks countdown until the bonus point indicator is cleared
-static uint8_t _waves;                  // counts the waves (_wave_tick_freq decreases slowly depending on _wave value)
-static uint8_t _shots_in_wave;          // number of shots in current wave. If 30 is reached, the game is over
-static uint8_t _invaders_shot;          // number of sucessfully shot invaders in current wave
-static uint8_t _invaders_shot_sum;      // current sum of invader digits shot (needed to determine if a ufo is coming)
-static invaders_signals_t _signals;     // holds severals flags
-static uint16_t _score;                 // score of the current game
-
-/// @brief return a random number. 0 <= return_value < num_values
-static inline uint8_t _get_rand_num(uint8_t num_values) {
-#if __EMSCRIPTEN__
-    return rand() % num_values;
-#else
-    return arc4random_uniform(num_values);
-#endif
-}
-
-/// @brief callback function to re-enable light and alarm buttons after playing a sound sequence
-static inline void _resume_buttons() {
-    _signals.suspend_buttons = false;
-}
-
-/// @brief play a sound sequence if the game is in beepy mode
-static inline void _play_sequence(invaders_state_t *state, int8_t *sequence) {
-    if (state->sound_on) watch_buzzer_play_sequence((int8_t *)sequence, NULL);
-}
-
-/// @brief draw the remaining defense lines
-static void _display_defense_lines() {
-    watch_display_character(' ', 1);
-    for (uint8_t i = 0; i < 3 - _defense_lines; i++) watch_set_pixel(_defense_lines_segdata[i][0], _defense_lines_segdata[i][1]);
-}
-
-/** @brief draw label followed by the given score value 
- * @param label string displayed in the upper left corner
- * @param score score to display
- */
-static void _display_score(char *label, uint16_t score) {
-    watch_display_character(label[0], 0);
-    watch_display_character(label[1], 1);
-    char buf[10];
-    sprintf(buf, "  %06d", (score * 10));
-    watch_display_string(buf, 2);
-}
-
-/// @brief draw an invader at the given position
-static inline void _display_invader(int8_t invader, uint8_t position) {
-    switch (invader) {
-    case 10:
-        watch_display_character('n', position);
-        break;
-    case -1:
-        watch_display_character(' ', position);
-        break;
-    default:
-        watch_display_character(invader + 48, position);
-        break;
-    }
-}
-
-/// @brief game over: show score and set state
-static void _game_over(invaders_state_t *state) {
-    _display_score("GO", _score);
-    _current_state = invaders_state_game_over;
-    movement_request_tick_frequency(1);
-    _signals.suspend_buttons = true;
-    if (state->sound_on) watch_buzzer_play_sequence((int8_t *)_sound_seq_game_over, _resume_buttons);
-    // save current score to highscore, if applicable
-    if (_score > state->highscore) state->highscore = _score;
-}
-
-/// @brief initialize the current wave
-static void _init_wave() {
-    uint8_t i;
-    if (_current_state == invaders_state_in_wave_break) {
-        _invader_idx = _invaders_shot;
-    } else {
-        _invader_idx = _invaders_shot = _invaders_shot_sum = _defense_lines = _shots_in_wave = 0;
-    }
-    // pre-fill invaders
-    for (i = _invader_idx; i < INVADERS_FACE_WAVE_INVADERS; i++) _wave_invaders[i] = _get_rand_num(10);
-    // init invaders field
-    for (i = 1; i < 6; i++) _invaders[i] = -1;
-    _invaders[0] = _wave_invaders[_invader_idx];
-    _wave_position = _aim = _bonus_countdown = 0;
-    _signals.ufo_next = _signals.inv_checking = _signals.suspend_buttons = false;
-    _current_state = invaders_state_playing;
-    // determine wave speed
-    _wave_tick_freq = 6 - ((_waves % INVADERS_FACE_WAVES_PER_STAGE) + 1) / 2;
-    if (_waves >= INVADERS_FACE_WAVES_PER_STAGE) _wave_tick_freq--;
-    // clear display
-    watch_display_string("        ", 2);
-    watch_display_character('0', 0);
-    _display_defense_lines();
-    // draw first invader
-    watch_display_character(_wave_invaders[_invader_idx] + 48, 9);
-}
-
-/** @brief move invaders and add a new one, if necessary
- * @returns true, if invaders have reached position 6, false otherwise
- */
-static bool _move_invaders() {
-    if (_wave_position == 5) return true;
-    _signals.inv_checking = true;
-    if (_invaders[_wave_position] >= 0) _wave_position++;
-    int8_t i;
-    // move invaders
-    for (i = _wave_position; i > 0; i--) _invaders[i] = _invaders[i - 1];
-    if (_invader_idx < INVADERS_FACE_WAVE_INVADERS - 1) {
-        // add invader
-        _invader_idx++;
-        if (_signals.ufo_next) {
-            _invaders[0] = 10;
-            _signals.ufo_next = false;
-        } else {
-            _invaders[0] = _wave_invaders[_invader_idx];
-        }
-    } else {
-        // just add an empty invader slot
-        _invaders[0] = -1;
-    }
-    // update display
-    for (i = 0; i <= _wave_position; i++) {
-        _display_invader(_invaders[i], 9 - i);
-    }
-    _signals.inv_checking = false;
-    return false;
-}
-
-void invaders_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(invaders_state_t));
-        memset(*context_ptr, 0, sizeof(invaders_state_t));
-        invaders_state_t *state = (invaders_state_t *)*context_ptr;
-        // default: sound on
-        state->sound_on = true;
-    }
-#if __EMSCRIPTEN__
-    // simulator only: seed the randon number generator
-    time_t t;
-    srand((unsigned) time(&t));
-#endif
-}
-
-void invaders_face_activate(void *context) {
-    (void) context;
-    _current_state = invaders_state_activated;
-    _signals.suspend_buttons = false;
-}
-
-bool invaders_face_loop(movement_event_t event, void *context) {
-    invaders_state_t *state = (invaders_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // show highscore
-            _display_score("GA", state->highscore);
-            break;
-        case EVENT_TICK:
-            _ticks++;
-            switch (_current_state) {
-                case invaders_state_in_wave_break:
-                case invaders_state_pre_game:
-                case invaders_state_next_wave:
-                    // wait 2 secs to start the first round
-                    if (_ticks >= 2) {
-                        _ticks = 0;
-                        _init_wave();
-                        _current_state = invaders_state_playing;
-                        movement_request_tick_frequency(4);
-                    }
-                    break;
-                case invaders_state_playing:
-                    // game is playing
-                    if (_ticks >= _wave_tick_freq) {
-                        _ticks = 0;
-                        if (_move_invaders()) {
-                            // invaders broke through
-                            if (_defense_lines < 2) {
-                                // start current wave over
-                                _defense_lines++;
-                                _display_defense_lines();
-                                _display_score("GA", _score);
-                                _current_state = invaders_state_in_wave_break;
-                                movement_request_tick_frequency(1);
-                                _play_sequence(state, (int8_t *)_sound_seq_def_gone);
-                            } else {
-                                // game over
-                                _game_over(state);
-                            }
-                        }
-                    }
-                    // handle bonus points indicators
-                    if (_bonus_countdown) {
-                        _bonus_countdown--;
-                        if (!_bonus_countdown) {
-                            watch_display_character(' ', 2);
-                            watch_display_character(' ', 3);
-                        }
-                    }
-                    break;
-                case invaders_state_pre_next_wave:
-                    if (_ticks >= 3) {
-                        // switch to next wave
-                        _ticks = 0;
-                        movement_request_tick_frequency(1);
-                        _display_score("GA", _score);
-                        watch_set_pixel(1, 9);
-                        watch_display_character((_waves % INVADERS_FACE_WAVES_PER_STAGE) + 49, 3);
-                        _current_state = invaders_state_next_wave;
-                        _waves++;
-                        if (_waves == INVADERS_FACE_WAVES_PER_STAGE * 2) _waves = 0;
-                        _play_sequence(state, (int8_t *)_sound_seq_next_wave);
-                    }
-                default:
-                    break;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            if (!_signals.suspend_buttons) {
-                if (_current_state == invaders_state_playing) {
-                    // cycle the aim
-                    _aim = (_aim + 1) % 11;
-                    _display_invader(_aim, 0);
-                } else if (_current_state == invaders_state_activated || _current_state == invaders_state_game_over) {
-                    // just illuminate the LED
-                    movement_illuminate_led();
-                }
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if ((_current_state == invaders_state_activated || _current_state == invaders_state_game_over) && !_signals.suspend_buttons) {
-                // switch between beepy and silent mode
-                state->sound_on = !state->sound_on;
-                watch_buzzer_play_note(BUZZER_NOTE_A7, state->sound_on ? 65 : 25);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-            if (!_signals.suspend_buttons) {
-                switch (_current_state) {
-                    case invaders_state_game_over:
-                    case invaders_state_activated:
-                        // initialize the game
-                        _waves = 0;
-                        _score = 0;
-                        movement_request_tick_frequency(1);
-                        _ticks = 0;
-                        _current_state = invaders_state_pre_game;
-                        _play_sequence(state, (int8_t *)_sound_seq_game_start);
-                        break;
-                    case invaders_state_playing: {
-                        // "shoot"
-                        _shots_in_wave++;
-                        if (_shots_in_wave == 30) {
-                            // max number of shots reached: game over
-                            _game_over(state);
-                        } else {
-                            // wait if we are currently deleting an invader
-                            while (_signals.inv_checking);
-                            // proceed
-                            _signals.inv_checking = true;
-                            bool skip = false;
-                            for (int8_t i = _wave_position; i >= 0 && !skip; i--) {
-                                // if (_invaders[i] == -1) break;
-                                if (_invaders[i] == _aim) {
-                                    // invader is shot
-                                    skip = true;
-                                    _invaders_shot++;
-                                    _play_sequence(state, _aim == 10 ? (int8_t *)_sound_seq_ufo_hit : (int8_t *)_sound_seq_shot_hit);
-                                    if (_invaders_shot == INVADERS_FACE_WAVE_INVADERS) {
-                                        // last invader shot: wave sucessfully completed
-                                        watch_display_character(' ', 9 - _wave_position);
-                                        _ticks = 0;
-                                        _current_state = invaders_state_pre_next_wave;
-                                        _signals.inv_checking = false;
-                                    } else {
-                                        // check for ufo appearance
-                                        if (_aim && _aim < 10) {
-                                            _invaders_shot_sum = (_invaders_shot_sum + _aim) % 10;
-                                            if (_invaders_shot_sum == 0) _signals.ufo_next = true;
-                                        }
-                                        // remove invader
-                                        if (_wave_position == 0 || i == 5) {
-                                            _invaders[i] = -1;
-                                        } else {
-                                            for (uint8_t j = i; j < _wave_position; j++) {
-                                                _invaders[j] = _invaders[j + 1];
-                                                _display_invader(_invaders[j], 9 - j);
-                                            }
-                                        }
-                                        watch_display_character(' ', 9 - _wave_position);
-                                        if (_wave_position) _wave_position--;
-                                        // update score
-                                        if (_aim == 10) {
-                                            // ufo shot. The original game uses a ridiculously complicated scoring system here...
-                                            uint8_t bonus_points = 0;
-                                            uint8_t j;
-                                            for (j = 0; j < sizeof(_bonus_points_helper) && !bonus_points; j++) {
-                                                if (_shots_in_wave == _bonus_points_helper[j]) {
-                                                    bonus_points = 30;
-                                                } else if (_shots_in_wave - 1 == _bonus_points_helper[j]) {
-                                                    bonus_points = 20;
-                                                }
-                                            }
-                                            if (!bonus_points) bonus_points = 10;
-                                            bonus_points += (6 - i);
-                                            if ((_waves >= INVADERS_FACE_WAVES_PER_STAGE) && i) bonus_points += (6 - i);
-                                            _score += bonus_points;
-                                            // represent bonus points by bars
-                                            for (j = 0; j < (bonus_points / 10); j++) watch_set_pixel(_bonus_points_segdata[j][0], _bonus_points_segdata[j][1]);
-                                            _bonus_countdown = 9;
-                                        } else {
-                                            // regular invader
-                                            _score += (6 - _wave_position) * (_waves >= INVADERS_FACE_WAVES_PER_STAGE ? 2 : 1);
-                                        }
-                                    }
-                                }
-                            }
-                            if (!skip) _play_sequence(state, (int8_t *)_sound_seq_shot_miss);
-                            _signals.inv_checking = false;
-                        }
-                        break;
-                    }    
-                    default:
-                        break;
-                }
-            }
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            // Movement's default loop handler will step in for any cases you don't handle above:
-            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
-            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
-            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
-            // You can override any of these behaviors by adding a case for these events to this switch statement.
-            return movement_default_loop_handler(event);
-    }
-
-    // return true if the watch can enter standby mode. Generally speaking, you should always return true.
-    // Exceptions:
-    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
-    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
-    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
-    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
-    return true;
-}
-
-void invaders_face_resign(void *context) {
-    (void) context;
-    _current_state = invaders_state_game_over;
-}
-
diff --git a/movement/watch_faces/complication/invaders_face.h b/movement/watch_faces/complication/invaders_face.h
deleted file mode 100644 (file)
index 8886286..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Andreas Nebinger
- *
- * 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 INVADERS_FACE_H_
-#define INVADERS_FACE_H_
-
-/*
- * Remake of the "famous" Casio Number Invaders Game
- *
- * This is an authentic remake of the invaders game, found on the Casio
- * calculator wristwatch CA-85 or CA-851. There were also some calculators
- * sold with this game, like MG-880.
- * 
- * How to play:
- * 
- * Press the alarm button to start the game.
- * "Invaders" (just digits, tbh) will start coming in from the right hand side.
- * Press the light button to "aim". The digit on the top of the display cycles
- * from 0 to 9. If your aiming digit is identical to one of the invaders,
- * press the alarm button to "shoot". The corresponding invader will disappear.
- * If the invaders reach beneath the very first position, you loose one defense 
- * line. When all three defense lines are gone, the game is over.
- * Also: If you shoot more than 29 times per round, you loose the game.
- * Good to know: There are 16 invaders per wave. There is a short break between
- * waves.
- * 
- * What are the "n" invaders? Ufos!
- * 
- * Whenever the sum of all invaders shot is divisible by 10 the next invader 
- * will be an ufo, represented by the n-symbol. Shooting a ufo gets you extra
- * points. Example: shoot 2, 5, 3 --> ufo next
- * 
- * As for points: the earlier you shoot an invader, the more points you get.
- *
- * Anything else? Long pressing the light button toggles sound on or off. (Not
- * while playing.)
- * 
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint16_t highscore;
-    bool sound_on;
-} invaders_state_t;
-
-void invaders_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void invaders_face_activate(void *context);
-bool invaders_face_loop(movement_event_t event, void *context);
-void invaders_face_resign(void *context);
-
-#define invaders_face ((const watch_face_t){ \
-    invaders_face_setup, \
-    invaders_face_activate, \
-    invaders_face_loop, \
-    invaders_face_resign, \
-    NULL, \
-})
-
-#endif // INVADERS_FACE_H_
-
diff --git a/movement/watch_faces/complication/kitchen_conversions_face.c b/movement/watch_faces/complication/kitchen_conversions_face.c
deleted file mode 100644 (file)
index 5c1d1dd..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 PrimmR
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "kitchen_conversions_face.h"
-
-typedef struct
-{
-    char name[6];          // Name to display on selection
-    double conv_factor_uk; // Unit as represented in base units (UK)
-    double conv_factor_us; // Unit as represented in base units (US)
-    int16_t linear_factor; // Addition of constant (For temperatures)
-} unit;
-
-#define TICK_FREQ 4
-
-#define MEASURES_COUNT 3 // Number of different measurement 'types'
-#define WEIGHT 0
-#define TEMP 1
-#define VOL 2
-
-// Names of measurements
-static char measures[MEASURES_COUNT][6] = {"WeIght", " Temp", " VOL"};
-
-// Number of items in each category
-#define WEIGHT_COUNT 4
-#define TEMP_COUNT 3
-#define VOL_COUNT 9
-const uint8_t units_count[4] = {WEIGHT_COUNT, TEMP_COUNT, VOL_COUNT};
-
-static const unit weights[WEIGHT_COUNT] = {
-    {" g", 1., 1., 0}, // BASE
-    {" kg", 1000., 1000, 0},
-    {"Ounce", 28.34952, 28.34952, 0},
-    {" Pound", 453.5924, 453.5924, 0},
-};
-
-static const unit temps[TEMP_COUNT] = {
-    {" # C", 1.8, 1.8, 32},
-    {" # F", 1., 1., 0}, // BASE
-    {"Gas Mk", 25., 25., 250},
-};
-
-static const unit vols[VOL_COUNT] = {
-    {"  n&L", 1., 1., 0}, // BASE (ml)
-    {"   L", 1000., 1000., 0},
-    {" Fl Oz", 28.41306, 29.57353, 0},
-    {" Tbsp", 17.75816, 14.78677, 0},
-    {" Tsp", 5.919388, 4.928922, 0},
-    {"  Cup", 284.1306, 236.5882, 0},
-    {" Pint", 568.2612, 473.1765, 0},
-    {" Quart", 1136.522, 946.353, 0},
-    {"Gallon", 4546.09, 3785.412, 0},
-};
-
-static int8_t calc_success_seq[5] = {BUZZER_NOTE_G6, 10, BUZZER_NOTE_C7, 10, 0};
-static int8_t calc_fail_seq[5] = {BUZZER_NOTE_C7, 10, BUZZER_NOTE_G6, 10, 0};
-
-// Resets all state variables to 0
-static void reset_state(kitchen_conversions_state_t *state)
-{
-    state->pg = measurement;
-    state->measurement_i = 0;
-    state->from_i = 0;
-    state->from_is_us = movement_use_imperial_units(); // If uses imperial, most likely to be US
-    state->to_i = 0;
-    state->to_is_us = movement_use_imperial_units();
-    state->selection_value = 0;
-    state->selection_index = 0;
-    state->light_held = false;
-}
-
-void kitchen_conversions_face_setup(uint8_t watch_face_index, void **context_ptr)
-{
-    (void)watch_face_index;
-    if (*context_ptr == NULL)
-    {
-        *context_ptr = malloc(sizeof(kitchen_conversions_state_t));
-        memset(*context_ptr, 0, sizeof(kitchen_conversions_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void kitchen_conversions_face_activate(void *context)
-{
-    kitchen_conversions_state_t *state = (kitchen_conversions_state_t *)context;
-
-    // Handle any tasks related to your watch face coming on screen.
-    movement_request_tick_frequency(TICK_FREQ);
-
-    reset_state(state, settings);
-}
-
-// Increments index pointer by 1, wrapping
-#define increment_wrapping(index, wrap) ({(index)++; index %= wrap; })
-
-static uint32_t pow_10(uint8_t n)
-{
-    uint32_t result = 1;
-    for (int i = 0; i < n; i++)
-    {
-        result *= 10;
-    }
-    return result;
-}
-
-// Returns correct list of units for the measurement index
-static unit *get_unit_list(uint8_t measurement_i)
-{
-    switch (measurement_i)
-    {
-    case WEIGHT:
-        return (unit *)weights;
-
-    case TEMP:
-        return (unit *)temps;
-
-    case VOL:
-        return (unit *)vols;
-
-    default:
-        return (unit *)weights;
-    }
-}
-
-// Increment digit by 1 in input (wraps)
-static void increment_input(kitchen_conversions_state_t *state)
-{
-    uint8_t digit = state->selection_value / pow_10(DISPLAY_DIGITS - 1 - state->selection_index) % 10;
-    if (digit != 9)
-    {
-        state->selection_value += pow_10(DISPLAY_DIGITS - 1 - state->selection_index);
-    }
-    else
-    {
-        state->selection_value -= 9 * pow_10(DISPLAY_DIGITS - 1 - state->selection_index);
-    }
-}
-
-// Displays the list of units in the selected category
-static void display_units(uint8_t measurement_i, uint8_t list_i)
-{
-    watch_display_string(get_unit_list(measurement_i)[list_i].name, 4);
-}
-
-static void display(kitchen_conversions_state_t *state, uint8_t subsec)
-{
-    watch_clear_display();
-
-    switch (state->pg)
-    {
-    case measurement:
-    {
-        watch_display_string("Un", 0);
-        watch_display_string(measures[state->measurement_i], 4);
-    }
-    break;
-
-    case from:
-        display_units(state->measurement_i, state->from_i);
-
-        // Display Fr if non-locale specific, else display locale and F
-        if (state->measurement_i == VOL)
-        {
-            watch_display_string("F", 3);
-
-            char *locale = state->from_is_us ? "A " : "GB";
-            watch_display_string(locale, 0);
-        }
-        else
-        {
-            watch_display_string("Fr", 0);
-        }
-
-        break;
-
-    case to:
-        display_units(state->measurement_i, state->to_i);
-
-        // Display To if non-locale specific, else display locale and T
-        if (state->measurement_i == VOL)
-        {
-            watch_display_string("T", 3);
-
-            char *locale = state->to_is_us ? "A " : "GB";
-            watch_display_string(locale, 0);
-        }
-        else
-        {
-            watch_display_string("To", 0);
-        }
-
-        break;
-
-    case input:
-    {
-        char buf[7];
-        sprintf(buf, "%06lu", state->selection_value);
-        watch_display_string(buf, 4);
-
-        // Only allow ints for Gas Mk
-        if (state->measurement_i == TEMP && state->from_i == 2)
-        {
-            watch_display_string("  ", 8);
-        }
-
-        // Blink digit (on & off) twice a second
-        if (subsec % 2)
-        {
-            watch_display_string(" ", 4 + state->selection_index);
-        }
-
-        watch_display_string("In", 0);
-    }
-    break;
-
-    case result:
-    {
-        unit froms = get_unit_list(state->measurement_i)[state->from_i];
-        unit tos = get_unit_list(state->measurement_i)[state->to_i];
-        // Chooses correct factor for locale
-        double f_conv_factor = state->from_is_us ? froms.conv_factor_us : froms.conv_factor_uk;
-        double t_conv_factor = state->to_is_us ? tos.conv_factor_us : tos.conv_factor_uk;
-        // Converts
-        double to_base = (state->selection_value * f_conv_factor) + 100 * froms.linear_factor;
-        double conversion = ((to_base - 100 * tos.linear_factor) / t_conv_factor);
-
-        // If number too large or too small
-        uint8_t lower_bound = (state->measurement_i == TEMP && state->to_i == 2) ? 100 : 0;
-        if (conversion >= 1000000 || conversion < lower_bound)
-        {
-            watch_set_indicator(WATCH_INDICATOR_BELL);
-            watch_display_string("Err", 5);
-
-            if (movement_button_should_sound())
-                watch_buzzer_play_sequence(calc_fail_seq, NULL);
-        }
-        else
-        {
-            uint32_t rounded = conversion + .5;
-            char buf[7];
-            sprintf(buf, "%6lu", rounded);
-            watch_display_string(buf, 4);
-
-            // Make sure LSDs always filled
-            if (rounded < 10)
-            {
-                watch_display_string("00", 7);
-            }
-            else if (rounded < 100)
-            {
-                watch_display_string("0", 7);
-            }
-
-            if (movement_button_should_sound())
-                watch_buzzer_play_sequence(calc_success_seq, NULL);
-        }
-        watch_display_string("=", 1);
-    }
-
-    break;
-
-    default:
-        break;
-    }
-}
-
-bool kitchen_conversions_face_loop(movement_event_t event, void *context)
-{
-    kitchen_conversions_state_t *state = (kitchen_conversions_state_t *)context;
-
-    switch (event.event_type)
-    {
-    case EVENT_ACTIVATE:
-        // Initial UI
-        display(state, settings, event.subsecond);
-        break;
-    case EVENT_TICK:
-        // Update for blink animation on input
-        if (state->pg == input)
-        {
-            display(state, settings, event.subsecond);
-
-            // Increments input twice a second when light button held
-            if (state->light_held && event.subsecond % 2)
-                increment_input(state);
-        }
-        break;
-    case EVENT_LIGHT_BUTTON_UP:
-        // Cycles options
-        switch (state->pg)
-        {
-        case measurement:
-            increment_wrapping(state->measurement_i, MEASURES_COUNT);
-            break;
-
-        case from:
-            increment_wrapping(state->from_i, units_count[state->measurement_i]);
-            break;
-
-        case to:
-            increment_wrapping(state->to_i, units_count[state->measurement_i]);
-            break;
-
-        case input:
-            increment_input(state);
-            break;
-
-        default:
-            break;
-        }
-
-        // Light button does nothing on final screen
-        if (state->pg != result)
-            display(state, settings, event.subsecond);
-
-        state->light_held = false;
-
-        break;
-
-    case EVENT_ALARM_BUTTON_UP:
-        // Increments selected digit
-        if (state->pg == input)
-        {
-
-            // Moves between digits in input
-            // Wraps at 6 digits unless gas mark selected
-            if (state->selection_index < (DISPLAY_DIGITS - 1) - 2 * (state->measurement_i == TEMP && state->from_i == 2))
-            {
-                state->selection_index++;
-            }
-            else
-            {
-                state->pg++;
-                display(state, settings, event.subsecond);
-            }
-        }
-        // Moves forward 1 page
-        else
-        {
-            if (state->pg == SCREEN_NUM - 1)
-            {
-                reset_state(state, settings);
-            }
-            else
-            {
-                state->pg++;
-            }
-
-            // Play boop
-            if (movement_button_should_sound())
-                watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
-        }
-
-        display(state, settings, event.subsecond);
-
-        state->light_held = false;
-
-        break;
-
-    case EVENT_ALARM_LONG_PRESS:
-        // Moves backwards through pages, resetting certain values
-        if (state->pg != measurement)
-        {
-            switch (state->pg)
-            {
-            case measurement:
-                state->measurement_i = 0;
-                break;
-
-            case from:
-                state->from_i = 0;
-                state->from_is_us = movement_use_imperial_units();
-                break;
-
-            case to:
-                state->to_i = 0;
-                state->to_is_us = movement_use_imperial_units();
-                break;
-
-            case input:
-                state->selection_index = 0;
-                state->selection_value = 0;
-                break;
-
-            case result:
-                state->selection_index = 0;
-                break;
-
-            default:
-                break;
-            }
-
-            state->pg--;
-            display(state, settings, event.subsecond);
-
-            // Play beep
-            if (movement_button_should_sound())
-                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-
-            state->light_held = false;
-        }
-        break;
-
-    case EVENT_LIGHT_LONG_PRESS:
-        // Switch between locales
-        if (state->measurement_i == VOL)
-        {
-            if (state->pg == from)
-            {
-                state->from_is_us = !state->from_is_us;
-            }
-            else if (state->pg == to)
-            {
-                state->to_is_us = !state->to_is_us;
-            }
-
-            if (state->pg == from || state->pg == to)
-            {
-                display(state, settings, event.subsecond);
-
-                // Play bleep
-                if (movement_button_should_sound())
-                    watch_buzzer_play_note(BUZZER_NOTE_E7, 50);
-            }
-        }
-
-        // Sets flag to increment input digit when light held
-        if (state->pg == input)
-            state->light_held = true;
-
-        break;
-
-    case EVENT_LIGHT_LONG_UP:
-        state->light_held = false;
-        break;
-
-    case EVENT_TIMEOUT:
-        movement_move_to_face(0);
-        break;
-
-    default:
-        return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void kitchen_conversions_face_resign(void *context)
-{
-    (void)context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
diff --git a/movement/watch_faces/complication/kitchen_conversions_face.h b/movement/watch_faces/complication/kitchen_conversions_face.h
deleted file mode 100644 (file)
index 33c6cf4..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 PrimmR
- *
- * 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 KITCHEN_CONVERSIONS_FACE_H_
-#define KITCHEN_CONVERSIONS_FACE_H_
-
-#include "movement.h"
-
-/*
- * Kitchen Conversions
- * A face that allows the user to convert between common kitchen units of measurement
- *
- * How to use
- * ----------
- * Short press the alarm button to move forward through menus, and long press to move backwards
- *
- * Press the light button to cycle through options in the menus
- *
- * When inputting a number, the light button moves forward one place and the alarm button increments a digit by one
- *
- * To convert between Imperial (GB) and US (A) measurements of volume, hold the light button
- *
- */
-
-#define SCREEN_NUM 5
-
-// Names of each page
-typedef enum
-{
-    measurement,
-    from,
-    to,
-    input,
-    result,
-} page_t;
-
-#define DISPLAY_DIGITS 6
-
-// Settings when app is running
-typedef struct
-{
-    page_t pg;
-    uint8_t measurement_i;
-    uint8_t from_i;
-    bool from_is_us;
-    uint8_t to_i;
-    bool to_is_us;
-    uint32_t selection_value;
-    uint8_t selection_index;
-    bool light_held;
-} kitchen_conversions_state_t;
-
-void kitchen_conversions_face_setup(uint8_t watch_face_index, void **context_ptr);
-void kitchen_conversions_face_activate(void *context);
-bool kitchen_conversions_face_loop(movement_event_t event, void *context);
-void kitchen_conversions_face_resign(void *context);
-
-#define kitchen_conversions_face ((const watch_face_t){ \
-    kitchen_conversions_face_setup,                     \
-    kitchen_conversions_face_activate,                  \
-    kitchen_conversions_face_loop,                      \
-    kitchen_conversions_face_resign,                    \
-    NULL,                                               \
-})
-
-#endif // KITCHEN_CONVERSIONS_FACE_H_
diff --git a/movement/watch_faces/complication/menstrual_cycle_face.c b/movement/watch_faces/complication/menstrual_cycle_face.c
deleted file mode 100644 (file)
index ccd678c..0000000
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Joseph Borne Komosa | @jokomo24
- *
- * 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.
- *
- * 
- * Menstrual Cycle Face
- * 
- * Background:
- * 
- * I discovered the Casio F-91W through my partner, appreciated the retro aesthetic of the watch,
- * and got one for myself. Soon afterward I discovered the Sensor Watch project and ordered two boards! 
- * I introduced the Sensor Watch to my partner who inquired whether she could track her menstrual cycle.
- * So I decided to implement a menstrual cycle watch face that also calculates the peak fertility window
- * using The Calendar Method. While this information may be useful when attempting to achieve or avoid 
- * pregnancy, it is important to understand that these are rough estimates at best.
- * 
- * How to use:
- * 
- * 1. To begin tracking, go to 'Last Period' page and toggle the alarm button to the number of days since 
- *    the last, most recent, period and hold the alarm button to enter. This will perform the following actions:
- *    - Store the corresponding date as the 'first' period in order to calculate the total_days_tracked.
- *    - Turn on the Signal Indicator to signify that tracking has been activated.
- *    - Deactivate this page and instead show the ticking animation.
- *    - Adjust the days left in the 'Period in <num> Days' page accordingly.
- *    - Activate the 'Period Is Here' page and no longer display 'NA'. To prevent accidental user entry,
- *      the page will display the ticking animation until ten days have passed since the date of the last 
- *      period entered.
- *    - Activate the 'Peak Fertility' page to begin showing the estimated window,
- *      as well as display the Alarm Indicator, on this page and on the main 'Period in <num> Days' page,
- *      whenever the current date falls within the Peak Fertility Window.
- * 
- * 2. Toggle and enter 'y' in the 'Period Is Here' page on the day of every sequential period afterward. 
- *    DO NOT FORGET TO DO SO!
- *    - If forgotten, the data will become inaccurate and tracking will need to be reset! -> (FIXME, allow one to enter a 'missed' period using the 'Last Period' page).
- *    This will perform the following actions:
- *    - Calculate this completed cycle's length and reevaluate the shortest and longest cycle variables.
- *    - Increment total_cycles by one.
- *    - Recalculate and save the average cycle for 'Average Cycle' page.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "menstrual_cycle_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-#define TYPICAL_AVG_CYC 28
-#define SECONDS_PER_DAY 86400
-
-#define MENSTRUAL_CYCLE_FACE_NUM_PAGES (6)
-enum {
-    period_in_num_days,
-    average_cycle,
-    peak_fertility_window,
-    period_is_here,
-    first_period,
-    reset,
-} page_titles_e;
-const char menstrual_cycle_face_titles[MENSTRUAL_CYCLE_FACE_NUM_PAGES][11] = {
-    "Prin   day",   // Period In <num> Days: Estimated days till the next period occurs
-    "Av  cycle ",   // Average Cycle: The average number of days estimated per cycle
-    "Peak Fert ",   // Peak Fertility Window: The first and last day of month (displayed top & bottom right, respectively, once tracking) for the estimated window of fertility
-    "Prishere  ",   // Period Is Here: Toggle and enter 'y' on the day the actual period occurs to improve Avg and Fert estimations
-    "Last Per  ",   // Last Period: Enter the number of days since the last period to begin tracking from that corresponding date by storing it as the 'first'
-    "    Reset ",   // Reset: Toggle and enter 'y' to reset tracking data
-};
-
-/* Beep function */
-static inline void beep() {
-    if (movement_button_should_sound())
-        watch_buzzer_play_note(BUZZER_NOTE_E8, 75);
-}
-
-// Calculate the total number of days for which menstrual cycle tracking has been active
-static inline uint32_t total_days_tracked(menstrual_cycle_state_t *state) {
-
-    // If tracking has not yet been activated, return 0
-    if (!(state->dates.reg)) 
-        return 0;
-
-    // Otherwise, set the start date to the first day of the first tracked cycle
-    watch_date_time_t date_time_start;
-    date_time_start.unit.second = 0;
-    date_time_start.unit.minute = 0;
-    date_time_start.unit.hour = 0;
-    date_time_start.unit.day = state->dates.bit.first_day;
-    date_time_start.unit.month = state->dates.bit.first_month;
-    date_time_start.unit.year = state->dates.bit.first_year;
-
-    // Get the current date and time
-    watch_date_time_t date_time_now = watch_rtc_get_date_time();
-
-    // Convert the start date and current date to Unix time
-    uint32_t unix_start = watch_utility_date_time_to_unix_time(date_time_start, state->utc_offset);
-    uint32_t unix_now = watch_utility_date_time_to_unix_time(date_time_now, state->utc_offset);
-
-    // Calculate the total number of days and return it
-    return (unix_now - unix_start) / SECONDS_PER_DAY;
-}
-
-// Calculate the number of days until the next menstrual period
-static inline int8_t days_till_period(menstrual_cycle_state_t *state) {
-
-    // Calculate the number of days left until the next period based on the average cycle length and the number of cycles tracked
-    int8_t days_left = (state->cycles.bit.average_cycle * (state->cycles.bit.total_cycles + 1)) - total_days_tracked(state);
-
-    // If the result is negative, return 0 (i.e., the period is expected to start today or has already started)
-    return (days_left < 0) ? 0 : days_left;
-}
-
-static inline void reset_tracking(menstrual_cycle_state_t *state) {
-
-    state->dates.bit.first_day = 0;
-    state->dates.bit.first_month = 0;
-    state->dates.bit.first_year = 0;
-
-    state->dates.bit.prev_day = 0;
-    state->dates.bit.prev_month = 0;
-    state->dates.bit.prev_year = 0;
-
-    state->cycles.bit.shortest_cycle = TYPICAL_AVG_CYC;
-    state->cycles.bit.longest_cycle = TYPICAL_AVG_CYC;
-    state->cycles.bit.average_cycle = TYPICAL_AVG_CYC;
-    state->cycles.bit.total_cycles = 0;
-
-    state->dates.bit.reserved = 0;
-    state->cycles.bit.reserved = 0;
-
-    watch_store_backup_data(state->dates.reg, state->backup_register_dt);
-    watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
-
-    watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-}
-
-/*
-Fertility Window based on "The Calendar Method"
-Source: https://www.womenshealth.gov/pregnancy/you-get-pregnant/trying-conceive
-
-The Calendar Method has several steps:
-
-Step 1: Track the menstrual cycle for 8–12 months. One cycle is from the first day of one 
-        period until the first day of the next period. The average cycle is 28 days, but 
-        it may be as short as 24 days or as long as 38 days.
-Step 2: Subtract 18 from the number of days in the shortest menstrual cycle.
-Step 3: Subtract 11 from the number of days in the longest menstrual cycle.
-Step 4: Using a calendar, mark down the start of the next period (using previous instead). Count ahead by the number
-        of days calculated in step 2. This is when peak fertility begins. Peak fertility ends
-        at the number of days calculated in step 3.
-NOTE: Right now, the fertility window face displays its estimated window as soon as tracking is activated, although
-      it is important to keep in mind that The Calendar Method states that peak accuracy of the window will be 
-      reached only after at least 8 months of tracking the menstrual cycle (can make it so that it only displays
-      after total_days_tracked >= 8 months...but the info is interesting and should already be taken with the understanding that,
-      in general, it is a rough estimation at best).
-*/
-typedef enum Fertile_Window {first_day, last_day} fertile_window;
-// Calculate the predicted starting or ending day of peak fertility
-static inline uint32_t get_day_pk_fert(menstrual_cycle_state_t *state, fertile_window which_day) {
-
-    // Get the date of the previous period
-    watch_date_time_t date_prev_period;
-    date_prev_period.unit.second = 0;
-    date_prev_period.unit.minute = 0;
-    date_prev_period.unit.hour = 0;
-    date_prev_period.unit.day = state->dates.bit.prev_day;
-    date_prev_period.unit.month = state->dates.bit.prev_month;
-    date_prev_period.unit.year = state->dates.bit.prev_year;
-
-    // Convert the previous period date to Unix time
-    uint32_t unix_prev_period = watch_utility_date_time_to_unix_time(date_prev_period, state->utc_offset);
-
-    // Calculate the Unix time of the predicted peak fertility day based on the length of the shortest/longest cycle
-    uint32_t unix_pk_date;
-    switch(which_day) {
-        case first_day:
-            unix_pk_date = unix_prev_period + ((state->cycles.bit.shortest_cycle - 18) * SECONDS_PER_DAY);
-            break;
-        case last_day:
-            unix_pk_date = unix_prev_period + ((state->cycles.bit.longest_cycle - 11) * SECONDS_PER_DAY);
-            break;
-    }
-
-    // Convert the Unix time of the predicted peak fertility day to a date/time and return the day of the month
-    return watch_utility_date_time_from_unix_time(unix_pk_date, state->utc_offset).unit.day;
-}
-
-// Determine if today falls within the predicted peak fertility window
-static inline bool inside_fert_window(menstrual_cycle_state_t *state) {
-
-    // If tracking has not yet been activated, return false
-    if (!(state->dates.reg)) 
-        return false;
-
-    // Get the current date/time
-    watch_date_time_t date_time_now = watch_rtc_get_date_time();
-
-    // Check if the current day falls between the first and last predicted peak fertility days
-    if (get_day_pk_fert(state, first_day) > get_day_pk_fert(state, last_day)) { // We are crossing over the end of the month
-        if (date_time_now.unit.day >= get_day_pk_fert(state, first_day) ||
-            date_time_now.unit.day <= get_day_pk_fert(state, last_day))
-            return true;
-    }
-    else if (date_time_now.unit.day >= get_day_pk_fert(state, first_day) &&
-             date_time_now.unit.day <= get_day_pk_fert(state, last_day))
-             return true;
-    // If the current day does not fall within the predicted peak fertility window, return false
-    return false;
-}
-
-// Update the shortest and longest menstrual cycles based on the previous menstrual cycle
-static inline void update_shortest_longest_cycle(menstrual_cycle_state_t *state) {
-
-    // Get the date of the previous menstrual cycle
-    watch_date_time_t date_prev_period;
-    date_prev_period.unit.second = 0;
-    date_prev_period.unit.minute = 0;
-    date_prev_period.unit.hour = 0;
-    date_prev_period.unit.day = state->dates.bit.prev_day;
-    date_prev_period.unit.month = state->dates.bit.prev_month;
-    date_prev_period.unit.year = state->dates.bit.prev_year;
-
-    // Convert the date of the previous menstrual cycle to UNIX time
-    uint32_t unix_prev_period = watch_utility_date_time_to_unix_time(date_prev_period, state->utc_offset);
-
-    // Calculate the length of the current menstrual cycle
-    uint8_t cycle_length = total_days_tracked(state) - (unix_prev_period / SECONDS_PER_DAY);
-
-    // Update the shortest or longest cycle length if necessary
-    if (cycle_length < state->cycles.bit.shortest_cycle)
-        state->cycles.bit.shortest_cycle = cycle_length;
-    else if (cycle_length > state->cycles.bit.longest_cycle)
-        state->cycles.bit.longest_cycle = cycle_length;
-}
-
-void menstrual_cycle_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(menstrual_cycle_state_t));
-        memset(*context_ptr, 0, sizeof(menstrual_cycle_state_t));
-        menstrual_cycle_state_t *state = ((menstrual_cycle_state_t *)*context_ptr);
-
-        state->dates.bit.first_day = 0;
-        state->dates.bit.first_month = 0;
-        state->dates.bit.first_year = 0;
-
-        state->dates.bit.prev_day = 0;
-        state->dates.bit.prev_month = 0;
-        state->dates.bit.prev_year = 0;
-
-        state->cycles.bit.shortest_cycle = TYPICAL_AVG_CYC;
-        state->cycles.bit.longest_cycle = TYPICAL_AVG_CYC;
-        state->cycles.bit.average_cycle = TYPICAL_AVG_CYC;
-        state->cycles.bit.total_cycles = 0;
-
-        state->dates.bit.reserved = 0;
-        state->cycles.bit.reserved = 0;
-
-        state->backup_register_dt = 0;
-        state->backup_register_cy = 0;
-    }
-
-    menstrual_cycle_state_t *state = ((menstrual_cycle_state_t *)*context_ptr);
-    if (!(state->backup_register_dt && state->backup_register_cy)) {
-        state->backup_register_dt = movement_claim_backup_register();
-        state->backup_register_cy = movement_claim_backup_register();
-
-        if (state->backup_register_dt && state->backup_register_cy) {
-            watch_store_backup_data(state->dates.reg, state->backup_register_dt);
-            watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
-        }
-    }
-    else {
-        state->dates.reg = watch_get_backup_data(state->backup_register_dt);
-        state->cycles.reg = watch_get_backup_data(state->backup_register_cy);
-    }
-}
-
-void menstrual_cycle_face_activate(void *context) {
-    menstrual_cycle_state_t *state = (menstrual_cycle_state_t *)context;
-    state->period_today = 0;
-    state->current_page = 0;
-    state->reset_tracking = 0;
-    state->utc_offset = movement_get_current_timezone_offset();
-    movement_request_tick_frequency(4); // we need to manually blink some pixels
-}
-
-bool menstrual_cycle_face_loop(movement_event_t event, void *context) {
-    menstrual_cycle_state_t *state = (menstrual_cycle_state_t *)context;
-    watch_date_time_t date_period;
-    uint8_t current_page = state->current_page;
-    uint8_t first_day_fert;
-    uint8_t last_day_fert;
-    uint32_t unix_now;
-    uint32_t unix_prev_period;
-    switch (event.event_type) {
-        case EVENT_TICK:
-        case EVENT_ACTIVATE:
-            // Do nothing; handled below.
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            movement_move_to_next_face();
-            return false;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            current_page = (current_page + 1) % MENSTRUAL_CYCLE_FACE_NUM_PAGES;
-            state->current_page = current_page;
-            state->days_prev_period = 0;
-            watch_clear_indicator(WATCH_INDICATOR_BELL);
-            if (watch_sleep_animation_is_running())
-                watch_stop_sleep_animation();
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            switch (current_page) {
-                case period_in_num_days:
-                    break;
-                case average_cycle:
-                    break;
-                case peak_fertility_window:
-                    break;
-                case period_is_here:
-                    if (state->period_today && total_days_tracked(state)) {
-                        // Calculate before updating date of last period
-                        update_shortest_longest_cycle(state);
-                        // Update the date of last period after calulating the, now previous, cycle length
-                        date_period = watch_rtc_get_date_time();
-                        state->dates.bit.prev_day = date_period.unit.day;
-                        state->dates.bit.prev_month = date_period.unit.month;
-                        state->dates.bit.prev_year = date_period.unit.year;
-                        // Calculate new cycle average
-                        state->cycles.bit.total_cycles += 1;
-                        state->cycles.bit.average_cycle = total_days_tracked(state) / state->cycles.bit.total_cycles;
-                        // Store the new data
-                        watch_store_backup_data(state->dates.reg, state->backup_register_dt);
-                        watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
-                        state->period_today = !(state->period_today);
-                        beep(settings);
-                    }
-                    break;
-                case first_period:
-                    // If tracking has not yet been activated
-                    if (!(state->dates.reg)) {
-                        unix_now = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), state->utc_offset);
-                        unix_prev_period = unix_now - (state->days_prev_period * SECONDS_PER_DAY);
-                        date_period = watch_utility_date_time_from_unix_time(unix_prev_period, state->utc_offset);
-                        state->dates.bit.first_day = date_period.unit.day;
-                        state->dates.bit.first_month = date_period.unit.month;
-                        state->dates.bit.first_year = date_period.unit.year;
-                        state->dates.bit.prev_day = date_period.unit.day;
-                        state->dates.bit.prev_month = date_period.unit.month;
-                        state->dates.bit.prev_year = date_period.unit.year;
-                        watch_store_backup_data(state->dates.reg, state->backup_register_dt);
-                        beep(settings);
-                    }
-                    break;
-                case reset:
-                    if (state->reset_tracking) {
-                        reset_tracking(state);
-                        state->reset_tracking = !(state->reset_tracking);
-                        beep(settings);
-                    }
-                    break;
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch (current_page) {
-                case period_in_num_days:
-                    break;
-                case average_cycle:
-                    break;
-                case peak_fertility_window:
-                    break;
-                case period_is_here:
-                    if (total_days_tracked(state))
-                        state->period_today = !(state->period_today);
-                    break;
-                case first_period:
-                    if (!(state->dates.reg)) 
-                        state->days_prev_period = (state->days_prev_period > 99) ? 0 : state->days_prev_period + 1; // Cycle through pages to quickly reset to 0
-                    break;
-                case reset:
-                    state->reset_tracking = !(state->reset_tracking);
-                    break;
-            }
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    watch_display_string((char *)menstrual_cycle_face_titles[current_page], 0);
-    if (state->dates.reg)
-        watch_set_indicator(WATCH_INDICATOR_SIGNAL); // signal that we are now in a tracking state
-
-    char buf[13];
-    switch (current_page) {
-        case period_in_num_days:
-            sprintf(buf, "%2d", days_till_period(state));
-            if (inside_fert_window(state))
-                watch_set_indicator(WATCH_INDICATOR_BELL);
-            watch_display_string(buf, 4);
-            break;
-        case average_cycle:
-            sprintf(buf, "%2d", state->cycles.bit.average_cycle);
-            watch_display_string(buf, 2);
-            break;
-        case peak_fertility_window:
-            if (event.subsecond % 5 && state->dates.reg) { // blink active for 3 quarter-seconds
-                first_day_fert = get_day_pk_fert(state, first_day);
-                last_day_fert = get_day_pk_fert(state, last_day);
-                sprintf(buf, "Fr%2d To %2d", first_day_fert, last_day_fert); // From: first day | To: last day
-                if (inside_fert_window(state))
-                    watch_set_indicator(WATCH_INDICATOR_BELL);
-                watch_display_string(buf, 0);
-            }
-            break;
-        case period_is_here:
-            if (event.subsecond % 5) { // blink active for 3 quarter-seconds
-                if (!(state->dates.reg))
-                    watch_display_string("NA", 8); // Not Applicable: Do not allow period entry until tracking is activated...
-                else if (state->period_today) 
-                    watch_display_string("y", 9);
-                else 
-                    watch_display_string("n", 9);
-            }
-            break;
-        case first_period:
-            if (state->dates.reg) {
-                if (!watch_sleep_animation_is_running())
-                    watch_start_sleep_animation(500); // Tracking activated
-            }
-            else if (event.subsecond % 5) { // blink active for 3 quarter-seconds
-                sprintf(buf, "%2d", state->days_prev_period);
-                watch_display_string(buf, 8);
-            }
-            break;
-        case reset:
-            // blink active for 3 quarter-seconds
-            if (event.subsecond % 5 && state->reset_tracking)
-                watch_display_string("y", 9);
-            else if (event.subsecond % 5)
-                watch_display_string("n", 9);
-            break;
-    }
-    return true;
-}
-
-void menstrual_cycle_face_resign(void *context) {
-    (void) context;
-}
\ No newline at end of file
diff --git a/movement/watch_faces/complication/menstrual_cycle_face.h b/movement/watch_faces/complication/menstrual_cycle_face.h
deleted file mode 100644 (file)
index 5f0dedc..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Joseph Borne Komosa | @jokomo24
- *
- * 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 MENSTRUAL_CYCLE_FACE_H_
-#define MENSTRUAL_CYCLE_FACE_H_
-
-#include "movement.h"
-
-typedef struct {
-    // Store the date of the 'first' and the total cycles since to calulate and store the average menstrual cycle.
-    // Store the date of the previous, most recent, period to calculate the cycle length.
-    // Store the shortest and longest cycle to calculate the fertility window for The Calender Method.
-    // NOTE: Not thrilled about using two registers, but could not find a way to perform The Calender Method
-    //       without requiring both the 'first' and 'prev' dates.
-    union {
-        struct {
-            uint8_t first_day : 5;
-            uint8_t first_month : 4;
-            uint8_t first_year : 6; // 0-63 (representing 2020-2083)
-            uint8_t prev_day : 5;
-            uint8_t prev_month : 4;
-            uint8_t prev_year : 6; // 0-63 (representing 2020-2083)
-            uint8_t reserved : 2; // left over bit space
-        } bit;
-        uint32_t reg; // Tracking's been activated if > 0
-    } dates;
-    union {
-        struct {
-            uint8_t shortest_cycle : 6; // For step 2 of The Calender Method 
-            uint8_t longest_cycle : 6; // For step 3 of The Calender Method 
-            uint8_t average_cycle : 6; // The average menstrual cycle lasts 28 days, but normal cycles can vary from 21 to 35 days
-            uint16_t total_cycles : 11; // The total cycles (periods) entered since the start of tracking
-            uint8_t reserved : 3; // left over bit space
-        } bit; 
-        uint32_t reg;
-    } cycles;
-    uint8_t backup_register_dt;
-    uint8_t backup_register_cy;
-    uint8_t current_page;
-    uint8_t days_prev_period;
-    int32_t utc_offset;
-    bool period_today;
-    bool reset_tracking;
-} menstrual_cycle_state_t;
-
-void menstrual_cycle_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void menstrual_cycle_face_activate(void *context);
-bool menstrual_cycle_face_loop(movement_event_t event, void *context);
-void menstrual_cycle_face_resign(void *context);
-
-#define menstrual_cycle_face ((const watch_face_t){ \
-    menstrual_cycle_face_setup, \
-    menstrual_cycle_face_activate, \
-    menstrual_cycle_face_loop, \
-    menstrual_cycle_face_resign, \
-    NULL, \
-})
-
-#endif // MENSTRUAL_CYCLE_FACE_H_
\ No newline at end of file
diff --git a/movement/watch_faces/complication/metronome_face.c b/movement/watch_faces/complication/metronome_face.c
deleted file mode 100644 (file)
index e599362..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Austin Teets
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "metronome_face.h"
-#include "watch.h"
-
-static const int8_t _sound_seq_start[] = {BUZZER_NOTE_C8, 2, 0};
-static const int8_t _sound_seq_beat[] = {BUZZER_NOTE_C6, 2, 0};
-
-void metronome_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(metronome_state_t));
-        memset(*context_ptr, 0, sizeof(metronome_state_t));
-    }
-}
-
-void metronome_face_activate(void *context) {
-    metronome_state_t *state = (metronome_state_t *)context;
-    movement_request_tick_frequency(2);
-    if (state->bpm == 0) {
-        state->count = 4;
-        state->bpm = 120;
-        state->soundOn = true;
-    }
-    state->mode = metWait;
-    state->correction = 0;
-    state->setCur = hundred;
-}
-
-static void _metronome_face_update_lcd(metronome_state_t *state) {
-    char buf[11];
-    if (state->soundOn) {
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    } else {
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-    }
-    sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp");
-    watch_display_string(buf, 0);
-}
-
-static void _metronome_start_stop(metronome_state_t *state) {
-    if (state->mode != metRun) {
-        movement_request_tick_frequency(64);
-        state->mode = metRun;
-        watch_clear_display();
-        double ticks = 3840.0 / (double)state->bpm;
-        state->tick = (int) ticks;
-        state->curTick = (int) ticks;
-        state->halfBeat = (int)(state->tick/2);
-        state->curCorrection = ticks - state->tick;
-        state->correction = ticks - state->tick;
-        state->curBeat = 1;
-    } else {
-        state->mode = metWait;
-        movement_request_tick_frequency(2);
-        _metronome_face_update_lcd(state);
-    }
-}
-
-static void _metronome_tick_beat(metronome_state_t *state) {
-    char buf[11];
-    if (state->soundOn) {
-        if (state->curBeat == 1) {
-            watch_buzzer_play_sequence((int8_t *)_sound_seq_start, NULL);
-        } else {
-            watch_buzzer_play_sequence((int8_t *)_sound_seq_beat, NULL);
-        }
-    }
-    sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp");    
-    watch_display_string(buf, 0);
-}
-
-static void _metronome_event_tick(uint8_t subsecond, metronome_state_t *state) {
-    (void) subsecond;
-
-    if (state->curCorrection >= 1) {
-        state->curCorrection -= 1;
-        state->curTick -= 1;
-    }
-    int diff = state->curTick - state->tick;
-    if(diff == 0) {
-        _metronome_tick_beat(state);
-        state->curTick = 0;
-        state->curCorrection += state->correction;
-        if (state->curBeat < state->count ) {
-            state->curBeat += 1;
-        } else {
-            state->curBeat = 1;
-        }
-    } else {
-        if (state->curTick == state->halfBeat)  {
-            watch_clear_display();
-        }
-        state->curTick += 1;
-    }
-}
-
-static void _metronome_setting_tick(uint8_t subsecond, metronome_state_t *state) {
-    char buf[13];
-    sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp"); 
-    if (subsecond%2 == 0) {
-        switch (state->setCur) {
-            case hundred:
-                buf[5] = ' ';
-                break;
-            case ten:
-                buf[6] = ' ';
-                break;
-            case one:
-                buf[7] = ' ';
-                break;
-            case count:
-                buf[3] = ' ';
-                break;
-            case alarm:
-                break;
-        }
-    }
-    if (state->setCur == alarm) {
-        sprintf(buf, "MN  8eep%s", state->soundOn ? "On" : " -");
-    }
-    if (state->soundOn) {
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    } else {
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-    }
-    watch_display_string(buf, 0);
-}
-
-static void _metronome_update_setting(metronome_state_t *state) {
-    char buf[13];
-    switch (state->setCur) {
-        case hundred:
-            if (state->bpm < 100) {
-                state->bpm += 100;
-            } else {
-                state->bpm -= 100;
-            }
-            break;
-        case ten:
-            if ((state->bpm / 10) % 10 < 9) {
-                state->bpm += 10;
-            } else {
-                state->bpm -= 90;
-            }
-            break;
-        case one:
-            if (state->bpm%10 < 9) {
-                state->bpm += 1;
-            } else {
-                state->bpm -= 9;
-            }
-            break;
-        case count:
-            if (state->count < 9) {
-                state->count += 1;
-            } else {
-                state->count = 2;
-            }
-            break;
-        case alarm:
-            state->soundOn = !state->soundOn;
-            break;
-    }
-    sprintf(buf, "MN %d %03d%s", state->count % 10, state->bpm, "bp"); 
-    if (state->setCur == alarm) {
-        sprintf(buf, "MN  8eep%s", state->soundOn ? "On" : " -");
-    }
-    if (state->soundOn) {
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    } else {
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-    }
-    watch_display_string(buf, 0);
-}
-
-bool metronome_face_loop(movement_event_t event, void *context) {
-    metronome_state_t *state = (metronome_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _metronome_face_update_lcd(state);
-            break;
-        case EVENT_TICK:
-            if (state->mode == metRun){
-                _metronome_event_tick(event.subsecond, state);
-            } else if (state->mode == setMenu) {
-                _metronome_setting_tick(event.subsecond, state);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if (state->mode == setMenu) {
-                _metronome_update_setting(state);
-            } else {
-                _metronome_start_stop(state);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            if (state->mode == setMenu) {
-                if (state->setCur < alarm) {
-                    state->setCur += 1;
-                } else {
-                    state->setCur = hundred;
-                }
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->mode != metRun && state->mode != setMenu) {
-                movement_request_tick_frequency(2);
-                state->mode = setMenu;
-                _metronome_face_update_lcd(state);
-            } else if (state->mode == setMenu) {
-                state->mode = metWait;
-                _metronome_face_update_lcd(state);
-            }
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            movement_move_to_next_face();
-            break;
-        case EVENT_TIMEOUT:
-            if (state->mode != metRun) {
-                movement_move_to_face(0);
-            }
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-    return true;
-}
-
-void metronome_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/complication/metronome_face.h b/movement/watch_faces/complication/metronome_face.h
deleted file mode 100644 (file)
index 32766da..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Austin Teets
- *
- * 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 METRONOME_FACE_H_
-#define METRONOME_FACE_H_
-
-#include "movement.h"
-
-/*
- * A Metronome watch complication
- * Allows the user to set the BPM, counts per measure, beep sound on/off
- * Screen flashes on on the beat and off on the half beat (1/8th note)
- * Beep will sound high for downbeat and low for subsequent beats in measure
- * USE:
- *      Press Alarm to start/stop metronome_face
- *      Hold Alarm to enter settings menu
- *          Short Light press will move through options
- *          Short Alarm press will increment/toggle options
- *          Long alarm press will exit options
- */
-typedef enum {
-    metWait,
-    metRun,
-    setMenu
-} metronome_mode_t;
-
-typedef enum {
-    hundred,
-    ten,
-    one,
-    count,
-    alarm
-} setting_cursor_t;
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint8_t bpm;
-    double correction;
-    double curCorrection;
-    int count;
-    int tick;
-    int curTick;
-    int curBeat;
-    int halfBeat;
-    metronome_mode_t mode : 3;
-    setting_cursor_t setCur : 4;
-    bool soundOn;
-} metronome_state_t;
-
-void metronome_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void metronome_face_activate(void *context);
-bool metronome_face_loop(movement_event_t event, void *context);
-void metronome_face_resign(void *context);
-
-#define metronome_face ((const watch_face_t){ \
-    metronome_face_setup, \
-    metronome_face_activate, \
-    metronome_face_loop, \
-    metronome_face_resign, \
-    NULL, \
-})
-
-#endif // METRONOME_FACE_H_
-
diff --git a/movement/watch_faces/complication/moon_phase_face.c b/movement/watch_faces/complication/moon_phase_face.c
deleted file mode 100644 (file)
index 7b28e44..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Joey Castillo
- *
- * Based on Phase of Moon App for Tidbyt
- * https://github.com/tidbyt/community/blob/main/apps/phaseofmoon/phase_of_moon.star
- * Copyright (c) 2022 Alan Fleming
- *
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-#include "moon_phase_face.h"
-#include "watch_utility.h"
-
-#define LUNAR_DAYS 29.53058770576
-#define LUNAR_SECONDS (LUNAR_DAYS * (24 * 60 * 60))
-#define FIRST_MOON 947182440 // Saturday, 6 January 2000 18:14:00 in unix epoch time
-#define NUM_PHASES 8
-
-static const float phase_changes[] = {0, 1, 6.38264692644, 8.38264692644, 13.76529385288, 15.76529385288, 21.14794077932, 23.14794077932, 28.53058770576, 29.53058770576};
-
-void moon_phase_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(moon_phase_state_t));
-        memset(*context_ptr, 0, sizeof(moon_phase_state_t));
-    }
-}
-
-void moon_phase_face_activate(void *context) {
-    (void) context;
-}
-
-static void _update(moon_phase_state_t *state, uint32_t offset) {
-    (void)state;
-    char buf[11];
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset()) + offset;
-    date_time = watch_utility_date_time_from_unix_time(now, movement_get_current_timezone_offset());
-    double currentfrac = fmod(now - FIRST_MOON, LUNAR_SECONDS) / LUNAR_SECONDS;
-    double currentday = currentfrac * LUNAR_DAYS;
-    uint8_t phase_index = 0;
-
-    for(phase_index = 0; phase_index <= NUM_PHASES; phase_index++) {
-        if (currentday > phase_changes[phase_index] && currentday <= phase_changes[phase_index + 1]) break;
-    }
-
-    watch_display_string(" ", 0);
-    switch (phase_index) {
-        case 0:
-        case 8:
-            sprintf(buf, "%2d Neu  ", date_time.unit.day);
-            break;
-        case 1:
-            sprintf(buf, "%2dCresnt", date_time.unit.day);
-            watch_set_pixel(2, 13);
-            watch_set_pixel(2, 15);
-            if (currentfrac > 0.125) watch_set_pixel(1, 13);
-            break;
-        case 2:
-            sprintf(buf, "%2d 1st q", date_time.unit.day);
-            watch_set_pixel(2, 13);
-            watch_set_pixel(2, 15);
-            watch_set_pixel(1, 13);
-            watch_set_pixel(1, 14);
-            break;
-        case 3:
-            sprintf(buf, "%2d Gibb ", date_time.unit.day);
-            watch_set_pixel(2, 13);
-            watch_set_pixel(2, 15);
-            watch_set_pixel(1, 14);
-            watch_set_pixel(1, 13);
-            watch_set_pixel(1, 15);
-            break;
-        case 4:
-            sprintf(buf, "%2d FULL ", date_time.unit.day);
-            watch_set_pixel(2, 13);
-            watch_set_pixel(2, 15);
-            watch_set_pixel(1, 14);
-            watch_set_pixel(2, 14);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(0, 14);
-            watch_set_pixel(0, 13);
-            watch_set_pixel(1, 13);
-            break;
-        case 5:
-            sprintf(buf, "%2d Gibb ", date_time.unit.day);
-            watch_set_pixel(1, 14);
-            watch_set_pixel(2, 14);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(0, 14);
-            watch_set_pixel(0, 13);
-            break;
-        case 6:
-            sprintf(buf, "%2d 3rd q", date_time.unit.day);
-            watch_set_pixel(1, 14);
-            watch_set_pixel(2, 14);
-            watch_set_pixel(0, 14);
-            watch_set_pixel(0, 13);
-            break;
-        case 7:
-            sprintf(buf, "%2dCresnt", date_time.unit.day);
-            watch_set_pixel(0, 14);
-            watch_set_pixel(0, 13);
-            if (currentfrac < 0.875) watch_set_pixel(2, 14);
-            break;
-    }
-    watch_display_string(buf, 2);
-}
-
-bool moon_phase_face_loop(movement_event_t event, void *context) {
-    moon_phase_state_t *state = (moon_phase_state_t *)context;
-    watch_date_time_t date_time;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _update(state, state->offset);
-            break;
-        case EVENT_TICK:
-            // only update once an hour
-            date_time = watch_rtc_get_date_time();
-            if ((date_time.unit.minute == 0) && (date_time.unit.second == 0)) _update(state, state->offset);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // update at the top of the hour OR if we're entering sleep mode with an offset.
-            // also, in sleep mode, always show the current moon phase (offset = 0).
-            if (state->offset || (watch_rtc_get_date_time().unit.minute == 0)) _update(state, 0);
-            // and kill the offset so when the wearer wakes up, it matches what's on screen.
-            state->offset = 0;
-            // finally: clear out the last two digits and replace them with the sleep mode indicator
-            watch_display_string("  ", 8);
-            if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(1000);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // Pressing the alarm adds an offset of one day to the displayed value,
-            // so you can see moon phases in the future.
-            state->offset += 86400;
-            _update(state, state->offset);
-            break;
-       case EVENT_ALARM_LONG_PRESS:
-           state->offset = 0;
-            _update(state, state->offset);
-           break;
-        case EVENT_TIMEOUT:
-            // QUESTION: Should timeout reset offset to 0?
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void moon_phase_face_resign(void *context) {
-    moon_phase_state_t *state = (moon_phase_state_t *)context;
-    state->offset = 0;
-}
diff --git a/movement/watch_faces/complication/moon_phase_face.h b/movement/watch_faces/complication/moon_phase_face.h
deleted file mode 100644 (file)
index b020982..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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 MOON_PHASE_FACE_H_
-#define MOON_PHASE_FACE_H_
-
-/*
- * MOON PHASE face
- *
- * The Moon Phase face is similar to the Sunrise/Sunset face: it displays the
- * current phase of the moon, along with the day of the month and a graphical
- * representation of the moon on the top row.
- * 
- * This graphical representation is a bit abstract. The segments that turn on
- * represent the shape of the moon, waxing from the bottom right and waning at
- * the top left. A small crescent at the bottom right will grow into a larger
- * crescent, then add lines in the center for a quarter and half moon. All
- * segments are on during a full moon. Then gradually the segments at the
- * bottom right will turn off, until all that remains is a small waning
- * crescent at the top left.
- * 
- * All segments turn off during a new moon.
- * 
- * On this screen you may press the Alarm button repeatedly to move forward
- * in time: the day of the month at the top right will advance by one day for
- * each button press, and both the text and the graphical representation will
- * display the moon phase for that day. Try pressing the Alarm button 27 times
- * now, just to visualize what the moon will look like over the next month.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint32_t offset;
-} moon_phase_state_t;
-
-void moon_phase_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void moon_phase_face_activate(void *context);
-bool moon_phase_face_loop(movement_event_t event, void *context);
-void moon_phase_face_resign(void *context);
-
-#define moon_phase_face ((const watch_face_t){ \
-    moon_phase_face_setup, \
-    moon_phase_face_activate, \
-    moon_phase_face_loop, \
-    moon_phase_face_resign, \
-    NULL, \
-})
-
-#endif // MOON_PHASE_FACE_H_
-
diff --git a/movement/watch_faces/complication/morsecalc_face.c b/movement/watch_faces/complication/morsecalc_face.c
deleted file mode 100644 (file)
index 0a4ad2c..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "watch.h"
-#include "watch_utility.h"
-#include "watch_private_display.h"
-
-#include "morsecalc_face.h"
-#include "morsecalc_display.h"
-
-/* mc_input Read an input into a morse code buffer 
- * Input: mc = index of MORSECODE_TREE[]
- *        len = max morse code char length
- *        in = character to read into buffer (0='.', 1='-', ignored otherwise).
- * If the buffer is full, reset it instead of entering the new character.
- */
-static void morsecode_input(unsigned int *mc, unsigned int len, char in) {
-    if(*mc >= (unsigned int) ((1<<len)-1)) *mc = 0;
-    else if((in == 0) | (in == 1)) *mc = (*mc)*2+in+1;
-    return;
-}
-
-
-// Clear token buffer
-void morsecalc_reset_token(morsecalc_state_t *mcs) { 
-    memset(mcs->token, '\0', MORSECALC_TOKEN_LEN*sizeof(mcs->token[0]));
-    mcs->idxt = 0;
-    return;
-}
-
-// Write a completed morse code character to the calculator
-void morsecalc_input(morsecalc_state_t * mcs) {
-    int status = 0;
-    char dec = MORSECODE_TREE[mcs->mc]; 
-    mcs->mc = 0;
-    switch(dec) {
-        case '\0': // Invalid character, do nothing
-            morsecalc_display_token(mcs);
-            break;
-            
-        case ' ': // Submit token to calculator
-            if(mcs->idxt > 0) {
-                mcs->token[mcs->idxt] = '\0';
-                status = calc_input(mcs->cs, mcs->token);
-                morsecalc_reset_token(mcs); 
-            } 
-            morsecalc_display_stack(mcs);   
-            break;
-            
-        case '(': // -.--. Erase previous character in token
-            if(mcs->idxt>0) {
-                mcs->idxt--;
-                mcs->token[mcs->idxt] = '\0';
-            }
-            morsecalc_display_token(mcs);
-            break;
-            
-        case 'S': // -.-.- Erase entire token without submitting
-            morsecalc_reset_token(mcs); 
-            morsecalc_display_stack(mcs);   
-            break;
-            
-        default: // Add character to token
-            if(mcs->idxt < MORSECALC_TOKEN_LEN-1) {
-                mcs->token[mcs->idxt] = dec;
-                mcs->idxt = min(mcs->idxt+1, MORSECALC_TOKEN_LEN); 
-                morsecalc_display_token(mcs);
-            }
-            else  watch_display_string("  full", 4); 
-            break;
-    }
-    
-    // Print errors if there are any
-    switch(status) {
-        case  0: break; // Success
-        case -1: watch_display_string("cmderr", 4); break; // Unrecognized command
-        case -2: watch_display_string("stkerr", 4); break; // Bad stack size
-        default: watch_display_string("   err", 4); break; // Other error 
-    }
-    
-    return;    
-}
-
-void morsecalc_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(morsecalc_state_t)); 
-        morsecalc_state_t *mcs = (morsecalc_state_t *)*context_ptr;
-        morsecalc_reset_token(mcs); 
-        
-        mcs->cs = (calc_state_t *) malloc(sizeof(calc_state_t));
-        calc_init(mcs->cs); 
-        mcs->mc = 0;
-        mcs->led_is_on = 0;
-    }
-    return;
-}
-
-void morsecalc_face_activate(void *context) {
-    morsecalc_state_t *mcs = (morsecalc_state_t *) context;
-    mcs->mc = 0;
-    morsecalc_display_stack(mcs);
-    return;
-}
-
-bool morsecalc_face_loop(movement_event_t event, void *context) {
-    morsecalc_state_t *mcs = (morsecalc_state_t *) context;
-    switch(event.event_type) {
-    // input
-    case EVENT_ALARM_BUTTON_UP:
-    // dot
-        morsecode_input(&mcs->mc, MORSECODE_LEN, 0);
-        morsecalc_display_token(mcs);
-        break;
-    case EVENT_LIGHT_BUTTON_UP:
-    // dash
-        morsecode_input(&mcs->mc, MORSECODE_LEN, 1);
-        morsecalc_display_token(mcs);
-        break;
-    case EVENT_MODE_BUTTON_UP:
-    // submit character (or quit)
-        if(mcs->mc || mcs->idxt) morsecalc_input(mcs); 
-        else movement_move_to_next_face();
-        break;
-
-    // show stack
-    case EVENT_ALARM_LONG_PRESS:
-        morsecalc_display_stack(mcs);
-        mcs->mc = 0;
-        break;
-
-    // toggle light
-    case EVENT_LIGHT_LONG_PRESS:
-        mcs->led_is_on = !mcs->led_is_on;
-        if(mcs->led_is_on) {
-            movement_color_t color = movement_backlight_color();
-                       watch_set_led_color(color.red ? (0xF | color.red << 4) : 0,
-                                                               color.green ? (0xF | color.green << 4) : 0);
-            movement_request_tick_frequency(4);
-        }
-        else {
-            watch_set_led_off();
-            movement_request_tick_frequency(1);
-        }
-        break;
-
-    // quit
-    case EVENT_TIMEOUT:
-        movement_move_to_face(0);
-        break;
-    case EVENT_MODE_LONG_PRESS:
-        movement_move_to_next_face();
-        break;
-
-    case EVENT_TICK:
-        if(mcs->led_is_on) { 
-            movement_color_t color = movement_backlight_color();
-                       watch_set_led_color(color.red ? (0xF | color.red << 4) : 0,
-                               color.green ? (0xF | color.green << 4) : 0);
-               }
-        break;
-    case EVENT_LIGHT_BUTTON_DOWN:
-        // don't light up every time light is hit
-        break;
-    default:
-        movement_default_loop_handler(event);
-        break;
-       }
-    
-    return true;
-}
-
-void morsecalc_face_resign(void *context) {
-    morsecalc_state_t *mcs = (morsecalc_state_t *) context;
-    mcs->led_is_on = 0;
-       watch_set_led_off();
-    return;
-}
-
diff --git a/movement/watch_faces/complication/morsecalc_face.h b/movement/watch_faces/complication/morsecalc_face.h
deleted file mode 100644 (file)
index 1430347..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * 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 MORSECALC_FACE_H_
-#define MORSECALC_FACE_H_
-
-/*
- * MORSECALC face
- * Morse-code-based RPN calculator
- *
- * The calculator is operated by first composing a **token** in Morse code,
- * then submitting it to the calculator. A token specifies either a calculator
- * operation or a float value.
- *
- * These two parts of the codebase are totally independent:
- *  1. The Morse-code reader (`mc.h`, `mc.c`)
- *  2. The RPN calculator (`calc.h`, `calc.c`, `calc_fn.h`, `calc_fn.c`, `small_strtod.c`)
- *
- * The user interface (`morsecalc_face.h`, `morsecalc_face.c`) lets you talk
- * to the RPN calculator through Morse code.
- *
- * ## Controls
- *  - `light` is dash
- *  - `alarm` is dot
- *  - `mode` is "finish character"
- *  - long-press `mode` or submit a blank token to switch faces
- *  - long-press `alarm` to show stack
- *  - long-press `light` to toggle the light
- *
- * ## Morse code token entry
- * As you enter `.`s and `-`s, the morse code char you've entered will
- * appear in the top center digit. At the top right is the # of morse code
- * `.`/`-` you've input so far. The character resets at the 6th `.`/`-`.
- *
- * Once you have the character you want to enter, push `mode` to enter it.
- *
- * The character will be appended to the current token, whose 6 trailing
- * chars are shown on the main display. Once you've typed in the token you
- * want, enter a blank Morse code character and then push `mode`.
- * This submits it to the calculator.
- *
- * Special characters:
- *  - Backspace is `(` (`-.--.`).
- *  - Clear token input without submitting to calculator is `Start
- *    transmission` (`-.-.-`).
- *
- * ## Writing commands
- * First the calculator will try to interpret the token as a command/stack operation.
- * Commands are defined in `calc_dict[]` in `movement/lib/morsecalc/calc_fns.h`.
- * If the command doesn't appear in the dictionary, the calculator tries to interpret the token as a number.
- *
- * ## Writing numbers
- * Numbers are written like floating point strings.
- * Entering a number pushes it to the top of the stack if there's room.
- * This can get long, so for convenience numerals can also be written in binary with .- = 01.
- *
- *     0   1    2    3    4    5    6    7    8    9
- *     .   -    -.   --   -..  -.-  --.  ---  -... -..-
- *     e   t    n    m    d    k    g    o    b    x
- *
- *  - Exponent signs must be entered as "p".
- *  - Decimal place "." can be entered as "h" (code ....)
- *  - Sign "-" can be entered as "Ch digraph" (code ----)
- *
- * For example: "4.2e-3" can be entered directly, or as "4h2pC3"
- *   similarly, "0.0042" can also be entered as "eheedn"
- * Once you submit a number to the watch face, it pushes it to the top of the stack if there's room.
- *
- * ## Number display
- * After a command runs, the top of the stack is displayed in this format:
- *
- *   - Main 4 digits = leading 4 digits
- *   - Last 2 digits = exponent
- *   - Top middle = [Stack location, Sign of number]
- *   - Top right = [Stack exponent, Sign of exponent]
- *
- * Blank sign digit means positive.
- * So for example, the watch face might look like this:
- *
- *     [   0 -5]
- *     [4200 03]
- *
- * ... representing `+4.200e-3` is in stack location 0 (the top) and it's one of five items in the stack.
- *
- * ## Looking at the stack
- * To show the top of the stack, push and hold `light`/`alarm` or submit a blank token by pushing `mode` a bunch of times.
- * To show the N-th stack item (0 through 9):
- *
- *  - Put in the Morse code for N without pushing the mode button.
- *  - Push and hold `alarm`.
- *
- * To show the memory register, use `m` instead of a number.
- *
- * To see all the calculator operations and their token aliases, see the `calc_dict[]` struct in `calc_fns.h`
- */
-
-#define MORSECALC_TOKEN_LEN 32
-#define MORSECODE_LEN 5
-
-#include "movement.h"
-#include "calc.h"
-
-/*
- * MC International Morse Code binary tree
- * Levels of the tree are concatenated.
- * '.' = 0 and '-' = 1.
- *
- * Capitals denote special characters:
- * C = Ch digraph
- * V = VERIFY (ITU-R "UNDERSTOOD")
- * R = REPEAT
- * W = WAIT
- * S = START TRANSMISSION
- * E = END OF WORK
- */
-static const char MORSECODE_TREE[] = " etianmsurwdkgohvf\0l\0pjbxcyzq\0C\x35\x34V\x33\0R\0\x32W\0+\0\0\0\0\x31\x36=/\0\0S(\0\x37\0\0\0\x38\0\x39\x30\0\0\0\0\0E\0\0\0\0\0\0?_\0\0\0\0\"\0\0.\0\0\0\0@\0\0\0'\0\0-\0\0\0\0\0\0\0\0;!\0)\0\0\0\0\0,\0\0\0\0:\0\0\0\0\0\0";
-
-void morsecalc_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void morsecalc_face_activate(void *context);
-bool morsecalc_face_loop(movement_event_t event, void *context);
-void morsecalc_face_resign(void *context);
-
-typedef struct {
-       calc_state_t *cs;
-       unsigned int mc; // Morse code character
-       char token[MORSECALC_TOKEN_LEN];
-       uint8_t idxt;
-       uint8_t led_is_on;
-} morsecalc_state_t;
-
-void morsecalc_reset_token(morsecalc_state_t *mcs);
-void morsecalc_input(morsecalc_state_t *mcs);
-
-#define morsecalc_face ((const watch_face_t){ \
-    morsecalc_face_setup, \
-    morsecalc_face_activate, \
-    morsecalc_face_loop, \
-    morsecalc_face_resign, \
-    NULL, \
-})
-
-#endif // MORSECALC_FACE_H_
diff --git a/movement/watch_faces/complication/orrery_face.c b/movement/watch_faces/complication/orrery_face.c
deleted file mode 100644 (file)
index fba821b..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "orrery_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "vsop87a_micro.h" // smaller size, less accurate
-#include "vsop87a_milli.h"
-#include "astrolib.h"
-
-#define NUM_AVAILABLE_BODIES 9
-
-static const char orrery_celestial_body_names[NUM_AVAILABLE_BODIES][3] = {
-    "ME",   // Mercury
-    "VE",   // Venus
-    "EA",   // Earth
-    "LU",   // Moon (Luna)
-    "MA",   // Mars
-    "JU",   // Jupiter
-    "SA",   // Saturn
-    "UR",   // Uranus
-    "NE"    // Neptune
-};
-
-static void _orrery_face_recalculate(orrery_state_t *state) {
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
-    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 et = astro_convert_jd_to_julian_millenia_since_j2000(jd);
-    double r[3] = {0};
-
-    switch(state->active_body_index) {
-        case 0:
-            vsop87a_milli_getMercury(et, r);
-            break;
-        case 1:
-            vsop87a_milli_getVenus(et, r);
-            break;
-        case 2:
-            vsop87a_milli_getEarth(et, r);
-            break;
-        case 3:
-            {
-                 double earth[3];
-                 double emb[3];
-                 vsop87a_milli_getEarth(et, earth);
-                 vsop87a_milli_getEmb(et, emb);
-                 vsop87a_milli_getMoon(earth, emb, r);
-             }
-            break;
-        case 4:
-            vsop87a_milli_getMars(et, r);
-            break;
-        case 5:
-            vsop87a_milli_getJupiter(et, r);
-            break;
-        case 6:
-            vsop87a_milli_getSaturn(et, r);
-            break;
-        case 7:
-            vsop87a_milli_getUranus(et, r);
-            break;
-        case 8:
-            vsop87a_milli_getNeptune(et, r);
-            break;
-    }
-    state->coords[0] = r[0];
-    state->coords[1] = r[1];
-    state->coords[2] = r[2];
-}
-
-static void _orrery_face_update(movement_event_t event, orrery_state_t *state) {
-    char buf[11];
-    switch (state->mode) {
-        case ORRERY_MODE_SELECTING_BODY:
-            watch_display_string("Orrery", 4);
-            if (event.subsecond % 2) {
-                watch_display_string((char *)orrery_celestial_body_names[state->active_body_index], 0);
-            } else {
-                watch_display_string("  ", 0);
-            }
-            if (event.subsecond == 0) {
-                watch_display_string("  ", 2);
-                switch (state->animation_state) {
-                    case 0:
-                        watch_set_pixel(0, 7);
-                        watch_set_pixel(2, 6);
-                        break;
-                    case 1:
-                        watch_set_pixel(1, 7);
-                        watch_set_pixel(2, 9);
-                        break;
-                    case 2:
-                        watch_set_pixel(2, 7);
-                        watch_set_pixel(0, 9);
-                        break;
-                }
-                state->animation_state = (state->animation_state + 1) % 3;
-            }
-            break;
-        case ORRERY_MODE_CALCULATING:
-            watch_clear_display();
-             // this takes a moment and locks the UI, flash C for "Calculating"
-            watch_start_character_blink('C', 100);
-            _orrery_face_recalculate(state);
-            watch_stop_blink();
-            state->mode = ORRERY_MODE_DISPLAYING_X;
-            // fall through
-        case ORRERY_MODE_DISPLAYING_X:
-            sprintf(buf, "%s X%6d", orrery_celestial_body_names[state->active_body_index], (int16_t)round(state->coords[0] * 100));
-            watch_display_string(buf, 0);
-            break;
-        case ORRERY_MODE_DISPLAYING_Y:
-            sprintf(buf, "%s Y%6d", orrery_celestial_body_names[state->active_body_index], (int16_t)round(state->coords[1] * 100));
-            watch_display_string(buf, 0);
-            break;
-        case ORRERY_MODE_DISPLAYING_Z:
-            sprintf(buf, "%s Z%6d", orrery_celestial_body_names[state->active_body_index], (int16_t)round(state->coords[2] * 100));
-            watch_display_string(buf, 0);
-            break;
-        case ORRERY_MODE_NUM_MODES:
-            // this case does not happen, but we need it to silence a warning.
-            break;
-    }
-}
-
-void orrery_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(orrery_state_t));
-        memset(*context_ptr, 0, sizeof(orrery_state_t));
-    }
-}
-
-void orrery_face_activate(void *context) {
-    (void) context;
-    movement_request_tick_frequency(4);
-}
-
-bool orrery_face_loop(movement_event_t event, void *context) {
-    orrery_state_t *state = (orrery_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            _orrery_face_update(event, state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch (state->mode) {
-                case ORRERY_MODE_SELECTING_BODY:
-                    // advance to next celestial body (move to calculations with a long press)
-                    state->active_body_index = (state->active_body_index + 1) % NUM_AVAILABLE_BODIES;
-                    break;
-                case ORRERY_MODE_CALCULATING:
-                    // ignore button press during calculations
-                    break;
-                case ORRERY_MODE_DISPLAYING_Z:
-                    // at last mode, wrap around
-                    state->mode = ORRERY_MODE_DISPLAYING_X;
-                    break;
-                default:
-                    // otherwise, advance to next mode
-                    state->mode++;
-                    break;
-            }
-            _orrery_face_update(event, state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->mode == ORRERY_MODE_SELECTING_BODY) {
-                // celestial body selected! this triggers a calculation in the update method.
-                state->mode = ORRERY_MODE_CALCULATING;
-                movement_request_tick_frequency(1);
-                _orrery_face_update(event, state);
-            } else if (state->mode != ORRERY_MODE_CALCULATING) {
-                // in all modes except "doing a calculation", return to the selection screen.
-                state->mode = ORRERY_MODE_SELECTING_BODY;
-                movement_request_tick_frequency(4);
-                _orrery_face_update(event, state);
-            }
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void orrery_face_resign(void *context) {
-    orrery_state_t *state = (orrery_state_t *)context;
-    state->mode = ORRERY_MODE_SELECTING_BODY;
-}
diff --git a/movement/watch_faces/complication/orrery_face.h b/movement/watch_faces/complication/orrery_face.h
deleted file mode 100644 (file)
index 4bf141e..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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 ORRERY_FACE_H_
-#define ORRERY_FACE_H_
-
-/*
- * ORRERY face
- *
- * The Orrery watch face is similar to the Astronomy watch face in that it
- * calculates properties of the planets, but instead of calculating their
- * positions in the sky, this watch face calculates their absolute locations
- * in the solar system. This is only useful if you want to plot the planets
- * on graph paper, but hey, you never know!
- * 
- * The controls are identical to the Astronomy watch face: while the title
- * screen (“Orrery”) is displayed, you can advance through the available
- * planets with repeated short presses on the Alarm button. The available
- * planets:
- * 
- *     ME - Mercury
- *     VE - Venus
- *     EA - Earth
- *     LU - Luna, the Earth’s moon
- *     MA - Mars
- *     JU - Jupiter
- *     SA - Saturn
- *     UR - Uranus
- *     NE - Neptune
- * 
- * Note that the sun is not available in this menu, as the sun is always at
- * (0,0,0) in this calculation.
- * 
- * Long press on the Alarm button to calculate the planet’s location, and
- * after a flashing “C” (for Calculating), you will be presented with the
- * planet’s X coordinate in astronomical units. Short press Alarm to cycle
- * through the X, Y and Z coordinates, and then long press Alarm to return
- * to planet selection.
- * 
- * The large numbers represent the whole number part, and the two smaller
- * numbers (in the seconds place) represent the decimal portion. So if you
- * see “SA X 736” and “SA Y -662”, you can read that as an X coordinate of
- * 7.36 AU and a Y coordinate of -6.62 AU. You can literally draw a dot at
- * (0, 0) to represent the sun, and a dot at (7.36, -6.62) to represent
- * Saturn. (The Z coordinates tend to be pretty close to zero, as the
- * planets largely orbit on a single plane, the ecliptic.)
- */
-
-#include "movement.h"
-
-typedef enum {
-    ORRERY_MODE_SELECTING_BODY = 0,
-    ORRERY_MODE_CALCULATING,
-    ORRERY_MODE_DISPLAYING_X,
-    ORRERY_MODE_DISPLAYING_Y,
-    ORRERY_MODE_DISPLAYING_Z,
-    ORRERY_MODE_NUM_MODES
-} orrery_mode_t;
-
-typedef struct {
-    orrery_mode_t mode;
-    uint8_t active_body_index;
-    double coords[3];
-    uint8_t animation_state;
-} orrery_state_t;
-
-void orrery_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void orrery_face_activate(void *context);
-bool orrery_face_loop(movement_event_t event, void *context);
-void orrery_face_resign(void *context);
-
-#define orrery_face ((const watch_face_t){ \
-    orrery_face_setup, \
-    orrery_face_activate, \
-    orrery_face_loop, \
-    orrery_face_resign, \
-    NULL, \
-})
-
-#endif // ORRERY_FACE_H_
diff --git a/movement/watch_faces/complication/periodic_face.c b/movement/watch_faces/complication/periodic_face.c
deleted file mode 100644 (file)
index f6647e9..0000000
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 PrimmR
- * Copyright (c) 2024 David Volovskiy
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "periodic_face.h"
-
-#define FREQ_FAST 8
-#define FREQ 2
-
-static bool _quick_ticks_running;
-static uint8_t _ts_ticks = 0;
-static int16_t _text_pos;
-static const char* _text_looping;
-static const char title_text[] = "Periodic Table";
-
-void periodic_face_setup(uint8_t watch_face_index, void **context_ptr)
-{
-    (void)watch_face_index;
-    if (*context_ptr == NULL)
-    {
-        *context_ptr = malloc(sizeof(periodic_state_t));
-        memset(*context_ptr, 0, sizeof(periodic_state_t));
-    }
-}
-
-void periodic_face_activate(void *context)
-{
-    periodic_state_t *state = (periodic_state_t *)context;
-
-    state->atomic_num = 0;
-    state->mode = 0;
-    state->selection_index = 0;
-    _quick_ticks_running = false;
-    movement_request_tick_frequency(FREQ);
-}
-
-typedef struct
-{
-    char symbol[3];
-    char name[14];  // Longest is Rutherfordium
-    int16_t year_discovered;  // Negative is BC
-    uint16_t atomic_mass;  // In units of 0.01 AMU
-    uint16_t electronegativity;  // In units of 0.01
-    char group[3];
-} element;
-
-typedef enum {
-    SCREEN_TITLE = 0,
-    SCREEN_ELEMENT,
-    SCREEN_ATOMIC_MASS,
-    SCREEN_DISCOVER_YEAR,
-    SCREEN_ELECTRONEGATIVITY,
-    SCREEN_FULL_NAME,    
-    SCREENS_COUNT
-} PeriodicScreens;
-
-const char screen_name[SCREENS_COUNT][3] = {
-    [SCREEN_ATOMIC_MASS] = "am",
-    [SCREEN_DISCOVER_YEAR] = " y",
-    [SCREEN_ELECTRONEGATIVITY] = "EL",
-    [SCREEN_FULL_NAME] = " n",
-};
-
-// Comments on the table denote symbols that cannot be displayed
-#define MAX_ELEMENT 118
-const element table[MAX_ELEMENT] = {
-    { .symbol = "H", .name = "Hydrogen", .year_discovered = 1671, .atomic_mass = 101, .electronegativity = 220, .group = "  " },
-    { .symbol = "HE", .name = "Helium", .year_discovered = 1868, .atomic_mass = 400, .electronegativity = 0, .group = "0" },
-    { .symbol = "LI", .name = "Lithium", .year_discovered = 1817, .atomic_mass = 694, .electronegativity = 98, .group = "1" },
-    { .symbol = "BE", .name = "Beryllium", .year_discovered = 1798, .atomic_mass = 901, .electronegativity = 157, .group = "2" },
-    { .symbol = "B", .name = "Boron", .year_discovered = 1787, .atomic_mass = 1081, .electronegativity = 204, .group = "3" },
-    { .symbol = "C", .name = "Carbon", .year_discovered = -26000, .atomic_mass = 1201, .electronegativity = 255, .group = "4" },
-    { .symbol = "N", .name = "Nitrogen", .year_discovered = 1772, .atomic_mass = 1401, .electronegativity = 304, .group = "5" },
-    { .symbol = "O", .name = "Oxygen", .year_discovered = 1771, .atomic_mass = 1600, .electronegativity = 344, .group = "6" },
-    { .symbol = "F", .name = "Fluorine", .year_discovered = 1771, .atomic_mass = 1900, .electronegativity = 398, .group = "7" },
-    { .symbol = "NE", .name = "Neon", .year_discovered = 1898, .atomic_mass = 2018, .electronegativity = 0, .group = "0" },
-    { .symbol = "NA", .name = "Sodium", .year_discovered = 1702, .atomic_mass = 2299, .electronegativity = 93, .group = "1" },
-    { .symbol = "MG", .name = "Magnesium", .year_discovered = 1755, .atomic_mass = 2431, .electronegativity = 131, .group = "2" },
-    { .symbol = "AL", .name = "Aluminium", .year_discovered = 1746, .atomic_mass = 2698, .electronegativity = 161, .group = "3" },
-    { .symbol = "SI", .name = "Silicon", .year_discovered = 1739, .atomic_mass = 2809, .electronegativity = 190, .group = "4" },
-    { .symbol = "P", .name = "Phosphorus", .year_discovered = 1669, .atomic_mass = 3097, .electronegativity = 219, .group = "5" },
-    { .symbol = "S", .name = "Sulfur", .year_discovered = -2000, .atomic_mass = 3206, .electronegativity = 258, .group = "6" },
-    { .symbol = "CL", .name = "Chlorine", .year_discovered = 1774, .atomic_mass = 3545., .electronegativity = 316, .group = "7" },
-    { .symbol = "AR", .name = "Argon", .year_discovered = 1894, .atomic_mass = 3995., .electronegativity = 0, .group = "0" },
-    { .symbol = "K", .name = "Potassium", .year_discovered = 1702, .atomic_mass = 3910, .electronegativity = 82, .group = "1" },
-    { .symbol = "CA", .name = "Calcium", .year_discovered = 1739, .atomic_mass = 4008, .electronegativity = 100, .group = "2" },
-    { .symbol = "SC", .name = "Scandium", .year_discovered = 1879, .atomic_mass = 4496, .electronegativity = 136, .group = " T" },
-    { .symbol = "TI", .name = "Titanium", .year_discovered = 1791, .atomic_mass = 4787, .electronegativity = 154, .group = " T" },
-    { .symbol = "W", .name = "Vanadium", .year_discovered = 1801, .atomic_mass = 5094, .electronegativity = 163, .group = " T" },
-    { .symbol = "CR", .name = "Chromium", .year_discovered = 1797, .atomic_mass = 5200, .electronegativity = 166, .group = " T" },
-    { .symbol = "MN", .name = "Manganese", .year_discovered = 1774, .atomic_mass = 5494, .electronegativity = 155, .group = " T" },
-    { .symbol = "FE", .name = "Iron", .year_discovered = -5000, .atomic_mass = 5585, .electronegativity = 183, .group = " T" },
-    { .symbol = "CO", .name = "Cobalt", .year_discovered = 1735, .atomic_mass = 5893, .electronegativity = 188, .group = " T" },
-    { .symbol = "NI", .name = "Nickel", .year_discovered = 1751, .atomic_mass = 5869, .electronegativity = 191, .group = " T" },
-    { .symbol = "CU", .name = "Copper", .year_discovered = -9000, .atomic_mass = 6355, .electronegativity = 190, .group = " T" },
-    { .symbol = "ZN", .name = "Zinc", .year_discovered = -1000, .atomic_mass = 6538, .electronegativity = 165, .group = " T" },
-    { .symbol = "GA", .name = "Gallium", .year_discovered = 1875, .atomic_mass = 6972, .electronegativity = 181, .group = "3" },
-    { .symbol = "GE", .name = "Germanium", .year_discovered = 1886, .atomic_mass = 7263, .electronegativity = 201, .group = "4" },
-    { .symbol = "AS", .name = "Arsenic", .year_discovered = 300, .atomic_mass = 7492, .electronegativity = 218, .group = "5" },
-    { .symbol = "SE", .name = "Selenium", .year_discovered = 1817, .atomic_mass = 7897, .electronegativity = 255, .group = "6" },
-    { .symbol = "BR", .name = "Bromine", .year_discovered = 1825, .atomic_mass = 7990., .electronegativity = 296, .group = "7" },
-    { .symbol = "KR", .name = "Krypton", .year_discovered = 1898, .atomic_mass = 8380, .electronegativity = 300, .group = "0" },
-    { .symbol = "RB", .name = "Rubidium", .year_discovered = 1861, .atomic_mass = 8547, .electronegativity = 82, .group = "1" },
-    { .symbol = "SR", .name = "Strontium", .year_discovered = 1787, .atomic_mass = 8762, .electronegativity = 95, .group = "2" },
-    { .symbol = "Y", .name = "Yttrium", .year_discovered = 1794, .atomic_mass = 8891, .electronegativity = 122, .group = " T" },
-    { .symbol = "ZR", .name = "Zirconium", .year_discovered = 1789, .atomic_mass = 9122, .electronegativity = 133, .group = " T" },
-    { .symbol = "NB", .name = "Niobium", .year_discovered = 1801, .atomic_mass = 9291, .electronegativity = 160, .group = " T" },
-    { .symbol = "MO", .name = "Molybdenum", .year_discovered = 1778, .atomic_mass = 9595, .electronegativity = 216, .group = " T" },
-    { .symbol = "TC", .name = "Technetium", .year_discovered = 1937, .atomic_mass = 9700, .electronegativity = 190, .group = " T" },
-    { .symbol = "RU", .name = "Ruthenium", .year_discovered = 1844, .atomic_mass = 10107, .electronegativity = 220, .group = " T" },
-    { .symbol = "RH", .name = "Rhodium", .year_discovered = 1804, .atomic_mass = 10291, .electronegativity = 228, .group = " T" },
-    { .symbol = "PD", .name = "Palladium", .year_discovered = 1802, .atomic_mass = 10642, .electronegativity = 220, .group = " T" },
-    { .symbol = "AG", .name = "Silver", .year_discovered = -5000, .atomic_mass = 10787, .electronegativity = 193, .group = " T" },
-    { .symbol = "CD", .name = "Cadmium", .year_discovered = 1817, .atomic_mass = 11241, .electronegativity = 169, .group = " T" },
-    { .symbol = "IN", .name = "Indium", .year_discovered = 1863, .atomic_mass = 11482, .electronegativity = 178, .group = "3" },
-    { .symbol = "SN", .name = "Tin", .year_discovered = -3500, .atomic_mass = 11871, .electronegativity = 196, .group = "4" },
-    { .symbol = "SB", .name = "Antimony", .year_discovered = -3000, .atomic_mass = 12176, .electronegativity = 205, .group = "5" },
-    { .symbol = "TE", .name = "Tellurium", .year_discovered = 1782, .atomic_mass = 12760, .electronegativity = 210, .group = "6" },
-    { .symbol = "I", .name = "Iodine", .year_discovered = 1811, .atomic_mass = 12690, .electronegativity = 266, .group = "7" },
-    { .symbol = "XE", .name = "Xenon", .year_discovered = 1898, .atomic_mass = 13129, .electronegativity = 260, .group = "0" },
-    { .symbol = "CS", .name = "Caesium", .year_discovered = 1860, .atomic_mass = 13291, .electronegativity = 79, .group = "1" },
-    { .symbol = "BA", .name = "Barium", .year_discovered = 1772, .atomic_mass = 13733., .electronegativity = 89, .group = "2" },
-    { .symbol = "LA", .name = "Lanthanum", .year_discovered = 1838, .atomic_mass = 13891, .electronegativity = 110, .group = "1a" },
-    { .symbol = "CE", .name = "Cerium", .year_discovered = 1803, .atomic_mass = 14012, .electronegativity = 112, .group = "1a" },
-    { .symbol = "PR", .name = "Praseodymium", .year_discovered = 1885, .atomic_mass = 14091, .electronegativity = 113, .group = "1a" },
-    { .symbol = "ND", .name = "Neodymium", .year_discovered = 1841, .atomic_mass = 14424, .electronegativity = 114, .group = "1a" },
-    { .symbol = "PM", .name = "Promethium", .year_discovered = 1945, .atomic_mass = 14500, .electronegativity = 113, .group = "1a" },
-    { .symbol = "SM", .name = "Samarium", .year_discovered = 1879, .atomic_mass = 15036., .electronegativity = 117, .group = "1a" },
-    { .symbol = "EU", .name = "Europium", .year_discovered = 1896, .atomic_mass = 15196, .electronegativity = 120, .group = "1a" },
-    { .symbol = "GD", .name = "Gadolinium", .year_discovered = 1880, .atomic_mass = 15725, .electronegativity = 120, .group = "1a" },
-    { .symbol = "TB", .name = "Terbium", .year_discovered = 1843, .atomic_mass = 15893, .electronegativity = 120, .group = "1a" },
-    { .symbol = "DY", .name = "Dysprosium", .year_discovered = 1886, .atomic_mass = 16250, .electronegativity = 122, .group = "1a" },
-    { .symbol = "HO", .name = "Holmium", .year_discovered = 1878, .atomic_mass = 16493, .electronegativity = 123, .group = "1a" },
-    { .symbol = "ER", .name = "Erbium", .year_discovered = 1843, .atomic_mass = 16726, .electronegativity = 124, .group = "1a" },
-    { .symbol = "TM", .name = "Thulium", .year_discovered = 1879, .atomic_mass = 16893, .electronegativity = 125, .group = "1a" },
-    { .symbol = "YB", .name = "Ytterbium", .year_discovered = 1878, .atomic_mass = 17305, .electronegativity = 110, .group = "1a" },
-    { .symbol = "LU", .name = "Lutetium", .year_discovered = 1906, .atomic_mass = 17497, .electronegativity = 127, .group = "1a" },
-    { .symbol = "HF", .name = "Hafnium", .year_discovered = 1922, .atomic_mass = 17849, .electronegativity = 130, .group = " T" },
-    { .symbol = "TA", .name = "Tantalum", .year_discovered = 1802, .atomic_mass = 18095, .electronegativity = 150, .group = " T" },
-    { .symbol = "W", .name = "Tungsten", .year_discovered = 1781, .atomic_mass = 18384, .electronegativity = 236, .group = " T" },
-    { .symbol = "RE", .name = "Rhenium", .year_discovered = 1908, .atomic_mass = 18621, .electronegativity = 190, .group = " T" },
-    { .symbol = "OS", .name = "Osmium", .year_discovered = 1803, .atomic_mass = 19023, .electronegativity = 220, .group = " T" },
-    { .symbol = "IR", .name = "Iridium", .year_discovered = 1803, .atomic_mass = 19222, .electronegativity = 220, .group = " T" },
-    { .symbol = "PT", .name = "Platinum", .year_discovered = -600, .atomic_mass = 19508, .electronegativity = 228, .group = " T" },
-    { .symbol = "AU", .name = "Gold", .year_discovered = -6000, .atomic_mass = 19697, .electronegativity = 254, .group = " T" },
-    { .symbol = "HG", .name = "Mercury", .year_discovered = -1500, .atomic_mass = 20059, .electronegativity = 200, .group = " T" },
-    { .symbol = "TL", .name = "Thallium", .year_discovered = 1861, .atomic_mass = 20438, .electronegativity = 162, .group = "3" },
-    { .symbol = "PB", .name = "Lead", .year_discovered = -7000, .atomic_mass = 20720, .electronegativity = 187, .group = "4" },
-    { .symbol = "BI", .name = "Bismuth", .year_discovered = 1500, .atomic_mass = 20898, .electronegativity = 202, .group = "5" },
-    { .symbol = "PO", .name = "Polonium", .year_discovered = 1898, .atomic_mass = 20900, .electronegativity = 200, .group = "6" },
-    { .symbol = "AT", .name = "Astatine", .year_discovered = 1940, .atomic_mass = 21000, .electronegativity = 220, .group = "7" },
-    { .symbol = "RN", .name = "Radon", .year_discovered = 1899, .atomic_mass = 22200, .electronegativity = 220, .group = "0" },
-    { .symbol = "FR", .name = "Francium", .year_discovered = 1939, .atomic_mass = 22300, .electronegativity = 79, .group = "1" },
-    { .symbol = "RA", .name = "Radium", .year_discovered = 1898, .atomic_mass = 22600, .electronegativity = 90, .group = "2" },
-    { .symbol = "AC", .name = "Actinium", .year_discovered = 1902, .atomic_mass = 22700, .electronegativity = 110, .group = "Ac" },
-    { .symbol = "TH", .name = "Thorium", .year_discovered = 1829, .atomic_mass = 23204, .electronegativity = 130, .group = "Ac" },
-    { .symbol = "PA", .name = "Protactinium", .year_discovered = 1913, .atomic_mass = 23104, .electronegativity = 150, .group = "Ac" },
-    { .symbol = "U", .name = "Uranium", .year_discovered = 1789, .atomic_mass = 23803, .electronegativity = 138, .group = "Ac" },
-    { .symbol = "NP", .name = "Neptunium", .year_discovered = 1940, .atomic_mass = 23700, .electronegativity = 136, .group = "Ac" },
-    { .symbol = "PU", .name = "Plutonium", .year_discovered = 1941, .atomic_mass = 24400, .electronegativity = 128, .group = "Ac" },
-    { .symbol = "AM", .name = "Americium", .year_discovered = 1944, .atomic_mass = 24300, .electronegativity = 113, .group = "Ac" },
-    { .symbol = "CM", .name = "Curium", .year_discovered = 1944, .atomic_mass = 24700, .electronegativity = 128, .group = "Ac" },
-    { .symbol = "BK", .name = "Berkelium", .year_discovered = 1949, .atomic_mass = 24700, .electronegativity = 130, .group = "Ac" },
-    { .symbol = "CF", .name = "Californium", .year_discovered = 1950, .atomic_mass = 25100, .electronegativity = 130, .group = "Ac" },
-    { .symbol = "ES", .name = "Einsteinium", .year_discovered = 1952, .atomic_mass = 25200, .electronegativity = 130, .group = "Ac" },
-    { .symbol = "FM", .name = "Fermium", .year_discovered = 1953, .atomic_mass = 25700, .electronegativity = 130, .group = "Ac" },
-    { .symbol = "MD", .name = "Mendelevium", .year_discovered = 1955, .atomic_mass = 25800, .electronegativity = 130, .group = "Ac" },
-    { .symbol = "NO", .name = "Nobelium", .year_discovered = 1965, .atomic_mass = 25900, .electronegativity = 130, .group = "Ac" },
-    { .symbol = "LR", .name = "Lawrencium", .year_discovered = 1961, .atomic_mass = 26600, .electronegativity = 130, .group = "Ac" },
-    { .symbol = "RF", .name = "Rutherfordium", .year_discovered = 1969, .atomic_mass = 26700, .electronegativity = 0, .group = " T" },
-    { .symbol = "DB", .name = "Dubnium", .year_discovered = 1970, .atomic_mass = 26800, .electronegativity = 0, .group = " T" },
-    { .symbol = "SG", .name = "Seaborgium", .year_discovered = 1974, .atomic_mass = 26700, .electronegativity = 0, .group = " T" },
-    { .symbol = "BH", .name = "Bohrium", .year_discovered = 1981, .atomic_mass = 27000, .electronegativity = 0, .group = " T" },
-    { .symbol = "HS", .name = "Hassium", .year_discovered = 1984, .atomic_mass = 27100, .electronegativity = 0, .group = " T" },
-    { .symbol = "MT", .name = "Meitnerium", .year_discovered = 1982, .atomic_mass = 27800, .electronegativity = 0, .group = " T" },
-    { .symbol = "DS", .name = "Darmstadtium", .year_discovered = 1994, .atomic_mass = 28100, .electronegativity = 0, .group = " T" },
-    { .symbol = "RG", .name = "Roentgenium", .year_discovered = 1994, .atomic_mass = 28200, .electronegativity = 0, .group = " T" },
-    { .symbol = "CN", .name = "Copernicium", .year_discovered = 1996, .atomic_mass = 28500, .electronegativity = 0, .group = " T" },
-    { .symbol = "NH", .name = "Nihonium", .year_discovered = 2004, .atomic_mass = 28600, .electronegativity = 0, .group = "3" },
-    { .symbol = "FL", .name = "Flerovium", .year_discovered = 1999, .atomic_mass = 28900, .electronegativity = 0, .group = "4" },
-    { .symbol = "MC", .name = "Moscovium", .year_discovered = 2003, .atomic_mass = 29000, .electronegativity = 0, .group = "5" },
-    { .symbol = "LW", .name = "Livermorium", .year_discovered = 2000, .atomic_mass = 29300, .electronegativity = 0, .group = "6" },
-    { .symbol = "TS", .name = "Tennessine", .year_discovered = 2009, .atomic_mass = 29400, .electronegativity = 0, .group = "7" },
-    { .symbol = "OG", .name = "Oganesson", .year_discovered = 2002, .atomic_mass = 29400, .electronegativity = 0, .group = "0" },
-};
-
-static void _make_upper(char *string) {
-    size_t i = 0;
-    while(string[i] != 0) {
-        if (string[i] >= 'a' && string[i] <= 'z')
-            string[i]-=32;  // 32 = 'a'-'A'
-        i++;
-    }
-}
-
-static void _display_element(periodic_state_t *state)
-{
-    char buf[9];
-    char ele[3];
-    uint8_t atomic_num = state->atomic_num;
-    strcpy(ele, table[atomic_num - 1].symbol);
-    _make_upper(ele);
-    sprintf(buf, "%2s%3d %-2s", table[atomic_num - 1].group, atomic_num, ele);
-    watch_display_string(buf, 2);
-}
-
-static void _display_atomic_mass(periodic_state_t *state)
-{
-    char buf[11];
-    uint16_t mass = table[state->atomic_num - 1].atomic_mass;
-    uint16_t integer = mass / 100;
-    uint16_t decimal = mass % 100;
-    if (decimal == 0)
-        sprintf(buf, "%-2s%2s%4d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer);
-    else
-        sprintf(buf, "%-2s%2s%3d_%.2d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer, decimal);
-    watch_display_string(buf, 0); 
-}
-
-static void _display_year_discovered(periodic_state_t *state)
-{
-    char buf[11];
-    char year_buf[7];
-    int16_t year = table[state->atomic_num - 1].year_discovered;
-    if (abs(year) > 9999)
-        sprintf(year_buf, "----  ");
-    else
-        sprintf(year_buf, "%4d  ", abs(year));
-    if (year < 0) {
-        year_buf[4] = 'b';
-        year_buf[5] = 'c';
-    }
-    sprintf(buf, "%-2s%-2s%s", table[state->atomic_num - 1].symbol, screen_name[state->mode], year_buf);
-    watch_display_string(buf, 0); 
-}
-
-static void _display_name(periodic_state_t *state)
-{
-    char buf[11];
-    _text_looping = table[state->atomic_num - 1].name;
-    _text_pos = 0;
-    sprintf(buf, "%-2s%-2s%s", table[state->atomic_num - 1].symbol, screen_name[state->mode], table[state->atomic_num - 1].name);
-    watch_display_string(buf, 0); 
-}
-
-static void _display_electronegativity(periodic_state_t *state)
-{
-    char buf[11];
-    uint16_t electronegativity = table[state->atomic_num - 1].electronegativity;
-    uint16_t integer = electronegativity / 100;
-    uint16_t decimal = electronegativity % 100;
-    if (decimal == 0)
-        sprintf(buf, "%-2s%2s%4d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer);
-    else
-        sprintf(buf, "%-2s%2s%3d_%.2d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer, decimal);
-    watch_display_string(buf, 0); 
-}
-
-static void start_quick_cyc(void){
-    _quick_ticks_running = true;
-    movement_request_tick_frequency(FREQ_FAST);
-}
-
-static void stop_quick_cyc(void){
-    _quick_ticks_running = false;
-    movement_request_tick_frequency(FREQ);
-}
-
-static int16_t _loop_text(const char* text, int8_t curr_loc, uint8_t char_len){
-    // if curr_loc, then use that many ticks as a delay before looping
-    char buf[15];
-    uint8_t next_pos;
-    uint8_t text_len = strlen(text);
-    uint8_t pos = 10 - char_len;
-    if (curr_loc == -1) curr_loc = 0;  // To avoid double-showing the 0
-    if (char_len >= text_len || curr_loc < 0) {
-        sprintf(buf, "%s", text);
-        watch_display_string(buf, pos);
-        if (curr_loc < 0) return ++curr_loc;
-        return 0;
-    }
-    else if (curr_loc == (text_len + 1))
-        curr_loc = 0;
-    next_pos = curr_loc + 1;
-    sprintf(buf, "%.6s %.6s", text + curr_loc, text);
-    watch_display_string(buf, pos);
-    return next_pos;
-}
-
-static void _display_title(periodic_state_t *state){
-    state->atomic_num = 0;
-    watch_clear_colon();
-    watch_clear_all_indicators();
-    _text_looping = title_text;
-    _text_pos = FREQ * -1;
-    _text_pos = _loop_text(_text_looping, _text_pos, 5);
-}
-
-static void _display_screen(periodic_state_t *state, bool should_sound){
-    watch_clear_display();
-    watch_clear_all_indicators();
-    switch (state->mode)
-    {
-    case SCREEN_TITLE:
-        _display_title(state);
-        break;
-    case SCREEN_ELEMENT:
-    case SCREENS_COUNT:
-        _display_element(state);
-        break;
-    case SCREEN_ATOMIC_MASS:
-        _display_atomic_mass(state);
-        break;
-    case SCREEN_DISCOVER_YEAR:
-        _display_year_discovered(state);
-        break;
-    case SCREEN_ELECTRONEGATIVITY:
-        _display_electronegativity(state);
-        break;
-    case SCREEN_FULL_NAME:
-        _display_name(state);
-        break;
-    }
-    if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
-}
-
-static void _handle_forward(periodic_state_t *state, bool should_sound){
-    state->atomic_num = (state->atomic_num % MAX_ELEMENT) + 1; // Wraps back to 1
-    state->mode = SCREEN_ELEMENT;
-    _display_screen(state, false);
-    if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
-}
-
-static void _handle_backward(periodic_state_t *state, bool should_sound){
-    if (state->atomic_num <= 1) state->atomic_num = MAX_ELEMENT;
-    else state->atomic_num = state->atomic_num - 1;
-    state->mode = SCREEN_ELEMENT;
-    _display_screen(state, false);
-    if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_A6, 50);
-}
-
-static void _handle_mode_still_pressed(periodic_state_t *state, bool should_sound) {
-    if (_ts_ticks != 0){
-        if (!HAL_GPIO_BTN_MODE_read()) {
-            _ts_ticks = 0;
-            return;
-        }
-        else if (--_ts_ticks == 0){
-            switch (state->mode)
-            {
-            case SCREEN_TITLE:
-                movement_move_to_face(0);
-                return;
-            case SCREEN_ELEMENT:
-                state->mode = SCREEN_TITLE;
-                _display_screen(state, should_sound);
-                break;
-            default:
-                state->mode = SCREEN_ELEMENT;
-                _display_screen(state, should_sound);
-                break;
-            }
-            _ts_ticks = 2;
-        }
-    }
-}
-
-bool periodic_face_loop(movement_event_t event, void *context)
-{
-    periodic_state_t *state = (periodic_state_t *)context;
-    switch (event.event_type)
-    {
-    case EVENT_ACTIVATE:
-        state->mode = SCREEN_TITLE;
-        _display_screen(state, false);
-        break;
-    case EVENT_TICK:
-        if (state->mode == SCREEN_TITLE) _text_pos = _loop_text(_text_looping, _text_pos, 5);
-        else if (state->mode == SCREEN_FULL_NAME) _text_pos = _loop_text(_text_looping, _text_pos, 6);
-        if (_quick_ticks_running) {
-            if (HAL_GPIO_BTN_LIGHT_read()) _handle_backward(state, false);
-            else if (HAL_GPIO_BTN_ALARM_read()) _handle_forward(state, false);
-            else stop_quick_cyc();
-        }
-
-        _handle_mode_still_pressed(state, movement_button_should_sound());
-        break;
-    case EVENT_LIGHT_BUTTON_UP:
-        if (state->mode <= SCREEN_ELEMENT) {
-            _handle_backward(state, movement_button_should_sound());
-        }
-        else {
-            state->mode = SCREEN_ELEMENT;
-            _display_screen(state, movement_button_should_sound());
-        }
-        break;
-    case EVENT_LIGHT_BUTTON_DOWN:
-        break;
-    case EVENT_ALARM_BUTTON_UP:
-        if (state->mode <= SCREEN_ELEMENT) {
-            _handle_forward(state, movement_button_should_sound());
-        }
-        else {
-            state->mode = SCREEN_ELEMENT;
-            _display_screen(state, movement_button_should_sound());
-        }
-        break;
-    case EVENT_ALARM_LONG_PRESS:
-        if (state->mode <= SCREEN_ELEMENT) {
-            start_quick_cyc();
-            _handle_forward(state, movement_button_should_sound());
-        }
-        break;
-    case EVENT_LIGHT_LONG_PRESS:
-        if (state->mode <= SCREEN_ELEMENT) {
-            start_quick_cyc();
-            _handle_backward(state, movement_button_should_sound());
-        }
-        else {
-            movement_illuminate_led();
-        }
-        break;
-    case EVENT_MODE_BUTTON_UP:
-        if (state->mode == SCREEN_TITLE) movement_move_to_next_face();
-        else {
-            state->mode = (state->mode + 1) % SCREENS_COUNT;
-            if (state->mode == SCREEN_TITLE)
-                state->mode = (state->mode + 1) % SCREENS_COUNT;
-            if (state->mode == SCREEN_ELEMENT){
-                _display_screen(state, false);
-                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_A6, 50);
-            }
-            else
-                _display_screen(state, movement_button_should_sound());
-        }
-        break;
-    case EVENT_MODE_LONG_PRESS:
-        switch (state->mode)
-        {
-        case SCREEN_TITLE:
-            movement_move_to_face(0);
-            return true;
-        case SCREEN_ELEMENT:
-            state->mode = SCREEN_TITLE;
-            _display_screen(state, movement_button_should_sound());
-            break;
-        default:
-            state->mode = SCREEN_ELEMENT;
-            _display_screen(state, movement_button_should_sound());
-            break;
-        }
-        _ts_ticks = 2;
-        break;
-    case EVENT_TIMEOUT:
-        // Display title after timeout
-        if (state->mode == SCREEN_TITLE) break;
-        state->mode = SCREEN_TITLE;
-        _display_screen(state, false);
-        break;
-    case EVENT_LOW_ENERGY_UPDATE:
-        // Display static title and tick animation during LE
-        watch_display_string("Pd   Table", 0);
-        watch_start_sleep_animation(500);
-        break;
-    default:
-        return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void periodic_face_resign(void *context)
-{
-    (void)context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
diff --git a/movement/watch_faces/complication/periodic_face.h b/movement/watch_faces/complication/periodic_face.h
deleted file mode 100644 (file)
index 409b1d8..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 PrimmR
- * Copyright (c) 2024 David Volovskiy
- *
- * 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 PERIODIC_FACE_H_
-#define PERIODIC_FACE_H_
-
-#include "movement.h"
-
-/*
- * Periodic Table Face
- * Allows for viewing data of the Periodic Table on your wrist.
- * When looking at an element, it'll show you the atomic number on the center of the screen,
- * symbol on the right, and it's group on the top-right.
- * Pressing the mode button will cycle through the pages.
- *   Page 1: Atomic Mass
- *   Page 2: Year Discovered
- *   Page 3: Electronegativity
- *   Page 4: Full Name of the Element
- * 
- * Controls:
- *    Mode Press
- *        On Title: Next Screen
- *        Else: Cycle through info of an element
- *    Mode Hold
- *        On Title: First Screen
- *        On Element Symbol Screen: Go to Title Screen
- *        Else: Go to Symbol Screen of current element
- *    If you are in a subscreen and just keep holding MODE, you will go through all of these menus without needing to depress.
- * 
- *    Light Press
- *        On Title or Element Symbol Screen: Previous Element
- *                                     Else: Display currenlt-selected element symbol page
- *    Light Hold
- *        On Title Screen or Element Symbol: Fast Cycle through Previous Elements
- *                                     Else: Activate LED backlight
- *
- *    Alarm Press
- *        On Title or Element Symbol Screen: Next Element
- *                                     Else: Display currenlt-selected element symbol page
- *    Alarm Hold
- *        On Title Screen or Element Symbol: Fast Cycle through Next Elements
- */
-
-#define MODE_VIEW 0
-#define MODE_SELECT 1
-
-typedef struct {
-    uint8_t atomic_num;
-    uint8_t mode;
-    uint8_t selection_index;
-} periodic_state_t;
-
-void periodic_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void periodic_face_activate(void *context);
-bool periodic_face_loop(movement_event_t event, void *context);
-void periodic_face_resign(void *context);
-
-#define periodic_face ((const watch_face_t){ \
-    periodic_face_setup, \
-    periodic_face_activate, \
-    periodic_face_loop, \
-    periodic_face_resign, \
-    NULL, \
-})
-
-#endif // PERIODIC_FACE_H_
-
diff --git a/movement/watch_faces/complication/planetary_hours_face.c b/movement/watch_faces/complication/planetary_hours_face.c
deleted file mode 100644 (file)
index f4011ba..0000000
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "sunriset.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "planetary_hours_face.h"
-
-#if __EMSCRIPTEN__
-#include <emscripten.h>
-#endif
-
-// STATIC FUNCTIONS AND CONSTANTS /////////////////////////////////////////////
-
-/** @brief 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
- */
-static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin
-static const char planetes[7][3] = {"Ch", "Ze", "Ar", "He", "Af", "Hr", "Se"}; // Greek
-
-/** @brief Ruler of each weekday for easy lookup
- */
-static const uint8_t plindex[7] = {3, 6, 2, 5, 1, 4, 0}; // day ruler index
-
-/** @brief Astrological symbol for each planet
- */
-static void _planetary_icon(uint8_t planet) {
-
-    watch_clear_pixel(0, 13);
-    watch_clear_pixel(0, 14);
-    watch_clear_pixel(1, 13);
-    watch_clear_pixel(1, 14);
-    watch_clear_pixel(1, 15);
-    watch_clear_pixel(2, 13);
-    watch_clear_pixel(2, 14);
-    watch_clear_pixel(2, 15);
-
-    switch (planet) {
-        case 0: // Saturn
-            watch_set_pixel(0, 14);
-            watch_set_pixel(2, 14);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(2, 13);
-            break;
-        case 1: // Jupiter
-            watch_set_pixel(0, 14);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(1, 14);
-            break;
-        case 2: // Mars
-            watch_set_pixel(2, 14);
-            watch_set_pixel(2, 15);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(2, 13);
-            watch_set_pixel(1, 13);\
-            break;
-        case 3: // Sol
-            watch_set_pixel(0, 14);
-            watch_set_pixel(2, 14);
-            watch_set_pixel(1, 13);
-            watch_set_pixel(2, 13);
-            watch_set_pixel(0, 13);
-            watch_set_pixel(2, 15);
-            break;
-        case 4: // Venus
-            watch_set_pixel(0, 14);
-            watch_set_pixel(0, 13);
-            watch_set_pixel(1, 13);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(1, 14);
-            break;
-        case 5: // Mercury
-            watch_set_pixel(0, 14);
-            watch_set_pixel(1, 13);
-            watch_set_pixel(1, 14);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(2, 15);
-            break;
-        case 6: // Luna
-            watch_set_pixel(2, 14);
-            watch_set_pixel(2, 15);
-            watch_set_pixel(2, 13);
-            break;
-    }
-}
-
-/** @details A solar phase can be a day phase between sunrise and sunset or an alternating night phase.
- *  This function calculates the start and end of the current phase based on a given geographic location.
- *  It also calculates the start of the next following phase.
- */
-static void _planetary_solar_phases(planetary_hours_state_t *state) {
-    uint8_t phase, h;
-    double sunrise, sunset;
-    double hour_duration, next_hour_duration;
-    uint32_t now_epoch;
-    uint32_t sunrise_epoch_today, sunset_epoch_today, midnight_epoch_today;
-    uint32_t sunset_epoch_yesterday, midnight_epoch_yesterday;
-    uint32_t sunrise_epoch_tomorrow, sunset_epoch_tomorrow, midnight_epoch_tomorrow;
-    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
-
-    // check if we have a location. If not, display error
-    if (movement_location.reg == 0) {
-        watch_display_string("    no Loc", 0);
-        state->no_location = true;
-        return;
-    }
-
-    // location detected
-    state->no_location = false;
-
-    watch_date_time_t date_time = watch_rtc_get_date_time(); // the current local date / time
-    watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0); // the current date / time in UTC
-    watch_date_time_t scratch_time; // scratchpad, contains different values at different times
-    watch_date_time_t midnight;
-    scratch_time.reg = midnight.reg = utc_now.reg;
-    midnight.unit.hour = midnight.unit.minute = midnight.unit.second = 0; // start of the day at midnight
-
-    // get location coordinate
-    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
-    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
-    double lat = (double)lat_centi / 100.0;
-    double lon = (double)lon_centi / 100.0;
-
-    // save UTC offset
-    state->utc_offset = ((double)movement_get_current_timezone_offset()) / 3600.0;
-
-    // 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);
-    
-    // calculate sunrise and sunset UNIX timestamps
-    midnight_epoch_today = watch_utility_date_time_to_unix_time(midnight, 0);
-    sunrise_epoch_today = midnight_epoch_today + sunrise * 3600;
-    sunset_epoch_today = midnight_epoch_today + sunset * 3600;
-
-    // go back to yesterday and calculate sunset
-    midnight_epoch_yesterday = midnight_epoch_today - 86400;
-    scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch_yesterday, 0);
-    sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
-    sunset_epoch_yesterday = midnight_epoch_yesterday + sunset * 3600;
-
-    // go to tomorrow and calculate sunrise and sunset
-    midnight_epoch_tomorrow = midnight_epoch_today + 86400;
-    scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch_tomorrow, 0);
-    sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
-    sunrise_epoch_tomorrow = midnight_epoch_tomorrow + sunrise * 3600;
-    sunset_epoch_tomorrow = midnight_epoch_tomorrow + sunset * 3600;
-
-    // get UNIX epoch time
-    now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
-
-    // by default we assume it is daytime (phase 1) between sunrise and sunset
-    phase = 1;
-    state->phase_start = sunrise_epoch_today;
-    state->phase_end = sunset_epoch_today;
-    state->phase_next = sunrise_epoch_tomorrow;
-    state->start_at_night = false;
-
-    // night time calculations
-    if ( now_epoch < sunrise_epoch_today && now_epoch < sunset_epoch_today ) phase = 0; // morning before dawn
-    if ( now_epoch > sunrise_epoch_today && now_epoch >= sunset_epoch_today ) phase = 2; // evening after dusk
-
-    // phase 0: we are before sunrise
-    if ( phase == 0) {
-        state->phase_start = sunset_epoch_yesterday;
-        state->phase_end = sunrise_epoch_today;
-        state->phase_next = sunset_epoch_today;
-        state->start_at_night = true;
-    }
-
-    // phase 2: we are after sunset
-    if ( phase == 2) {
-        state->phase_start = sunset_epoch_today;
-        state->phase_end = sunrise_epoch_tomorrow;
-        state->phase_next = sunset_epoch_tomorrow;
-        state->start_at_night = true;
-    }
-
-    // calculate the duration of a planetary hour during this and the next solar phase
-    hour_duration = ( state->phase_end - state->phase_start ) / 12.0;
-    next_hour_duration = ( state->phase_next - state->phase_end ) / 12.0;
-
-    // populate list of 24 planetary hour start points in UNIX timestamp format
-    // starting from the beginning of the current phase
-    for ( h = 0; h < 24; h++ ) {
-        if ( h < 12 ) state->planetary_hours[h] = state->phase_start + h * hour_duration; // current phase
-        else state->planetary_hours[h] = state->phase_end + ( h - 12 ) * next_hour_duration; // next phase
-    }
-
-    // initialize
-    state->hour = 0;
-    state->ruler = 0;
-    state->skip_to_current = true;
-
-}
-
-/** @details A planetary hour is one of exactly twelve hours of a solar phase. Its length varies.
- *  This function calculates the current planetary hour and divides it up into relative minutes and seconds.
- *  It also calculates the current planetary ruler of the hour and of the day.
- */
-static void _planetary_hours(planetary_hours_state_t *state) {
-    char buf[14];
-    char ruler[3];
-    uint8_t weekday, planet, planetary_hour;
-    uint32_t current_hour_epoch;
-    watch_date_time_t scratch_time;
-
-    // check if we have a location. If not, display error
-    if ( state->no_location ) {
-        watch_display_string("    no Loc", 0);
-        return;
-    }
-
-    // get current time
-    watch_date_time_t date_time = watch_rtc_get_date_time(); // the current local date / time
-    watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0); // the current date / time in UTC
-    current_hour_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
-    
-    // set the current planetary hour as default screen
-    if ( state->skip_to_current ) {
-        state->hour = ( current_hour_epoch - state->phase_start ) / (( state->phase_end - state->phase_start ) / 12.0);
-        state->skip_to_current = false;
-    }
-    
-
-    // when current phase ends calculate the next phase
-    if ( watch_utility_date_time_to_unix_time(utc_now, 0) >= state->phase_end ) {
-        _planetary_solar_phases(state);
-        return;
-    }
-
-    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
-
-    // roll over hour iterator
-    if ( state->hour < 0 ) state->hour = 23;
-    if ( state->hour > 23 ) state->hour = 0;
-    if ( state->ruler < 0 ) state->hour = 2;
-    if ( state->ruler > 2 ) state->hour = 0;
-
-    // clear indicators
-    watch_clear_indicator(WATCH_INDICATOR_BELL);
-    watch_clear_indicator(WATCH_INDICATOR_LAP);
-
-    // display bell indicator when displaying the current planetary hour
-    if ( state->hour < 24 )
-        if ( current_hour_epoch >= state->planetary_hours[state->hour] && current_hour_epoch < state->planetary_hours[state->hour + 1]) {
-            watch_set_indicator(WATCH_INDICATOR_BELL);
-        }
-
-    // display LAP indicator when the hours of the next phase belong to the next day
-    if ( state->start_at_night == true && state->hour > 11 )
-        watch_set_indicator(WATCH_INDICATOR_LAP);
-
-    // determine weekday from start of current phase
-    scratch_time = watch_utility_date_time_from_unix_time(state->phase_start, 0);
-    scratch_time = watch_utility_date_time_convert_zone(scratch_time, 0, state->utc_offset * 3600);
-    weekday = watch_utility_get_iso8601_weekday_number(scratch_time.unit.year, scratch_time.unit.month, scratch_time.unit.day) - 1;
-    
-    // which planetary hour are we in?
-    planetary_hour = state->hour % 12;
-
-    // accomodate night hour count
-    if ( state->hour < 12 ) {
-        if ( state->start_at_night ) {
-            planetary_hour += 12;
-        }
-    } else {
-        if ( state->start_at_night ) {
-            weekday = ( weekday + 1 ) % 7;
-        } else {
-            planetary_hour += 12;
-        }
-    }
-
-    // make datetime object for selected planetary hour
-    scratch_time = watch_utility_date_time_from_unix_time(state->planetary_hours[state->hour], 0);
-    scratch_time = watch_utility_date_time_convert_zone(scratch_time, 0, state->utc_offset * 3600);
-    
-    // round minutes
-    if (scratch_time.unit.second < 30 && scratch_time.unit.minute > 0 ) scratch_time.unit.minute--;
-    else if ( scratch_time.unit.minute < 59 ) scratch_time.unit.minute++;
-
-    // if we are in 12 hour mode, do some cleanup
-    if (!movement_clock_mode_24h()) {
-        if (scratch_time.unit.hour < 12) {
-            watch_clear_indicator(WATCH_INDICATOR_PM);
-        } else {
-            watch_set_indicator(WATCH_INDICATOR_PM);
-        }
-        scratch_time.unit.hour %= 12;
-        if (scratch_time.unit.hour == 0) scratch_time.unit.hour = 12;
-    }
-    
-    // planetary ruler of the hour
-    planet = ( plindex[weekday] + planetary_hour ) % 7;
-
-    // latin or greek ruler names or astrological symbol
-    if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3);
-    if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3);
-    if ( state->ruler == 2 ) strncpy(ruler, "  ", 3);
-
-    // display planetary time with ruler of the hour or ruler of the day
-    sprintf(buf, "%s%2d%2d%02d  ", ruler, (planetary_hour % 24) + 1, scratch_time.unit.hour, scratch_time.unit.minute);
-
-    watch_set_colon();
-    watch_display_string(buf, 0);
-
-    if ( state->ruler == 2 ) _planetary_icon(planet);
-}
-
-// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
-
-void planetary_hours_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(planetary_hours_state_t));
-        memset(*context_ptr, 0, sizeof(planetary_hours_state_t));
-    }
-}
-
-void planetary_hours_face_activate(void *context) {
-    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
-
-#if __EMSCRIPTEN__
-    int16_t browser_lat = EM_ASM_INT({ return lat; });
-    int16_t browser_lon = EM_ASM_INT({ return lon; });
-    if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
-        movement_location_t browser_loc;
-        browser_loc.bit.latitude = browser_lat;
-        browser_loc.bit.longitude = browser_lon;
-        watch_store_backup_data(browser_loc.reg, 1);
-    }
-#endif
-
-    planetary_hours_state_t *state = (planetary_hours_state_t *)context; 
-    _planetary_solar_phases(state);
-
-}
-
-bool planetary_hours_face_loop(movement_event_t event, void *context) {
-    planetary_hours_state_t *state = (planetary_hours_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Show your initial UI here.
-            watch_clear_indicator(WATCH_INDICATOR_PM);
-            watch_clear_indicator(WATCH_INDICATOR_24H);
-            _planetary_hours(state);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            state->ruler = (state->ruler + 1) % 3;
-            _planetary_hours(state);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            state->skip_to_current = true;
-            _planetary_hours(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            state->hour++;
-            _planetary_hours(state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->hour--;
-            _planetary_hours(state);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-    return true;
-}
-
-void planetary_hours_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/complication/planetary_hours_face.h b/movement/watch_faces/complication/planetary_hours_face.h
deleted file mode 100644 (file)
index 5e94aa0..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- * Copyright (c) 2022 Joey Castillo (sunrise_sunset_face)
- *
- * 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 planetary_hours_face_H_
-#define planetary_hours_face_H_
-
-/*
- * PLANETARY HOURS face
- * 
- * Background
- *
- * Both the 24 hour day and the order of our weekdays have quite esoteric roots.
- * The ancient Egyptians divided the day up into 12 hours of sunlight and 12 hours
- * of night time. Obviously the length of these hours varied throughout the year.
- * 
- * The Greeks assigned each hour a ruler of the planetary gods in the ancient
- * "Chaldean" order from slowest (Chronos for Saturn) to fastest (Selene for Moon).
- * Because 24 hours cannot be equally divided by seven, the planetary rulers carried
- * over to the first hour of the next day, effectively ruling over the entire day 
- * and lending the whole day their name. The seven day week was born.
- * 
- * PLANETARY HOUR CHART COMPLICATION
- * 
- * This complication watch face displays the start time of the current planetary hour 
- * according to the given location and day of the year. The number of the current
- * planetary hour (1 - 24) is indicated at the top right.
- * 
- * Short pressing the ALARM button flips through the start times of the following 
- * planetary hours, long pressing it flips backwards in time. A long press of the 
- * LIGHT button immediately switches back to the start time of the current hour.
- * The Bell indicator always marks the current planetary hour in the list.
- * The LAP indicator shows up when the hours of the next phase are part of the 
- * upcoming day instead of the current one. This happens when the watch face is 
- * launched after sunset.
- * 
- * The planetary ruler of the current hour and day is displayed at the top in 
- * Latin or Greek shorthand notation:
- * 
- * Saturn (SA) / Chronos (CH) / ♄
- * Jupiter (JU) / Zeus (ZE) / ♃
- * Mars (MA) / Ares (AR) / ♂
- * Sol (SO) / Helios (HE) / ☉
- * Venus (VE) / Aphrodite (AF) / ♀
- * Mercury (ME) / Hermes (HR) / ☿
- * Luna (LU) / Selene (SE) / ☾
- * 
- * A short press of the LIGHT button toggles between Latin and Greek ruler shorthand
- * notation.
- * 
- * (IMPORTANT: Make sure the watch's time, timezone and location are set correctly for this
- * watch face to work properly!)
- */
-
-#include "movement.h"
-#include "sunrise_sunset_face.h"
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint32_t planetary_hours[24];
-    uint32_t phase_start;
-    uint32_t phase_end;
-    uint32_t phase_next;
-    bool next;
-    double utc_offset;
-    bool no_location;
-    int8_t hour;
-    int8_t ruler;
-    bool start_at_night;
-    bool skip_to_current;
-    sunrise_sunset_state_t sunstate;
-} planetary_hours_state_t;
-
-void planetary_hours_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void planetary_hours_face_activate(void *context);
-bool planetary_hours_face_loop(movement_event_t event, void *context);
-void planetary_hours_face_resign(void *context);
-
-#define planetary_hours_face ((const watch_face_t){ \
-    planetary_hours_face_setup, \
-    planetary_hours_face_activate, \
-    planetary_hours_face_loop, \
-    planetary_hours_face_resign, \
-    NULL, \
-})
-
-#endif // planetary_hours_face_H_
-
diff --git a/movement/watch_faces/complication/planetary_time_face.c b/movement/watch_faces/complication/planetary_time_face.c
deleted file mode 100644 (file)
index d19937b..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "sunriset.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "planetary_time_face.h"
-
-#if __EMSCRIPTEN__
-#include <emscripten.h>
-#endif
-
-// STATIC FUNCTIONS AND CONSTANTS /////////////////////////////////////////////
-
-/** @brief 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
- */
-static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin
-static const char planetes[7][3] = {"Ch", "Ze", "Ar", "He", "Af", "Hr", "Se"}; // Greek
-
-/** @brief Ruler of each weekday for easy lookup
- */
-static const uint8_t plindex[7] = {3, 6, 2, 5, 1, 4, 0}; // day ruler index
-
-/** @brief Astrological symbol for each planet
- */
-static void _planetary_icon(uint8_t planet) {
-
-    watch_clear_pixel(0, 13);
-    watch_clear_pixel(0, 14);
-    watch_clear_pixel(1, 13);
-    watch_clear_pixel(1, 14);
-    watch_clear_pixel(1, 15);
-    watch_clear_pixel(2, 13);
-    watch_clear_pixel(2, 14);
-    watch_clear_pixel(2, 15);
-
-    switch (planet) {
-        case 0: // Saturn
-            watch_set_pixel(0, 14);
-            watch_set_pixel(2, 14);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(2, 13);
-            break;
-        case 1: // Jupiter
-            watch_set_pixel(0, 14);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(1, 14);
-            break;
-        case 2: // Mars
-            watch_set_pixel(2, 14);
-            watch_set_pixel(2, 15);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(2, 13);
-            watch_set_pixel(1, 13);\
-            break;
-        case 3: // Sol
-            watch_set_pixel(0, 14);
-            watch_set_pixel(2, 14);
-            watch_set_pixel(1, 13);
-            watch_set_pixel(2, 13);
-            watch_set_pixel(0, 13);
-            watch_set_pixel(2, 15);
-            break;
-        case 4: // Venus
-            watch_set_pixel(0, 14);
-            watch_set_pixel(0, 13);
-            watch_set_pixel(1, 13);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(1, 14);
-            break;
-        case 5: // Mercury
-            watch_set_pixel(0, 14);
-            watch_set_pixel(1, 13);
-            watch_set_pixel(1, 14);
-            watch_set_pixel(1, 15);
-            watch_set_pixel(2, 15);
-            break;
-        case 6: // Luna
-            watch_set_pixel(2, 14);
-            watch_set_pixel(2, 15);
-            watch_set_pixel(2, 13);
-            break;
-    }
-}
-
-/** @details solar phase can be a day phase between sunrise and sunset or an alternating night phase.
- *  This function calculates the start and end of the current phase based on a given geographic location.
- */
-static void _planetary_solar_phase(planetary_time_state_t *state) {
-    uint8_t phase;
-    double sunrise, sunset;
-    uint32_t now_epoch, sunrise_epoch, sunset_epoch, midnight_epoch;
-    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
-
-    // check if we have a location. If not, display error
-    if (movement_location.reg == 0) {
-        watch_display_string("    no Loc", 0);
-        state->no_location = true;
-        return;
-    }
-
-    // location detected
-    state->no_location = false;
-
-    watch_date_time_t date_time = watch_rtc_get_date_time(); // the current local date / time
-    watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0); // the current date / time in UTC
-    watch_date_time_t scratch_time; // scratchpad, contains different values at different times
-    watch_date_time_t midnight;
-    scratch_time.reg = midnight.reg = utc_now.reg;
-    midnight.unit.hour = midnight.unit.minute = midnight.unit.second = 0; // start of the day at midnight
-
-    // get location coordinate
-    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
-    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
-    double lat = (double)lat_centi / 100.0;
-    double lon = (double)lon_centi / 100.0;
-
-    // save UTC offset
-    state->utc_offset = ((double)movement_get_current_timezone_offset()) / 3600.0;
-
-    // get UNIX epoch time
-    now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
-    midnight_epoch = watch_utility_date_time_to_unix_time(midnight, 0);
-
-    // 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);
-    
-    // calculate sunrise and sunset UNIX timestamps
-    sunrise_epoch = midnight_epoch + sunrise * 3600;
-    sunset_epoch = midnight_epoch + sunset * 3600;
-
-    // by default we assume it is daytime (phase 1) between sunrise and sunset
-    phase = 1;
-    state->night = false;
-    state->phase_start = sunrise_epoch;
-    state->phase_end = sunset_epoch;
-
-    // night time calculations
-    if ( now_epoch < sunrise_epoch && now_epoch < sunset_epoch ) phase = 0; // morning before dawn
-    if ( now_epoch > sunrise_epoch && now_epoch >= sunset_epoch ) phase = 2; // evening after dusk
-
-    // phase 0: we are before sunrise
-    if ( phase == 0) {
-        // go back to yesterday and calculate sunset
-        midnight_epoch -= 86400;
-        scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch, 0);
-        sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
-        sunset_epoch = midnight_epoch + sunset * 3600;
-        // we are still in yesterday's night hours
-        state->night = true;
-        state->phase_start = sunset_epoch;
-        state->phase_end = sunrise_epoch;
-    }
-
-    // phase 2: we are after sunset
-    if ( phase == 2) {
-        // skip to tomorrow and calculate sunrise
-        midnight_epoch += 86400;
-        scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch, 0);
-        sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
-        sunrise_epoch = midnight_epoch + sunrise * 3600;
-        // we are still in yesterday's night hours
-        state->night = true;
-        state->phase_start = sunset_epoch;
-        state->phase_end = sunrise_epoch;
-    }
-
-    // calculate the duration of a planetary second during this solar phase 
-    // and convert to Hertz so we can call a faster tick rate
-    state->freq = (1 / ((double)( state->phase_end - state->phase_start ) / 43200));
-}
-
-/** @details A planetary hour is one of exactly twelve hours of a solar phase. Its length varies.
- *  This function calculates the current planetary hour and divides it up into relative minutes and seconds.
- *  It also calculates the current planetary ruler of the hour and of the day.
- */
-static void _planetary_time(movement_event_t event, planetary_time_state_t *state) {
-    char buf[14];
-    char ruler[3];
-    double night_hour_count = 0.0;
-    uint8_t weekday, planet, planetary_hour;
-    double hour_duration, current_hour, current_minute, current_second;
-
-        watch_set_colon();
-
-    // get current time and convert to UTC
-    state->scratch = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_get_current_timezone_offset(), 0); 
-
-    // when current phase ends calculate the next phase
-    if ( watch_utility_date_time_to_unix_time(state->scratch, 0) >= state->phase_end ) {
-        _planetary_solar_phase(state);
-        return;
-    }
-
-    if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
-
-    // PM for night hours, otherwise the night hours are counted from 13
-    if ( state->night ) {
-        if (movement_clock_mode_24h()) night_hour_count = 12;
-        else watch_set_indicator(WATCH_INDICATOR_PM);
-    }
-
-    // calculate the duration of a planetary hour during this solar phase
-    hour_duration = (( state->phase_end - state->phase_start)) / 12.0;
-
-    // which planetary hour are we in?
-
-    // RTC only provides full second precision, so we have to manually add subseconds with each tick
-    current_hour = ((( watch_utility_date_time_to_unix_time(state->scratch, 0) ) + event.subsecond * 0.11111111) - state->phase_start ) / hour_duration;
-    planetary_hour = floor(current_hour) + ( state->night ? 12 : 0 );
-    current_hour  += night_hour_count; //adjust for 24hr display
-    current_minute = modf(current_hour, &current_hour) * 60.0;
-    current_second = modf(current_minute, &current_minute) * 60.0;
-
-    // the day changes after sunrise, so if we are at night it is yesterday's planetary day
-    // hence we take the datetime object of when the last solar phase started as the current day
-    // and then fill in the hours and minutes
-    state->scratch = watch_utility_date_time_from_unix_time(state->phase_start, 0);
-    state->scratch.unit.hour = floor(current_hour);
-    state->scratch.unit.minute = floor(current_minute);
-    state->scratch.unit.second = (uint8_t)floor(current_second) % 60;
-
-    // what weekday is it (0 - 6)
-    weekday = watch_utility_get_iso8601_weekday_number(state->scratch.unit.year, state->scratch.unit.month, state->scratch.unit.day) - 1;
-
-    // planetary ruler of the hour or the day
-    if ( state->day_ruler ) planet = plindex[weekday];
-    else planet = ( plindex[weekday] + planetary_hour ) % 7;
-
-    // latin or greek ruler names or astrological symbol
-    if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3);
-    if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3);
-    if ( state->ruler == 2 ) strncpy(ruler, "  ", 3);
-    
-    // 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);
-    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);
-
-    if ( state->ruler == 2 ) _planetary_icon(planet);
-
-}
-
-// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
-
-void planetary_time_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(planetary_time_state_t));
-        memset(*context_ptr, 0, sizeof(planetary_time_state_t));
-    }
-}
-
-void planetary_time_face_activate(void *context) {
-    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
-
-#if __EMSCRIPTEN__
-    int16_t browser_lat = EM_ASM_INT({ return lat; });
-    int16_t browser_lon = EM_ASM_INT({ return lon; });
-    if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
-        movement_location_t browser_loc;
-        browser_loc.bit.latitude = browser_lat;
-        browser_loc.bit.longitude = browser_lon;
-        watch_store_backup_data(browser_loc.reg, 1);
-    }
-#endif
-
-    planetary_time_state_t *state = (planetary_time_state_t *)context;
-    
-    // calculate phase
-    _planetary_solar_phase(state);
-}
-
-bool planetary_time_face_loop(movement_event_t event, void *context) {
-    planetary_time_state_t *state = (planetary_time_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _planetary_time(event, state);
-            if ( state->freq > 1 )
-                // for hours with shorter seconds let's increase the tick to not skip seconds in the display
-                movement_request_tick_frequency( 8 );
-            break;
-        case EVENT_TICK:
-            _planetary_time(event, state);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            state->ruler = (state->ruler + 1) % 3;
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // Just in case you have need for another button.
-            state->day_ruler = !state->day_ruler;
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            watch_start_sleep_animation(500);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void planetary_time_face_resign(void *context) {
-    (void) context;
-    movement_request_tick_frequency( 1 );
-}
-
diff --git a/movement/watch_faces/complication/planetary_time_face.h b/movement/watch_faces/complication/planetary_time_face.h
deleted file mode 100644 (file)
index 0dd594e..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- * Copyright (c) 2022 Joey Castillo (sunrise_sunset_face)
- *
- * 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 planetary_time_face_H_
-#define planetary_time_face_H_
-
-/*
- * PLANETARY TIME face
- *
- * BACKGROUND
- *
- * Both the 24 hour day and the order of our weekdays have quite esoteric roots.
- * The ancient Egyptians divided the day up into 12 hours of sunlight and 12 hours
- * of night time. Obviously the length of these hours varied throughout the year.
- * 
- * The Greeks assigned each hour a ruler of the planetary gods in the ancient
- * "Chaldean" order from slowest (Chronos for Saturn) to fastest (Selene for Moon).
- * Because 24 hours cannot be equally divided by seven, the planetary rulers carried
- * over to the first hour of the next day, effectively ruling over the entire day 
- * and lending the whole day their name. The seven day week was born.
- * 
- * PLANETARY TIME COMPLICATION
- * 
- * The hour digits of this complication watch-face display the current planetary hour 
- * according to the given location and day of the year (First hour from 12am to 1am,
- * the second hour from 1am to 2am, and so forth).
- * 
- * Like with normal clocks the minutes and seconds help dividing the hour into smaller
- * units. On this watch-face, all units naturally vary in length because the planetary
- * hours are not fixed by duration but by the moments of sunrise and sunset which 
- * obviously vary throughout the year, especially in higher latitudes.
- * 
- * On this watch-face the hours indicated as 12am to 12pm (00:00 - 12:00) are used for
- * the planetary daytime hours between sunrise and sunset and hours indicated as 12pm 
- * to 12am (12:00 - 00:00) are used for the planetary night hours after sunset and before 
- * sunrise.
- * 
- * The planetary ruler of the current hour and day is displayed at the top in Latin or 
- * Greek shorthand notation:
- * 
- * Saturn (SA) / Chronos (CH) / ♄
- * Jupiter (JU) / Zeus (ZE) / ♃
- * Mars (MA) / Ares (AR) / ♂
- * Sol (SO) / Helios (HE) / ☉
- * Venus (VE) / Aphrodite (AF) / ♀
- * Mercury (ME) / Hermes (HR) / ☿
- * Luna (LU) / Selene (SE) / ☾
- * 
- * The ALARM button toggles between displaying the ruler of the hour and the ruler of the day
- * 
- * The LIGHT button toggles between Latin and Greek ruler shorthand notation
- * 
- * (IMPORTANT: Make sure the watch's time, timezone and location are set correctly for this
- * watch face to work properly!)
- */
-
-#include "movement.h"
-#include "sunrise_sunset_face.h"
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint32_t phase_start;
-    uint32_t phase_end;
-    bool night;
-    double utc_offset;
-    double freq;
-    uint8_t ruler;
-    bool day_ruler;
-    bool no_location;
-    sunrise_sunset_state_t sunstate;
-    watch_date_time_t scratch;
-} planetary_time_state_t;
-
-void planetary_time_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void planetary_time_face_activate(void *context);
-bool planetary_time_face_loop(movement_event_t event, void *context);
-void planetary_time_face_resign(void *context);
-
-#define planetary_time_face ((const watch_face_t){ \
-    planetary_time_face_setup, \
-    planetary_time_face_activate, \
-    planetary_time_face_loop, \
-    planetary_time_face_resign, \
-    NULL, \
-})
-
-#endif // planetary_time_face_H_
-
diff --git a/movement/watch_faces/complication/probability_face.c b/movement/watch_faces/complication/probability_face.c
deleted file mode 100644 (file)
index 5698430..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Spencer Bywater
- *
- * 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.
- */
-
-// Emulator only: need time() to seed the random number generator.
-#if __EMSCRIPTEN__
-#include <time.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include "probability_face.h"
-
-#define DEFAULT_DICE_SIDES 2
-#define PROBABILITY_ANIMATION_TICK_FREQUENCY 8
-const uint16_t NUM_DICE_TYPES = 8; // Keep this consistent with # of dice types below
-const uint16_t DICE_TYPES[] = {2, 4, 6, 8, 10, 12, 20, 100};
-
-
-// --------------
-// Custom methods
-// --------------
-
-static void display_dice_roll(probability_state_t *state) {
-    char buf[8];
-    if (state->rolled_value == 0) {
-        if (state->dice_sides == 100) {
-            sprintf(buf, " C    ");
-        } else {
-            sprintf(buf, "%2d    ", state->dice_sides);
-        }
-    } else if (state->dice_sides == 2) {
-        if (state->rolled_value == 1) {
-            sprintf(buf, "%2d   H", state->dice_sides);
-        } else {
-            sprintf(buf, "%2d   T", state->dice_sides);
-        }
-    } else if (state->dice_sides == 100) {
-        sprintf(buf, " C %3d", state->rolled_value);
-    } else {
-        sprintf(buf, "%2d %3d", state->dice_sides, state->rolled_value);
-    }
-    watch_display_string(buf, 4);
-}
-
-static void generate_random_number(probability_state_t *state) {
-    // Emulator: use rand. Hardware: use arc4random.
-    #if __EMSCRIPTEN__
-    state->rolled_value = rand() % state->dice_sides + 1;
-    #else
-    state->rolled_value = arc4random_uniform(state->dice_sides) + 1;
-    #endif
-}
-
-static void display_dice_roll_animation(probability_state_t *state) {
-    if (state->is_rolling) {
-        if (state->animation_frame == 0) {
-            watch_display_string("   ", 7);
-            watch_set_pixel(1, 4);
-            watch_set_pixel(1, 6);
-            state->animation_frame = 1;
-        } else if (state->animation_frame == 1) {
-            watch_clear_pixel(1, 4);
-            watch_clear_pixel(1, 6);
-            watch_set_pixel(2, 4);
-            watch_set_pixel(0, 6);
-            state->animation_frame = 2;
-        } else if (state->animation_frame == 2) {
-            watch_clear_pixel(2, 4);
-            watch_clear_pixel(0, 6);
-            watch_set_pixel(2, 5);
-            watch_set_pixel(0, 5);
-            state->animation_frame = 3;
-        } else if (state->animation_frame == 3) {
-            state->animation_frame = 0;
-            state->is_rolling = false;
-            movement_request_tick_frequency(1);
-            display_dice_roll(state);
-        }
-    }
-}
-
-
-// ---------------------------
-// Standard watch face methods
-// ---------------------------
-void probability_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(probability_state_t));
-        memset(*context_ptr, 0, sizeof(probability_state_t));
-    }
-    // Emulator only: Seed random number generator
-    #if __EMSCRIPTEN__
-    srand(time(NULL));
-    #endif
-}
-
-void probability_face_activate(void *context) {
-    probability_state_t *state = (probability_state_t *)context;
-
-    state->dice_sides = DEFAULT_DICE_SIDES;
-    state->rolled_value = 0;
-    watch_display_string("PR", 0);
-}
-
-bool probability_face_loop(movement_event_t event, void *context) {
-    probability_state_t *state = (probability_state_t *)context;
-
-    if (state->is_rolling && event.event_type != EVENT_TICK) {
-        return true;
-    }
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            display_dice_roll(state);
-            break;
-        case EVENT_TICK:
-            display_dice_roll_animation(state);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // Change how many sides the die has
-            for (int i = 0; i < NUM_DICE_TYPES; i++) {
-                if (DICE_TYPES[i] == state->dice_sides) {
-                    if (i == NUM_DICE_TYPES - 1) {
-                        state->dice_sides = DICE_TYPES[0];
-                    } else {
-                        state->dice_sides = DICE_TYPES[i + 1];
-                    }
-                    break;
-                }
-            }
-            state->rolled_value = 0;
-            display_dice_roll(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // Roll the die
-            generate_random_number(state);
-            state->is_rolling = true;
-            // Dice rolling animation begins on next tick and new roll will be displayed on completion
-            movement_request_tick_frequency(PROBABILITY_ANIMATION_TICK_FREQUENCY);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            watch_display_string("SLEEP ", 4);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void probability_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/probability_face.h b/movement/watch_faces/complication/probability_face.h
deleted file mode 100644 (file)
index 6ce1b1f..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Spencer Bywater
- *
- * 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 PROBABILITY_FACE_H_
-#define PROBABILITY_FACE_H_
-
-/*
- * PROBABILITY face
- *
- * This face is a dice-rolling random number generator.
- * Supports dice with 2, 4, 6, 8, 10, 12, 20, or 100 sides.
- *
- * Press LIGHT to cycle through die type.
- * The current die size is indicated on the left ("C" for 100)
- *
- * Press ALARM to roll the selected die.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint8_t dice_sides;
-    uint8_t rolled_value;
-    uint8_t animation_frame;
-    bool is_rolling;
-} probability_state_t;
-
-void probability_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void probability_face_activate(void *context);
-bool probability_face_loop(movement_event_t event, void *context);
-void probability_face_resign(void *context);
-
-#define probability_face ((const watch_face_t){ \
-    probability_face_setup, \
-    probability_face_activate, \
-    probability_face_loop, \
-    probability_face_resign, \
-    NULL, \
-})
-
-#endif // PROBABILITY_FACE_H_
-
diff --git a/movement/watch_faces/complication/pulsometer_face.c b/movement/watch_faces/complication/pulsometer_face.c
deleted file mode 100644 (file)
index 1c29080..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-
-/*
- * MIT License
- *
- * Copyright © 2021-2022 Joey Castillo <joeycastillo@utexas.edu> <jose.castillo@gmail.com>
- * Copyright © 2023 Jeremy O'Brien <neutral@fastmail.com>
- * Copyright © 2024 Matheus Afonso Martins Moreira <matheus.a.m.moreira@gmail.com> (https://www.matheusmoreira.com/)
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "pulsometer_face.h"
-#include "watch.h"
-
-#ifndef PULSOMETER_FACE_TITLE
-#define PULSOMETER_FACE_TITLE "PL"
-#endif
-
-#ifndef PULSOMETER_FACE_CALIBRATION_DEFAULT
-#define PULSOMETER_FACE_CALIBRATION_DEFAULT (30)
-#endif
-
-#ifndef PULSOMETER_FACE_CALIBRATION_INCREMENT
-#define PULSOMETER_FACE_CALIBRATION_INCREMENT (10)
-#endif
-
-// tick frequency will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.)
-#ifndef PULSOMETER_FACE_FREQUENCY_FACTOR
-#define PULSOMETER_FACE_FREQUENCY_FACTOR (4ul)
-#endif
-
-#define PULSOMETER_FACE_FREQUENCY (1 << PULSOMETER_FACE_FREQUENCY_FACTOR)
-
-typedef struct {
-    bool measuring;
-    int16_t pulses;
-    int16_t ticks;
-    int8_t calibration;
-} pulsometer_state_t;
-
-static void pulsometer_display_title(pulsometer_state_t *pulsometer) {
-    (void) pulsometer;
-    watch_display_string(PULSOMETER_FACE_TITLE, 0);
-}
-
-static void pulsometer_display_calibration(pulsometer_state_t *pulsometer) {
-    char buf[3];
-    snprintf(buf, sizeof(buf), "%2hhd", pulsometer->calibration);
-    watch_display_string(buf, 2);
-}
-
-static void pulsometer_display_measurement(pulsometer_state_t *pulsometer) {
-    char buf[7];
-    snprintf(buf, sizeof(buf), "%-6hd", pulsometer->pulses);
-    watch_display_string(buf, 4);
-}
-
-static void pulsometer_indicate(pulsometer_state_t *pulsometer) {
-    if (pulsometer->measuring) {
-        watch_set_indicator(WATCH_INDICATOR_LAP);
-    } else {
-        watch_clear_indicator(WATCH_INDICATOR_LAP);
-    }
-}
-
-static void pulsometer_start_measurement(pulsometer_state_t *pulsometer) {
-    pulsometer->measuring = true;
-    pulsometer->pulses = INT16_MAX;
-    pulsometer->ticks = 0;
-
-    pulsometer_indicate(pulsometer);
-
-    movement_request_tick_frequency(PULSOMETER_FACE_FREQUENCY);
-}
-
-static void pulsometer_measure(pulsometer_state_t *pulsometer) {
-    if (!pulsometer->measuring) { return; }
-
-    pulsometer->ticks++;
-
-    float ticks_per_minute = 60 << PULSOMETER_FACE_FREQUENCY_FACTOR;
-    float pulses_while_button_held = ticks_per_minute / pulsometer->ticks;
-    float calibrated_pulses = pulses_while_button_held * pulsometer->calibration;
-    calibrated_pulses += 0.5f;
-
-    pulsometer->pulses = (int16_t) calibrated_pulses;
-
-    pulsometer_display_measurement(pulsometer);
-}
-
-static void pulsometer_stop_measurement(pulsometer_state_t *pulsometer) {
-    movement_request_tick_frequency(1);
-
-    pulsometer->measuring = false;
-
-    pulsometer_display_measurement(pulsometer);
-    pulsometer_indicate(pulsometer);
-}
-
-static void pulsometer_cycle_calibration(pulsometer_state_t *pulsometer, int8_t increment) {
-    if (pulsometer->measuring) { return; }
-
-    if (pulsometer->calibration <= 0) {
-        pulsometer->calibration = 1;
-    }
-
-    int8_t last = pulsometer->calibration;
-    pulsometer->calibration += increment;
-
-    if (pulsometer->calibration > 39) {
-        pulsometer->calibration = last == 39? 1 : 39;
-    }
-
-    pulsometer_display_calibration(pulsometer);
-}
-
-void pulsometer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        pulsometer_state_t *pulsometer = malloc(sizeof(pulsometer_state_t));
-
-        pulsometer->calibration = PULSOMETER_FACE_CALIBRATION_DEFAULT;
-        pulsometer->pulses = 0;
-        pulsometer->ticks = 0;
-
-        *context_ptr = pulsometer;
-    }
-}
-
-void pulsometer_face_activate(void *context) {
-
-    pulsometer_state_t *pulsometer = context;
-
-    pulsometer->measuring = false;
-
-    pulsometer_display_title(pulsometer);
-    pulsometer_display_calibration(pulsometer);
-    pulsometer_display_measurement(pulsometer);
-}
-
-bool pulsometer_face_loop(movement_event_t event, void *context) {
-
-    pulsometer_state_t *pulsometer = (pulsometer_state_t *) context;
-
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_DOWN:
-            pulsometer_start_measurement(pulsometer);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-        case EVENT_ALARM_LONG_UP:
-            pulsometer_stop_measurement(pulsometer);
-            break;
-        case EVENT_TICK:
-            pulsometer_measure(pulsometer);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            pulsometer_cycle_calibration(pulsometer, 1);
-            break;
-        case EVENT_LIGHT_LONG_UP:
-            pulsometer_cycle_calibration(pulsometer, PULSOMETER_FACE_CALIBRATION_INCREMENT);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // Inhibit the LED
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void pulsometer_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/pulsometer_face.h b/movement/watch_faces/complication/pulsometer_face.h
deleted file mode 100644 (file)
index 78c906c..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-
-/*
- * MIT License
- *
- * Copyright © 2021-2022 Joey Castillo <joeycastillo@utexas.edu> <jose.castillo@gmail.com>
- * Copyright © 2022 Alexsander Akers <me@a2.io>
- * Copyright © 2023 Alex Utter <ooterness@gmail.com>
- * Copyright © 2024 Matheus Afonso Martins Moreira <matheus.a.m.moreira@gmail.com> (https://www.matheusmoreira.com/)
- *
- * 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 PULSOMETER_FACE_H_
-#define PULSOMETER_FACE_H_
-
-/*
- * PULSOMETER face
- *
- * The pulsometer implements a classic mechanical watch complication.
- * A mechanical pulsometer involves a chronograph with a scale that
- * allows the user to compute the number of heart beats per minute
- * in less time. The scale is calibrated, or graduated, for a fixed
- * number of heart beats, most often 30. The user starts the chronograph
- * and simultaneously begins counting the heart beats. The movement of
- * the chronograph's seconds hand over time automatically performs the
- * computations required. When the calibrated number of heart beats
- * is reached, the chronograph is stopped and the seconds hand shows
- * the heart rate.
- *
- * The Sensor Watch pulsometer improves this design with user calibration:
- * it can be graduated to any value between 1 and 39 pulsations per minute.
- * The default is still 30, mirroring the classic pulsometer calibration.
- * This feature allows the user to reconfigure the pulsometer to count
- * many other types of periodic minutely events, making it more versatile.
- * For example, it can be set to 5 respirations per minute to turn it into
- * an asthmometer, a nearly identical mechanical watch complication
- * that doctors might use to quickly measure respiratory rate.
- *
- * To use the pulsometer, hold the ALARM button and count the pulses.
- * When the calibrated number of pulses is reached, release the button.
- * The display will show the number of pulses per minute.
- *
- * In order to measure heart rate, feel for a pulse using the hand with
- * the watch while holding the button down with the other.
- * The pulse can be easily felt on the carotid artery of the neck.
- *
- * In order to measure breathing rate, simply hold the ALARM button
- * and count the number of breaths.
- *
- * To calibrate the pulsometer, press LIGHT
- * to cycle to the next integer calibration.
- * Long press LIGHT to cycle it by 10.
- */
-
-#include "movement.h"
-
-void pulsometer_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void pulsometer_face_activate(void *context);
-bool pulsometer_face_loop(movement_event_t event, void *context);
-void pulsometer_face_resign(void *context);
-
-#define pulsometer_face ((const watch_face_t){ \
-    pulsometer_face_setup, \
-    pulsometer_face_activate, \
-    pulsometer_face_loop, \
-    pulsometer_face_resign, \
-    NULL, \
-})
-
-#endif // PULSOMETER_FACE_H_
diff --git a/movement/watch_faces/complication/randonaut_face.c b/movement/watch_faces/complication/randonaut_face.c
deleted file mode 100644 (file)
index fd9ca5d..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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.
- */
-
-// Emulator only: need time() to seed the random number generator.
-#if __EMSCRIPTEN__
-#include <time.h>
-#else
-#include "saml22j18a.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "filesystem.h"
-#include "randonaut_face.h"
-
-#define R 6371 // Earth's radius in km
-#define PI 3.14159265358979323846
-
-static void _get_location_from_file(randonaut_state_t *state);
-static void _save_point_to_file(randonaut_state_t *state);
-static void _get_entropy(randonaut_state_t *state);
-static void _generate_blindspot(randonaut_state_t *state);
-static void _randonaut_face_display(randonaut_state_t *state);
-static void _generate_blindspot(randonaut_state_t *state);
-static uint32_t _get_pseudo_entropy(uint32_t max);
-static uint32_t _get_true_entropy(void);
-static void _get_entropy(randonaut_state_t *state);
-
-// MOVEMENT WATCH FACE FUNCTIONS //////////////////////////////////////////////
-
-void randonaut_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(randonaut_state_t));
-        memset(*context_ptr, 0, sizeof(randonaut_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void randonaut_face_activate(void *context) {
-    randonaut_state_t *state = (randonaut_state_t *)context;
-    _get_location_from_file(state);
-    state->face.mode = 0;
-    state->radius = 1000;
-    _get_entropy(state);
-    state->chance = true;
-    // Handle any tasks related to your watch face coming on screen.
-}
-
-bool randonaut_face_loop(movement_event_t event, void *context) {
-    randonaut_state_t *state = (randonaut_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Show your initial UI here.
-            break;
-        case EVENT_TICK:
-            // If needed, update your display here.
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            switch ( state->face.mode ) {
-                case 0: // home
-                    state->face.mode = 2; //point
-                    state->face.location_format = 0; // title
-                    break;
-                case 1: // generate
-                    state->face.mode = 0; //home
-                    break;
-                case 2: // point
-                    state->face.mode = 0; //home
-                    break;
-                case 3: // setup radius
-                    state->face.mode = 4; // toggle to RNG
-                    break;
-                case 4: // setup RNG
-                    state->face.mode = 3; // toggle to Radius
-                    break;
-                case 5: // data processing
-                    break;
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            switch ( state->face.mode ) {
-                case 3: // setup
-                case 4:
-                    state->face.mode = 0; //home
-                    break;
-                default:
-                    state->face.mode = 3; //setup
-                    watch_clear_display();
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch ( state->face.mode ) {
-                case 0: //home
-                    state->face.mode = 1; // generate
-                    break;
-                case 2: // point
-                    state->face.location_format = (( state->face.location_format + 1) % (7));
-                    if ( state->face.location_format == 0 ) 
-                        state->face.location_format++;
-                    break;
-                case 3: //setup radius
-                    state->radius += 500;
-                    if ( state->radius > 10000 )
-                        state->radius = 1000;
-                    break;
-                case 4: //setup RNG
-                    state->face.rng = (state->face.rng + 1) % 3;
-                    switch ( state->face.rng ) {
-                        case 0:
-                            state->chance = true;
-                            break;
-                        case 1:
-                            state->chance = false;
-                            state->quantum = true;
-                            break;
-                        case 2:
-                            state->chance = false;
-                            state->quantum = false;
-                            break;
-                    }
-                    break;
-                case 5: // data processing
-                    _save_point_to_file(state);
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if ( state->face.mode == 5 )
-                state->face.mode = 0; // home
-            else
-                state->face.mode = 5; // data processing
-            break;
-        case EVENT_TIMEOUT:
-            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
-            // you may uncomment this line to move back to the first watch face in the list:
-            // movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
-            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
-            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
-            // watch_start_sleep_animation(500);
-            break;
-        default:
-            // Movement's default loop handler will step in for any cases you don't handle above:
-            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
-            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
-            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
-            // You can override any of these behaviors by adding a case for these events to this switch statement.
-            return movement_default_loop_handler(event);
-    }
-
-    _randonaut_face_display(state);
-
-    // return true if the watch can enter standby mode. Generally speaking, you should always return true.
-    // Exceptions:
-    //  * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
-    //  * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
-    // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
-    // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
-    return true;
-}
-
-void randonaut_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
-
-// PRIVATE STATIC FUNCTIONS ///////////////////////////////////////////////////
-
-/** @brief display handler
- */
-static void _randonaut_face_display(randonaut_state_t *state) {
-    char buf[12];
-    watch_clear_colon();
-    switch ( state->face.mode ) {
-        case 0: //home
-            sprintf(buf, "RA   Rando");
-            break;
-        case 1: //generate
-            if ( state->quantum )
-                // All Hail Steve /;[;[/.;]/[.;[/;/;/;/;.;.];.]]--=/ 
-                for ( uint8_t c = 100; c > 0; c--) {
-                    watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(0x33-0x1C));
-                    watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(3432-3409));
-                    watch_set_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(0xE +9));
-                    watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(23));
-                    watch_set_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(12+7+11));
-                    if( c < 70 ) {
-                        watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(12+7+11));
-                    }
-                    if ( c < 60 ) {
-                        watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(0xD68-0xD4A));
-                    }
-                    if ( c < 50 ) { 
-                        watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(14+9));
-                    }
-                    delay_ms(_get_pseudo_entropy(c)+20);
-                    if ( c < 30 ) {
-                        watch_display_string(" ",_get_pseudo_entropy(10));
-                    }
-                    watch_clear_pixel(_get_pseudo_entropy(02),_get_pseudo_entropy(3432-3409));
-                    watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(51-28));
-                    watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(23));
-                    if ( c < 20 ) {
-                        watch_clear_pixel(_get_pseudo_entropy(02),_get_pseudo_entropy(51-28));
-                        watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(14+9));
-                        watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(0xD68-0xD4A));
-                        watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(3432-3409));
-                        watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(12+7+11));
-                        watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(51-28));
-                    }
-                }
-            else
-                for ( uint8_t c = 30; c > 0; c--) {
-                    watch_display_string("1", _get_pseudo_entropy(10));
-                    watch_display_string("0", _get_pseudo_entropy(10));
-                    watch_display_string("11", _get_pseudo_entropy(10));
-                    watch_display_string("00", _get_pseudo_entropy(10));
-                    delay_ms(50);
-                    watch_display_string(" ", _get_pseudo_entropy(10));
-                    watch_display_string(" ", _get_pseudo_entropy(10));
-                    watch_display_string(" ", _get_pseudo_entropy(10));
-                    watch_display_string(" ", _get_pseudo_entropy(10));
-                }
-            _generate_blindspot(state);
-            watch_clear_display();
-            state->face.mode = 2; // point
-            state->face.location_format = 1; // distance
-            watch_display_string("RA   Found", 0);
-            delay_ms(500);
-            sprintf(buf, "RA   Found");
-            break;
-        case 2: //point
-            switch ( state->face.location_format ) {
-                case 0:
-                    sprintf(buf, "RA   Point");
-                    break;
-                case 1: // distance to point
-                    watch_clear_display();
-                    sprintf(buf, "DI m %d", state->point.distance );
-                    break;
-                case 2: // bearing relative to point
-                    watch_clear_display();
-                    sprintf(buf, "BE # %d", state->point.bearing );
-                    break;
-                case 3: // latitude DD._____
-                    sprintf(state->scratchpad, "%07d", abs((int32_t)(state->point.latitude)));
-                    sprintf(buf, "LA #%c %c%c  ", state->point.latitude < 0 ? '-' : '+', state->scratchpad[0], state->scratchpad[1]);
-                    break;
-                case 4: // latitude __.DDDDD
-                    sprintf(buf, "LA , %c%c%c%c%c", state->scratchpad[2], state->scratchpad[3],state->scratchpad[4], state->scratchpad[5],state->scratchpad[6]);
-                    break;
-                case 5: // longitude DD._____
-                    sprintf(state->scratchpad, "%08d", abs((int32_t)(state->point.longitude)));
-                    sprintf(buf, "LO #%c%c%c%c  ", state->point.longitude < 0 ? '-' : '+',state->scratchpad[0], state->scratchpad[1], state->scratchpad[2]);
-                    break;
-                case 6: // longitude __.DDDDD
-                    sprintf(buf, "LO , %c%c%c%c%c", state->scratchpad[3], state->scratchpad[4],state->scratchpad[5], state->scratchpad[6],state->scratchpad[7]);
-                    break;
-            }
-            break;
-        case 3: // setup radius
-            watch_set_colon();
-            if ( state->radius < 10000 )
-                sprintf(buf, "RA m %d ", state->radius);
-            else
-                sprintf(buf, "RA m%d ", state->radius);
-            break;
-        case 4: // setup RNG
-            sprintf(buf, "RN G %s ", state->chance ? "Chnce" : (state->quantum ? "True" : "Psudo"));
-            break;
-        case 5: // data processing
-            sprintf(buf, "WR   File ");
-    }
-    watch_display_string(buf, 0);
-}
-
-/** @brief Official Randonautica Blindspot Algorithm
- */
-static void _generate_blindspot(randonaut_state_t *state) {
-
-    _get_entropy(state);
-
-    double lat = (double)state->location.latitude / 100000;
-    double lon = (double)state->location.longitude / 100000;
-    uint16_t radius = state->radius;
-
-    const double random_distance  = radius * sqrt( (double)state->entropy / INT32_MAX ) / 1000.0;
-    const double random_bearing = 2.0 * PI * (double)state->entropy / INT32_MAX;
-
-    const double phi = lat * PI / 180;
-    const double lambda = lon * PI / 180;
-    const double alpha = random_distance / R;
-
-    lat = asin( sin(phi) * cos(alpha) + cos(phi) * sin(alpha) * cos(random_bearing) );
-    lon = lambda + atan2( sin(random_bearing) * sin(alpha) * cos(phi), cos(alpha) - sin(phi) * sin( lat ));
-    
-    state->point.latitude  = (int)round(lat * (180 / PI) * 100000);
-    state->point.longitude = (int)round(lon * (180 / PI) * 100000);
-    state->point.distance = random_distance * 1000;
-    state->point.bearing   = (uint16_t)round(random_bearing * (180 / PI) < 0 ? random_bearing * (180 / PI) + 360 : random_bearing * (180 / PI));
-}
-
-
-/** @brief pseudo random number generator
- */
-static uint32_t _get_pseudo_entropy(uint32_t max) {
-    #if __EMSCRIPTEN__
-    return rand() % max;
-    #else
-    return arc4random_uniform(max);
-    #endif
-}
-
-/** @brief true random number generator
- */
-static uint32_t _get_true_entropy(void) {
-    #if __EMSCRIPTEN__
-    return rand() % INT32_MAX;
-    #else
-    hri_mclk_set_APBCMASK_TRNG_bit(MCLK);
-    hri_trng_set_CTRLA_ENABLE_bit(TRNG);
-
-    while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready
-
-    watch_disable_TRNG();
-    hri_mclk_clear_APBCMASK_TRNG_bit(MCLK);
-    return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it
-    #endif
-}
-
-/** @brief get location from place.loc
- */
-static void _get_location_from_file(randonaut_state_t *state) {
-    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
-    coordinate_t place;
-    if (filesystem_file_exists("place.loc")) {
-        if (filesystem_read_file("place.loc", (char*)&place, sizeof(place)))
-            state->location = place;
-    } else {
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-        state->location.latitude = movement_location.bit.latitude * 1000;
-        state->location.longitude = movement_location.bit.longitude * 1000;
-    }
-}
-
-/** @brief save generated point to place.loc
- */
-static void _save_point_to_file(randonaut_state_t *state) {
-    watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    coordinate_t place;
-    place.latitude = state->point.latitude;
-    place.longitude = state->point.longitude;
-    if (filesystem_write_file("place.loc", (char*)&place, sizeof(place))) {
-        delay_ms(100);
-        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-    } else {
-        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-        delay_ms(500);
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-        
-    }
-}
-
-/** @brief get pseudo/quantum entropy and filter modulo bias
- */
-static void _get_entropy(randonaut_state_t *state) {
-    if ( state->chance ) {
-        state->quantum = (bool)(state->entropy % 2);
-    }   
-    do {
-        if ( !state->quantum ) {
-            state->entropy = _get_pseudo_entropy(INT32_MAX);
-        } else {
-            state->entropy = _get_true_entropy();
-        }
-    } while (state->entropy >= INT32_MAX || state->entropy <= 0);
-    state->entropy %= INT32_MAX;
-}
\ No newline at end of file
diff --git a/movement/watch_faces/complication/randonaut_face.h b/movement/watch_faces/complication/randonaut_face.h
deleted file mode 100644 (file)
index 72e4726..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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 RANDONAUT_FACE_H_
-#define RANDONAUT_FACE_H_
-
-/*
- * RANDONAUT face
- * ==============
- *
- * Randonauting is a way to turn the world around you into an adventure and get the user outside 
- * of their day-to-day routine by using a random number generator to derive a coordinate to journey 
- * to. In Randonauts lore so-called "Blind Spots" are places you cannot reach methodologically. They
- * may exist in your own backyard for your whole life and you will never even notice them, because 
- * you simply have no reason to go to that exact place or look in its direction. Since the very 
- * limitations of our behavioral algorithms are the reason for the existence of blindspots, they 
- * can only be found using a randomizer.
- * 
- * This watch face generates a random location based on the watch's location and a set radius using
- * the official Randonautica Blind Spot algorithm.
- * 
- * The ALARM button starts the random location generation and then automatically displays the found
- * Blind Spot.
- * 
- * By pressing ALARM again the user can flip through different pieces of information about the Blind
- * Spot: Distance (DI), Bearing Degree (BE), Latitude degrees and decimal digits (LA), Longitude 
- * degrees and decimal digits (LO).
- * 
- * Pressing LIGHT switches between generating a new blind spot ("Rando") and displaying the info of
- * the last generated one ("Point").
- * 
- * LONG PRESSING LIGHT toggles setup mode. Here pressing LIGHT switches between setting the desired
- * radius (RA) and setting the random number generator (RNG) for generating the blind spot. 
- * 
- * ALARM changes the values respectively:
- * 
- * - The radius can be set in 500 meter steps between 1000 and 10,000 meters
- * 
- * - The RNG can be set to "true" which utilizes the SAML22J's internal True Random Number Generator
- * - Setting it to "psudo" will use the pseudorandom number generation algorithm arc4random
- * - Setting it to "chance" will randomly chose either of the RNGs for each generation (default)
- *
- * LONG PRESSING ALARM toggles DATA mode in which the currently generated Blind Spot coordinate can
- * be written to the <place.loc> file on the watch (press ALARM) and set as active high precision 
- * location used by other watch faces. It does not overwrite the low precision location information
- * in the watch register commonly used for astronomical watch faces.
- * 
- */
-
-#include "movement.h"
-#include "place_face.h"
-
-typedef struct {
-    uint8_t mode :3;
-    uint8_t location_format :3;
-    uint8_t rng: 2;
-} randonaut_face_mode_t;
-
-typedef struct {
-    int32_t latitude : 26;
-    int32_t longitude : 26;
-    uint16_t distance : 14;
-    uint16_t bearing : 9;
-} randonaut_coordinate_t;
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    coordinate_t location;
-    randonaut_coordinate_t point;
-    uint16_t radius : 14;
-    uint32_t entropy;
-    bool quantum;
-    bool chance;
-    randonaut_face_mode_t face;
-    char scratchpad[10];
-} randonaut_state_t;
-
-void randonaut_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void randonaut_face_activate(void *context);
-bool randonaut_face_loop(movement_event_t event, void *context);
-void randonaut_face_resign(void *context);
-
-#define randonaut_face ((const watch_face_t){ \
-    randonaut_face_setup, \
-    randonaut_face_activate, \
-    randonaut_face_loop, \
-    randonaut_face_resign, \
-    NULL, \
-})
-
-#endif // RANDONAUT_FACE_H_
-
diff --git a/movement/watch_faces/complication/ratemeter_face.c b/movement/watch_faces/complication/ratemeter_face.c
deleted file mode 100644 (file)
index 746ab39..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 David Singleton
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "ratemeter_face.h"
-#include "watch.h"
-
-#define RATEMETER_FACE_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.)
-#define RATEMETER_FACE_FREQUENCY (1 << RATEMETER_FACE_FREQUENCY_FACTOR)
-
-void ratemeter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) *context_ptr = malloc(sizeof(ratemeter_state_t));
-}
-
-void ratemeter_face_activate(void *context) {
-    memset(context, 0, sizeof(ratemeter_state_t));
-}
-
-bool ratemeter_face_loop(movement_event_t event, void *context) {
-    ratemeter_state_t *ratemeter_state = (ratemeter_state_t *)context;
-    char buf[14];
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_display_string("ra          ", 0);
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-            if (ratemeter_state->ticks != 0) {
-                ratemeter_state->rate = (int16_t)(60.0 / ((float)ratemeter_state->ticks / (float)RATEMETER_FACE_FREQUENCY));
-            }
-            ratemeter_state->ticks = 0;
-            movement_request_tick_frequency(RATEMETER_FACE_FREQUENCY);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            break;
-        case EVENT_TICK:
-            if (ratemeter_state->rate == 0) {
-                watch_display_string("ra          ", 0);
-            } else {
-                if (ratemeter_state->rate > 500) {
-                    watch_display_string("ra      Hi", 0);
-                } else if (ratemeter_state->rate < 1) {
-                    watch_display_string("ra      Lo", 0);
-                } else {
-                    sprintf(buf, "ra  %-3d pn", ratemeter_state->rate);
-                    watch_display_string(buf, 0);
-                }
-            }
-            ratemeter_state->ticks++;
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void ratemeter_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/ratemeter_face.h b/movement/watch_faces/complication/ratemeter_face.h
deleted file mode 100644 (file)
index 85ac255..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 RATEMETER_FACE_H_
-#define RATEMETER_FACE_H_
-
-/*
- * RATE METER face
- *
- * The rate meter shows the rate per minute at which the ALARM button is
- * being pressed. This is particularly useful in sports where cadence
- * tracking is useful. For instance, rowing coaches often use a dedicated
- * rate meter - clicking the rate button each time the crew puts their oars
- * in the water to see the rate (strokes per minute) on the rate meter.
- */
-
-#include "movement.h"
-
-typedef struct {
-    int16_t rate;
-    int16_t ticks;
-} ratemeter_state_t;
-
-void ratemeter_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void ratemeter_face_activate(void *context);
-bool ratemeter_face_loop(movement_event_t event, void *context);
-void ratemeter_face_resign(void *context);
-
-#define ratemeter_face ((const watch_face_t){ \
-    ratemeter_face_setup, \
-    ratemeter_face_activate, \
-    ratemeter_face_loop, \
-    ratemeter_face_resign, \
-    NULL, \
-})
-
-#endif // RATEMETER_FACE_H_
diff --git a/movement/watch_faces/complication/rpn_calculator_alt_face.c b/movement/watch_faces/complication/rpn_calculator_alt_face.c
deleted file mode 100644 (file)
index 03a75ec..0000000
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 James Haggerty
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "rpn_calculator_alt_face.h"
-
-static void show_fn(calculator_state_t *state, uint8_t subsecond);
-
-void rpn_calculator_alt_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(calculator_state_t));
-        memset(*context_ptr, 0, sizeof(calculator_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-static void show_number(double num) {
-    char buf[9] = {0};
-    bool negative = num < 0;
-    int max_digits = negative ? 5 : 6;
-
-    // Add back in for debugging...
-    // printf("%f\n", num);
-
-    if (isnan(num)) {
-        watch_clear_colon();
-        watch_display_string("  nan   ", 2);
-        return;
-    }
-
-    if (negative) {
-        num = -num;
-    }
-
-    // Can we reasonably represent this number without a decimal point?
-    if (num == 0 || (num >= 0.5 && fabs(num - (int)num) < 0.0001)) {
-        if (floor(log10(num)) + 1 <= max_digits) {
-            if (negative) {
-                sprintf(buf, "  -%-5d", (int)round(num));
-            } else {
-                sprintf(buf, "  %-6d", (int)round(num));
-            }
-            watch_clear_colon();
-            watch_display_string(buf, 2);
-            return;
-        }
-    }
-
-    // Is this a floating point number where scientific
-    // notation won't get us much? (i.e. between 0.1 and 1)
-    if (num < 1 && num >= 0.0999) {
-        // Display as boring floating point number... (e.g. 0.25)
-        int digits = (int)round(num * 10000);
-        sprintf(buf, "   0%04d", digits);
-        if (negative) {
-            buf[2 ] = '-';
-        }
-        watch_set_colon();
-        watch_display_string(buf, 2);
-        return;
-    }
-
-    // Fall back to scientific notation
-
-    // Calculate exponent
-    int exponent = 0;
-    while (num < 1) {
-        num *= 10;
-        --exponent;
-    }
-
-    while (num >= 10) {
-        num /= 10;
-        ++exponent;
-    }
-
-    if (exponent < -9) {
-        sprintf(buf, "  small ");
-        watch_clear_colon();
-        watch_display_string(buf, 2);
-        return;
-    }
-
-    if (exponent > 39) {
-        sprintf(buf, "   big  ");
-        watch_clear_colon();
-        watch_display_string(buf, 2);
-        return;
-    }
-
-    sprintf(buf, "%2d%c%05d", exponent, negative ? '-' : ' ', (int)round(num * 10000));
-    watch_set_colon();
-    watch_display_string(buf, 2);
-}
-
-#define C (s->stack[s->stack_size - 1])
-#define PUSH(x) (s->stack[++s->stack_size - 1] = x)
-#define POP() (s->stack[s->stack_size-- - 1])
-
-void rpn_calculator_alt_face_activate(void *context) {
-    calculator_state_t *s = (calculator_state_t *)context;
-    s->min = s->max = NAN;
-}
-
-static void change_mode(calculator_state_t *s, enum calculator_mode mode) {
-    s->mode = mode;
-    s->fn_index = 0;
-    show_fn(s, 0);
-    // Faster tick in operation mode so we can blink.
-    movement_request_tick_frequency(mode == CALC_OPERATION ? 4 : 1);
-}
-
-// Binary-ish search to find the right number. direction is +1 if it should be bigger, -1 if it should be smaller.
-static void adjust_number(calculator_state_t *s, int direction) {
-    if (direction > 0) {
-        s->min = C;
-    } else {
-        s->max = C;
-    }
-
-    // If the direction we want to go has no bound (i.e. isnan),
-    // then first get the sign right (moving to 0, then +-10), and
-    // after than go up by *10.
-    if (isnan(direction > 0 ? s->max : s->min)) {
-        if (direction * C < 0) {
-            C = 0;
-        } else if (C == 0) {
-            C = direction * 10;
-        } else {
-            C *= 10;
-        }
-    } else {
-        // We have a higher and lower bound. Split them.
-        C = (s->max + s->min) / 2;
-        // Subtract 0.1 so we don't apply most significant rounding to things that are _exactly_ 1/10/100 apart.
-        double mag = log10(fabs(s->max - s->min)) - 0.1;
-        if (mag > 0.0) {
-            // i.e. the different is >= 2, which means we want to round aggressively
-            // to not show people complicated looking numbers.
-            // e.g. this takes a number like 3.2 to 3, or a number like 464 to 500
-            // (depending on how fine-grained 'mag' tells us to be).
-            float div = pow(10, floor(mag));
-            int sign = C < 0 ? -1 : 1;
-            C = sign * floor(fabs(C) / div) * div;
-        }
-    }
-}
-
-static void fn_number(calculator_state_t *s) {
-    PUSH(10);
-    s->min = s->max = NAN;
-    change_mode(s, CALC_NUMBER);
-}
-
-static void fn_add(calculator_state_t *s) {
-    double a = POP();
-    double b = POP();
-    PUSH(a + b);
-}
-
-static void fn_sub(calculator_state_t *s) {
-    double a = POP();
-    double b = POP();
-    PUSH(b - a);
-}
-
-static void fn_mul(calculator_state_t *s) {
-    double a = POP();
-    double b = POP();
-    PUSH(a * b);
-}
-
-static void fn_div(calculator_state_t *s) {
-    double a = POP();
-    double b = POP();
-    PUSH(b / a);
-}
-
-static void fn_pow(calculator_state_t *s) {
-    double a = POP();
-    double b = POP();
-    PUSH(pow(b, a));
-}
-
-static void fn_sqrt(calculator_state_t *s) {
-    double x = POP();
-    PUSH(sqrt(x));
-}
-
-static void fn_log(calculator_state_t *s) {
-    double x = POP();
-    PUSH(log(x));
-}
-
-static void fn_log10(calculator_state_t *s) {
-    double x = POP();
-    PUSH(log10(x));
-}
-
-static void fn_e(calculator_state_t *s) {
-    PUSH(M_E);
-}
-
-static void fn_sin(calculator_state_t *s) {
-    double x = POP();
-    PUSH(sin(x));
-}
-
-static void fn_cos(calculator_state_t *s) {
-    double x = POP();
-    PUSH(cos(x));
-}
-
-static void fn_tan(calculator_state_t *s) {
-    double x = POP();
-    PUSH(tan(x));
-}
-
-static void fn_pi(calculator_state_t *s) {
-    PUSH(M_PI);
-}
-
-static void fn_pop(calculator_state_t *s) {
-    --s->stack_size;
-}
-
-static void fn_swap(calculator_state_t *s) {
-    double a = POP();
-    double b = POP();
-    PUSH(a);
-    PUSH(b);
-}
-
-static void fn_duplicate(calculator_state_t *s) {
-    double a = POP();
-    PUSH(a);
-    PUSH(a);
-}
-
-static void fn_clear(calculator_state_t *s) {
-    s->stack_size = 0;
-}
-
-static void fn_size(calculator_state_t *s) {
-    double a = s->stack_size;
-    PUSH(a);
-}
-
-struct {
-    char name[2];
-    uint8_t input;
-    uint8_t output;
-    void (*func)(calculator_state_t *);
-} functions[] = {
-    {{'n', 'o'}, 0, 1, fn_number},
-    {{'*', ' '}, 2, 1, fn_add},  // First position * actually looks like a '+'.
-    {{'-', ' '}, 2, 1, fn_sub},
-    {{'H', ' '}, 2, 1, fn_mul},  // For actual *, we throw in the middle vertical segment onto the H.
-    {{'/', ' '}, 2, 1, fn_div},  // There's also some minor hackery on '/'.
-    {{'P', 'o'}, 2, 1, fn_pow},
-    {{'S', 'r'}, 1, 1, fn_sqrt},
-    {{'L', 'n'}, 1, 1, fn_log},
-    {{'L', 'o'}, 1, 1, fn_log10},
-    {{'e', ' '}, 0, 1, fn_e},
-    {{'P', 'i'}, 0, 1, fn_pi},
-    {{'C', 'o'}, 1, 1, fn_cos},
-    {{'S', 'i'}, 1, 1, fn_sin},
-    {{'T', 'a'}, 1, 1, fn_tan},
-    // Stack operations. Accessible via secondary_fn_index (i.e. alarm long press).
-    {{'P', 'O'}, 1, 0, fn_pop},  // This ends up displaying the same as 'POW'. But at least it's in a different place.
-    {{'S', 'W'}, 2, 2, fn_swap},
-    {{'d', 'u'}, 1, 1, fn_duplicate},  // Uppercase 'D' is a bit too 'O' for me.
-    {{'C', 'L'}, 1, 0, fn_clear},  // Operation lie - takes _everything_ off the stack, but a check of 1 is sufficient.
-    {{'L', 'E'}, 1, 0, fn_size},
-};
-
-#define FUNCTIONS_LEN (sizeof(functions) / sizeof(functions[0]))
-#define SECONDARY_FN_INDEX (FUNCTIONS_LEN - 4)
-
-// Show the function name (using day display)
-static void show_fn(calculator_state_t *s, uint8_t subsecond) {
-    if (subsecond % 2) {
-        // blink
-        watch_display_string("  ", 0);
-        return;
-    }
-
-    char *name = functions[s->fn_index].name;
-    char buf[3] = {name[0], name[1], '\0'};
-    watch_display_string(buf, 0);
-    // The first position has a bunch of segments, and I have minor
-    // disagreements with the character set choices in watch_display_string,
-    // so we tweak a little here.
-    switch (buf[0]) {
-        case 'H':
-            // Use the middle segment lines to make our 'H' a '*'-ish thing.
-            watch_set_pixel(1, 14);
-            break;
-        case '/':
-            // Add a middle bar to division.
-            watch_set_pixel(1, 15);
-            break;
-        default:
-            break;
-    }
-}
-
-// Show the top of the stack (using everything except day display).
-static void show_stack_top(calculator_state_t *s) {
-    if (s->stack_size > 0) {
-        show_number(C);
-    } else {
-        watch_display_string("  ------", 2);
-        watch_clear_colon();
-    }
-}
-
-bool rpn_calculator_alt_face_loop(movement_event_t event, void *context) {
-    calculator_state_t *s = (calculator_state_t *)context;
-
-    int proposed_stack_size;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            change_mode(s, CALC_OPERATION);
-            show_stack_top(s);
-            break;
-        case EVENT_TICK:
-            if (s->mode == CALC_OPERATION) {
-                show_fn(s, event.subsecond);
-            }
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            if (s->mode == CALC_NUMBER) {
-                adjust_number(s, -1);
-                show_stack_top(s);
-            } else {
-                movement_move_to_next_face();
-                return false;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            proposed_stack_size = s->stack_size - functions[s->fn_index].input;
-
-            if (s->mode == CALC_NUMBER) {
-                change_mode(s, CALC_OPERATION);
-            } else if (proposed_stack_size < 0 || proposed_stack_size + functions[s->fn_index].output > CALC_MAX_STACK_SIZE) {
-                movement_play_signal();
-                break;
-            } else {
-                functions[s->fn_index].func(s);
-                show_stack_top(s);
-                s->fn_index = 0;
-                show_fn(s, 0);
-            }
-
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if (s->mode == CALC_NUMBER) {
-                adjust_number(s, 1);
-                show_stack_top(s);
-            } else {
-                s->fn_index = (s->fn_index + 1) % FUNCTIONS_LEN;
-                show_fn(s, 0);
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (s->mode == CALC_OPERATION) {
-                if (s->fn_index == 0) {
-                    s->fn_index = SECONDARY_FN_INDEX;
-                } else {
-                    s->fn_index = 0;
-                }
-                show_fn(s, 0);
-            }
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
-    // you should return false since the PWM driver does not operate in standby mode.
-    return true;
-}
-
-void rpn_calculator_alt_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
-
diff --git a/movement/watch_faces/complication/rpn_calculator_alt_face.h b/movement/watch_faces/complication/rpn_calculator_alt_face.h
deleted file mode 100644 (file)
index 4e74115..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 <#author_name#>
- *
- * 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 CALCULATOR_FACE_H_
-#define CALCULATOR_FACE_H_
-
-/* 
- * RPN Calculator alternate face.
- *
- * Operations appear in the 'day' section; ALARM changes between operations when
- * operation is flashing. LIGHT executes current operation.
- *
- * This is the alternate face because it has a non-traditional number entry system which
- * I call 'guess a number'. In number entry mode, the watch tries to guess which number you
- * want, and you respond with 'smaller' (left - MODE) or larger (right - ALARM). This means
- * that when you _are_ entering a number, MODE will no longer move between faces!
- *
- * Example of entering the number 27
- *  - select the NO operation (probably unnecessary, as this is the default),
- *    and execute it by hitting LIGHT.
- *  - you are now in number entry mode; you know this because nothing is flashing.
- *  - Watch displays 10; you hit ALARM to say you want a larger number.
- *  - Watch displays 100; you hit MODE to say you want a smaller number.
- *  - Continuing: 50 -> MODE -> 30 -> MODE -> 20 -> ALARM -> 27
- *  - Hit LIGHT to add the number to the stack (and now 'NO' is flashing
- *    again, indicating you're back in operation selection mode).
- *
- * One other thing to watch out for is how quickly it will switch into scientific notation
- * due to the limitations of the display when you have large numbers or non-integer values.
- * In this mode, the 'colon' serves at the decimal point, and the numbers in the top right
- * are the exponent.
- *
- * As with the main movement firmware, this has the concept of 'secondary' functions which
- * you can jump to by a long hold of ALARM on NO. These are functions to do with stack
- * manipulation (pop, swap, dupe, clear, size (le)). If you're _not_ on NO, a long
- * hold will take you back to it.
- *
- * See 'functions' in "rpn_calculator_alt_face.c" for names of all operations.
- */
-
-#include "movement.h"
-
-#define CALC_MAX_STACK_SIZE 20
-
-enum calculator_mode {
-    CALC_OPERATION = 0,
-    CALC_NUMBER,
-};
-
-typedef struct {
-    double stack[CALC_MAX_STACK_SIZE];
-    uint8_t stack_size;  // this is the current stack top + 1 (so that '0' means nothing on the stack)
-    uint8_t fn_index;
-
-    double min;
-    double max;
-
-    enum calculator_mode mode;
-} calculator_state_t;
-
-void rpn_calculator_alt_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void rpn_calculator_alt_face_activate(void *context);
-bool rpn_calculator_alt_face_loop(movement_event_t event, void *context);
-void rpn_calculator_alt_face_resign(void *context);
-
-#define rpn_calculator_alt_face ((const watch_face_t){ \
-    rpn_calculator_alt_face_setup, \
-    rpn_calculator_alt_face_activate, \
-    rpn_calculator_alt_face_loop, \
-    rpn_calculator_alt_face_resign, \
-    NULL, \
-})
-
-#endif // CALCULATOR_FACE_H_
-
diff --git a/movement/watch_faces/complication/rpn_calculator_face.c b/movement/watch_faces/complication/rpn_calculator_face.c
deleted file mode 100644 (file)
index 2c5a883..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Niclas Hoyer
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "rpn_calculator_face.h"
-
-static void draw_number(char *buf, float num) {
-    float f = fmodf(num, 1.0) * 100;
-    sprintf(buf, "CA  %4d%02d", ((int)num) % 10000, (int) f);
-}
-
-static void draw_op(char *buf, rpn_calculator_op_t op) {
-    switch (op) {
-        case rpn_calculator_op_add:
-            sprintf(buf, "CA     Add");
-        break;
-        case rpn_calculator_op_sub:
-            sprintf(buf, "CA     sub");
-        break;
-        case rpn_calculator_op_mul:
-            sprintf(buf, "CA    n&ul");
-        break;
-        case rpn_calculator_op_div:
-            sprintf(buf, "CA     div");
-        break;
-        case rpn_calculator_op_pow:
-            sprintf(buf, "CA     pow");
-        break;
-        case rpn_calculator_op_sqrt:
-            sprintf(buf, "CA    sqrt");
-        break;
-        case rpn_calculator_op_pi:
-            sprintf(buf, "CA      pi");
-            break;
-        default:
-            break;
-    }
-}
-
-static void printf_stack(rpn_calculator_state_t *state) {
-    printf("Stack: [%f, %f, %f, %f], top: %d\n",
-        state->stack[0],
-        state->stack[1],
-        state->stack[2],
-        state->stack[3],
-        state->top
-    );
-}
-
-static void next_op(rpn_calculator_state_t *state) {
-    state->op += 1;
-    state->op = state->op % RPN_CALCULATOR_MAX_OPS;
-}
-
-// increase a digit of a floating point number
-// FIXME: this converts the number to string and back, there might
-// be better ways to do this
-static float inc_digit(float num, uint8_t position) {
-    char buf[8];
-    if (position > 5) {
-        return 0.0;
-    }
-    // inverse position (rtl)
-    position = 5 - position;
-    float f = fmodf(num, 1.0) * 100;
-    sprintf(buf, "%04d%02d", ((int)num) % 10000, (int) f);
-    uint8_t digit = buf[position] - '0';
-    digit = (digit + 1) % 10;
-    buf[position] = digit + '0';
-    printf("buf: %s\n", buf);
-    return strtof(buf, NULL) / 100.0;
-}
-
-static void stack_push(rpn_calculator_state_t *state, float f) {
-    printf_stack(state);
-    printf("push: %f\n", f);
-    state->top++;
-    if (state->top >= RPN_CALCULATOR_STACK_SIZE) {
-        // FIXME: implement this using a circular buffer?
-        for (int i=0; i<RPN_CALCULATOR_STACK_SIZE-1; i++) {
-            state->stack[i] = state->stack[i+1];
-        }
-    }
-    state->stack[state->top] = f;
-}
-
-static float stack_peek(rpn_calculator_state_t *state) {
-    if (state->top > -1) {
-        return state->stack[state->top];
-    }
-    return 0;
-}
-
-static float stack_pop(rpn_calculator_state_t *state) {
-    printf_stack(state);
-    float f = stack_peek(state);
-    state->stack[state->top] = 0;
-    printf("pop: %f\n", f);
-    if (state->top > -1) {
-        state->top--;
-    } else {
-        state->top = -1; // empty
-    }
-    return f;
-}
-
-static void run_op(rpn_calculator_state_t *state) {
-    printf_stack(state);
-    bool op_found = false;
-    // ops without parameters
-    switch (state->op)  {
-        case rpn_calculator_op_pi:
-            stack_push(state, M_PI);
-            op_found = true;
-            break;
-        default:
-            break;
-    }
-    if (op_found) {
-        state->mode = rpn_calculator_waiting;
-        return;
-    }
-
-    // ops with one parameter
-    if (state->top < 0) {
-        state->mode = rpn_calculator_err;
-        return;
-    }
-    float right = stack_pop(state);
-    printf("right: %f\n", right);
-    switch (state->op)  {
-        case rpn_calculator_op_sqrt:
-            stack_push(state, sqrt(right));
-            op_found = true;
-            break;
-        default:
-            break;
-    }
-    if (op_found) {
-        state->mode = rpn_calculator_waiting;
-        return;
-    }
-
-    // ops with two parameters
-    if (state->top < 0) {
-        // no parameter left -> error
-        state->mode = rpn_calculator_err;
-        return;
-    }
-    float left = stack_pop(state);
-    printf("left: %f\n", left);
-    switch (state->op)  {
-        case rpn_calculator_op_add:
-            stack_push(state, left + right);
-            op_found = true;
-            break;
-        case rpn_calculator_op_sub:
-            stack_push(state, left - right);
-            op_found = true;
-            break;
-        case rpn_calculator_op_mul:
-            stack_push(state, left * right);
-            op_found = true;
-            break;
-        case rpn_calculator_op_div:
-            stack_push(state, left / right);
-            op_found = true;
-            break;
-        case rpn_calculator_op_pow:
-            stack_push(state, powf(left, right));
-            op_found = true;
-            break;
-        default:
-            break;
-    }
-    if (op_found) {
-        state->mode = rpn_calculator_waiting;
-        return;
-    }
-    state->mode = rpn_calculator_err;
-}
-
-static void draw(rpn_calculator_state_t *state, uint8_t subsecond) {
-    char buf[16];
-    switch (state->mode) {
-        case rpn_calculator_err:
-            sprintf(buf, "CA   err  ");
-            break;
-        case rpn_calculator_number:
-            draw_number(buf, stack_peek(state));
-            uint8_t i = 4 + (5 - state->selection);
-            if (buf[i] == ' ') {
-                buf[i] = '0';
-            }
-            if (subsecond % 2) {
-                buf[i] = ' ';
-            }
-            break;
-        case rpn_calculator_waiting:
-            printf_stack(state);
-            draw_number(buf, stack_peek(state));
-            break;
-        case rpn_calculator_op:
-            draw_op(buf, state->op);
-            break;
-        default:
-            break;
-    }
-    watch_display_string(buf, 0);
-}
-
-void rpn_calculator_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(rpn_calculator_state_t));
-        memset(*context_ptr, 0, sizeof(rpn_calculator_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-        rpn_calculator_state_t *state = *context_ptr;
-        state->top = -1;
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void rpn_calculator_face_activate(void *context) {
-    (void) context;
-
-    // Handle any tasks related to your watch face coming on screen.
-}
-
-bool rpn_calculator_face_loop(movement_event_t event, void *context) {
-
-    rpn_calculator_state_t *state = (rpn_calculator_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            draw(state, event.subsecond);
-            break;
-        case EVENT_TICK:
-            if (state->mode == rpn_calculator_number) {
-                draw(state, event.subsecond);
-            }
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            switch (state->mode) {
-                case rpn_calculator_number:
-                    state->mode = rpn_calculator_waiting;
-                    draw(state, event.subsecond);
-                    movement_request_tick_frequency(1);
-                    break;
-                default:
-                    state->mode = rpn_calculator_waiting;
-                    movement_request_tick_frequency(1);
-                    movement_move_to_next_face();
-                break;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            switch (state->mode) {
-                case rpn_calculator_waiting:
-                    state->mode = rpn_calculator_op;
-                    draw(state, event.subsecond);
-                    break;
-                case rpn_calculator_number:
-                    state->selection = (state->selection + 1) % 6;
-                    draw(state, event.subsecond);
-                    break;
-                case rpn_calculator_op:
-                    next_op(state);
-                    draw(state, event.subsecond);
-                    break;
-                default:
-                    movement_illuminate_led();
-                    break;
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch (state->mode) {
-                case rpn_calculator_waiting:
-                    state->mode = rpn_calculator_number;
-                    state->selection = 2;
-                    stack_push(state, 0);
-                    draw(state, event.subsecond);
-                    movement_request_tick_frequency(4);
-                    break;
-                case rpn_calculator_number:
-                    state->stack[state->top] = inc_digit(state->stack[state->top], state->selection);
-                    printf_stack(state);
-                    draw(state, event.subsecond);
-                    break;
-                case rpn_calculator_err:
-                    state->mode = rpn_calculator_waiting;
-                    draw(state, event.subsecond);
-                    break;
-                case rpn_calculator_op:
-                    run_op(state);
-                    draw(state, event.subsecond);
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_TIMEOUT:
-            state->mode = rpn_calculator_waiting;
-            movement_request_tick_frequency(1);
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
-    // you should return false since the PWM driver does not operate in standby mode.
-    return true;
-}
-
-void rpn_calculator_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
diff --git a/movement/watch_faces/complication/rpn_calculator_face.h b/movement/watch_faces/complication/rpn_calculator_face.h
deleted file mode 100644 (file)
index a9daad6..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Niclas Hoyer
- *
- * 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 RPN_CALCULATOR_FACE_H_
-#define RPN_CALCULATOR_FACE_H_
-
-/*
- * RPN CALCULATOR face
- *
- * A calculator face using reverse polish notation (RPN).
- *
- * For usage instructions, please refer to the wiki:
- * https://www.sensorwatch.net/docs/watchfaces/complication/#rpn-calculator
- */
-
-#include "movement.h"
-
-#define RPN_CALCULATOR_STACK_SIZE 4
-#define RPN_CALCULATOR_MAX_OPS 7;
-
-typedef enum {
-    rpn_calculator_op_add,
-    rpn_calculator_op_sub,
-    rpn_calculator_op_mul,
-    rpn_calculator_op_div,
-    rpn_calculator_op_pow,
-    rpn_calculator_op_sqrt,
-    rpn_calculator_op_pi
-} rpn_calculator_op_t;
-
-typedef enum {
-    rpn_calculator_waiting,
-    rpn_calculator_op,
-    rpn_calculator_number,
-    rpn_calculator_err,
-} rpn_calculator_mode_t;
-
-typedef struct {
-    rpn_calculator_mode_t mode;
-    rpn_calculator_op_t op;
-    float stack[RPN_CALCULATOR_STACK_SIZE];
-    int8_t top;
-    uint8_t selection;
-} rpn_calculator_state_t;
-
-void rpn_calculator_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void rpn_calculator_face_activate(void *context);
-bool rpn_calculator_face_loop(movement_event_t event, void *context);
-void rpn_calculator_face_resign(void *context);
-
-#define rpn_calculator_face ((const watch_face_t){ \
-    rpn_calculator_face_setup, \
-    rpn_calculator_face_activate, \
-    rpn_calculator_face_loop, \
-    rpn_calculator_face_resign, \
-    NULL, \
-})
-
-#endif // RPN_CALCULATOR_FACE_H_
-
diff --git a/movement/watch_faces/complication/sailing_face.c b/movement/watch_faces/complication/sailing_face.c
deleted file mode 100644 (file)
index 9f8109b..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Jan H. Voigt
- * Copyright (c) 2022 Wesley Ellis
- * Copyright (c) 2022 Niclas Hoyer
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "sailing_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-#define sl_SELECTIONS 6
-#define DEFAULT_MINUTES { 5,4,1,0,0,0 }
-
-static inline int32_t get_tz_offset() {
-    return movement_get_current_timezone_offset();
-}
-
-static int lap = 0;
-bool ringflag = false;
-int8_t double_beep[] = {BUZZER_NOTE_C8, 4, BUZZER_NOTE_REST, 5, BUZZER_NOTE_C8, 5, 0};
-int8_t single_beep[] = {BUZZER_NOTE_C8, 4, 0};
-int8_t long_beep[] = {BUZZER_NOTE_C8, 40, 0};
-int beepseconds[] = {600, 540, 480, 420, 360, 300, 240, 180, 120, 60, 30, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; //seconds before start that can trigger the buzzer. Every whole minute, 30s before start & 10s countdown.
-int beepseconds_size = sizeof(beepseconds) / sizeof(int);
-int beepflag = 0;
-int alarmflag = 3;
-
-static void reset(sailing_state_t *state) {
-    state->index = 0;
-    state->mode = sl_waiting;
-    movement_cancel_background_task();
-    watch_clear_indicator(WATCH_INDICATOR_LAP);
-    beepflag = 0;
-    ringflag = false;
-}
-
-static void counting(sailing_state_t *state) {
-    state->mode = sl_counting;  
-    movement_cancel_background_task();
-    watch_set_indicator(WATCH_INDICATOR_LAP);
-}
-
-static void draw(sailing_state_t *state, uint8_t subsecond) {
-
-    char tmp[24];
-    char buf[16];
-
-    uint32_t delta;
-    div_t result;
-    uint8_t hrs, min, sec;
-
-    switch (state->mode) {
-        case sl_running:
-            if (state->now_ts > state->target_ts) {
-                delta = 0;
-                counting(state); //in case buttons are pressed while sound is played (esp. in the last 10s), the timing of ring might be thrown off. Beep stops and the switch to counting doesn't occur. temporary fix/safety net. 
-            } else {
-                delta = state->target_ts - state->now_ts;
-            }
-            result = div(delta, 60);
-            min = result.quot;
-            sec = result.rem;
-
-            if (min > 0) {
-                sprintf(buf, "SA1L  %2d%02d", min, sec);
-            } else {
-                sprintf(buf, "SA1L  %2d  ", sec);
-            }
-            break;
-        case sl_waiting:
-            sprintf(buf, "SA1L  %2d%02d", state->minutes[0], 0);
-            break;
-        case sl_setting:
-            // this sprintf to a larger tmp is to guarantee that no buffer overflows
-            // occur here (and to squelch the corresponding compiler warning)
-            if (state->minutes[0] == 10) { //print 10 vertically.
-                sprintf(tmp, "SA1L %1d%1d%1d%1d%1d",
-                    state->minutes[1],
-                    state->minutes[2],
-                    state->minutes[3],
-                    state->minutes[4],
-                    state->minutes[5]
-                );
-                memcpy(buf, tmp, sizeof(buf));
-                if (subsecond % 2) {
-                    buf[4 + state->selection] = ' ';
-                }
-                watch_display_string(buf, 0);
-                if (!(subsecond % 2) || state->selection != 0) {
-                    watch_set_pixel(0, 18);
-                    watch_set_pixel(0, 19);
-                    watch_set_pixel(1, 18);
-                    watch_set_pixel(1, 19);
-                }
-                return;
-            }
-            sprintf(tmp, "SA1L%1d%1d%1d%1d%1d%1d",
-                state->minutes[0],
-                state->minutes[1],
-                state->minutes[2],
-                state->minutes[3],
-                state->minutes[4],
-                state->minutes[5]
-            );
-            memcpy(buf, tmp, sizeof(buf));
-            if (subsecond % 2) {
-                buf[4 + state->selection] = ' ';
-            }
-            break;
-        case sl_counting:
-            delta = state->now_ts - state->target_ts;
-            if (state->now_ts <= state->target_ts) {
-                sprintf(buf, "SA1L  %2d  ", 0);
-            }
-            else {
-                result = div(delta, 3600);
-                hrs = result.quot;
-                delta -= 60*hrs;
-                result = div(delta, 60);
-                min = result.quot;
-                sec = result.rem;
-                sprintf(buf, "SL%2d%2d%02d%02d", lap, hrs, min, sec);//implement counting
-                if (hrs > 23) {
-                    reset(state);
-                }
-            }
-            break;
-    }
-    watch_display_string(buf, 0);
-}
-
-static void ring(sailing_state_t *state) {    
-// if ring is called in background (while on another face), a button press can interrupt and cancel the execution.
-// To reduce the probability of cancelling all future alarms, the new alarm is set as soon as possible after calling ring.
-    movement_cancel_background_task();
-    if (beepflag + 1 == beepseconds_size) { //equivalent to (beepflag + 1 == sizeof(beepseconds) / sizeof(int)) but without needing to divide here => quicker
-        if (alarmflag != 0){
-            watch_buzzer_play_sequence(long_beep, NULL);
-        }
-        movement_cancel_background_task();
-        counting(state);
-        return;
-    }
-    state->nextbeep_ts = state->target_ts - beepseconds[beepflag+1];
-    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings));
-    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.
-    for (int i = 0; i < 5; i++) {
-        if (beepseconds[beepflag] == 60 * state->minutes[i]) {
-            if (alarmflag > 1) {
-                watch_buzzer_play_sequence((int8_t *)double_beep, NULL);
-            }   
-            ringflag = true;           
-        }
-    }
-    if (!ringflag) {
-        if (alarmflag == 3) {
-            watch_buzzer_play_sequence((int8_t *)single_beep, NULL);
-        }
-    }
-    ringflag = false;
-    beepflag++;
-}
-
-static void start(sailing_state_t *state) {//gets called by starting / switching to next signal
-    while (beepseconds[beepflag] < state->minutes[state->index]*60) {
-        state->index++;
-    }
-    while (beepseconds[beepflag] > state->minutes[state->index]*60) {
-        beepflag++;
-    }
-    if (state->index > 5 || state->minutes[state->index] == 0) {
-        watch_date_time_t now = watch_rtc_get_date_time();
-        state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
-        state->target_ts = state->now_ts;
-        if (alarmflag != 0){
-            watch_buzzer_play_sequence(long_beep, NULL);
-        }
-        counting(state);
-        return;
-    }
-    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;
-    watch_date_time_t now = watch_rtc_get_date_time();
-    state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
-    state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, state->minutes[state->index], 0);
-    ring(state, settings);
-}
-
-static void settings_increment(sailing_state_t *state) {
-    state->minutes[state->selection] += 1;
-    uint8_t max = 11;
-    if (state->selection > 0) {
-        max = state->minutes[state->selection-1];
-    }
-    if (state->minutes[state->selection] >= max) {
-        state->minutes[state->selection] = 0;
-    }
-    // ensure that minutes are decreasing
-    if (state->selection < 5) {
-        for (uint8_t i = 0; i < 5; i++) {
-            if (state->minutes[i+1] >= state->minutes[i]) {
-                if (state->minutes[i] > 0) {
-                    state->minutes[i+1] = state->minutes[i] - 1;
-                } else {
-                    state->minutes[i+1] = 0;
-                }
-            }
-        }
-    }
-    return;
-}
-
-void sailing_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(sailing_state_t));
-        sailing_state_t *state = (sailing_state_t *)*context_ptr;
-        memset(*context_ptr, 0, sizeof(sailing_state_t));
-        static const uint8_t default_minutes[6] = DEFAULT_MINUTES;
-        memcpy(&state->minutes, default_minutes, sizeof(default_minutes));
-        state->watch_face_index = watch_face_index;
-    }
-}
-
-void sailing_face_activate(void *context) {
-    sailing_state_t *state = (sailing_state_t *)context;
-    if(state->mode == sl_running) {
-        watch_date_time_t now = watch_rtc_get_date_time();
-        state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
-    }
-    if(state->mode == sl_counting) {
-        watch_date_time_t now = watch_rtc_get_date_time();
-        state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
-        watch_set_indicator(WATCH_INDICATOR_LAP);
-    }
-    switch (alarmflag) {
-        case 0: //no sound
-            watch_clear_indicator(WATCH_INDICATOR_BELL);
-            watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-            break;
-        case 1: //sound at start only
-            watch_set_indicator(WATCH_INDICATOR_BELL);
-            watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-            break;
-        case 2: //sound at set minutes
-            watch_clear_indicator(WATCH_INDICATOR_BELL);
-            watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-            break;
-        case 3: //sound at every minute, 30s, 10-0s
-            watch_set_indicator(WATCH_INDICATOR_BELL);
-            watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-            break;
-    }
-}
-
-
-bool sailing_face_loop(movement_event_t event, void *context) {
-    sailing_state_t *state = (sailing_state_t *)context;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            draw(state, event.subsecond, settings);
-            break;
-        case EVENT_TICK:
-            if (state->mode == sl_running || state->mode == sl_counting) {
-                state->now_ts++;
-            }
-            draw(state, event.subsecond, settings);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (state->mode == sl_running) {
-                reset(state);
-            }
-            if (state->mode == sl_counting) {
-                reset(state);
-            }
-            if (state->mode == sl_setting) {
-                if (alarmflag == 3) {
-                    alarmflag = 0;
-                }
-                else {
-                    alarmflag++;
-                }
-                switch (alarmflag) {
-                    case 0: //no sound
-                        watch_clear_indicator(WATCH_INDICATOR_BELL);
-                        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-                        break;
-                    case 1: //sound at start only
-                        watch_set_indicator(WATCH_INDICATOR_BELL);
-                        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-                        break;
-                    case 2: //sound at set minutes
-                        watch_clear_indicator(WATCH_INDICATOR_BELL);
-                        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-                        break;
-                    case 3: //sound at every minute, 30s, 10-0s
-                        watch_set_indicator(WATCH_INDICATOR_BELL);
-                        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-                        break;
-                }
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            switch(state->mode) {
-                case sl_running:
-                    movement_illuminate_led();
-                    break;
-                case sl_waiting:
-                    state->mode = sl_setting;
-                    movement_request_tick_frequency(4);
-                    break;
-                case sl_setting:
-                    state->selection++;
-                    if(state->selection >= sl_SELECTIONS) {
-                        state->selection = 0;
-                        state->mode = sl_waiting;
-                        movement_request_tick_frequency(1);
-                    }
-                    break;
-                case sl_counting:
-                    movement_illuminate_led();
-                    break;
-            }
-            draw(state, event.subsecond, settings);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch(state->mode) {
-                case sl_running:
-                    start(state, settings);
-                    break;
-                case sl_waiting:
-                    start(state, settings);
-                    break;
-                case sl_setting:
-                    settings_increment(state);
-                    break;
-                case sl_counting:
-                    //implement lap counting up to 39
-                    if (lap <39){
-                        lap++;
-                    }
-                    break;
-            }
-            draw(state, event.subsecond, settings);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            ring(state, settings);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->mode == sl_setting) {
-                    static const uint8_t default_minutes[6] = DEFAULT_MINUTES;
-                    memcpy(&state->minutes, default_minutes, sizeof(default_minutes));
-                    state->index = 0;
-                    draw(state, event.subsecond, settings);
-                    break;
-            }
-            if (state->mode == sl_counting) {
-                lap = 0;
-            }
-            break;
-        case EVENT_TIMEOUT:
-            if (state->mode != sl_running && state->mode != sl_counting) {
-                movement_move_to_face(0);
-            }
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void sailing_face_resign(void *context) {
-    sailing_state_t *state = (sailing_state_t *)context;
-    if (state->mode == sl_setting) {
-        state->selection = 0;
-        state->mode = sl_waiting;
-    }
-}
diff --git a/movement/watch_faces/complication/sailing_face.h b/movement/watch_faces/complication/sailing_face.h
deleted file mode 100644 (file)
index b9a5cfa..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Jan H. Voigt
- * Copyright (c) 2022 Wesley Ellis
- * Copyright (c) 2022 Niclas Hoyer
- *
- * 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 SAILING_FACE_H_
-#define SAILING_FACE_H_
-
-/*
- * SAILING face
- * Implements a sailing timer.
- *
- * Usage:
- *
- * Waiting mode:
- * LIGHT button enters settings
- * ALARM button starts the timer (sailing mode).
- *
- * Sailing mode:
- * ALARM button switches to next programmed start signal.
- * Long press on LIGHT button resets timer and enters waiting mode.
- * Countdown to zero, then switch to counting mode.
- *
- * Counting mode:
- * After the start signal (0s), the duration of the race is counted (like a stopwatch timer).
- * ALARM button increases the lap counter, ALARM long press resets lap counter.
- * Long press on LIGHT button resets timer and enters waiting mode.
- *
- * Setting mode:
- * ALARM button increases active (blinking) signal. Goes to 0 if upper boundary
- * (11 or whatever the signal left to the active one is set to) is met.
- * 10 is printed vertically (letter o plus top segment).
- * ALARM button long press resets to default minutes (5-4-1-0).
- * LIGHT button cycles through the signals.
- * Long press on LIGHT button cycles through sound modes:
- * - Bell indicator: Sound at start (0s) only.
- * - Signal indicator: Sound at each programmed signal and at start.
- * - Bell+Signal: Sound at each minute, at 30s and at 10s countdown.
- * - No indicator: No sound.
- */
-
-#include "movement.h"
-
-typedef enum {
-    sl_waiting,
-    sl_running,
-    sl_setting,
-    sl_counting
-} sailing_mode_t;
-
-typedef struct {
-    uint8_t watch_face_index;
-    uint32_t target_ts;
-    uint32_t now_ts;
-    uint32_t nextbeep_ts;
-    uint8_t index;
-    uint8_t minutes[6];
-    uint8_t selection;
-    sailing_mode_t mode;
-} sailing_state_t;
-
-
-void sailing_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void sailing_face_activate(void *context);
-bool sailing_face_loop(movement_event_t event, void *context);
-void sailing_face_resign(void *context);
-
-#define sailing_face ((const watch_face_t){ \
-    sailing_face_setup, \
-    sailing_face_activate, \
-    sailing_face_loop, \
-    sailing_face_resign, \
-    NULL, \
-})
-
-#endif // sailing_FACE_H_
diff --git a/movement/watch_faces/complication/ships_bell_face.c b/movement/watch_faces/complication/ships_bell_face.c
deleted file mode 100644 (file)
index fc4d51b..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 buckket
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "ships_bell_face.h"
-
-static void ships_bell_ring() {
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-
-    date_time.unit.hour %= 4;
-    date_time.unit.hour = date_time.unit.hour == 0 && date_time.unit.minute < 30 ? 4 : date_time.unit.hour;
-
-    for (uint8_t i = 0; i < date_time.unit.hour; i++) {
-        watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
-        watch_buzzer_play_note(BUZZER_NOTE_C8, 100);
-        watch_buzzer_play_note(BUZZER_NOTE_REST, 250);
-    }
-
-    if (date_time.unit.minute >= 30 ? 1 : 0) {
-        watch_buzzer_play_note(BUZZER_NOTE_C8, 100);
-    }
-}
-
-static void ships_bell_draw(ships_bell_state_t *state) {
-    char buf[8];
-
-    if (state->on_watch) {
-        sprintf(buf, "%d", state->on_watch);
-    } else {
-        sprintf(buf, " ");
-    }
-
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    date_time.unit.hour %= 4;
-
-    sprintf(buf + 1, " %d%02d%02d", date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
-    watch_display_string(buf, 3);
-}
-
-void ships_bell_face_setup(uint8_t watch_face_index, void **context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(ships_bell_state_t));
-        memset(*context_ptr, 0, sizeof(ships_bell_state_t));
-    }
-}
-
-void ships_bell_face_activate(void *context) {
-
-    ships_bell_state_t *state = (ships_bell_state_t *) context;
-    if (state->bell_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-    else watch_clear_indicator(WATCH_INDICATOR_BELL);
-
-    watch_display_string("SB", 0);
-    watch_set_colon();
-}
-
-bool ships_bell_face_loop(movement_event_t event, void *context) {
-
-    ships_bell_state_t *state = (ships_bell_state_t *) context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            ships_bell_draw(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            state->bell_enabled = !state->bell_enabled;
-            if (state->bell_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
-            else watch_clear_indicator(WATCH_INDICATOR_BELL);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->on_watch = (state->on_watch + 1) % 4;
-            ships_bell_draw(state);
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        case EVENT_BACKGROUND_TASK:
-            if (watch_is_buzzer_or_led_enabled()) {
-                ships_bell_ring();
-            } else {
-                watch_enable_buzzer();
-                ships_bell_ring();
-                watch_disable_buzzer();
-            }
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void ships_bell_face_resign(void *context) {
-    (void) context;
-}
-
-movement_watch_face_advisory_t ships_bell_face_advise(void *context) {
-    ships_bell_state_t *state = (ships_bell_state_t *) context;
-    movement_watch_face_advisory_t retval = { 0 };
-
-    if (!state->bell_enabled) return retval;
-
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    if (!(date_time.unit.minute == 0 || date_time.unit.minute == 30)) return retval;
-
-    date_time.unit.hour %= 12;
-    // #SecondMovement: This was migrated to the new advisory API but not tested. Needs more testing!
-    switch (state->on_watch) {
-        case 1:
-            if ((date_time.unit.hour >= 4 && date_time.unit.hour < 8) ||
-                (date_time.unit.hour == 8 && date_time.unit.minute == 0))
-                retval.wants_background_task = true;
-                break;
-        case 2:
-            if ((date_time.unit.hour >= 8 && date_time.unit.hour < 12) ||
-                (date_time.unit.hour == 0 && date_time.unit.minute == 0))
-                retval.wants_background_task = true;
-                break;
-        case 3:
-            if ((date_time.unit.hour >= 0 && date_time.unit.hour < 4) ||
-                (date_time.unit.hour == 4 && date_time.unit.minute == 0))
-                retval.wants_background_task = true;
-                break;
-        default:
-            retval.wants_background_task = true;
-    }
-
-    return retval;
-}
diff --git a/movement/watch_faces/complication/ships_bell_face.h b/movement/watch_faces/complication/ships_bell_face.h
deleted file mode 100644 (file)
index bda1697..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 buckket
- *
- * 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 SHIPS_BELL_FACE_H_
-#define SHIPS_BELL_FACE_H_
-
-/*
- * SHIP'S BELL face
- * A ship's bell complication.
- *
- * See: https://en.wikipedia.org/wiki/Ship%27s_bell#Simpler_system
- *
- * Similar to the default hourly signal of the simple_clock_face this complication will use the buzzer to signal
- * the time in half-hour intervals according to the scheme mentioned above.
- *
- * Additionally, the user can specify one of the three watches
- * of the standard merchant watch system to only receive signals during this watch.
- *
- * If no watch is specified all signals are emitted.
- *
- * Usage:
- *   - short press Alarm button: Turn on/off bell
- *   - long press Alarm button: Cycle through the watches (All/1/2/3)
- */
-
-#include "movement.h"
-
-typedef struct {
-    bool bell_enabled;
-    uint8_t on_watch;
-} ships_bell_state_t;
-
-void ships_bell_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void ships_bell_face_activate(void *context);
-bool ships_bell_face_loop(movement_event_t event, void *context);
-void ships_bell_face_resign(void *context);
-movement_watch_face_advisory_t ships_bell_face_advise(void *context);
-
-#define ships_bell_face ((const watch_face_t){ \
-    ships_bell_face_setup, \
-    ships_bell_face_activate, \
-    ships_bell_face_loop, \
-    ships_bell_face_resign, \
-    ships_bell_face_advise, \
-})
-
-#endif // SHIPS_BELL_FACE_H_
diff --git a/movement/watch_faces/complication/simon_face.c b/movement/watch_faces/complication/simon_face.c
deleted file mode 100644 (file)
index 41bc5c3..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 <#author_name#>
- *
- * 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.
- */
-
-#include "simon_face.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-// Emulator only: need time() to seed the random number generator
-#if __EMSCRIPTEN__
-#include <time.h>
-#endif
-
-static char _simon_display_buf[12];
-static uint8_t _timer;
-static uint16_t _delay_beep;
-static uint16_t _timeout;
-static uint8_t _secSub;
-
-static inline uint8_t _simon_get_rand_num(uint8_t num_values) {
-#if __EMSCRIPTEN__
-    return rand() % num_values;
-#else
-    return arc4random_uniform(num_values);
-#endif
-}
-
-static void _simon_clear_display(simon_state_t *state) {
-    if (state->playing_state == SIMON_NOT_PLAYING) {
-        watch_display_string("          ", 0);
-    } else {
-        sprintf(_simon_display_buf, "  %2d      ", state->sequence_length);
-        watch_display_string(_simon_display_buf, 0);
-    }
-}
-
-static void _simon_not_playing_display(simon_state_t *state) {
-    _simon_clear_display(state);
-
-    sprintf(_simon_display_buf, "SI  %d", state->best_score);
-    if (!state->soundOff)
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    else
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-    if (!state->lightOff)
-        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    else
-        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-    watch_display_string(_simon_display_buf, 0);
-    switch (state->mode)
-    {
-    case SIMON_MODE_EASY:
-        watch_display_string("E", 9);
-        break;
-    case SIMON_MODE_HARD:
-        watch_display_string("H", 9);
-        break;
-    default:
-        break;
-    }
-}
-
-static void _simon_reset(simon_state_t *state) {
-    state->playing_state = SIMON_NOT_PLAYING;
-    state->listen_index = 0;
-    state->sequence_length = 0;
-    _simon_not_playing_display(state);
-}
-
-
-static void _simon_display_note(SimonNote note, simon_state_t *state) {
-    char *ndtemplate = NULL;
-
-    switch (note) {
-        case SIMON_LED_NOTE:
-            ndtemplate = "LI%2d      ";
-            break;
-        case SIMON_ALARM_NOTE:
-            ndtemplate = "  %2d    AL";
-            break;
-        case SIMON_MODE_NOTE:
-            ndtemplate = "  %2dDE    ";
-            break;
-        case SIMON_WRONG_NOTE:
-            ndtemplate = "OH  NOOOOO";
-    }
-
-    sprintf(_simon_display_buf, ndtemplate, state->sequence_length);
-    watch_display_string(_simon_display_buf, 0);
-}
-
-static void _simon_play_note(SimonNote note, simon_state_t *state, bool skip_rest) {
-    _simon_display_note(note, state);
-    switch (note) {
-        case SIMON_LED_NOTE:
-            if (!state->lightOff) watch_set_led_yellow();
-            if (state->soundOff)
-                delay_ms(_delay_beep);
-            else
-                watch_buzzer_play_note(BUZZER_NOTE_D3, _delay_beep);
-            break;
-        case SIMON_MODE_NOTE:
-            if (!state->lightOff) watch_set_led_red();
-            if (state->soundOff)
-                delay_ms(_delay_beep);
-            else
-               watch_buzzer_play_note(BUZZER_NOTE_E4, _delay_beep);
-            break;
-        case SIMON_ALARM_NOTE:
-            if (!state->lightOff) watch_set_led_green();
-            if (state->soundOff)
-                delay_ms(_delay_beep);
-            else
-               watch_buzzer_play_note(BUZZER_NOTE_C3, _delay_beep);
-            break;
-        case SIMON_WRONG_NOTE:
-            if (state->soundOff)
-                delay_ms(800);
-            else
-               watch_buzzer_play_note(BUZZER_NOTE_A1, 800);
-            break;
-    }
-    watch_set_led_off();
-
-    if (note != SIMON_WRONG_NOTE) {
-        _simon_clear_display(state);
-        if (!skip_rest) {
-            watch_buzzer_play_note(BUZZER_NOTE_REST, (_delay_beep * 2)/3);
-        }
-    }
-}
-
-
-static void _simon_setup_next_note(simon_state_t *state) {
-    if (state->sequence_length > state->best_score) {
-        state->best_score = state->sequence_length;
-    }
-
-    _simon_clear_display(state);
-    state->playing_state = SIMON_TEACHING;
-    state->sequence[state->sequence_length] = _simon_get_rand_num(3) + 1;
-    state->sequence_length = state->sequence_length + 1;
-    state->teaching_index = 0;
-    state->listen_index = 0;
-}
-
-static void _simon_listen(SimonNote note, simon_state_t *state) {
-    if (state->sequence[state->listen_index] == note) {
-        _simon_play_note(note, state, true);
-        state->listen_index++;
-        _timer = 0;
-
-        if (state->listen_index == state->sequence_length) {
-            state->playing_state = SIMON_READY_FOR_NEXT_NOTE;
-        }
-    } else {
-        _simon_play_note(SIMON_WRONG_NOTE, state, true);
-        _simon_reset(state);
-    }
-}
-
-static void _simon_begin_listening(simon_state_t *state) {
-    state->playing_state = SIMON_LISTENING_BACK;
-    state->listen_index = 0;
-}
-
-static void _simon_change_speed(simon_state_t *state){
-      switch (state->mode)
-  {
-  case SIMON_MODE_HARD:
-        _delay_beep = DELAY_FOR_TONE_MS / 2;
-        _secSub = SIMON_FACE_FREQUENCY / 2;
-        _timeout = (TIMER_MAX * SIMON_FACE_FREQUENCY) / 2;
-    break;
-  default:
-        _delay_beep = DELAY_FOR_TONE_MS;
-        _secSub = SIMON_FACE_FREQUENCY;
-        _timeout = TIMER_MAX * SIMON_FACE_FREQUENCY;
-    break;
-  }
-}
-
-void simon_face_setup(uint8_t watch_face_index,
-        void **context_ptr) {
-    (void)watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(simon_state_t));
-        memset(*context_ptr, 0, sizeof(simon_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens
-        // only at boot.
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch
-    // wakes from deep sleep.
-#if __EMSCRIPTEN__
-    // simulator only: seed the randon number generator
-    time_t t;
-    srand((unsigned)time(&t));
-#endif
-}
-
-void simon_face_activate(void *context) {
-  (void) settings;
-  (void) context;
-  simon_state_t *state = (simon_state_t *)context;
-  _simon_change_speed(state);
-  movement_request_tick_frequency(SIMON_FACE_FREQUENCY);
-   _timer = 0;
-}
-
-bool simon_face_loop(movement_event_t event,
-        void *context) {
-    simon_state_t *state = (simon_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Show your initial UI here.
-            _simon_reset(state);
-            break;
-        case EVENT_TICK:
-            if (state->playing_state == SIMON_LISTENING_BACK && state->mode != SIMON_MODE_EASY)
-            {
-                _timer++;
-                if(_timer >= (_timeout)){
-                    _timer = 0;
-                    _simon_play_note(SIMON_WRONG_NOTE, state, true);
-                    _simon_reset(state);
-                }
-            }
-            else if (state->playing_state == SIMON_TEACHING && event.subsecond  == 0) {
-                SimonNote note = state->sequence[state->teaching_index];
-                // if this is the final note in the sequence, don't play the rest to let
-                // the player jump in faster
-                _simon_play_note(note, state, state->teaching_index == (state->sequence_length - 1));
-                state->teaching_index++;
-
-                if (state->teaching_index == state->sequence_length) {
-                    _simon_begin_listening(state);
-                }
-            }
-            else if (state->playing_state == SIMON_READY_FOR_NEXT_NOTE && (event.subsecond % _secSub)  == 0) {
-                _timer = 0;
-                _simon_setup_next_note(state);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (state->playing_state == SIMON_NOT_PLAYING) {
-                state->lightOff = !state->lightOff;
-                _simon_not_playing_display(state);
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->playing_state == SIMON_NOT_PLAYING) {
-                state->soundOff = !state->soundOff;
-                _simon_not_playing_display(state);
-                if (!state->soundOff)
-                    watch_buzzer_play_note(BUZZER_NOTE_D3, _delay_beep);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (state->playing_state == SIMON_NOT_PLAYING) {
-                state->sequence_length = 0;
-                watch_clear_indicator(WATCH_INDICATOR_BELL);
-                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-                _simon_setup_next_note(state);
-            } else if (state->playing_state == SIMON_LISTENING_BACK) {
-                _simon_listen(SIMON_LED_NOTE, state);
-            }
-            break;
-        case EVENT_MODE_LONG_PRESS:
-            if (state->playing_state == SIMON_NOT_PLAYING) {
-                movement_move_to_face(0);
-            } else {
-                state->playing_state = SIMON_NOT_PLAYING;
-                _simon_reset(state);
-            }
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            if (state->playing_state == SIMON_NOT_PLAYING) {
-                movement_move_to_next_face();
-            } else if (state->playing_state == SIMON_LISTENING_BACK) {
-                _simon_listen(SIMON_MODE_NOTE, state);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if (state->playing_state == SIMON_LISTENING_BACK) {
-                _simon_listen(SIMON_ALARM_NOTE, state);
-            }
-            else if (state->playing_state == SIMON_NOT_PLAYING){
-                state->mode = (state->mode + 1) % SIMON_MODE_TOTAL;
-                _simon_change_speed(state);
-                _simon_not_playing_display(state);
-            }
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void simon_face_resign(void *context) {
-    (void)context;
-    watch_set_led_off();
-    watch_set_buzzer_off();
-}
diff --git a/movement/watch_faces/complication/simon_face.h b/movement/watch_faces/complication/simon_face.h
deleted file mode 100644 (file)
index 63e8389..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 <#author_name#>
- *
- * 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 SIMON_FACE_H_
-#define SIMON_FACE_H_
-
-#include "movement.h"
-
-/*
- * simon_face
- * -----------
- * The classic electronic game, Simon, reduced to be played on a Sensor-Watch
- *
- * How to play:
- *
- * When first arriving at the face, it will show your best score.
- *
- * Press the light button to start the game.
- *
- * A sequence will be played, starting with length 1. The sequence can be
- * made up of tones corresponding to any of the three buttons.
- *
- * light button: "LI" will display at the top of the screen, the LED will be yellow, and a high D will play
- * mode button: "DE" will display at the left of the screen, the LED will be red, and a high E will play
- * alarm button: "AL" will display on the right of the screen, the LED will be green, and a high C will play
- *
- * Once the sequence has finished, press the same buttons to recreate the sequence.
- *
- * If correct, the sequence will get one tone longer and play again. See how long of a sequence you can get.
- *
- * If you recreate the sequence incorrectly, a low note will play with "OH NOOOOO" displayed and the game is over.
- * Press light to play again.
- *
- * Once playing, long press the mode button when it is your turn to exit the game early.
- */
-
-#define MAX_SEQUENCE 99
-
-typedef enum SimonNote {
-    SIMON_LED_NOTE = 1,
-    SIMON_MODE_NOTE,
-    SIMON_ALARM_NOTE,
-    SIMON_WRONG_NOTE
-} SimonNote;
-
-typedef enum SimonPlayingState {
-    SIMON_NOT_PLAYING = 0,
-    SIMON_TEACHING,
-    SIMON_LISTENING_BACK,
-    SIMON_READY_FOR_NEXT_NOTE
-} SimonPlayingState;
-
-typedef enum SimonMode {
-    SIMON_MODE_NORMAL = 0,  // 5 Second timeout if nothing is input
-    SIMON_MODE_EASY,  // There is no timeout in this mode
-    SIMON_MODE_HARD,  // The speed of the teaching is doubled and th etimeout is halved
-    SIMON_MODE_TOTAL
-} SimonMode;
-
-typedef struct {
-    uint8_t best_score;
-    SimonNote sequence[MAX_SEQUENCE];
-    uint8_t sequence_length;
-    uint8_t teaching_index;
-    uint8_t listen_index;
-    bool soundOff;
-    bool lightOff;
-    uint8_t mode:6;
-    SimonPlayingState playing_state;
-} simon_state_t;
-
-void simon_face_setup(uint8_t watch_face_index, void **context_ptr);
-void simon_face_activate(void *context);
-bool simon_face_loop(movement_event_t event, void *context);
-void simon_face_resign(void *context);
-
-#define simon_face                                                             \
-    ((const watch_face_t){                                                       \
-     simon_face_setup,                                                        \
-     simon_face_activate,                                                     \
-     simon_face_loop,                                                         \
-     simon_face_resign,                                                       \
-     NULL,                                                                    \
-     })
-
-#define TIMER_MAX 5
-#define SIMON_FACE_FREQUENCY 8
-#define DELAY_FOR_TONE_MS 300
-
-#endif // SIMON_FACE_H_
diff --git a/movement/watch_faces/complication/simple_calculator_face.c b/movement/watch_faces/complication/simple_calculator_face.c
deleted file mode 100644 (file)
index 7c849cd..0000000
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 Patrick McGuire
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "simple_calculator_face.h"
-
-void simple_calculator_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(simple_calculator_state_t));
-        memset(*context_ptr, 0, sizeof(simple_calculator_state_t));
-    }
-}
-
-static void reset_to_zero(calculator_number_t *number) {
-    number->negative = false;
-    number->hundredths = 0;
-    number->tenths = 0;
-    number->ones = 0;
-    number->tens = 0;
-    number->hundreds = 0;
-    number->thousands = 0;
-}
-
-void simple_calculator_face_activate(void *context) {
-    simple_calculator_state_t *state = (simple_calculator_state_t *)context;
-    state->placeholder = PLACEHOLDER_ONES;
-    state->mode = MODE_ENTERING_FIRST_NUM;
-    reset_to_zero(&state->second_num);
-    reset_to_zero(&state->result);
-    movement_request_tick_frequency(4);
-}
-
-static void increment_placeholder(calculator_number_t *number, calculator_placeholder_t placeholder) {
-    uint8_t *digits[] = {
-        &number->hundredths,
-        &number->tenths,
-        &number->ones,
-        &number->tens,
-        &number->hundreds,
-        &number->thousands
-    };
-    *digits[placeholder] = (*digits[placeholder] + 1) % 10;
-}
-
-static float convert_to_float(calculator_number_t number) {
-    float result = 0.0;
-
-    // Add the whole number portion
-    result += number.thousands * 1000.0f;
-    result += number.hundreds * 100.0f;
-    result += number.tens * 10.0f;
-    result += number.ones * 1.0f;
-
-    // Add the fractional portion
-    result += number.tenths * 0.1f;
-    result += number.hundredths * 0.01f;
-
-    // Round to nearest hundredth
-    result = roundf(result * 100) / 100;
-    
-    // Handle negative numbers
-    if (number.negative) result = -result;
-    //printf("convert_to_float results = %f\n", result); // For debugging
-
-    return result;
-}
-
-static char* update_display_number(calculator_number_t *number, char *display_string, uint8_t which_num) {
-    char sign = ' ';
-    if (number->negative) sign = '-';
-
-    sprintf(display_string, "CA%d%c%d%d%d%d%d%d",
-            which_num,
-            sign,
-            number->thousands,
-            number->hundreds,
-            number->tens,
-            number->ones,
-            number->tenths,
-            number->hundredths);
-
-    return display_string;
-}
-
-static void set_operation(simple_calculator_state_t *state) {
-    switch (state->operation) {
-        case OP_ADD:
-            watch_display_string("       Add", 0);
-            break;
-        case OP_SUB:
-            watch_display_string("       sub", 0);
-            break;
-        case OP_MULT:
-            watch_display_string("      n&ul", 0);
-            break;
-        case OP_DIV:
-            watch_display_string("       div", 0);
-            break;
-        case OP_ROOT:
-            watch_display_string("      root", 0);
-            break;
-        case OP_POWER:
-            watch_display_string("       pow", 0);
-            break;
-    }
-}
-
-static void cycle_operation(simple_calculator_state_t *state) {
-    state->operation = (state->operation + 1) % OPERATIONS_COUNT; // Assuming there are 6 operations
-}
-
-
-static calculator_number_t convert_to_string(float number) {
-    calculator_number_t result;
-
-    // Handle negative numbers
-    if (number < 0) {
-        number = -number;
-        result.negative = true;
-    } else result.negative = false;
-
-    // Get each digit from each placeholder
-    int int_part = (int)number;
-
-    float decimal_part_float = ((number - int_part) * 100); // two decimal places
-    //printf("decimal_part_float = %f\n", decimal_part_float); //For debugging
-
-    int decimal_part = round(decimal_part_float);
-    //printf("decimal_part = %d\n", decimal_part); //For debugging
-
-    result.thousands = int_part / 1000 % 10;
-    result.hundreds = int_part / 100 % 10;
-    result.tens = int_part / 10 % 10;
-    result.ones = int_part % 10;
-
-    result.tenths = decimal_part / 10 % 10;
-    result.hundredths = decimal_part % 10;
-
-    return result;
-}
-
-// This is the main function for setting the first_num and second_num
-// WISH: there must be a way to pass less to this function?
-static void set_number(calculator_number_t *number, calculator_placeholder_t placeholder, char *display_string, char *temp_display_string, movement_event_t event, uint8_t which_num) {
-
-    // Create the display index
-    uint8_t display_index;
-
-    // Update display string with current number and copy into temp string
-    update_display_number(number, display_string, which_num);
-    strcpy(temp_display_string, display_string);
-    
-    // Determine the display index based on the placeholder
-    display_index = 9 - placeholder;
-    
-    // Blink selected placeholder
-    // Check if `event.subsecond` is even
-    if (event.subsecond % 2 == 0) {
-        // Replace the character at the index corresponding to the current placeholder with a space
-        temp_display_string[display_index] = ' ';
-    }
-    
-    // Display the (possibly modified) string
-    watch_display_string(temp_display_string, 0);
-} 
-
-static void view_results(simple_calculator_state_t *state, char *display_string) {
-
-    // Initialize float variables to do the math
-    float first_num_float, second_num_float, result_float = 0.0f;
-
-    // Convert the passed numbers to floats
-    first_num_float = convert_to_float(state->first_num);
-    second_num_float = convert_to_float(state->second_num);
-    
-    // Perform the calculation based on the selected operation
-    switch (state->operation) {
-        case OP_ADD:
-            result_float = first_num_float + second_num_float;
-            break;
-        case OP_SUB:
-            result_float = first_num_float - second_num_float;
-            break;
-        case OP_MULT:
-            result_float = first_num_float * second_num_float;
-            break;
-        case OP_DIV:
-            if (second_num_float != 0) {
-                result_float = first_num_float / second_num_float;
-            } else {
-                state->mode = MODE_ERROR;
-                return;
-            }
-            break;
-        case OP_ROOT:
-            if (first_num_float >= 0) {
-                result_float = sqrtf(first_num_float);
-            } else {
-                state->mode = MODE_ERROR;
-                return;
-            }
-            break;
-        case OP_POWER:
-            result_float = powf(first_num_float, second_num_float);
-            break;
-        default:
-            result_float = 0.0f;
-            break;
-    }
-
-    // Be sure the result can fit on the watch display, else error
-    if (result_float > 9999.99 || result_float < -9999.99) {
-        state->mode = MODE_ERROR;
-        return;
-    }
-    
-    result_float = roundf(result_float * 100.0f) / 100.0f; // Might not be needed
-
-    //printf("result as float = %f\n", result_float); // For debugging
-    
-    // Convert the float result to a string
-    // This isn't strictly necessary, but allows easily reusing the result as
-    // the next calculation's first_num
-    state->result = convert_to_string(result_float);
-    
-    // Update the display with the result
-    update_display_number(&state->result, display_string, 3);
-
-    //printf("display_string = %s\n", display_string); // For debugging
-
-    watch_display_string(display_string, 0);
-}
-
-// Used both when returning from errors and when long pressing MODE
-static void reset_all(simple_calculator_state_t *state) {
-    reset_to_zero(&state->first_num);
-    reset_to_zero(&state->second_num);
-    state->mode = MODE_ENTERING_FIRST_NUM;
-    state->operation = OP_ADD;
-    state->placeholder = PLACEHOLDER_ONES;
-}
-
-bool simple_calculator_face_loop(movement_event_t event, void *context) {
-    simple_calculator_state_t *state = (simple_calculator_state_t *)context;
-    char display_string[10];
-    char temp_display_string[10];  // Temporary buffer for blinking effect
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK: 
-            switch (state->mode) {
-                case MODE_ENTERING_FIRST_NUM:
-                    // See the WISH for this function above
-                    set_number(&state->first_num, 
-                            state->placeholder,
-                            display_string, 
-                            temp_display_string, 
-                            event,
-                            1);
-                    break;                
-
-                case MODE_CHOOSING:
-                    set_operation(state);
-                    break;
-
-                case MODE_ENTERING_SECOND_NUM:
-                    // If doing a square root calculation, skip to results
-                    if (state->operation == OP_ROOT) {
-                        state->mode = MODE_VIEW_RESULTS;
-                    } else {
-                        // See the WISH for this function above
-                        set_number(&state->second_num, 
-                            state->placeholder,
-                            display_string, 
-                            temp_display_string, 
-                            event,
-                            2);
-                    }
-                    break;
-
-                case MODE_VIEW_RESULTS:
-                    view_results(state, display_string);
-                    break;
-
-                case MODE_ERROR:
-                    watch_display_string("CA  Error ", 0);
-                    break;
-            }
-            break;
-
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-
-        case EVENT_LIGHT_BUTTON_UP:
-            switch (state->mode) {
-                case MODE_ENTERING_FIRST_NUM:
-                case MODE_ENTERING_SECOND_NUM:
-                    // Move to the next placeholder when the light button is pressed
-                    state->placeholder = (state->placeholder + 1) % MAX_PLACEHOLDERS; // Loop back to the start after PLACEHOLDER_THOUSANDS
-                    break;
-                case MODE_CHOOSING:
-                    cycle_operation(state);
-                    break;
-                case MODE_ERROR:
-                    reset_all(state);
-                    break;
-                case MODE_VIEW_RESULTS:
-                    break;
-            }
-            break;
-
-        case EVENT_LIGHT_LONG_PRESS:
-            switch (state->mode) {
-                case MODE_ENTERING_FIRST_NUM:
-                    // toggle negative on state->first_num
-                    state->first_num.negative = !state->first_num.negative;
-                    break;
-                case MODE_ENTERING_SECOND_NUM:
-                    // toggle negative on state->second_num
-                    state->second_num.negative = !state->second_num.negative;
-                    break;
-                case MODE_ERROR:
-                    reset_all(state);
-                    break;
-                case MODE_CHOOSING:
-                case MODE_VIEW_RESULTS:
-                    break;
-            }
-            break;
-
-        case EVENT_ALARM_BUTTON_UP:
-            switch (state->mode) {
-                case MODE_ENTERING_FIRST_NUM:
-                    // Increment the digit in the current placeholder
-                    increment_placeholder(&state->first_num, state->placeholder);
-                    update_display_number(&state->first_num, display_string, 1);
-
-                    //printf("display_string = %s\n", display_string); // For debugging
-
-                    break;
-                case MODE_CHOOSING:
-                    // Confirm and select the current operation
-                    state->mode = MODE_ENTERING_SECOND_NUM;                
-                    break;
-                case MODE_ENTERING_SECOND_NUM:
-                    // Increment the digit in the current placeholder
-                    increment_placeholder(&state->second_num, state->placeholder);
-                    update_display_number(&state->second_num, display_string, 2);
-
-                    //printf("display_string = %s\n", display_string); // For debugging
-
-                    break;
-                case MODE_ERROR:
-                    reset_all(state);
-                    break;
-                case MODE_VIEW_RESULTS:
-                    break;
-            }
-            break;
-
-        case EVENT_ALARM_LONG_PRESS:
-            switch (state->mode) {
-                case MODE_ENTERING_FIRST_NUM:
-                    reset_to_zero(&state->first_num);
-                    break;
-                case MODE_ENTERING_SECOND_NUM:
-                    reset_to_zero(&state->second_num);
-                    break;
-                case MODE_ERROR:
-                    reset_all(state);
-                    break;
-                case MODE_CHOOSING:
-                case MODE_VIEW_RESULTS:
-                    break;
-            }
-            break;
-
-        case EVENT_MODE_BUTTON_DOWN:
-            break;
-
-        case EVENT_MODE_BUTTON_UP:
-            if (state->mode == MODE_ERROR) {
-                reset_all(state);
-            } else if (state->mode == MODE_ENTERING_FIRST_NUM &&
-                    state->first_num.hundredths == 0 &&
-                    state->first_num.tenths == 0 &&
-                    state->first_num.ones== 0 &&
-                    state->first_num.tens == 0 &&
-                    state->first_num.hundreds == 0 &&
-                    state->first_num.thousands == 0) { 
-                movement_move_to_next_face();
-            } else {
-                // Reset the placeholder and proceed to the next MODE
-                state->placeholder = PLACEHOLDER_ONES;
-                state->mode = (state->mode + 1) % 4;
-                // When looping back to MODE_ENTERING_FIRST_NUM, reuse the
-                // previous calculation's results as the next calculation's
-                // first_num; also reset other numbers
-                if (state->mode == MODE_ENTERING_FIRST_NUM) {
-                    state->first_num = state->result;
-                    reset_to_zero(&state->second_num);
-                    reset_to_zero(&state->result);
-                }
-            }
-            break;
-
-        case EVENT_MODE_LONG_PRESS:
-            // Move to next face if first number is 0
-            if (state->first_num.hundredths == 0 &&
-                    state->first_num.tenths == 0 &&
-                    state->first_num.ones== 0 &&
-                    state->first_num.tens == 0 &&
-                    state->first_num.hundreds == 0 &&
-                    state->first_num.thousands == 0) { 
-                movement_move_to_face(0);
-            // otherwise, start over
-            } else {
-                reset_all(state);
-            }
-            break;
-
-        case EVENT_TIMEOUT:
-            movement_request_tick_frequency(1);
-            movement_move_to_face(0);
-            break;
-
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void simple_calculator_face_resign(void *context) {
-    (void) context;
-    movement_request_tick_frequency(1);
-}
-
diff --git a/movement/watch_faces/complication/simple_calculator_face.h b/movement/watch_faces/complication/simple_calculator_face.h
deleted file mode 100644 (file)
index 8a8ea02..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 Patrick McGuire
- *
- * 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 SIMPLE_CALCULATOR_FACE_H_
-#define SIMPLE_CALCULATOR_FACE_H_
-
-#include "movement.h"
-
-/*
- * Simple Calculator
- *
- * How to use:
- *
- * Flow:
- * Enter first number -> Select operator -> Enter second number -> View Results
- * 
- * How to read the display:
- * - "CA" is displayed at the top to tell you that you're in the CAlculator
- * - The top-right digit (1, 2, or 3) lets you know whether you're entering the
- *   first number (1), entering the second number (2), or viewing the results (3).
- * - To the right of the top-right digit will show the number's sign. If the
- *   number is negative, a "-" will be displayed, otherwise it is empty.
- * - The 4 large digits to the left are whole numbers and the 2 smaller digits
- *   on the right are the tenths and hundredths decimal places.
- *
- * Entering the first number:
- * - Press ALARM to increment the selected (blinking) digit
- * - Press LIGHT to move to the next placeholder
- * - LONG PRESS the LIGHT button to toggle the number's sign to make it 
- *   negative
- * - LONG PRESS the ALARM button to reset the number to 0
- * - Press MODE to proceed to selecting the operator
- *
- * Selecting the operator:
- * - Press the LIGHT button to cycle through available operators. They are:
- *   + Add
- *   - Subtract
- *   * Multiply
- *   / Divide
- *   sqrtf() Square root
- *   powf() Power (exponent calculation)
- * - Press MODE or ALARM to proceed to entering the second number
- *
- * Entering the second number:
- * - Everything is the same as setting the first number except that pressing
- *   MODE here will proceed to viewing the results
- *
- * Viewing the results:
- * - Pressing MODE will start a new calculation with the result as the first
- *   number. (LONG PRESS ALARM to reset the value to 0)
- *
- * Errors:
- * - An error will be triggered if the result is not able to be displayed, that
- * is, if the value is greater than 9,999.99 or less than -9,999.99.
- * - An error will also be triggered if an impossible operation is selected,
- *   for instance trying to divide by 0 or get the square root of a negative
- *   number.
- * - Exit error mode and start over with any button press.
- *
- */
-
-#define OPERATIONS_COUNT 6
-#define MAX_PLACEHOLDERS 6
-
-typedef struct {
-    bool negative;
-    uint8_t hundredths;
-    uint8_t tenths;
-    uint8_t ones;
-    uint8_t tens;
-    uint8_t hundreds;
-    uint8_t thousands;
-} calculator_number_t;
-
-typedef enum {
-    PLACEHOLDER_HUNDREDTHS,
-    PLACEHOLDER_TENTHS,
-    PLACEHOLDER_ONES,
-    PLACEHOLDER_TENS,
-    PLACEHOLDER_HUNDREDS,
-    PLACEHOLDER_THOUSANDS
-} calculator_placeholder_t;
-
-typedef enum {
-    OP_ADD,
-    OP_SUB,
-    OP_MULT,
-    OP_DIV,
-    OP_ROOT,
-    OP_POWER,
-} calculator_operation_t;
-
-typedef enum {
-    MODE_ENTERING_FIRST_NUM,
-    MODE_CHOOSING,
-    MODE_ENTERING_SECOND_NUM,
-    MODE_VIEW_RESULTS,
-    MODE_ERROR
-} calculator_mode_t;
-
-typedef struct {
-    calculator_number_t first_num;
-    calculator_number_t second_num;
-    calculator_number_t result;
-    calculator_operation_t operation;
-    calculator_mode_t mode;
-    calculator_placeholder_t placeholder;
-} simple_calculator_state_t;
-
-void simple_calculator_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void simple_calculator_face_activate(void *context);
-bool simple_calculator_face_loop(movement_event_t event, void *context);
-void simple_calculator_face_resign(void *context);
-
-#define simple_calculator_face ((const watch_face_t){ \
-    simple_calculator_face_setup, \
-    simple_calculator_face_activate, \
-    simple_calculator_face_loop, \
-    simple_calculator_face_resign, \
-    NULL, \
-})
-
-#endif // SIMPLE_CALCULATOR_FACE_H_
-
diff --git a/movement/watch_faces/complication/simple_coin_flip_face.c b/movement/watch_faces/complication/simple_coin_flip_face.c
deleted file mode 100644 (file)
index 7e0003f..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Wesley Aptekar-Cassels
- *
- * 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.
- */
-
-#if __EMSCRIPTEN__
-#include <time.h>
-#else
-#include "saml22j18a.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include "simple_coin_flip_face.h"
-
-#define SIMPLE_COIN_FLIP_REQUIRE_LONG_PRESS_FOR_REFLIP true
-
-void simple_coin_flip_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(simple_coin_flip_state_t));
-        memset(*context_ptr, 0, sizeof(simple_coin_flip_state_t));
-    }
-}
-
-void simple_coin_flip_face_activate(void *context) {
-    (void) context;
-}
-
-static uint32_t get_random(uint32_t max) {
-    #if __EMSCRIPTEN__
-    return rand() % max;
-    #else
-    return arc4random_uniform(max);
-    #endif
-}
-
-static void animation_0() {
-    watch_display_string("  ", 8);
-    watch_set_pixel(0, 3);
-    watch_set_pixel(0, 6);
-}
-
-static void animation_1() {
-    watch_display_string("  ", 8);
-    watch_set_pixel(1, 3);
-    watch_set_pixel(1, 5);
-}
-
-static void animation_2() {
-    watch_display_string("  ", 8);
-    watch_set_pixel(2, 2);
-    watch_set_pixel(2, 4);
-}
-
-bool simple_coin_flip_face_loop(movement_event_t event, void *context) {
-    simple_coin_flip_state_t *state = (simple_coin_flip_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_display_string("flip", 5);
-            state->animation_frame = 0;
-            break;
-        case EVENT_TICK:
-            switch (state->animation_frame) {
-                case 0:
-                case 7:
-                    return true;
-                case 1:
-                    movement_request_tick_frequency(8);
-                    watch_display_string("      ", 4);
-                    // fallthrough
-                case 5:
-                    animation_0();
-                    break;
-                case 2:
-                case 4:
-                    animation_1();
-                    break;
-                case 3:
-                    animation_2();
-                    break;
-                case 6:
-                    movement_request_tick_frequency(1);
-                    if (get_random(2)) {
-                        watch_display_string("Heads ", 4);
-                    } else {
-                        watch_display_string(" Tails", 4);
-                    }
-                    break;
-            }
-            state->animation_frame++;
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-        case EVENT_ALARM_BUTTON_UP:
-            if (!SIMPLE_COIN_FLIP_REQUIRE_LONG_PRESS_FOR_REFLIP || state->animation_frame == 0) {
-                state->animation_frame = 1;
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-        case EVENT_LIGHT_LONG_PRESS:
-            state->animation_frame = 1;
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void simple_coin_flip_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/complication/simple_coin_flip_face.h b/movement/watch_faces/complication/simple_coin_flip_face.h
deleted file mode 100644 (file)
index 710acca..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Wesley Aptekar-Cassels
- *
- * 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 SIMPLE_COIN_FLIP_FACE_H_
-#define SIMPLE_COIN_FLIP_FACE_H_
-
-#include "movement.h"
-
-/*
- * A extremely simple coin flip face.
- *
- * Press ALARM or LIGHT to flip a coin, after a short animation it will display
- * "Heads" or "Tails". Long-press to flip again (you can change a #define to
- * allow a short-press to reflip as well, if you'd like).
- *
- * This is for people who want a simpler UI than probability_face or
- * randonaut_face. While those have more features, this one is more immediately
- * obvious - useful, for instance, if you are using a coin flip to agree on
- * something with someone, and want the operation to be clear to someone who
- * has not had anything explained to them.
- */
-
-typedef struct {
-    uint8_t animation_frame;
-} simple_coin_flip_state_t;
-
-void simple_coin_flip_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void simple_coin_flip_face_activate(void *context);
-bool simple_coin_flip_face_loop(movement_event_t event, void *context);
-void simple_coin_flip_face_resign(void *context);
-
-#define simple_coin_flip_face ((const watch_face_t){ \
-    simple_coin_flip_face_setup, \
-    simple_coin_flip_face_activate, \
-    simple_coin_flip_face_loop, \
-    simple_coin_flip_face_resign, \
-    NULL, \
-})
-
-#endif // SIMPLE_COIN_FLIP_FACE_H_
-
diff --git a/movement/watch_faces/complication/smallchess_face.c b/movement/watch_faces/complication/smallchess_face.c
deleted file mode 100644 (file)
index 6583872..0000000
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Jeremy O'Brien
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "smallchesslib.h"
-
-#include "smallchess_face.h"
-#include "watch.h"
-
-#define PIECE_LIST_END_MARKER 0xff
-
-int8_t cpu_done_beep[] = {BUZZER_NOTE_C5, 5, BUZZER_NOTE_C6, 5, BUZZER_NOTE_C7, 5, 0};
-
-static void smallchess_init_board(smallchess_face_state_t *state) {
-    SCL_gameInit((SCL_Game *)state->game, 0);
-    memset(state->moveable_pieces, 0xff, sizeof(state->moveable_pieces));
-    memset(state->moveable_dests, 0xff, sizeof(state->moveable_dests));
-}
-
-void smallchess_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(smallchess_face_state_t));
-        memset(*context_ptr, 0, sizeof(smallchess_face_state_t));
-
-        /* now alloc/init the game board */
-        smallchess_face_state_t *state = (smallchess_face_state_t *)*context_ptr;
-        state->game = malloc(sizeof(SCL_Game));
-        smallchess_init_board(*context_ptr);
-    }
-}
-
-void smallchess_face_activate(void *context) {
-    (void) context;
-}
-
-static void _smallchess_calc_moveable_pieces(smallchess_face_state_t *state) {
-    int moveable_pieces_idx = 0;
-    SCL_Game *game = (SCL_Game *)state->game;
-    for (int i = 0; i < SCL_BOARD_SQUARES; ++i) {
-        if (game->board[i] != '.' &&
-                SCL_pieceIsWhite(game->board[i]) == SCL_boardWhitesTurn(game->board)) {
-            SCL_SquareSet moveable_pieces = SCL_SQUARE_SET_EMPTY;
-            SCL_boardGetMoves(game->board, i, moveable_pieces);
-            if (SCL_squareSetSize(moveable_pieces) != 0) {
-                state->moveable_pieces[moveable_pieces_idx] = i;
-                moveable_pieces_idx++;
-            }
-        }
-    }
-    state->moveable_pieces[moveable_pieces_idx] = PIECE_LIST_END_MARKER;
-    state->moveable_pieces_idx = 0;
-}
-
-static void _smallchess_make_ai_move(smallchess_face_state_t *state) {
-    char ai_from_str[3] = {0};
-    char ai_to_str[3] = {0};
-    uint8_t rep_from, rep_to;
-    char ai_prom;
-
-    watch_clear_display();
-    watch_start_character_blink('C', 100);
-    SCL_gameGetRepetiotionMove(state->game, &rep_from, &rep_to);
-
-#ifndef __EMSCRIPTEN__
-    hri_oscctrl_write_OSC16MCTRL_FSEL_bf(OSCCTRL, OSCCTRL_OSC16MCTRL_FSEL_16_Val);
-#endif
-    SCL_getAIMove(state->game, 3, 0, 0, SCL_boardEvaluateStatic, NULL, 0, rep_from, rep_to, &state->ai_from_square, &state->ai_to_square, &ai_prom);
-#ifndef __EMSCRIPTEN__
-    hri_oscctrl_write_OSC16MCTRL_FSEL_bf(OSCCTRL, OSCCTRL_OSC16MCTRL_FSEL_4_Val);
-#endif
-
-    SCL_gameMakeMove(state->game, state->ai_from_square, state->ai_to_square, ai_prom);
-    watch_stop_blink();
-
-    watch_buzzer_play_sequence(cpu_done_beep, NULL);
-
-    /* cache the move as a string for SHOW_CPU_MOVE state */
-    SCL_squareToString(state->ai_from_square, ai_from_str);
-    SCL_squareToString(state->ai_to_square, ai_to_str);
-    snprintf(state->last_move_str, sizeof(state->last_move_str), " %s-%s", ai_from_str, ai_to_str);
-
-    /* now cache the list of legal pieces we can move */
-    _smallchess_calc_moveable_pieces(state);
-}
-
-static char _smallchess_make_lowercase(char c) {
-    if (c < 0x61)
-        return c + 0x20;
-
-    return c;
-}
-
-static void _smallchess_get_endgame_string(smallchess_face_state_t *state, char *buf, uint8_t len) {
-    uint8_t endgame_state = ((SCL_Game *)state->game)->state;
-    uint16_t ply = ((SCL_Game *)state->game)->ply;
-
-    switch (endgame_state) {
-        case SCL_GAME_STATE_WHITE_WIN:
-            snprintf(buf, len, "Wh%2dm&ate ", ply);
-            break;
-        case SCL_GAME_STATE_BLACK_WIN:
-            snprintf(buf, len, "bL%2dm&ate ", ply);
-            break;
-        case SCL_GAME_STATE_DRAW:
-        case SCL_GAME_STATE_DRAW_STALEMATE:
-        case SCL_GAME_STATE_DRAW_REPETITION:
-        case SCL_GAME_STATE_DRAW_50:
-        case SCL_GAME_STATE_DRAW_DEAD:
-            snprintf(buf, len, "  %2d Drauu", ply);
-            break;
-        default:
-            snprintf(buf, len, "  %2d Error", ply);
-            break;
-    }
-}
-
-static void _smallchess_face_update_lcd(smallchess_face_state_t *state) {
-    uint8_t start_square;
-    uint8_t end_square;
-    char start_coord[3] = {0};
-    char end_coord[3] = {0};
-    char buf[14] = {0};
-
-    uint16_t ply = ((SCL_Game *)state->game)->ply;
-
-    switch (state->state) {
-        case SMALLCHESS_MENU_RESUME:
-            snprintf(buf, sizeof(buf), "SC%2dResume", ply);
-            break;
-        case SMALLCHESS_MENU_UNDO:
-            snprintf(buf, sizeof(buf), "SC%2d Undo ", ply);
-            break;
-        case SMALLCHESS_MENU_SHOW_LAST_MOVE:
-            snprintf(buf, sizeof(buf), "SC%2dShLast", ply);
-            break;
-        case SMALLCHESS_MENU_NEW_WHITE:
-            snprintf(buf, sizeof(buf), "Wh%2dStart ", ply);
-            break;
-        case SMALLCHESS_MENU_NEW_BLACK:
-            snprintf(buf, sizeof(buf), "bL%2dStart ", ply);
-            break;
-        case SMALLCHESS_SHOW_CPU_MOVE:
-        case SMALLCHESS_SHOW_LAST_MOVE:
-            snprintf(buf,
-                    sizeof(buf),
-                    "%c %2d%s",
-                    _smallchess_make_lowercase(((SCL_Game *)state->game)->board[state->ai_to_square]),
-                    ply,
-                    state->last_move_str);
-
-            break;
-        case SMALLCHESS_SELECT_PIECE:
-            if (((SCL_Game *)state->game)->state != SCL_GAME_STATE_PLAYING) {
-                _smallchess_get_endgame_string(state, buf, sizeof(buf));
-                break;
-            }
-            start_square = state->moveable_pieces[state->moveable_pieces_idx];
-            SCL_squareToString(start_square, start_coord);
-            snprintf(buf,
-                    sizeof(buf),
-                    "%c %2d %s-  ",
-                    _smallchess_make_lowercase(((SCL_Game *)state->game)->board[start_square]),
-                    ply + 1,
-                    start_coord);
-            break;
-        case SMALLCHESS_SELECT_DEST:
-            start_square = state->moveable_pieces[state->moveable_pieces_idx];
-            SCL_squareToString(start_square, start_coord);
-            end_square = state->moveable_dests[state->moveable_dests_idx];
-            SCL_squareToString(end_square, end_coord);
-            snprintf(buf,
-                    sizeof(buf),
-                    "%c %2d %s-%s",
-                    _smallchess_make_lowercase(((SCL_Game *)state->game)->board[start_square]),
-                    ply + 1,
-                    start_coord,
-                    end_coord);
-            break;
-        default:
-            break;
-    }
-
-    watch_display_string(buf, 0);
-}
-
-static void _smallchess_select_main_menu_subitem(smallchess_face_state_t *state) {
-    char from_str[3] = {0};
-    char to_str[3] = {0};
-    char prom;
-
-    switch (state->state) {
-        case SMALLCHESS_MENU_RESUME:
-            state->state = SMALLCHESS_SELECT_PIECE;
-            break;
-        case SMALLCHESS_MENU_UNDO:
-            /* undo twice to undo the CPU's move and our move */
-            SCL_gameUndoMove((SCL_Game *)state->game);
-            SCL_gameUndoMove((SCL_Game *)state->game);
-            /* and re-calculate the moveable pieces for this new state */
-            _smallchess_calc_moveable_pieces(state);
-            break;
-        case SMALLCHESS_MENU_NEW_WHITE:
-            SCL_gameInit((SCL_Game *)state->game, 0);
-            _smallchess_calc_moveable_pieces(state);
-            state->state = SMALLCHESS_SELECT_PIECE;
-            break;
-        case SMALLCHESS_MENU_NEW_BLACK:
-            SCL_gameInit((SCL_Game *)state->game, 0);
-            /* force a move since black is playing */
-            _smallchess_make_ai_move(state);
-            state->state = SMALLCHESS_SHOW_CPU_MOVE;
-            break;
-        case SMALLCHESS_MENU_SHOW_LAST_MOVE:
-            /* fetch the move */
-            SCL_recordGetMove(((SCL_Game *)state->game)->record, ((SCL_Game *)state->game)->ply - 1, &state->ai_from_square, &state->ai_to_square, &prom);
-            SCL_squareToString(state->ai_from_square, from_str);
-            SCL_squareToString(state->ai_to_square, to_str);
-            snprintf(state->last_move_str, sizeof(state->last_move_str), " %s-%s", from_str, to_str);
-            state->state = SMALLCHESS_SHOW_LAST_MOVE;
-            break;
-        default:
-            break;
-    }
-}
-
-static void _smallchess_handle_select_piece_button_event(smallchess_face_state_t *state, movement_event_t event) {
-    SCL_SquareSet moveable_dests = SCL_SQUARE_SET_EMPTY;
-
-    /* back to main menu on any event when game ends */
-    if (((SCL_Game *)state->game)->state != SCL_GAME_STATE_PLAYING) {
-        state->state = SMALLCHESS_MENU_RESUME;
-        return;
-    }
-
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_UP:
-            // check for no moves possible state (shouldn't happen but this will prevent weirdness)
-            if (state->moveable_pieces[0] == PIECE_LIST_END_MARKER) {
-                return;
-            }
-
-            state->moveable_pieces_idx += 1;
-            if (state->moveable_pieces_idx >= NUM_ELEMENTS(state->moveable_pieces)) {
-                state->moveable_pieces_idx = 0;
-            }
-
-            if (state->moveable_pieces[state->moveable_pieces_idx] == PIECE_LIST_END_MARKER) {
-                state->moveable_pieces_idx = 0;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            // check for no moves possible state (shouldn't happen but this will prevent weirdness)
-            if (state->moveable_pieces[0] == PIECE_LIST_END_MARKER) {
-                return;
-            }
-
-            /* handle wrap around */
-            if (state->moveable_pieces_idx == 0) {
-                for (unsigned int i = 0; i < NUM_ELEMENTS(state->moveable_pieces); i++) {
-                    if (state->moveable_pieces[i] == 0xff) {
-                        state->moveable_pieces_idx = i - 1;
-                        break;
-                    }
-                }
-            } else {
-                state->moveable_pieces_idx -= 1;
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (((SCL_Game *)state->game)->ply == 0) {
-                state->state = SMALLCHESS_MENU_NEW_WHITE;
-            } else {
-                state->state = SMALLCHESS_MENU_RESUME;
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            /* pre-calculate the possible moves this piece can make */
-            SCL_boardGetMoves(((SCL_Game *)state->game)->board, state->moveable_pieces[state->moveable_pieces_idx], moveable_dests);
-            state->moveable_dests_idx = 0;
-            SCL_SQUARE_SET_ITERATE_BEGIN(moveable_dests)
-                state->moveable_dests[state->moveable_dests_idx] = iteratedSquare;
-                state->moveable_dests_idx++;
-            SCL_SQUARE_SET_ITERATE_END
-            state->moveable_dests[state->moveable_dests_idx] = PIECE_LIST_END_MARKER;
-            state->moveable_dests_idx = 0;
-            state->state = SMALLCHESS_SELECT_DEST;
-        default:
-            break;
-    }
-}
-
-static void _smallchess_handle_select_dest_button_event(smallchess_face_state_t *state, movement_event_t event) {
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_UP:
-            // check for no moves possible state (shouldn't happen but this will prevent weirdness)
-            if (state->moveable_dests[0] == PIECE_LIST_END_MARKER) {
-                return;
-            }
-            state->moveable_dests_idx += 1;
-            if (state->moveable_dests_idx >= (sizeof(state->moveable_dests) / sizeof(state->moveable_dests[0]))) {
-                state->moveable_dests_idx = 0;
-            }
-
-            if (state->moveable_dests[state->moveable_dests_idx] == PIECE_LIST_END_MARKER) {
-                state->moveable_dests_idx = 0;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            // check for no moves possible state (shouldn't happen but this will prevent weirdness)
-            if (state->moveable_dests[0] == PIECE_LIST_END_MARKER) {
-                return;
-            }
-
-            /* handle wrap around */
-            if (state->moveable_dests_idx == 0) {
-                for (unsigned int i = 0; i < NUM_ELEMENTS(state->moveable_dests); i++) {
-                    if (state->moveable_dests[i] == 0xff) {
-                        state->moveable_dests_idx = i - 1;
-                        break;
-                    }
-                }
-            } else {
-                state->moveable_dests_idx -= 1;
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            state->state = SMALLCHESS_SELECT_PIECE;
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            SCL_gameMakeMove((SCL_Game *)state->game, state->moveable_pieces[state->moveable_pieces_idx], state->moveable_dests[state->moveable_dests_idx], 'q');
-
-            /* if the player didn't win or draw here, calculate a move */
-            if (((SCL_Game *)state->game)->state == SCL_GAME_STATE_PLAYING) {
-                _smallchess_make_ai_move(state);
-                state->state = SMALLCHESS_SHOW_CPU_MOVE;
-            } else {
-                /* player ended the game through mate or draw; jump to select piece screen to show state */
-                state->state = SMALLCHESS_SELECT_PIECE;
-            }
-            break;
-        default:
-            break;
-    }
-}
-
-/* this just waits until any button is hit */
-static void _smallchess_handle_show_cpu_move_button_event(smallchess_face_state_t *state, movement_event_t event) {
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_UP:
-        case EVENT_LIGHT_BUTTON_UP:
-        case EVENT_ALARM_LONG_PRESS:
-        case EVENT_LIGHT_LONG_PRESS:
-            state->state = SMALLCHESS_SELECT_PIECE;
-            break;
-        default:
-            break;
-    }
-}
-
-static void _smallchess_handle_show_last_move_button_event(smallchess_face_state_t *state, movement_event_t event) {
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_UP:
-        case EVENT_LIGHT_BUTTON_UP:
-        case EVENT_ALARM_LONG_PRESS:
-        case EVENT_LIGHT_LONG_PRESS:
-            state->state = SMALLCHESS_MENU_SHOW_LAST_MOVE;
-            break;
-        default:
-            break;
-    }
-}
-
-static void _smallchess_handle_playing_button_event(smallchess_face_state_t *state, movement_event_t event) {
-    if (state->state == SMALLCHESS_SELECT_PIECE) {
-        _smallchess_handle_select_piece_button_event(state, event);
-    } else if (state->state == SMALLCHESS_SELECT_DEST) {
-        _smallchess_handle_select_dest_button_event(state, event);
-    } else if (state->state == SMALLCHESS_SHOW_CPU_MOVE) {
-        _smallchess_handle_show_cpu_move_button_event(state, event);
-    } else if (state->state == SMALLCHESS_SHOW_LAST_MOVE) {
-        _smallchess_handle_show_last_move_button_event(state, event);
-    }
-}
-
-static void _smallchess_handle_main_menu_button_event(smallchess_face_state_t *state, movement_event_t event) {
-    uint16_t ply = ((SCL_Game *)state->game)->ply;
-
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_UP:
-            /* no game started; only offer start white/start black */
-            if (ply == 0) {
-                if (state->state == SMALLCHESS_MENU_NEW_WHITE) {
-                    state->state = SMALLCHESS_MENU_NEW_BLACK;
-                } else {
-                    state->state = SMALLCHESS_MENU_NEW_WHITE;
-                }
-            } else {
-                state->state++;
-                if (state->state >= SMALLCHESS_PLAYING_SPLIT) {
-                    state->state = SMALLCHESS_MENU_RESUME;
-                }
-            }
-
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            /* no game started; only offer start white/start black */
-            if (ply == 0) {
-                if (state->state == SMALLCHESS_MENU_NEW_BLACK) {
-                    state->state = SMALLCHESS_MENU_NEW_WHITE;
-                } else {
-                    state->state = SMALLCHESS_MENU_NEW_BLACK;
-                }
-            } else {
-                if (state->state == SMALLCHESS_MENU_RESUME) {
-                    state->state = SMALLCHESS_PLAYING_SPLIT - 1;
-                } else {
-                    state->state--;
-                }
-            }
-
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            _smallchess_select_main_menu_subitem(state);
-            break;
-        default:
-            break;
-    }
-}
-
-static void _smallchess_handle_button_event(smallchess_face_state_t *state, movement_event_t event) {
-    if (state->state < SMALLCHESS_PLAYING_SPLIT) {
-        /* in main menu */
-        _smallchess_handle_main_menu_button_event(state, event);
-    } else if (state->state > SMALLCHESS_PLAYING_SPLIT) {
-        /* in piece selection */
-        _smallchess_handle_playing_button_event(state, event);
-    }
-}
-
-bool smallchess_face_loop(movement_event_t event, void *context) {
-    smallchess_face_state_t *state = (smallchess_face_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            if (((SCL_Game *)state->game)->ply == 0) {
-                state->state = SMALLCHESS_MENU_NEW_WHITE;
-            } else {
-                state->state = SMALLCHESS_MENU_RESUME;
-            }
-            _smallchess_face_update_lcd(state);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-        case EVENT_LIGHT_LONG_PRESS:
-        case EVENT_ALARM_BUTTON_UP:
-        case EVENT_ALARM_LONG_PRESS:
-            _smallchess_handle_button_event(state, event);
-            _smallchess_face_update_lcd(state);
-            break;
-        case EVENT_TICK:
-            break;
-        case EVENT_TIMEOUT:
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void smallchess_face_resign(void *context) {
-    (void) context;
-    watch_set_led_off();
-}
diff --git a/movement/watch_faces/complication/smallchess_face.h b/movement/watch_faces/complication/smallchess_face.h
deleted file mode 100644 (file)
index 97d8179..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Jeremy O'Brien
- *
- * 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 SMALLCHESS_FACE_H_
-#define SMALLCHESS_FACE_H_
-
-#include "movement.h"
-
-/*
- * Chess watchface
- *
- * Implements a (very) simple chess engine.
- * Uses smallchesslib for the engine: https://codeberg.org/drummyfish/smallchesslib
- *
- * When moving a piece, only valid pieces and moves are presented.
- *
- * Interaction is done through a simple menu/submenu system:
- * - Light button: navigate backwards through the current menu
- * - Alarm button: navigate forwards through the current menu
- * - Light button (long press): navigate up to the parent menu
- * - Alarm button (long press): select the current item or submenu
- */
-
-enum smallchess_state {
-    /* main menu */
-    SMALLCHESS_MENU_RESUME,
-    SMALLCHESS_MENU_SHOW_LAST_MOVE,
-    SMALLCHESS_MENU_UNDO,
-    SMALLCHESS_MENU_NEW_WHITE,
-    SMALLCHESS_MENU_NEW_BLACK,
-
-    SMALLCHESS_PLAYING_SPLIT,
-
-    /* playing game submenu */
-    SMALLCHESS_SHOW_LAST_MOVE,
-    SMALLCHESS_SHOW_CPU_MOVE,
-    SMALLCHESS_SELECT_PIECE,
-    SMALLCHESS_SELECT_DEST,
-};
-
-#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]))
-#define SMALLCHESS_NUM_PIECES 16 // number of pieces each player has
-
-typedef struct {
-    void *game;
-    enum smallchess_state state;
-    uint8_t moveable_pieces[SMALLCHESS_NUM_PIECES + 1];
-    uint8_t moveable_pieces_idx;
-    uint8_t moveable_dests[29]; // this magic number represents the maximum number of moves a piece can make (queen in center of board)
-                                // plus one for the end list marker
-    uint8_t moveable_dests_idx;
-    char last_move_str[7];
-    uint8_t ai_from_square, ai_to_square;
-} smallchess_face_state_t;
-
-void smallchess_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void smallchess_face_activate(void *context);
-bool smallchess_face_loop(movement_event_t event, void *context);
-void smallchess_face_resign(void *context);
-
-#define smallchess_face ((const watch_face_t){ \
-    smallchess_face_setup, \
-    smallchess_face_activate, \
-    smallchess_face_loop, \
-    smallchess_face_resign, \
-    NULL, \
-})
-
-#endif // SMALLCHESS_FACE_H_
diff --git a/movement/watch_faces/complication/solstice_face.c b/movement/watch_faces/complication/solstice_face.c
deleted file mode 100644 (file)
index d981c4f..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Wesley Aptekar-Cassels
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "watch_utility.h"
-#include "solstice_face.h"
-
-// Find solstice or equinox time in JDE for a given year, via method from Meeus Ch 27
-static double calculate_solstice_equinox(uint16_t year, uint8_t k) {
-    double Y = ((double)year - 2000) / 1000;
-    double approx_terms[4][5] = {
-        {2451623.80984, 365242.37404, 0.05169, -0.00411, -0.00057}, // March equinox
-        {2451716.56767, 365241.62603, 0.00325, 0.00888, -0.00030}, // June solstice
-        {2451810.21715, 365242.01767, -0.11575, 0.00337, 0.00078}, // September equinox
-        {2451900.05952, 365242.74049, -0.06223, -0.00823, 0.00032}, // December solstice
-    };
-    double JDE0 = approx_terms[k][0] + Y * (approx_terms[k][1] + Y * (approx_terms[k][2] + Y * (approx_terms[k][3] + Y * approx_terms[k][4])));
-    double T = (JDE0 - 2451545.0) / 36525;
-    double W = 35999.373 * T - 2.47;
-    double dlambda = 1 + (0.0334 * cos(W * M_PI / 180.0)) + (0.0007 * cos(2 * W * M_PI / 180.0));
-    double correction_terms[25][3] = {
-        {485,324.96,1934.136},
-        {203,337.23,32964.467},
-        {199,342.08,20.186},
-        {182,27.85,445267.112},
-        {156,73.14,45036.886},
-        {136,171.52,22518.443},
-        {77,222.54,65928.934},
-        {74,296.72,3034.906},
-        {70,243.58,9037.513},
-        {58,119.81,33718.147},
-        {52,297.17,150.678},
-        {50,21.02,2281.226},
-        {45,247.54,29929.562},
-        {44,325.15,31555.956},
-        {29,60.93,4443.417},
-        {18,155.12,67555.328},
-        {17,288.79,4562.452},
-        {16,198.04,62894.029},
-        {14,199.76,31436.921},
-        {12,95.39,14577.848},
-        {12,287.11,31931.756},
-        {12,320.81,34777.259},
-        {9,227.73,1222.114},
-        {8,15.45,16859.074},
-    };
-    double S = 0;
-    for (int i = 0; i < 25; i++) {
-        S += correction_terms[i][0] * cos((correction_terms[i][1] + correction_terms[i][2] * T) * M_PI / 180.0);
-    }
-    double JDE = JDE0 + (0.00001 * S) / dlambda;
-
-    return JDE;
-}
-
-// Convert JDE to Gergorian datetime as per Meeus Ch 7
-static watch_date_time_t jde_to_date_time(double JDE) {
-    double tmp = JDE + 0.5;
-    double Z = floor(tmp);
-    double F = fmod(tmp, 1);
-    double A;
-    if (Z < 2299161) {
-        A = Z;
-    } else {
-        double alpha = floor((Z - 1867216.25) / 36524.25);
-        A = Z + 1 + alpha - floor(alpha / 4);
-    }
-    double B = A + 1524;
-    double C = floor((B - 122.1) / 365.25);
-    double D = floor(365.25 * C);
-    double E = floor((B - D) / 30.6001);
-    double day = B - D - floor(30.6001 * E) + F;
-    double month;
-    if (E < 14) {
-        month = E - 1;
-    } else {
-        month = E - 13;
-    }
-    double year;
-    if (month > 2) {
-        year = C - 4716;
-    } else {
-        year = C - 4715;
-    }
-
-    double hours = fmod(day, 1) * 24;
-    double minutes = fmod(hours, 1) * 60;
-    double seconds = fmod(minutes, 1) * 60;
-
-    watch_date_time_t result = {.unit = {
-        floor(seconds),
-        floor(minutes),
-        floor(hours),
-        floor(day),
-        floor(month),
-        floor(year - 2020)
-    }};
-
-    return result;
-}
-
-static void calculate_datetimes(solstice_state_t *state) {
-    for (int i = 0; i < 4; i++) {
-        // TODO: handle DST changes
-        state->datetimes[i] = jde_to_date_time(calculate_solstice_equinox(2020 + state->year, i) + (movement_get_current_timezone_offset() / (3600.0*24.0)));
-    }
-}
-
-void solstice_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(solstice_state_t));
-        solstice_state_t *state = (solstice_state_t *)*context_ptr;
-
-        watch_date_time_t now = watch_rtc_get_date_time();
-        state->year = now.unit.year;
-        state->index = 0;
-        calculate_datetimes(state, settings);
-
-        uint32_t now_unix = watch_utility_date_time_to_unix_time(now, 0);
-        for (int i = 0; i < 4; i++) {
-            if (state->index == 0 && watch_utility_date_time_to_unix_time(state->datetimes[i], 0) > now_unix) {
-                state->index = i;
-            }
-        }
-    }
-}
-
-void solstice_face_activate(void *context) {
-    (void) context;
-}
-
-static void show_main_screen(solstice_state_t *state) {
-    char buf[11];
-    watch_date_time_t date_time = state->datetimes[state->index];
-    sprintf(buf, "  %2d  %2d%02d", date_time.unit.year + 20, date_time.unit.month, date_time.unit.day);
-    watch_display_string(buf, 0);
-}
-
-static void show_date_time(solstice_state_t *state) {
-    char buf[11];
-    watch_date_time_t date_time = state->datetimes[state->index];
-    if (!movement_clock_mode_24h()) {
-        if (date_time.unit.hour < 12) {
-            watch_clear_indicator(WATCH_INDICATOR_PM);
-        } else {
-            watch_set_indicator(WATCH_INDICATOR_PM);
-        }
-        date_time.unit.hour %= 12;
-        if (date_time.unit.hour == 0) date_time.unit.hour = 12;
-    }
-    sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
-    watch_set_colon();
-    watch_display_string(buf, 0);
-}
-
-bool solstice_face_loop(movement_event_t event, void *context) {
-    solstice_state_t *state = (solstice_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ALARM_LONG_PRESS:
-            show_date_time(state);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (state->index == 0) {
-                if (state->year == 0) {
-                    break;
-                }
-                state->year--;
-                state->index = 3;
-                calculate_datetimes(state, settings);
-            } else {
-                state->index--;
-            }
-            show_main_screen(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            state->index++;
-            if (state->index > 3) {
-                if (state->year == 83) {
-                    break;
-                }
-                state->year++;
-                state->index = 0;
-                calculate_datetimes(state, settings);
-            }
-            show_main_screen(state);
-            break;
-        case EVENT_ALARM_LONG_UP:
-            watch_clear_colon();
-            watch_clear_indicator(WATCH_INDICATOR_PM);
-            show_main_screen(state);
-            break;
-        case EVENT_ACTIVATE:
-            show_main_screen(state);
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void solstice_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/complication/solstice_face.h b/movement/watch_faces/complication/solstice_face.h
deleted file mode 100644 (file)
index 57dcb4e..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Wesley Aptekar-Cassels
- *
- * 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 SOLSTICE_FACE_H_
-#define SOLSTICE_FACE_H_
-
-#include "movement.h"
-
-/*
- * A face for telling the dates and times of solstices and equinoxes
- *
- * It shows the upcoming solstice or equinox by default. The upper right number
- * is the year, and the bottom numbers are the date in MMDD format. Use the
- * alarm / light buttons to go forwards / backwards in time. Long press the
- * alarm button to show the time of the event, including what weekday it is on,
- * in your local timezone (DST is not handled).
- *
- * Supports the years 2020 - 2083. The calculations are reasonably accurate for
- * years between 1000 and 3000, but limitations in the sensor watch libraries
- * (which could easily be worked around) prevent making use of that.
- */
-
-typedef struct {
-    watch_date_time_t datetimes[4];
-    uint8_t year;
-    uint8_t index;
-} solstice_state_t;
-
-void solstice_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void solstice_face_activate(void *context);
-bool solstice_face_loop(movement_event_t event, void *context);
-void solstice_face_resign(void *context);
-
-#define solstice_face ((const watch_face_t){ \
-    solstice_face_setup, \
-    solstice_face_activate, \
-    solstice_face_loop, \
-    solstice_face_resign, \
-    NULL, \
-})
-
-#endif // SOLSTICE_FACE_H_
-
diff --git a/movement/watch_faces/complication/stopwatch_face.c b/movement/watch_faces/complication/stopwatch_face.c
deleted file mode 100644 (file)
index c2ef899..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Wesley Ellis
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "stopwatch_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-// distant future for background task: January 1, 2083
-// see stopwatch_face_activate for details
-static const watch_date_time_t distant_future = {
-    .unit = {0, 0, 0, 1, 1, 63}
-};
-
-void stopwatch_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(stopwatch_state_t));
-        memset(*context_ptr, 0, sizeof(stopwatch_state_t));
-    }
-}
-
-static void _stopwatch_face_update_display(stopwatch_state_t *stopwatch_state, bool show_seconds) {
-    if (stopwatch_state->running) {
-        watch_date_time_t now = watch_rtc_get_date_time();
-        uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
-        uint32_t start_timestamp = watch_utility_date_time_to_unix_time(stopwatch_state->start_time, 0);
-        stopwatch_state->seconds_counted = now_timestamp - start_timestamp;
-    }
-
-    if (stopwatch_state->seconds_counted >= 3456000) {
-        // display maxes out just shy of 40 days, thanks to the limit on the day digits (0-39)
-        stopwatch_state->running = false;
-        movement_cancel_background_task();
-        watch_display_string("st39235959", 0);
-        return;
-    }
-
-    watch_duration_t duration = watch_utility_seconds_to_duration(stopwatch_state->seconds_counted);
-    char buf[14];
-
-    sprintf(buf, "st  %02d%02d  ", duration.hours, duration.minutes);
-    watch_display_string(buf, 0);
-
-    if (duration.days != 0) {
-        sprintf(buf, "%2d", (uint8_t)duration.days);
-        watch_display_string(buf, 2);
-    }
-
-    if (show_seconds) {
-        sprintf(buf, "%02d", duration.seconds);
-        watch_display_string(buf, 8);
-    }
-}
-
-void stopwatch_face_activate(void *context) {
-    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
-
-    stopwatch_state_t *stopwatch_state = (stopwatch_state_t *)context;
-    if (stopwatch_state->running) {
-        // because the low power update happens on the minute mark, and the wearer could start
-        // the stopwatch anytime, the low power update could fire up to 59 seconds later than
-        // we need it to, causing the stopwatch to display stale data.
-        // So let's schedule a background task that will never fire. This will keep the watch
-        // from entering low energy mode while the stopwatch is on screen. This background task
-        // will remain scheduled until the stopwatch stops OR this watch face resigns.
-        movement_schedule_background_task(distant_future);
-    }
-}
-
-bool stopwatch_face_loop(movement_event_t event, void *context) {
-    stopwatch_state_t *stopwatch_state = (stopwatch_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_set_colon();
-            // fall through
-        case EVENT_TICK:
-            if (stopwatch_state->start_time.reg == 0) {
-                watch_display_string("st  000000", 0);
-            } else {
-                _stopwatch_face_update_display(stopwatch_state, true);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            movement_illuminate_led();
-            if (!stopwatch_state->running) {
-                stopwatch_state->start_time.reg = 0;
-                stopwatch_state->seconds_counted = 0;
-                watch_display_string("st  000000", 0);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-            if (movement_button_should_sound()) {
-                watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-            }
-            stopwatch_state->running = !stopwatch_state->running;
-            if (stopwatch_state->running) {
-                // we're running now, so we need to set the start_time.
-                if (stopwatch_state->start_time.reg == 0) {
-                    // if starting from the reset state, easy: we start now.
-                    stopwatch_state->start_time = watch_rtc_get_date_time();
-                } else {
-                    // if resuming with time already on the clock, the original start time isn't valid anymore!
-                    // so let's fetch the current time...
-                    uint32_t timestamp = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), 0);
-                    // ...subtract the seconds we've already counted...
-                    timestamp -= stopwatch_state->seconds_counted;
-                    // and resume from the "virtual" start time that's that many seconds ago.
-                    stopwatch_state->start_time = watch_utility_date_time_from_unix_time(timestamp, 0);
-                }
-                // schedule our keepalive task when running...
-                movement_schedule_background_task(distant_future);
-            } else {
-                // and cancel it when stopped.
-                movement_cancel_background_task();
-            }
-            break;
-        case EVENT_TIMEOUT:
-            // explicitly ignore the timeout event so we stay on screen
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(1000);
-            if (!stopwatch_state->running) {
-                // since the tick animation is running, displaying the stopped time could be misleading,
-                // as it could imply that the stopwatch is running. instead, show a blank display to
-                // indicate that we are in sleep mode.
-                watch_display_string("st  ----  ", 0);
-            } else {
-                // this OTOH shouldn't happen anymore; if we're running, we shouldn't enter low energy mode
-                _stopwatch_face_update_display(stopwatch_state, false);
-                watch_set_indicator(WATCH_INDICATOR_BELL);
-            }
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void stopwatch_face_resign(void *context) {
-    (void) context;
-
-    // regardless of whether we're running or stopped, cancel the task
-    // that was keeping us awake while on screen.
-    movement_cancel_background_task();
-}
diff --git a/movement/watch_faces/complication/stopwatch_face.h b/movement/watch_faces/complication/stopwatch_face.h
deleted file mode 100644 (file)
index 5f9d66b..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Wesley Ellis
- * 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 STOPWATCH_FACE_H_
-#define STOPWATCH_FACE_H_
-
-/*
- * STOPWATCH FACE
- *
- * The Stopwatch face provides basic stopwatch functionality: you can start
- * and stop the stopwatch with the alarm button. Pressing the light button
- * when the timer is stopped resets it.
- *
- * This face does not count sub-seconds.
- * See also: "fast_stopwatch_face.h"
- */
-
-#include "movement.h"
-
-typedef struct {
-    bool running;
-    watch_date_time_t start_time; // while running, show the difference between this time and now
-    uint32_t seconds_counted;   // set this value when paused, and show that instead.
-} stopwatch_state_t;
-
-void stopwatch_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void stopwatch_face_activate(void *context);
-bool stopwatch_face_loop(movement_event_t event, void *context);
-void stopwatch_face_resign(void *context);
-
-#define stopwatch_face ((const watch_face_t){ \
-    stopwatch_face_setup, \
-    stopwatch_face_activate, \
-    stopwatch_face_loop, \
-    stopwatch_face_resign, \
-    NULL, \
-})
-
-#endif // STOPWATCH_FACE_H_
diff --git a/movement/watch_faces/complication/tachymeter_face.c b/movement/watch_faces/complication/tachymeter_face.c
deleted file mode 100644 (file)
index ddaafc5..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Raymundo Cassani
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "tachymeter_face.h"
-#include "watch_utility.h"
-
-static uint32_t _distance_from_struct(distance_digits_t dist_digits) {
-    // distance from digitwise distance
-    uint32_t retval = (dist_digits.thousands * 100000 +
-                       dist_digits.hundreds  *  10000 +
-                       dist_digits.tens      *   1000 +
-                       dist_digits.ones      *    100 +
-                       dist_digits.tenths    *     10 +
-                       dist_digits.hundredths);// * 1
-    return retval;
-}
-
-void tachymeter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void)watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(tachymeter_state_t));
-        memset(*context_ptr, 0, sizeof(tachymeter_state_t));
-        tachymeter_state_t *state = (tachymeter_state_t *)*context_ptr;
-        // Default distance
-        state->dist_digits.ones = 1;
-        state->distance = _distance_from_struct(state->dist_digits);
-    }
-}
-
-void tachymeter_face_activate(void *context) {
-    (void)context;
-    movement_request_tick_frequency(4); // 4Hz
-}
-
-static void _tachymeter_face_distance_lcd(movement_event_t event, tachymeter_state_t *state){
-    char buf[11];
-    // Distance from digits
-    state->distance = _distance_from_struct(state->dist_digits);
-    sprintf(buf, "TC %c%06lu", state->running ? ' ' : 'd',  state->distance);
-    // Blinking display when editing
-    if (state->editing) {
-        // Blink 'd'
-        if (event.subsecond < 2) {
-            buf[3] = ' ';
-        }
-        // Blink active digit
-        if (event.subsecond % 2) {
-            buf[state->active_digit + 4] = ' ';
-        }
-    }
-    watch_display_string(buf, 0);
-}
-
-static void _tachymeter_face_totals_lcd(tachymeter_state_t *state, bool show_time){
-    char buf[15];
-    if (!show_time){
-        sprintf(buf, "TC %c%6lu", 'h',  state->total_speed);
-    } else {
-        sprintf(buf, "TC %c%6lu", 't',  state->total_time);
-    }
-    watch_display_string(buf, 0);
-    if (!show_time){
-        // Show '/' besides 'H'
-        watch_set_pixel(0, 9);
-        watch_set_pixel(0, 10);
-    }
-}
-
-bool tachymeter_face_loop(movement_event_t event, void *context) {
-    tachymeter_state_t *state = (tachymeter_state_t *)context;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Show distance in UI
-            if (state->total_time == 0) {
-                _tachymeter_face_distance_lcd(event, state);
-            }
-            break;
-        case EVENT_TICK:
-            // Show editing distance (blinking)
-            if (state->editing) {
-                _tachymeter_face_distance_lcd(event, state);
-            }
-            if (!state->running && state->total_time != 0) {
-            // Display results if finished and not cleared
-                if (event.subsecond < 2) {
-                     _tachymeter_face_totals_lcd(state, true);
-                } else {
-                     _tachymeter_face_totals_lcd(state, false);
-                }
-            } else if (state->running){
-                watch_display_string("  ", 2);
-                switch (state->animation_state) {
-                    case 0:
-                        watch_set_pixel(0, 7);
-                        break;
-                    case 1:
-                        watch_set_pixel(1, 7);
-                        break;
-                    case 2:
-                        watch_set_pixel(2, 7);
-                        break;
-                    case 3:
-                        watch_set_pixel(2, 6);
-                        break;
-                    case 4:
-                        watch_set_pixel(2, 8);
-                        break;
-                    case 5:
-                        watch_set_pixel(0, 8);
-                        break;
-                }
-                state->animation_state = (state->animation_state + 1) % 6;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (state->editing){
-                // Go to next digit
-                state->active_digit = (state->active_digit + 1) % 6;
-            } else {
-                movement_illuminate_led();
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (!state->running && !state->editing){
-                if (state->total_time != 0){
-                    // Clear results
-                    state->total_time = 0;
-                    state->total_speed = 0;
-                } else {
-                    // Default distance
-                    state->dist_digits.thousands  = 0;
-                    state->dist_digits.hundreds   = 0;
-                    state->dist_digits.tens       = 0;
-                    state->dist_digits.ones       = 1;
-                    state->dist_digits.tenths     = 0;
-                    state->dist_digits.hundredths = 0;
-                    state->distance = _distance_from_struct(state->dist_digits);
-                }
-            _tachymeter_face_distance_lcd(event, state);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if (!state->running && state->total_time == 0){
-                if (movement_button_should_sound() && !state->editing) {
-                    watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-                }
-                if (!state->editing) {
-                    // Start running
-                    state->running = true;
-                    state->start_seconds = watch_rtc_get_date_time();
-                    state->start_subsecond = event.subsecond;
-                    state->total_time = 0;
-                } else {
-                    // Alarm button to increase active digit
-                    switch (state->active_digit) {
-                        case 0:
-                            state->dist_digits.thousands = (state->dist_digits.thousands + 1) % 10;
-                            break;
-                        case 1:
-                            state->dist_digits.hundreds = (state->dist_digits.hundreds + 1) % 10;
-                            break;
-                        case 2:
-                            state->dist_digits.tens = (state->dist_digits.tens + 1) % 10;
-                            break;
-                        case 3:
-                            state->dist_digits.ones = (state->dist_digits.ones + 1) % 10;
-                            break;
-                        case 4:
-                            state->dist_digits.tenths = (state->dist_digits.tenths + 1) % 10;
-                            break;
-                        case 5:
-                            state->dist_digits.hundredths = (state->dist_digits.hundredths + 1) % 10;
-                            break;
-                    }
-                }
-            } else if (state->running) {
-                if (movement_button_should_sound() && !state->editing) {
-                    watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
-                }
-                // Stop running
-                state->running = false;
-                watch_date_time_t now = watch_rtc_get_date_time();
-                uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
-                uint32_t start_timestamp = watch_utility_date_time_to_unix_time(state->start_seconds, 0);
-                // Total time in centiseconds
-                state->total_time = ((now_timestamp*100) + (event.subsecond*25)) - ((start_timestamp*100) + (state->start_subsecond*25));
-                // Total speed in distance units per hour
-                state->total_speed = (uint32_t)(3600 * 100 * state->distance / state->total_time);
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (!state->running && state->total_time == 0){
-                if (!state->editing) {
-                    // Enter editing
-                    state->editing = true;
-                    state->active_digit = 0;
-                    if (movement_button_should_sound()) {
-                        watch_buzzer_play_note(BUZZER_NOTE_C7, 80);
-                        watch_buzzer_play_note(BUZZER_NOTE_C8, 80);
-                    }
-                } else {
-                    // Exit editing
-                    state->editing = false;
-                    // Validate distance
-                    if(_distance_from_struct(state->dist_digits) == 0){
-                        state->dist_digits.ones = 1;
-                    }
-                    _tachymeter_face_distance_lcd(event, state);
-                    if (movement_button_should_sound()) {
-                        watch_buzzer_play_note(BUZZER_NOTE_C8, 80);
-                        watch_buzzer_play_note(BUZZER_NOTE_C7, 80);
-                    }
-                }
-            }
-            break;
-        case EVENT_TIMEOUT:
-            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
-            // you may uncomment this line to move back to the first watch face in the list:
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
-            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
-            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
-            // watch_start_sleep_animation(500);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
-    // you should return false since the PWM driver does not operate in standby mode.
-    return true;
-}
-
-void tachymeter_face_resign(void *context) {
-    (void)context;
-    // handle any cleanup before your watch face goes off-screen.
-}
diff --git a/movement/watch_faces/complication/tachymeter_face.h b/movement/watch_faces/complication/tachymeter_face.h
deleted file mode 100644 (file)
index d299cb8..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Raymundo Cassani
- *
- * 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 TACHYMETER_FACE_H_
-#define TACHYMETER_FACE_H_
-
-/*
- * TACHYMETER face
- *
- * The Tachymeter complication emulates the tachymeter function often
- * present in watches, that computes the average speed in [units per hour]
- * for a given distance given in [units].
- *
- * Use case:
- *     User sets the distance
- *     User starts the tachymeter when the trip begins
- *     User stops the tachymeter when the trip ends
- *     The watch presents the average speed and trip duration in seconds
- * 
- * Usage:
- *     Go to tachymeter face, TC is shown in the Weekday Digits
- *     A steady d in the Day Digits indicates the distance to be used.
- *         To edit the distance:
- *         Long-press the Alarm button, the distance edition page (d will blink)
- *         Use the Light button to change the editing (blinking) digit, and press Alarm to increase its value
- *         Once done, long-press the Alarm button to exit the distance edition page
- *     Press the Alarm button to start the tachymeter.
- *         A running animation will appear in the Day Digits
- *     Press the Alarm button to stop the tachymeter
- *     The average speed and total time information will alternate.
- *         The average speed will be shown alongside /h in the Day Digits;
- *         and the total time will be shown alongside t in the Day Digits.
- *     Long press the Light button to return to the distance d page,
- *         and restart the tachymeter from there.
- *     Long-press the light button in the steady distance page to reset
- *         the distance to 1.00
- * 
- * Pending design points
- * o movement_request_tick_frequency(4) is used to obtain a 4Hz ticking, thus
- *   having a time resolution of 250 ms. Not sure if using event.subsecond`
- *   is the proper way to get the fractions of second for the start and
- *   final times.
- * o For distance and average speed, the Second Digits (position 8 and 9)
- *   can be seen as decimals, thus possible to show distances as short as
- *   0.01 km (or miles) and speeds as low as 0.01 km/h (or mph). However,
- *   if the same idea is used for the total time (showing hundredths),
- *   this limits the display to 9999.99 seconds (~2h:45m).
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint8_t thousands: 4;   // 0-9 (must wrap at 10)
-    uint8_t hundreds: 4;    // 0-9 (must wrap at 10)
-    uint8_t tens: 4;        // 0-9 (must wrap at 10)
-    uint8_t ones: 4;        // 0-9 (must wrap at 10)
-    uint8_t tenths: 4;      // 0-9 (must wrap at 10)
-    uint8_t hundredths: 4;  // 0-9 (must wrap at 10)
-} distance_digits_t;
-
-typedef struct {
-    bool running;                  // tachymeter status
-    bool editing;                  // editing distance
-    uint8_t active_digit;          // active digit at editing distance
-    uint8_t animation_state;       // running animation state
-    watch_date_time_t start_seconds; // start_seconds
-    int8_t start_subsecond;        // start_subsecond count (each count = 250 ms)
-    distance_digits_t dist_digits; // distance digitwise
-    uint32_t distance;             // distance
-    uint32_t total_time;           // total_time = now - start_time (in cs)
-    uint32_t total_speed;          // 3600 * 100 * distance / total_time
-} tachymeter_state_t;
-
-void tachymeter_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void tachymeter_face_activate(void *context);
-bool tachymeter_face_loop(movement_event_t event, void *context);
-void tachymeter_face_resign(void *context);
-
-#define tachymeter_face ((const watch_face_t){ \
-    tachymeter_face_setup, \
-    tachymeter_face_activate, \
-    tachymeter_face_loop, \
-    tachymeter_face_resign, \
-    NULL, \
-})
-
-#endif // TACHYMETER_FACE_H_
-
diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c
deleted file mode 100644 (file)
index 128e84a..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Andrew Mike
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "tally_face.h"
-#include "watch.h"
-
-#define TALLY_FACE_MAX 9999
-#define TALLY_FACE_MIN -99
-
-static bool _init_val;
-static bool _quick_ticks_running;
-
-static const int16_t _tally_default[] = {
-    0,
-
-#ifdef TALLY_FACE_PRESETS_MTG
-    20,
-    40,
-#endif /* TALLY_FACE_PRESETS_MTG */
-
-#ifdef TALLY_FACE_PRESETS_YUGIOH
-    4000,
-    8000,
-#endif /* TALLY_FACE_PRESETS_YUGIOH */
-
-};
-
-#define TALLY_FACE_PRESETS_SIZE() (sizeof(_tally_default) / sizeof(int16_t))
-
-void tally_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(tally_state_t));
-        memset(*context_ptr, 0, sizeof(tally_state_t));
-        tally_state_t *state = (tally_state_t *)*context_ptr;
-        state->tally_default_idx = 0;
-        state->tally_idx = _tally_default[state->tally_default_idx];
-        _init_val = true;
-    }
-}
-
-void tally_face_activate(void *context) {
-    (void) context;
-    _quick_ticks_running = false;
-}
-
-static void start_quick_cyc(void){
-    _quick_ticks_running = true;
-    movement_request_tick_frequency(8);
-}
-
-static void stop_quick_cyc(void){
-    _quick_ticks_running = false;
-    movement_request_tick_frequency(1);
-}
-
-static void tally_face_increment(tally_state_t *state, bool sound_on) {
-        bool soundOn = !_quick_ticks_running && sound_on;
-        _init_val = false;
-        if (state->tally_idx >= TALLY_FACE_MAX){
-            if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_E7, 30);
-        }
-        else {
-            state->tally_idx++;
-            print_tally(state, sound_on);
-            if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_E6, 30);
-        }
-}
-
-static void tally_face_decrement(tally_state_t *state, bool sound_on) {
-        bool soundOn = !_quick_ticks_running && sound_on;
-        _init_val = false;
-        if (state->tally_idx <= TALLY_FACE_MIN){
-            if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_C5SHARP_D5FLAT, 30);
-        }
-        else {
-            state->tally_idx--;
-            print_tally(state, sound_on);
-            if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_C6SHARP_D6FLAT, 30);
-        }
-}
-
-static bool tally_face_should_move_back(tally_state_t *state) {
-    if (TALLY_FACE_PRESETS_SIZE() <= 1) { return false; }
-    return state->tally_idx == _tally_default[state->tally_default_idx];
-}
-
-bool tally_face_loop(movement_event_t event, void *context) {
-    tally_state_t *state = (tally_state_t *)context;
-    static bool using_led = false;
-
-    if (using_led) {
-        if(!HAL_GPIO_BTN_MODE_read() && !HAL_GPIO_BTN_LIGHT_read() && !HAL_GPIO_BTN_ALARM_read())
-            using_led = false;
-        else {
-            if (event.event_type == EVENT_LIGHT_BUTTON_DOWN || event.event_type == EVENT_ALARM_BUTTON_DOWN)
-                movement_illuminate_led();
-            return true;
-        }
-    }
-
-    switch (event.event_type) {
-        case EVENT_TICK:
-            if (_quick_ticks_running) {
-                bool light_pressed = HAL_GPIO_BTN_LIGHT_read();
-                bool alarm_pressed = HAL_GPIO_BTN_ALARM_read();
-                if (light_pressed && alarm_pressed) stop_quick_cyc();
-                else if (light_pressed) tally_face_increment(state, movement_button_should_sound());
-                else if (alarm_pressed) tally_face_decrement(state, movement_button_should_sound());
-                else stop_quick_cyc();
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            tally_face_decrement(state, movement_button_should_sound());
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            tally_face_decrement(state, movement_button_should_sound());
-            start_quick_cyc();
-            break;
-        case EVENT_MODE_LONG_PRESS:
-            if (tally_face_should_move_back(state)) {
-                _init_val = true;
-                movement_move_to_face(0);
-            }
-            else {
-                state->tally_idx = _tally_default[state->tally_default_idx]; // reset tally index
-                _init_val = true;
-                //play a reset tune
-                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_G6, 30);
-                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_REST, 30);
-                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_E6, 30);
-                print_tally(state, movement_button_should_sound());
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            tally_face_increment(state, movement_button_should_sound());
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-        case EVENT_ALARM_BUTTON_DOWN:
-            if (HAL_GPIO_BTN_MODE_read()) {
-                movement_illuminate_led();
-                using_led = true;
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (TALLY_FACE_PRESETS_SIZE() > 1 && _init_val){
-                state->tally_default_idx = (state->tally_default_idx + 1) % TALLY_FACE_PRESETS_SIZE();
-                state->tally_idx = _tally_default[state->tally_default_idx];
-                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_E6, 30);
-                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_REST, 30);
-                if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_G6, 30);
-                print_tally(state, movement_button_should_sound());
-            }
-            else{
-                tally_face_increment(state, movement_button_should_sound());
-                start_quick_cyc();
-            }
-            break;
-        case EVENT_ACTIVATE:
-            print_tally(state, movement_button_should_sound());
-            break;
-        case EVENT_TIMEOUT:
-            // ignore timeout
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-// print tally index at the center of display.
-void print_tally(tally_state_t *state, bool sound_on) {
-    char buf[14];
-    if (sound_on)
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    else
-        watch_clear_indicator(WATCH_INDICATOR_BELL);
-    if (state->tally_idx >= 0)
-        sprintf(buf, "TA  %4d  ", (int)(state->tally_idx)); // center of LCD display
-    else
-        sprintf(buf, "TA     %-3d", (int)(state->tally_idx)); // center of LCD display
-    watch_display_string(buf, 0);
-}
-
-void tally_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/tally_face.h b/movement/watch_faces/complication/tally_face.h
deleted file mode 100644 (file)
index 7b18a33..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Andrew Mike
- *
- * 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 TALLY_FACE_H_
-#define TALLY_FACE_H_
-
-/*
- * TALLY face
- *
- * Tally face is designed to act as a tally counter.
- *
- * Alarm
- *    Press: Decrement
- *    Hold : Fast Decrement
- *
- * Light
- *    Press: Increment
- *    Hold : On initial value: Cycles through other initial values.
- *                       Else: Fast Increment
- *
- * Mode
- *    Press: Next face
- *    Hold : On initial value: Go to first face.
- *                       Else: Resets counter
- * 
- * Incrementing or Decrementing the tally will beep if Beeping is set in the global Preferences
- */
-
-#include "movement.h"
-
-typedef struct {
-    int16_t tally_idx;
-    uint8_t tally_default_idx;
-} tally_state_t;
-
-//#define TALLY_FACE_PRESETS_MTG
-//#define TALLY_FACE_PRESETS_YUGIOH
-
-
-void tally_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void tally_face_activate(void *context);
-bool tally_face_loop(movement_event_t event, void *context);
-void tally_face_resign(void *context);
-
-void print_tally(tally_state_t *state, bool sound_on);
-
-#define tally_face ((const watch_face_t){ \
-    tally_face_setup, \
-    tally_face_activate, \
-    tally_face_loop, \
-    tally_face_resign, \
-    NULL, \
-})
-
-#endif // TALLY_FACE_H_
diff --git a/movement/watch_faces/complication/tarot_face.c b/movement/watch_faces/complication/tarot_face.c
deleted file mode 100644 (file)
index ae81941..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Jeremy O'Brien
- * Base code copied from Spencer Bywater's probability face
- *
- * 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.
- */
-
-// Emulator only: need time() to seed the random number generator.
-#if __EMSCRIPTEN__
-#include <time.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include "tarot_face.h"
-
-#define TAROT_ANIMATION_TICK_FREQUENCY 8
-#define FLIPPED_BIT_POS 7
-#define FLIPPED_MASK ((uint8_t)(1 << FLIPPED_BIT_POS))
-
-
-// --------------
-// Custom methods
-// --------------
-
-static char major_arcana[][7] = {
-    " FOOL ",
-    "Mgcian",
-    "HPrsts",
-    "En&prs", // Empress
-    "En&por", // Emperor
-    "Hiroph",
-    "Lovers",
-    "Chriot",
-    "Strgth",
-    "Hrn&it", // Hermit
-    " Frtun",
-    "Justce",
-    "Hangn&", // Hangman
-    " Death",
-    " tmprn",
-    " Devil",
-    " Tower",
-    "  Star",
-    "n&OON ", // Moon
-    "  Sun ",
-    "Jdgmnt",
-    " World",
-};
-#define NUM_MAJOR_ARCANA (sizeof(major_arcana) / sizeof(*major_arcana))
-
-static char suits[][7] = {
-    " wands",
-    "  cups",
-    "swords",
-    " coins",
-};
-
-#define NUM_MINOR_ARCANA 56
-#define NUM_CARDS_PER_SUIT 14
-
-#define NUM_TAROT_CARDS (NUM_MAJOR_ARCANA + NUM_MINOR_ARCANA)
-
-static void init_deck(tarot_state_t *state) {
-    memset(state->drawn_cards, 0xff, sizeof(state->drawn_cards));
-    state->current_card = 0;
-}
-
-static void tarot_display(tarot_state_t *state) {
-    char buf[12];
-    char *start_end_string;
-    uint8_t card;
-    bool flipped;
-
-    // deck is initialized; show current draw mode and return
-    if (state->drawn_cards[0] == 0xff) {
-        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-        if (state->major_arcana_only) {
-            sprintf(buf, "TA%2dn&ajor", state->num_cards_to_draw);
-        } else {
-            sprintf(buf, "TA%2d   All", state->num_cards_to_draw);
-        }
-        watch_display_string(buf, 0);
-        return;
-    }
-
-    // show a special status if we're looking at the first or last card in the spread
-    if (state->current_card == 0) {
-        start_end_string = "St";
-    } else if (state->current_card == state->num_cards_to_draw - 1) {
-        start_end_string = "En";
-    } else {
-        start_end_string = "  ";
-    }
-
-    // figure out the card we're showing
-    card = state->drawn_cards[state->current_card];
-    flipped = (card & FLIPPED_MASK) ? true : false; // check flipped bit
-    card &= ~FLIPPED_MASK; // remove the flipped bit
-    if (card < NUM_MAJOR_ARCANA) {
-        // major arcana
-
-        // show start/end, no rank, card name
-        sprintf(buf, "%s  %s", start_end_string, major_arcana[card]);
-    } else {
-        // minor arcana
-        uint8_t suit = (card - NUM_MAJOR_ARCANA) / NUM_CARDS_PER_SUIT;
-        uint8_t rank = ((card - NUM_MAJOR_ARCANA) % NUM_CARDS_PER_SUIT) + 1;
-
-        // show start/end, rank + suit
-        sprintf(buf, "%s%2d%s", start_end_string, rank, suits[suit]);
-    }
-
-    watch_display_string(buf, 0);
-
-    if (flipped) {
-        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    } else {
-        watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-    }
-}
-
-static uint8_t get_rand_num(uint8_t num_values) {
-    // Emulator: use rand. Hardware: use arc4random.
-#if __EMSCRIPTEN__
-    return rand() % num_values;
-#else
-    return arc4random_uniform(num_values);
-#endif
-}
-
-static uint8_t draw_one_card(tarot_state_t *state) {
-    if (state->major_arcana_only) {
-        return get_rand_num(NUM_MAJOR_ARCANA);
-    } else {
-        return get_rand_num(NUM_TAROT_CARDS);
-    }
-}
-
-static bool already_drawn(tarot_state_t *state, uint8_t drawn_card) {
-    for (int i = 0; state->drawn_cards[i] != 0xff && i < state->num_cards_to_draw; i++) {
-        if ((state->drawn_cards[i] & ~FLIPPED_MASK) == drawn_card) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-static void pick_cards(tarot_state_t *state) {
-    uint8_t card;
-
-    for (int i = 0; i < state->num_cards_to_draw; i++) {
-        card = draw_one_card(state);
-        while (already_drawn(state, card)) {
-            card = draw_one_card(state);
-        }
-        card |= get_rand_num(2) << FLIPPED_BIT_POS; // randomly flip the card
-        state->drawn_cards[i] = card;
-    }
-}
-
-static void display_animation(tarot_state_t *state) {
-    if (state->animation_frame == 0) {
-        watch_display_string("   ", 7);
-        watch_set_pixel(1, 4);
-        watch_set_pixel(1, 6);
-        state->animation_frame = 1;
-    } else if (state->animation_frame == 1) {
-        watch_clear_pixel(1, 4);
-        watch_clear_pixel(1, 6);
-        watch_set_pixel(2, 4);
-        watch_set_pixel(0, 6);
-        state->animation_frame = 2;
-    } else if (state->animation_frame == 2) {
-        watch_clear_pixel(2, 4);
-        watch_clear_pixel(0, 6);
-        watch_set_pixel(2, 5);
-        watch_set_pixel(0, 5);
-        state->animation_frame = 3;
-    } else if (state->animation_frame == 3) {
-        state->animation_frame = 0;
-        state->is_picking = false;
-        movement_request_tick_frequency(1);
-        tarot_display(state);
-    }
-}
-
-
-// ---------------------------
-// Standard watch face methods
-// ---------------------------
-void tarot_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(tarot_state_t));
-        memset(*context_ptr, 0, sizeof(tarot_state_t));
-    }
-    // Emulator only: Seed random number generator
-    #if __EMSCRIPTEN__
-    srand(time(NULL));
-    #endif
-}
-
-void tarot_face_activate(void *context) {
-    tarot_state_t *state = (tarot_state_t *)context;
-
-    watch_display_string("TA", 0);
-    init_deck(state);
-    state->num_cards_to_draw = 3;
-    state->major_arcana_only = true;
-}
-
-bool tarot_face_loop(movement_event_t event, void *context) {
-    tarot_state_t *state = (tarot_state_t *)context;
-
-    if (state->is_picking && event.event_type != EVENT_TICK) {
-        return true;
-    }
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            tarot_display(state);
-            break;
-        case EVENT_TICK:
-            if (state->is_picking) {
-                display_animation(state);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (state->drawn_cards[0] == 0xff) {
-                // deck is inited; cycle through # cards to draw
-                state->num_cards_to_draw++;
-                if (state->num_cards_to_draw > 10) {
-                    state->num_cards_to_draw = 3;
-                }
-            } else {
-                // cycle through the drawn cards
-                state->current_card = (state->current_card + 1) % state->num_cards_to_draw;
-            }
-            tarot_display(state);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (state->drawn_cards[0] == 0xff) {
-                // at main screen; cycle major arcana mode
-                state->major_arcana_only = !state->major_arcana_only;
-            } else {
-                // at card view screen; go back to draw screen
-                init_deck(state);
-            }
-            tarot_display(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // Draw cards
-            watch_display_string("      ", 4);
-            watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-            init_deck(state);
-            pick_cards(state);
-            state->is_picking = true;
-            // card picking animation begins on next tick and new cards will be displayed on completion
-            movement_request_tick_frequency(TAROT_ANIMATION_TICK_FREQUENCY);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            watch_display_string("SLEEP ", 4);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void tarot_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/tarot_face.h b/movement/watch_faces/complication/tarot_face.h
deleted file mode 100644 (file)
index 8c83d51..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Jeremy O'Brien
- *
- * 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 TAROT_FACE_H_
-#define TAROT_FACE_H_
-
-/*
- * TAROT CARD watch face
- *
- * Draw from a deck of tarot cards. Can choose between major arcana only or
- * entire deck.
- *
- * In tarot reading, a card orientation can be upright or inverted, and the
- * interpertation of the card can change depending on this state. This face
- * lights the alarm indicator to show when a card is inverted. Just ignore it
- * if you prefer not to deal with card inversions.
- *
- * This face uses the terms "Wands", "Cups", "Swords" and "Coins" for the four
- * suits, and numbers to represent the 14 ranked cards, with the cards 11-14
- * representing the Page, the Knight, the Queen, and King respectively.
- *
- * Default draw is a 3-card major arcana spread.
- *
- * To make it easier to keep track of where you are in the list of drawn cards,
- * after drawing, "St" is shown for the 1st card in the spread and "En" is
- * shown for the last card.
- *
- * At any point, the mode button can be held to return to your first configured
- * watch face.
- *
- * When "Major" or "All" is shown:
- * - Light button: cycle # of cards to draw
- * - Light button (long press): toggle between major arcana and all cards
- * - Alarm button: shuffle deck and draw cards
- *
- * After cards are drawn/showing:
- * - Light button: view the next drawn card
- * - Alarm button: shuffle and re-draw new cards
- * - Light button (long press): go back to Draw screen, for choosing different draw parameters.
- */
-
-#include "movement.h"
-
-#define MAX_CARDS_TO_DRAW 10
-
-typedef struct {
-    uint8_t drawn_cards[MAX_CARDS_TO_DRAW];
-    uint8_t current_card;
-    uint8_t animation_frame;
-    uint8_t num_cards_to_draw;
-    bool major_arcana_only;
-    bool is_picking;
-} tarot_state_t;
-
-void tarot_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void tarot_face_activate(void *context);
-bool tarot_face_loop(movement_event_t event, void *context);
-void tarot_face_resign(void *context);
-
-#define tarot_face ((const watch_face_t){ \
-    tarot_face_setup, \
-    tarot_face_activate, \
-    tarot_face_loop, \
-    tarot_face_resign, \
-    NULL, \
-})
-
-#endif // TAROT_FACE_H_
diff --git a/movement/watch_faces/complication/tempchart_face.c b/movement/watch_faces/complication/tempchart_face.c
deleted file mode 100644 (file)
index e8e1cfa..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Mikhail Svarichevsky
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "tempchart_face.h"
-#include "watch.h"
-#include "watch_private_display.h"
-#include "filesystem.h"
-#include "thermistor_driver.h"
-
-struct {
-    uint8_t stat[24 * 70];
-    uint16_t num_div;
-} tempchart_state;
-
-static void tempchart_save(void) {
-    filesystem_write_file("tempchart.ini", (char*)&tempchart_state, sizeof(tempchart_state));
-}
-
-void tempchart_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    // These next two lines just silence the compiler warnings associated with unused parameters.
-    // We have no use for the settings or the watch_face_index, so we make that explicit here.
-    (void) context_ptr;
-    (void) watch_face_index;
-    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
-    if (filesystem_get_file_size("tempchart.ini") != sizeof(tempchart_state)) {
-        // No previous ini or old version of ini file - create new config file
-        tempchart_state.num_div = 0;
-        for (int i = 0; i < 24 * 70; i++)
-            tempchart_state.stat[i] = 0;
-        tempchart_save();
-    } else
-        filesystem_read_file("tempchart.ini", (char*)&tempchart_state, sizeof(tempchart_state));
-
-}
-
-void tempchart_face_activate(void *context) {
-    // same as above: silence the warning, we don't need to check the settings.
-    (void) context;
-}
-
-static void display(void) {
-    int sum = 0;
-    for (int i = 0; i < 24 * 70; i++)
-        sum += tempchart_state.stat[i];
-
-    char buf[24];
-    sprintf(buf, "TS%2d%6d", tempchart_state.num_div, sum);
-    watch_display_string(buf, 0);
-}
-
-bool tempchart_face_loop(movement_event_t event, void *context) {
-    (void) context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-             display();
-        case EVENT_TICK:
-            // on activate and tick, if we are animating,
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // This low energy mode update occurs once a minute, if the watch face is in the
-            // foreground when Movement enters low energy mode. We have the option of supporting
-            // this mode, but since our watch face animates once a second, the "Hello there" face
-            // isn't very useful in this mode. So we choose not to support it. (continued below)
-            break;
-        case EVENT_TIMEOUT:
-            // ... Instead, we respond to the timeout event. This event happens after a configurable
-            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
-            // resign control if we want to, and in this case, we do.
-            // This function will return the watch to the first screen (usually a simple clock),
-            // and it will do it long before the watch enters low energy mode. This ensures we
-            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
-            movement_move_to_face(0);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            // Here we measure temperature and do main frequency correction
-            thermistor_driver_enable();
-            float temperature_c = thermistor_driver_get_temperature();
-            thermistor_driver_disable();
-            watch_date_time_t date_time = watch_rtc_get_date_time();
-
-            int temp = round(temperature_c * 2);
-            if ((temp < 0) || (temp >= 70)) break;
-
-            if (tempchart_state.stat[date_time.unit.hour + temp * 24] == 255) { // We've reached the limit
-              tempchart_state.num_div++;
-              for (int i = 0; i < 24 * 70; i++)
-                tempchart_state.stat[i] = (tempchart_state.stat[i] + 1) >> 1; // So that we don't lose 1
-            }
-            tempchart_state.stat[date_time.unit.hour+temp*24]++;
-
-            if (date_time.unit.hour == 0 && date_time.unit.minute == 10)
-                tempchart_save();
-
-            break;
-
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void tempchart_face_resign(void *context) {
-    // our watch face, like most watch faces, has nothing special to do when resigning.
-    // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
-    (void) context;
-}
-
-//background freq correction
-movement_watch_face_advisory_t tempchart_face_advise(void *context) {
-    (void) context;
-    movement_watch_face_advisory_t retval = { 0 };
-
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    // Updating data every 5 minutes
-    retval.wants_background_task = date_time.unit.minute % 5 == 0;
-
-    return retval;
-}
diff --git a/movement/watch_faces/complication/tempchart_face.h b/movement/watch_faces/complication/tempchart_face.h
deleted file mode 100644 (file)
index 3c462cd..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 TEMPCHART_FACE_H_
-#define TEMPCHART_FACE_H_
-
-/*
- * TEMPERATURE CHART face
- *
- * Gathers temperature statistics in a chart form.
- * Statistics bins are per hour / per 0.5°C.
- *
- * Saved to file every day at 00:00.
- * Can help improve watch precision in the future. 
- *
- * If you can gather statistics over few months, and then send "tempchart.ini"
- * to "3@14.by", it will help future generations of precision quartz watches.
- */
-
-#include "movement.h"
-
-void tempchart_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void tempchart_face_activate(void *context);
-bool tempchart_face_loop(movement_event_t event, void *context);
-void tempchart_face_resign(void *context);
-movement_watch_face_advisory_t tempchart_face_advise(void *context);
-
-
-#define tempchart_face ((const watch_face_t){ \
-    tempchart_face_setup, \
-    tempchart_face_activate, \
-    tempchart_face_loop, \
-    tempchart_face_resign, \
-    tempchart_face_advise, \
-})
-
-#endif // TEMPCHART_FACE_H_
diff --git a/movement/watch_faces/complication/time_left_face.c b/movement/watch_faces/complication/time_left_face.c
deleted file mode 100644 (file)
index 29a76fe..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Andreas Nebinger, based on the work of 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "time_left_face.h"
-#include "watch.h"
-#include "watch_private_display.h"
-#include "watch_utility.h"
-
-const char _state_titles[][3] = {{'D', 'L', ' '}, {'D', 'L', ' '}, {'D', 'A', ' '}, {'D', 'A', ' '}, {'Y', 'R', 'b'}, {'M', 'O', 'b'}, {'D', 'A', 'b'},
-                                 {'Y', 'R', 'd'}, {'M', 'O', 'd'}, {'D', 'A', 'd'}};
-const uint8_t TIME_LEFT_FACE_STATES = sizeof(_state_titles) / 3;            // total number of state pages
-#define TIME_LEFT_FACE_SETTINGS_STATE 4                                     // number of first settings state
-const uint8_t _percentage_segdata[][2] = {{1, 2}, {2, 2}, {2, 3}, {1, 3}};  // segment data for drawing the percentage sign
-const uint8_t _animation_segdata[][2] = {{2, 8}, {1, 8}, {2, 7}, {2, 6}};  // segment data for the ticking animation
-
-static bool _quick_ticks_running;
-
-static uint32_t _juliandaynum(uint16_t year, uint16_t month, uint16_t day) {
-    // from here: https://en.wikipedia.org/wiki/Julian_day#Julian_day_number_calculation
-    return (1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100))/4 + day - 32075;
-}
-
-/// @brief displays an integer value with or without using positions 8 and 9
-static void _display_integer(char *buf) {
-    if (buf[1] == ' ') { 
-        // display at position 4 if the value is short enough, don't use positions 8 and 9
-        watch_display_character(' ', 8);
-        watch_display_character(' ', 9);
-        watch_display_string((char *)buf + 2, 4);
-    } else  {
-        // otherwise just display using position 8 and 9
-        watch_display_string(buf, 4);
-    }
-    watch_clear_colon();
-}
-
-///@brief display a percentage value
-static void _display_percentage(float percentage, char *buf) {
-    // always stay positive
-    if (percentage < 0) {
-        percentage *= -1;
-        // Switch display to days 'O'ver
-        watch_display_character('O', 1);
-    }
-    int32_t integral = percentage;
-    if (integral >= 100) {
-        // percentage equals 100 or more: don't do fractional part
-        sprintf(buf, " %3li o", integral);
-        watch_clear_colon();
-    } else {
-        // display percentage with two decimal places
-        uint8_t fraction = (int)(percentage * (float)100) % 100;
-        sprintf(buf, "%2li%02u o", integral, fraction);
-        watch_set_colon();
-    }
-    watch_display_string(buf, 4);
-    // draw (parts of) percentage symbol
-    for (uint8_t i = 0; i < sizeof(_percentage_segdata) / 2; i++) 
-        watch_set_pixel(_percentage_segdata[i][0], _percentage_segdata[i][1]);
-}
-
-/// @brief draw the current state to the display
-static void _draw(time_left_state_t *state, uint8_t subsecond) {
-    char buf[17];
-    watch_display_character(_state_titles[state->current_page][0], 0);
-    watch_display_character(_state_titles[state->current_page][1], 1);
-    watch_display_character(' ', 2);
-    watch_display_character(_state_titles[state->current_page][2], 3);
-    if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) {
-        // we are displaying days left or days from birth
-        watch_date_time_t date_time = watch_rtc_get_date_time();
-        uint32_t julian_current_day = _juliandaynum(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day);
-        uint32_t julian_target_day = _juliandaynum(state->target_date.bit.year, state->target_date.bit.month, state->target_date.bit.day);
-        int32_t days_left = julian_target_day - julian_current_day;
-        if (state->current_page == 0) {
-            // display number of days left
-            sprintf(buf, "%6li", days_left);
-            _display_integer(buf);
-        } else {
-            // calculate starting date
-            uint32_t julian_start_day = _juliandaynum(state->birth_date.bit.year, state->birth_date.bit.month, state->birth_date.bit.day);
-            if ((state->current_page & 1) == 1) {
-                float percentage_left;
-                if (julian_start_day == julian_target_day) {
-                    // failsafe
-                    percentage_left = 0;
-                } else {
-                    // display correct percentages
-                    percentage_left = (float)days_left * (float)100 / (float)(julian_target_day - julian_start_day);
-                }
-                _display_percentage(state->current_page == 1 ? percentage_left : 100 - percentage_left, buf);
-            } else {
-                // display days from birth
-                sprintf(buf, "%6li", (int32_t)julian_current_day - julian_start_day);
-                _display_integer(buf);
-            }
-        }
-    } else {
-        // we are in settings mode
-        switch (state->current_page) {
-        case TIME_LEFT_FACE_SETTINGS_STATE:
-            // birth year
-            sprintf(buf, "%04u  ", state->birth_date.bit.year);
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 1:
-            // birth month
-            sprintf(buf, "  %02u", state->birth_date.bit.month);
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 2:
-            // birth day of month
-            sprintf(buf, "  %02u", state->birth_date.bit.day);
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 3:
-            // target year
-            sprintf(buf, "%04u", state->target_date.bit.year);
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 4:
-            // target month
-            sprintf(buf, "  %02u", state->target_date.bit.month);
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 5:
-            // target day of month
-            sprintf(buf, "  %02u", state->target_date.bit.day);
-            break;
-        default:
-            break;
-        }
-        // blink current settings position or display value
-        if ((subsecond & 1) == 1) 
-            watch_display_string("    ", 4);
-        else
-            watch_display_string(buf, 4);
-    }
-}
-
-/// @brief handle short or long pressing the alarm button
-static void _handle_alarm_button(time_left_state_t *state) {
-    
-    switch (state->current_page) {
-        case TIME_LEFT_FACE_SETTINGS_STATE: // birth year
-            state->birth_date.bit.year++;
-            if (state->birth_date.bit.year > state->current_year + 10) state->birth_date.bit.year = 1959;
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 1: // birth month
-            state->birth_date.bit.month = (state->birth_date.bit.month % 12) + 1;
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 2: // birth day
-            state->birth_date.bit.day = (state->birth_date.bit.day % watch_utility_days_in_month(state->birth_date.bit.month, state->birth_date.bit.year)) + 1;
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 3: // target year
-            state->target_date.bit.year++;
-            if (state->target_date.bit.year  >2083) state->target_date.bit.year = state->current_year - 10;
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 4: // target month
-            state->target_date.bit.month = (state->target_date.bit.month % 12) + 1;
-            break;
-        case TIME_LEFT_FACE_SETTINGS_STATE + 5: // target day
-            state->target_date.bit.day = (state->target_date.bit.day % watch_utility_days_in_month(state->target_date.bit.month, state->birth_date.bit.year)) + 1;
-            break;
-    }
-}
-
-static void _initiate_setting(time_left_state_t *state) {
-    state->current_page = TIME_LEFT_FACE_SETTINGS_STATE;
-    watch_clear_colon();
-    movement_request_tick_frequency(4);
-}
-
-static void _resume_setting(time_left_state_t *state) {
-    state->current_page = 0;
-    movement_request_tick_frequency(1);
-}
-
-static void _abort_quick_ticks() {
-    if (_quick_ticks_running) {
-        _quick_ticks_running = false;
-        movement_request_tick_frequency(4);
-    }
-}
-
-void time_left_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(time_left_state_t));
-        memset(*context_ptr, 0, sizeof(time_left_state_t));
-        time_left_state_t *state = (time_left_state_t *)*context_ptr;
-        state->birth_date.reg = watch_get_backup_data(2);
-        if (state->birth_date.reg == 0) {
-            // if birth date is totally blank, set a reasonable starting date. this works well for anyone under 63, but
-            // you can keep pressing to go back to 1900; just pass the current year. also picked this date because if you
-            // set it to 1959-01-02, it counts up from the launch of Luna-1, the first spacecraft to leave the well.
-            state->birth_date.bit.year = 1959;
-            state->birth_date.bit.month = 1;
-            state->birth_date.bit.day = 1;
-            watch_store_backup_data(state->birth_date.reg, 2);
-            // set target date to today + 10 years (just to have any value)
-            watch_date_time_t date_time = watch_rtc_get_date_time();
-            state->target_date.bit.year = date_time.unit.year + WATCH_RTC_REFERENCE_YEAR + 10;
-            state->target_date.bit.month = date_time.unit.month;
-            state->target_date.bit.day = date_time.unit.day;
-        }
-    }
-}
-
-void time_left_face_activate(void *context) {
-    time_left_state_t *state = (time_left_state_t *)context;
-
-    // stash the current year, useful in birthday setting mode
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    state->current_year = date_time.unit.year + WATCH_RTC_REFERENCE_YEAR;
-    _quick_ticks_running = false;
-    // fetch the user's birth date from the birthday register
-    state->birth_date.reg = watch_get_backup_data(2);
-    // store original value of the birthdate to be able to detect changes on resigning
-    state->birth_date_when_activated.reg = state->birth_date.reg;
-}
-
-bool time_left_face_loop(movement_event_t event, void *context) {
-    time_left_state_t *state = (time_left_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _draw(state, event.subsecond);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-        case EVENT_TICK: {
-            uint8_t subsecond = event.subsecond;
-            if (_quick_ticks_running) {
-                if (HAL_GPIO_BTN_ALARM_read()) {
-                    _handle_alarm_button(state);
-                    subsecond = 0;
-                } else _abort_quick_ticks();
-            }
-            if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
-                // we are in settings mode. Draw to blink
-                _draw(state, subsecond);
-            } else {
-                // otherwise, check if we have to update. the display only needs to change at midnight
-                watch_date_time_t date_time = watch_rtc_get_date_time();
-                if (date_time.unit.hour == 0 &&  date_time.unit.minute == 0 && date_time.unit.second == 0) {
-                    _draw(state, subsecond);
-                }
-                // handle the ticking animation
-                uint8_t animation_step = date_time.unit.second % (sizeof(_animation_segdata) / 2);
-                watch_set_pixel(_animation_segdata[animation_step][0], _animation_segdata[animation_step][1]);
-                if (animation_step == 0) animation_step = (sizeof(_animation_segdata) / 2) - 1;
-                else animation_step--;
-                watch_clear_pixel(_animation_segdata[animation_step][0], _animation_segdata[animation_step][1]);
-            }
-            break;
-        }
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // do not illuminate here (this is done when releasing the button)
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) movement_illuminate_led();
-            else {
-                // cycle through the settings pages
-                state->current_page++;
-                if (state->current_page >= TIME_LEFT_FACE_STATES) {
-                    // we have done a full settings cycle, so resume to normal
-                    _resume_setting(state);
-                    _draw(state, event.subsecond);
-                }
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
-                _resume_setting(state);
-            } else {
-                _initiate_setting(state);
-            }
-            _draw(state, event.subsecond);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            _abort_quick_ticks();
-            if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) {
-                // alternate between days left and percentage left
-                state->current_page = (state->current_page + 1) % TIME_LEFT_FACE_SETTINGS_STATE;
-            } else {
-                // we are in settings mode, so increment the current settings value
-                _handle_alarm_button(state);
-            }
-            _draw(state, 0);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
-                // initiate fast cycling for settings values
-                movement_request_tick_frequency(8);
-                _quick_ticks_running = true;
-                _handle_alarm_button(state);
-                _draw(state, 0);
-            }
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void time_left_face_resign(void *context) {
-    time_left_state_t *state = (time_left_state_t *)context;
-
-    if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) _resume_setting(state);
-
-    // if the user changed their birth date, store it to the birth date register
-    if (state->birth_date_when_activated.reg != state->birth_date.reg) {
-        watch_store_backup_data(state->birth_date.reg, 2);
-    }
-}
diff --git a/movement/watch_faces/complication/time_left_face.h b/movement/watch_faces/complication/time_left_face.h
deleted file mode 100644 (file)
index 8f20ca0..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Andreas Nebinger, based on the work of 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 TIME_LEFT_FACE_H_
-#define TIME_LEFT_FACE_H_
-
-/*
- * TIME LEFT face
- *
- * The Time Left Face helps you to visualize how far you have proceeded in a certain
- * time span. Much like the Day One Face, you can set your beginning date. In addition
- * to that, you also set your target or destination date. You can then use the face
- * to display your progress in different ways.
- * 
- * Usage:
- * 
- * - Long pressing of the light button starts the settings mode:
- *   - First, you set the beginning date (indicated by a 'b' in the upper right corner).
- *   - Start by setting the year (indicated by the letter 'YR'). Use the alarm button
- *     to cycle the value. Short pressing the light button brings you to the next
- *     settings page.
- *   - Set the values in this order:
- *     a. beginning date (indicated by a 'b'): year - month - day
- *     b. destination date (indicated by a 'd'): year - month - day
- *   - After cycling through all settings pages, the face resumes to display mode.
- * 
- * - In display mode, use the alarm button (short press) to cycle through these four
- *   types of display:
- *   a. number of days left ('DL') until the destination date is reached.
- *   b. remaining days expressed as percentage of total time span. The value is shown
- *      with two decimals, using the colon as decimal point.
- *   c. number of days passed ('DA') since the beginning date.
- *   d. number of days passed expressed as percentage of total time span. (Two decimal
- *      points.)
- *
- * What is this for?
- *
- * You can use this watch face to be reminded of any kind of progess between a set
- * start and end date. The brave among us can use it as a kind of memento mori
- * visualization. Set your date of birth and look up the average life expectancy of
- * your age cohort based on publicly available mortality tables. Then, set the
- * statistically expected day of death as the target date and you will be able to
- * see how much of your time has passed and how much is still to come.
- * 
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint8_t current_page;
-    uint16_t current_year;
-    movement_birthdate_t birth_date;
-    movement_birthdate_t birth_date_when_activated;
-    movement_birthdate_t target_date;
-} time_left_state_t;
-
-void time_left_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void time_left_face_activate(void *context);
-bool time_left_face_loop(movement_event_t event, void *context);
-void time_left_face_resign(void *context);
-
-#define time_left_face ((const watch_face_t){ \
-    time_left_face_setup, \
-    time_left_face_activate, \
-    time_left_face_loop, \
-    time_left_face_resign, \
-    NULL, \
-})
-
-#endif // TIME_LEFT_FACE_H_
diff --git a/movement/watch_faces/complication/timer_face.c b/movement/watch_faces/complication/timer_face.c
deleted file mode 100644 (file)
index de48e35..0000000
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Andreas Nebinger, building on Wesley Ellis’ countdown_face.c
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "timer_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-static const uint32_t _default_timer_values[] = {0x000200, 0x000500, 0x000A00, 0x001400, 0x002D02}; // default timers: 2 min, 5 min, 10 min, 20 min, 2 h 45 min
-
-// sound sequence for a single beeping sequence
-static const int8_t _sound_seq_beep[] = {BUZZER_NOTE_C8, 3, BUZZER_NOTE_REST, 3, -2, 2, BUZZER_NOTE_C8, 5, BUZZER_NOTE_REST, 25, 0};
-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 void _signal_callback() {
-    if (_beeps_to_play) {
-        _beeps_to_play--;
-        watch_buzzer_play_sequence((int8_t *)_sound_seq_beep, _signal_callback);
-    }
-}
-
-static void _start(timer_state_t *state, bool with_beep) {
-    if (state->timers[state->current_timer].value == 0) return;
-    watch_date_time_t now = watch_rtc_get_date_time();
-    state->now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
-    if (state->mode == pausing)
-        state->target_ts = state->now_ts + state->paused_left;
-    else
-        state->target_ts = watch_utility_offset_timestamp(state->now_ts, 
-                                                          state->timers[state->current_timer].unit.hours, 
-                                                          state->timers[state->current_timer].unit.minutes, 
-                                                          state->timers[state->current_timer].unit.seconds);
-    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(state->target_ts, movement_get_current_timezone_offset());
-    state->mode = running;
-    movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
-    watch_set_indicator(WATCH_INDICATOR_BELL);
-    if (with_beep) watch_buzzer_play_sequence((int8_t *)_sound_seq_start, NULL);
-}
-
-static void _draw(timer_state_t *state, uint8_t subsecond) {
-    char buf[14];
-    uint32_t delta;
-    div_t result;
-    uint8_t h, min, sec;
-
-    switch (state->mode) {
-        case pausing:
-            if (state->pausing_seconds % 2)
-                watch_clear_indicator(WATCH_INDICATOR_BELL);
-            else
-                watch_set_indicator(WATCH_INDICATOR_BELL);
-            if (state->pausing_seconds != 1)
-                // not 1st iteration (or 256th): do not write anything
-                return;
-            // fall through
-        case running:
-            delta = state->target_ts - state->now_ts;
-            result = div(delta, 60);
-            sec = result.rem;
-            result = div(result.quot, 60);
-            min = result.rem;
-            h = result.quot;
-            sprintf(buf, " %02u%02u%02u", h, min, sec);
-            break;
-        case setting:
-            if (state->settings_state == 1) {
-                // ask it the current timer shall be erased
-                sprintf(buf, " CLEAR%c", state->erase_timer_flag ? 'y' : 'n');
-                watch_clear_colon();
-            } else if (state->settings_state == 5) {
-                sprintf(buf, "  LOOP%c", state->timers[state->current_timer].unit.repeat ? 'y' : 'n');
-                watch_clear_colon();
-            } else {
-                sprintf(buf, " %02u%02u%02u", state->timers[state->current_timer].unit.hours,
-                        state->timers[state->current_timer].unit.minutes,
-                        state->timers[state->current_timer].unit.seconds);
-                watch_set_colon();
-            }
-            break;
-        case waiting:
-            sprintf(buf, " %02u%02u%02u", state->timers[state->current_timer].unit.hours,
-                    state->timers[state->current_timer].unit.minutes,
-                    state->timers[state->current_timer].unit.seconds);
-            break;
-    }
-    buf[0] = 49 + state->current_timer;
-    if (state->mode == setting && subsecond % 2) {
-        // blink the current settings value
-        if (state->settings_state == 0) buf[0] = ' ';
-        else if (state->settings_state == 1 || state->settings_state == 5) buf[6] = ' ';
-        else buf[(state->settings_state - 1) * 2 - 1] = buf[(state->settings_state - 1) * 2] = ' ';
-    }
-    watch_display_string(buf, 3);
-    // set lap indicator when we have a looping timer
-    if (state->timers[state->current_timer].unit.repeat) watch_set_indicator(WATCH_INDICATOR_LAP);
-    else watch_clear_indicator(WATCH_INDICATOR_LAP);
-}
-
-static void _reset(timer_state_t *state) {
-    state->mode = waiting;
-    movement_cancel_background_task_for_face(state->watch_face_index);
-    watch_clear_indicator(WATCH_INDICATOR_BELL);
-}
-
-static void _set_next_valid_timer(timer_state_t *state) {
-    if ((state->timers[state->current_timer].value & 0xFFFFFF) == 0) {
-        uint8_t i = state->current_timer;
-        do {
-            i = (i + 1) % TIMER_SLOTS;
-        } while ((state->timers[i].value & 0xFFFFFF) == 0 && i != state->current_timer);
-        state->current_timer = i;
-    }
-}
-
-static void _resume_setting(timer_state_t *state) {
-    state->settings_state = 0;
-    state->mode = waiting;
-    movement_request_tick_frequency(1);
-    _set_next_valid_timer(state);
-}
-
-static void _settings_increment(timer_state_t *state) {
-    switch(state->settings_state) {
-        case 0:
-            state->current_timer = (state->current_timer + 1) % TIMER_SLOTS;
-            break;
-        case 1:
-            state->erase_timer_flag ^= 1;
-            break;
-        case 2:
-            state->timers[state->current_timer].unit.hours = (state->timers[state->current_timer].unit.hours + 1) % 24;
-            break;
-        case 3:
-            state->timers[state->current_timer].unit.minutes = (state->timers[state->current_timer].unit.minutes + 1) % 60;
-            break;
-        case 4:
-            state->timers[state->current_timer].unit.seconds = (state->timers[state->current_timer].unit.seconds + 1) % 60;
-            break;
-        case 5:
-            state->timers[state->current_timer].unit.repeat ^= 1;
-            break;
-        default:
-            // should never happen
-            break;
-    }
-    return;
-}
-
-static void _abort_quick_cycle(timer_state_t *state) {
-    if (state->quick_cycle) {
-        state->quick_cycle = false;
-        movement_request_tick_frequency(4);
-    }
-}
-
-static inline bool _check_for_signal() {
-    if (_beeps_to_play) {
-        _beeps_to_play = 0;
-        return true;
-    }
-    return false;
-}
-
-void timer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(timer_state_t));
-        timer_state_t *state = (timer_state_t *)*context_ptr;
-        memset(*context_ptr, 0, sizeof(timer_state_t));
-        state->watch_face_index = watch_face_index;
-        for (uint8_t i = 0; i < sizeof(_default_timer_values) / sizeof(uint32_t); i++) {
-            state->timers[i].value = _default_timer_values[i];
-        }
-    }
-}
-
-void timer_face_activate(void *context) {
-    timer_state_t *state = (timer_state_t *)context;
-    watch_display_string("TR", 0);
-    watch_set_colon();
-    if(state->mode == running) {
-        watch_date_time_t now = watch_rtc_get_date_time();
-        state->now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    } else {
-        state->pausing_seconds = 1;
-        _beeps_to_play = 0;
-    }
-}
-
-bool timer_face_loop(movement_event_t event, void *context) {
-    timer_state_t *state = (timer_state_t *)context;
-    uint8_t subsecond = event.subsecond;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _draw(state, event.subsecond);
-            break;
-        case EVENT_TICK:
-            if (state->mode == running) state->now_ts++;
-            else if (state->mode == pausing) state->pausing_seconds++;
-            else if (state->quick_cycle) {
-                if (HAL_GPIO_BTN_ALARM_read()) {
-                    _settings_increment(state);
-                    subsecond = 0;
-                } else _abort_quick_cycle(state);
-            }
-            _draw(state, subsecond);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            switch (state->mode) {
-                case pausing:
-                case running:
-                    movement_illuminate_led();
-                    break;
-                case setting:
-                    if (state->erase_timer_flag) {
-                        state->timers[state->current_timer].value = 0;
-                        state->erase_timer_flag = false;
-                    }
-                    state->settings_state = (state->settings_state + 1) % 6;
-                    if (state->settings_state == 1 && state->timers[state->current_timer].value == 0) state->settings_state = 2;
-                    else if (state->settings_state == 5 && (state->timers[state->current_timer].value & 0xFFFFFF) == 0) state->settings_state = 0;
-                    break;
-                default:
-                    break;
-            }
-            _draw(state, event.subsecond);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (state->mode == waiting) movement_illuminate_led();
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            _abort_quick_cycle(state);
-            if (_check_for_signal()) break;;
-            switch (state->mode) {
-                case running:
-                    state->mode = pausing;
-                    state->pausing_seconds = 0;
-                    state->paused_left = state->target_ts - state->now_ts;
-                    movement_cancel_background_task();
-                    break;
-                case pausing:
-                    _start(statemovement_get_current_timezone_offset(), false);
-                    break;
-                case waiting: {
-                    uint8_t last_timer = state->current_timer;
-                    state->current_timer = (state->current_timer + 1) % TIMER_SLOTS;
-                    _set_next_valid_timer(state);
-                    // start the time immediately if there is only one valid timer slot
-                    if (last_timer == state->current_timer) _start(statemovement_get_current_timezone_offset(), true);
-                    break;
-                }
-                case setting:
-                    _settings_increment(state);
-                    subsecond = 0;
-                    break;
-            }
-            _draw(state, subsecond);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (state->mode == waiting) {
-                // initiate settings
-                state->mode = setting;
-                state->settings_state = 0;
-                state->erase_timer_flag = false;
-                movement_request_tick_frequency(4);
-            } else if (state->mode == setting) {
-                _resume_setting(state);
-            }
-            _draw(state, event.subsecond);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            // play the alarm
-            _beeps_to_play = 4;
-            watch_buzzer_play_sequence((int8_t *)_sound_seq_beep, _signal_callback);
-            _reset(state);
-            if (state->timers[state->current_timer].unit.repeat) _start(statemovement_get_current_timezone_offset(), false);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            switch(state->mode) {
-                case setting:
-                    switch (state->settings_state) {
-                        case 0:
-                            state->current_timer = 0;
-                            break;
-                        case 2:
-                        case 3:
-                        case 4:
-                            state->quick_cycle = true;
-                            movement_request_tick_frequency(8);
-                            break;
-                        default:
-                            break;
-                    }
-                    break;
-                case waiting:
-                    _start(statemovement_get_current_timezone_offset(), true);
-                    break;
-                case pausing:
-                case running:
-                    _reset(state);
-                    if (movement_button_should_sound()) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
-                    break;
-                default:
-                    break;
-            }
-            _draw(state, event.subsecond);
-            break;
-        case EVENT_ALARM_LONG_UP:
-            _abort_quick_cycle(state);
-            break;
-        case EVENT_MODE_LONG_PRESS:
-        case EVENT_TIMEOUT:
-            _abort_quick_cycle(state);
-            movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void timer_face_resign(void *context) {
-    timer_state_t *state = (timer_state_t *)context;
-    if (state->mode == setting) {
-        state->settings_state = 0;
-        state->mode = waiting;
-    }
-}
diff --git a/movement/watch_faces/complication/timer_face.h b/movement/watch_faces/complication/timer_face.h
deleted file mode 100644 (file)
index b59c958..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Andreas Nebinger, based on Wesley Ellis’ countdown face.
- *
- * 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 TIMER_FACE_H_
-#define TIMER_FACE_H_
-
-/*
- * TIMER face
- * Advanced timer/countdown face with pre-set timer lengths
- * 
- * This watch face provides the functionality of starting a countdown by choosing 
- * one out of nine programmable timer presets. A timer/countdown can be 23 hours,
- * 59 minutes, and 59 seconds max. A timer can also be set to auto-repeat, which
- * is indicated by the lap indicator.
- *
- * How to use in NORMAL mode:
- *   - Short-pressing the alarm button cycles through all pre-set timer lengths.
- *     Find the current timer slot number in the upper right-hand corner.
- *   - Long-pressing the alarm button starts the timer.
- *   - Long-pressing the light button initiates settings mode.
- * 
- * How to use in SETTINGS mode:
- *   - There are up to nine slots for storing a timer setting. The current slot is 
- *     indicated by the number in the upper right-hand corner.
- *   - Short-pressing the light button cycles through the settings values of each
- *     timer slot in the following order: hours - minutes - seconds - timer repeat
- *   - Short-pressing the alarm button alters the current settings value.
- *   - Long-pressing the light button resumes to normal mode.
- * 
- */
-
-#include "movement.h"
-
-#define TIMER_SLOTS 9           // offer 9 timer slots
-
-typedef enum {
-    waiting,
-    running,
-    setting,
-    pausing
-} timer_mode_t;
-
-typedef union {
-    struct {
-        uint8_t hours;
-        uint8_t minutes;
-        uint8_t seconds;
-        bool repeat;
-    } unit;
-    uint32_t value;
-} timer_setting_t;
-
-typedef struct {
-    uint32_t target_ts;
-    uint32_t now_ts;
-    uint16_t paused_left;
-    uint8_t pausing_seconds;
-    uint8_t watch_face_index;
-    timer_setting_t timers[TIMER_SLOTS]; 
-    uint8_t settings_state : 4;
-    uint8_t current_timer : 4;
-    uint8_t set_timers : 4;
-    bool erase_timer_flag : 1;
-    timer_mode_t mode : 3;
-    bool quick_cycle : 1;
-} timer_state_t;
-
-void timer_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void timer_face_activate(void *context);
-bool timer_face_loop(movement_event_t event, void *context);
-void timer_face_resign(void *context);
-
-#define timer_face ((const watch_face_t){ \
-    timer_face_setup, \
-    timer_face_activate, \
-    timer_face_loop, \
-    timer_face_resign, \
-    NULL, \
-})
-
-
-#endif // TIMER_FACE_H_
diff --git a/movement/watch_faces/complication/tomato_face.c b/movement/watch_faces/complication/tomato_face.c
deleted file mode 100644 (file)
index d33f8f0..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Wesley Ellis
- *
- * 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 NONINFtomato_ringEMENT. 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "tomato_face.h"
-#include "watch_utility.h"
-
-static uint8_t focus_min = 25;
-static uint8_t break_min = 5;
-
-static uint8_t get_length(tomato_state_t *state) {
-    uint8_t length;
-    if (state->kind == tomato_focus) {
-        length = focus_min;
-    } else {
-        length = break_min;
-    }
-
-    return length;
-}
-
-static void tomato_start(tomato_state_t *state) {
-    watch_date_time_t now = watch_rtc_get_date_time();
-    int8_t length = (int8_t) get_length(state);
-
-    state->mode = tomato_run;
-    state->now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
-    state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, length, 0);
-    watch_date_time_t target_dt = watch_utility_date_time_from_unix_time(state->target_ts, movement_get_current_timezone_offset());
-    movement_schedule_background_task(target_dt);
-    watch_set_indicator(WATCH_INDICATOR_BELL);
-}
-
-static void tomato_draw(tomato_state_t *state) {
-    char buf[16];
-
-    uint32_t delta;
-    div_t result;
-    uint8_t min = 0;
-    uint8_t sec = 0;
-    char kind;
-
-    if (state->kind == tomato_break) {
-        kind = 'b';
-    } else {
-        kind = 'f';
-    }
-
-    switch (state->mode) {
-        case tomato_run:
-            delta = state->target_ts - state->now_ts;
-            result = div(delta, 60);
-            min = result.quot;
-            sec = result.rem;
-            break;
-        case tomato_ready:
-            min = get_length(state);
-            sec = 0;
-            break;
-    }
-    if (state->visible) {
-        sprintf(buf, "TO %c%2d%02d%2d", kind, min, sec, state->done_count);
-        watch_display_string(buf, 0);
-    }
-}
-
-static void tomato_reset(tomato_state_t *state) {
-    state->mode = tomato_ready;
-    movement_cancel_background_task();
-    watch_clear_indicator(WATCH_INDICATOR_BELL);
-}
-
-static void tomato_ring(tomato_state_t *state) {
-    movement_play_signal();
-    tomato_reset(state);
-    if (state->kind == tomato_focus) {
-        state->kind = tomato_break;
-        state->done_count++;
-    } else {
-        state->kind = tomato_focus;
-    }
-}
-
-void tomato_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(tomato_state_t));
-        tomato_state_t *state = (tomato_state_t*)*context_ptr;
-        memset(*context_ptr, 0, sizeof(tomato_state_t));
-        state->mode=tomato_ready;
-        state->kind= tomato_focus;
-        state->done_count = 0;
-        state->visible = true;
-    }
-}
-
-void tomato_face_activate(void *context) {
-    tomato_state_t *state = (tomato_state_t *)context;
-    if (state->mode == tomato_run) {
-        watch_date_time_t now = watch_rtc_get_date_time();
-        state->now_ts = watch_utility_date_time_to_unix_time(now, movement_get_current_timezone_offset());
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-    }
-    watch_set_colon();
-    state->visible = true;
-}
-
-bool tomato_face_loop(movement_event_t event, void *context) {
-    tomato_state_t *state = (tomato_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            tomato_draw(state);
-            break;
-        case EVENT_TICK:
-            if (state->mode == tomato_run) {
-                state->now_ts++;
-            }
-            tomato_draw(state);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            movement_illuminate_led();
-            if (state->mode == tomato_ready) {
-                if (state->kind == tomato_break) {
-                    state->kind = tomato_focus;
-                } else {
-                    state->kind = tomato_break;
-                }
-            }
-            tomato_draw(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch(state->mode) {
-                case tomato_run:
-                    tomato_reset(state);
-                    break;
-                case tomato_ready:
-                    tomato_start(state);
-                    break;
-            }
-            tomato_draw(state);
-
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->done_count = 0;
-            break;
-        case EVENT_BACKGROUND_TASK:
-            tomato_ring(state);
-            tomato_draw(state);
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void tomato_face_resign(void *context) {
-    tomato_state_t *state = (tomato_state_t *)context;
-    state->visible = false;
-    (void) context;
-}
-
diff --git a/movement/watch_faces/complication/tomato_face.h b/movement/watch_faces/complication/tomato_face.h
deleted file mode 100644 (file)
index e06a7c8..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Wesley Ellis
- *
- * 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 TOMATO_FACE_H_
-#define TOMATO_FACE_H_
-
-/*
- * TOMATO TIMER face
- *
- * Add a "tomato" timer watch face that alternates between 25 and 5 minute
- * timers as in the Pomodoro Technique.
- *  https://en.wikipedia.org/wiki/Pomodoro_Technique
- *
- * The top right letter shows mode (f for focus or b for break).
- * The bottom right shows how many focus sessions you've completed.
- * (You can reset the count with a long press of alarm)
- *
- * When you show up and it says 25 minutes, you can start it (alarm),
- *  switch to 5 minute (light) mode or leave (mode).
- *
- * When it's running you can reset (alarm), or leave (mode).
- *
- * When it's done, we beep and go back to step 1, changing switching
- *  mode from focus to break (or break to focus)
- */
-
-#include "movement.h"
-
-typedef enum {
-    tomato_ready,
-    tomato_run,
-    // to_pause, // TODO implement pausing
-} tomato_mode;
-
-typedef enum {
-    tomato_break,
-    tomato_focus,
-} tomato_kind;
-
-typedef struct {
-    uint32_t target_ts;
-    uint32_t now_ts;
-    tomato_mode mode;
-    tomato_kind kind;
-    uint8_t done_count;
-    bool visible;
-} tomato_state_t;
-
-void tomato_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void tomato_face_activate(void *context);
-bool tomato_face_loop(movement_event_t event, void *context);
-void tomato_face_resign(void *context);
-
-#define tomato_face ((const watch_face_t){ \
-    tomato_face_setup, \
-    tomato_face_activate, \
-    tomato_face_loop, \
-    tomato_face_resign, \
-    NULL, \
-})
-
-#endif // TOMATO_FACE_H_
-
diff --git a/movement/watch_faces/complication/toss_up_face.c b/movement/watch_faces/complication/toss_up_face.c
deleted file mode 100644 (file)
index 28b99c9..0000000
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "toss_up_face.h"
-#if __EMSCRIPTEN__
-#include <time.h>
-#else
-#include "saml22j18a.h"
-#endif
-
-static const char heads[] = { '8', 'h', '4', 'E', '(' };
-static const char tails[] = { '0', '+', 'N', '3', ')' };
-static const uint8_t dd[] = {2, 4, 6, 8, 10,12,20,24,30,32,36,48,99}; 
-
-static void _roll_dice_multiple(char* result, uint8_t* dice, uint8_t num_dice);
-static void _sort_coins(char* token, uint8_t num_bits, uint8_t bits, char* heads, char* tails);
-void _display_coins(char* token, bool* bit_array, uint8_t length, toss_up_state_t *state);
-static void _toss_up_face_display(toss_up_state_t *state);
-static void _dice_animation(toss_up_state_t *state);
-static void _coin_animation(toss_up_state_t *state);
-
-// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
-
-void toss_up_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(toss_up_state_t));
-        memset(*context_ptr, 0, sizeof(toss_up_state_t));
-        toss_up_state_t *state = (toss_up_state_t *)*context_ptr;
-
-        // defaults
-        state->coin_num = 1;
-        state->dice_num = 1;
-        state->dice_sides[0] = 6;
-        state->dice_sides[1] = 6;
-        state->dice_sides[2] = 6;
-        state->coin_style[0] = '8';
-        state->coin_style[1] = '0';
-    }
-}
-
-void toss_up_face_activate(void *context) {
-    (void) context;
-}
-
-bool toss_up_face_loop(movement_event_t event, void *context) {
-    toss_up_state_t *state = (toss_up_state_t *)context;
-    uint8_t i = 0;
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            watch_display_string("    Coins ", 0);
-            break;
-        case EVENT_TICK:
-            if ( state->animate ) {
-                state->animation = (state->animation + 1);
-                _toss_up_face_display(state);
-            } 
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if ( state->animate ) break;
-            // change between coins and dice
-            if ( state->mode <= 1 ) state->mode = 2;
-            else if ( state->mode >= 2 ) state->mode = 0;
-            _toss_up_face_display(state);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // toss
-            if ( state->animate ) break;
-            switch (state->mode) {
-                case 0:
-                    state->mode++;
-                    // fall through
-                case 1:
-                    state->animate = true;
-                    for (i = 0; i < state->coin_num; i++) {
-                        state->coins[i] = divine_bit();
-                    }             
-                    break;
-                case 2:
-                    state->mode++;
-                    // fall through
-                case 3:
-                    state->animate = true;
-                    for (i = 0; i < state->dice_num; i++) {
-                        state->dice[i] = roll_dice(state->dice_sides[i]);
-                    }  
-                    break;
-                default:
-                    break;
-            }
-            _toss_up_face_display(state);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if ( state->animate ) break;
-            state->animate = false;
-            switch (state->mode) {
-                case 0: // change to default coin style
-                    state->coin_style[0] = heads[0];
-                    state->coin_style[1] = tails[0];
-                    state->coinface = 0;
-                    break;
-                case 1: // change the coin style
-                    state->coinface = (state->coinface + 1) % 5;
-                    state->coin_style[0] = heads[state->coinface];
-                    state->coin_style[1] = tails[state->coinface];         
-                    break;
-                case 2: // change to default dice sides
-                    state->dice_sides[0] = 6;
-                    state->dice_sides[1] = 6;
-                    state->dice_sides[2] = 6;
-                    state->dd = 0;
-                    break;
-                case 3: // change the sides of the dice
-                    state->dd = (state->dd + 1) % 13;
-                    state->dice_sides[state->dice_num-1] = dd[state->dd];
-                    state->dice[state->dice_num-1] = dd[state->dd];
-                    break;
-                default:
-                    break;
-            }
-            _toss_up_face_display(state);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if ( state->animate ) break;
-            state->animate = false;
-            switch (state->mode) {
-                case 0: // back to one coin
-                    state->coin_num = 1;
-                    break;
-                case 1: // up to 6 coins total
-                    state->coin_num = (state->coin_num % 6) + 1;             
-                    break;
-                case 2: // back to one dice
-                    state->dice_num = 1;
-                    break;
-                case 3: // add up to 3 dice total
-                    state->dice_num = (state->dice_num % 3) + 1;
-                    state->dd = 0;
-                    break;
-                default:
-                    break;
-            }
-            _toss_up_face_display(state);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void toss_up_face_resign(void *context) {
-    (void) context;
-}
-
-// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
-
-/** @brief handles the display 
- */
-static void _toss_up_face_display(toss_up_state_t *state) {
-    char buf[11] = {0};
-    char token[7] = {0};
-    switch ( state->mode ) {
-        case 0: // coins title
-            sprintf(buf, "    Coins ");
-            break;
-        case 1: // coins divination
-            _coin_animation(state);
-            if ( !state->animate ) {
-                watch_clear_display();
-                _display_coins(token, state->coins, state->coin_num, state);
-                sprintf(buf, "    %s", token);
-            }
-            break;
-        case 2: // dice title
-            sprintf(buf, "    Dice ");
-            break;
-        case 3: // dice divination
-            _dice_animation(state);
-            if ( !state->animate ) {
-                _roll_dice_multiple(token, state->dice, state->dice_num + 1);
-                sprintf(buf, "    %s", token);
-            }
-            break;
-        default:
-            break;
-    }
-    watch_display_string(buf, 0);
-}
-
-/** @brief divination method to derive a bit from 32 TRNG bits
- */
-uint8_t divine_bit(void) {
-    uint32_t stalks;
-    do { // modulo bias filter
-        stalks = get_true_entropy(); // get 32 TRNG bits as stalks
-    } while (stalks >= INT32_MAX || stalks <= 0);
-
-    uint8_t pile1_xor = 0;
-    uint8_t pile2_xor = 0;
-    // Divide the stalks into two piles, alternating ends
-    for (uint8_t i = 0; i < 16; i++) {
-        uint8_t left_bit = (stalks >> (31 - 2*i)) & 1;
-        uint8_t right_bit = (stalks >> (30 - 2*i)) & 1;
-        if (i % 2 == 0) {
-            pile1_xor ^= left_bit;
-            pile2_xor ^= right_bit;
-        } else {
-            pile1_xor ^= right_bit;
-            pile2_xor ^= left_bit;
-        }
-    }
-    // Take the XOR of the pile results
-    uint8_t result_xor = pile1_xor ^ pile2_xor;
-    // Output 1 if result_xor is 1, 0 otherwise
-    return result_xor;
-}
-
-/** @brief get 32 True Random Number bits
- */
-uint32_t get_true_entropy(void) {
-    #if __EMSCRIPTEN__
-    return rand() % INT32_MAX;
-    #else
-    hri_mclk_set_APBCMASK_TRNG_bit(MCLK);
-    hri_trng_set_CTRLA_ENABLE_bit(TRNG);
-
-    while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready
-
-    watch_disable_TRNG();
-
-    hri_mclk_clear_APBCMASK_TRNG_bit(MCLK);
-    return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it
-    #endif
-}
-
-// COIN FUNCTIONS /////////////////////////////////////////////////////////////
-
-/** @brief sort tossed coins into a pile of heads and a pile of tails
- */
-static void _sort_coins(char* token, uint8_t num_bits, uint8_t bits, char* heads, char* tails) {
-    uint8_t num_ones = 0;
-    for (uint8_t i = 0; i < num_bits; i++) {
-        if ((bits >> i) & 1) {
-            *token++ = *heads;
-            num_ones++;
-        }
-    }
-    if ( num_bits < 6 ) {
-        for (uint8_t i = 0; i < (6 - num_bits); i++) {
-            *token++ = ' ';
-        }
-    }
-    for (uint8_t i = 0; i < (num_bits - num_ones); i++) {
-
-        *token++ = *tails;
-    }
-}
-
-/** @brief convert bool array of coinflips to integer for sorting
- */
-void _display_coins(char* token, bool* bit_array, uint8_t length, toss_up_state_t *state) {
-    uint8_t bits = 0;
-    for (uint8_t i = 0; i < length; i++) {
-        if (bit_array[i]) {
-            bits |= (1 << (length - 1 - i));
-        }
-    }
-    _sort_coins(token, length, bits, &state->coin_style[0], &state->coin_style[1]);
-}
-
-/** @brief coin animation
- */
-static void _coin_animation(toss_up_state_t *state) {
-    bool heads = false;
-    bool tails = false;
-    for (uint8_t i = 0; i < state->coin_num; i++) {
-        if (state->coins[i] == true) {
-            heads = true;
-        } else {
-            tails = true;
-        }
-    }
-    movement_request_tick_frequency(32);
-    switch ( state->animation ) {
-        case 0:
-            watch_display_string("      ", 4);
-            if ( heads ) {
-                watch_set_pixel(0, 18);
-                watch_set_pixel(2, 18);
-            } else {
-                state->animation = 12;
-            }
-            break;
-        case 1:
-            if ( heads ) {
-                watch_set_pixel(1, 18);
-            }
-            break;
-        case 2:
-            if ( heads ) {
-                watch_set_pixel(0, 19);
-                watch_set_pixel(2, 19);
-            }
-            break;
-        case 3:
-            if ( heads ) {
-                watch_clear_pixel(0, 18);
-                watch_clear_pixel(2, 18);
-            }
-            break;
-        case 4:
-            if ( heads ) {
-                watch_clear_pixel(1, 18);
-            }
-            break;
-        case 5:
-            if ( heads ) {
-                watch_clear_pixel(0, 19);
-                watch_clear_pixel(2, 19);
-                watch_set_pixel(1, 17);
-                watch_set_pixel(0, 20);
-            }
-            break;
-        case 6:
-            if ( heads ) {
-                watch_set_pixel(2, 20);
-                watch_set_pixel(0, 21);
-            }
-            break;
-        case 7:
-            if ( heads ) {
-                watch_set_pixel(1, 21);
-                watch_set_pixel(2, 21);
-            }
-            break;
-        case 8:
-            if ( heads ) {
-                watch_clear_pixel(1, 17);
-                watch_clear_pixel(0, 20);
-            }
-            break;
-        case 9:
-            if ( heads ) {
-                watch_clear_pixel(2, 20);
-                watch_clear_pixel(0, 21);
-            }
-            break;
-        case 10:
-            if ( heads ) {
-                watch_clear_pixel(1, 21);
-                watch_clear_pixel(2, 21);
-                watch_set_pixel(1, 22);
-                watch_set_pixel(2, 22);
-            }
-            break;
-        case 11:
-            if ( heads ) {
-                watch_set_pixel(0, 22);
-            }
-            break;
-        case 12:
-            if ( heads ) {
-                watch_set_pixel(2, 23);
-                watch_set_pixel(0, 23);
-            }
-            if ( tails ) {
-                watch_set_pixel(0, 18);
-                watch_set_pixel(2, 18);
-            }
-            break;
-        case 13:
-            if ( heads ) {
-                watch_clear_pixel(1, 22);
-                watch_clear_pixel(2, 22);
-            }
-            if ( tails ) {
-                watch_set_pixel(1, 18);
-            }
-            break;
-        case 14:
-            if ( heads ) {
-                watch_clear_pixel(0, 22);
-            }
-            if ( tails ) {
-                watch_set_pixel(0, 19);
-                watch_set_pixel(2, 19);
-            }
-            break;
-        case 15:
-            if ( heads ) {
-                watch_clear_pixel(2, 23);
-                watch_clear_pixel(0, 23);
-                watch_set_pixel(2, 0);
-                watch_set_pixel(1, 0);
-            }
-            if ( tails ) {
-                watch_clear_pixel(0, 18);
-                watch_clear_pixel(2, 18);
-            }
-            break;
-        case 16:
-            if ( heads ) {
-                watch_set_pixel(2, 1);
-                watch_set_pixel(0, 0);
-            }
-            if ( tails ) {
-                watch_clear_pixel(1, 18);
-            }
-            break;
-        case 17:
-            if ( heads ) {
-                watch_set_pixel(2, 10);
-                watch_set_pixel(0, 1);
-            }
-            if ( tails ) {
-                watch_clear_pixel(0, 19);
-                watch_clear_pixel(2, 19);
-                watch_set_pixel(1, 17);
-                watch_set_pixel(0, 20);
-            }
-            break;
-        case 18:
-            if ( heads ) {
-                watch_clear_pixel(2, 0);
-                watch_clear_pixel(1, 0);
-            }
-            if ( tails ) {
-                watch_set_pixel(2, 20);
-                watch_set_pixel(0, 21);
-            }
-            break;
-        case 19:
-            if ( heads ) {
-                watch_clear_pixel(2, 1);
-                watch_clear_pixel(0, 0);
-            }
-            if ( tails ) {
-                watch_set_pixel(1, 21);
-                watch_set_pixel(2, 21);
-            }
-            break;
-        case 20:
-            if ( heads ) {
-                watch_set_pixel(2, 1);
-                watch_set_pixel(0, 0);
-            }
-            if ( tails ) {
-                watch_clear_pixel(1, 17);
-                watch_clear_pixel(0, 20);
-            }
-            break;
-        case 21:
-            if ( heads ) {
-                watch_set_pixel(2, 0);
-                watch_set_pixel(1, 0);
-            }
-            if ( tails ) {
-                watch_clear_pixel(2, 20);
-                watch_clear_pixel(0, 21);
-            }
-            break;
-        case 22:
-            if ( heads ) {
-                watch_clear_pixel(2, 10);
-                watch_clear_pixel(0, 1);
-            }
-            if ( tails ) {
-                watch_clear_pixel(1, 21);
-                watch_clear_pixel(2, 21);
-                watch_set_pixel(1, 22);
-                watch_set_pixel(2, 22);
-            }
-            break;
-        case 23:
-            if ( heads ) {
-                watch_clear_pixel(2, 1);
-                watch_clear_pixel(0, 0);
-            }
-            if ( tails ) {
-                watch_set_pixel(0, 22);
-            }
-            break;
-        case 24:
-            if ( heads ) {
-                watch_set_pixel(2, 23);
-                watch_set_pixel(0, 23);
-                watch_clear_pixel(2, 0);
-                watch_clear_pixel(1, 0);
-            }
-            if ( tails ) {
-                watch_set_pixel(2, 23);
-                watch_set_pixel(0, 23);
-            }
-            break;
-        case 25:
-            if ( heads ) {
-                watch_set_pixel(0, 22);
-            }
-            if ( tails ) {
-                watch_clear_pixel(1, 22);
-                watch_clear_pixel(2, 22);
-            }
-            break;
-        case 26:
-            if ( heads ) {
-                watch_set_pixel(1, 22);
-                watch_set_pixel(2, 22);
-            }
-            if ( tails ) {
-                watch_clear_pixel(0, 22);
-            }
-            break;
-        case 27:
-            if ( heads ) {
-                watch_clear_pixel(2, 23);
-                watch_clear_pixel(0, 23);
-            }
-            if ( tails ) {
-                watch_clear_pixel(2, 23);
-                watch_clear_pixel(0, 23);
-                watch_set_pixel(2, 0);
-                watch_set_pixel(1, 0);
-            }
-            break;
-        case 28:
-            if ( heads ) {
-                watch_clear_pixel(0, 22);
-            }
-            if ( tails ) {
-                watch_set_pixel(2, 1);
-                watch_set_pixel(0, 0);
-            }
-            break;
-        case 29:
-            if ( heads ) {
-                watch_set_pixel(1, 21);
-                watch_set_pixel(2, 21);
-                watch_clear_pixel(1, 22);
-                watch_clear_pixel(2, 22);
-            }
-            if ( tails ) {
-                watch_set_pixel(2, 10);
-                watch_set_pixel(0, 1);
-            }
-            break;
-        case 30:
-            if ( heads ) {
-                watch_set_pixel(2, 20);
-                watch_set_pixel(0, 21);
-            }
-            if ( tails ) {
-                watch_clear_pixel(1, 0);
-                watch_clear_pixel(2, 0);
-            }
-            break;
-        case 31:
-            if ( heads ) {
-                watch_set_pixel(1, 17);
-                watch_set_pixel(0, 20);
-            }
-            if ( tails ) {
-                watch_clear_pixel(2, 1);
-                watch_clear_pixel(0, 0);
-            }
-            break;
-        case 32:
-            if ( heads ) {
-                watch_clear_pixel(1, 21);
-                watch_clear_pixel(2, 21);
-            }
-            if ( tails ) {
-                watch_clear_pixel(2, 10);
-                watch_clear_pixel(0, 1);
-                watch_set_pixel(0, 2);
-                watch_set_pixel(1, 2);
-            }
-            break;
-        case 33:
-            if ( heads ) {
-                watch_clear_pixel(2, 20);
-                watch_clear_pixel(0, 21);
-            }
-            if ( tails ) {
-                watch_set_pixel(2, 2);
-                watch_set_pixel(0, 3);
-            }
-            break;
-        case 34:
-            if ( heads ) {
-                watch_set_pixel(0, 19);
-                watch_set_pixel(2, 19);
-                watch_clear_pixel(1, 17);
-                watch_clear_pixel(0, 20);
-            }
-            if ( tails ) {
-                watch_set_pixel(2, 3);
-                watch_set_pixel(0, 4);
-            }
-            break;
-        case 35:
-            if ( heads ) {
-                watch_set_pixel(1, 18);
-            }
-            if ( tails ) {
-                watch_clear_pixel(1, 2);
-                watch_clear_pixel(0, 2);
-            }
-            break;
-        case 36:
-            if ( heads ) {
-                watch_set_pixel(0, 18);
-                watch_set_pixel(2, 18);
-            }
-            if ( tails ) {
-                watch_clear_pixel(2, 2);
-                watch_clear_pixel(0, 3);
-            }
-            break;
-        case 37:
-            if ( heads ) {
-                watch_clear_pixel(0, 19);
-                watch_clear_pixel(2, 19);
-            }
-            if ( tails ) {
-                watch_clear_pixel(2, 3);
-                watch_clear_pixel(0, 4);
-                watch_set_pixel(1, 4);
-                watch_set_pixel(0, 5);
-            }
-            break;
-        case 38:
-            if ( heads ) {
-                watch_clear_pixel(1, 18);
-            }
-            if ( tails ) {
-                watch_set_pixel(2, 4);
-                watch_set_pixel(0, 6);
-            }
-            break;
-        case 39:
-            if ( heads ) {
-                watch_clear_pixel(0, 18);
-                watch_clear_pixel(2, 18);
-            }
-            if ( tails ) {
-                watch_set_pixel(1, 6);
-                watch_set_pixel(2, 5);
-            }
-            state->animate = false;
-            state->animation = 0;
-            movement_request_tick_frequency(1);
-    }
-}
-
-// DICE FUNCTIONS /////////////////////////////////////////////////////////////
-
-/** @brief rolls a dice
- */
-uint8_t roll_dice(uint8_t sides) {
-    uint8_t bits_needed = 0;
-    uint8_t temp_sides = sides - 1;
-    uint8_t result = 0;
-    while (temp_sides > 0) {
-        bits_needed++; // how many bits do we need to represent this number?
-        temp_sides >>= 1; // Shift right to check the next bit
-    }
-    do {
-        result = 0;
-        for (int i = 0; i < bits_needed; i++) {
-            result <<= 1; // Shift left to make room for the next bit
-            result |= divine_bit(); // Add the next bit to the result
-        }
-    } while ( result > sides -1 );
-    return result + 1; // Add 1 to convert the range from 0 to sides-1 to 1 to sides
-}
-
-/** @brief roll multiple dice and print a char array for displaying them
- */
-static void _roll_dice_multiple(char* result, uint8_t* dice, uint8_t num_dice) {
-    // initialize the result array to all spaces
-    memset(result, ' ', 6);
-
-    // roll the dice and write the result to the result array
-    for (uint8_t i = 0; i < num_dice-1; i++) {
-        uint8_t dice_result = dice[i];
-        uint8_t tens_digit = dice_result / 10;
-        uint8_t ones_digit = dice_result % 10;
-        result[(i * 2)] = tens_digit == 0 ? ' ' : (char)('0' + tens_digit);
-        result[(i * 2) + 1] = (char)('0' + ones_digit);
-    }
-}
-
-/** @brief dice animation
- */
-static void _dice_animation(toss_up_state_t *state) {
-    watch_display_string("      ", 4);
-    for (uint8_t i = 0; i < state->dice_num; i++) {
-        watch_display_string("0",i*2 + 5);
-    }
-    movement_request_tick_frequency(16);
-    switch ( state->animation ) {
-        case 0:
-            watch_clear_pixel(1, 17);
-            watch_clear_pixel(0, 0);
-            watch_clear_pixel(1, 6);
-            break;
-        case 1:
-            watch_clear_pixel(2, 20);
-            watch_clear_pixel(1, 0);
-            watch_clear_pixel(0, 6);
-            break;
-        case 2:
-            watch_clear_pixel(2, 21);
-            watch_clear_pixel(2, 0);
-            watch_clear_pixel(0, 5);
-            break;
-        case 3:
-            watch_clear_pixel(1, 21);
-            watch_clear_pixel(2, 1);
-            watch_clear_pixel(1, 4);
-            break;
-        case 4:
-            watch_clear_pixel(0, 21);
-            watch_clear_pixel(2, 10);
-            watch_clear_pixel(2, 4);
-            break;
-        case 5:
-            watch_clear_pixel(0, 20);
-            watch_clear_pixel(0, 1);
-            watch_clear_pixel(2, 5);
-            break;
-        case 6:
-            watch_clear_pixel(1, 17);
-            watch_clear_pixel(0, 0);
-            watch_clear_pixel(1, 6);
-            break;
-        case 7:
-            watch_clear_pixel(2, 20);
-            watch_clear_pixel(1, 0);
-            watch_clear_pixel(0, 6);
-            break;
-        case 8:
-            watch_clear_pixel(2, 21);
-            watch_clear_pixel(2, 0);
-            watch_clear_pixel(0, 5);
-            break;
-        case 9:
-            watch_clear_pixel(1, 21);
-            watch_clear_pixel(2, 1);
-            watch_clear_pixel(1, 4);
-            break;
-        case 10:
-            watch_clear_pixel(0, 21);
-            watch_clear_pixel(2, 10);
-            watch_clear_pixel(2, 4);
-            break;
-        case 11:
-            watch_clear_pixel(0, 20);
-            watch_clear_pixel(0, 1);
-            watch_clear_pixel(2, 5);
-            state->animate = false;
-            state->animation = 0;
-            movement_request_tick_frequency(1);
-    }
-}
\ No newline at end of file
diff --git a/movement/watch_faces/complication/toss_up_face.h b/movement/watch_faces/complication/toss_up_face.h
deleted file mode 100644 (file)
index d6a77d7..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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 TOSS_UP_FACE_H_
-#define TOSS_UP_FACE_H_
-
-/*
- * TOSS UP face
- * ============
- *
- * Playful watch face for games of chance or divination using coins or dice.
- * 
- * LIGHT switches between Coins and Dice mode
- * 
- * COINS
- * =====
- * 
- * ALARM tosses a coin. If it lands on heads it gets sorted to the left side of the
- * display, if it lands on tails then sorted to the right side.
- * 
- * LONG PRESSING ALARM adds up to 5 more coins to the toss for more nuance in the decision
- * making (e.g. three heads vs two tails could be read as "yes, but with serious doubts").
- * 
- * LONG PRESSING LIGHT flips through additional style for the coins from the default Ө/O 
- * to H/T (heads/tails), Y/N (yes/no), E/Ǝ, C/Ↄ
- * 
- * LONG PRESSING ALARM on the "Coins" title page resets to one coin.
- * LONG PRESSING LIGHT on the "Coins" title page resets the style to Ө/O
- * 
- * DICE
- * ====
- * 
- * ALARM rolls a six sided dice.
- * 
- * LONG PRESSING ALARM adds up to 2 more dice to the roll.
- * 
- * LONG PRESSING LIGHT flips through other available polyhedral dice types with less or more
- * than the default 6 sides. The options are D2, D4, D6, D8, D10, D12, D20, D24, D30, D32, D36, 
- * D48, and a hypothetical D99. 
- * 
- * When more than one dice is used for a roll this changes only the last added dice. (see Note
- * below)
- *
- * LONG PRESSING ALARM on the "Dice" title page resets to one dice.
- * LONG PRESSING LIGHT on the "Dice" title page resets the dice to D6.
- * 
- * Please Note: If you need let's say a D8, D12, and D20 for your rolls then the procedure to
- * set this up would be as follows: from the default screen where you can roll the one D6 dice 
- * you would LONG PRESS LIGHT a few times to change the D6 to a D8, then LONG PRESS ALARM to add 
- * a second dice, LONG PRESS LIGHT again until the second dice changes to D12, then LONG PRESS
- * ALARM to add the third dice and LONG PRESS LIGHT again a few times until it becomes a D20.
- *
- */
-
-#include "movement.h"
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint32_t entropy;
-    uint8_t mode : 4; // 1 coin, 2 coins, 3 coins, 4 coins, dice, iching, geomnc
-    bool setup;
-    bool coins[6];
-    uint8_t coin_num : 3;
-    char coin_style[2];
-    uint8_t coinface : 3;
-    uint8_t dice[3];
-    uint8_t dice_num : 2;
-    uint8_t dd : 6;
-    uint8_t dice_sides[3];
-    uint8_t animation;
-    bool animate;
-} toss_up_state_t;
-
-uint32_t get_true_entropy(void);
-uint8_t divine_bit(void);
-uint8_t roll_dice(uint8_t sides);
-void toss_up_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void toss_up_face_activate(void *context);
-bool toss_up_face_loop(movement_event_t event, void *context);
-void toss_up_face_resign(void *context);
-
-#define toss_up_face ((const watch_face_t){ \
-    toss_up_face_setup, \
-    toss_up_face_activate, \
-    toss_up_face_loop, \
-    toss_up_face_resign, \
-    NULL, \
-})
-
-#endif // TOSS_UP_FACE_H_
-
diff --git a/movement/watch_faces/complication/totp_face.c b/movement/watch_faces/complication/totp_face.c
deleted file mode 100644 (file)
index c159f59..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-
-/*
- * MIT License
- *
- * Copyright © 2021 Wesley Ellis (https://github.com/tahnok)
- * Copyright © 2021-2023 Joey Castillo <joeycastillo@utexas.edu>
- * Copyright © 2022 Jack Bond-Preston <jackbondpreston@outlook.com>
- * Copyright © 2023 Alex Utter <ooterness@gmail.com>
- * Copyright © 2023 Emilien Court <emilien.court@telecomnancy.net>
- * Copyright © 2023 Jeremy O'Brien <neutral@fastmail.com>
- * Copyright © 2024 Matheus Afonso Martins Moreira <matheus.a.m.moreira@gmail.com> (https://www.matheusmoreira.com/)
- * Copyright © 2024 Max Zettlmeißl <max@zettlmeissl.de>
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "totp_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "TOTP.h"
-#include "base32.h"
-
-#ifndef TOTP_FACE_MAX_KEY_LENGTH
-#define TOTP_FACE_MAX_KEY_LENGTH 128
-#endif
-
-typedef struct {
-    unsigned char labels[2];
-    hmac_alg algorithm;
-    uint32_t period;
-    size_t encoded_key_length;
-    unsigned char *encoded_key;
-} totp_t;
-
-#define CREDENTIAL(label, key_array, algo, timestep) \
-    (const totp_t) { \
-        .encoded_key = ((unsigned char *) key_array), \
-        .encoded_key_length = sizeof(key_array) - 1, \
-        .period = (timestep), \
-        .labels = (#label), \
-        .algorithm = (algo), \
-    }
-
-////////////////////////////////////////////////////////////////////////////////
-// Enter your TOTP key data below
-
-static totp_t credentials[] = {
-    CREDENTIAL(2F, "JBSWY3DPEHPK3PXP", SHA1, 30),
-    CREDENTIAL(AC, "JBSWY3DPEHPK3PXP", SHA1, 30),
-};
-
-// END OF KEY DATA.
-////////////////////////////////////////////////////////////////////////////////
-
-static inline totp_t *totp_at(size_t i) {
-    return &credentials[i];
-}
-
-static inline totp_t *totp_current(totp_state_t *totp_state) {
-    return totp_at(totp_state->current_index);
-}
-
-static inline size_t totp_total(void) {
-    return sizeof(credentials) / sizeof(*credentials);
-}
-
-static void totp_validate_key_lengths(void) {
-    for (size_t n = totp_total(), i = 0; i < n; ++i) {
-        totp_t *totp = totp_at(i);
-
-        if (UNBASE32_LEN(totp->encoded_key_length) > TOTP_FACE_MAX_KEY_LENGTH) {
-            // Key exceeds static limits, turn it off by zeroing the length
-            totp->encoded_key_length = 0;
-        }
-    }
-}
-
-static void totp_generate(totp_state_t *totp_state) {
-    totp_t *totp = totp_current(totp_state);
-
-    if (totp->encoded_key_length <= 0) {
-        // Key exceeded static limits and was turned off
-        totp_state->current_decoded_key_length = 0;
-        return;
-    }
-
-    totp_state->current_decoded_key_length = base32_decode(totp->encoded_key, totp_state->current_decoded_key);
-
-    if (totp_state->current_decoded_key_length == 0) {
-        // Decoding failed for some reason
-        // Not a base 32 string?
-        return;
-    }
-
-    TOTP(
-        totp_state->current_decoded_key,
-        totp_state->current_decoded_key_length,
-        totp->period,
-        totp->algorithm
-    );
-}
-
-static void totp_display_error(totp_state_t *totp_state) {
-    char buf[10 + 1];
-    totp_t *totp = totp_current(totp_state);
-
-    snprintf(buf, sizeof(buf), "%c%c  ERROR ", totp->labels[0], totp->labels[1]);
-    watch_display_string(buf, 0);
-}
-
-static void totp_display_code(totp_state_t *totp_state) {
-    char buf[14];
-    div_t result;
-    uint8_t valid_for;
-    totp_t *totp = totp_current(totp_state);
-
-    result = div(totp_state->timestamp, totp->period);
-    if (result.quot != totp_state->steps) {
-        totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
-        totp_state->steps = result.quot;
-    }
-    valid_for = totp->period - result.rem;
-    sprintf(buf, "%c%c%2d%06lu", totp->labels[0], totp->labels[1], valid_for, totp_state->current_code);
-
-    watch_display_string(buf, 0);
-}
-
-static void totp_display(totp_state_t *totp_state) {
-    if (totp_state->current_decoded_key_length > 0) {
-        totp_display_code(totp_state);
-    } else {
-        totp_display_error(totp_state);
-    }
-}
-
-static void totp_generate_and_display(totp_state_t *totp_state) {
-    totp_generate(totp_state);
-    totp_display(totp_state);
-}
-
-static inline uint32_t totp_compute_base_timestamp() {
-    return watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_get_current_timezone_offset());
-}
-
-void totp_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    totp_validate_key_lengths();
-
-    if (*context_ptr == NULL) {
-        totp_state_t *totp = malloc(sizeof(totp_state_t));
-        totp->current_decoded_key = malloc(TOTP_FACE_MAX_KEY_LENGTH);
-        *context_ptr = totp;
-    }
-}
-
-void totp_face_activate(void *context) {
-
-    totp_state_t *totp = (totp_state_t *) context;
-
-    totp->timestamp = totp_compute_base_timestamp();
-    totp->steps = 0;
-    totp->current_code = 0;
-    totp->current_index = 0;
-    totp->current_decoded_key_length = 0;
-    // totp->current_decoded_key is already initialized in setup
-
-    totp_generate_and_display(totp);
-}
-
-bool totp_face_loop(movement_event_t event, void *context) {
-
-    totp_state_t *totp_state = (totp_state_t *) context;
-
-    switch (event.event_type) {
-        case EVENT_TICK:
-            totp_state->timestamp++;
-            // fall through
-        case EVENT_ACTIVATE:
-            totp_display(totp_state);
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if ((size_t)totp_state->current_index + 1 < totp_total()) {
-                totp_state->current_index++;
-            } else {
-                // wrap around to first key
-                totp_state->current_index = 0;
-            }
-
-            totp_generate_and_display(totp_state);
-
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (totp_state->current_index == 0) {
-                // Wrap around to the last credential.
-                totp_state->current_index = totp_total() - 1;
-            } else {
-                totp_state->current_index--;
-            }
-
-            totp_generate_and_display(totp_state);
-
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-        case EVENT_ALARM_LONG_PRESS:
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            movement_illuminate_led();
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void totp_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/totp_face.h b/movement/watch_faces/complication/totp_face.h
deleted file mode 100644 (file)
index 0dc1b8e..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-
-/*
- * MIT License
- *
- * Copyright © 2021 Wesley Ellis (https://github.com/tahnok)
- * Copyright © 2021-2022 Joey Castillo <joeycastillo@utexas.edu>
- * Copyright © 2022 Alexsander Akers <me@a2.io>
- * Copyright © 2022 Jack Bond-Preston <jackbondpreston@outlook.com>
- * Copyright © 2023 Alex Utter <ooterness@gmail.com>
- * Copyright © 2024 Matheus Afonso Martins Moreira <matheus.a.m.moreira@gmail.com> (https://www.matheusmoreira.com/)
- * Copyright © 2024 Max Zettlmeißl <max@zettlmeissl.de>
- *
- * 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 TOTP_FACE_H_
-#define TOTP_FACE_H_
-
-/*
- * TOTP face
- * Time-based one-time password (TOTP) generator
- *
- * Generate one-time passwords often used for two-factor authentication.
- * The secret key must be set by hand, by editing "totp_face.c".
- *
- * Available algorithms:
- *  o SHA1 (most TOTP codes use this)
- *  o SHA224
- *  o SHA256
- *  o SHA384
- *  o SHA512
- *
- * Instructions:
- *  o Find your secret key(s).
- *      o Use https://github.com/susam/mintotp to generate test codes for
- *        verification
- *  o Edit global `credentials` variable in "totp_face.c" to configure your
- *    TOTP credentials. The file includes two examples that you can use as a
- *    reference. Credentials are added with the `CREDENTIAL` macro in the form
- *    `CREDENTIAL(label, key, algorithm, timestep)` where:
- *      o `label` is a 2 character label that is displayed in the weekday digits
- *        to identify the TOTP credential.
- *      o `key` is a string with the base32 encoded secret.
- *      o `algorithm` is one of the supported hashing algorithms listed above.
- *      o `timestep` is how often the TOTP refreshes in seconds. This is usually
- *        30 seconds.
- *
- * If you have more than one secret key, press ALARM to cycle through them.
- * Press LIGHT to cycle in the other direction or keep it pressed longer to
- * activate the light.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint32_t timestamp;
-    uint8_t steps;
-    uint32_t current_code;
-    uint8_t current_index;
-    uint8_t *current_decoded_key;
-    size_t current_decoded_key_length;
-} totp_state_t;
-
-void totp_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void totp_face_activate(void *context);
-bool totp_face_loop(movement_event_t event, void *context);
-void totp_face_resign(void *context);
-
-#define totp_face ((const watch_face_t){ \
-    totp_face_setup, \
-    totp_face_activate, \
-    totp_face_loop, \
-    totp_face_resign, \
-    NULL, \
-})
-
-#endif // TOTP_FACE_H_
diff --git a/movement/watch_faces/complication/totp_face_lfs.c b/movement/watch_faces/complication/totp_face_lfs.c
deleted file mode 100644 (file)
index 439aca3..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Wesley Ellis (https://github.com/tahnok)
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "TOTP.h"
-#include "base32.h"
-
-#include "watch.h"
-#include "watch_utility.h"
-#include "filesystem.h"
-
-#include "totp_face_lfs.h"
-
-#define MAX_TOTP_RECORDS 30
-#define MAX_TOTP_SECRET_SIZE 128
-#define TOTP_FILE "totp_uris.txt"
-
-const char* TOTP_URI_START = "otpauth://totp/";
-
-struct totp_record {
-    char label[2];
-    hmac_alg algorithm;
-    uint8_t period;
-    uint8_t secret_size;
-
-    union {
-        uint8_t *secret;
-        struct {
-            uint16_t file_secret_offset;
-            uint16_t file_secret_length;
-        };
-    };
-};
-
-/* This is used if we're not storing all the secrets but instead
- * calculating them on demand. Avoids malloc in normal operation.
- */
-static uint8_t current_secret[MAX_TOTP_SECRET_SIZE];
-
-static struct totp_record totp_records[MAX_TOTP_RECORDS];
-static uint8_t num_totp_records = 0;
-
-static void init_totp_record(struct totp_record *totp_record) {
-    totp_record->label[0] = 'A';
-    totp_record->label[1] = 'A';
-    totp_record->algorithm = SHA1;
-    totp_record->period = 30;
-    totp_record->secret_size = 0;
-}
-
-static bool totp_face_lfs_read_param(struct totp_record *totp_record, char *param, char *value) {
-    if (!strcmp(param, "issuer")) {
-        if (value[0] == '\0' || value[1] == '\0') {
-            printf("TOTP issuer must be >= 2 chars, got '%s'\n", value);
-            return false;
-        }
-        totp_record->label[0] = value[0];
-        totp_record->label[1] = value[1];
-    } else if (!strcmp(param, "secret")) {
-        totp_record->file_secret_length = strlen(value);
-        if (UNBASE32_LEN(totp_record->file_secret_length) > MAX_TOTP_SECRET_SIZE) {
-            printf("TOTP secret too long: %s\n", value);
-            return false;
-        }
-        totp_record->secret_size = base32_decode((unsigned char *)value, current_secret);
-        if (totp_record->secret_size == 0) {
-            printf("TOTP can't decode secret: %s\n", value);
-            return false;
-        }
-    } else if (!strcmp(param, "digits")) {
-        if (!strcmp(param, "6")) {
-            printf("TOTP got %s, not 6 digits\n", value);
-            return false;
-        }
-    } else if (!strcmp(param, "period")) {
-        totp_record->period = atoi(value);
-        if (totp_record->period == 0) {
-            printf("TOTP invalid period %s\n", value);
-            return false;
-        }
-    } else if (!strcmp(param, "algorithm")) {
-        if (!strcmp(value, "SHA1")) {
-            totp_record->algorithm = SHA1;
-        }
-        else if (!strcmp(value, "SHA224")) {
-            totp_record->algorithm = SHA224;
-        }
-        else if (!strcmp(value, "SHA256")) {
-            totp_record->algorithm = SHA256;
-        }
-        else if (!strcmp(value, "SHA384")) {
-            totp_record->algorithm = SHA384;
-        }
-        else if (!strcmp(value, "SHA512")) {
-            totp_record->algorithm = SHA512;
-        }
-        else {
-            printf("TOTP ignored due to algorithm %s\n", value);
-            return false;
-        }
-    }
-
-    return true;
-}
-
-static void totp_face_lfs_read_file(char *filename) {
-    // For 'format' of file, see comment at top.
-    const size_t uri_start_len = strlen(TOTP_URI_START);
-
-    if (!filesystem_file_exists(filename)) {
-        printf("TOTP file error: %s\n", filename);
-        return;
-    }
-
-    char line[256];
-    int32_t offset = 0, old_offset = 0;
-    while (old_offset = offset, filesystem_read_line(filename, line, &offset, 255) && strlen(line)) {
-        if (num_totp_records == MAX_TOTP_RECORDS) {
-            printf("TOTP max records: %d\n", MAX_TOTP_RECORDS);
-            break;
-        }
-
-        // Check that it looks like a URI
-        if (strncmp(TOTP_URI_START, line, uri_start_len)) {
-            printf("TOTP invalid uri start: %s\n", line);
-            continue;
-        }
-
-        // Check that we can find a '?' (to start our parameters)
-        char *param;
-        char *param_saveptr = NULL;
-        char *params = strchr(line + uri_start_len, '?');
-        if (params == NULL) {
-            printf("TOTP no params: %s\n", line);
-            continue;
-        }
-
-        // Process the parameters and put them in the record
-        init_totp_record(&totp_records[num_totp_records]);
-        bool error = false;
-        param = strtok_r(params + 1, "&", &param_saveptr);
-        do {
-            char *param_middle = strchr(param, '=');
-            *param_middle = '\0';
-            if (totp_face_lfs_read_param(&totp_records[num_totp_records], param, param_middle + 1)) {
-                if (!strcmp(param, "secret")) {
-                    totp_records[num_totp_records].file_secret_offset = old_offset + (param_middle + 1 - line);
-                }
-            } else {
-                error = true;
-            }
-        } while ((param = strtok_r(NULL, "&", &param_saveptr)));
-
-        if (error) {
-            totp_records[num_totp_records].secret_size = 0;
-            continue;
-        }
-
-        // If we found a probably valid TOTP record, keep it.
-        if (totp_records[num_totp_records].secret_size) {
-            num_totp_records += 1;
-        } else {
-            printf("TOTP missing secret: %s\n", line);
-        }
-    }
-}
-
-void totp_face_lfs_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(totp_lfs_state_t));
-    }
-
-#if !(__EMSCRIPTEN__)
-    if (num_totp_records == 0) {
-        totp_face_lfs_read_file(TOTP_FILE);
-    }
-#endif
-}
-
-static uint8_t *totp_face_lfs_get_file_secret(struct totp_record *record) {
-    char buffer[BASE32_LEN(MAX_TOTP_SECRET_SIZE) + 1];
-    int32_t file_secret_offset = record->file_secret_offset;
-
-    /* TODO filesystem_read_line is quite inefficient. Consider writing a new function,
-     * and keeping the file open?
-     */
-    if (!filesystem_read_line(TOTP_FILE, buffer, &file_secret_offset, record->file_secret_length + 1)) {
-        /* Shouldn't happen at this point. Return current_secret, which is misleading but will not cause a crash. */
-        printf("TOTP can't read expected secret from totp_uris.txt (failed readline)\n");
-        return current_secret;
-    }
-    if (base32_decode((unsigned char *)buffer, current_secret) != record->secret_size) {
-        printf("TOTP can't properly decode secret '%s' from totp_uris.txt; failed at offset %d; read to %ld\n", buffer, record->file_secret_offset, file_secret_offset);
-    }
-    return current_secret;
-}
-
-static void totp_face_set_record(totp_lfs_state_t *totp_state, int i) {
-    struct totp_record *record;
-
-    if (num_totp_records == 0 && i >= num_totp_records) {
-        return;
-    }
-
-    totp_state->current_index = i;
-    record = &totp_records[i];
-
-    TOTP(
-        totp_face_lfs_get_file_secret(record),
-        record->secret_size,
-        record->period,
-        record->algorithm
-    );
-    totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
-    totp_state->steps = totp_state->timestamp / record->period;
-}
-
-void totp_face_lfs_activate(void *context) {
-    memset(context, 0, sizeof(totp_lfs_state_t));
-    totp_lfs_state_t *totp_state = (totp_lfs_state_t *)context;
-
-#if __EMSCRIPTEN__
-    if (num_totp_records == 0) {
-        // Doing this here rather than in setup makes things a bit more pleasant in the simulator, since there's no easy way to trigger
-        // setup again after uploading the data.
-        totp_face_lfs_read_file(TOTP_FILE);
-    }
-#endif
-
-    totp_state->timestamp = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_get_current_timezone_offset());
-    totp_face_set_record(totp_state, 0);
-}
-
-static void totp_face_display(totp_lfs_state_t *totp_state) {
-    uint8_t index = totp_state->current_index;
-    char buf[14];
-
-    if (num_totp_records == 0) {
-        watch_display_string("No2F Codes", 0);
-        return;
-    }
-
-    div_t result = div(totp_state->timestamp, totp_records[index].period);
-    if (result.quot != totp_state->steps) {
-        totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
-        totp_state->steps = result.quot;
-    }
-    uint8_t valid_for = totp_records[index].period - result.rem;
-
-    sprintf(buf, "%c%c%2d%06lu", totp_records[index].label[0], totp_records[index].label[1], valid_for, totp_state->current_code);
-
-    watch_display_string(buf, 0);
-}
-
-bool totp_face_lfs_loop(movement_event_t event, void *context) {
-
-    totp_lfs_state_t *totp_state = (totp_lfs_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_TICK:
-            totp_state->timestamp++;
-            totp_face_display(totp_state);
-            break;
-        case EVENT_ACTIVATE:
-            totp_face_display(totp_state);
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            totp_face_set_record(totp_state, (totp_state->current_index + 1) % num_totp_records);
-            totp_face_display(totp_state);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            totp_face_set_record(totp_state, (totp_state->current_index + num_totp_records - 1) % num_totp_records);
-            totp_face_display(totp_state);
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-        case EVENT_ALARM_LONG_PRESS:
-        case EVENT_LIGHT_BUTTON_DOWN:
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            movement_illuminate_led();
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void totp_face_lfs_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/complication/totp_face_lfs.h b/movement/watch_faces/complication/totp_face_lfs.h
deleted file mode 100644 (file)
index 16276a2..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Wesley Ellis (https://github.com/tahnok)
- *
- * 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 TOTP_FACE_LFS_H_
-#define TOTP_FACE_LFS_H_
-
-/*
- * TOTP-LFS face
- * Time-based one-time password (TOTP) generator using LFS
- *
- * Reads from a file "totp_uris.txt", containing a single secret key in a
- * series of URLs. Each line is what's in a QR code, e.g.:
- *   otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
- *   otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30
- *
- * This is also the same as what Aegis exports in plain-text format.
- * This face performs minimal sanitisation of input, however, and you
- * will only see errors if you use the simulator or view the serial console.
- *
- * At the moment, to get the records onto the filesystem, start a serial connection and do:
- *   echo otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example > totp_uris.txt
- *   echo otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 >> totp_uris.txt
- * (note the double >> in the second one)
- *
- * You may want to customise the characters that appear to identify the 2FA
- * code. These are just the first two characters of the issuer, and it's fine
- * to modify the URI.
- *
- * If you have more than one secret key, press ALARM to cycle through them.
- * Press LIGHT to cycle in the other direction or keep it pressed longer to
- * activate the light.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint32_t timestamp;
-    uint8_t steps;
-    uint32_t current_code;
-    uint8_t current_index;
-} totp_lfs_state_t;
-
-void totp_face_lfs_setup(uint8_t watch_face_index, void ** context_ptr);
-void totp_face_lfs_activate(void *context);
-bool totp_face_lfs_loop(movement_event_t event, void *context);
-void totp_face_lfs_resign(void *context);
-
-#define totp_face_lfs ((const watch_face_t){ \
-    totp_face_lfs_setup, \
-    totp_face_lfs_activate, \
-    totp_face_lfs_loop, \
-    totp_face_lfs_resign, \
-    NULL, \
-})
-
-#endif // TOTP_FACE_LFS_H_
diff --git a/movement/watch_faces/complication/tuning_tones_face.c b/movement/watch_faces/complication/tuning_tones_face.c
deleted file mode 100644 (file)
index 686de68..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Per Waagø
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "tuning_tones_face.h"
-
-/*
-
-    This face plays a tone that can be used as a reference when tuning
-    musical instrument.
-
-    - The alarm button (short press) starts and stops the tone
-    - The light button (short press) changes which note is played. The name
-    of the note is shown in the display.
-
-*/
-
-typedef struct Note {
-    watch_buzzer_note_t note;
-    char * name;
-} Note;
-
-static Note notes[] = {
-    { .note = BUZZER_NOTE_C5, .name = "C " },
-    { .note = BUZZER_NOTE_C5SHARP_D5FLAT, .name = "Db" },
-    { .note = BUZZER_NOTE_D5, .name = "D " },
-    { .note = BUZZER_NOTE_D5SHARP_E5FLAT, .name = "Eb" },
-    { .note = BUZZER_NOTE_E5, .name = "E " },
-    { .note = BUZZER_NOTE_F5, .name = "F " },
-    { .note = BUZZER_NOTE_F5SHARP_G5FLAT, .name = "Gb" },
-    { .note = BUZZER_NOTE_G5, .name = "G " },
-    { .note = BUZZER_NOTE_G5SHARP_A5FLAT, .name = "Ab" },
-    { .note = BUZZER_NOTE_A5, .name = "A " },
-    { .note = BUZZER_NOTE_A5SHARP_B5FLAT, .name = "Bb" },
-    { .note = BUZZER_NOTE_B5, .name = "B " },
-};
-
-static size_t note_count = sizeof notes / sizeof *notes;
-
-static void draw(tuning_tones_state_t *state)
-{
-    watch_display_string(notes[state->note_ind].name, 8);
-}
-
-void tuning_tones_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        tuning_tones_state_t *state = malloc(sizeof *state);
-        memset(state, 0, sizeof *state);
-        state->note_ind = 9;
-        *context_ptr = state;
-    }
-}
-
-void tuning_tones_face_activate(void *context) {
-    (void) context;
-}
-
-static void update_buzzer(const tuning_tones_state_t *state)
-{
-    if (state->playing) {
-        watch_set_buzzer_off();
-        watch_set_buzzer_period_and_duty_cycle(NotePeriods[notes[state->note_ind].note], 25);
-        watch_set_buzzer_on();
-    }
-}
-
-bool tuning_tones_face_loop(movement_event_t event, void *context) {
-    tuning_tones_state_t *state = (tuning_tones_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            draw(state);
-            break;
-        case EVENT_TICK:
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            state->note_ind++;
-            if (state->note_ind == note_count) {
-                state->note_ind = 0;
-            }
-            update_buzzer(state);
-            draw(state);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-            state->playing = !state->playing;
-            if (!state->playing) {
-                watch_set_buzzer_off();
-            } else {
-                update_buzzer(state);
-            }
-        case EVENT_ALARM_BUTTON_UP:
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return !state->playing;
-}
-
-void tuning_tones_face_resign(void *context) {
-    tuning_tones_state_t *state = (tuning_tones_state_t *)context;
-
-    if (state->playing) {
-        state->playing = false;
-        watch_set_buzzer_off();
-    }
-}
diff --git a/movement/watch_faces/complication/tuning_tones_face.h b/movement/watch_faces/complication/tuning_tones_face.h
deleted file mode 100644 (file)
index 08672a5..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Per Waagø
- *
- * 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 TUNING_TONES_FACE_H_
-#define TUNING_TONES_FACE_H_
-
-#include "movement.h"
-
-/*
- * A DESCRIPTION OF YOUR WATCH FACE
- *
- * and a description of how use it
- *
- */
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    bool playing;
-    size_t note_ind;
-} tuning_tones_state_t;
-
-void tuning_tones_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void tuning_tones_face_activate(void *context);
-bool tuning_tones_face_loop(movement_event_t event, void *context);
-void tuning_tones_face_resign(void *context);
-
-#define tuning_tones_face ((const watch_face_t){ \
-    tuning_tones_face_setup, \
-    tuning_tones_face_activate, \
-    tuning_tones_face_loop, \
-    tuning_tones_face_resign, \
-    NULL, \
-})
-
-#endif // TUNING_TONES_FACE_H_
-
diff --git a/movement/watch_faces/complication/wake_face.c b/movement/watch_faces/complication/wake_face.c
deleted file mode 100644 (file)
index 30982da..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Josh Berson, building on Wesley Ellis’ countdown_face.c
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "wake_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-
-//
-// Private
-//
-
-static
-void _wake_face_update_display(wake_face_state_t *state) {
-    uint8_t hour = state->hour;
-
-    watch_clear_display();
-    if ( movement_clock_mode_24h() )
-        watch_set_indicator(WATCH_INDICATOR_24H);
-    else {
-        if ( hour >= 12 )
-            watch_set_indicator(WATCH_INDICATOR_PM);
-        hour = hour % 12 ? hour % 12 : 12;
-    }
-
-    if ( state->mode )
-        watch_set_indicator(WATCH_INDICATOR_BELL);
-
-    static char lcdbuf[11];
-    sprintf(lcdbuf, "WA  %2d%02d  ", hour, state->minute);
-
-    watch_set_colon();
-    watch_display_string(lcdbuf, 0);
-}
-
-//
-// Exported
-//
-
-void wake_face_setup(uint8_t watch_face_index, void **context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(wake_face_state_t));
-        wake_face_state_t *state = (wake_face_state_t *)*context_ptr;
-        memset(*context_ptr, 0, sizeof(wake_face_state_t));
-
-        state->hour = 5;
-        state->minute = 0;
-        state->mode = 0;
-    }
-}
-
-void wake_face_activate(void *context) {
-    (void) context;
-}
-void wake_face_resign(void *context) {
-    (void) context;
-}
-
-movement_watch_face_advisory_t wake_face_advise(void *context) {
-    wake_face_state_t *state = (wake_face_state_t *)context;
-    movement_watch_face_advisory_t retval = { 0 };
-
-    if ( state->mode ) {
-        watch_date_time_t now = watch_rtc_get_date_time();
-        retval.wants_background_task = state->hour==now.unit.hour && state->minute==now.unit.minute;
-        // We’re at the mercy of the advise handler
-        // In Safari, the emulator triggers at the ›end‹ of the minute
-        // Converting to Unix timestamps and taking a difference between now and wake
-        // is not an easy win — because the timestamp for wake has to rely on now
-        // for its date. So first we’d have to see if the TOD of wake is after that
-        // of now. If it is, take tomorrow’s date, calculating month and year rollover
-        // if need be.
-    }
-
-    return retval;
-}
-
-bool wake_face_loop(movement_event_t event, void *context) {
-    wake_face_state_t *state = (wake_face_state_t *)context;
-
-    switch (event.event_type) {
-    case EVENT_ACTIVATE:
-    case EVENT_TICK:
-        _wake_face_update_display(state);
-        break;
-    case EVENT_LIGHT_BUTTON_UP:
-        state->hour = (state->hour + 1) % 24;
-        _wake_face_update_display(state);
-        break;
-    case EVENT_LIGHT_LONG_PRESS:
-        state->hour = (state->hour + 6) % 24;
-        _wake_face_update_display(state);
-        break;
-    case EVENT_ALARM_BUTTON_UP:
-        state->minute = (state->minute + 10) % 60;
-        _wake_face_update_display(state);
-        break;
-    case EVENT_ALARM_LONG_PRESS:
-        state->mode ^= 1;
-        _wake_face_update_display(state);
-        break;
-    case EVENT_BACKGROUND_TASK:
-        movement_play_alarm();
-            // 2022-07-23: Thx @joeycastillo for the dedicated “alarm” signal
-        break;
-    case EVENT_TIMEOUT:
-        movement_move_to_face(0);
-        break;
-    case EVENT_LOW_ENERGY_UPDATE:
-        break;
-    case EVENT_LIGHT_BUTTON_DOWN:
-        // don't light up every time light is hit
-        break;
-    default:
-        movement_default_loop_handler(event);
-        break;
-    }
-
-    return true;
-}
diff --git a/movement/watch_faces/complication/wake_face.h b/movement/watch_faces/complication/wake_face.h
deleted file mode 100644 (file)
index 008a540..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Josh Berson
- *
- * 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 WAKE_FACE_H_
-#define WAKE_FACE_H_
-
-/*
- * WAKE daily alarm face
- *
- * Basic daily alarm clock face. Seems useful if nothing else in the interest
- * of feature parity with the F-91W’s OEM module, 593.
- *
- * Also experiments with caret-free UI: One button cycles hours, the other
- * minutes, so there’s no toggling between display and adjust modes and no
- * cycling the caret through the UI.
- *   º LIGHT advances hour by 1
- *   º LIGHT long press advances hour by 6
- *   º ALARM advances minute by 10
- *   º ALARM long press cycles through signal modes (just one at the moment)
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint32_t hour : 5;
-    uint32_t minute : 6;
-    uint32_t mode : 1;
-} wake_face_state_t;
-
-void wake_face_setup(uint8_t watch_face_index, void **context_ptr);
-void wake_face_activate(void *context);
-bool wake_face_loop(movement_event_t event, void *context);
-void wake_face_resign(void *context);
-movement_watch_face_advisory_t wake_face_advise(void *context);
-
-#define wake_face ((const watch_face_t){ \
-    wake_face_setup, \
-    wake_face_activate, \
-    wake_face_loop, \
-    wake_face_resign, \
-    wake_face_advise \
-})
-
-#endif // WAKE_FACE_H_
-
diff --git a/movement/watch_faces/complication/wareki_face.c b/movement/watch_faces/complication/wareki_face.c
deleted file mode 100644 (file)
index ec87659..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include "wareki_face.h"
-#include "filesystem.h"
-#include "watch_utility.h"
-
-
-//Long press status flag
-static bool _alarm_button_press;
-static bool _light_button_press;
-
-
-void wareki_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    
-    //printf("wareki_setup() \n");
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(wareki_state_t));
-        memset(*context_ptr, 0, sizeof(wareki_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-
-}
-
-
-// splash view
-static void draw_wareki_splash(wareki_state_t *state) {    
-    (void) state;
-    char buf[11];
-    
-    watch_clear_colon();
-
-    sprintf(buf, "%s","wa  ------");
-    
-    watch_display_string(buf, 0);
-}
-
-
-//draw year and Japanese wareki
-static void draw_year_and_wareki(wareki_state_t *state) {
-    char buf[27];
-    
-    if(state->disp_year < REIWA_GANNEN){
-        //Heisei
-        sprintf(buf, " h%2d%4d  ", (int)state->disp_year - HEISEI_GANNEN + 1, (int)state->disp_year);
-    }
-    else{
-        //Reiwa
-        sprintf(buf, " r%2d%4d  ", (int)state->disp_year - REIWA_GANNEN + 1 , (int)state->disp_year);
-    }
-    watch_display_string(buf, 0);
-}
-
-
-void wareki_activate(void *context) {
-
-    //printf("wareki_activate() \n");
-
-    wareki_state_t *state = (wareki_state_t *)context;
-
-    if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
-
-    state->active = false;
-
-    _alarm_button_press = false;
-    _light_button_press = false;
-
-    state->real_year = watch_rtc_get_date_time().unit.year + WATCH_RTC_REFERENCE_YEAR;
-    state->start_year = state->real_year;
-    state->disp_year = state->real_year;
-
-    movement_request_tick_frequency(1);
-}
-
-
-void addYear(wareki_state_t* state,int count){
-
-    state->disp_year = state->disp_year + count;
-
-    if(state->disp_year > REIWA_LIMIT ){
-        state->disp_year = REIWA_LIMIT;
-    } 
-    else{
-        //watch_buzzer_play_note(BUZZER_NOTE_C8, 30);        
-    }
-}
-
-
-void subYear(wareki_state_t* state,int count){
-
-    state->disp_year = state->disp_year - count;
-
-    if(state->disp_year < 1989 ){
-        state->disp_year = 1989;
-    } 
-    else{
-        //watch_buzzer_play_note(BUZZER_NOTE_C7, 30);
-    }
-}
-
-
-bool wareki_loop(movement_event_t event, void *context) {
-    wareki_state_t *state = (wareki_state_t *)context;
-
-    state->real_year = watch_rtc_get_date_time().unit.year + WATCH_RTC_REFERENCE_YEAR;
-
-    if( state->real_year != state->start_year ){
-        state->start_year = state->real_year;
-        state->disp_year = state->real_year;
-    }
-
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            draw_wareki_splash(state);
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            movement_move_to_next_face();
-            break;
-
-        case EVENT_LOW_ENERGY_UPDATE:
-        case EVENT_TICK:          
-
-            //printf("tick %d\n",state->disp_year );
-
-            if (_alarm_button_press && HAL_GPIO_BTN_ALARM_read()){
-                //printf("ALARM ON\n");
-            }
-            else{
-                //printf("ALARM OFF\n");
-                _alarm_button_press = false;
-            }
-
-            if (_light_button_press && HAL_GPIO_BTN_LIGHT_read()){
-                //printf("LIGHT ON\n");
-            }
-            else{
-                //printf("LIGHT OFF\n");
-                _light_button_press = false;
-            }
-
-            if (_alarm_button_press) {
-                addYear(state,1);
-            }
-            if (_light_button_press) {
-                subYear(state,1);
-            }
-            
-            draw_year_and_wareki(state);
-
-            break;
-
-        case EVENT_LIGHT_BUTTON_DOWN:
-            //printf("LIGHT DOWN\n");
-            subYear(state,1);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            //printf("LIGHTPRESS \n"); 
-            _light_button_press = true;
-            movement_request_tick_frequency(8);
-            break;
-        case EVENT_LIGHT_LONG_UP:
-            //printf("LIGHTPRESS UP\n");
-            _light_button_press = false;
-            movement_request_tick_frequency(4);
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            //printf("LIGHT UP\n");
-            _light_button_press = false;
-            movement_request_tick_frequency(4);
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-            //printf("ALARM DOWN\n");
-            addYear(state,1);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            //printf("LONGPRESS \n");
-            _alarm_button_press = true;
-            movement_request_tick_frequency(8);
-            break;
-        case EVENT_ALARM_LONG_UP:
-            //printf("LONGPRESS UP\n");
-            _alarm_button_press = false;
-            movement_request_tick_frequency(4);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            //printf("ALARM UP\n");
-            movement_request_tick_frequency(4);
-            break;
-
-        case EVENT_TIMEOUT:
-            //printf("time out ! \n");
-            movement_move_to_face(0);
-
-
-            break;
-        //case EVENT_LOW_ENERGY_UPDATE:
-            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
-            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
-            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
-            // watch_start_sleep_animation(500);
-            //break;
-        default:
-            // Movement's default loop handler will step in for any cases you don't handle above:
-            // * EVENT_LIGHT_BUTTON_DOWN lights the LED
-            // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
-            // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
-            // You can override any of these behaviors by adding a case for these events to this switch statement.
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void wareki_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/complication/wareki_face.h b/movement/watch_faces/complication/wareki_face.h
deleted file mode 100644 (file)
index 4dffc46..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef WAREKI_FACE_H_
-#define WAREKI_FACE_H_
-
-#include "movement.h"
-
-#define REIWA_LIMIT 2018 + 31
-#define REIWA_GANNEN  2019
-#define HEISEI_GANNEN 1989
-
-typedef struct {
-    bool active;
-    uint32_t disp_year;     //Current displayed year 
-    uint32_t start_year;    //Year when this screen was launched
-    uint32_t real_year;     //The actual current year     
-} wareki_state_t;
-
-
-void wareki_setup(uint8_t watch_face_index, void ** context_ptr);
-void wareki_activate(void *context);
-bool wareki_loop(movement_event_t event, void *context);
-void wareki_resign(void *context);
-void addYear(wareki_state_t* state,int count);
-void subYear(wareki_state_t* state,int count);
-
-#define wareki_face ((const watch_face_t){ \
-    wareki_setup, \
-    wareki_activate, \
-    wareki_loop, \
-    wareki_resign, \
-    NULL, \
-})
-
-#endif // WAREKI_FACE_H_
-
diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c
deleted file mode 100644 (file)
index a93a0e0..0000000
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 <David Volovskiy>
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "wordle_face.h"
-#include "watch_utility.h"
-
-static uint32_t get_random(uint32_t max) {
-    #if __EMSCRIPTEN__
-    return rand() % max;
-    #else
-    return arc4random_uniform(max);
-    #endif
-}
-
-static uint8_t get_first_pos(WordleLetterResult *word_elements_result) {
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        if (word_elements_result[i] != WORDLE_LETTER_CORRECT)
-            return i;
-    }
-    return 0;
-}
-
-static uint8_t get_next_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) {
-    for (size_t pos = curr_pos; pos < WORDLE_LENGTH;) {
-        if (word_elements_result[++pos] != WORDLE_LETTER_CORRECT)
-            return pos;
-    }
-    return WORDLE_LENGTH;
-}
-
-static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) {
-    if (curr_pos == 0) return 0;
-    for (int8_t pos = curr_pos; pos >= 0;) {
-        if (word_elements_result[--pos] != WORDLE_LETTER_CORRECT)
-            return pos;
-    }
-    return curr_pos;
-}
-
-static void get_next_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters, const bool skip_wrong_letter) {
-    do {
-        if (word_elements[curr_pos] >= WORDLE_NUM_VALID_LETTERS) word_elements[curr_pos] = 0;
-        else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % WORDLE_NUM_VALID_LETTERS;
-    } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]);  
-}
-
-static void get_prev_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters, const bool skip_wrong_letter) {
-    do {
-        if (word_elements[curr_pos] >= WORDLE_NUM_VALID_LETTERS) word_elements[curr_pos] = WORDLE_NUM_VALID_LETTERS - 1;
-        else word_elements[curr_pos] = (word_elements[curr_pos] + WORDLE_NUM_VALID_LETTERS - 1) % WORDLE_NUM_VALID_LETTERS;
-    } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]);  
-}
-
-static void display_letter(wordle_state_t *state, bool display_dash) {
-    char buf[1 + 1];
-    if (state->word_elements[state->position] >= WORDLE_NUM_VALID_LETTERS) {
-        if (display_dash)
-            watch_display_string("-", state->position + 5);
-        else
-            watch_display_string(" ", state->position + 5);
-        return;
-    }
-    sprintf(buf, "%c", _valid_letters[state->word_elements[state->position]]);
-    watch_display_string(buf, state->position + 5);
-}
-
-static void display_all_letters(wordle_state_t *state) {
-    uint8_t prev_pos = state->position;
-    watch_display_string(" ", 4);
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        state->position = i;
-        display_letter(state, false);
-    }
-    state->position = prev_pos;
-}
-
-#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
-static void display_not_in_dict(wordle_state_t *state) {
-    state->curr_screen = SCREEN_NO_DICT;
-    watch_display_string("nodict", 4);
-}
-
-static void display_already_guessed(wordle_state_t *state) {
-    state->curr_screen = SCREEN_ALREADY_GUESSED;
-    watch_display_string("GUESSD", 4);
-}
-
-static uint32_t check_word_in_dict(uint8_t *word_elements) {
-    bool is_exact_match;
-    for (uint16_t i = 0; i < WORDLE_NUM_WORDS; i++) {
-        is_exact_match = true;
-        for (size_t j = 0; j < WORDLE_LENGTH; j++) {
-            if (_valid_letters[word_elements[j]] != _valid_words[i][j]) {
-                is_exact_match = false;
-                break;  
-            }
-        }
-        if (is_exact_match) return i;
-    }
-    for (uint16_t i = 0; i < WORDLE_NUM_POSSIBLE_WORDS; i++) {
-        is_exact_match = true;
-        for (size_t j = 0; j < WORDLE_LENGTH; j++) {
-            if (_valid_letters[word_elements[j]] != _possible_words[i][j]) {
-                is_exact_match = false;
-                break;  
-            }
-        }
-        if (is_exact_match) return WORDLE_NUM_WORDS + i;
-    }
-    return WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS;
-}
-#endif
-
-static bool check_word(wordle_state_t *state) {
-    // Exact
-    bool is_exact_match = true;
-    bool answer_already_accounted[WORDLE_LENGTH] = { false };
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        if (_valid_letters[state->word_elements[i]] == _valid_words[state->curr_answer][i]) {
-            state->word_elements_result[i] = WORDLE_LETTER_CORRECT;
-            answer_already_accounted[i] = true;
-        }
-        else {
-            state->word_elements_result[i] = WORDLE_LETTER_WRONG;
-            is_exact_match = false;
-        }
-    }
-    if (is_exact_match) return true;
-    // Wrong Location
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        if (state->word_elements_result[i] != WORDLE_LETTER_WRONG) continue;
-        for (size_t j = 0; j < WORDLE_LENGTH; j++) {
-            if (answer_already_accounted[j]) continue;
-            if (_valid_letters[state->word_elements[i]] == _valid_words[state->curr_answer][j]) {
-                state->word_elements_result[i] = WORDLE_LETTER_WRONG_LOC;
-                answer_already_accounted[j] = true;
-                break;
-            }
-        }
-    }
-    return false;
-}
-
-static void show_skip_wrong_letter_indicator(bool skipping, WordleScreen curr_screen) {
-    if (curr_screen >= SCREEN_PLAYING) return;
-    if (skipping)
-        watch_display_string("H", 3);
-    else
-        watch_display_string(" ", 3);
-}
-
-static void update_known_wrong_letters(wordle_state_t *state) {
-    bool wrong_loc[WORDLE_NUM_VALID_LETTERS] = {false};
-    // To ignore letters that appear, but are in the wrong location, as letters that are guessed
-    // more often than they appear in the word will display as WORDLE_LETTER_WRONG
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        if (state->word_elements_result[i] == WORDLE_LETTER_WRONG_LOC) {
-            for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) {
-                if (state->word_elements[i] == j)
-                    wrong_loc[j] = true;
-            }
-        }
-    }
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        if (state->word_elements_result[i] == WORDLE_LETTER_WRONG) {
-            for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) {
-                if (state->word_elements[i] == j && !wrong_loc[j])
-                    state->known_wrong_letters[j] = true;
-            }
-        }
-    }
-}
-
-static void display_attempt(uint8_t attempt) {
-    char buf[3];
-    sprintf(buf, "%d", attempt+1);
-    watch_display_string(buf, 3);
-}
-
-static void display_playing(wordle_state_t *state) {
-    state->curr_screen = SCREEN_PLAYING;
-    display_attempt(state->attempt);
-    display_all_letters(state);
-}
-
-static void reset_all_elements(wordle_state_t *state) {
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        state->word_elements[i] = WORDLE_NUM_VALID_LETTERS;
-        state->word_elements_result[i] = WORDLE_LETTER_WRONG;
-    }
-    for (size_t i = 0; i < WORDLE_NUM_VALID_LETTERS; i++){
-        state->known_wrong_letters[i] = false;
-    }
-#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
-    for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) {
-        state->guessed_words[i] = WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS;
-    }
-#endif
-    state->using_random_guess = false;
-    state->attempt = 0;
-}
-
-static void reset_incorrect_elements(wordle_state_t *state) {
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT)
-            state->word_elements[i] = WORDLE_NUM_VALID_LETTERS;
-    }
-}
-
-static void reset_board(wordle_state_t *state) {
-    reset_all_elements(state);
-    state->curr_answer = get_random(WORDLE_NUM_WORDS);
-    watch_clear_colon();
-    state->position = get_first_pos(state->word_elements_result);
-    display_playing(state);
-    watch_display_string(" -", 4);
-#if __EMSCRIPTEN__
-    printf("ANSWER: %s\r\n", _valid_words[state->curr_answer]);
-#endif
-}
-
-static void display_title(wordle_state_t *state) {
-    state->curr_screen = SCREEN_TITLE;
-    watch_display_string("WO  WordLE", 0);
-    show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
-}
-
-#if WORDLE_USE_DAILY_STREAK != 2
-static void display_continue_result(bool continuing) {
-    watch_display_string(continuing ? "y" : "n", 9);
-}
-
-static void display_continue(wordle_state_t *state) {
-    state->curr_screen = SCREEN_CONTINUE;
-    watch_display_string("Cont ", 4);
-    show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
-    display_continue_result(state->continuing);
-}
-#endif
-
-static void display_streak(wordle_state_t *state) {
-    char buf[12];
-    state->curr_screen = SCREEN_STREAK;
-#if WORDLE_USE_DAILY_STREAK == 2
-    if (state->streak > 99)
-        sprintf(buf, "WO  St--dy");
-    else
-        sprintf(buf, "WO  St%2ddy", state->streak);
-#else
-    sprintf(buf, "WO  St%4d", state->streak);
-#endif
-    watch_display_string(buf, 0);
-    watch_set_colon();
-    show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
-}
-
-#if WORDLE_USE_DAILY_STREAK == 2
-static void display_wait(wordle_state_t *state) {
-    state->curr_screen = SCREEN_WAIT;
-    if (state->streak < 40) {
-        char buf[13];
-        sprintf(buf,"WO%2d WaIt ", state->streak);
-        watch_display_string(buf, 0);
-    }
-    else {  // Streak too long to display in top-right
-        watch_display_string("WO   WaIt ", 0);
-    }
-    show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
-}
-#endif
-
-static uint32_t get_day_unix_time(void) {
-    watch_date_time_t now = watch_rtc_get_date_time();
-#if WORDLE_USE_DAILY_STREAK == 2
-    now.unit.hour = now.unit.minute = now.unit.second = 0;
-#endif
-    return watch_utility_date_time_to_unix_time(now, 0);
-}
-
-static void display_lose(wordle_state_t *state, uint8_t subsecond) {
-    char buf[WORDLE_LENGTH + 6];
-    sprintf(buf," L   %s", subsecond % 2 ? _valid_words[state->curr_answer] : "     ");
-    watch_display_string(buf, 0);
-}
-
-static void display_win(wordle_state_t *state, uint8_t subsecond) {
-    (void) state;
-    char buf[13];
-    sprintf(buf," W   %s  ", subsecond % 2 ? "NICE" : "JOb ");
-    watch_display_string(buf, 0);
-}
-
-static bool is_playing(const wordle_state_t *state) {
-    if (state->attempt > 0) return true;
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        if (state->word_elements[i] != WORDLE_NUM_VALID_LETTERS)
-            return true;
-    }
-    return false;
-}
-
-static void display_result(wordle_state_t *state, uint8_t subsecond) {
-    char buf[WORDLE_LENGTH + 1];
-    for (size_t i = 0; i < WORDLE_LENGTH; i++)
-    {
-        switch (state->word_elements_result[i])
-        {
-        case WORDLE_LETTER_WRONG:
-            buf[i] = '-';
-            break;
-        case WORDLE_LETTER_CORRECT:
-            buf[i] = _valid_letters[state->word_elements[i]];
-            break;
-        case WORDLE_LETTER_WRONG_LOC:
-            if (subsecond % 2)
-                buf[i] = ' ';
-            else
-               buf[i] = _valid_letters[state->word_elements[i]]; 
-        default:
-            break;
-        }
-    }
-    watch_display_string(buf, 5);
-}
-
-static bool act_on_btn(wordle_state_t *state, const uint8_t pin) {
-    switch (state->curr_screen)
-    {
-    case SCREEN_RESULT:
-        reset_incorrect_elements(state);
-        state->position = get_first_pos(state->word_elements_result);
-        display_playing(state);
-        return true;
-    case SCREEN_TITLE:
-#if WORDLE_USE_DAILY_STREAK == 2
-        if (state->day_last_game_started == get_day_unix_time()) {
-            display_wait(state);
-        }
-        else if (is_playing(state))
-            display_playing(state);
-        else
-            display_streak(state);
-#else
-        if (is_playing(state)) {
-            state->continuing = true;
-            display_continue(state);
-        }
-        else
-            display_streak(state);
-#endif
-        return true;
-    case SCREEN_STREAK:
-        state->day_last_game_started = get_day_unix_time();
-        reset_board(state);
-        return true;
-    case SCREEN_WIN:
-    case SCREEN_LOSE:
-        display_title(state);
-        return true;
-    case SCREEN_NO_DICT:
-    case SCREEN_ALREADY_GUESSED:
-        state->position = get_first_pos(state->word_elements_result);
-        display_playing(state);
-        return true;
-#if WORDLE_USE_DAILY_STREAK == 2
-    case SCREEN_WAIT:
-        (void) pin;
-        display_title(state);
-        return true;
-#else
-    case SCREEN_CONTINUE:
-        switch (pin)
-        {
-        case BTN_ALARM:
-            if (state->continuing)
-                display_playing(state);
-            else {
-                reset_board(state);
-                state->streak = 0;
-                display_streak(state);
-            }
-            break;
-        case BTN_LIGHT:
-            state->continuing = !state->continuing;
-            display_continue_result(state->continuing);
-            break;
-        }
-        return true;
-#endif
-    default:
-        return false;
-    }
-    return false;
-}
-
-static void get_result(wordle_state_t *state) {
-#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
-    // Check if it's in the dict
-    uint16_t in_dict = check_word_in_dict(state->word_elements);
-    if (in_dict == WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS) {
-        display_not_in_dict(state);
-        return;
-    }
-
-    // Check if already guessed
-    for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) {
-        if(in_dict == state->guessed_words[i]) {
-            display_already_guessed(state);
-            return;
-        }
-    }
-
-    state->guessed_words[state->attempt] = in_dict;
-#endif
-    bool exact_match = check_word(state);
-    if (exact_match) {
-        reset_all_elements(state);
-        state->curr_screen = SCREEN_WIN;
-        if (state->streak < 0x7F)
-            state->streak++;
-#if WORDLE_USE_DAILY_STREAK == 2
-        state->day_last_game_started = get_day_unix_time();  // On the edge-case where we solve the puzzle at midnight
-#endif
-        return;
-    }
-    if (++state->attempt >= WORDLE_MAX_ATTEMPTS) {
-        reset_all_elements(state);
-        state->curr_screen = SCREEN_LOSE;
-        state->streak = 0;
-        return;
-    }
-    update_known_wrong_letters(state);
-    state->curr_screen = SCREEN_RESULT;
-    return;
-}
-
-#if (WORDLE_USE_RANDOM_GUESS != 0)
-static void insert_random_guess(wordle_state_t *state) {
-    uint16_t random_guess;
-    do {  // Don't allow the guess to be the same as the answer
-        random_guess = get_random(_num_random_guess_words);
-    } while (random_guess == state->curr_answer); 
-    for (size_t i = 0; i < WORDLE_LENGTH; i++) {
-        for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++)
-        {
-            if (_valid_words[random_guess][i] == _valid_letters[j])
-                state->word_elements[i] = j;     
-        }
-    } 
-    state->position = WORDLE_LENGTH - 1;
-    display_all_letters(state);
-    state->using_random_guess = true;
-}
-#endif
-
-void wordle_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(wordle_state_t));
-        memset(*context_ptr, 0, sizeof(wordle_state_t));
-        wordle_state_t *state = (wordle_state_t *)*context_ptr;
-        state->curr_screen = SCREEN_TITLE;
-        state->skip_wrong_letter = false;
-        reset_all_elements(state);
-    }
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void wordle_face_activate(void *context) {
-    wordle_state_t *state = (wordle_state_t *)context;
-#if WORDLE_USE_DAILY_STREAK != 0
-    uint32_t now = get_day_unix_time();
-    uint32_t one_day = 60 *60 * 24;
-    if ((WORDLE_USE_DAILY_STREAK == 2 && now >= (state->day_last_game_started + (2*one_day)))
-        || (now >= (state->day_last_game_started + one_day) && is_playing(state))) {
-        state->streak = 0;
-        reset_board(state);
-    }
-#endif
-    state->using_random_guess = false;
-    if (is_playing(state) && state->curr_screen >= SCREEN_RESULT) {
-        reset_incorrect_elements(state);
-        state->position = get_first_pos(state->word_elements_result); 
-    }
-    movement_request_tick_frequency(2);
-    display_title(state);
-}
-
-bool wordle_face_loop(movement_event_t event, void *context) {
-    wordle_state_t *state = (wordle_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_TICK:
-            switch (state->curr_screen)
-            {
-            case SCREEN_PLAYING:
-                if (event.subsecond % 2) {
-                    display_letter(state, true);
-                } else {
-                    watch_display_string(" ", state->position + 5);
-                }
-                break;
-            case SCREEN_RESULT:
-                display_result(state, event.subsecond);
-                break;
-            case SCREEN_LOSE:
-                display_lose(state, event.subsecond);
-                break;
-            case SCREEN_WIN:
-                display_win(state, event.subsecond);
-                break;
-            default:
-                break;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (act_on_btn(state, BTN_LIGHT)) break;
-            get_next_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter);
-            display_letter(state, true);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (state->curr_screen < SCREEN_PLAYING) {
-                state->skip_wrong_letter = !state->skip_wrong_letter;
-                show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen);
-                break;
-            }
-            if (state->curr_screen != SCREEN_PLAYING) break;
-            get_prev_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter);
-            display_letter(state, true);
-            break; 
-        case EVENT_ALARM_BUTTON_UP:
-            if (act_on_btn(state, BTN_ALARM)) break;
-            display_letter(state, true);
-            if (state->word_elements[state->position] == WORDLE_NUM_VALID_LETTERS) break;
-#if (WORDLE_USE_RANDOM_GUESS != 0)
-            if (HAL_GPIO_BTN_LIGHT_read() &&
-            (state->using_random_guess || (state->attempt == 0 && state->position == 0))) {
-                insert_random_guess(state);
-                break;
-            }
-#endif
-            state->position = get_next_pos(state->position, state->word_elements_result);
-            if (state->position >= WORDLE_LENGTH) {
-                get_result(state);
-                state->using_random_guess = false;
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->curr_screen != SCREEN_PLAYING) break;
-            display_letter(state, true);
-            state->position = get_prev_pos(state->position, state->word_elements_result);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-        case EVENT_ACTIVATE:
-            break;
-        case EVENT_TIMEOUT:
-            if (state->curr_screen >= SCREEN_RESULT) {
-                reset_incorrect_elements(state);
-                state->position = get_first_pos(state->word_elements_result); 
-                display_title(state);
-            }
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            if (state->curr_screen != SCREEN_TITLE)
-                display_title(state);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-    return true;
-}
-
-void wordle_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h
deleted file mode 100644 (file)
index 5cc2f09..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 <David Volovskiy>
- *
- * 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 WORDLE_FACE_H_
-#define WORDLE_FACE_H_
-
-#include "movement.h"
-
-/*
- * Wordle Face
- * A port of NY Times' Wordle game (https://www.nytimes.com/games/wordle/index.html)
- * A random 5 letter word is chosen and you have WORDLE_MAX_ATTEMPTS attempts to guess it.
- * Each guess must be a valid 5-letter word found in _legal_words in the C file.
- * The only letters used are _valid_letters, also found in the C file.
- * After a guess, the letters in the correct spot will remain, 
- * and the letters found in the word, but in the incorrect spot will blink.
- * The screen after the title screen if a new game is started shows the streak of games won in a row.
- * 
- * If WORDLE_USE_DAILY_STREAK is set to True, then the game can only be played once per day,
- *                  and the streak resets to 0 if a day goes by without playing the game.
- * 
- * Controls:
- *    Light Press
- *        If Playing: Next letter
- *        Else: Next screen
- *    Light Hold
- *        If Playing: Previous letter
- *        Else: Toggle Hard-Mode. This is skipping over letters that have been confirmed
- *              to not be in the word (indicated via 'H' in the top-right)
- *
- *    Alarm Press
- *        If Playing: If WORDLE_USE_RANDOM_GUESS is set and Light btn held and 
- *                    (on first letter or already used a random guess) 
- *                    and first attempt: Use a random 5 letter word with all letters that are different.
- *                    Else: Next position
- *        Else: Next screen
- *    Alarm Hold
- *        If Playing: Previous position
- *        Else: None
- * 
- * Note: Actual Hard Mode in Wordle game is "Any revealed hints must be used in subsequent guesses"
- *       But that came off as clunky UX on the Casio. So instead it only removes unused letters from the keyboard
- *       as that also simplifies the keyboard.
- */
-
-#define WORDLE_LENGTH 5
-#define WORDLE_MAX_ATTEMPTS 6
-/*  WORDLE_USE_DAILY_STREAK
- *  0 = Don't ever reset the streak or the puzzle.
- *  1 = Reset the streak and puzzle 24hrs after starting a puzzle and not finishing it.
- *      If the last puzzle was started at 8AM, it'll be considered failed at 8AM the next day.
- *  2 = Reset the streak and puzzle if a puzzle goes unsolved or not started a day after the previous one.
- *      If the last puzzle was started at 8AM, it'll be considered failed at midnight the next day.
- *      This will not be the case if the puzzle is started at 8AM, continued at 11:59PM and solved at 12:01AM, the game will let that slide.
- *      Starting a new game instead of continuing is not allowed in this state.
-*/
-#define WORDLE_USE_DAILY_STREAK 1
-#define WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES false  // This allows non-words to be entered and repeat guesses to be made. It saves ~11.5KB of ROM.
-/*  WORDLE_USE_RANDOM_GUESS
- *  0 = Don't allow quickly choosing a random quess
- *  1 = Allow using a random guess of any value that can be an answer
- *  2 = Allow using a random guess of any value that can be an answer where all of its letters are unique
- *  3 = Allow using a random guess of any value that can be an answer, and it's considered one of the best initial choices.
-*/
-#define WORDLE_USE_RANDOM_GUESS 2
-#include "wordle_face_dict.h"
-
-#define WORDLE_NUM_WORDS (sizeof(_valid_words) / sizeof(_valid_words[0]))
-#define WORDLE_NUM_POSSIBLE_WORDS (sizeof(_possible_words) / sizeof(_possible_words[0]))
-#define WORDLE_NUM_VALID_LETTERS (sizeof(_valid_letters) / sizeof(_valid_letters[0]))
-
-typedef enum {
-    WORDLE_LETTER_WRONG = 0,
-    WORDLE_LETTER_WRONG_LOC,
-    WORDLE_LETTER_CORRECT,
-    WORDLE_LETTER_COUNT
-} WordleLetterResult;
-
-typedef enum {
-    SCREEN_TITLE = 0,
-    SCREEN_STREAK,
-    SCREEN_CONTINUE,
-#if WORDLE_USE_DAILY_STREAK
-    SCREEN_WAIT,
-#endif
-    SCREEN_PLAYING,
-    SCREEN_RESULT,
-    SCREEN_WIN,
-    SCREEN_LOSE,
-    SCREEN_NO_DICT,
-    SCREEN_ALREADY_GUESSED,
-    SCREEN_COUNT
-} WordleScreen;
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint8_t word_elements[WORDLE_LENGTH];
-    WordleLetterResult word_elements_result[WORDLE_LENGTH];
-#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
-    uint16_t guessed_words[WORDLE_MAX_ATTEMPTS];
-#endif
-    uint8_t attempt : 4;
-    uint8_t position : 3;
-    bool using_random_guess : 1;
-    uint16_t curr_answer : 14;
-    bool continuing : 1;
-    bool skip_wrong_letter : 1;
-    uint8_t streak;
-    WordleScreen curr_screen;
-    bool known_wrong_letters[WORDLE_NUM_VALID_LETTERS];
-    uint32_t day_last_game_started;
-} wordle_state_t;
-
-void wordle_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void wordle_face_activate(void *context);
-bool wordle_face_loop(movement_event_t event, void *context);
-void wordle_face_resign(void *context);
-
-#define wordle_face ((const watch_face_t){ \
-    wordle_face_setup, \
-    wordle_face_activate, \
-    wordle_face_loop, \
-    wordle_face_resign, \
-    NULL, \
-})
-
-#endif // WORDLE_FACE_H_
-
diff --git a/movement/watch_faces/complication/wordle_face_dict.h b/movement/watch_faces/complication/wordle_face_dict.h
deleted file mode 100644 (file)
index 6b82976..0000000
+++ /dev/null
@@ -1,293 +0,0 @@
-#ifndef WORDLE_FACE_DICT_H_
-#define WORDLE_FACE_DICT_H_
-
-#ifndef WORDLE_LENGTH
-#define WORDLE_LENGTH 5
-#endif
-
-#ifndef WORDLE_USE_RANDOM_GUESS
-#define WORDLE_USE_RANDOM_GUESS 2
-#endif
-
-static const char _valid_letters[] = {'A', 'C', 'E', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S', 'T'};
-
-// From: https://matthewminer.name/projects/calculators/wordle-words-left/
-// Number of words found: 432
-static const char _valid_words[][WORDLE_LENGTH + 1] = {
-    "SLATE", "STARE", "SNARE", "SANER", "CRANE", "STALE", "CRATE", "RAISE", "TRACE", 
-    "SHARE", "ARISE", "SCARE", "SPARE", "CHAOS", "TAPIR", "CAIRN", "TENOR", "CLEAN", 
-    "HEART", "SCOPE", "SNARL", "SLEPT", "SINCE", "EPOCH", "SPACE", "RELIC", "SPOIL", 
-    "LITER", "LEAPT", "LANCE", "RANCH", "HORSE", "LEACH", "LATER", "STEAL", "CHEAP", 
-    "SHORT", "ETHIC", "CHANT", "ACTOR", "REACH", "SEPIA", "ONSET", "SPLAT", "LEANT", 
-    "REACT", "OCTAL", "SPORE", "IRATE", "CORAL", "NICER", "SPILT", "SCENT", "PANIC", 
-    "SHIRT", "PECAN", "SLAIN", "SPLIT", "ROACH", "ASCOT", "PHONE", "LITHE", "STOIC", 
-    "STRIP", "RENAL", "POISE", "ENACT", "CHEAT", "PITCH", "NOISE", "INLET", "PEARL", 
-    "POLAR", "PEACH", "STOLE", "CASTE", "CREST", "CRONE", "ETHOS", "THEIR", "STONE", 
-    "SHIRE", "LATCH", "HASTE", "CLOSE", "SPINE", "SLANT", "SPEAR", "SCALE", "CAPER", 
-    "RETCH", "PESTO", "CHIRP", "SPORT", "OPTIC", "SNAIL", "PRICE", "PLANE", "TORCH", 
-    "PASTE", "RECAP", "SOLAR", "CRASH", "LINER", "OPINE", "ASHEN", "PALER", "ECLAT", 
-    "SPELT", "TRIAL", "PERIL", "SLICE", "SCANT", "SAINT", "POSIT", "ATONE", "SPIRE", 
-    "COAST", "INEPT", "SHOAL", "CLASH", "THORN", "PHASE", "SCORE", "TRICE", "PERCH", 
-    "PORCH", "SHEAR", "CHOIR", "RHINO", "PLANT", "SHONE", "CHORE", "LEARN", "ALTER", 
-    "CHAIN", "PANEL", "PLIER", "STEIN", "COPSE", "SONIC", "ALIEN", "CHOSE", "ACORN", 
-    "ANTIC", "CHEST", "OTHER", "CHINA", "TALON", "SCORN", "PLAIN", "PILOT", "RIPEN", 
-    "PATCH", "SPICE", "CLONE", "SCION", "SCONE", "STRAP", "PARSE", "SHALE", "RISEN", 
-    "CANOE", "INTER", "LEASH", "ISLET", "PRINT", "SHINE", "NORTH", "CLEAT", "PLAIT", 
-    "SCRAP", "CLEAR", "SLOTH", "LAPSE", "CHAIR", "SNORT", "SHARP", "OPERA", "STAIN", 
-    "TEACH", "TRAIL", "TRAIN", "LATHE", "PIANO", "PINCH", "PETAL", "STERN", "PRONE", 
-    "PROSE", "PLEAT", "TROPE", "PLACE", "POSER", "INERT", "CHASE", "CAROL", "STAIR", 
-    "SATIN", "SPITE", "LOATH", "ROAST", "ARSON", "SHAPE", "CLASP", "LOSER", "SALON", 
-    "CATER", "SHALT", "INTRO", "ALERT", "PENAL", "SHORE", "RINSE", "CREPT", "APRON", 
-    "SONAR", "AISLE", "AROSE", "HATER", "NICHE", "POINT", "EARTH", "PINTO", "THOSE", 
-    "CLOTH", "NOTCH", "TOPIC", "RESIN", "SCALP", "HEIST", "HERON", "TRIPE", "TONAL", 
-    "TAPER", "SHORN", "TONIC", "HOIST", "SNORE", "STORE", "SLOPE", "OCEAN", "CHART", 
-    "PAINT", "SPENT", "SNIPE", "CRISP", "TRASH", "PATIO", "PLATE", "HOTEL", "LEAST", 
-    "ALONE", "RALPH", "SPIEL", "SIREN", "RATIO", "STOOP", "TROLL", "ATOLL", "SLASH", 
-    "RETRO", "CREEP", "STILT", "SPREE", "TASTE", "CACHE", "CANON", "EATEN", "TEPEE", 
-    "SHEET", "SNEER", "ERROR", "NATAL", "SLEEP", "STINT", "TROOP", "SHALL", "STALL", 
-    "PIPER", "TOAST", "NASAL", "CORER", "THERE", "POOCH", "SCREE", "ELITE", "ALTAR", 
-    "PENCE", "EATER", "ALPHA", "TENTH", "LINEN", "SHEER", "TAINT", "HEATH", "CRIER", 
-    "TENSE", "CARAT", "CANAL", "APNEA", "THESE", "HATCH", "SHELL", "CIRCA", "APART", 
-    "SPILL", "STEEL", "LOCAL", "STOOL", "SHEEN", "RESET", "STEEP", "ELATE", "PRESS", 
-    "SLEET", "CROSS", "TOTAL", "TREAT", "ONION", "STATE", "CINCH", "ASSET", "THREE", 
-    "TORSO", "SNOOP", "PENNE", "SPOON", "SHEEP", "PAPAL", "STILL", "CHILL", "THETA", 
-    "LEECH", "INNER", "HONOR", "LOOSE", "CONIC", "SCENE", "COACH", "CONCH", "LATTE", 
-    "ERASE", "ESTER", "PEACE", "PASTA", "INANE", "SPOOL", "TEASE", "HARSH", "PIECE", 
-    "STEER", "SCOOP", "NINTH", "OTTER", "OCTET", "EERIE", "RISER", "LAPEL", "HIPPO", 
-    "PREEN", "ETHER", "AORTA", "SENSE", "TRACT", "SHOOT", "SLOOP", "REPEL", "TITHE", 
-    "IONIC", "CELLO", "CHESS", "SOOTH", "COCOA", "TITAN", "TOOTH", "TIARA", "CRESS", 
-    "SLOSH", "RARER", "TERSE", "ERECT", "HELLO", "PARER", "RIPER", "NOOSE", "CREPE", 
-    "CACAO", "ILIAC", "POSSE", "CACTI", "EASEL", "LASSO", "ROOST", "ALLOT", "COLON", 
-    "LEPER", "TEETH", "TITLE", "HENCE", "NIECE", "PAPER", "TRITE", "SPELL", "RACER", 
-    "ATTIC", "CRASS", "HITCH", "LEASE", "CEASE", "ROTOR", "ELOPE", "APPLE", "CHILI", 
-    "START", "PHOTO", "SALSA", "STASH", "PRIOR", "TAROT", "COLOR", "CHEER", "CLASS", 
-    "ARENA", "ELECT", "ENTER", "CATCH", "TENET", "TACIT", "TRAIT", "TERRA", "LILAC", 
-};
-
-// These are words that'll never be used, but still need to be in the dictionary for guesses.
-// Number of words found: 1898
-static const char _possible_words[][WORDLE_LENGTH + 1] = {
-#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES
-    "AALII", "AARTI", "ACAIS", "ACARI", "ACCAS", "ACERS", "ACETA", "ACHAR", "ACHES", 
-    "ACHOO", "ACINI", "ACNES", "ACRES", "ACROS", "ACTIN", "ACTON", "AECIA", "AEONS", 
-    "AERIE", "AEROS", "AESIR", "AHEAP", "AHENT", "AHINT", "AINEE", "AIOLI", "AIRER", 
-    "AIRNS", "AIRTH", "AIRTS", "AITCH", "ALAAP", "ALANE", "ALANS", "ALANT", "ALAPA", 
-    "ALAPS", "ALATE", "ALCOS", "ALECS", "ALEPH", "ALIAS", "ALINE", "ALIST", "ALLEE", 
-    "ALLEL", "ALLIS", "ALOES", "ALOHA", "ALOIN", "ALOOS", "ALTHO", "ALTOS", "ANANA", 
-    "ANATA", "ANCHO", "ANCLE", "ANCON", "ANEAR", "ANELE", "ANENT", "ANILE", "ANILS", 
-    "ANION", "ANISE", "ANLAS", "ANNAL", "ANNAS", "ANNAT", "ANOAS", "ANOLE", "ANSAE", 
-    "ANTAE", "ANTAR", "ANTAS", "ANTES", "ANTIS", "ANTRA", "ANTRE", "APACE", "APERS", 
-    "APERT", "APHIS", "APIAN", "APIOL", "APISH", "APOOP", "APORT", "APPAL", "APPEL", 
-    "APPRO", "APRES", "APSES", "APSIS", "APSOS", "APTER", "ARARS", "ARCHI", "ARCOS", 
-    "AREAE", "AREAL", "AREAR", "AREAS", "ARECA", "AREIC", "ARENE", "AREPA", "ARERE", 
-    "ARETE", "ARETS", "ARETT", "ARHAT", "ARIAS", "ARIEL", "ARILS", "ARIOT", "ARISH", 
-    "ARLES", "ARNAS", "AROHA", "ARPAS", "ARPEN", "ARRAH", "ARRAS", "ARRET", "ARRIS", 
-    "ARSES", "ARSIS", "ARTAL", "ARTEL", "ARTIC", "ARTIS", "ASANA", "ASCON", "ASHES", 
-    "ASHET", "ASPEN", "ASPER", "ASPIC", "ASPIE", "ASPIS", "ASPRO", "ASSAI", "ASSES", 
-    "ASSOT", "ASTER", "ASTIR", "ATAPS", "ATILT", "ATLAS", "ATOCS", "ATRIA", "ATRIP", 
-    "ATTAP", "ATTAR", "CACAS", "CAECA", "CAESE", "CAINS", "CALLA", "CALLS", "CALOS", 
-    "CALPA", "CALPS", "CANEH", "CANER", "CANES", "CANNA", "CANNS", "CANSO", "CANST", 
-    "CANTO", "CANTS", "CAPAS", "CAPES", "CAPHS", "CAPLE", "CAPON", "CAPOS", "CAPOT", 
-    "CAPRI", "CARAP", "CARER", "CARES", "CARET", "CARLE", "CARLS", "CARNS", "CARON", 
-    "CARPI", "CARPS", "CARRS", "CARSE", "CARTA", "CARTE", "CARTS", "CASAS", "CASCO", 
-    "CASES", "CASTS", "CATES", "CECAL", "CEILI", "CEILS", "CELLA", "CELLI", "CELLS", 
-    "CELTS", "CENSE", "CENTO", "CENTS", "CEORL", "CEPES", "CERCI", "CERES", "CERIA", 
-    "CERIC", "CERNE", "CEROC", "CEROS", "CERTS", "CESSE", "CESTA", "CESTI", "CETES", 
-    "CHACE", "CHACO", "CHAIS", "CHALS", "CHANA", "CHAPE", "CHAPS", "CHAPT", "CHARA", 
-    "CHARE", "CHARR", "CHARS", "CHATS", "CHEEP", "CHELA", "CHELP", "CHERE", "CHERT", 
-    "CHETH", "CHIAO", "CHIAS", "CHICA", "CHICH", "CHICO", "CHICS", "CHIEL", "CHILE", 
-    "CHINE", "CHINO", "CHINS", "CHIPS", "CHIRL", "CHIRO", "CHIRR", "CHIRT", "CHITS", 
-    "CHOCO", "CHOCS", "CHOIL", "CHOLA", "CHOLI", "CHOLO", "CHONS", "CHOON", "CHOPS", 
-    "CHOTA", "CHOTT", "CIELS", "CILIA", "CILLS", "CINCT", "CINES", "CIONS", "CIPPI", 
-    "CIRCS", "CIRES", "CIRLS", "CIRRI", "CISCO", "CISTS", "CITAL", "CITER", "CITES", 
-    "CLACH", "CLAES", "CLANS", "CLAPS", "CLAPT", "CLARO", "CLART", "CLAST", "CLATS", 
-    "CLEEP", "CLEPE", "CLEPT", "CLIES", "CLINE", "CLINT", "CLIPE", "CLIPS", "CLIPT", 
-    "CLITS", "CLONS", "CLOOP", "CLOOT", "CLOPS", "CLOTE", "CLOTS", "COACT", "COALA", 
-    "COALS", "COAPT", "COATE", "COATI", "COATS", "COCAS", "COCCI", "COCCO", "COCOS", 
-    "COHEN", "COHOE", "COHOS", "COILS", "COINS", "COIRS", "COITS", "COLAS", "COLES", 
-    "COLIC", "COLIN", "COLLS", "COLTS", "CONES", "CONIA", "CONIN", "CONNE", "CONNS", 
-    "CONTE", "CONTO", "COOCH", "COOEE", "COOER", "COOLS", "COONS", "COOPS", "COOPT", 
-    "COOST", "COOTS", "COPAL", "COPEN", "COPER", "COPES", "COPRA", "CORES", "CORIA", 
-    "CORNI", "CORNO", "CORNS", "CORPS", "CORSE", "CORSO", "COSEC", "COSES", "COSET", 
-    "COSIE", "COSTA", "COSTE", "COSTS", "COTAN", "COTES", "COTHS", "COTTA", "COTTS", 
-    "CRAAL", "CRAIC", "CRANS", "CRAPE", "CRAPS", "CRARE", "CREEL", "CREES", "CRENA", 
-    "CREPS", "CRIAS", "CRIES", "CRINE", "CRIOS", "CRIPE", "CRIPS", "CRISE", "CRITH", 
-    "CRITS", "CROCI", "CROCS", "CRONS", "CROOL", "CROON", "CROPS", "CRORE", "CROST", 
-    "CTENE", "EALES", "EARLS", "EARNS", "EARNT", "EARST", "EASER", "EASES", "EASLE", 
-    "EASTS", "EATHE", "ECHES", "ECHOS", "EISEL", "ELAIN", "ELANS", "ELCHI", "ELINT", 
-    "ELOIN", "ELOPS", "ELPEE", "ELSIN", "ENATE", "ENIAC", "ENLIT", "ENOLS", "ENROL", 
-    "ENTIA", "EORLS", "EOSIN", "EPACT", "EPEES", "EPHAH", "EPHAS", "EPHOR", "EPICS", 
-    "EPOPT", "EPRIS", "ERICA", "ERICS", "ERNES", "EROSE", "ERSES", "ESCAR", "ESCOT", 
-    "ESILE", "ESNES", "ESSES", "ESTOC", "ESTOP", "ESTRO", "ETAPE", "ETATS", "ETENS", 
-    "ETHAL", "ETHNE", "ETICS", "ETNAS", "ETTIN", "ETTLE", "HAARS", "HAETS", "HAHAS", 
-    "HAILS", "HAINS", "HAINT", "HAIRS", "HAITH", "HALAL", "HALER", "HALES", "HALLO", 
-    "HALLS", "HALON", "HALOS", "HALSE", "HALTS", "HANAP", "HANCE", "HANCH", "HANSA", 
-    "HANSE", "HANTS", "HAOLE", "HAPPI", "HARES", "HARLS", "HARNS", "HAROS", "HARPS", 
-    "HARTS", "HASPS", "HASTA", "HATES", "HATHA", "HEALS", "HEAPS", "HEARE", "HEARS", 
-    "HEAST", "HEATS", "HECHT", "HEELS", "HEILS", "HEIRS", "HELES", "HELIO", "HELLS", 
-    "HELOS", "HELOT", "HELPS", "HENCH", "HENNA", "HENTS", "HEPAR", "HERES", "HERLS", 
-    "HERNS", "HEROS", "HERSE", "HESPS", "HESTS", "HETES", "HETHS", "HIANT", "HILAR", 
-    "HILCH", "HILLO", "HILLS", "HILTS", "HINTS", "HIOIS", "HIREE", "HIRER", "HIRES", 
-    "HISTS", "HITHE", "HOARS", "HOAST", "HOERS", "HOISE", "HOLES", "HOLLA", "HOLLO", 
-    "HOLON", "HOLOS", "HOLTS", "HONAN", "HONER", "HONES", "HOOCH", "HOONS", "HOOPS", 
-    "HOORS", "HOOSH", "HOOTS", "HOPER", "HOPES", "HORAH", "HORAL", "HORAS", "HORIS", 
-    "HORNS", "HORST", "HOSEL", "HOSEN", "HOSER", "HOSES", "HOSTA", "HOSTS", "HOTCH", 
-    "HOTEN", "ICERS", "ICHES", "ICHOR", "ICIER", "ICONS", "ICTAL", "ICTIC", "ILEAC", 
-    "ILEAL", "ILIAL", "ILLER", "ILLTH", "INAPT", "INCEL", "INCLE", "INION", "INNIT", 
-    "INSET", "INSPO", "INTEL", "INTIL", "INTIS", "INTRA", "IOTAS", "IPPON", "IRONE", 
-    "IRONS", "ISHES", "ISLES", "ISNAE", "ISSEI", "ISTLE", "ITHER", "LAARI", "LACER", 
-    "LACES", "LACET", "LAERS", "LAHAL", "LAHAR", "LAICH", "LAICS", "LAIRS", "LAITH", 
-    "LALLS", "LANAI", "LANAS", "LANCH", "LANES", "LANTS", "LAPIN", "LAPIS", "LARCH", 
-    "LAREE", "LARES", "LARIS", "LARNS", "LARNT", "LASER", "LASES", "LASSI", "LASTS", 
-    "LATAH", "LATEN", "LATHI", "LATHS", "LEANS", "LEAPS", "LEARE", "LEARS", "LEATS", 
-    "LEEAR", "LEEPS", "LEERS", "LEESE", "LEETS", "LEHRS", "LEIRS", "LEISH", "LENES", 
-    "LENIS", "LENOS", "LENSE", "LENTI", "LENTO", "LEONE", "LEPRA", "LEPTA", "LERES", 
-    "LERPS", "LESES", "LESTS", "LETCH", "LETHE", "LIANA", "LIANE", "LIARS", "LIART", 
-    "LICHI", "LICHT", "LICIT", "LIENS", "LIERS", "LILLS", "LILOS", "LILTS", "LINAC", 
-    "LINCH", "LINES", "LININ", "LINNS", "LINOS", "LINTS", "LIONS", "LIPAS", "LIPES", 
-    "LIPIN", "LIPOS", "LIRAS", "LIROT", "LISLE", "LISPS", "LISTS", "LITAI", "LITAS", 
-    "LITES", "LITHO", "LITHS", "LITRE", "LLANO", "LOACH", "LOANS", "LOAST", "LOCHE", 
-    "LOCHS", "LOCIE", "LOCIS", "LOCOS", "LOESS", "LOHAN", "LOINS", "LOIPE", "LOIRS", 
-    "LOLLS", "LONER", "LOOIE", "LOONS", "LOOPS", "LOOTS", "LOPER", "LOPES", "LORAL", 
-    "LORAN", "LOREL", "LORES", "LORIC", "LORIS", "LOSEL", "LOSEN", "LOSES", "LOTAH", 
-    "LOTAS", "LOTES", "LOTIC", "LOTOS", "LOTSA", "LOTTA", "LOTTE", "LOTTO", "NAANS", 
-    "NACHE", "NACHO", "NACRE", "NAHAL", "NAILS", "NAIRA", "NALAS", "NALLA", "NANAS", 
-    "NANCE", "NANNA", "NANOS", "NAPAS", "NAPES", "NAPOO", "NAPPA", "NAPPE", "NARAS", 
-    "NARCO", "NARCS", "NARES", "NARIC", "NARIS", "NARRE", "NASHI", "NATCH", "NATES", 
-    "NATIS", "NEALS", "NEAPS", "NEARS", "NEATH", "NEATS", "NEELE", "NEEPS", "NEESE", 
-    "NEIST", "NELIS", "NENES", "NEONS", "NEPER", "NEPIT", "NERAL", "NEROL", "NERTS", 
-    "NESTS", "NETES", "NETOP", "NETTS", "NICHT", "NICOL", "NIHIL", "NILLS", "NINER", 
-    "NINES", "NINON", "NIPAS", "NIRLS", "NISEI", "NISSE", "NITER", "NITES", "NITON", 
-    "NITRE", "NITRO", "NOAHS", "NOELS", "NOILS", "NOINT", "NOIRS", "NOLES", "NOLLS", 
-    "NOLOS", "NONAS", "NONCE", "NONES", "NONET", "NONIS", "NOOIT", "NOONS", "NOOPS", 
-    "NOPAL", "NORIA", "NORIS", "NOSER", "NOSES", "NOTAL", "NOTER", "NOTES", "OASES", 
-    "OASIS", "OASTS", "OATEN", "OATER", "OATHS", "OCHER", "OCHES", "OCHRE", "OCREA", 
-    "OCTAN", "OCTAS", "OHIAS", "OHONE", "OILER", "OINTS", "OLEIC", "OLEIN", "OLENT", 
-    "OLEOS", "OLIOS", "OLLAS", "OLLER", "OLLIE", "OLPAE", "OLPES", "ONCER", "ONCES", 
-    "ONCET", "ONERS", "ONTIC", "OONTS", "OORIE", "OOSES", "OPAHS", "OPALS", "OPENS", 
-    "OPEPE", "OPPOS", "OPSIN", "OPTER", "ORACH", "ORALS", "ORANT", "ORATE", "ORCAS", 
-    "ORCIN", "ORIEL", "ORLES", "ORLON", "ORLOP", "ORNIS", "ORPIN", "ORRIS", "ORTHO", 
-    "OSCAR", "OSHAC", "OSIER", "OSSIA", "OSTIA", "OTTAR", "OTTOS", "PAALS", "PAANS", 
-    "PACAS", "PACER", "PACES", "PACHA", "PACOS", "PACTA", "PACTS", "PAEAN", "PAEON", 
-    "PAILS", "PAINS", "PAIRE", "PAIRS", "PAISA", "PAISE", "PALAS", "PALEA", "PALES", 
-    "PALET", "PALIS", "PALLA", "PALLS", "PALPI", "PALPS", "PALSA", "PANCE", "PANES", 
-    "PANNE", "PANNI", "PANTO", "PANTS", "PAOLI", "PAOLO", "PAPAS", "PAPES", "PAPPI", 
-    "PARAE", "PARAS", "PARCH", "PAREN", "PAREO", "PARES", "PARIS", "PARLE", "PAROL", 
-    "PARPS", "PARRA", "PARRS", "PARTI", "PARTS", "PASEO", "PASES", "PASHA", "PASSE", 
-    "PASTS", "PATEN", "PATER", "PATES", "PATHS", "PATIN", "PATTE", "PEALS", "PEANS", 
-    "PEARE", "PEARS", "PEART", "PEASE", "PEATS", "PECHS", "PEECE", "PEELS", "PEENS", 
-    "PEEPE", "PEEPS", "PEERS", "PEINS", "PEISE", "PELAS", "PELES", "PELLS", "PELON", 
-    "PELTA", "PELTS", "PENES", "PENIE", "PENIS", "PENNA", "PENNI", "PENTS", "PEONS", 
-    "PEPLA", "PEPOS", "PEPSI", "PERAI", "PERCE", "PERCS", "PEREA", "PERES", "PERIS", 
-    "PERNS", "PERPS", "PERSE", "PERST", "PERTS", "PESOS", "PESTS", "PETAR", "PETER", 
-    "PETIT", "PETRE", "PETRI", "PETTI", "PETTO", "PHARE", "PHEER", "PHENE", "PHEON", 
-    "PHESE", "PHIAL", "PHISH", "PHOCA", "PHONO", "PHONS", "PHOTS", "PHPHT", "PIANI", 
-    "PIANS", "PICAL", "PICAS", "PICOT", "PICRA", "PIERS", "PIERT", "PIETA", "PIETS", 
-    "PILAE", "PILAO", "PILAR", "PILCH", "PILEA", "PILEI", "PILER", "PILES", "PILIS", 
-    "PILLS", "PINAS", "PINES", "PINNA", "PINON", "PINOT", "PINTA", "PINTS", "PIONS", 
-    "PIPAL", "PIPAS", "PIPES", "PIPET", "PIPIS", "PIPIT", "PIRAI", "PIRLS", "PIRNS", 
-    "PISCO", "PISES", "PISOS", "PISTE", "PITAS", "PITHS", "PITON", "PITOT", "PITTA", 
-    "PLAAS", "PLANS", "PLAPS", "PLASH", "PLAST", "PLATS", "PLATT", "PLEAS", "PLENA", 
-    "PLEON", "PLESH", "PLICA", "PLIES", "PLOAT", "PLOPS", "PLOTS", "POACH", "POEPS", 
-    "POETS", "POLER", "POLES", "POLIO", "POLIS", "POLLS", "POLOS", "POLTS", "PONCE", 
-    "PONES", "PONTS", "POOHS", "POOLS", "POONS", "POOPS", "POORI", "POORT", "POOTS", 
-    "POPES", "POPPA", "PORAE", "PORAL", "PORER", "PORES", "PORIN", "PORNO", "PORNS", 
-    "PORTA", "PORTS", "POSES", "POSHO", "POSTS", "POTAE", "POTCH", "POTES", "POTIN", 
-    "POTOO", "POTTO", "POTTS", "PRANA", "PRAOS", "PRASE", "PRATE", "PRATS", "PRATT", 
-    "PREES", "PRENT", "PREON", "PREOP", "PREPS", "PRESA", "PRESE", "PREST", "PRIAL", 
-    "PRIER", "PRIES", "PRILL", "PRION", "PRISE", "PRISS", "PROAS", "PROIN", "PROLE", 
-    "PROLL", "PROPS", "PRORE", "PROSO", "PROSS", "PROST", "PROTO", "PSION", "PSOAE", 
-    "PSOAI", "PSOAS", "PSORA", "RACES", "RACHE", "RACON", "RAIAS", "RAILE", "RAILS", 
-    "RAINE", "RAINS", "RAITA", "RAITS", "RALES", "RANAS", "RANCE", "RANEE", "RANIS", 
-    "RANTS", "RAPER", "RAPES", "RAPHE", "RAPPE", "RAREE", "RARES", "RASER", "RASES", 
-    "RASPS", "RASSE", "RASTA", "RATAL", "RATAN", "RATAS", "RATCH", "RATEL", "RATER", 
-    "RATES", "RATHA", "RATHE", "RATHS", "RATOO", "RATOS", "REAIS", "REALO", "REALS", 
-    "REANS", "REAPS", "REARS", "REAST", "REATA", "REATE", "RECAL", "RECCE", "RECCO", 
-    "RECIT", "RECON", "RECTA", "RECTI", "RECTO", "REECH", "REELS", "REENS", "REEST", 
-    "REINS", "REIST", "RELET", "RELIE", "RELIT", "RELLO", "RENIN", "RENNE", "RENOS", 
-    "RENTE", "RENTS", "REOIL", "REPIN", "REPLA", "REPOS", "REPOT", "REPPS", "REPRO", 
-    "RERAN", "RESAT", "RESEE", "RESES", "RESIT", "RESTO", "RESTS", "RETIA", "RETIE", 
-    "RHEAS", "RHIES", "RHINE", "RHONE", "RIALS", "RIANT", "RIATA", "RICER", "RICES", 
-    "RICHT", "RICIN", "RIELS", "RILES", "RILLE", "RILLS", "RINES", "RIOTS", "RIPES", 
-    "RIPPS", "RISES", "RISHI", "RISPS", "RITES", "RITTS", "ROANS", "ROARS", "ROATE", 
-    "ROHES", "ROILS", "ROINS", "ROIST", "ROLES", "ROLLS", "RONEO", "RONES", "RONIN", 
-    "RONNE", "RONTE", "RONTS", "ROONS", "ROOPS", "ROOSA", "ROOSE", "ROOTS", "ROPER", 
-    "ROPES", "RORAL", "RORES", "RORIC", "RORIE", "RORTS", "ROSES", "ROSET", "ROSHI", 
-    "ROSIN", "ROSIT", "ROSTI", "ROSTS", "ROTAL", "ROTAN", "ROTAS", "ROTCH", "ROTES", 
-    "ROTIS", "ROTLS", "ROTON", "ROTOS", "ROTTE", "SACRA", "SAICE", "SAICS", "SAILS", 
-    "SAINE", "SAINS", "SAIRS", "SAIST", "SAITH", "SALAL", "SALAT", "SALEP", "SALES", 
-    "SALET", "SALIC", "SALLE", "SALOL", "SALOP", "SALPA", "SALPS", "SALSE", "SALTO", 
-    "SALTS", "SANES", "SANSA", "SANTO", "SANTS", "SAOLA", "SAPAN", "SAPOR", "SARAN", 
-    "SAREE", "SARIN", "SARIS", "SAROS", "SASER", "SASIN", "SASSE", "SATAI", "SATES", 
-    "SATIS", "SCAIL", "SCALA", "SCALL", "SCANS", "SCAPA", "SCAPE", "SCAPI", "SCARP", 
-    "SCARS", "SCART", "SCATH", "SCATS", "SCATT", "SCEAT", "SCENA", "SCOOT", "SCOPA", 
-    "SCOPS", "SCOTS", "SCRAE", "SCRAN", "SCRAT", "SCRIP", "SEALS", "SEANS", "SEARE", 
-    "SEARS", "SEASE", "SEATS", "SECCO", "SECHS", "SECTS", "SEELS", "SEEPS", "SEERS", 
-    "SEHRI", "SEILS", "SEINE", "SEIRS", "SEISE", "SELAH", "SELES", "SELLA", "SELLE", 
-    "SELLS", "SENAS", "SENES", "SENNA", "SENOR", "SENSA", "SENSI", "SENTE", "SENTI", 
-    "SENTS", "SEPAL", "SEPIC", "SEPTA", "SEPTS", "SERAC", "SERAI", "SERAL", "SERER", 
-    "SERES", "SERIC", "SERIN", "SERON", "SERRA", "SERRE", "SERRS", "SESSA", "SETAE", 
-    "SETAL", "SETON", "SETTS", "SHAHS", "SHANS", "SHAPS", "SHARN", "SHASH", "SHCHI", 
-    "SHEAL", "SHEAS", "SHEEL", "SHENT", "SHEOL", "SHERE", "SHERO", "SHETS", "SHIAI", 
-    "SHIEL", "SHIER", "SHIES", "SHILL", "SHINS", "SHIPS", "SHIRR", "SHIRS", "SHISH", 
-    "SHISO", "SHIST", "SHITE", "SHITS", "SHLEP", "SHOAT", "SHOER", "SHOES", "SHOLA", 
-    "SHOOL", "SHOON", "SHOOS", "SHOPE", "SHOPS", "SHORL", "SHOTE", "SHOTS", "SHOTT", 
-    "SHRIS", "SIALS", "SICES", "SICHT", "SIENS", "SIENT", "SIETH", "SILEN", "SILER", 
-    "SILES", "SILLS", "SILOS", "SILTS", "SINES", "SINHS", "SIPES", "SIREE", "SIRES", 
-    "SIRIH", "SIRIS", "SIROC", "SIRRA", "SISAL", "SISES", "SISTA", "SISTS", "SITAR", 
-    "SITES", "SITHE", "SLAES", "SLANE", "SLAPS", "SLART", "SLATS", "SLEER", "SLIER", 
-    "SLIPE", "SLIPS", "SLIPT", "SLISH", "SLITS", "SLOAN", "SLOES", "SLOOT", "SLOPS", 
-    "SLOTS", "SNAPS", "SNARS", "SNASH", "SNATH", "SNEAP", "SNEES", "SNELL", "SNIES", 
-    "SNIPS", "SNIRT", "SNITS", "SNOEP", "SNOOL", "SNOOT", "SNOTS", "SOAPS", "SOARE", 
-    "SOARS", "SOCAS", "SOCES", "SOCLE", "SOILS", "SOLAH", "SOLAN", "SOLAS", "SOLEI", 
-    "SOLER", "SOLES", "SOLON", "SOLOS", "SONCE", "SONES", "SONNE", "SONSE", "SOOLE", 
-    "SOOLS", "SOOPS", "SOOTE", "SOOTS", "SOPHS", "SOPOR", "SOPRA", "SORAL", "SORAS", 
-    "SOREE", "SOREL", "SORER", "SORES", "SORNS", "SORRA", "SORTA", "SORTS", "SOTHS", 
-    "SOTOL", "SPAER", "SPAES", "SPAHI", "SPAIL", "SPAIN", "SPAIT", "SPALE", "SPALL", 
-    "SPALT", "SPANE", "SPANS", "SPARS", "SPART", "SPATE", "SPATS", "SPEAL", "SPEAN", 
-    "SPEAT", "SPECS", "SPECT", "SPEEL", "SPEER", "SPEIL", "SPEIR", "SPEOS", "SPETS", 
-    "SPIAL", "SPICA", "SPICS", "SPIER", "SPIES", "SPILE", "SPINA", "SPINS", "SPIRT", 
-    "SPITS", "SPOOR", "SPOOT", "SPOSH", "SPOTS", "SPRAT", "SPRIT", "STANE", "STAPH", 
-    "STAPS", "STARN", "STARR", "STARS", "STATS", "STEAN", "STEAR", "STEEN", "STEIL", 
-    "STELA", "STELE", "STELL", "STENO", "STENS", "STENT", "STEPS", "STEPT", "STERE", 
-    "STETS", "STICH", "STIES", "STILE", "STIPA", "STIPE", "STIRE", "STIRP", "STIRS", 
-    "STOAE", "STOAI", "STOAS", "STOAT", "STOEP", "STOIT", "STOLN", "STONN", "STOOR", 
-    "STOPE", "STOPS", "STOPT", "STOSS", "STOTS", "STOTT", "STRAE", "STREP", "STRIA", 
-    "STROP", "TAALS", "TAATA", "TACAN", "TACES", "TACET", "TACHE", "TACHO", "TACHS", 
-    "TACOS", "TACTS", "TAELS", "TAHAS", "TAHRS", "TAILS", "TAINS", "TAIRA", "TAISH", 
-    "TAITS", "TALAR", "TALAS", "TALCS", "TALEA", "TALER", "TALES", "TALLS", "TALPA", 
-    "TANAS", "TANHS", "TANNA", "TANTI", "TANTO", "TAPAS", "TAPEN", "TAPES", "TAPET", 
-    "TAPIS", "TAPPA", "TARAS", "TARES", "TARNS", "TAROC", "TAROS", "TARPS", "TARRE", 
-    "TARSI", "TARTS", "TASAR", "TASER", "TASES", "TASSA", "TASSE", "TASSO", "TATAR", 
-    "TATER", "TATES", "TATHS", "TATIE", "TATTS", "TEALS", "TEARS", "TEATS", "TECHS", 
-    "TECTA", "TEELS", "TEENE", "TEENS", "TEERS", "TEHRS", "TEILS", "TEINS", "TELAE", 
-    "TELCO", "TELES", "TELIA", "TELIC", "TELLS", "TELOI", "TELOS", "TENCH", "TENES", 
-    "TENIA", "TENNE", "TENNO", "TENON", "TENTS", "TEPAL", "TEPAS", "TERAI", "TERAS", 
-    "TERCE", "TERES", "TERNE", "TERNS", "TERTS", "TESLA", "TESTA", "TESTE", "TESTS", 
-    "TETES", "TETHS", "TETRA", "TETRI", "THALE", "THALI", "THANA", "THANE", "THANS", 
-    "THARS", "THECA", "THEES", "THEIC", "THEIN", "THENS", "THESP", "THETE", "THILL", 
-    "THINE", "THINS", "THIOL", "THIRL", "THOLE", "THOLI", "THORO", "THORP", "THRAE", 
-    "THRIP", "THROE", "TIANS", "TIARS", "TICAL", "TICCA", "TICES", "TIERS", "TILER", 
-    "TILES", "TILLS", "TILTH", "TILTS", "TINAS", "TINCT", "TINEA", "TINES", "TINTS", 
-    "TIPIS", "TIRES", "TIRLS", "TIROS", "TIRRS", "TITCH", "TITER", "TITIS", "TITRE", 
-    "TOCOS", "TOEAS", "TOHOS", "TOILE", "TOILS", "TOISE", "TOITS", "TOLAN", "TOLAR", 
-    "TOLAS", "TOLES", "TOLLS", "TOLTS", "TONER", "TONES", "TONNE", "TOOLS", "TOONS", 
-    "TOOTS", "TOPEE", "TOPER", "TOPES", "TOPHE", "TOPHI", "TOPHS", "TOPIS", "TOPOI", 
-    "TOPOS", "TORAH", "TORAN", "TORAS", "TORCS", "TORES", "TORIC", "TORII", "TOROS", 
-    "TOROT", "TORRS", "TORSE", "TORSI", "TORTA", "TORTE", "TORTS", "TOSAS", "TOSES", 
-    "TOTER", "TOTES", "TRANS", "TRANT", "TRAPE", "TRAPS", "TRAPT", "TRASS", "TRATS", 
-    "TRATT", "TREEN", "TREES", "TRESS", "TREST", "TRETS", "TRIAC", "TRIER", "TRIES", 
-    "TRILL", "TRINE", "TRINS", "TRIOL", "TRIOR", "TRIOS", "TRIPS", "TRIST", "TROAT", 
-    "TROIS", "TRONA", "TRONC", "TRONE", "TRONS", "TROTH", "TROTS", "TSARS", 
-#endif
-};
-
-#if (WORDLE_USE_RANDOM_GUESS == 3)
-static const uint16_t _num_random_guess_words = 13;  // The valid_words array begins with this many words that are considered the top 3% best options.
-#elif (WORDLE_USE_RANDOM_GUESS == 2)
-static const uint16_t _num_random_guess_words = 257;  // The valid_words array begins with this many words where each letter is different.
-#elif (WORDLE_USE_RANDOM_GUESS == 1)
-static const uint16_t _num_random_guess_words = _num_words;
-#endif
-
-#endif // WORDLE_FACE_DICT_H_
\ No newline at end of file
diff --git a/movement/watch_faces/demo/beeps_face.c b/movement/watch_faces/demo/beeps_face.c
deleted file mode 100644 (file)
index 5211271..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 Wesley
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "beeps_face.h"
-
-void beeps_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(beeps_state_t));
-        memset(*context_ptr, 0, sizeof(beeps_state_t));
-        // Do any one-time tasks in here; the inside of this conditional happens only at boot.
-    }
-}
-
-void beeps_face_activate(void *context) {
-    (void) context;
-}
-
-static void _beep_face_update_lcd(beeps_state_t *state) {
-    char buf[11];
-    const char buzzernote[][7] = {"   5500", "   5827", "   6174","   6541","   6930","   7342","   7778","   8241","   8731","   9250","   9800","  10383","  11000","  11654","  12347","  13081","  13859","  14683","  15556","  16481","  17461","  18500","  19600","  20765","  22000","  23308","  24694","  26163","  27718","  29366","  31113","  32963","  34923","  36999","  39200","  41530","  44000","  46616","  49388","  52325","  55437","  58733","  62225","  65925","  69846","  73999","  78399","  83061","  88000","  93233","  98777"," 104650"," 110873"," 117466"," 124451"," 131851"," 139691"," 147998"," 156798"," 166122"," 176000"," 186466"," 197553"," 209300"," 221746"," 234932"," 248902"," 263702"," 279383"," 295996"," 313596"," 332244"," 352000"," 372931"," 395107"," 418601"," 443492"," 469863"," 497803"," 527404"," 558765"," 591991"," 627193"," 664488"," 704000"," 745862"," 790213"};
-    sprintf(buf, "HZ %s", buzzernote[state->frequency]);
-    watch_display_string(buf, 0);
-}
-
-bool beeps_face_loop(movement_event_t event, void *context) {
-    beeps_state_t *state = (beeps_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _beep_face_update_lcd(state);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            state->frequency = (state->frequency + 1) % 87;
-            _beep_face_update_lcd(state);
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-            if (state->frequency == 0) {
-                watch_buzzer_play_note(BUZZER_NOTE_A1, 500);
-            } else if (state->frequency == 1) {
-                watch_buzzer_play_note(BUZZER_NOTE_A1SHARP_B1FLAT, 500);
-            } else if (state->frequency == 2) {
-                watch_buzzer_play_note(BUZZER_NOTE_B1, 500);
-            } else if (state->frequency == 3) {
-                watch_buzzer_play_note(BUZZER_NOTE_C2, 500);
-            } else if (state->frequency == 4) {
-                watch_buzzer_play_note(BUZZER_NOTE_C2SHARP_D2FLAT, 500);
-            } else if (state->frequency == 5) {
-                watch_buzzer_play_note(BUZZER_NOTE_D2, 500);
-            } else if (state->frequency == 6) {
-                watch_buzzer_play_note(BUZZER_NOTE_D2SHARP_E2FLAT, 500);
-            } else if (state->frequency == 7) {
-                watch_buzzer_play_note(BUZZER_NOTE_E2, 500);
-            } else if (state->frequency == 8) {
-                watch_buzzer_play_note(BUZZER_NOTE_F2, 500);
-            } else if (state->frequency == 9) {
-                watch_buzzer_play_note(BUZZER_NOTE_F2SHARP_G2FLAT, 500);
-            } else if (state->frequency == 10) {
-                watch_buzzer_play_note(BUZZER_NOTE_G2, 500);
-            } else if (state->frequency == 11) {
-                watch_buzzer_play_note(BUZZER_NOTE_G2SHARP_A2FLAT, 500);
-            } else if (state->frequency == 12) {
-                watch_buzzer_play_note(BUZZER_NOTE_A2, 500);
-            } else if (state->frequency == 13) {
-                watch_buzzer_play_note(BUZZER_NOTE_A2SHARP_B2FLAT, 500);
-            } else if (state->frequency == 14) {
-                watch_buzzer_play_note(BUZZER_NOTE_B2, 500);
-            } else if (state->frequency == 15) {
-                watch_buzzer_play_note(BUZZER_NOTE_C3, 500);
-            } else if (state->frequency == 16) {
-                watch_buzzer_play_note(BUZZER_NOTE_C3SHARP_D3FLAT, 500);
-            } else if (state->frequency == 17) {
-                watch_buzzer_play_note(BUZZER_NOTE_D3, 500);
-            } else if (state->frequency == 18) {
-                watch_buzzer_play_note(BUZZER_NOTE_D3SHARP_E3FLAT, 500);
-            } else if (state->frequency == 19) {
-                watch_buzzer_play_note(BUZZER_NOTE_E3, 500);
-            } else if (state->frequency == 20) {
-                watch_buzzer_play_note(BUZZER_NOTE_F3, 500);
-            } else if (state->frequency == 21) {
-                watch_buzzer_play_note(BUZZER_NOTE_F3SHARP_G3FLAT, 500);
-            } else if (state->frequency == 22) {
-                watch_buzzer_play_note(BUZZER_NOTE_G3, 500);
-            } else if (state->frequency == 23) {
-                watch_buzzer_play_note(BUZZER_NOTE_G3SHARP_A3FLAT, 500);
-            } else if (state->frequency == 24) {
-                watch_buzzer_play_note(BUZZER_NOTE_A3, 500);
-            } else if (state->frequency == 25) {
-                watch_buzzer_play_note(BUZZER_NOTE_A3SHARP_B3FLAT, 500);
-            } else if (state->frequency == 26) {
-                watch_buzzer_play_note(BUZZER_NOTE_B3, 500);
-            } else if (state->frequency == 27) {
-                watch_buzzer_play_note(BUZZER_NOTE_C4, 500);
-            } else if (state->frequency == 28) {
-                watch_buzzer_play_note(BUZZER_NOTE_C4SHARP_D4FLAT, 500);
-            } else if (state->frequency == 29) {
-                watch_buzzer_play_note(BUZZER_NOTE_D4, 500);
-            } else if (state->frequency == 30) {
-                watch_buzzer_play_note(BUZZER_NOTE_D4SHARP_E4FLAT, 500);
-            } else if (state->frequency == 31) {
-                watch_buzzer_play_note(BUZZER_NOTE_E4, 500);
-            } else if (state->frequency == 32) {
-                watch_buzzer_play_note(BUZZER_NOTE_F4, 500);
-            } else if (state->frequency == 33) {
-                watch_buzzer_play_note(BUZZER_NOTE_F4SHARP_G4FLAT, 500);
-            } else if (state->frequency == 34) {
-                watch_buzzer_play_note(BUZZER_NOTE_G4, 500);
-            } else if (state->frequency == 35) {
-                watch_buzzer_play_note(BUZZER_NOTE_G4SHARP_A4FLAT, 500);
-            } else if (state->frequency == 36) {
-                watch_buzzer_play_note(BUZZER_NOTE_A4, 500);
-            } else if (state->frequency == 37) {
-                watch_buzzer_play_note(BUZZER_NOTE_A4SHARP_B4FLAT, 500);
-            } else if (state->frequency == 38) {
-                watch_buzzer_play_note(BUZZER_NOTE_B4, 500);
-            } else if (state->frequency == 39) {
-                watch_buzzer_play_note(BUZZER_NOTE_C5, 500);
-            } else if (state->frequency == 40) {
-                watch_buzzer_play_note(BUZZER_NOTE_C5SHARP_D5FLAT, 500);
-            } else if (state->frequency == 41) {
-                watch_buzzer_play_note(BUZZER_NOTE_D5, 500);
-            } else if (state->frequency == 42) {
-                watch_buzzer_play_note(BUZZER_NOTE_D5SHARP_E5FLAT, 500);
-            } else if (state->frequency == 43) {
-                watch_buzzer_play_note(BUZZER_NOTE_E5, 500);
-            } else if (state->frequency == 44) {
-                watch_buzzer_play_note(BUZZER_NOTE_F5, 500);
-            } else if (state->frequency == 45) {
-                watch_buzzer_play_note(BUZZER_NOTE_F5SHARP_G5FLAT, 500);
-            } else if (state->frequency == 46) {
-                watch_buzzer_play_note(BUZZER_NOTE_G5, 500);
-            } else if (state->frequency == 47) {
-                watch_buzzer_play_note(BUZZER_NOTE_G5SHARP_A5FLAT, 500);
-            } else if (state->frequency == 48) {
-                watch_buzzer_play_note(BUZZER_NOTE_A5, 500);
-            } else if (state->frequency == 49) {
-                watch_buzzer_play_note(BUZZER_NOTE_A5SHARP_B5FLAT, 500);
-            } else if (state->frequency == 50) {
-                watch_buzzer_play_note(BUZZER_NOTE_B5, 500);
-            } else if (state->frequency == 51) {
-                watch_buzzer_play_note(BUZZER_NOTE_C6, 500);
-            } else if (state->frequency == 52) {
-                watch_buzzer_play_note(BUZZER_NOTE_C6SHARP_D6FLAT, 500);
-            } else if (state->frequency == 53) {
-                watch_buzzer_play_note(BUZZER_NOTE_D6, 500);
-            } else if (state->frequency == 54) {
-                watch_buzzer_play_note(BUZZER_NOTE_D6SHARP_E6FLAT, 500);
-            } else if (state->frequency == 55) {
-                watch_buzzer_play_note(BUZZER_NOTE_E6, 500);
-            } else if (state->frequency == 56) {
-                watch_buzzer_play_note(BUZZER_NOTE_F6, 500);
-            } else if (state->frequency == 57) {
-                watch_buzzer_play_note(BUZZER_NOTE_F6SHARP_G6FLAT, 500);
-            } else if (state->frequency == 58) {
-                watch_buzzer_play_note(BUZZER_NOTE_G6, 500);
-            } else if (state->frequency == 59) {
-                watch_buzzer_play_note(BUZZER_NOTE_G6SHARP_A6FLAT, 500);
-            } else if (state->frequency == 60) {
-                watch_buzzer_play_note(BUZZER_NOTE_A6, 500);
-            } else if (state->frequency == 61) {
-                watch_buzzer_play_note(BUZZER_NOTE_A6SHARP_B6FLAT, 500);
-            } else if (state->frequency == 62) {
-                watch_buzzer_play_note(BUZZER_NOTE_B6, 500);
-            } else if (state->frequency == 63) {
-                watch_buzzer_play_note(BUZZER_NOTE_C7, 500);
-            } else if (state->frequency == 64) {
-                watch_buzzer_play_note(BUZZER_NOTE_C7SHARP_D7FLAT, 500);
-            } else if (state->frequency == 65) {
-                watch_buzzer_play_note(BUZZER_NOTE_D7, 500);
-            } else if (state->frequency == 66) {
-                watch_buzzer_play_note(BUZZER_NOTE_D7SHARP_E7FLAT, 500);
-            } else if (state->frequency == 67) {
-                watch_buzzer_play_note(BUZZER_NOTE_E7, 500);
-            } else if (state->frequency == 68) {
-                watch_buzzer_play_note(BUZZER_NOTE_F7, 500);
-            } else if (state->frequency == 69) {
-                watch_buzzer_play_note(BUZZER_NOTE_F7SHARP_G7FLAT, 500);
-            } else if (state->frequency == 70) {
-                watch_buzzer_play_note(BUZZER_NOTE_G7, 500);
-            } else if (state->frequency == 71) {
-                watch_buzzer_play_note(BUZZER_NOTE_G7SHARP_A7FLAT, 500);
-            } else if (state->frequency == 72) {
-                watch_buzzer_play_note(BUZZER_NOTE_A7, 500);
-            } else if (state->frequency == 73) {
-                watch_buzzer_play_note(BUZZER_NOTE_A7SHARP_B7FLAT, 500);
-            } else if (state->frequency == 74) {
-                watch_buzzer_play_note(BUZZER_NOTE_B7, 500);
-            } else if (state->frequency == 75) {
-                watch_buzzer_play_note(BUZZER_NOTE_C8, 500);
-            } else if (state->frequency == 76) {
-                watch_buzzer_play_note(BUZZER_NOTE_C8SHARP_D8FLAT, 500);
-            } else if (state->frequency == 77) {
-                watch_buzzer_play_note(BUZZER_NOTE_D8, 500);
-            } else if (state->frequency == 78) {
-                watch_buzzer_play_note(BUZZER_NOTE_D8SHARP_E8FLAT, 500);
-            } else if (state->frequency == 79) {
-                watch_buzzer_play_note(BUZZER_NOTE_E8, 500);
-            } else if (state->frequency == 80) {
-                watch_buzzer_play_note(BUZZER_NOTE_F8, 500);
-            } else if (state->frequency == 81) {
-                watch_buzzer_play_note(BUZZER_NOTE_F8SHARP_G8FLAT, 500);
-            } else if (state->frequency == 82) {
-                watch_buzzer_play_note(BUZZER_NOTE_G8, 500);
-            } else if (state->frequency == 83) {
-                watch_buzzer_play_note(BUZZER_NOTE_G8SHARP_A8FLAT, 500);
-            } else if (state->frequency == 84) {
-                watch_buzzer_play_note(BUZZER_NOTE_A8, 500);
-            } else if (state->frequency == 85) {
-                watch_buzzer_play_note(BUZZER_NOTE_A8SHARP_B8FLAT, 500);
-            } else if (state->frequency == 86) {
-                watch_buzzer_play_note(BUZZER_NOTE_B8, 500);
-            }
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-    return true;
-}
-
-void beeps_face_resign(void *context) {
-    (void) context;
-}
-
diff --git a/movement/watch_faces/demo/beeps_face.h b/movement/watch_faces/demo/beeps_face.h
deleted file mode 100644 (file)
index 5e44c90..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 Wesley
- *
- * 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 BEEPS_FACE_H_
-#define BEEPS_FACE_H_
-
-#include "movement.h"
-
-/*
- * A simple watch face to test the different Buzzer Notes.
- *
- * Press the Light button to play a sound.
- * Press the Alarm button to change the frequency.
- *
- * The watch face displays the frequency of the buzzer it will play
- * this allows you to reference the watch_buzzer.h file to find the
- * corresponding note.
- *
- * The watch_buzzer.h file is found at watch-library/shared/watch/watch_buzzer.h
- */
-
-typedef struct {
-    uint8_t frequency;
-} beeps_state_t;
-
-void beeps_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void beeps_face_activate(void *context);
-bool beeps_face_loop(movement_event_t event, void *context);
-void beeps_face_resign(void *context);
-
-#define beeps_face ((const watch_face_t){ \
-    beeps_face_setup, \
-    beeps_face_activate, \
-    beeps_face_loop, \
-    beeps_face_resign, \
-    NULL, \
-})
-
-#endif // BEEPS_FACE_H_
-
diff --git a/movement/watch_faces/demo/demo_face.c b/movement/watch_faces/demo/demo_face.c
deleted file mode 100644 (file)
index e423b7d..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "demo_face.h"
-#include "watch.h"
-
-typedef enum {
-    DEMO_FACE_TIME = 0,
-    DEMO_FACE_WORLD_TIME,
-    DEMO_FACE_BEATS,
-    DEMO_FACE_TOTP,
-    DEMO_FACE_TEMP_F,
-    DEMO_FACE_TEMP_C,
-    DEMO_FACE_TEMP_LOG_1,
-    DEMO_FACE_TEMP_LOG_2,
-    DEMO_FACE_DAY_ONE,
-    DEMO_FACE_STOPWATCH,
-    DEMO_FACE_PULSOMETER,
-    DEMO_FACE_BATTERY_VOLTAGE,
-    DEMO_FACE_NUM_FACES
-} demo_face_index_t;
-
-void demo_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(demo_face_index_t));
-        memset(*context_ptr, 0, sizeof(demo_face_index_t));
-    }
-}
-
-void demo_face_activate(void *context) {
-    (void) context;
-    movement_request_tick_frequency(0);
-    // ensure the watch never enters low energy mode
-    movement_set_low_energy_timeout(0);
-    movement_set_backlight_dwell(3);
-}
-
-bool demo_face_loop(movement_event_t event, void *context) {
-    demo_face_index_t *screen = (demo_face_index_t *)context;
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_UP:
-            *screen = ((*screen) + 1) % DEMO_FACE_NUM_FACES;
-            // fall through
-        case EVENT_ACTIVATE:
-            switch (*screen) {
-                case DEMO_FACE_TIME:
-                    watch_display_string("TH10101036", 0);
-                    watch_set_colon();
-                    break;
-                case DEMO_FACE_WORLD_TIME:
-                    watch_display_string("UT10 21036", 0);
-                    watch_set_indicator(WATCH_INDICATOR_PM);
-                    break;
-                case DEMO_FACE_BEATS:
-                    watch_display_string("bt   64125", 0);
-                    watch_clear_indicator(WATCH_INDICATOR_PM);
-                    watch_clear_colon();
-                    break;
-                case DEMO_FACE_TOTP:
-                    watch_display_string("2F29808494", 0);
-                    break;
-                case DEMO_FACE_TEMP_F:
-                    watch_display_string("TE  72.1#F", 0);
-                    break;
-                case DEMO_FACE_TEMP_C:
-                    watch_display_string("TE  22.3#C", 0);
-                    break;
-                case DEMO_FACE_TEMP_LOG_1:
-                    watch_display_string("TL  43.6#F", 0);
-                    break;
-                case DEMO_FACE_TEMP_LOG_2:
-                    watch_display_string("AT 6100000", 0);
-                    watch_set_colon();
-                    break;
-                case DEMO_FACE_DAY_ONE:
-                    watch_clear_colon();
-                    watch_display_string("DA   12879", 0);
-                    break;
-                case DEMO_FACE_STOPWATCH:
-                    watch_display_string("ST 01042  ", 0);
-                    watch_set_colon();
-                    break;
-                case DEMO_FACE_PULSOMETER:
-                    watch_display_string("    68 bpn", 0);
-                    watch_clear_colon();
-                    break;
-                case DEMO_FACE_BATTERY_VOLTAGE:
-                    watch_display_string("BA  2.97 V", 0);
-                    break;
-                case DEMO_FACE_NUM_FACES:
-                    // we won't get here, but silence the warning
-                    break;
-            }
-            break;
-        case EVENT_TIMEOUT:
-            // ignore timeout
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void demo_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/demo/demo_face.h b/movement/watch_faces/demo/demo_face.h
deleted file mode 100644 (file)
index ec336e5..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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 DEMO_FACE_H_
-#define DEMO_FACE_H_
-
-/*
- * DEMO FACE
- *
- * This watch was designed for the Crowd Supply marketing team, so they could
- * photograph the various functions of Sensor Watch. The Alarm button advances
- * through static screens that simulate different watch faces.
- *
- * This watch face may only be useful to you if you need to photograph Sensor
- * Watch, i.e. for a blog post.
- */
-
-#include "movement.h"
-
-void demo_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void demo_face_activate(void *context);
-bool demo_face_loop(movement_event_t event, void *context);
-void demo_face_resign(void *context);
-
-#define demo_face ((const watch_face_t){ \
-    demo_face_setup, \
-    demo_face_activate, \
-    demo_face_loop, \
-    demo_face_resign, \
-    NULL, \
-})
-
-#endif // DEMO_FACE_H_
diff --git a/movement/watch_faces/demo/frequency_correction_face.c b/movement/watch_faces/demo/frequency_correction_face.c
deleted file mode 100644 (file)
index badd327..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 <#author_name#>
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "frequency_correction_face.h"
-
-// NOTE: since this face deals directly with the SAM L22's SUPC and RTC registers,
-// it won't build for the simulator or really do anything. so let's not.
-#ifndef __EMSCRIPTEN__
-
-// Waveform output. Comes out on pin A1 of the 9-pin connector. Output is enabled
-// when the watch face is activated and disabled when deactivated.
-// * The RTC's PEREOn enables an event at a frequency of 1024/(2^(n+3)), allowing
-//   event generation from 128 Hz to 1 Hz.
-// * The SUPC's RTCTGL causes the selected output to toggle on an RTC event.
-// Putting it together, PEREO0 generates a 64 Hz square wave on A1, PEREO1 a 32 Hz
-// wave, on and on to an 0.5 Hz square wave for PEREO7.
-static void _enable_output(uint8_t period_event_output) {
-    SUPC->BKOUT.bit.EN = 1;
-    SUPC->BKOUT.bit.RTCTGL = 1;
-    RTC->MODE2.CTRLA.bit.ENABLE = 0;
-    while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
-    RTC->MODE2.EVCTRL.reg = RTC_MODE2_EVCTRL_PEREO(1 << period_event_output);
-    RTC->MODE2.CTRLA.bit.ENABLE = 1;
-    while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
-}
-
-// The display: "FC" for "Frequency Correction" in the title slot; at the top right,
-// the periodic event output that's enabled:: 0 for PEREO0, 1 for PEREO1, etc.
-// The main line displays the frequency correction value from -127 to 127.
-// Controls: A short press on the Light button increases FREQCORR by 1 and a short
-// press on the Alarm button decreases FREQCORR by 1. A long press on ALARM cycles
-// through the available output frequencies.
-static void _update_display(uint8_t period_event_output) {
-    char buf[14];
-    sprintf(buf, "FC%2d%6d", period_event_output, (int8_t)RTC->MODE2.FREQCORR.reg);
-    watch_display_string(buf, 0);
-}
-
-static void _disable_output() {
-    SUPC->BKOUT.bit.EN = 0;
-    SUPC->BKOUT.bit.RTCTGL = 0;
-    RTC->MODE2.CTRLA.bit.ENABLE = 0;
-    while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
-    RTC->MODE2.EVCTRL.reg = RTC_MODE2_EVCTRL_PEREO(0);
-    RTC->MODE2.CTRLA.bit.ENABLE = 1;
-    while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
-}
-
-void frequency_correction_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(frequency_correction_state_t));
-        frequency_correction_state_t *state = (frequency_correction_state_t *)*context_ptr;
-        state->period_event_output = 0;
-    }
-}
-
-void frequency_correction_face_activate(void *context) {
-    frequency_correction_state_t *state = (frequency_correction_state_t *)context;
-    _enable_output(state->period_event_output);
-}
-
-bool frequency_correction_face_loop(movement_event_t event, void *context) {
-    frequency_correction_state_t *state = (frequency_correction_state_t *)context;
-    int8_t freqcorr;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _update_display(state->period_event_output);
-            break;
-        case EVENT_TICK:
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            freqcorr = RTC->MODE2.FREQCORR.reg;
-            if (freqcorr < 127) {
-                RTC->MODE2.FREQCORR.reg++;
-                while(RTC->MODE2.SYNCBUSY.reg);
-            }
-            _update_display(state->period_event_output);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            freqcorr = RTC->MODE2.FREQCORR.reg;
-            if (freqcorr > -127) {
-                RTC->MODE2.FREQCORR.reg--;
-                while(RTC->MODE2.SYNCBUSY.reg);
-            }
-            _update_display(state->period_event_output);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            state->period_event_output = (state->period_event_output + 1) % 8;
-            _update_display(state->period_event_output);
-            _enable_output(state->period_event_output);
-            break;
-        case EVENT_TIMEOUT:
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            watch_start_sleep_animation(500);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void frequency_correction_face_resign(void *context) {
-    (void) context;
-
-    _disable_output();
-}
-
-#else
-
-void frequency_correction_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    (void) context_ptr;
-}
-
-void frequency_correction_face_activate(void *context) {
-    (void) context;
-}
-
-bool frequency_correction_face_loop(movement_event_t event, void *context) {
-    (void) context;
-    (void) event;
-
-    return true;
-}
-
-void frequency_correction_face_resign(void *context) {
-    (void) context;
-}
-
-#endif
diff --git a/movement/watch_faces/demo/frequency_correction_face.h b/movement/watch_faces/demo/frequency_correction_face.h
deleted file mode 100644 (file)
index 7d25866..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 <#author_name#>
- *
- * 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 FREQUENCY_CORRECTION_FACE_H_
-#define FREQUENCY_CORRECTION_FACE_H_
-
-/*
- * FREQUENCY CORRECTION FACE
- *
- * While active, this face generates a square-wave on pin A1 of the 9-pin
- * connector. The output frequency is adjustable from 64 Hz to 0.5 Hz.
- * Long-press ALARM to cycle through available frequencies.
- *
- * This face also displays the value of the watch's frequency-correction
- * register. This setting varies from -127 to +127. Press LIGHT to increment
- * or ALARM to decrement the setting.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint8_t period_event_output;
-} frequency_correction_state_t;
-
-void frequency_correction_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void frequency_correction_face_activate(void *context);
-bool frequency_correction_face_loop(movement_event_t event, void *context);
-void frequency_correction_face_resign(void *context);
-
-#define frequency_correction_face ((const watch_face_t){ \
-    frequency_correction_face_setup, \
-    frequency_correction_face_activate, \
-    frequency_correction_face_loop, \
-    frequency_correction_face_resign, \
-    NULL, \
-})
-
-#endif // FREQUENCY_CORRECTION_FACE_H_
-
diff --git a/movement/watch_faces/demo/hello_there_face.c b/movement/watch_faces/demo/hello_there_face.c
deleted file mode 100644 (file)
index 39ab8bc..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "hello_there_face.h"
-#include "watch.h"
-
-void hello_there_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    // These next two lines just silence the compiler warnings associated with unused parameters.
-    // We have no use for the settings or the watch_face_index, so we make that explicit here.
-    (void) watch_face_index;
-    // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
-    if (*context_ptr == NULL) {
-        // in this case, we allocate an area of memory sufficient to store the stuff we need to track.
-        *context_ptr = malloc(sizeof(hello_there_state_t));
-    }
-}
-
-void hello_there_face_activate(void *context) {
-    // same as above: silence the warning, we don't need to check the settings.
-    // we do however need to set some things in our context. Here we cast it to the correct type...
-    hello_there_state_t *state = (hello_there_state_t *)context;
-    // ...and set the initial state of our watch face. We start out displaying the word 'Hello',
-    state->current_word = 0;
-    // and animate by default.
-    state->animating = true;
-}
-
-bool hello_there_face_loop(movement_event_t event, void *context) {
-    hello_there_state_t *state = (hello_there_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            // on activate and tick, if we are animating,
-            if (state->animating) {
-                // we display the current word,
-                if (state->current_word == 0) watch_display_string("Hello ", 4);
-                else watch_display_string(" there", 4);
-                // and increment it so that it will update on the next tick.
-                state->current_word = (state->current_word + 1) % 2;
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            // when the user presses 'alarm', we toggle the state of the animation. If animating,
-            // we stop; if stopped, we resume.
-            state->animating = !state->animating;   
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // This low energy mode update occurs once a minute, if the watch face is in the
-            // foreground when Movement enters low energy mode. We have the option of supporting
-            // this mode, but since our watch face animates once a second, the "Hello there" face
-            // isn't very useful in this mode. So we choose not to support it. (continued below)
-            break;
-        case EVENT_TIMEOUT:
-            // ... Instead, we respond to the timeout event. This event happens after a configurable
-            // interval on screen (1-30 minutes). The watch will give us this event as a chance to
-            // resign control if we want to, and in this case, we do.
-            // This function will return the watch to the first screen (usually a simple clock),
-            // and it will do it long before the watch enters low energy mode. This ensures we
-            // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
-            movement_move_to_face(0);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void hello_there_face_resign(void *context) {
-    // our watch face, like most watch faces, has nothing special to do when resigning.
-    // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
-    (void) context;
-}
diff --git a/movement/watch_faces/demo/hello_there_face.h b/movement/watch_faces/demo/hello_there_face.h
deleted file mode 100644 (file)
index df942d0..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 HELLO_THERE_FACE_H_
-#define HELLO_THERE_FACE_H_
-
-/*
- * HELLO THERE FACE
- *
- * A simple demo that displays the word "Hello" and then the word "there",
- * on an endless loop. Press ALARM to pause or resume the animation.
- */
-
-#include "movement.h"
-
-typedef struct {
-    uint8_t current_word;
-    bool animating;
-} hello_there_state_t;
-
-void hello_there_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void hello_there_face_activate(void *context);
-bool hello_there_face_loop(movement_event_t event, void *context);
-void hello_there_face_resign(void *context);
-
-#define hello_there_face ((const watch_face_t){ \
-    hello_there_face_setup, \
-    hello_there_face_activate, \
-    hello_there_face_loop, \
-    hello_there_face_resign, \
-    NULL, \
-})
-
-#endif // HELLO_THERE_FACE_H_
diff --git a/movement/watch_faces/demo/lis2dw_logging_face.c b/movement/watch_faces/demo/lis2dw_logging_face.c
deleted file mode 100644 (file)
index 467a99f..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "lis2dw_logging_face.h"
-#include "lis2dw.h"
-#include "watch.h"
-
-// This watch face is just for testing; if we want to build accelerometer support, it will likely have to be part of Movement itself.
-// The watch face only logs events when it is on screen and not in low energy mode, so you should set LE mode to Never when using it
-// and make it the first watch face in the list (so we come back to it from other modes).
-// On an interrupt, it flashes the Signal icon, and displays the axis or axes that were over the threshold.
-// The main display contains, from left to right, the number of interrupt events that were detected in each of the last three minutes.
-// Pressing the alarm button enters the log mode, where the main display shows the number of interrupts detected in each of the last
-// 24 hours (the hour is shown in the top right digit and AM/PM indicator, if the clock is set to 12 hour mode)
-
-static void _lis2dw_logging_face_update_display(lis2dw_logger_state_t *logger_state, lis2dw_wakeup_source_t wakeup_source) {
-    char buf[14];
-    char time_indication_character;
-    int8_t pos;
-    watch_date_time_t date_time;
-
-    if (logger_state->log_ticks) {
-        pos = (logger_state->data_points - 1 - logger_state->display_index) % LIS2DW_LOGGING_NUM_DATA_POINTS;
-        if (pos < 0) {
-            watch_clear_colon();
-            sprintf(buf, "NO   data ");
-        } else {
-            date_time = logger_state->data[pos].timestamp;
-            watch_set_colon();
-            if (movement_clock_mode_24h()) {
-                watch_set_indicator(WATCH_INDICATOR_24H);
-            } else {
-                if (date_time.unit.hour > 11) watch_set_indicator(WATCH_INDICATOR_PM);
-                date_time.unit.hour %= 12;
-                if (date_time.unit.hour == 0) date_time.unit.hour = 12;
-            }
-            switch (logger_state->axis_index) {
-                case 0:
-                    sprintf(buf, "3A%2d%02d%4lu", date_time.unit.hour, date_time.unit.minute, logger_state->data[pos].x_interrupts + logger_state->data[pos].y_interrupts + logger_state->data[pos].z_interrupts);
-                    break;
-                case 1:
-                    sprintf(buf, "XA%2d%02d%4lu", date_time.unit.hour, date_time.unit.minute, logger_state->data[pos].x_interrupts);
-                    break;
-                case 2:
-                    sprintf(buf, "YA%2d%02d%4lu", date_time.unit.hour, date_time.unit.minute, logger_state->data[pos].y_interrupts);
-                    break;
-                case 3:
-                    sprintf(buf, "ZA%2d%02d%4lu", date_time.unit.hour, date_time.unit.minute, logger_state->data[pos].z_interrupts);
-                    break;
-            }
-        }
-    } else {
-        date_time = watch_rtc_get_date_time();
-        watch_clear_colon();
-        watch_clear_indicator(WATCH_INDICATOR_PM);
-        watch_clear_indicator(WATCH_INDICATOR_24H);
-        if ((59 - date_time.unit.second) < 10) time_indication_character = '0' + (59 - date_time.unit.second);
-        else time_indication_character = (date_time.unit.second % 2) ? 'i' : '_';
-        sprintf(buf, "%c%c%c%c%2d%2d%2d",
-            (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_Y) ? 'Y' : ' ',
-            (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_X) ? 'X' : ' ',
-            (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_Z) ? 'Z' : ' ',
-            time_indication_character,
-            logger_state->interrupts[0],
-            logger_state->interrupts[1],
-            logger_state->interrupts[2]);
-    }
-    watch_display_string(buf, 0);
-}
-
-static void _lis2dw_logging_face_log_data(lis2dw_logger_state_t *logger_state) {
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    // we get this call 15 minutes late; i.e. at 6:15 we're logging events for 6:00.
-    // so: if we're at the top of the hour, roll the hour back too (7:00 task logs data for 6:45)
-    if (date_time.unit.minute == 0) date_time.unit.hour = (date_time.unit.hour + 23) % 24;
-
-    // // then roll the minute back.
-    date_time.unit.minute = (date_time.unit.minute + 45) % 60;
-
-    size_t pos = logger_state->data_points % LIS2DW_LOGGING_NUM_DATA_POINTS;
-    logger_state->data[pos].timestamp.reg = date_time.reg;
-    logger_state->data[pos].x_interrupts = logger_state->x_interrupts_this_hour;
-    logger_state->data[pos].y_interrupts = logger_state->y_interrupts_this_hour;
-    logger_state->data[pos].z_interrupts = logger_state->z_interrupts_this_hour;
-    logger_state->data_points++;
-    logger_state->x_interrupts_this_hour = 0;
-    logger_state->y_interrupts_this_hour = 0;
-    logger_state->z_interrupts_this_hour = 0;
-}
-
-void lis2dw_logging_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(lis2dw_logger_state_t));
-        memset(*context_ptr, 0, sizeof(lis2dw_logger_state_t));
-        watch_enable_i2c();
-        lis2dw_begin();
-        lis2dw_set_low_power_mode(LIS2DW_LP_MODE_2); // lowest power 14-bit mode, 25 Hz is 3.5 µA @ 1.8V w/ low noise, 3µA without
-        lis2dw_set_low_noise_mode(true); // consumes a little more power
-        lis2dw_set_range(LIS2DW_CTRL6_VAL_RANGE_4G);
-        lis2dw_set_data_rate(LIS2DW_DATA_RATE_25_HZ); // is this enough for training?
-        // threshold is 1/64th of full scale, so for a FS of ±4G this is 1.25G
-        lis2dw_configure_wakeup_int1(10, true, false);
-    }
-}
-
-void lis2dh_logging_face_activate(void *context) {
-    lis2dh_logger_state_t *logger_state = (lis2dh_logger_state_t *)context;
-    logger_state->display_index = 0;
-    logger_state->log_ticks = 0;
-    HAL_GPIO_A4_in();
-}
-
-bool lis2dw_logging_face_loop(movement_event_t event, void *context) {
-    lis2dw_logger_state_t *logger_state = (lis2dw_logger_state_t *)context;
-    lis2dw_wakeup_source_t wakeup_source = 0;
-    lis2dw_interrupt_source_t interrupt_source = 0;
-
-    switch (event.event_type) {
-        case EVENT_LIGHT_BUTTON_DOWN:
-            logger_state->axis_index = (logger_state->axis_index + 1) % 4;
-            logger_state->log_ticks = 255;
-            _lis2dw_logging_face_update_display(logger_state, wakeup_source);
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            if (logger_state->log_ticks) logger_state->display_index = (logger_state->display_index + 1) % LIS2DW_LOGGING_NUM_DATA_POINTS;
-            logger_state->log_ticks = 255;
-            logger_state->axis_index = 0;
-            _lis2dw_logging_face_update_display(logger_state, wakeup_source);
-            break;
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            if (logger_state->log_ticks > 0) {
-                logger_state->log_ticks--;
-            } else {
-                logger_state->display_index = 0;
-            }
-            interrupt_source = lis2dw_get_interrupt_source();
-            if (interrupt_source) {
-                watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-                wakeup_source = lis2dw_get_wakeup_source();
-                logger_state->interrupts[0]++;
-                if (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_X) logger_state->x_interrupts_this_hour++;
-                if (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_Y) logger_state->y_interrupts_this_hour++;
-                if (wakeup_source & LIS2DW_WAKEUP_SRC_WAKEUP_Z) logger_state->z_interrupts_this_hour++;
-            } else {
-                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-            }
-            _lis2dw_logging_face_update_display(logger_state, wakeup_source);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            _lis2dw_logging_face_log_data(logger_state);
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void lis2dw_logging_face_resign(void *context) {
-    (void) context;
-    HAL_GPIO_A4_off();
-}
-
-movement_watch_face_advisory_t lis2dw_logging_face_advise(void *context) {
-    lis2dw_logger_state_t *logger_state = (lis2dw_logger_state_t *)context;
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    movement_watch_face_advisory_t retval = { 0 };
-
-    // this is kind of an abuse of the API, but, let's use the 1 minute tick to shift all our data over.
-    logger_state->interrupts[2] = logger_state->interrupts[1];
-    logger_state->interrupts[1] = logger_state->interrupts[0];
-    logger_state->interrupts[0] = 0;
-
-    // and do our logging task every 15 minutes
-    retval.wants_background_task = date_time.unit.minute % 15 == 0;
-
-    return retval;
-}
diff --git a/movement/watch_faces/demo/lis2dw_logging_face.h b/movement/watch_faces/demo/lis2dw_logging_face.h
deleted file mode 100644 (file)
index 49d48ee..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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 LIS2DW_LOGGING_FACE_H_
-#define LIS2DW_LOGGING_FACE_H_
-
-/*
- * LIS2DW Accelerometer Data Logger
- *
- * This is an experimental watch face for logging data on the “Sensor Watch
- * Motion Express” board. I will add more documentation for this watch face
- * once this sensor board is more widely available.
- */
-
-#include "movement.h"
-#include "watch.h"
-
-#define LIS2DW_LOGGING_NUM_DATA_POINTS (96)
-
-typedef struct {
-    watch_date_time_t timestamp;
-    uint32_t x_interrupts;
-    uint32_t y_interrupts;
-    uint32_t z_interrupts;
-} lis2dw_logger_data_point_t;
-
-typedef struct {
-    uint8_t display_index;  // the index we are displaying on screen
-    uint8_t axis_index;     // the index we are displaying on screen
-    uint8_t log_ticks;      // when the user taps the ALARM button, we enter log mode
-    int32_t data_points;    // the absolute number of data points logged
-    uint8_t interrupts[3];  // the number of interrupts we have logged in each of the last 3 minutes
-    uint32_t x_interrupts_this_hour;  // the number of interrupts we have logged in the last hour
-    uint32_t y_interrupts_this_hour;  // the number of interrupts we have logged in the last hour
-    uint32_t z_interrupts_this_hour;  // the number of interrupts we have logged in the last hour
-    lis2dw_logger_data_point_t data[LIS2DW_LOGGING_NUM_DATA_POINTS];
-} lis2dw_logger_state_t;
-
-void lis2dw_logging_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void lis2dw_logging_face_activate(void *context);
-bool lis2dw_logging_face_loop(movement_event_t event, void *context);
-void lis2dw_logging_face_resign(void *context);
-movement_watch_face_advisory_t lis2dw_logging_face_advise(void *context);
-
-#define lis2dw_logging_face ((const watch_face_t){ \
-    lis2dw_logging_face_setup, \
-    lis2dw_logging_face_activate, \
-    lis2dw_logging_face_loop, \
-    lis2dw_logging_face_resign, \
-    lis2dw_logging_face_advise, \
-})
-
-#endif // LIS2DW_LOGGING_FACE_H_
diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c
deleted file mode 100644 (file)
index 7258474..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "accelerometer_data_acquisition_face.h"
-#include "watch_utility.h"
-#include "lis2dw.h"
-#include "spiflash.h"
-
-#define ACCELEROMETER_RANGE LIS2DW_RANGE_4_G
-#define ACCELEROMETER_LPMODE LIS2DW_LP_MODE_2
-#define ACCELEROMETER_FILTER LIS2DW_BANDWIDTH_FILTER_DIV2
-#define ACCELEROMETER_LOW_NOISE true
-#define SECONDS_TO_RECORD 15
-
-static const char activity_types[][3] = {
-    "TE",   // Testing
-    "ID",   // Idle
-    "OF",   // Off-wrist
-    "SL",   // Sleeping
-    "WH",   // Washing Hands
-    "WA",   // Walking
-    "WB",   // Walking with Beverage
-    "JO",   // Jogging
-    "RU",   // Running
-    "BI",   // Biking
-    "HI",   // Hiking
-    "EL",   // Elliptical
-    "SU",   // Stairs Up
-    "SD",   // Stairs Down
-    "WL",   // Weight Lifting
-};
-
-static void update(accelerometer_data_acquisition_state_t *state);
-static void update_settings(accelerometer_data_acquisition_state_t *state);
-static void advance_current_setting(accelerometer_data_acquisition_state_t *state);
-static void start_reading(accelerometer_data_acquisition_state_t *state);
-static void continue_reading(accelerometer_data_acquisition_state_t *state);
-static void finish_reading(accelerometer_data_acquisition_state_t *state);
-static bool wait_for_flash_ready(void);
-static int16_t get_next_available_page(void);
-static void write_buffer_to_page(uint8_t *buf, uint16_t page);
-static void write_page(accelerometer_data_acquisition_state_t *state);
-static void log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading, uint8_t centiseconds);
-
-void accelerometer_data_acquisition_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)*context_ptr;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(accelerometer_data_acquisition_state_t));
-        memset(*context_ptr, 0, sizeof(accelerometer_data_acquisition_state_t));
-        state = (accelerometer_data_acquisition_state_t *)*context_ptr;
-        state->beep_with_countdown = true;
-        state->countdown_length = 3;
-    }
-    spi_flash_init();
-    wait_for_flash_ready();
-    uint8_t buf[256] = {0xFF};
-    spi_flash_read_data(0, buf, 256);
-    if (buf[0] & 0xF0) {
-        // mark first four pages as used
-        buf[0] = 0x0F;
-        wait_for_flash_ready();
-        HAL_GPIO_A3_clr();
-        spi_flash_command(CMD_ENABLE_WRITE);
-        wait_for_flash_ready();
-        spi_flash_write_data(0, buf, 256);
-    }
-
-}
-
-void accelerometer_data_acquisition_face_activate(void *context) {
-    accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
-    state->next_available_page = get_next_available_page();
-}
-
-bool accelerometer_data_acquisition_face_loop(movement_event_t event, void *context) {
-    accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            switch (state->mode) {
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
-                    update(state);
-                    if (state->repeat_ticks > 0) {
-                        state->repeat_ticks--;
-                        if (state->repeat_ticks == 0) {
-                            state->countdown_ticks = state->countdown_length;
-                            state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN;
-                        }
-                    }
-                    break;
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
-                    if (state->next_available_page < 0) {
-                        state->countdown_ticks = 0;
-                        state->repeat_ticks = 0;
-                        state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
-                    }
-                    if (state->countdown_ticks > 0) {
-                        state->countdown_ticks--;
-                        printf("countdown: %d\n", state->countdown_ticks);
-                        if (state->countdown_ticks == 0) {
-                            // at zero, begin reading
-                            state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING;
-                            state->reading_ticks = SECONDS_TO_RECORD + 1;
-                            // also beep if the user asked for it
-                            if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
-                            start_reading(state);
-                        } else if (state->countdown_ticks < 3) {
-                            // beep for last two ticks before reading
-                            if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C5, 75);
-                        }
-                    }
-                    update(state);
-                    break;
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING:
-                    if (state->reading_ticks > 0) {
-                        state->reading_ticks--;
-                        if (state->reading_ticks > 0) {
-                            continue_reading(state);
-                        } else {
-                            finish_reading(state);
-                            state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
-                            watch_buzzer_play_note(BUZZER_NOTE_C4, 125);
-                            watch_buzzer_play_note(BUZZER_NOTE_REST, 50);
-                            watch_buzzer_play_note(BUZZER_NOTE_C4, 125);
-                        }
-                    }
-                    update(state);
-                    break;
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
-                    update_settings(state);
-                    break;
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            switch (state->mode) {
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
-                    state->activity_type_index = (state->activity_type_index + 1) % (sizeof(activity_types) / sizeof(activity_types[0]));
-                    update(state);
-                    break;
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
-                    state->settings_page++;
-                    if (state->settings_page > ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT) {
-                        state->settings_page = 0;
-                        state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
-                        update(state);
-                    } else {
-                        update_settings(state);
-                    }
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            movement_illuminate_led();
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            printf("Alarm up! Mode is %d\n", state->mode);
-            switch (state->mode) {
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
-                    state->countdown_ticks = state->countdown_length;
-                    printf("Setting countdown ticks to %d\n", state->countdown_ticks);
-                    state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN;
-                    printf("and mode to %d\n", state->mode);
-                    update(state);
-                    break;
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
-                    // cancel countdown
-                    state->countdown_ticks = 0;
-                    state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
-                    update(state);
-                    break;
-                case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
-                    advance_current_setting(state);
-                    update_settings(state);
-                    break;
-                default:
-                    break;
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            printf("Alarm long\n");
-            if (state->mode == ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE) {
-                state->repeat_ticks = 0;
-                state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS;
-                update_settings(state);
-            }
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void accelerometer_data_acquisition_face_resign(void *context) {
-    accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
-    if (state->reading_ticks) {
-        state->reading_ticks = 0;
-        finish_reading(state);
-    }
-    state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
-    state->settings_page = 0;
-    state->countdown_ticks = 0;
-    state->repeat_ticks = 0;
-    state->reading_ticks = 0;
-}
-
-static void update(accelerometer_data_acquisition_state_t *state) {
-    char buf[14];
-    uint8_t ticks = 0;
-    switch (state->mode) {
-        case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
-            ticks = state->countdown_length;
-            break;
-        case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
-            ticks = state->countdown_ticks;
-            break;
-        case ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING:
-            ticks = state->reading_ticks;
-            break;
-        default:
-            ticks = 0;
-            break;
-    }
-    sprintf(buf, "%s%2dre%2d#o",
-            activity_types[state->activity_type_index],
-            ticks,
-            (8192 - state->next_available_page) / 82);
-    watch_display_string(buf, 0);
-
-    watch_set_colon();
-
-    // special case: display full if full, <1% if nearly full
-    if (state->next_available_page < 0) watch_display_string(" FUL", 6);
-    else if (state->next_available_page > 8110) watch_display_string("<1", 6);
-
-    // Bell if beep enabled
-    if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL);
-    else watch_clear_indicator(WATCH_INDICATOR_BELL);
-
-    // Signal if sensing
-    if (state->reading_ticks) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-    else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-
-    // LAP if repeating
-    if (state->repeat_ticks) watch_set_indicator(WATCH_INDICATOR_LAP);
-    else watch_clear_indicator(WATCH_INDICATOR_LAP);
-
-}
-
-static void update_settings(accelerometer_data_acquisition_state_t *state) {
-    char buf[13];
-    watch_clear_colon();
-    if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL);
-    else watch_clear_indicator(WATCH_INDICATOR_BELL);
-    switch (state->settings_page) {
-        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND:
-            sprintf(buf, "SO  Beep %c", state->beep_with_countdown ? 'Y' : 'N');
-            watch_display_string(buf, 0);
-            break;
-        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY:
-            sprintf(buf, "DL  %2d SeC", state->countdown_length);
-            watch_display_string(buf, 0);
-            break;
-        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT:
-            if (state->repeat_interval == 0) {
-                watch_display_string("rE  none  ", 0);
-            } else {
-                sprintf(buf, "rE  %2dn&in", state->repeat_interval / 60);
-                watch_display_string(buf, 0);
-            }
-            break;
-    }
-}
-
-static void advance_current_setting(accelerometer_data_acquisition_state_t *state) {
-    switch (state->settings_page) {
-        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND:
-            state->beep_with_countdown = !state->beep_with_countdown;
-            break;
-        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY:
-            // this is so lazy, i'm sorry
-            if (state->countdown_length == 1) state->countdown_length = 3;
-            else if (state->countdown_length == 3) state->countdown_length = 10;
-            else if (state->countdown_length == 10) state->countdown_length = 30;
-            else state->countdown_length = 1;
-            break;
-        case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT:
-            if (state->repeat_interval == 0) state->repeat_interval = 60;
-            else if (state->repeat_interval == 60) state->repeat_interval = 600;
-            else if (state->repeat_interval == 600) state->repeat_interval = 1800;
-            else if (state->repeat_interval == 1800) state->repeat_interval = 3600;
-            else state->repeat_interval = 0;
-            break;
-    }
-}
-
-static int16_t get_next_available_page(void) {
-    uint8_t buf[256] = {0};
-
-    uint16_t page = 0;
-    for(int16_t i = 0; i < 4; i++) {
-        wait_for_flash_ready();
-        spi_flash_read_data(i * 256, buf, 256);
-        for(int16_t j = 0; j < 256; j++) {
-            if(buf[j] == 0) {
-                page += 8;
-            } else {
-                page += __builtin_clz(((uint32_t)buf[j]) << 24);
-                break;
-            }
-        }
-    }
-
-    if (page >= 8192) return -1;
-
-    return page;
-}
-
-static void write_buffer_to_page(uint8_t *buf, uint16_t page) {
-    uint32_t address = 256 * page;
-
-    wait_for_flash_ready();
-    HAL_GPIO_A3_clr();
-    spi_flash_command(CMD_ENABLE_WRITE);
-    wait_for_flash_ready();
-    HAL_GPIO_A3_clr();
-    spi_flash_write_data(address, buf, 256);
-    wait_for_flash_ready();
-
-    uint8_t buf2[256];
-    HAL_GPIO_A3_clr();
-    spi_flash_read_data(address, buf2, 256);
-    wait_for_flash_ready();
-
-    uint8_t used_pages[256] = {0xFF};
-    uint16_t address_to_mark_used = page / 8;
-    uint8_t header_page = address_to_mark_used / 256;
-    uint8_t used_byte = 0x7F >> (page % 8);
-    uint8_t offset_in_buf = address_to_mark_used % 256;
-
-    printf("\twrite 256 bytes to address %ld, page %d.\n", address, page);
-    for(int i = 0; i < 256; i++) {
-        if (buf[i] != buf2[i]) {
-            printf("\tData mismatch detected at offset  %d: %d != %d.\n", i, buf[i], buf2[i]);
-        }
-    }
-
-    HAL_GPIO_A3_clr();
-    spi_flash_read_data(header_page * 256, used_pages, 256);
-    used_pages[offset_in_buf] = used_byte;
-    HAL_GPIO_A3_clr();
-    spi_flash_command(CMD_ENABLE_WRITE);
-    wait_for_flash_ready();
-    HAL_GPIO_A3_clr();
-    spi_flash_write_data(header_page * 256, used_pages, 256);
-    wait_for_flash_ready();
-}
-
-static bool wait_for_flash_ready(void) {
-    HAL_GPIO_A3_clr();
-    bool ok = true;
-    uint8_t read_status_response[1] = {0x00};
-    do {
-        ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1);
-    } while ((read_status_response[0] & 0x3) != 0);
-    delay_ms(1); // why do i need this?
-    HAL_GPIO_A3_set();
-    return ok;
-}
-
-static void write_page(accelerometer_data_acquisition_state_t *state) {
-    if (state->next_available_page > 0) {
-        write_buffer_to_page((uint8_t *)(state->records), state->next_available_page);
-        wait_for_flash_ready();
-        state->next_available_page++;
-    }
-    state->pos = 0;
-    memset(state->records, 0xFF, sizeof(state->records));
-}
-
-static void log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading, uint8_t centiseconds) {
-    accelerometer_data_acquisition_record_t record;
-    record.data.x.record_type = ACCELEROMETER_DATA_ACQUISITION_DATA;
-    record.data.y.lpmode = ACCELEROMETER_LPMODE;
-    record.data.z.filter = ACCELEROMETER_FILTER;
-    record.data.x.accel = (reading.x >> 2) + 8192;
-    record.data.y.accel = (reading.y >> 2) + 8192;
-    record.data.z.accel = (reading.z >> 2) + 8192;
-    record.data.counter = 100 * (SECONDS_TO_RECORD - state->reading_ticks + 1) + centiseconds;
-    printf("logged data point for %d\n", record.data.counter);
-    state->records[state->pos++] = record;
-    if (state->pos >= 32) {
-        write_page(state);
-    }
-}
-
-static void start_reading(accelerometer_data_acquisition_state_t *state) {
-    printf("Start reading\n");
-    watch_enable_i2c();
-    lis2dw_begin();
-    lis2dw_set_data_rate(LIS2DW_DATA_RATE_25_HZ);
-    lis2dw_set_range(ACCELEROMETER_RANGE);
-    lis2dw_set_low_power_mode(ACCELEROMETER_LPMODE);
-    lis2dw_set_bandwidth_filtering(ACCELEROMETER_FILTER);
-    if (ACCELEROMETER_LOW_NOISE) lis2dw_set_low_noise_mode(true);
-    lis2dw_enable_fifo();
-
-    accelerometer_data_acquisition_record_t record;
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
-    record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER;
-    record.header.info.range = ACCELEROMETER_RANGE;
-    record.header.info.temperature = lis2dw_get_temperature();
-    record.header.char1 = activity_types[state->activity_type_index][0];
-    record.header.char2 = activity_types[state->activity_type_index][1];
-    record.header.timestamp = state->starting_timestamp;
-
-    state->records[state->pos++] = record;
-    lis2dw_fifo_t fifo;
-    lis2dw_read_fifo(&fifo); // dump the fifo, this starts a fresh round of data in continue_reading
-}
-
-static void continue_reading(accelerometer_data_acquisition_state_t *state) {
-    printf("Continue reading\n");
-    lis2dw_fifo_t fifo;
-    lis2dw_read_fifo(&fifo);
-
-    fifo.count = min(fifo.count, 25); // hacky, but we need a consistent data rate; if we got a 26th data point, chuck it.
-    uint8_t offset = 4 * (25 - fifo.count); // also hacky: we're sometimes short at the start. align to beginning of next second.
-    // TODO: use the threshold interrupt for this, will mean we get consistent 25 Hz data as the accelerometer sees it.
-
-    for(int i = 0; i < fifo.count; i++) {
-        log_data_point(state, fifo.readings[i], i * 4 + offset);
-    }
-}
-
-static void finish_reading(accelerometer_data_acquisition_state_t *state) {
-    printf("Finish reading\n");
-    if (state->pos != 0) {
-        write_page(state);
-    }
-    lis2dw_set_data_rate(LIS2DW_DATA_RATE_POWERDOWN);
-    watch_disable_i2c();
-
-    state->repeat_ticks = state->repeat_interval;
-}
diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h
deleted file mode 100644 (file)
index 107faea..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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 ACCELEROMETER_DATA_ACQUISITION_FACE_H_
-#define ACCELEROMETER_DATA_ACQUISITION_FACE_H_
-
-/*
- * ACCELEROMETER DATA ACQUISITION
- *
- * TODO: Add description here, including controls.
- */
-
-#include "movement.h"
-
-#define ACCELEROMETER_DATA_ACQUISITION_INVALID ((uint64_t)(0b11))   // all bits are 1 when the flash is erased
-#define ACCELEROMETER_DATA_ACQUISITION_HEADER ((uint64_t)(0b10))
-#define ACCELEROMETER_DATA_ACQUISITION_DATA ((uint64_t)(0b01))    
-#define ACCELEROMETER_DATA_ACQUISITION_DELETED ((uint64_t)(0b00))   // You can always write a 0 to any 1 bit
-
-typedef union {
-    struct {
-        struct {
-            uint16_t record_type : 2;   // see above, helps us identify record types when reading back
-            uint16_t range : 2;         // accelerometer range (see lis2dw_range_t)
-            uint16_t temperature : 12;  // raw value from the temperature sensor
-        } info;
-        uint8_t char1 : 8;              // First character of the activity type
-        uint8_t char2 : 8;              // Second character of the activity type
-        uint32_t timestamp : 32;        // UNIX timestamp for the measurement
-    } header;
-    struct {
-        struct {
-            uint16_t record_type : 2;   // duplicate; this is the same field as info above
-            uint16_t accel : 14;        // X acceleration value, raw, offset by 8192
-        } x;
-        struct {
-            uint16_t lpmode : 2;        // low power mode (see lis2dw_low_power_mode_t)
-            uint16_t accel : 14;        // Y acceleration value, raw, offset by 8192
-        } y;
-        struct {
-            uint16_t filter : 2;        // bandwidth filtering selection (see lis2dw_bandwidth_filtering_mode_t)
-            uint16_t accel : 14;        // Z acceleration value, raw, offset by 8192
-        } z;
-        uint32_t counter : 16;          // number of centiseconds since timestamp in header
-    } data;
-    uint64_t value;
-} accelerometer_data_acquisition_record_t;
-
-typedef enum {
-    ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE,
-    ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN,
-    ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING,
-    ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS,
-} accelerometer_data_acquisition_mode_t;
-
-typedef enum {
-    ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND,
-    ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY,
-    ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT,
-    // ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_NAME,
-} accelerometer_data_acquisition_settings_page_t;
-
-typedef struct {
-    // mode
-    accelerometer_data_acquisition_mode_t mode;
-    accelerometer_data_acquisition_settings_page_t settings_page;
-    // current settings
-    uint8_t activity_type_index;   // active activity type
-    bool beep_with_countdown;   // should we beep at the countdown
-    uint8_t countdown_length;   // how many seconds to count down
-    uint16_t repeat_interval;   // how many seconds to wait for a repeat
-    // info about the flash chip
-    int16_t next_available_page;
-    // transient properties
-    uint8_t countdown_ticks;
-    uint8_t repeat_ticks;
-    uint8_t reading_ticks;
-    uint32_t starting_timestamp;
-    accelerometer_data_acquisition_record_t records[32];
-    uint16_t pos;
-} accelerometer_data_acquisition_state_t;
-
-void accelerometer_data_acquisition_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void accelerometer_data_acquisition_face_activate(void *context);
-bool accelerometer_data_acquisition_face_loop(movement_event_t event, void *context);
-void accelerometer_data_acquisition_face_resign(void *context);
-
-#define accelerometer_data_acquisition_face ((const watch_face_t){ \
-    accelerometer_data_acquisition_face_setup, \
-    accelerometer_data_acquisition_face_activate, \
-    accelerometer_data_acquisition_face_loop, \
-    accelerometer_data_acquisition_face_resign, \
-    NULL, \
-})
-
-#endif // ACCELEROMETER_DATA_ACQUISITION_FACE_H_
diff --git a/movement/watch_faces/sensor/alarm_thermometer_face.c b/movement/watch_faces/sensor/alarm_thermometer_face.c
deleted file mode 100644 (file)
index 62b0c88..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 Christian Buschau
- *
- * 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.
- */
-
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-#include "alarm_thermometer_face.h"
-#include "thermistor_driver.h"
-
-static float _alarm_thermometer_face_update(bool in_fahrenheit) {
-    thermistor_driver_enable();
-    float temperature_c = thermistor_driver_get_temperature();
-    char buf[14];
-    if (in_fahrenheit) {
-        sprintf(buf, "%4.1f#F", temperature_c * 1.8 + 32.0);
-    } else {
-        sprintf(buf, "%4.1f#C", temperature_c);
-    }
-    watch_display_string(buf, 4);
-    thermistor_driver_disable();
-    return temperature_c;
-}
-
-static void _alarm_thermometer_face_clear(int last[]) {
-    for (size_t i = 0; i < LAST_SIZE; i++) {
-        last[i] = INT_MIN;
-    }
-}
-
-void alarm_thermometer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(alarm_thermometer_state_t));
-        memset(*context_ptr, 0, sizeof(alarm_thermometer_state_t));
-    }
-}
-
-void alarm_thermometer_face_activate(void *context) {
-    alarm_thermometer_state_t *state = (alarm_thermometer_state_t *)context;
-    state->mode = MODE_NORMAL;
-    _alarm_thermometer_face_clear(state->last);
-    watch_display_string("AT", 0);
-}
-
-bool alarm_thermometer_face_loop(movement_event_t event, void *context) {
-    alarm_thermometer_state_t *state = (alarm_thermometer_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            _alarm_thermometer_face_update(movement_use_imperial_units());
-            break;
-        case EVENT_TICK:
-            if (watch_rtc_get_date_time().unit.second % 5 == 0) {
-                switch (state->mode) {
-                    case MODE_NORMAL:
-                        _alarm_thermometer_face_update(movement_use_imperial_units());
-                        break;
-                    case MODE_ALARM:
-                        for (size_t i = LAST_SIZE - 1; i > 0; i--) {
-                            state->last[i] = state->last[i - 1];
-                        }
-                        state->last[0] = roundf(_alarm_thermometer_face_update(movement_use_imperial_units()) * 10.0f);
-                        bool constant = true;
-                        for (size_t i = 1; i < LAST_SIZE; i++) {
-                            if (state->last[i - 1] != state->last[i]) {
-                                constant = false;
-                                break;
-                            }
-                        }
-                        if (constant) {
-                            state->mode = MODE_FREEZE;
-                            watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-                            movement_play_alarm();
-                        }
-                        break;
-                    case MODE_FREEZE:
-                        break;
-                }
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch (state->mode) {
-                case MODE_NORMAL:
-                    state->mode = MODE_ALARM;
-                    watch_set_indicator(WATCH_INDICATOR_BELL);
-                    _alarm_thermometer_face_clear(state->last);
-                    break;
-                case MODE_FREEZE:
-                    state->mode = MODE_NORMAL;
-                    watch_clear_indicator(WATCH_INDICATOR_BELL);
-                    watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-                    break;
-                case MODE_ALARM:
-                    state->mode = MODE_NORMAL;
-                    watch_clear_indicator(WATCH_INDICATOR_BELL);
-                    _alarm_thermometer_face_update(movement_use_imperial_units());
-                    break;
-            }
-            if (movement_button_should_sound()) {
-                watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->mode != MODE_FREEZE) {
-                movement_set_use_imperial_units(!movement_use_imperial_units());
-                _alarm_thermometer_face_update(movement_use_imperial_units());
-            }
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            if (!watch_sleep_animation_is_running()) {
-                state->mode = MODE_NORMAL;
-                watch_clear_indicator(WATCH_INDICATOR_BELL);
-                watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
-                watch_start_sleep_animation(1000);
-            }
-            if (watch_rtc_get_date_time().unit.minute % 5 == 0) {
-                _alarm_thermometer_face_update(movement_use_imperial_units());
-                watch_display_string("  ", 8);
-            }
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void alarm_thermometer_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/sensor/alarm_thermometer_face.h b/movement/watch_faces/sensor/alarm_thermometer_face.h
deleted file mode 100644 (file)
index 6b1ecde..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2024 Christian Buschau
- *
- * 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 ALARM_THERMOMETER_FACE_H_
-#define ALARM_THERMOMETER_FACE_H_
-
-#include <limits.h>
-#include "movement.h"
-
-/*
- * ALARM THERMOMETER
- *
- * This watch face shows the current temperature in degrees Celsius. Press and
- * hold the alarm button to toggle between Celsius and Fahrenheit. Press and
- * release the alarm button to start a "timer". The watch will sound an alarm
- * when the temperature remains constant for at least 30 seconds and the
- * temperature will stop updating until you press the alarm button. You can
- * cancel the alarm by pressing the button again. If the temperature doesn't
- * remain constant until the low energy timeout is reached, the alarm will stop.
- * This is useful to measure e.g. the room temperature. If you lay off your
- * watch from your wrist, it will take some time until it cools down, and will
- * notify you when the measurement is constant enough.
- * THIS WATCH FACE IS NOT INTENDED TO DIAGNOSE, TREAT, CURE OR PREVENT ANY
- * DISEASE.
- */
-
-#define LAST_SIZE 6
-
-typedef enum {
-    MODE_NORMAL,
-    MODE_ALARM,
-    MODE_FREEZE
-} alarm_thermometer_mode_t;
-
-typedef struct {
-    int last[LAST_SIZE];
-    alarm_thermometer_mode_t mode;
-} alarm_thermometer_state_t;
-
-void alarm_thermometer_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void alarm_thermometer_face_activate(void *context);
-bool alarm_thermometer_face_loop(movement_event_t event, void *context);
-void alarm_thermometer_face_resign(void *context);
-
-#define alarm_thermometer_face ((const watch_face_t){ \
-    alarm_thermometer_face_setup, \
-    alarm_thermometer_face_activate, \
-    alarm_thermometer_face_loop, \
-    alarm_thermometer_face_resign, \
-    NULL, \
-})
-
-#endif // ALARM_THERMOMETER_FACE_H_
diff --git a/movement/watch_faces/sensor/lightmeter_face.c b/movement/watch_faces/sensor/lightmeter_face.c
deleted file mode 100644 (file)
index 243613c..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 CC
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "lightmeter_face.h"
-#include "watch_utility.h"
-#include "watch_slcd.h"
-
-uint16_t lightmeter_mod(uint16_t m, uint16_t n) { return (m%n + n)%n; }  
-
-void lightmeter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(lightmeter_state_t));
-        lightmeter_state_t *state = (lightmeter_state_t*) *context_ptr;
-        state->waiting_for_conversion = 0;
-        state->lux = 0.0;
-        state->mode = 0;
-        state->iso = LIGHTMETER_ISO_100;
-        state->ap = LIGHTMETER_AP_4P0;
-    }
-}
-
-void lightmeter_face_activate(void *context) {
-    lightmeter_state_t *state = (lightmeter_state_t*) context;
-    state->waiting_for_conversion = 0;
-    lightmeter_show_ev(state); // Print most current reading
-    watch_enable_i2c();
-    return;
-}
-
-void lightmeter_show_ev(lightmeter_state_t *state) {
-
-    float ev = max(min(
-                 log2(state->lux) + 
-                 lightmeter_isos[state->iso].ev + 
-                 LIGHTMETER_CALIBRATION,
-            99), -9);
-    int evt = round(2*ev); // Truncated EV
-
-    // Print EV
-    char strbuff[7];
-    watch_clear_all_indicators();
-    watch_display_string("EV        ", 0); 
-
-    sprintf(strbuff, "%2i", (uint16_t) abs(evt/2)); // Print whole part of EV 
-    watch_display_string(strbuff, 2); 
-    if(evt%2) watch_set_indicator(WATCH_INDICATOR_LAP); // Indicate half stop
-    if(ev<0) watch_set_pixel(1,9);  // Indicate negative EV
-
-    // Handle lux mode
-    if(state->mode == 1) {
-        sprintf(strbuff, "%6.0f", min(state->lux, 999999.0)); 
-        watch_display_string(strbuff, 4); 
-        return;
-    }
-
-    // Find and print best shutter speed
-    uint16_t bestsh = 0;
-    float besterr = 1.0/0.0;
-    float errbuf = 1.0/0.0;
-    float comp_ev = ev + lightmeter_aps[state->ap].ev; 
-    for(uint16_t ind = 2; ind < LIGHTMETER_N_SHS; ind++) {
-        errbuf = comp_ev + lightmeter_shs[ind].ev;
-        if( fabs(errbuf) < fabs(besterr)) {
-            besterr = errbuf;
-            bestsh = ind;
-        }
-    }
-    if(besterr >= 0.5) watch_display_string(lightmeter_shs[LIGHTMETER_SH_HIGH].str, 4); 
-    else if(besterr <= -0.5) watch_display_string(lightmeter_shs[LIGHTMETER_SH_LOW].str, 4); 
-    else watch_display_string(lightmeter_shs[bestsh].str, 4); 
-
-    // Print aperture
-    watch_display_string(lightmeter_aps[state->ap].str, 7); 
-    return;
-}
-
-bool lightmeter_face_loop(movement_event_t event, void *context) {
-    lightmeter_state_t *state = (lightmeter_state_t*) context;
-    
-    opt3001_Config_t c;
-    switch (event.event_type) {
-        case EVENT_TICK:
-            if(state->waiting_for_conversion) { // Check if measurement is ready...
-                c = opt3001_readConfig(lightmeter_addr);
-                if(c.ConversionReady) {
-                    state->waiting_for_conversion = 0;
-                    opt3001_t result = opt3001_readResult(lightmeter_addr);
-                    state->lux = result.lux;
-                    lightmeter_show_ev(state); 
-                } 
-            }
-            break;
-
-        case EVENT_ALARM_BUTTON_UP: // Increment aperture 
-            state->ap = lightmeter_mod(state->ap+1, LIGHTMETER_N_APS);
-
-            lightmeter_show_ev(state); 
-            break;
-
-        case EVENT_LIGHT_BUTTON_UP: // Decrement aperture 
-            if(state->ap == 0) state->ap = LIGHTMETER_N_APS-1; 
-            else state->ap = lightmeter_mod(state->ap-1, LIGHTMETER_N_APS);
-
-            lightmeter_show_ev(state); 
-            break;
-
-        case EVENT_LIGHT_LONG_PRESS: // Cycle ISO
-            state->iso = lightmeter_mod(state->iso+1, LIGHTMETER_N_ISOS);
-
-            watch_clear_all_indicators();
-            watch_display_string("EV  ", 0); 
-            watch_display_string(lightmeter_isos[state->iso].str, 4); 
-            break;
-
-        case EVENT_ALARM_LONG_PRESS: // Take measurement
-            opt3001_writeConfig(lightmeter_addr, lightmeter_takeNewReading);
-            state->waiting_for_conversion = 1;
-
-            watch_clear_all_indicators();
-            watch_display_string("EV  ", 0); 
-            watch_display_string(lightmeter_isos[state->iso].str, 4); 
-            watch_set_indicator(WATCH_INDICATOR_SIGNAL);
-            break;
-
-        case EVENT_MODE_LONG_PRESS: // Toggle mode
-            state->mode = !state->mode;
-            lightmeter_show_ev(state); 
-            break;
-
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-    return true;
-}
-
-void lightmeter_face_resign(void *context) {
-    (void) context;
-    opt3001_writeConfig(lightmeter_addr, lightmeter_off);
-    watch_disable_i2c();
-    return;
-}
diff --git a/movement/watch_faces/sensor/lightmeter_face.h b/movement/watch_faces/sensor/lightmeter_face.h
deleted file mode 100644 (file)
index fcb7cda..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 CC
- *
- * 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 LIGHTMETER_FACE_H_
-#define LIGHTMETER_FACE_H_
-
-/*
- * Aperture-priority Light Meter Face
- *
- * Tested with the "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001" flexboard.
- * This flexboard could use a revision: 
- *
- *  - The thermistor components should be moved west a mm or flipped to the backside
- *    to avoid stressing the flexboard against the processor so much.
- *  - The 'no connect' pad falls off easily.
- *
- * Controls:
- *
- *  - Trigger a measurement by long-pressing Alarm.
- *    Sensor integration is happening when the Signal indicator is on.
- *
- *  - ISO setting can be cycled by long-pressing Light.
- *    During integration the current ISO setting will be displayed. 
- *
- *  - EV measurement in the top right: "LAP" indicates "half stop". 
- *    So "LAP -1" means EV = -1.5. Likewise "LAP 13" means EV = +13.5  
- *
- *  - Aperture in the bottom right: the last 3 main digits are the f-stop. 
- *    Adjust this number in half-stop increments using Alarm = +1/2 and Light = -1/2. 
- *
- *  - Best shutter speed in the bottom left: the first 3 digits are the shutter speed. 
- *    Some special chars are needed here: "-" = seconds, "h" = extra half second, "K" = thousands.
- *    "HI" or "LO" if there's no shutter in the dictionary within 0.5 stops of correct exposure.
- *
- *  - Mode long-press changes the main digits to show raw sensor lux measurements.
- */
-
-#include "movement.h"
-#include "opt3001.h"
-
-#define LIGHTMETER_CALIBRATION 2.58
-typedef struct { 
-    char * str;
-    float ev;
-} lightmeter_ev_t;
-
-static const lightmeter_ev_t lightmeter_isos[] = {
-    {" i  25", -2},
-    {" i  50", -1},
-    {" i 100", 0},
-    {" i 160", 0.68},
-    {" i 200", 1},
-    {" i 400", 2},
-    {" i 800", 3},
-    {" i1600", 4}};
-typedef enum { 
-    LIGHTMETER_ISO_25, LIGHTMETER_ISO_50, LIGHTMETER_ISO_100, LIGHTMETER_ISO_160, LIGHTMETER_ISO_200, LIGHTMETER_ISO_400, LIGHTMETER_ISO_800, LIGHTMETER_ISO_1600, 
-    LIGHTMETER_N_ISOS
-} lightmeter_iso_t; 
-
-static const lightmeter_ev_t lightmeter_aps[] = {
-    {"1.4", 0},
-    {"1.8", -0.5},
-    {"2.0", -1},
-    {"2.4", -1.5},
-    {"2.8", -2},
-    {"3.3", -2.5},
-    {"4.0", -3},
-    {"4.8", -3.5},
-    {"5.6", -4},
-    {"6.7", -4.5},
-    {"8.0", -5},
-    {"9.5", -5.5},
-    {"11.", -6},
-    {"13.", -6.5},
-    {"16.", -7},
-    {"19.", -7.5},
-    {"22.", -8}};
-typedef enum { 
-    LIGHTMETER_AP_1P4, LIGHTMETER_AP_1P8, LIGHTMETER_AP_2P0, LIGHTMETER_AP_2P4, LIGHTMETER_AP_2P8, LIGHTMETER_AP_3P3, LIGHTMETER_AP_4P0, LIGHTMETER_AP_4P8, LIGHTMETER_AP_5P6, LIGHTMETER_AP_6P7, LIGHTMETER_AP_8P0, LIGHTMETER_AP_9P5, 
-    LIGHTMETER_AP_11, LIGHTMETER_AP_13, LIGHTMETER_AP_16, LIGHTMETER_AP_19, LIGHTMETER_AP_22,
-    LIGHTMETER_N_APS
-} lightmeter_ap_t; 
-
-static const lightmeter_ev_t lightmeter_shs[] = {
-    {"LO-",   99},
-    {"HI ",  -99},
-    {"30-",  5.0},
-    {"20-",  4.5},
-    {"15-",  4.0},
-    {"11-",  3.5},
-    {"8- ",  3.0},
-    {"6- ",  2.5},
-    {"4- ",  2.0},
-    {"3- ",  1.5},
-    {"2- ",  1.0},
-    {"1h-",  0.5},
-    {"1  ",  0.0},
-    {"1h ", -0.5},
-    {"2  ", -1.0},
-    {"3  ", -1.5},
-    {"4  ", -2.0},
-    {"6  ", -2.5},
-    {"8  ", -3.0},
-    {"12 ", -3.5},
-    {"15 ", -4.0},
-    {"20 ", -4.5},
-    {"30 ", -5.0},
-    {"45 ", -5.5},
-    {"60 ", -6.0},
-    {"90 ", -6.5},
-    {"125", -7.0},
-    {"180", -7.5},
-    {"250", -8.0},
-    {"350", -8.5},
-    {"500", -9.0},
-    {"750", -9.5},
-    {"1K ", -10.0},
-    {"1K5", -10.5},
-    {"2K ", -11.0},
-    {"3K ", -11.5},
-    {"4K ", -12.0},
-    {"6K ", -12.5},
-    {"8K ", -13.0}};
-typedef enum { 
-    LIGHTMETER_SH_LOW, LIGHTMETER_SH_HIGH, 
-    LIGHTMETER_SH_30S, LIGHTMETER_SH_20S, LIGHTMETER_SH_15S, LIGHTMETER_SH_11S, LIGHTMETER_SH_8S, LIGHTMETER_SH_6S, LIGHTMETER_SH_3S, LIGHTMETER_SH_4S, LIGHTMETER_SH_2S, LIGHTMETER_SH_1HS, 
-    LIGHTMETER_SH_1, LIGHTMETER_SH_1H, LIGHTMETER_SH_2, LIGHTMETER_SH_3, LIGHTMETER_SH_4, LIGHTMETER_SH_6, LIGHTMETER_SH_8, LIGHTMETER_SH_12, LIGHTMETER_SH_15, LIGHTMETER_SH_20, LIGHTMETER_SH_30, LIGHTMETER_SH_45, LIGHTMETER_SH_60, LIGHTMETER_SH_90, LIGHTMETER_SH_125, LIGHTMETER_SH_180, LIGHTMETER_SH_250, LIGHTMETER_SH_350, LIGHTMETER_SH_500, LIGHTMETER_SH_750,
-    LIGHTMETER_SH_1K, LIGHTMETER_SH_1K5, LIGHTMETER_SH_2K, LIGHTMETER_SH_3K, LIGHTMETER_SH_4K, LIGHTMETER_SH_6K, LIGHTMETER_SH_8K,
-    LIGHTMETER_N_SHS
-} lightmeter_sh_t; 
-
-typedef struct {
-    lightmeter_iso_t iso;
-    lightmeter_ap_t ap;
-    bool waiting_for_conversion;
-    float lux;
-    int mode; 
-} lightmeter_state_t;
-
-static const opt3001_Config_t lightmeter_takeNewReading = { 
-    .RangeNumber = 0B1100,
-    .ConversionTime = 0B1,
-    .Latch = 0B1,
-    .ModeOfConversionOperation = 0B01
-};
-
-static const opt3001_Config_t lightmeter_off = { 
-    .ModeOfConversionOperation = 0B00
-};
-
-uint16_t lightmeter_mod(uint16_t m, uint16_t n); 
-
-void lightmeter_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void lightmeter_face_activate(void *context);
-void lightmeter_show_ev(lightmeter_state_t *state);
-bool lightmeter_face_loop(movement_event_t event, void *context);
-void lightmeter_face_resign(void *context);
-
-static const uint8_t lightmeter_addr = 0x44;
-
-#define lightmeter_face ((const watch_face_t){ \
-    lightmeter_face_setup, \
-    lightmeter_face_activate, \
-    lightmeter_face_loop, \
-    lightmeter_face_resign, \
-    NULL, \
-})
-
-#endif // LIGHTMETER_FACE_H_
diff --git a/movement/watch_faces/sensor/minmax_face.c b/movement/watch_faces/sensor/minmax_face.c
deleted file mode 100644 (file)
index 7f52006..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Mark Blyth
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "minmax_face.h"
-#include "thermistor_driver.h"
-#include "watch.h"
-
-
-static float _get_displayed_temperature_c(minmax_state_t *state){
-    float min_temp = 1000;
-    float max_temp = -1000;
-    for(int i = 0; i < LOGGING_DATA_POINTS; i++){
-      if(state->hourly_maxs[i] > max_temp){
-         max_temp = state->hourly_maxs[i];
-       }
-      if(state->hourly_mins[i] < min_temp){
-         min_temp = state->hourly_mins[i];
-       }
-    }
-    if(state->show_min) return min_temp;
-    return max_temp;
-}
-
-
-static void _minmax_face_log_data(minmax_state_t *logger_state) {
-    thermistor_driver_enable();
-    size_t pos = (size_t) watch_rtc_get_date_time().unit.hour;
-    float temp_c = thermistor_driver_get_temperature();
-    // If no data yet, initialise with current temperature
-    if(!logger_state->have_logged){
-      logger_state->have_logged = true;
-      for(int i=0; i<LOGGING_DATA_POINTS; i++){
-       logger_state->hourly_mins[i] = temp_c;
-       logger_state->hourly_maxs[i] = temp_c;
-      }
-    }
-    // On new hour, update lists to current temperature
-    else if(watch_rtc_get_date_time().unit.minute < 2){
-      logger_state->hourly_mins[pos] = temp_c;
-      logger_state->hourly_maxs[pos] = temp_c;
-    }
-    // Log hourly highs and lows
-    else if(logger_state->hourly_mins[pos] > temp_c){
-      logger_state->hourly_mins[pos] = temp_c;
-    }
-    else if(logger_state->hourly_maxs[pos] < temp_c){
-      logger_state->hourly_maxs[pos] = temp_c;
-    }
-    thermistor_driver_disable();
-}
-
-static void _minmax_face_update_display(float temperature_c, bool in_fahrenheit) {
-    char buf[14];
-    if (in_fahrenheit) {
-        sprintf(buf, "%4.0f#F", temperature_c * 1.8 + 32.0);
-    } else {
-        sprintf(buf, "%4.0f#C", temperature_c);
-    }
-    watch_display_string(buf, 4);
-}
-
-
-void minmax_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(minmax_state_t));
-        memset(*context_ptr, 0, sizeof(minmax_state_t));
-    }
-}
-
-
-void minmax_face_activate(void *context) {
-    minmax_state_t *state = (minmax_state_t *)context;
-    state->show_min = true;
-    watch_display_string("MN", 0); // Start with minimum temp
-}
-
-bool minmax_face_loop(movement_event_t event, void *context) {
-    minmax_state_t *state = (minmax_state_t *)context;
-    float temp_c;
-
-    switch (event.event_type) {
-       case EVENT_ACTIVATE:
-           temp_c = _get_displayed_temperature_c(state);
-           _minmax_face_update_display(temp_c, movement_use_imperial_units());
-            break;
-
-       case EVENT_LIGHT_LONG_PRESS:
-        movement_set_use_imperial_units(!movement_use_imperial_units());
-           temp_c = _get_displayed_temperature_c(state);
-           _minmax_face_update_display(temp_c, movement_use_imperial_units());
-            break;
-
-       case EVENT_ALARM_BUTTON_UP:
-           state->show_min = !state->show_min;
-           if(state->show_min){
-               watch_display_string("MN", 0);
-           } else {
-               watch_display_string("MX", 0);
-           }
-           temp_c = _get_displayed_temperature_c(state);
-           _minmax_face_update_display(temp_c, movement_use_imperial_units());
-            break;
-
-       case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-       case EVENT_BACKGROUND_TASK:
-            _minmax_face_log_data(state);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-    return true;
-}
-
-
-void minmax_face_resign(void *context) {
-    (void) context;
-}
-
-
-movement_watch_face_advisory_t minmax_face_advise(void *context) {
-    (void) context;
-
-    // this will get called at the top of each minute; always request bg task
-    movement_watch_face_advisory_t retval = {
-        .wants_background_task = 1,
-    };
-
-    return retval;
-}
diff --git a/movement/watch_faces/sensor/minmax_face.h b/movement/watch_faces/sensor/minmax_face.h
deleted file mode 100644 (file)
index 330f532..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Mark Blyth
- *
- * 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 MINMAX_FACE_H_
-#define MINMAX_FACE_H_
-
-#include "movement.h"
-#include "watch.h"
-
-#define LOGGING_DATA_POINTS (24)
-
-/*
- * Log for the min. and max. temperature over the last 24h.
- *
- * Temperature is logged once a minute, every minute. Results are
- * stored, noting the highest and lowest temperatures observed within
- * any given hour. The watch face then displays the minimum or maximum
- * temperature recorded over the last 24h.
- *
- * A long press of the light button changes units between Celsius and
- * Fahrenheit. Pressing the alarm button switches between displaying the
- * minimum and maximum observed temperatures. If no buttons are pressed,
- * the watch face will eventually time out and return home.
- */
-
-typedef struct {
-  bool show_min;
-  bool have_logged;
-  float hourly_mins[LOGGING_DATA_POINTS];
-  float hourly_maxs[LOGGING_DATA_POINTS];
-} minmax_state_t;
-
-void minmax_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void minmax_face_activate(void *context);
-bool minmax_face_loop(movement_event_t event, void *context);
-void minmax_face_resign(void *context);
-movement_watch_face_advisory_t minmax_face_advise(void *context);
-
-#define minmax_face ((const watch_face_t){ \
-    minmax_face_setup, \
-    minmax_face_activate, \
-    minmax_face_loop, \
-    minmax_face_resign, \
-    minmax_face_advise, \
-})
-
-#endif // MINMAX_FACE_H_
-
diff --git a/movement/watch_faces/sensor/thermistor_testing_face.c b/movement/watch_faces/sensor/thermistor_testing_face.c
deleted file mode 100644 (file)
index 964ef44..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "thermistor_testing_face.h"
-#include "thermistor_driver.h"
-#include "watch.h"
-
-static void _thermistor_testing_face_update_display(bool in_fahrenheit) {
-    thermistor_driver_enable();
-    float temperature_c = thermistor_driver_get_temperature();
-    char buf[14];
-    if (in_fahrenheit) {
-        sprintf(buf, "%4.1f#F", temperature_c * 1.8 + 32.0);
-    } else {
-        sprintf(buf, "%4.1f#C", temperature_c);
-    }
-    watch_display_string(buf, 4);
-    thermistor_driver_disable();
-}
-
-void thermistor_testing_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    (void) context_ptr;
-    // force one setting: never enter low energy mode.
-    // I'm using this watch face to test the temperature sensor boards; there's no need for it.
-    movement_set_low_energy_timeout(0);
-}
-
-void thermistor_testing_face_activate(void *context) {
-    (void) context;
-    watch_display_string("TE", 0);
-    movement_request_tick_frequency(8);
-}
-
-bool thermistor_testing_face_loop(movement_event_t event, void *context) {
-    (void) context;
-    switch (event.event_type) {
-        case EVENT_ALARM_BUTTON_DOWN:
-            movement_set_use_imperial_units(!movement_use_imperial_units());
-            _thermistor_testing_face_update_display(movement_use_imperial_units());
-            break;
-        case EVENT_ACTIVATE:
-        case EVENT_TICK:
-            _thermistor_testing_face_update_display(movement_use_imperial_units());
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    return true;
-}
-
-void thermistor_testing_face_resign(void *context) {
-    (void) context;
-}
diff --git a/movement/watch_faces/sensor/thermistor_testing_face.h b/movement/watch_faces/sensor/thermistor_testing_face.h
deleted file mode 100644 (file)
index 720a8ea..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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 THERMISTOR_TESTING_FACE_H_
-#define THERMISTOR_TESTING_FACE_H_
-
-/*
- * THERMISTOR TESTING FACE
- *
- * This watch face is designed for testing temperature sensor boards.
- * It displays temperature readings at a relatively fast rate of 8 Hz,
- * and disables low energy mode so my testing device doesn't sleep.
- * You more than likely want to use temperature_display_face instead.
- *
- * Press ALARM to toggle display of metric vs. imperial units.
- */
-
-#include "movement.h"
-
-void thermistor_testing_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void thermistor_testing_face_activate(void *context);
-bool thermistor_testing_face_loop(movement_event_t event, void *context);
-void thermistor_testing_face_resign(void *context);
-
-#define thermistor_testing_face ((const watch_face_t){ \
-    thermistor_testing_face_setup, \
-    thermistor_testing_face_activate, \
-    thermistor_testing_face_loop, \
-    thermistor_testing_face_resign, \
-    NULL, \
-})
-
-#endif // THERMISTOR_TESTING_FACE_H_
diff --git a/movement/watch_faces/settings/finetune_face.c b/movement/watch_faces/settings/finetune_face.c
deleted file mode 100644 (file)
index e1b64a9..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "finetune_face.h"
-#include "nanosec_face.h"
-#include "watch_utility.h"
-
-extern nanosec_state_t nanosec_state;
-
-int total_adjustment;
-int8_t finetune_page;
-
-void finetune_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    (void) context_ptr;
-    // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
-}
-
-void finetune_face_activate(void *context) {
-    (void) context;
-
-    // Handle any tasks related to your watch face coming on screen.
-    watch_display_string("FT", 0);
-    total_adjustment = 0;
-    finetune_page = 0;
-}
-
-static float finetune_get_hours_passed(void) {
-    uint32_t current_time = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), 0);
-    return (current_time - nanosec_state.last_correction_time) / 3600.0f;
-}
-
-static float finetune_get_correction(void) {
-    return total_adjustment / (finetune_get_hours_passed() * 3600.0f) * 1000.0f;
-}
-
-static void finetune_update_display(void) {
-    char buf[25];
-
-    if (finetune_page == 0) {
-        watch_display_string("FT", 0);
-        watch_date_time_t date_time = watch_rtc_get_date_time();
-        sprintf(buf, "%02d", date_time.unit.second);
-        watch_display_string(buf, 8);
-
-        sprintf(buf, "%04d", abs(total_adjustment));
-        watch_display_string(buf, 4);
-
-        if (total_adjustment < 0) {
-            watch_display_string("--", 2);
-        } else {
-            watch_display_string("  ", 2);
-        }
-    } else if (finetune_page == 1) {
-        float hours = finetune_get_hours_passed();
-
-        sprintf(buf, "DT  %4d%02d", (int)hours, (int)(fmodf(hours, 1.) * 100));
-        watch_display_string(buf, 0);
-    } else if (finetune_page == 2) {
-        if (finetune_get_hours_passed() < 6) {
-            sprintf(buf, " F  6HR   ");
-            watch_display_string(buf, 0);
-        } else {
-            float correction = finetune_get_correction();
-            sprintf(buf, " F%s%2d%04d", (total_adjustment < 0) ? " -" : "  ", (int)fabsf(correction), (int)(remainderf(fabsf(correction), 1.) * 10000));
-            watch_display_string(buf, 0);
-        }
-    }
-}
-
-static void finetune_adjust_subseconds(int delta) {
-    // Update display first ot make it appear faster for the user
-    if (delta > 500)
-        total_adjustment += (delta - 1000);
-    else
-        total_adjustment += delta;
-    finetune_update_display();
-
-    // Then delay clock
-    watch_rtc_enable(false);
-    delay_ms(delta);
-    if (delta > 500) {
-        watch_date_time_t date_time = watch_rtc_get_date_time();
-        date_time.unit.second = (date_time.unit.second + 1) % 60;
-        if (date_time.unit.second == 0) { // Overflow
-            date_time.unit.minute = (date_time.unit.minute + 1) % 60;
-            if (date_time.unit.minute == 0) { // Overflow
-                date_time.unit.hour = (date_time.unit.hour + 1) % 24;
-                if (date_time.unit.hour == 0) // Overflow
-                    date_time.unit.day++;
-            }
-        }
-        watch_rtc_set_date_time(date_time);
-    }
-    watch_rtc_enable(true);
-}
-
-static void finetune_update_correction_time(void) {
-    // Update aging, as we update correciton time - we must bake accrued aging into static offset
-    nanosec_state.freq_correction += roundf(nanosec_get_aging() * 100);
-
-    // Remember when we last corrected time
-    nanosec_state.last_correction_time = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), 0);
-    nanosec_save();
-    movement_move_to_face(0); // Go to main face after saving settings
-}
-
-bool finetune_face_loop(movement_event_t event, void *context) {
-
-    (void) context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Show your initial UI here.
-            finetune_update_display();
-            break;
-
-        case EVENT_TICK:
-            // If needed, update your display here, at canonical 0.5sec position.
-            // We flash green LED once per minute to measure clock error, when we are not on first screen
-            if (finetune_page!=0) {
-                watch_date_time_t date_time;
-                date_time = watch_rtc_get_date_time();
-                if (date_time.unit.second == 0) {
-                    watch_set_led_green();
-                    #ifndef __EMSCRIPTEN__
-                    delay_us(500);
-                    #endif
-                    watch_set_led_off();
-                }
-            }
-
-            finetune_update_display();
-            break;
-
-        case EVENT_MODE_BUTTON_UP:
-            // Only allow for fast exit when correction is 0!!!
-            if (finetune_page == 0 && total_adjustment == 0) {
-                movement_move_to_next_face();
-            } else {
-                finetune_page = (finetune_page + 1) % 3;
-                finetune_update_display();
-            }
-            break;
-
-        case EVENT_MODE_LONG_PRESS:
-            // You shouldn't need to change this case; Mode almost always moves to the next watch face.
-            finetune_page = (finetune_page + 1) % 3;
-            finetune_update_display();
-            break;
-
-        case EVENT_LIGHT_LONG_PRESS:
-            // We are making it slower by 250ms
-            if (finetune_page == 0) {
-                finetune_adjust_subseconds(250);
-            } else if (finetune_page == 2 && finetune_get_hours_passed() >= 6) {
-                // Applying ppm correction, only if >6 hours passed
-                nanosec_state.freq_correction += (int)round(finetune_get_correction() * 100);
-                finetune_update_correction_time();
-            }
-            break;
-
-        case EVENT_LIGHT_BUTTON_UP:
-            // We are making it slower by 25ms
-            if (finetune_page == 0) {
-                finetune_adjust_subseconds(25);
-            }
-            break;
-
-        case EVENT_ALARM_LONG_PRESS:
-            if (finetune_page == 0) {
-                finetune_adjust_subseconds(750);
-            } else if (finetune_page == 2) {
-                // Exit without applying correction to ppm, but update correction time
-                finetune_update_correction_time();
-            }
-            break;
-
-        case EVENT_ALARM_BUTTON_UP:
-            if (finetune_page == 0) {
-                finetune_adjust_subseconds(975);
-            }
-            break;
-
-        case EVENT_TIMEOUT:
-            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
-            // you may uncomment this line to move back to the first watch face in the list:
-            if (total_adjustment == 0) // Timeout only works if no adjustment was made
-                movement_move_to_face(0);
-            break;
-
-        case EVENT_LOW_ENERGY_UPDATE:
-            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
-            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
-            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
-            // watch_start_sleep_animation(500);
-            break;
-
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
-    // you should return false since the PWM driver does not operate in standby mode.
-    return true;
-}
-
-void finetune_face_resign(void *context) {
-    (void) context;
-
-    if (total_adjustment != 0) {
-        finetune_update_correction_time();
-    }
-}
diff --git a/movement/watch_faces/settings/finetune_face.h b/movement/watch_faces/settings/finetune_face.h
deleted file mode 100644 (file)
index 905df76..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
- *
- * 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 FINETUNE_FACE_H_
-#define FINETUNE_FACE_H_
-
-/*
- * FINETUNE face
- *
- * FineTune face allows to align watch with sub-second precision in 25/250ms
- * accuracy. Counts time since previous finetune, and allows to calculate &
- * apply ppm correction for nanosec.
- *
- * Best used in conjunction with the NANOSEC face.
- *
- * Main screen - adjust delay (light/alarm)
- * Long MODE press - show hours since previous finetune
- * Long MODE press - show calculated ppm correction.
- *  You can apply it with long LIGHT, or just reset finetune timer with long ALARM.
- *
- * Finetune will apply crystal aging correction on every finetune save
- * (as aging is calculated since "last finetune" timestamp); but you should
- * worry about aging only on second/third years of watch calibration (if you
- * are really looking at less than 10 seconds per year of error).
- *
- * Warning, do not use at the first second of a month, as you might stay at
- * the same month and it will surprise you. Just wait 1 second...We are not
- * fully replicating RTC timer behavior when RTC is off.
- * Simulating months and years is... too much complexity.
- *
- * For full usage instructions, please refer to the wiki:
- *  https://www.sensorwatch.net/docs/watchfaces/nanosec/
- */
-
-#include "movement.h"
-
-typedef struct {
-    // Anything you need to keep track of, put it here!
-    uint8_t unused;
-} finetune_state_t;
-
-void finetune_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void finetune_face_activate(void *context);
-bool finetune_face_loop(movement_event_t event, void *context);
-void finetune_face_resign(void *context);
-
-#define finetune_face ((const watch_face_t){ \
-    finetune_face_setup, \
-    finetune_face_activate, \
-    finetune_face_loop, \
-    finetune_face_resign, \
-    NULL, \
-})
-
-#endif // FINETUNE_FACE_H_
-
diff --git a/movement/watch_faces/settings/nanosec_face.c b/movement/watch_faces/settings/nanosec_face.c
deleted file mode 100644 (file)
index 8efdacc..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "thermistor_driver.h"
-#include "nanosec_face.h"
-#include "filesystem.h"
-#include "watch_utility.h"
-
-int16_t freq_correction_residual = 0; // Dithering 0.1ppm correction, does not need to be configured.
-int16_t freq_correction_previous = -30000;
-#define dithering 31
-
-nanosec_state_t nanosec_state;
-
-#define nanosec_max_screen 7
-int8_t nanosec_screen = 0;
-bool nanosec_changed = false; // We try to avoid saving settings when no changes were made, for example when just browsing through face
-
-const float voltage_coefficient = 0.241666667 * dithering; // 10 * ppm/V. Nominal frequency is at 3V.
-
-static void nanosec_init_profile(void) {
-    nanosec_changed = true;
-    nanosec_state.correction_cadence = 10;
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    nanosec_state.last_correction_time = watch_utility_date_time_to_unix_time(date_time, 0);
-
-    // init data after changing profile - do that once per profile selection
-    switch (nanosec_state.correction_profile) {
-        case 0: // No tempco, no dithering
-            nanosec_state.freq_correction = 0;
-            nanosec_state.center_temperature = 2500;
-            nanosec_state.quadratic_tempco = 0;
-            nanosec_state.cubic_tempco = 0;
-            nanosec_state.aging_ppm_pa = 0;
-            break;
-        case 1: // No tempco, with dithering
-            nanosec_state.freq_correction = 0;
-            nanosec_state.center_temperature = 2500;
-            nanosec_state.quadratic_tempco = 0;
-            nanosec_state.cubic_tempco = 0;
-            nanosec_state.aging_ppm_pa = 0;
-            break;
-        case 2: // Datasheet correction
-            nanosec_state.freq_correction = 0;
-            nanosec_state.center_temperature = 2500;
-            nanosec_state.quadratic_tempco = 3400;
-            nanosec_state.cubic_tempco = 0;
-            nanosec_state.aging_ppm_pa = 0;
-            break;
-        case 3: // Datasheet correction + cubic coefficient
-            nanosec_state.freq_correction = 0;
-            nanosec_state.center_temperature = 2500;
-            nanosec_state.quadratic_tempco = 3400;
-            nanosec_state.cubic_tempco = 1360;
-            nanosec_state.aging_ppm_pa = 0;
-            break;
-        case 4: // Full custom
-            nanosec_state.freq_correction = 1768;
-            nanosec_state.center_temperature = 2653;
-            nanosec_state.quadratic_tempco = 4091;
-            nanosec_state.cubic_tempco = 1359;
-            nanosec_state.aging_ppm_pa = 0;
-            break;
-    }
-}
-
-static void nanosec_internal_write_RTC_correction(int16_t value, int16_t sign) {
-    if (sign == 0) {
-        if (value == freq_correction_previous)
-            return; // Do not write same correction value twice
-        freq_correction_previous = value;
-    } else {
-        if (value == -freq_correction_previous)
-            return; // Do not write same correction value twice
-        freq_correction_previous = -value;
-    }
-
-    watch_rtc_freqcorr_write(value, sign);
-}
-
-// Receives clock correction, already corrected for temperature and battery voltage, multiplied by "dithering"
-static void apply_RTC_correction(int16_t correction) {
-    correction += freq_correction_residual;
-    int32_t correction_lr = (int32_t)correction * 2 / dithering; // int division
-    if (correction_lr & 1) {
-        if (correction_lr > 0) {
-            correction_lr++;
-        } else {
-            correction_lr--;
-        }
-    }
-    correction_lr >>= 1;
-    freq_correction_residual = correction - correction_lr * dithering;
-
-    // Warning! Freqcorr is not signed int8!!
-    // First we clamp it to 8-bit range
-    if (correction_lr > 127) {
-        nanosec_internal_write_RTC_correction(127, 0);
-    } else if (correction_lr < -127) {
-        nanosec_internal_write_RTC_correction(127, 1);
-    } else if (correction_lr < 0) {
-        nanosec_internal_write_RTC_correction(abs(correction_lr), 1);
-    } else { // correction
-        nanosec_internal_write_RTC_correction(correction_lr, 0);
-    }
-}
-
-// User-related saves
-void nanosec_ui_save(void) {
-    if (nanosec_changed)
-        nanosec_save();
-}
-
-// This is low-level save function, that can be used by other faces
-void nanosec_save(void) {
-    if (nanosec_state.correction_profile == 0) {
-        freq_correction_residual = 0;
-        apply_RTC_correction(nanosec_state.freq_correction * 1.0f * dithering / 100); // Will be divided by dithering inside, final resolution is mere 1ppm
-    }
-
-    filesystem_write_file("nanosec.ini", (char*)&nanosec_state, sizeof(nanosec_state));
-    nanosec_changed = false;
-}
-
-void nanosec_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-
-    if (*context_ptr == NULL) {
-        if (filesystem_get_file_size("nanosec.ini") != sizeof(nanosec_state)) {
-            // No previous ini or old version of ini file - create new config file
-            nanosec_state.correction_profile = 3;
-            nanosec_init_profile();
-            nanosec_ui_save();
-        } else {
-            filesystem_read_file("nanosec.ini", (char*)&nanosec_state, sizeof(nanosec_state));
-        }
-
-        freq_correction_residual = 0;
-        nanosec_screen = 0;
-
-        *context_ptr = (void *)1; // No need to re-read from filesystem when exiting low power mode
-    }
-}
-
-void nanosec_face_activate(void *context) {
-    (void) context;
-
-    // Handle any tasks related to your watch face coming on screen.
-    nanosec_changed = false;
-}
-
-static void nanosec_update_display() {
-    char buf[14];
-
-    switch (nanosec_screen) {
-        case 0:
-            sprintf(buf, "FC  %6d", nanosec_state.freq_correction);
-            break;
-        case 1:
-            sprintf(buf, "T0  %6d", nanosec_state.center_temperature);
-            break;
-        case 2:
-            sprintf(buf, "2C  %6d", nanosec_state.quadratic_tempco);
-            break;
-        case 3:
-            sprintf(buf, "3C  %6d", nanosec_state.cubic_tempco);
-            break;
-        case 4: // Profile
-            sprintf(buf, "PR      P%1d", nanosec_state.correction_profile);
-            break;
-        case 5: // Cadence
-            sprintf(buf, "CD      %2d", nanosec_state.correction_cadence);
-            break;
-        case 6: // Aging
-            sprintf(buf, "AA  %6d", nanosec_state.aging_ppm_pa);
-            break;
-    }
-    watch_display_string(buf, 0);
-}
-
-static void value_increase(int16_t delta) {
-    nanosec_changed = true;
-
-    switch (nanosec_screen) {
-        case 0:
-            nanosec_state.freq_correction += delta;
-            break;
-        case 1:
-            nanosec_state.center_temperature += delta;
-            break;
-        case 2:
-            nanosec_state.quadratic_tempco += delta;
-            break;
-        case 3:
-            nanosec_state.cubic_tempco += delta;
-            break;
-        case 4: // Profile
-            nanosec_state.correction_profile = (nanosec_state.correction_profile + delta) % nanosec_profile_count;
-            // if ALARM decreases profile below 0, roll back around
-            if (nanosec_state.correction_profile < 0) {
-                nanosec_state.correction_profile += nanosec_profile_count;
-            }
-            break;
-        case 5: // Cadence
-            switch (nanosec_state.correction_cadence) {
-                case 1:
-                    nanosec_state.correction_cadence = (delta > 0) ? 5 : 60;
-                    break;
-                case 5:
-                    nanosec_state.correction_cadence = (delta > 0) ? 10 : 1;
-                    break;
-                case 10:
-                    nanosec_state.correction_cadence = (delta > 0) ? 20 : 5;
-                    break;
-                case 20:
-                    nanosec_state.correction_cadence = (delta > 0) ? 60 : 10;
-                    break;
-                case 60:
-                    nanosec_state.correction_cadence = (delta > 0) ? 1 : 20;
-                    break;
-            }
-            break;
-        case 6: // Aging
-            nanosec_state.aging_ppm_pa += delta;
-            break;
-    }
-
-    nanosec_update_display();
-}
-
-static void nanosec_next_edit_screen(void) {
-    nanosec_screen = (nanosec_screen + 1) % nanosec_max_screen;
-    nanosec_update_display();
-}
-
-float nanosec_get_aging() // Returns aging correction in ppm
-{
-    watch_date_time_t date_time = watch_rtc_get_date_time();
-    float years = (watch_utility_date_time_to_unix_time(date_time, 0) - nanosec_state.last_correction_time) / 31536000.0f; // Years passed since finetune
-    return years*nanosec_state.aging_ppm_pa/100.0f;
-}
-
-
-bool nanosec_face_loop(movement_event_t event, void *context) {
-    (void) context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            // Show your initial UI here.
-            nanosec_screen = 0; // Start at page 0
-            nanosec_update_display();
-            break;
-        case EVENT_TICK:
-            break;
-        case EVENT_MODE_BUTTON_UP:
-            if (nanosec_screen == 0) { // we can exit face only on the 0th page
-                nanosec_ui_save();
-                movement_move_to_next_face();
-            } else {
-                nanosec_next_edit_screen();
-            }
-            break;
-        case EVENT_MODE_LONG_PRESS:
-            nanosec_next_edit_screen();
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            value_increase(1);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            if (nanosec_screen == 4) { // If we are in profile - apply profiles
-                nanosec_init_profile();
-                nanosec_screen = 0;
-                nanosec_update_display();
-            } else {
-                value_increase(50);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            value_increase(-1);
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (nanosec_screen == 4) { // If we are in profile - still decrease by 1
-                value_increase(-1);
-            } else {
-                value_increase(-50);
-            }
-            break;
-        case EVENT_TIMEOUT:
-            // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
-            // you may uncomment this line to move back to the first watch face in the list:
-            // movement_move_to_face(0);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
-            // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
-            // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
-            // watch_start_sleep_animation(500);
-            break;
-        case EVENT_BACKGROUND_TASK:
-            // Here we measure temperature and do main frequency correction
-            thermistor_driver_enable();
-            float temperature_c = thermistor_driver_get_temperature();
-            float voltage = (float)watch_get_vcc_voltage() / 1000.0;
-            thermistor_driver_disable();
-            // L22 correction scaling is 0.95367ppm per 1 in FREQCORR
-            // At wrong temperature crystall starting to run slow, negative correction will speed up frequency to correct
-            // Default 32kHz correciton factor is -0.034, centered around 25°C
-            float dt = temperature_c - nanosec_state.center_temperature / 100.0;
-
-            int16_t correction = round((
-                        nanosec_state.freq_correction / 100.0f * dithering +
-                        (-nanosec_state.quadratic_tempco / 100000.0 * dithering) * dt * dt +
-                        (nanosec_state.cubic_tempco / 10000000.0 * dithering) * dt * dt * dt +
-                        (voltage - 3.0) * voltage_coefficient +
-                        nanosec_get_aging() * dithering
-                        ) / 0.95367); // 1 correction unit is 0.095367ppm.
-
-            apply_RTC_correction(correction);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
-    // you should return false since the PWM driver does not operate in standby mode.
-    return true;
-}
-
-void nanosec_face_resign(void *context) {
-    (void) context;
-
-    nanosec_ui_save();
-}
-
-// Background freq correction
-movement_watch_face_advisory_t nanosec_face_advise(void *context) {
-    (void) context;
-    movement_watch_face_advisory_t retval = { 0 };
-
-    // No need for background correction if we are on profile 0 - static hardware correction.
-    if (nanosec_state.correction_profile != 0) {
-        watch_date_time_t date_time = watch_rtc_get_date_time();
-        retval.wants_background_task = date_time.unit.minute % nanosec_state.correction_cadence == 0;
-    }
-
-    return retval;
-}
diff --git a/movement/watch_faces/settings/nanosec_face.h b/movement/watch_faces/settings/nanosec_face.h
deleted file mode 100644 (file)
index 105773a..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
- *
- * 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 NANOSEC_FACE_H_
-#define NANOSEC_FACE_H_
-
-/*
- * NANOSEC face
- *
- * The goal of nanosec face is dramatic improvement of SensorWatch accuracy.
- * Minimum goal is <60 seconds of error per year. Full success is if we can
- * reach <15 seconds per year (<0.47ppm error).
- *
- * Best used in conjunction with the FINETUNE face.
- *
- * It implements temperature correction using tempco from datasheet (and
- * allows to adjust these) and allows to introduce offset fix. Therefore
- * requires temperature sensor board.
- *
- * Most users will need to apply profile 3 ("default") or 2 ("conservative
- * datasheet"), and tune first parameter "static offset" (as it's different
- * for every crystal sample).
- *
- * Frequency correction is dithered over 31 correction intervals (31x10
- * minutes or ~5 hours), to allow <0.1ppm correction resolution.
- *  * 1ppm is 0.0864 sec per day.
- *  * 0.1ppm is 0.00864 sec per day.
- *
- * To stay under 1ppm error you would need calibration of your specific
- * instance of quartz crystal after some "burn-in" (ideally 1 year).
- *
- * Should improve TOTP experience.
- *
- * Default funing fork tempco: -0.034 ppm/°C², centered around 25°C
- * We add optional cubic coefficient, which was measured in practice on my sample.
- *
- * Cadence (CD) - how many minutes between corrections. Default 10 minutes.
- * Every minute might be too much. Every hour - slightly less power
- * consumption but also less precision.
- *
- * Can compensate crystal aging (ppm/year) - but you really should be worrying
- * about it on second/third years of watch calibration.
- *
- * For full usage instructions, please refer to the wiki:
- *  https://www.sensorwatch.net/docs/watchfaces/nanosec/
- */
-
-#include "movement.h"
-
-#define nanosec_profile_count 5
-typedef struct {
-    // Correction profiles:
-    // 0 - static hardware correction.
-    // 1 - static correction with dithering.
-    // 2 - datasheet quadratic correction (universal).
-    // 3 - cubic correction conservative (likely universal).
-    // 4 - cubic correction finetuned (sample-specific).
-    int8_t correction_profile;
-    int16_t freq_correction; // Static correction - multiplied by 100
-    int16_t center_temperature; // Multiplied by 100, +25.0 -> +2500
-    int16_t quadratic_tempco; // 0.034 -> 3400, multiplied by 100000. Stored positive, used as negative.
-    int16_t cubic_tempco; // default 0, 0.000136 -> 1360, multiplied by 10000000. Stored positive, used positive.
-    int8_t correction_cadence;
-    uint32_t last_correction_time; // Not used at the moment - but will in the future
-    int16_t aging_ppm_pa; // multiplied by 100. Aging per year. 
-} nanosec_state_t;
-
-void nanosec_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void nanosec_face_activate(void *context);
-bool nanosec_face_loop(movement_event_t event, void *context);
-void nanosec_face_resign(void *context);
-movement_watch_face_advisory_t nanosec_face_advise(void *context);
-void nanosec_ui_save(void);
-void nanosec_save(void);
-float nanosec_get_aging(void);
-
-
-#define nanosec_face ((const watch_face_t) { \
-    nanosec_face_setup, \
-    nanosec_face_activate, \
-    nanosec_face_loop, \
-    nanosec_face_resign, \
-    nanosec_face_advise, \
-})
-
-#endif // NANOSEC_FACE_H_
-
diff --git a/movement/watch_faces/settings/place_face.h b/movement/watch_faces/settings/place_face.h
deleted file mode 100644 (file)
index 94bf164..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
- *
- * 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 place_FACE_H_
-#define place_FACE_H_
-
-#include "movement.h"
-
-/*
- * PLACE FACE
- * ==========
- *
- * Based on and expanded from the Sunrise/Sunset face. Outsourced the location setting functionality to 
- * its own face. Also serves as a converter between different coordinate notation formats.
- * 
- * With the LIGHT button each place coordinate can be shown and edited in 4 different display modes:
- * 
- * 1) Decimal Latitude and Longitude (WGS84) up to 5 decimal points
- * 2) Latitude and Longitude (WGS84) in traditional DD°MM'SS" notation
- * 3) Ten digit Open Location Code (aka. PlusCode) format
- * 4) Ten digit Geohash format
- * 
- * Using the ALARM button the user can flip through 2 pages of coordinate info to see the first and
- * second sets of digits.
- * 
- * (please also refer to the notes on precision below)
- *
- * Editing Mode
- * ============
- * 
- * A LONG PRESS of the LIGHT button toggles editing mode for each of the selected notations.
- * 
- * In this mode LIGHT moves the cursor and ALARM changes the letter cycling through the available
- * alphabet or numbers. 
- * 
- * When OLC or Geohash display are edited, Digit Info mode is activated. It serves as a workaround 
- * for the limitation of how ambiguously alphanumeric characters are displayed on the main seven segment 
- * digits of the watch face ( S or 5, I or 1, U or W?).
- * 
- * The selected letter is also shown in the much easier to read alphanumeric 8 segment weekday digit above.
- * In addition the '24H' indicator is active when the selected digit represents a number and the 'PM' 
- * indicator for a letter. 
- * 
- * A LONG PRESS of LIGHT saves the changes.
- * 
- * Coordinates are read or stored to both the traditional internal location register and a file on 
- * the LFS file system ("place.loc"). By default the Watch Face loads the coordinates from file
- * when activated. If no file is present, the coordinates are loaded from the register.
- * (please also see the notes on precision below)
- * 
- * Auxiliary Mode: Digit Info
- * ==========================
- * 
- * A LONG PRESS of the ALARM button toggles Digit Info mode when OLC or Geohash display is active.
- * (LAP indicator is on) It is a means of being able to see the detailed Digit Info as described above
- * but without the risk of accidentally editing any of digits.
- * 
- * Both ALARM and LIGHT buttons can be used to flip through the letters.
- * 
- * Notes on Coordinate Precision
- * =============================
- * 
- * The common WGS84 Latitude and Longitude degrees naturally do not represent meters in distance 
- * on the ground. 1° Longitude on the equatorial line equals a width of 111.32 kilometers, but 
- * at 40° latitude further North or South it is approximately 85 kilometers wide. The closer to 
- * the poles the narrower (and more precise) the latitude degrees get.
- * 
- * The Sensor Watch's traditional 16bit location register only stores latitudes and longitudes 
- * with two decimal points. That equals a longitudal precision of 36 arc seconds, or ~1111 meters
- * at the equator - precise enough for astronomical calculations, but not if you want to store the 
- * location of let's say a building.
- * 
- * Hence we propose the <place.loc> file that serves the same purpose, but with a precision of 
- * five decimal digits. That equals 0.04 arc seconds or 1.11 meters at the equator.
- * 
- * Please also note that the different notations of this watch face also have varying magnitudes 
- * of precision:
- * 
- * | Format             | Notation               | Precision at Equator | Precision at 67° N/S |
- * | ------------------ | ---------------------- | -------------------- | -------------------- |
- * | 2d. Decimal LatLon | 29.98, 31.13           |           1111.320 m |            435.125 m |
- * | 5d. Decimal LatLon | 29.97916, 31.13417     |              1.111 m |              0.435 m |
- * | DMS LatLon         | N 29°58′45″, E 31°8′3″ |             30.833 m |             12.083 m |
- * | Open Location Code | 7GXHX4HM+MM            |             13.875 m |             13.875 m |
- * | Geohash            | stq4s3x1qu             |              1.189 m |              0.596 m |
- * 
- * Since all notations are internally converted into degrees with 5 decimal points, expect some
- * rounding errors when editing or loading the coordinates in other notation formats.
- * 
- */
-
-static const char olc_alphabet[20] = "23456789CFGHJMPQRUWX";
-static const char geohash_alphabet[32] =  "0123456789bCdEfGhjkmnpqrstuVwxyz";
-
-typedef struct {
-    uint8_t sign: 1;        // 0-1
-    uint8_t hundreds: 1;    // 0-1
-    uint8_t tens: 4;        // 0-9
-    uint8_t ones: 4;        // 0-9
-    uint8_t d01: 4;         // 0-9
-    uint8_t d02: 4;         // 0-9
-    uint8_t d03: 4;         // 0-9
-    uint8_t d04: 4;         // 0-9
-    uint8_t d05: 4;         // 0-9
-} place_format_decimal_latlon_t;
-
-typedef struct {
-    uint8_t sign: 1;        // 0-1
-    uint8_t hundreds: 1;    // 0-1
-    uint8_t tens: 4;        // 0-9
-    uint8_t ones: 4;        // 0-9
-    uint8_t mins_tens: 3;   // 0-5
-    uint8_t mins_ones: 4;   // 0-9
-    uint8_t secs_tens: 3;   // 0-5
-    uint8_t secs_ones: 4;   // 0-9
-} place_format_dms_latlon_t;
-
-typedef struct {
-    uint8_t lat1: 5;        // 2-X
-    uint8_t lon1: 5;        // 2-X
-    uint8_t lat2: 5;        // 2-X
-    uint8_t lon2: 5;        // 2-X
-    uint8_t lat3: 5;        // 2-X
-    uint8_t lon3: 5;        // 2-X
-    uint8_t lat4: 5;        // 2-X
-    uint8_t lon4: 5;        // 2-X
-    uint8_t lat5: 5;        // 2-X
-    uint8_t lon5: 5;        // 2-X
-} place_format_olc_t;
-
-typedef struct {
-    int32_t latitude : 25;
-    int32_t longitude : 26;
-} coordinate_t;
-
-typedef struct {
-    place_format_decimal_latlon_t latitude;
-    place_format_decimal_latlon_t longitude;
-} place_coordinate_t;
-
-typedef struct {
-    uint8_t d01: 6;          // 0-z
-    uint8_t d02: 6;          // 0-z 
-    uint8_t d03: 6;          // 0-z
-    uint8_t d04: 6;          // 0-z
-    uint8_t d05: 6;          // 0-z
-    uint8_t d06: 6;          // 0-z
-    uint8_t d07: 6;          // 0-z
-    uint8_t d08: 6;          // 0-z
-    uint8_t d09: 6;          // 0-z
-    uint8_t d10: 6;          // 0-z
-} place_format_geohash_t;
-
-typedef struct {
-    double max;
-    double min;
-} place_format_geohash_interval;
-
-typedef struct {
-    uint8_t min_digit : 1;
-    uint8_t max_digit : 3;
-} place_mode_schema_page_t;
-
-typedef struct {
-    uint8_t max_page : 3;
-    place_mode_schema_page_t page[4];
-} place_mode_schema_mode_t;
-
-enum place_modes_e {
-    MODE_DECIMAL = 0,
-    MODE_DMS,
-    MODE_OLC,
-    MODE_GEOHASH
-};
-
-typedef struct {
-    enum place_modes_e mode;
-    uint8_t page : 3;
-    int8_t active_digit: 4;
-    bool edit;
-    bool digit_info;
-    place_format_decimal_latlon_t working_latitude;
-    place_format_decimal_latlon_t working_longitude;
-    place_format_dms_latlon_t working_dms_latitude;
-    place_format_dms_latlon_t working_dms_longitude;
-    place_format_olc_t working_pluscode;
-    place_format_geohash_t working_geohash;
-    place_mode_schema_mode_t modes[4];
-} place_state_t;
-
-// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
-
-void place_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void place_face_activate(void *context);
-bool place_face_loop(movement_event_t event, void *context);
-void place_face_resign(void *context);
-
-void place_latlon_to_olc(char *pluscode, double latitude, double longitude);
-void place_latlon_to_geohash(char *geohash, double latitude, double longitude);
-
-#define place_face ((const watch_face_t){ \
-    place_face_setup, \
-    place_face_activate, \
-    place_face_loop, \
-    place_face_resign, \
-    NULL, \
-})
-
-#endif // place_FACE_H_
-
diff --git a/movement/watch_faces/settings/save_load_face.c b/movement/watch_faces/settings/save_load_face.c
deleted file mode 100644 (file)
index d298229..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Wesley Aptekar-Cassels
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "save_load_face.h"
-#include "filesystem.h"
-
-static void save(save_load_state_t *state) {
-    savefile_t savefile = {
-        1,
-        watch_get_backup_data(0),
-        watch_get_backup_data(1),
-        watch_get_backup_data(2),
-        watch_get_backup_data(3),
-        watch_get_backup_data(4),
-        watch_get_backup_data(5),
-        watch_get_backup_data(6),
-        watch_get_backup_data(7),
-        watch_rtc_get_date_time(),
-    };
-    state->slot[state->index] = savefile;
-    char filename[23];
-    sprintf(filename, "save_load_face_%d.bin", state->index);
-    filesystem_write_file(filename, (char*)&savefile, sizeof(savefile_t));
-}
-
-static void load(save_load_state_t *state) {
-    watch_store_backup_data(state->slot[state->index].b0, 0);
-    watch_store_backup_data(state->slot[state->index].b1, 1);
-    watch_store_backup_data(state->slot[state->index].b2, 2);
-    watch_store_backup_data(state->slot[state->index].b3, 3);
-    watch_store_backup_data(state->slot[state->index].b4, 4);
-    watch_store_backup_data(state->slot[state->index].b5, 5);
-    watch_store_backup_data(state->slot[state->index].b6, 6);
-    watch_store_backup_data(state->slot[state->index].b7, 7);
-}
-
-static void load_saves_to_state(save_load_state_t *state) {
-    for (uint8_t i = 0; i < SAVE_LOAD_SLOTS; i++) {
-        char filename[23];
-        sprintf(filename, "save_load_face_%d.bin", i);
-        if (filesystem_get_file_size(filename) != sizeof(savefile_t)) {
-            state->slot[i].version = 0;
-            continue;
-        }
-        filesystem_read_file(filename, (char*)&state->slot[i], sizeof(savefile_t));
-        if (state->slot[i].version != 1) {
-            state->slot[i].version = 0;
-        }
-    }
-}
-
-void save_load_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) {
-        *context_ptr = malloc(sizeof(save_load_state_t));
-        memset(*context_ptr, 0, sizeof(save_load_state_t));
-    }
-}
-
-void save_load_face_activate(void *context) {
-    save_load_state_t *state = (save_load_state_t *)context;
-    state->index = 0;
-    state->update_timeout = 0;
-    load_saves_to_state(state);
-}
-
-static void update_display(save_load_state_t *state) {
-    char buf[11];
-    sprintf(buf, "SL %1d", state->index);
-    watch_display_string(buf, 0);
-
-    if (state->slot[state->index].version) {
-        sprintf(buf, "%02d%02d%02d", state->slot[state->index].rtc.unit.year + 20, state->slot[state->index].rtc.unit.month, state->slot[state->index].rtc.unit.day);
-        watch_display_string(buf, 4);
-    } else {
-        watch_display_string("no dat", 4);
-    }
-}
-
-bool save_load_face_loop(movement_event_t event, void *context) {
-    save_load_state_t *state = (save_load_state_t *)context;
-
-    switch (event.event_type) {
-        case EVENT_ACTIVATE:
-            update_display(state);
-            break;
-        case EVENT_TICK:
-            if (state->update_timeout) {
-                if (!--state->update_timeout) {
-                    update_display(state);
-                }
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            state->index = (state->index + 1) % SAVE_LOAD_SLOTS;
-            update_display(state);
-            break;
-        case EVENT_LIGHT_LONG_PRESS:
-            save(state);
-            watch_display_string("Saved ", 4);
-            state->update_timeout = 3;
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            if (state->slot[state->index].version) {
-                load(state);
-                watch_display_string("Loaded", 4);
-                state->update_timeout = 3;
-            }
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void save_load_face_resign(void *context) {
-    (void) context;
-
-    // handle any cleanup before your watch face goes off-screen.
-}
-
diff --git a/movement/watch_faces/settings/save_load_face.h b/movement/watch_faces/settings/save_load_face.h
deleted file mode 100644 (file)
index 4e962fb..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Wesley Aptekar-Cassels
- *
- * 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 SAVE_LOAD_FACE_H_
-#define SAVE_LOAD_FACE_H_
-
-#include "movement.h"
-#include "watch_rtc.h"
-
-/*
- * Save/Load face
- *
- * This allows you to save your settings (including location, birthday, etc) to
- * LFS, which is not wiped when firmware is updated, and then load them again.
- * It provides multiple save slots (four by default).
- *
- * Press ALARM to cycle through save slots. Long press LIGHT to save to the
- * current slot. Long press ALARM to load from the current slot. The date that
- * a save was taken is shown in YYMMDD format , or "no dat" if the slot is
- * empty. The index of the current slot is displayed in the upper right corner.
- *
- * While the time that a save was taken is recorded, it is not currently
- * restored.
- */
-
-typedef struct savefile {
-    uint8_t version;
-    uint32_t b0;
-    uint32_t b1;
-    uint32_t b2;
-    uint32_t b3;
-    uint32_t b4;
-    uint32_t b5;
-    uint32_t b6;
-    uint32_t b7;
-    watch_date_time_t rtc;
-} savefile_t;
-
-#define SAVE_LOAD_SLOTS 4
-
-typedef struct {
-    uint8_t index;
-    uint8_t update_timeout;
-    savefile_t slot[SAVE_LOAD_SLOTS];
-} save_load_state_t;
-
-void save_load_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void save_load_face_activate(void *context);
-bool save_load_face_loop(movement_event_t event, void *context);
-void save_load_face_resign(void *context);
-
-#define save_load_face ((const watch_face_t){ \
-    save_load_face_setup, \
-    save_load_face_activate, \
-    save_load_face_loop, \
-    save_load_face_resign, \
-    NULL, \
-})
-
-#endif // SAVE_LOAD_FACE_H_
-
diff --git a/movement/watch_faces/settings/set_time_hackwatch_face.c b/movement/watch_faces/settings/set_time_hackwatch_face.c
deleted file mode 100644 (file)
index 6551019..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2022 Joey Castillo
- * Copyright (c) 2022 Mikhail Svarichevsky https://3.14.by/
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include "set_time_hackwatch_face.h"
-#include "watch.h"
-#include "watch_utility.h"
-#include "zones.h"
-
-char set_time_hackwatch_face_titles[][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO"};
-#define set_time_hackwatch_face_NUM_SETTINGS (sizeof(set_time_hackwatch_face_titles) / sizeof(*set_time_hackwatch_face_titles))
-
-watch_date_time_t date_time_settings;
-
-void set_time_hackwatch_face_setup(uint8_t watch_face_index, void ** context_ptr) {
-    (void) watch_face_index;
-    if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t));
-}
-
-void set_time_hackwatch_face_activate(void *context) {
-    *((uint8_t *)context) = 3;
-    movement_request_tick_frequency(32);
-    date_time_settings = watch_rtc_get_date_time();
-}
-
-bool set_time_hackwatch_face_loop(movement_event_t event, void *context) {
-    uint8_t current_page = *((uint8_t *)context);
-
-    if (event.subsecond == 15) // Delay displayed time update by ~0.5 seconds, to align phase exactly to main clock at 1Hz
-        date_time_settings = watch_rtc_get_date_time();
-
-    static int8_t seconds_reset_sequence;
-
-    switch (event.event_type) {
-        case EVENT_MODE_BUTTON_UP:
-            if (current_page == 2)
-                watch_rtc_enable(true);
-            movement_move_to_next_face();
-            return false;
-        case EVENT_LIGHT_LONG_PRESS:
-            current_page = (current_page + set_time_hackwatch_face_NUM_SETTINGS - 1) % set_time_hackwatch_face_NUM_SETTINGS;
-            if (current_page == 2)
-                seconds_reset_sequence = 0;
-
-            *((uint8_t *)context) = current_page;
-            break;
-        case EVENT_LIGHT_BUTTON_UP:
-            if (current_page == 2)
-                watch_rtc_enable(true);
-
-            current_page = (current_page + 1) % set_time_hackwatch_face_NUM_SETTINGS;
-            if (current_page == 2)
-                seconds_reset_sequence = 0;
-
-            *((uint8_t *)context) = current_page;
-            break;
-        case EVENT_TICK:
-            // We use it to wait for "middle" subsecond position
-            if (current_page == 2 && seconds_reset_sequence == 1 && event.subsecond == 15) { // wait ~0.5sec - until we reach half second point
-                watch_rtc_enable(false);
-                seconds_reset_sequence = 2;
-
-                // Set new time while RTC is off, to get perfect start
-                if (date_time_settings.unit.second > 30) {
-                    date_time_settings.unit.minute = (date_time_settings.unit.minute + 1) % 60; // Roll to next minute if we are almost there
-                    if (date_time_settings.unit.minute == 0) { // Overflow
-                        date_time_settings.unit.hour = (date_time_settings.unit.hour + 1) % 24;
-                        if (date_time_settings.unit.hour == 0) // Overflow
-                            date_time_settings.unit.day++;
-                    }
-                }
-                date_time_settings.unit.second = 0;
-                watch_rtc_set_date_time(date_time_settings);
-            }
-            break;
-        case EVENT_ALARM_BUTTON_DOWN:
-            if (current_page == 2) {
-                watch_rtc_enable(true); // If it is disabled accidentally - re-enable it
-                seconds_reset_sequence = 1; // Waiting for whole second
-            }
-            break;
-        case EVENT_ALARM_LONG_PRESS:
-            switch (current_page) {
-                case 0: // hour
-                    date_time_settings.unit.hour = (date_time_settings.unit.hour + 24 -1) % 24;
-                    break;
-                case 1: // minute
-                    date_time_settings.unit.minute = (date_time_settings.unit.minute + 60 - 1) % 60;
-                    break;
-                case 3: // year
-                    // only allow 2021-2061. fix this sometime later
-                    date_time_settings.unit.year = (date_time_settings.unit.year + 50 - 1) % 50;
-                    break;
-                case 4: // month
-                    date_time_settings.unit.month = (date_time_settings.unit.month + 12 - 2) % 12 + 1;
-                    break;
-                case 5: // day
-                    date_time_settings.unit.day = date_time_settings.unit.day - 2;
-                    if (date_time_settings.unit.day == 0) {
-                        date_time_settings.unit.day = days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR);
-                    } else
-                        date_time_settings.unit.day++;
-                    break;
-                case 6: // time zone
-                    if (movement_get_timezone_index() > 0) {
-                        movement_set_timezone_index(movement_get_timezone_index() - 1);
-                    } else {
-                        movement_set_timezone_index(NUM_ZONE_NAMES - 1);
-                    }
-                    break;
-            }
-            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);
-            break;
-
-        case EVENT_ALARM_LONG_UP://Setting seconds on long release
-            switch (current_page) {
-                case 2: // second
-                    seconds_reset_sequence = 0;
-                    watch_rtc_enable(true);
-                    break;
-            }
-            break;
-        case EVENT_ALARM_BUTTON_UP:
-            switch (current_page) {
-                case 0: // hour
-                    date_time_settings.unit.hour = (date_time_settings.unit.hour + 1) % 24;
-                    break;
-                case 1: // minute
-                    date_time_settings.unit.minute = (date_time_settings.unit.minute + 1) % 60;
-                    break;
-                case 2: // second
-                    seconds_reset_sequence = 0;
-                    watch_rtc_enable(true);
-                    break;
-                case 3: // year
-                    // only allow 2021-2061. fix this sometime later
-                    date_time_settings.unit.year = ((date_time_settings.unit.year % 50) + 1);
-                    break;
-                case 4: // month
-                    date_time_settings.unit.month = (date_time_settings.unit.month % 12) + 1;
-                    break;
-                case 5: // day
-                    date_time_settings.unit.day = (date_time_settings.unit.day % watch_utility_days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR)) + 1;
-                    break;
-                case 6: // time zone
-                    movement_set_timezone_index(movement_get_timezone_index() + 1);
-                    if (movement_get_timezone_index() >= NUM_ZONE_NAMES) movement_set_timezone_index(0);
-                    break;
-            }
-            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);
-            //TODO: Do not update whole RTC, just what we are changing
-            break;
-        case EVENT_TIMEOUT:
-            movement_move_to_face(0);
-            break;
-        case EVENT_LIGHT_BUTTON_DOWN:
-            // don't light up every time light is hit
-            break;
-        default:
-            movement_default_loop_handler(event);
-            break;
-    }
-
-    char buf[11];
-    if (current_page < 3) {
-        watch_set_colon();
-        if (movement_clock_mode_24h()) {
-            watch_set_indicator(WATCH_INDICATOR_24H);
-            sprintf(buf,
-                    "%s  %2d%02d%02d",
-                    set_time_hackwatch_face_titles[current_page],
-                    date_time_settings.unit.hour,
-                    date_time_settings.unit.minute,
-                    date_time_settings.unit.second);
-        } else {
-            sprintf(buf,
-                    "%s  %2d%02d%02d",
-                    set_time_hackwatch_face_titles[current_page],
-                    (date_time_settings.unit.hour % 12) ? (date_time_settings.unit.hour % 12) : 12,
-                    date_time_settings.unit.minute,
-                    date_time_settings.unit.second);
-            if (date_time_settings.unit.hour < 12) {
-                watch_clear_indicator(WATCH_INDICATOR_PM);
-            } else {
-                watch_set_indicator(WATCH_INDICATOR_PM);
-            }
-        }
-    } else if (current_page < 6) {
-        watch_clear_colon();
-        watch_clear_indicator(WATCH_INDICATOR_24H);
-        watch_clear_indicator(WATCH_INDICATOR_PM);
-        sprintf(buf,
-                "%s  %2d%02d%02d",
-                set_time_hackwatch_face_titles[current_page],
-                date_time_settings.unit.year + 20,
-                date_time_settings.unit.month,
-                date_time_settings.unit.day);
-    } else {
-        if ((event.subsecond / 8 ) % 2) {
-            watch_clear_colon();
-            sprintf(buf, "%s        ", set_time_hackwatch_face_titles[current_page]);
-        } else {
-            watch_set_colon();
-            sprintf(buf,
-                    "%s %3d%02d  ",
-                    set_time_hackwatch_face_titles[current_page],
-                    (int8_t)(movement_get_current_timezone_offset() / 3600),
-                    (int8_t)(movement_get_current_timezone_offset() % 3600) * (movement_get_current_timezone_offset() < 0 ? -1 : 1));
-        }
-    }
-
-    // blink up the parameter we're setting
-    if ( (event.subsecond / 8) % 2) {
-        switch (current_page) {
-            case 0:
-            case 3:
-                buf[4] = buf[5] = ' ';
-                break;
-            case 1:
-            case 4:
-                buf[6] = buf[7] = ' ';
-                break;
-            case 2:
-                // Only blink first number when setting seconds, to make it easier to see subsecond error
-                buf[8] = ' ';
-                break;
-            case 5:
-                buf[8] = buf[9] = ' ';
-                break;
-        }
-    }
-
-    watch_display_string(buf, 0);
-
-    return true;
-}
-
-void set_time_hackwatch_face_resign(void *context) {
-    (void) context;
-    watch_set_led_off();
-    movement_store_settings();
-}
diff --git a/movement/watch_faces/settings/set_time_hackwatch_face.h b/movement/watch_faces/settings/set_time_hackwatch_face.h
deleted file mode 100644 (file)
index ad883a6..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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 SET_TIME_HACKWATCH_FACE_H_
-#define SET_TIME_HACKWATCH_FACE_H_
-
-/*
- * SET TIME HACKWATCH
- *
- * This is an extended version of set_time face which allow setting seconds
- * precisely. To achieve that - press and hold alarm button few seconds before
- * 00 and release exaclty as reference clock turns 00.
- *
- * All settings can go up, or down (long alarm press).
- * 
- * The challenge is that SensorWatch display is delayed 0.5 seconds vs hardware
- * RTC clock. It is caused by interrupts being generated by raising edge of
- * counter. It means there is no way to precisely trigger at 0.5s, as events
- * at different frequencies slightly mismatch. This watch face achieves this
- * approximately by triggering at 15th out of 32Hz events.
- * 
- * If you are <30 seconds when setting seconds - you will stay in the same
- * minute. Otherwise - you will go to next minute. 
- * 
- * Note that changing anything will slightly delay subseconds counter. This
- * is why this face sets seconds last to achiveve best precision. Still,
- * best possible precision is achieved with finetune face. 
- */
-
-#include "movement.h"
-
-void set_time_hackwatch_face_setup(uint8_t watch_face_index, void ** context_ptr);
-void set_time_hackwatch_face_activate(void *context);
-bool set_time_hackwatch_face_loop(movement_event_t event, void *context);
-void set_time_hackwatch_face_resign(void *context);
-
-#define set_time_hackwatch_face ((const watch_face_t){ \
-    set_time_hackwatch_face_setup, \
-    set_time_hackwatch_face_activate, \
-    set_time_hackwatch_face_loop, \
-    set_time_hackwatch_face_resign, \
-    NULL, \
-})
-
-#endif // SET_TIME_HACKWATCH_FACE_H_