Morsecalc refactor (#229)
* Added Morse code based RPN calculator * added manual and memory register * fixed morsecalc negative indicator, edited header comment * adjusted stack display controls * Fixed warnings. Added calculator token aliasing ability. Added binary shorthand for numeral entry. Extended morse code binary tree. * ui tweaks * Update movement_config.h * silence warning * Reorganized codebase and simplified morse code reading routines. * added 'quit if submission is empty' behavior * reverted rules.mk change for merge into main * corrected timeout behavior * consolidated morsecode lib into one file; deleted old mc.c mc.h * consolidated morsecode lib into one file; deleted old mc.c mc.h * removed specious null in morsecode bintree string --------- Co-authored-by: Christian Chapman <user@debian> Co-authored-by: joeycastillo <joeycastillo@utexas.edu>
This commit is contained in:
committed by
GitHub
parent
16a96d16b2
commit
0a836cecea
@@ -26,7 +26,6 @@
|
||||
## 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`)
|
||||
@@ -39,7 +38,7 @@ The user interface (`morsecalc_face.h`, `morsecalc_face.c`) lets you talk to the
|
||||
- `light` is dash
|
||||
- `alarm` is dot
|
||||
- `mode` is "finish character"
|
||||
- long-press `mode` to quit
|
||||
- long-press `mode` or submit a blank token to switch faces
|
||||
- long-press `alarm` to show stack
|
||||
- long-press `light` to toggle the light
|
||||
|
||||
@@ -48,7 +47,6 @@ As you enter `.`s and `-`s, the morse code char you've entered will appear in th
|
||||
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.
|
||||
|
||||
@@ -77,7 +75,6 @@ This can get long, so for convenience numerals can also be written in binary wit
|
||||
|
||||
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
|
||||
@@ -97,7 +94,6 @@ So for example, the watch face might look like this:
|
||||
... 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):
|
||||
|
||||
@@ -113,101 +109,12 @@ To see all the calculator operations and their token aliases, see the `calc_dict
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "morsecalc_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
#include "watch_private_display.h"
|
||||
|
||||
// Display float on screen
|
||||
void morsecalc_print_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_print_token(morsecalc_state_t *mcs) {
|
||||
watch_display_string(" ", 0); // Clear display
|
||||
|
||||
// Print morse code buffer
|
||||
char c = mc_dec(mcs->mc->b); // 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
|
||||
watch_display_character('0'+(mcs->mc->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;
|
||||
}
|
||||
#include "morsecalc_face.h"
|
||||
#include "morsecalc_display.h"
|
||||
|
||||
// Clear token buffer
|
||||
void morsecalc_reset_token(morsecalc_state_t *mcs) {
|
||||
@@ -216,75 +123,46 @@ void morsecalc_reset_token(morsecalc_state_t *mcs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Print stack or memory register contents.
|
||||
void morsecalc_print_stack(morsecalc_state_t * mcs) {
|
||||
watch_display_string(" ", 0); // Clear display
|
||||
|
||||
char c = mc_dec(mcs->mc->b);
|
||||
if('m' == c) { // Display memory
|
||||
morsecalc_print_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_print_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;
|
||||
}
|
||||
|
||||
// Write something into the morse code buffer.
|
||||
// Input: c = dot (0), dash (1), or 'complete' ('x')
|
||||
void morsecalc_input(morsecalc_state_t * mcs, char c) {
|
||||
// Write a completed morse code character to the calculator
|
||||
void morsecalc_input(morsecalc_state_t * mcs) {
|
||||
int status = 0;
|
||||
if( c != 'x' ) { // Dot or dash received
|
||||
mc_input(mcs->mc, c);
|
||||
morsecalc_print_token(mcs);
|
||||
}
|
||||
else { // Morse code character finished
|
||||
char dec = mc_dec(mcs->mc->b);
|
||||
mc_reset(mcs->mc);
|
||||
switch(dec) {
|
||||
case '\0': // Invalid character, do nothing
|
||||
morsecalc_print_token(mcs);
|
||||
break;
|
||||
|
||||
case ' ': // Submit token to calculator
|
||||
if(strlen(mcs->token) > 0) {
|
||||
status = calc_input(mcs->cs, mcs->token);
|
||||
morsecalc_reset_token(mcs);
|
||||
}
|
||||
morsecalc_print_stack(mcs);
|
||||
break;
|
||||
|
||||
case '(': // -.--. Erase previous character in token
|
||||
if(mcs->idxt>0) {
|
||||
mcs->idxt--;
|
||||
mcs->token[mcs->idxt] = '\0';
|
||||
}
|
||||
morsecalc_print_token(mcs);
|
||||
break;
|
||||
|
||||
case 'S': // -.-.- Erase entire token without submitting
|
||||
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_print_stack(mcs);
|
||||
break;
|
||||
|
||||
default: // Add character to token
|
||||
if(mcs->idxt < MORSECALC_TOKEN_LEN-1) {
|
||||
mcs->token[mcs->idxt] = dec;
|
||||
mcs->idxt++;
|
||||
morsecalc_print_token(mcs);
|
||||
}
|
||||
else watch_display_string(" full", 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -308,10 +186,7 @@ void morsecalc_face_setup(movement_settings_t *settings, uint8_t watch_face_inde
|
||||
|
||||
mcs->cs = (calc_state_t *) malloc(sizeof(calc_state_t));
|
||||
calc_init(mcs->cs);
|
||||
|
||||
mcs->mc = (mc_state_t *) malloc(sizeof(mc_state_t));
|
||||
mc_reset(mcs->mc);
|
||||
|
||||
mcs->mc = 0;
|
||||
mcs->led_is_on = 0;
|
||||
}
|
||||
return;
|
||||
@@ -320,8 +195,8 @@ void morsecalc_face_setup(movement_settings_t *settings, uint8_t watch_face_inde
|
||||
void morsecalc_face_activate(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
morsecalc_state_t *mcs = (morsecalc_state_t *) context;
|
||||
mc_reset(mcs->mc);
|
||||
morsecalc_print_stack(mcs);
|
||||
mcs->mc = 0;
|
||||
morsecalc_display_stack(mcs);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -331,21 +206,24 @@ bool morsecalc_face_loop(movement_event_t event, movement_settings_t *settings,
|
||||
// input
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
// dot
|
||||
morsecalc_input(mcs, '.');
|
||||
morsecode_input(&mcs->mc, MORSECODE_LEN, 0);
|
||||
morsecalc_display_token(mcs);
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
// dash
|
||||
morsecalc_input(mcs, '-');
|
||||
morsecode_input(&mcs->mc, MORSECODE_LEN, 1);
|
||||
morsecalc_display_token(mcs);
|
||||
break;
|
||||
case EVENT_MODE_BUTTON_UP:
|
||||
// submit character
|
||||
morsecalc_input(mcs, 'x');
|
||||
// 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_print_stack(mcs);
|
||||
mc_reset(mcs->mc);
|
||||
morsecalc_display_stack(mcs);
|
||||
mcs->mc = 0;
|
||||
break;
|
||||
|
||||
// toggle light
|
||||
@@ -364,7 +242,7 @@ bool morsecalc_face_loop(movement_event_t event, movement_settings_t *settings,
|
||||
|
||||
// quit
|
||||
case EVENT_TIMEOUT:
|
||||
movement_move_to_next_face();
|
||||
movement_move_to_face(0);
|
||||
break;
|
||||
case EVENT_MODE_LONG_PRESS:
|
||||
movement_move_to_next_face();
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
|
||||
#ifndef MORSECALC_FACE_H_
|
||||
#define MORSECALC_FACE_H_
|
||||
#define MORSECALC_TOKEN_LEN 9
|
||||
|
||||
#define MORSECALC_TOKEN_LEN 32
|
||||
#define MORSECODE_LEN 5
|
||||
|
||||
#include "movement.h"
|
||||
#include "calc.h"
|
||||
#include "mc.h"
|
||||
#include "morsecode.c"
|
||||
|
||||
void morsecalc_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
|
||||
void morsecalc_face_activate(movement_settings_t *settings, void *context);
|
||||
@@ -37,17 +39,14 @@ void morsecalc_face_resign(movement_settings_t *settings, void *context);
|
||||
|
||||
typedef struct {
|
||||
calc_state_t *cs;
|
||||
mc_state_t *mc;
|
||||
unsigned int mc; // Morse code character
|
||||
char token[MORSECALC_TOKEN_LEN];
|
||||
uint8_t idxt;
|
||||
uint8_t led_is_on;
|
||||
} morsecalc_state_t;
|
||||
|
||||
void morsecalc_print_float(double d);
|
||||
void morsecalc_print_token(morsecalc_state_t *mcs);
|
||||
void morsecalc_print_stack(morsecalc_state_t *mcs);
|
||||
void morsecalc_reset_token(morsecalc_state_t *mcs);
|
||||
void morsecalc_input(morsecalc_state_t *mcs, char c);
|
||||
void morsecalc_input(morsecalc_state_t *mcs);
|
||||
|
||||
#define morsecalc_face ((const watch_face_t){ \
|
||||
morsecalc_face_setup, \
|
||||
|
||||
Reference in New Issue
Block a user