mirror of
https://github.com/travisgoodspeed/goodwatch
synced 2024-11-21 23:58:31 +00:00
Hebrew Calender, GCC8 and Fall-Through Buttons (#141)
* Library support for the hebrew calendar, with test cases. Not interface yet. #140 * Cleanup for 16-bit cleanliness. Still not working in the watch. #140 * Hebrew calendar app begins to work on the watch. #140 * Finishes the Hebrew calendar and fall-through button support. GCC8 is now default. #140 #139 #119
This commit is contained in:
parent
9c9ab02717
commit
e21465869b
2
firmware/.gitignore
vendored
2
firmware/.gitignore
vendored
@ -1,6 +1,4 @@
|
||||
config.h
|
||||
buildtime.h
|
||||
libs/assembler
|
||||
libs/pocsag
|
||||
codeplugstr.c
|
||||
dmesg.bin
|
||||
|
@ -5,17 +5,19 @@ RPN_APP = 1
|
||||
HEX_APP = 1
|
||||
STOPWATCH_APP = 1
|
||||
PHRASE_APP = 0
|
||||
SHABBAT_APP = 0
|
||||
HEBREW_APP = 1
|
||||
SHABBAT_APP = 1
|
||||
RNG_APP = 1
|
||||
TUNER_APP = 1
|
||||
MORSE_APP = 1
|
||||
TUNER_APP = 0
|
||||
MORSE_APP = 0
|
||||
BEACON_APP = 0
|
||||
OOK_APP = 1
|
||||
COUNTER_APP = 1
|
||||
DMESG_APP = 1
|
||||
PAGER_APP = 1
|
||||
DMESG_APP = 0
|
||||
PAGER_APP = 0
|
||||
JUKEBOX_APP = 1
|
||||
|
||||
|
||||
#set default flashing serial port, dont override if passed in as an argument
|
||||
PORT = /dev/ttyUSB0
|
||||
#Mandatory applets.
|
||||
@ -30,6 +32,10 @@ ifeq ($(SHABBAT_APP),1)
|
||||
APPS_OBJ += apps/shabbat.o
|
||||
APPS_DEFINES += SHABBAT_APP
|
||||
endif
|
||||
ifeq ($(HEBREW_APP),1)
|
||||
APPS_OBJ += apps/hebrew.o libs/hebrew.o
|
||||
APPS_DEFINES += HEBREW_APP
|
||||
endif
|
||||
ifeq ($(DMESG_APP),1)
|
||||
APPS_OBJ += apps/dmesg.o
|
||||
APPS_DEFINES += DMESG_APP
|
||||
@ -88,12 +94,12 @@ APPS_DEFINES += JUKEBOX_APP
|
||||
endif
|
||||
|
||||
|
||||
#Standard Debian gcc-msp430 and msp430mcu packages.
|
||||
CC = msp430-gcc -mmcu=cc430f6137 -Wall -I. -Os $(addprefix -D, $(APPS_DEFINES)) -Wl,--gc-sections -fdata-sections -ffunction-sections
|
||||
#GCC8 from Texas Instruments, not the GCC4 that ships with Debian.
|
||||
CC = msp430-elf-gcc -minrt -msmall -mmcu=cc430f6137 -Wall -I. -I/opt/msp430-gcc-support-files/include -Os $(addprefix -D, $(APPS_DEFINES)) -Wl,--gc-sections,--print-gc-sections -fdata-sections -ffunction-sections -fno-asynchronous-unwind-tables -flto
|
||||
|
||||
BSL = ../bin/cc430-bsl.py -r 38400 -p $(PORT)
|
||||
|
||||
modules=rtcasm-r15.o main.o lcd.o lcdtext.o rtc.o keypad.o bcd.o apps.o\
|
||||
modules=rtcasm-r12.o main.o lcd.o lcdtext.o rtc.o keypad.o bcd.o apps.o\
|
||||
applist.o adc.o ref.o codeplugstr.o \
|
||||
sidebutton.o power.o uart.o monitor.o ucs.o buzz.o \
|
||||
radio.o packet.o dmesg.o codeplug.o rng.o descriptor.o \
|
||||
@ -112,14 +118,16 @@ buildtime.h:
|
||||
../bin/buildtime.py >buildtime.h
|
||||
|
||||
goodwatch.elf: $(modules) $(apps) *.h
|
||||
$(CC) -T msp430.x -o goodwatch.elf $(modules) $(apps)
|
||||
#$(CC) -T msp430.x -o goodwatch.elf $(modules) $(apps)
|
||||
$(CC) -T cc430f6137.ld -o goodwatch.elf $(modules) $(apps)
|
||||
../bin/printsizes.py goodwatch.elf || echo "Please install python-pyelftools."
|
||||
|
||||
goodwatch.hex: goodwatch.elf
|
||||
msp430-objcopy -O ihex goodwatch.elf goodwatch.hex
|
||||
msp430-elf-objcopy -O ihex goodwatch.elf goodwatch.hex
|
||||
|
||||
clean:
|
||||
rm -rf *~ */*~ *.hex *.elf *.o */*.o goodwatch githash.h buildtime.h html latex goodwatch.elf energytrace.png energytrace.txt codeplugstr.c dmesg.bin
|
||||
cd libs && make clean
|
||||
erase:
|
||||
$(BSL) -e
|
||||
sbwflash: goodwatch.hex codeplug.hex
|
||||
|
@ -88,12 +88,12 @@ APPS_DEFINES += JUKEBOX_APP
|
||||
endif
|
||||
|
||||
|
||||
#GCC8 from Texas Instruments, not the GCC4 that ships with Debian.
|
||||
CC = msp430-elf-gcc -minrt -msmall -mmcu=cc430f6137 -Wall -I. -I/opt/msp430-gcc-support-files/include -Os $(addprefix -D, $(APPS_DEFINES)) -Wl,--gc-sections,--print-gc-sections -fdata-sections -ffunction-sections -fno-asynchronous-unwind-tables -flto
|
||||
#Standard Debian gcc-msp430 and msp430mcu packages.
|
||||
CC = msp430-gcc -mmcu=cc430f6137 -Wall -I. -Os $(addprefix -D, $(APPS_DEFINES)) -Wl,--gc-sections -fdata-sections -ffunction-sections
|
||||
|
||||
BSL = ../bin/cc430-bsl.py -r 38400 -p $(PORT)
|
||||
|
||||
modules=rtcasm-r12.o main.o lcd.o lcdtext.o rtc.o keypad.o bcd.o apps.o\
|
||||
modules=rtcasm-r15.o main.o lcd.o lcdtext.o rtc.o keypad.o bcd.o apps.o\
|
||||
applist.o adc.o ref.o codeplugstr.o \
|
||||
sidebutton.o power.o uart.o monitor.o ucs.o buzz.o \
|
||||
radio.o packet.o dmesg.o codeplug.o rng.o descriptor.o \
|
||||
@ -112,12 +112,11 @@ buildtime.h:
|
||||
../bin/buildtime.py >buildtime.h
|
||||
|
||||
goodwatch.elf: $(modules) $(apps) *.h
|
||||
#$(CC) -T msp430.x -o goodwatch.elf $(modules) $(apps)
|
||||
$(CC) -T cc430f6137.ld -o goodwatch.elf $(modules) $(apps)
|
||||
$(CC) -T msp430.x -o goodwatch.elf $(modules) $(apps)
|
||||
../bin/printsizes.py goodwatch.elf || echo "Please install python-pyelftools."
|
||||
|
||||
goodwatch.hex: goodwatch.elf
|
||||
msp430-elf-objcopy -O ihex goodwatch.elf goodwatch.hex
|
||||
msp430-objcopy -O ihex goodwatch.elf goodwatch.hex
|
||||
|
||||
clean:
|
||||
rm -rf *~ */*~ *.hex *.elf *.o */*.o goodwatch githash.h buildtime.h html latex goodwatch.elf energytrace.png energytrace.txt codeplugstr.c dmesg.bin
|
@ -78,11 +78,21 @@ const struct app subapps[]={
|
||||
|
||||
#ifdef SHABBAT_APP
|
||||
//Kosher applet for Shabbat that disables all inputs except the SET button.
|
||||
{.name="shabbat ", .init=shabbat_init, .draw=shabbat_draw, .exit=shabbat_exit,
|
||||
{.name="shabbat", .init=shabbat_init, .draw=shabbat_draw, .exit=shabbat_exit,
|
||||
.keypress=shabbat_keypress
|
||||
},
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HEBREW_APP
|
||||
//Hebrew Calendar applet. Falls through so that it can run from the clock.
|
||||
{.name="hebrew", .fallthrough=hebrew_keypress
|
||||
//.init=hebrew_init, .draw=hebrew_draw, .exit=hebrew_exit,
|
||||
//.keypress=hebrew_keypress,
|
||||
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef PHRASE_APP
|
||||
// Phrase - passphrase generator
|
||||
{.name="phrase", .init=phrase_init, .draw=phrase_draw, .exit=phrase_exit,
|
||||
|
@ -18,6 +18,7 @@ extern const struct app clock_applet;
|
||||
#include "apps/phrase.h"
|
||||
#include "apps/rngapp.h"
|
||||
#include "apps/shabbat.h"
|
||||
#include "apps/hebrew.h"
|
||||
#include "apps/dmesg.h"
|
||||
|
||||
//Then radio apps.
|
||||
|
@ -29,9 +29,23 @@ struct app {
|
||||
*/
|
||||
int (*keypress)(char ch);//A keypress has arrived.
|
||||
|
||||
/* Sometimes an app would like to operate without being explicitly
|
||||
entered. For example, we might want the OOK app to be able to
|
||||
ring a doorbell or buzz a dog collar without taking the trouble
|
||||
to manually enter it. For those cases, we have a fallthrough
|
||||
handler with the same calling convention as the keypress()
|
||||
function. They might even be defined to the same handler.
|
||||
|
||||
For now, only the clock applet falls through to another, and it
|
||||
only falls through for the buttons 1,2,3,-. This is subject to
|
||||
change.
|
||||
*/
|
||||
int (*fallthrough)(char ch);//A keypress has fallen through from the clock.
|
||||
|
||||
/* Callbacks for packets being sent and received. Set to null if unused. */
|
||||
void (*packetrx)(uint8_t *packet, int len); //A packet has arrived.
|
||||
void (*packettx)(void); //A packet has been sent.
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -264,9 +264,10 @@ int clock_keypress(char ch){
|
||||
break;
|
||||
|
||||
|
||||
case '1':
|
||||
/* 1 shows the the voltage. To save on power, the reference is
|
||||
not active when idling. */
|
||||
case '=':
|
||||
/* = shows the the voltage. To save on power, the reference is
|
||||
* not active when idling. This used to be the 1 button.
|
||||
*/
|
||||
ref_on();
|
||||
vcc=adc_getvcc();
|
||||
ref_off();
|
||||
@ -301,6 +302,14 @@ int clock_keypress(char ch){
|
||||
*/
|
||||
clock_playtime(1);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* All unused buttons fall through to the handler of the selected
|
||||
submenu applet, but only the third row (1,2,3,-) is expected to
|
||||
remain unused in the long run.
|
||||
*/
|
||||
return submenu_fallthrough(ch);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
156
firmware/apps/hebrew.c
Normal file
156
firmware/apps/hebrew.c
Normal file
@ -0,0 +1,156 @@
|
||||
/*! \file hebrew.c
|
||||
\brief Hebrew Calendar Application
|
||||
|
||||
This applet serves to display the time, but it also displays the
|
||||
Hebrew day, month, year, and dow. This will be the first applet to
|
||||
use fallthrough mode to also be accessible from the main screen.
|
||||
|
||||
Later, this might be merged with the Shabbat app, but until it
|
||||
stabilizes, they will be separate.
|
||||
|
||||
This applet only works in GCC8.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api.h"
|
||||
#include "applist.h"
|
||||
|
||||
#include "libs/hebrew.h"
|
||||
|
||||
static struct hebrew_date hdate;
|
||||
|
||||
//! Updates the hebrew date from the secular.
|
||||
static void hebrew_update(){
|
||||
static uint16_t oldhash=0;
|
||||
uint16_t newhash=RTCYEAR+RTCMON+RTCDAY; //Could be a better hash. Must be fast!
|
||||
|
||||
//Don't recalculate if the date is already right!
|
||||
if(newhash==oldhash)
|
||||
return;
|
||||
|
||||
//Don't run if we fail the selftest.
|
||||
if(!hebrew_selftest()){
|
||||
printf("Selftest failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
oldhash=newhash;
|
||||
|
||||
//Convert the date by running through days since 1900-1-1.
|
||||
uint32_t udate=hebrew_get_universal(RTCYEAR, RTCMON, RTCDAY);
|
||||
//uint32_t udate=hebrew_get_universal(1900, 1, 1);
|
||||
hebrew_calendar_from_universal(udate, &hdate);
|
||||
}
|
||||
|
||||
//! Draw the day of the week.
|
||||
static void hebrew_draw_dow(){
|
||||
lcd_string(hdaysofweek[RTCDOW]);
|
||||
}
|
||||
|
||||
//! Draw the day and month.
|
||||
static void hebrew_draw_date(){
|
||||
//Call this before anything that uses the date!
|
||||
hebrew_update();
|
||||
|
||||
/* Because the month index is ambiguous between the Biblical and
|
||||
Civil calendars, we must display the month. hmonths[] is an
|
||||
array of abreviated strings that are right-aligned on the
|
||||
display, leaving the two leftmost digits for the number.
|
||||
|
||||
Also, Adar 1 is the "extra month" in leap years, and we must
|
||||
manually override the table as an exception to that.
|
||||
*/
|
||||
if(hdate.month==12 && hebrew_calendar_leap_year_p(hdate.year))
|
||||
lcd_string(" adar1");
|
||||
else
|
||||
lcd_string(hmonths[hdate.month]);
|
||||
lcd_digit(7, (hdate.day/10)%10);
|
||||
lcd_digit(6, hdate.day%10);
|
||||
}
|
||||
|
||||
//! Draw the year, month, and day.
|
||||
static void hebrew_draw_year(){
|
||||
//Call this before anything that uses the date!
|
||||
hebrew_update();
|
||||
|
||||
unsigned int year=hdate.year;
|
||||
unsigned int month=hdate.month;
|
||||
unsigned int day=hdate.day;
|
||||
|
||||
lcd_digit(7,(year/1000)%10);
|
||||
lcd_digit(6,(year/100)%10);
|
||||
lcd_digit(5,(year/10)%10);
|
||||
lcd_digit(4,year%10);
|
||||
setperiod(4,1);
|
||||
setcolon(0);
|
||||
lcd_digit(3,month/10);
|
||||
lcd_digit(2,month%10);
|
||||
setperiod(2,1);
|
||||
lcd_digit(1,day/10);
|
||||
lcd_digit(0,day%10);
|
||||
|
||||
setam(0);
|
||||
setpm(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//! Enter the Hebrew application.
|
||||
void hebrew_init(){
|
||||
|
||||
}
|
||||
|
||||
//! Exit the Hebrew application.
|
||||
int hebrew_exit(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char lastchar=0;
|
||||
|
||||
//! Draw the Hebrew screen.
|
||||
void hebrew_draw(){
|
||||
/* When complete, we'll just exit right out of the Hebrew applet and
|
||||
let fallthrough mode do its job. For now, just render the time
|
||||
innefficiently and hope for the best.
|
||||
*/
|
||||
|
||||
//Draw the time, usually.
|
||||
if(!lastchar)
|
||||
draw_time(1);
|
||||
}
|
||||
|
||||
//! Keypress handler for the hebrew applet.
|
||||
int hebrew_keypress(char ch){
|
||||
lcd_zero();
|
||||
|
||||
/* We use the top row as it would be used in the Clock applet, and
|
||||
also the second row from the bottom, which will be dedicated to
|
||||
fallthrough mode.
|
||||
*/
|
||||
switch(lastchar=ch){
|
||||
case '8':
|
||||
case '2':
|
||||
hebrew_draw_year();
|
||||
break;
|
||||
|
||||
case '9':
|
||||
case '3':
|
||||
hebrew_draw_dow();
|
||||
break;
|
||||
|
||||
case '/':
|
||||
case '-':
|
||||
hebrew_draw_date();
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
17
firmware/apps/hebrew.h
Normal file
17
firmware/apps/hebrew.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*! \file hebrew.h
|
||||
\brief Hebrew Calendar Application
|
||||
|
||||
*/
|
||||
|
||||
//! Enter the Hebrew application.
|
||||
void hebrew_init();
|
||||
|
||||
//! Exit the Hebrew application.
|
||||
int hebrew_exit();
|
||||
|
||||
//! Draw the Hebrew screen.
|
||||
void hebrew_draw();
|
||||
|
||||
//! Keypress handler for the hebrew applet.
|
||||
int hebrew_keypress(char ch);
|
||||
|
@ -72,7 +72,15 @@ int submenu_exit(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
//! Draw the submenu selected.
|
||||
void submenu_drawselected(){
|
||||
lcd_string("selected");
|
||||
//! Lets a keypress fall through from the clock to the select submenu applet.
|
||||
int submenu_fallthrough(char ch){
|
||||
/* Technically any key which is not handled by the clock applet will
|
||||
fall through, but only the third row (1,2,3,-) is expected to
|
||||
remain unused.
|
||||
*/
|
||||
if(subapps[subindex].fallthrough)
|
||||
return subapps[subindex].fallthrough(ch);
|
||||
|
||||
//Return 1 if there is no handler, so tha the screen is redrawn.
|
||||
return 1;
|
||||
}
|
||||
|
@ -9,9 +9,6 @@ void submenu_draw();
|
||||
//! Change the selected applet.
|
||||
int submenu_keypress(char c);
|
||||
|
||||
//! Draw the submenu selected.
|
||||
void submenu_drawselected();
|
||||
|
||||
//! On exit, set the submenu app.
|
||||
int submenu_exit();
|
||||
|
||||
|
4
firmware/libs/.gitignore
vendored
Normal file
4
firmware/libs/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
assembler
|
||||
pocsag
|
||||
hebrew
|
||||
jukebox
|
@ -1,12 +1,13 @@
|
||||
# This is just for testing the libraries. They are build with
|
||||
# firmware/Makefile when running in the watch.
|
||||
|
||||
EXECS= assembler pocsag jukebox
|
||||
EXECS= assembler pocsag jukebox hebrew
|
||||
|
||||
run: all
|
||||
./assembler
|
||||
./pocsag
|
||||
./jukebox
|
||||
./hebrew
|
||||
|
||||
clean:
|
||||
rm -rf *.o $(EXECS)
|
||||
@ -14,10 +15,14 @@ all: $(EXECS)
|
||||
|
||||
|
||||
assembler: assembler.c assembler.h
|
||||
$(CC) -DSTANDALONE -o assembler $<
|
||||
$(CC) -Werror -DSTANDALONE -o assembler $<
|
||||
|
||||
pocsag: pocsag.c pocsag.h
|
||||
$(CC) -Werror -DSTANDALONE -o pocsag $<
|
||||
|
||||
jukebox: jukebox.c jukebox.h
|
||||
$(CC) -DSTANDALONE -o jukebox $<
|
||||
|
||||
hebrew: hebrew.c hebrew.h
|
||||
$(CC) -Werror -DSTANDALONE -o hebrew $<
|
||||
|
||||
|
368
firmware/libs/hebrew.c
Normal file
368
firmware/libs/hebrew.c
Normal file
@ -0,0 +1,368 @@
|
||||
/*! \file hebrew.c
|
||||
\brief Hebrew calendar library for LCD watches.
|
||||
|
||||
This is a library for implementing the modern Hebrew calendar on an
|
||||
LCD wristwatch, sourced from the RTC module of an MSP430. I'm new
|
||||
to this calendar, so I'd suggest double-checking this code before
|
||||
relying on it.
|
||||
|
||||
--Travis Goodspeed
|
||||
|
||||
Ported to MSP430 from CLISP's spvw_calendar.c, which was ported from Emacs.
|
||||
Copyright (C) 1995, 1997 Free Software Foundation, Inc.
|
||||
Copyright (C) 2003 Bruno Haible
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hebrew.h"
|
||||
|
||||
|
||||
/* Test whether the given year is a Hebrew calendar leap year. */
|
||||
int hebrew_calendar_leap_year_p (uint16_t year) {
|
||||
return ((7 * year + 1) % 19) < 7;
|
||||
}
|
||||
|
||||
/* Months up to mean conjunction of Tishri of the given year. */
|
||||
static uint32_t hebrew_calendar_elapsed_months (uint32_t year) {
|
||||
return ((year - 1) / 19) * 235
|
||||
+ ((year - 1) % 19) * 12
|
||||
+ (((year - 1) % 19) * 7 + 1) / 19;
|
||||
}
|
||||
|
||||
/* Days up to mean conjunction of Tishri of the given year. */
|
||||
static uint32_t hebrew_calendar_elapsed_days (uint16_t year) {
|
||||
uint32_t months_elapsed = hebrew_calendar_elapsed_months (year);
|
||||
uint32_t parts_elapsed = (months_elapsed % 1080) * 793 + 204;
|
||||
uint32_t hours_elapsed =
|
||||
5 + months_elapsed * 12 + (months_elapsed / 1080) * 793
|
||||
+ (parts_elapsed / 1080);
|
||||
uint32_t parts = (hours_elapsed % 24) * 1080 + (parts_elapsed % 1080);
|
||||
uint32_t day = 1 + months_elapsed * 29 + (hours_elapsed / 24);
|
||||
uint32_t day1 =
|
||||
(parts >= 19440
|
||||
|| ((day % 7) == 2 && parts >= 9924
|
||||
&& !hebrew_calendar_leap_year_p (year))
|
||||
|| ((day % 7) == 1 && parts >= 16789
|
||||
&& hebrew_calendar_leap_year_p (year - 1))
|
||||
? day + 1
|
||||
: day);
|
||||
uint32_t day2 =
|
||||
((day1 % 7) == 0 || (day1 % 7) == 3 || (day1 % 7) == 5
|
||||
? day1 + 1
|
||||
: day1);
|
||||
return day2;
|
||||
}
|
||||
|
||||
/* Return the number of days in the given year. */
|
||||
/* Example:
|
||||
hebrew_calendar_days_in_year (5763) = 385
|
||||
hebrew_calendar_days_in_year (5764) = 355
|
||||
Note that the result is in the range 351..357 or 380..386.
|
||||
Probably (but I cannot prove it) it is in the range 353..355 or 383..385.
|
||||
*/
|
||||
static int hebrew_calendar_days_in_year (uint16_t year) {
|
||||
return hebrew_calendar_elapsed_days (year + 1)
|
||||
- hebrew_calendar_elapsed_days (year);
|
||||
}
|
||||
|
||||
/* Test whether in the given year, the Heshvan month is long. */
|
||||
static int hebrew_calendar_long_heshvan_p (uint16_t year) {
|
||||
return (hebrew_calendar_days_in_year (year) % 10) == 5;
|
||||
}
|
||||
|
||||
/* Test whether in the given year, the Kislev month is short. */
|
||||
static int hebrew_calendar_short_kislev_p (uint16_t year) {
|
||||
return (hebrew_calendar_days_in_year (year) % 10) == 3;
|
||||
}
|
||||
|
||||
/* Return the number of months of the given year. */
|
||||
static int hebrew_calendar_months_in_year (uint16_t year) {
|
||||
return (hebrew_calendar_leap_year_p (year) ? 13 : 12);
|
||||
}
|
||||
|
||||
/* Return the number of days in the given month of the given year. */
|
||||
static int hebrew_calendar_last_day_of_month (uint16_t year, uint16_t month) {
|
||||
/* Note that month 7 is the first month, and month 6 is the last one. */
|
||||
switch (month) {
|
||||
case 7: /* Tishri */
|
||||
return 30;
|
||||
case 8: /* Heshvan */
|
||||
return (hebrew_calendar_long_heshvan_p (year) ? 30 : 29);
|
||||
case 9: /* Kislev */
|
||||
return (hebrew_calendar_short_kislev_p (year) ? 29 : 30);
|
||||
case 10: /* Teveth */
|
||||
return 29;
|
||||
case 11: /* Shevat */
|
||||
return 30;
|
||||
case 12: /* Adar, or - if leap year - Adar I */
|
||||
return (hebrew_calendar_leap_year_p (year) ? 30 : 29);
|
||||
case 13: /* - only if leap year - Adar II */
|
||||
return 29;
|
||||
case 1: /* Nisan */
|
||||
return 30;
|
||||
case 2: /* Iyar */
|
||||
return 29;
|
||||
case 3: /* Sivan */
|
||||
return 30;
|
||||
case 4: /* Tammuz */
|
||||
return 29;
|
||||
case 5: /* Av */
|
||||
return 30;
|
||||
case 6: /* Elul */
|
||||
return 29;
|
||||
default:
|
||||
//TODO: Some sort of error message here.
|
||||
//abort ();
|
||||
return 300; //Very wrong for now.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return the Hebrew date corresponding to a given universal date (= number
|
||||
of days since 1900-01-01). */
|
||||
/* Example:
|
||||
hebrew_calendar_from_universal (37888) = { 5763, 6, 29 }
|
||||
hebrew_calendar_from_universal (37889) = { 5764, 7, 1 }
|
||||
*/
|
||||
void hebrew_calendar_from_universal (uint32_t udate, struct hebrew_date *result) {
|
||||
uint32_t elapsed_days;
|
||||
uint32_t remaining_days;
|
||||
uint16_t max_month;
|
||||
uint16_t month;
|
||||
|
||||
//Floating point solution fails in MSP430.
|
||||
//uint16_t year = (uint16_t)((float)udate/(float)365.2422) + 5661;
|
||||
uint16_t year = udate*10000/3652422 + 5661;
|
||||
|
||||
year++;
|
||||
do{
|
||||
year--;
|
||||
elapsed_days = hebrew_calendar_elapsed_days(year) - 2067024L;
|
||||
}while(udate<elapsed_days);
|
||||
|
||||
remaining_days = udate - elapsed_days;
|
||||
max_month = hebrew_calendar_months_in_year (year);
|
||||
for (month = 7; month <= max_month; month++) {
|
||||
uint16_t mlength = hebrew_calendar_last_day_of_month (year, month);
|
||||
if (remaining_days < mlength)
|
||||
break;
|
||||
remaining_days -= mlength;
|
||||
}
|
||||
|
||||
if (month > max_month) {
|
||||
for (month = 1; month < 7; month++) {
|
||||
uint16_t mlength = hebrew_calendar_last_day_of_month (year, month);
|
||||
if (remaining_days < mlength)
|
||||
break;
|
||||
remaining_days -= mlength;
|
||||
}
|
||||
|
||||
//TODO: Re-enable this sanity check.
|
||||
//if (month == 7) abort ();
|
||||
}
|
||||
|
||||
result->year = year;
|
||||
result->month = month;
|
||||
result->day = remaining_days + 1;
|
||||
}
|
||||
|
||||
/* Return the number of Hanukka candles for a given universal date.
|
||||
static int hebrew_calendar_hanukka_candles (int udate) {
|
||||
// The first day of Hanukka is on 25 Kislev.
|
||||
struct hebrew_date date;
|
||||
int hanukka_first_day;
|
||||
|
||||
hebrew_calendar_from_universal (udate, &date);
|
||||
hanukka_first_day = hebrew_calendar_to_universal (date.year, 9, 25);
|
||||
if (udate - hanukka_first_day >= 0 && udate - hanukka_first_day <= 7)
|
||||
return (udate - hanukka_first_day + 1);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
// To store number of days in all months from January to Dec.
|
||||
const int monthDays[12] = {31, 28, 31, 30, 31, 30,
|
||||
31, 31, 30, 31, 30, 31};
|
||||
|
||||
//! This function counts number of leap years before the given date.
|
||||
static int countLeapYears(int y, int m, int d) {
|
||||
int years = y;
|
||||
|
||||
// Check if the current year needs to be considered for the count
|
||||
// of leap years or not
|
||||
if (m <= 2)
|
||||
years--;
|
||||
|
||||
// A year is a leap year if it is a multiple of 4, multiple of 400
|
||||
// and not a multiple of 100.
|
||||
return years / 4 - years / 100 + years / 400;
|
||||
}
|
||||
|
||||
// This function returns number of days since 1900-01-01
|
||||
uint32_t hebrew_get_universal(uint16_t y, uint16_t m, uint16_t d) {
|
||||
// Total number of days since 0.
|
||||
uint32_t n = ((uint32_t)y)*365 + d;
|
||||
for (uint16_t i=0; i< m-1; i++)
|
||||
n += monthDays[i];
|
||||
n += countLeapYears(y, m, d);
|
||||
|
||||
// Add 1900.
|
||||
return n-693961;
|
||||
}
|
||||
|
||||
const char *hdaysofweek[7]={
|
||||
" Rishon",
|
||||
" Sheni",
|
||||
" Shlishi",
|
||||
" Revii",
|
||||
"Chamishi",
|
||||
" Shishi",
|
||||
" Shabbat"
|
||||
};
|
||||
|
||||
/* Months are in the religious order, not the civil one, so that the
|
||||
leap years come at the end. 0 is an error.
|
||||
|
||||
I've shortened these to leave room for the number, so the left two
|
||||
spaces are reserved for that.
|
||||
*/
|
||||
const char *hmonths[14]={
|
||||
" Error",
|
||||
//Religious year begins here.
|
||||
" Nssn", //Nisan, 7 Civil
|
||||
" Iyar",
|
||||
" Svn", //Sivan
|
||||
" TMMZ", //Tammuz
|
||||
" Av",
|
||||
" Elul", //6 Civil
|
||||
" Tish", //1 Civil, Tishrei
|
||||
" Chesh", //Cheshvan
|
||||
" Kslv",
|
||||
" Tevet",
|
||||
" Shvt", //Shevat
|
||||
" Adar", //Adar I in leap years.
|
||||
" Adar2"
|
||||
};
|
||||
|
||||
|
||||
|
||||
int hebrew_selftest(){
|
||||
//Quick sanity check.
|
||||
|
||||
if(!hebrew_calendar_leap_year_p (5763)
|
||||
|| hebrew_calendar_leap_year_p (5764)){
|
||||
printf("Error in leap years.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(hebrew_calendar_elapsed_months (5763) != 71266
|
||||
|| hebrew_calendar_elapsed_months (5764) != 71279){
|
||||
printf("Error in elapsed months.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(hebrew_calendar_elapsed_days (5763) != 2104528){
|
||||
printf("Error in elapsed days.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//Define this for unit testing in Unix.
|
||||
#ifdef STANDALONE
|
||||
|
||||
/* Return the number of days since 1900-01-01 of a given Hebrew date. */
|
||||
static int hebrew_calendar_to_universal (int year, int month, int day) {
|
||||
uint32_t days;
|
||||
int m;
|
||||
|
||||
days = hebrew_calendar_elapsed_days (year) - 2067024;
|
||||
if (month < 7) {
|
||||
int max_month = hebrew_calendar_months_in_year (year);
|
||||
for (m = 7; m <= max_month; m++)
|
||||
days += hebrew_calendar_last_day_of_month (year, m);
|
||||
for (m = 1; m < month; m++)
|
||||
days += hebrew_calendar_last_day_of_month (year, m);
|
||||
} else {
|
||||
for (m = 7; m < month; m++)
|
||||
days += hebrew_calendar_last_day_of_month (year, m);
|
||||
}
|
||||
days += day - 1;
|
||||
return days;
|
||||
}
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
//Very important, time.h can't be used in functions above.
|
||||
#include <time.h>
|
||||
|
||||
//Gregorian calendar.
|
||||
time_t gtime;
|
||||
struct tm* gtm;
|
||||
//Gregorian year, month, day, day of week
|
||||
int y=0, m=0, d=0, dow=0;
|
||||
//Days since 1900;
|
||||
uint32_t udate=0;
|
||||
|
||||
//Hebrew calendar.
|
||||
struct hebrew_date hdate;
|
||||
|
||||
//! Unix tool for testing.
|
||||
int main(){
|
||||
//Asserts to test the sanity of our conversion functions.
|
||||
assert(hebrew_calendar_leap_year_p (5763));
|
||||
assert(!hebrew_calendar_leap_year_p (5764));
|
||||
assert(hebrew_calendar_elapsed_months (5763) == 71266);
|
||||
assert(hebrew_calendar_elapsed_months (5764) == 71279);
|
||||
assert(hebrew_calendar_elapsed_days (5763) == 2104528);
|
||||
assert(hebrew_calendar_elapsed_days (5764) == 2104913);
|
||||
assert(hebrew_calendar_days_in_year (5763) == 385);
|
||||
assert(hebrew_calendar_days_in_year (5764) == 355);
|
||||
assert(hebrew_calendar_to_universal (5763, 6, 29) == 37888);
|
||||
assert(hebrew_calendar_to_universal (5764, 7, 1) == 37889);
|
||||
//hebrew_calendar_from_universal (37888) = { 5763, 6, 29 }
|
||||
hebrew_calendar_from_universal(37888, &hdate);
|
||||
assert(hdate.year==5763 &&
|
||||
hdate.month==6 &&
|
||||
hdate.day==29);
|
||||
//hebrew_calendar_from_universal (37889) = { 5764, 7, 1 }
|
||||
hebrew_calendar_from_universal(37889, &hdate);
|
||||
assert(hdate.year==5764 &&
|
||||
hdate.month==7 &&
|
||||
hdate.day==1);
|
||||
|
||||
//Get the current time.
|
||||
gtime=time(0);
|
||||
gtm=gmtime(>ime);
|
||||
|
||||
//Decode it to what would be in the MSP430 registers.
|
||||
y=gtm->tm_year+1900;
|
||||
m=gtm->tm_mon+1; //1 is january
|
||||
d=gtm->tm_mday;
|
||||
dow=gtm->tm_wday;
|
||||
printf("Gregorian: %04d.%02d.%02d, DOW=%d\n",
|
||||
y,m,d,dow);
|
||||
|
||||
udate=hebrew_get_universal(y,m,d);
|
||||
printf("Days since 1900: %d\n",
|
||||
udate);
|
||||
|
||||
hebrew_calendar_from_universal(udate, &hdate);
|
||||
printf("Hebrew: %02d %s %04d, %s\n",
|
||||
hdate.day, hmonths[hdate.month], hdate.year,
|
||||
hdaysofweek[dow]);
|
||||
|
||||
assert(hebrew_selftest());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif //STANDALONE
|
20
firmware/libs/hebrew.h
Normal file
20
firmware/libs/hebrew.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*! \file hebrew.h
|
||||
\brief Hebrew calendar library.
|
||||
|
||||
We try to define the bare minimum of exported symbols. See main()
|
||||
in libs/hebrew.c for the host-side test cases and apps/hebrew.c for
|
||||
the device-side GUI code.
|
||||
*/
|
||||
|
||||
|
||||
struct hebrew_date { uint16_t year; uint16_t month; uint16_t day; };
|
||||
|
||||
extern uint32_t hebrew_get_universal(uint16_t y, uint16_t m, uint16_t d);
|
||||
extern void hebrew_calendar_from_universal (uint32_t udate, struct hebrew_date *result);
|
||||
extern int hebrew_calendar_leap_year_p (uint16_t year);
|
||||
|
||||
extern int hebrew_selftest();
|
||||
|
||||
|
||||
extern const char *hdaysofweek[];
|
||||
extern const char *hmonths[];
|
Loading…
Reference in New Issue
Block a user