diff --git a/movement/make/Makefile b/movement/make/Makefile index 61b705f..361f7c9 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -112,7 +112,6 @@ SRCS += \ ../watch_faces/complication/invaders_face.c \ ../watch_faces/clock/world_clock2_face.c \ ../watch_faces/complication/time_left_face.c \ - ../watch_faces/complication/randonaut_face.c \ ../watch_faces/complication/toss_up_face.c \ ../watch_faces/complication/geomancy_face.c \ # New watch faces go above this line. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index 0e2a45b..a75deae 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -87,7 +87,6 @@ #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" diff --git a/movement/watch_faces/complication/randonaut_face.c b/movement/watch_faces/complication/randonaut_face.c deleted file mode 100644 index a4ed6ec..0000000 --- a/movement/watch_faces/complication/randonaut_face.c +++ /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 -#else -#include "saml22j18a.h" -#endif - -#include -#include -#include -#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); -static uint32_t (*__0x2_)(uint32_t) = &_get_pseudo_entropy; -static void (*_0x22)(uint8_t,uint8_t) = &watch_clear_pixel; -static void (*___0xf322)(uint8_t,uint8_t) = &watch_set_pixel; - -// MOVEMENT WATCH FACE FUNCTIONS ////////////////////////////////////////////// - -void randonaut_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { - (void) settings; - (void) watch_face_index; - if (*context_ptr == NULL) { - *context_ptr = malloc(sizeof(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(movement_settings_t *settings, void *context) { - (void) settings; - 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, movement_settings_t *settings, 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_tick_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, settings); - } - - _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(movement_settings_t *settings, void *context) { - (void) settings; - (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[11]; - 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--) {//////////////// - ___0xf322(__0x2_(/*0xD68 _0x22*//*__0x22*/////// - /*_0x22*/0x2),__0x2_(/* _0x22*//*_0x22*//////// - 0x33-0x1C));___0xf322(__0x2_(/*___0x2222_22___*/ - /*0x2*/0x2),__0x2_(3432/*_0x22*//*_0x2222*////// - -3409));___0xf322(__0x2_(/*0x2*//*____0x222222*/ - 002),__0x2_(0xE +9));___0xf322(/*_0x2222222222*/ - __0x2_(0x2),__0x2_(23));___0xf322(/*____0x2222*/ - /*0x2*/__0x2_(002),__0x2_(12+7+11));/*___00x22*/ - if(/*_0x22*/c<70){_0x22(__0x2_(2),/*____0x2222*/ - __0x2_(12+7+11));}if(c<60){_0x22(/*_______0x22*/ - /*_0x22*/__0x2_(002),__0x2_(0xD68-/*_0x2222222*/ - 0xD4A));}if(c<50){_0x22(__0x2_(0x2),/*____0x22*/ - __0x2_(14+9));}delay_ms(__0x2_(c)+20);if/*_0x2*/ - (c<30){watch_display_string(" ",__0x2_(/*_2**2*/ - 10));}_0x22(__0x2_(02),__0x2_(3432-3409)/*0x22*/ - );_0x22(__0x2_(002),__0x2_(51-28));/*_____0x22*/ - /**/ _0x22(__0x2_(0x2),__0x2_(23));if(c<20)//// - /*_*/{_0x22(__0x2_(02),__0x2_(51-28));/*__0x22*/ - /*_0x22*/_0x22(__0x2_(2),__0x2_(14+9));/*_0x22*/ - /*_0x22*/_0x22(__0x2_(0x2),__0x2_(0xD68-0xD4A)); - /*_0x22*/_0x22(__0x2_(0x2),__0x2_(3432-3409));// - /*_0x22*/_0x22(__0x2_(002),__0x2_(12+7+11));//// - /*_0x22**_0x22*/_0x22(__0x2_(2),__0x2_(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(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(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"), state->radius); - 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 - - hri_trng_clear_CTRLA_ENABLE_bit(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 index fabde79..0000000 --- a/movement/watch_faces/complication/randonaut_face.h +++ /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_ - -#include "movement.h" -#include "place_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 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. - * - */ - -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(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); -void randonaut_face_activate(movement_settings_t *settings, void *context); -bool randonaut_face_loop(movement_event_t event, movement_settings_t *settings, void *context); -void randonaut_face_resign(movement_settings_t *settings, 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/settings/place_face.h b/movement/watch_faces/settings/place_face.h deleted file mode 100644 index a98c264..0000000 --- a/movement/watch_faces/settings/place_face.h +++ /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 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(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); -void place_face_activate(movement_settings_t *settings, void *context); -bool place_face_loop(movement_event_t event, movement_settings_t *settings, void *context); -void place_face_resign(movement_settings_t *settings, 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_ -