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:
Travis Goodspeed 2020-01-22 19:21:04 -05:00 committed by GitHub
parent 9c9ab02717
commit e21465869b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 647 additions and 33 deletions

2
firmware/.gitignore vendored
View File

@ -1,6 +1,4 @@
config.h
buildtime.h
libs/assembler
libs/pocsag
codeplugstr.c
dmesg.bin

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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.

View File

@ -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.
};

View File

@ -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
View 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
View 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);

View File

@ -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;
}

View File

@ -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
View File

@ -0,0 +1,4 @@
assembler
pocsag
hebrew
jukebox

View File

@ -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
View 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(&gtime);
//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
View 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[];