summaryrefslogtreecommitdiffstats
path: root/movement/watch_faces/settings/place_face.h
blob: a98c264213f11c712d415303e25920ff65b49794 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
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(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_