Commits

Trammell Hudson committed e3c2686

Started on solar/lunar clock

Comments (0)

Files changed (190)

+SRCS += main.c
+SRCS += lunar.c
+SRCS += draw.c
+SRCS += sin_table.c
+
+include ../Makefile.rules
+
+
+# Convert a lunar.gif image to a "compressed" png with the
+# three luma samples converted into rgb format
+# Images are from tycho.usno.navy.mil
+resources/%.png: images/%.gif
+	convert \
+		-crop 81x81+10+10 \
+		$< \
+		gray:- \
+	| convert \
+		-depth 8 \
+		-size 27x81 \
+		rgb:- \
+		$@
+
+images: \
+	resources/m000.png \
+	resources/m012.png \
+	resources/m024.png \
+	resources/m038.png \
+	resources/m050.png \
+	resources/m064.png \
+	resources/m076.png \
+	resources/m090.png \
+	resources/m102.png \
+	resources/m114.png \
+	resources/m128.png \
+	resources/m140.png \
+	resources/m154.png \
+	resources/m166.png \
+	resources/m180.png \
+	resources/m192.png \
+	resources/m204.png \
+	resources/m218.png \
+	resources/m230.png \
+	resources/m244.png \
+	resources/m256.png \
+	resources/m270.png \
+	resources/m282.png \
+	resources/m294.png \
+	resources/m308.png \
+	resources/m320.png \
+	resources/m334.png \
+	resources/m346.png \
+
+/** \file
+ * Drawing functions.
+ */
+#include "draw.h"
+
+
+void
+draw_pixel(
+	color24_t c,
+	int32_t x,
+	int32_t y
+)
+{
+	//printf("pix %d %d @ %d %d %d\n", x, y, c.red, c.blue, c.green);
+	if (x >= VSCREEN_WIDTH || y >= VSCREEN_HEIGHT)
+		return;
+	if (x < 0 || y < 0)
+		return;
+
+	x >>= VSCREEN_SHIFT;
+	y >>= VSCREEN_SHIFT;
+	pulse_set_draw_window(x, y, x, y);
+	pulse_draw_point24(c);
+}
+
+
+
+static inline int32_t
+vscreen_ipart(
+	int32_t x
+)
+{
+	return x & ~(VSCREEN_PIXEL - 1);
+}
+
+
+static inline int32_t
+vscreen_fpart(
+	int32_t x
+)
+{
+	return x & (VSCREEN_PIXEL - 1);
+}
+
+
+static inline int32_t
+vscreen_rfpart(
+	int32_t x
+)
+{
+	return VSCREEN_PIXEL - vscreen_fpart(x);
+}
+
+
+static inline int32_t
+vscreen_round(
+	int32_t x
+)
+{
+	return vscreen_ipart(x + (VSCREEN_PIXEL / 2));
+}
+
+
+static void
+draw_pixel_aa(
+	color24_t c,
+	int32_t x,
+	int32_t y,
+	uint32_t frac
+)
+{
+	if (c.red)
+		c.red = (c.red * frac) >> VSCREEN_SHIFT;
+	if (c.blue)
+		c.blue = (c.blue * frac) >> VSCREEN_SHIFT;
+	if (c.green)
+		c.green = (c.green * frac) >> VSCREEN_SHIFT;
+
+	draw_pixel(c, x, y);
+}
+
+
+/** Draw using Xiaolin Wu's line algorithm */
+#define GRAD_SCALE 256
+
+void
+draw_line_horiz(
+	color24_t c,
+	int32_t x1,
+	int32_t y1,
+	int32_t x2,
+	int32_t y2
+)
+{
+	const int32_t dx = x2 - x1;
+	const int32_t dy = y2 - y1;
+
+	if (x2 < x1)
+	{
+		int32_t temp = x1;
+		x1 = x2;
+		x2 = temp;
+		temp = y1;
+		y1 = y2;
+		y2 = temp;
+	}
+
+	// The gradient is scaled by another shift factor since
+	// otherwise we won't hit the end points correctly.
+	const int32_t grad = (dy * GRAD_SCALE * VSCREEN_PIXEL) / dx;
+	const int32_t half_pixel = VSCREEN_PIXEL / 2;
+
+	int32_t xend, yend, xgap;
+
+	// Handle first endpoint
+	xend = vscreen_round(x1);
+	yend = y1 + (grad * (xend - x1)) / GRAD_SCALE / VSCREEN_PIXEL;
+	xgap = vscreen_rfpart(x1 + half_pixel);
+	const int32_t xpxl1 = xend;
+	const int32_t ypxl1 = vscreen_ipart(yend);
+	draw_pixel_aa(c, xpxl1, ypxl1, (vscreen_rfpart(yend) * xgap) / VSCREEN_PIXEL);
+	draw_pixel_aa(c, xpxl1, ypxl1 + VSCREEN_PIXEL, (vscreen_fpart(yend) * xgap) / VSCREEN_PIXEL);
+	int32_t intery = yend * GRAD_SCALE + grad; // first y-intersection
+
+	//printf("%d %d %d => %d %d grad %d\n", x1, xend, y1, x2, y2, grad);
+
+
+	// Handle second endpoint
+	xend = vscreen_round(x2);
+	yend = y2 + (grad * (xend - x2)) / GRAD_SCALE / VSCREEN_PIXEL;
+	xgap = vscreen_fpart(x2 + half_pixel);
+	const int32_t xpxl2 = xend;
+	const int32_t ypxl2 = vscreen_ipart(yend);
+	draw_pixel_aa(c, xpxl2, ypxl2, (vscreen_rfpart(yend) * xgap) / VSCREEN_PIXEL);
+	draw_pixel_aa(c, xpxl2, ypxl2 + VSCREEN_PIXEL, (vscreen_fpart(yend) * xgap) / VSCREEN_PIXEL);
+
+	//printf("x: %d %d\n", xpxl1 + 1, xpxl2);
+
+	// Loop (this could make a single window draw)
+	for (int32_t x = xpxl1 + VSCREEN_PIXEL ; x < xpxl2 ; x += VSCREEN_PIXEL)
+	{
+		int32_t y = intery / GRAD_SCALE;
+		intery += grad;
+		draw_pixel_aa(c, x, vscreen_ipart(y), vscreen_rfpart(y));
+		draw_pixel_aa(c, x, vscreen_ipart(y)+VSCREEN_PIXEL, vscreen_fpart(y));
+	}
+}
+
+
+static void
+draw_line_vert(
+	color24_t c,
+	int32_t x1,
+	int32_t y1,
+	int32_t x2,
+	int32_t y2
+)
+{
+	const int32_t dx = x2 - x1;
+	const int32_t dy = y2 - y1;
+
+	if (y2 < y1)
+	{
+		int32_t temp = x1;
+		x1 = x2;
+		x2 = temp;
+		temp = y1;
+		y1 = y2;
+		y2 = temp;
+	}
+
+	const int32_t grad = (dx * VSCREEN_PIXEL * GRAD_SCALE) / dy;
+	const int32_t half_pixel = VSCREEN_PIXEL / 2;
+
+	int32_t xend, yend, ygap;
+
+	// Handle first endpoint
+	yend = vscreen_round(y1);
+	xend = x1 + (grad * (yend - y1)) / GRAD_SCALE / VSCREEN_PIXEL;
+	ygap = vscreen_rfpart(y1 + half_pixel);
+	const int32_t ypxl1 = yend;
+	const int32_t xpxl1 = vscreen_ipart(xend);
+	draw_pixel_aa(c, xpxl1, ypxl1, (vscreen_rfpart(xend) * ygap) / VSCREEN_PIXEL);
+	draw_pixel_aa(c, xpxl1 + VSCREEN_PIXEL, ypxl1, (vscreen_fpart(xend) * ygap) / VSCREEN_PIXEL);
+	int32_t interx = xend * GRAD_SCALE + grad; // first x-intersection
+
+
+	// Handle second endpoint
+	yend = vscreen_round(y2);
+	xend = x2 + (grad * (yend - y2)) / GRAD_SCALE / VSCREEN_PIXEL;
+	ygap = vscreen_fpart(y2 + half_pixel);
+	const int32_t ypxl2 = yend;
+	const int32_t xpxl2 = vscreen_ipart(xend);
+	draw_pixel_aa(c, xpxl2, ypxl2, (vscreen_rfpart(xend) * ygap) / VSCREEN_PIXEL);
+	draw_pixel_aa(c, xpxl2 + VSCREEN_PIXEL, ypxl2, (vscreen_fpart(xend) * ygap) / VSCREEN_PIXEL);
+
+	//printf("x: %d %d\n", xpxl1 + 1, xpxl2);
+
+	// Loop (this could make a single window draw)
+	for (int32_t y = ypxl1 + VSCREEN_PIXEL ; y < ypxl2 ; y += VSCREEN_PIXEL)
+	{
+		int32_t x = interx / GRAD_SCALE;
+		interx += grad;
+
+		draw_pixel_aa(c, vscreen_ipart(x), y, vscreen_rfpart(x));
+		draw_pixel_aa(c, vscreen_ipart(x)+VSCREEN_PIXEL, y, vscreen_fpart(x));
+	}
+}
+
+
+void
+draw_line(
+	color24_t c,
+	int32_t x0,
+	int32_t y0,
+	int32_t x1,
+	int32_t y1
+)
+{
+	const int32_t dx = x1 > x0 ? x1 - x0 : x0 - x1;
+	const int32_t dy = y1 > y0 ? y1 - y0 : y0 - y1;
+	if (dx == 0 && dy == 0)
+		draw_pixel(c, x0, y0);
+	else
+	if (dx > dy)
+		draw_line_horiz(c, x0, y0, x1, y1);
+	else
+		draw_line_vert(c, x0, y0, x1, y1);
+
+/*
+	// Mark where we should start and end
+	draw_pixel(COLOR_WHITE, x0, y0);
+	draw_pixel(COLOR_BLUE, x1, y1);
+*/
+}
+
+
+
+
+static char buf[2];
+struct PWidgetTextDynamic font;
+
+void
+draw_monostring(
+	int32_t x,
+	int32_t y,
+	color24_t color,
+	PulseResource font_name,
+	int width,
+	const char * s
+)
+{
+	pulse_init_dynamic_text_widget(
+		&font,
+		buf,
+		font_name,
+		color,
+		PWTS_TRUNCATE
+	);
+
+	uint32_t orig_x = x >> VSCREEN_SHIFT;
+
+	struct PWTextBox box = {
+		.left		= orig_x,
+		.top		= y >> VSCREEN_SHIFT,
+		.right		= SCREEN_WIDTH - 1,
+		.bottom		= SCREEN_HEIGHT - 1,
+	};
+
+	buf[1] = '\0';
+
+	while ((buf[0] = *s++))
+	{
+		pulse_render_text(&box, &font);
+		box.left += width;
+	}
+}
+/** \file
+ * Line and pixel drawing routines.
+ * Using a virtual screen buffer that is twice the resolution
+ * along each axis so that anti-aliasing can be done later.
+ */
+#ifndef _draw_h_
+
+#include <pulse_os.h>
+#include <pulse_types.h>
+#include "app_resources.h"
+
+#define VSCREEN_SHIFT	0
+#define VSCREEN_PIXEL	(1 << VSCREEN_SHIFT)
+#define VSCREEN_MASK	(VSCREEN_PIXEL - 1)
+#define VSCREEN_WIDTH	(((int32_t) SCREEN_WIDTH) << VSCREEN_SHIFT)
+#define VSCREEN_HEIGHT	(((int32_t) SCREEN_HEIGHT) << VSCREEN_SHIFT)
+
+/** Predefined colors that we use */
+#define COLOR_GRAY		((color24_t) { 0x18, 0x18, 0x18, 0 } )
+#define COLOR_RED		((color24_t) { 0xFF, 0x00, 0x00, 0 } )
+#define COLOR_GREEN		((color24_t) { 0x00, 0xFF, 0x00, 0 } )
+#define COLOR_DARKGREEN		((color24_t) { 0x00, 0x10, 0x00, 0 } )
+#define COLOR_YELLOW		((color24_t) { 0xF0, 0xF0, 0x00, 0 } )
+#define COLOR_MIDNIGHTBLUE	((color24_t) { 0x19, 0x19, 0x70, 0 } )
+#define COLOR_BLUE		((color24_t) { 0x00, 0x00, 0xC0, 0 } )
+#define COLOR_WHITE		((color24_t) { 0xFF, 0xFF, 0xFF, 0 } )
+#define COLOR_BLACK		((color24_t) { 0x00, 0x00, 0x00, 0 } )
+
+
+/* Draw a pixel in vscreen coordinates */
+void
+draw_pixel(
+	color24_t c,
+	int32_t x,
+	int32_t y
+);
+
+
+void
+fill(
+	color24_t color,
+	int32_t x0,
+	int32_t y0,
+	int32_t x1,
+	int32_t y1
+);
+
+
+/* Draw a non-antialised line using Bresenham's_line_algorithm
+ * in vscreen coordinates */
+void
+draw_line(
+	color24_t c,
+	int32_t x0,
+	int32_t y0,
+	int32_t x1,
+	int32_t y1
+);
+
+
+void
+draw_monostring(
+	int32_t x,
+	int32_t y,
+	color24_t color,
+	PulseResource font,
+	int width,
+	const char * s
+);
+
+#endif
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
Added
New image
+/** \file
+ * Draw a lunar face clock as well as the sun-up, sun-down times.
+ * None of that is ready yet; this is a first import with copy.
+ */
+#include <string.h>
+#include "main.h"
+#include "draw.h"
+#include "sin_table.h"
+
+
+void
+init(void)
+{
+}
+
+
+void
+button_down(void)
+{
+	init();
+}
+
+
+void
+button_up(
+	uint32_t ms
+)
+{
+	// Nothing to do
+	(void) ms;
+}
+
+
+
+static void
+circle_line(
+	color24_t color,
+	int32_t x0,
+	int32_t y0,
+	int32_t radius
+)
+{
+	int32_t f = 1 - radius;
+	int ddF_x = 1;
+	int ddF_y = -2 * radius;
+	int x = 0;
+	int y = radius;
+
+	draw_pixel(color, x0, y0 + radius);
+	draw_pixel(color, x0, y0 - radius);
+	draw_pixel(color, x0 + radius, y0);
+	draw_pixel(color, x0 - radius, y0);
+
+	while (x < y)
+	{
+		if (f >= 0)
+		{
+			y--;
+			ddF_y += 2;
+			f += ddF_y;
+		}
+		x++;
+		ddF_x += 2;
+		f += ddF_x;
+
+		draw_pixel(color, x0 + x, y0 + y);
+		draw_pixel(color, x0 + x, y0 - y);
+		draw_pixel(color, x0 - x, y0 + y);
+		draw_pixel(color, x0 - x, y0 - y);
+
+		draw_pixel(color, x0 + y, y0 + x);
+		draw_pixel(color, x0 + y, y0 - x);
+		draw_pixel(color, x0 - y, y0 + x);
+		draw_pixel(color, x0 - y, y0 - x);
+	}
+}
+
+
+void
+fill(
+	color24_t color,
+	int32_t x0,
+	int32_t y0,
+	int32_t x1,
+	int32_t y1
+)
+{
+	int32_t temp;
+	if (x0 > x1)
+	{
+		temp = x0;
+		x0 = x1;
+		x1 = temp;
+	}
+	if (y0 > y1)
+	{
+		temp = y0;
+		y0 = y1;
+		y1 = temp;
+	}
+
+	if (x1 < 0 || y1 < 0)
+		return;
+	if (x0 >= SCREEN_WIDTH || y1 >= SCREEN_HEIGHT)
+		return;
+	if (x0 < 0)
+		x0 = 0;
+	if (y0 < 0)
+		y0 = 0;
+	if (x1 >= SCREEN_WIDTH)
+		x1 = SCREEN_WIDTH - 1;
+	if (y1 >= SCREEN_HEIGHT)
+		y1 = SCREEN_HEIGHT - 1;
+
+	int32_t h = y1 - y0 + 1;
+	int32_t w = x1 - x0 + 1;
+	uint16_t num = w * h;
+	if (num == 0)
+		return;
+	
+	pulse_set_draw_window(x0, y0, x1, y1);
+
+	while (num--)
+		pulse_draw_point24(color);
+}
+
+
+static void
+circle_fill(
+	color24_t color,
+	int32_t x0,
+	int32_t y0,
+	int32_t radius
+)
+{
+	int32_t f = 1 - radius;
+	int ddF_x = 1;
+	int ddF_y = -2 * radius;
+	int x = 0;
+	int y = radius;
+
+	fill(color, x0 - radius, y0, x0 + radius, y0);
+
+	while (x < y)
+	{
+		if (f >= 0)
+		{
+			y--;
+			ddF_y += 2;
+			f += ddF_y;
+		}
+		x++;
+		ddF_x += 2;
+		f += ddF_x;
+
+		fill(color, x0 - x, y0 + y, x0 + x, y0 + y);
+		fill(color, x0 - x, y0 - y, x0 + x, y0 - y);
+		fill(color, x0 - y, y0 + x, x0 + y, y0 + x);
+		fill(color, x0 - y, y0 - x, x0 + y, y0 - x);
+	}
+}
+
+
+#if 0
+static void
+draw_gradient_arc(
+	int32_t radius,
+	int32_t dot_radius,
+	uint16_t initial_angle
+)
+{
+	const uint16_t arc_len = 0xB000;
+	const uint16_t step = 128;
+
+	for (uint32_t t = 0 ; t < arc_len  ; t += step)
+	{
+		const uint16_t angle = initial_angle + t - arc_len;
+		const int16_t sx = (radius * sin_lookup(angle)) / 32768;
+		const int16_t sy = (radius * cos_lookup(angle)) / 32768;
+
+		color24_t color = {
+			(t * dot_color.red) / arc_len,
+			(t * dot_color.green) / arc_len,
+			(t * dot_color.blue) / arc_len,
+		};
+
+		circle_fill(
+			color,
+			SCREEN_WIDTH/2 + sx,
+			SCREEN_HEIGHT/2 - sy,
+			dot_radius
+		);
+	}
+}
+#endif
+
+
+void
+draw(
+	uint32_t cur_ms
+)
+{
+	uint32_t ms = cur_ms % 1000; cur_ms /= 1000;
+	const uint32_t new_sec = cur_ms % 60; cur_ms /= 60;
+	const uint8_t new_min = cur_ms % 60; cur_ms /= 60;
+	const uint8_t new_hour = cur_ms % 12; // cur_ms %= 24;
+
+#if 0
+	const uint16_t sec_angle = ((new_sec * 6 + (ms*6) / 1000) * 32768) / 180;
+	const uint16_t min_angle = ((new_min * 6 + (new_sec*6) / 60) * 32768) / 180;
+	const uint16_t hour_angle = ((new_hour * 30 + (new_min*30) / 60) * 32768) / 180;
+
+	const int32_t sec_radius = SCREEN_WIDTH/2;
+	const int32_t sec_dot_radius = 3;
+	if (sec_angle != last_sec_angle || first_time)
+	{
+		draw_gradient_arc(
+			sec_radius,
+			sec_dot_radius,
+			sec_angle
+		);
+		last_sec_angle = sec_angle;
+	}
+
+	const int32_t min_dot_radius =  8;
+	const int32_t min_radius = sec_radius - sec_dot_radius - min_dot_radius - 1;
+	if (min_angle != last_min_angle || first_time)
+	{
+		draw_gradient_arc(
+			min_radius,
+			min_dot_radius,
+			min_angle
+		);
+		last_min_angle = min_angle;
+	}
+
+	const int32_t hour_dot_radius = 12;
+	const int32_t hour_radius = min_radius - min_dot_radius - hour_dot_radius - 1;
+	if (hour_angle != last_hour_angle || first_time)
+	{
+		draw_gradient_arc(
+			hour_radius,
+			hour_dot_radius,
+			hour_angle
+		);
+		last_hour_angle = hour_angle;
+	}
+
+	first_time = 0;
+#endif
+}
+/** \file
+ * Main program for all of the 30-days watches.
+ *
+ * The mundane bits involved with power management, setup, etc are
+ * handled here.  The bootloader mode is also done here.
+ *
+ * The watch program only needs to implement:
+ *
+ * init (called by wakeup)
+ * draw
+ *
+ */
+#include "main.h"
+#include "draw.h"
+
+
+static uint32_t not_charging;
+static uint8_t shutdown_scheduled;
+
+
+/** At wake up, sample the boot time in ms and use it to scale
+ * the ms timestamp to a time since midnight
+ */
+static uint32_t ms_since_midnight;
+static uint32_t epoch_ms;
+
+/* Track how long the button has been held down for */
+static uint8_t do_load;
+static uint32_t button_down_ms;
+
+
+
+
+static void
+do_empty_loop(void)
+{
+	static uint8_t last_sec;
+	struct pulse_time_tm now;
+	pulse_get_time_date(&now);
+	if (now.tm_sec == last_sec)
+		return;
+	last_sec = now.tm_sec;
+
+	printf("%02d:%02d:%02d\n", now.tm_hour, now.tm_min, now.tm_sec);
+}
+
+
+
+void
+main_app_loop(void)
+{
+	// Limit the update rate to 30 fps == 33 ms/frame
+	const uint64_t millis = pulse_get_millis();
+	if (button_down_ms && millis - button_down_ms > 1000)
+	{
+		do_load = !do_load;
+		button_down_ms = 0;
+		return;
+	}
+	if (do_load)
+	{
+		do_empty_loop();
+		return;
+	}
+
+	// Draw the charging indicator
+	color24_t charge_light = not_charging
+		? (color24_t){10,10,10}
+		: (color24_t){0x80,0,0};
+
+	fill(
+		charge_light,
+		0,
+		8,
+		2,
+		12
+	);
+
+        // If the not-charging state has not been toggled in the last
+        // 30 seconds, schedule a power down soon.
+        if (not_charging && millis - not_charging > 60000)
+	{
+		if (!shutdown_scheduled)
+			pulse_update_power_down_timer(1);
+		shutdown_scheduled = 1;
+		return;
+	}
+
+	// Compute the ms since midnight
+	uint32_t cur_ms = ms_since_midnight + (millis - epoch_ms);
+	draw(cur_ms);
+}
+
+
+static void
+wakeup(void *arg)
+{
+	pulse_oled_set_brightness(100);
+	pulse_blank_canvas();
+	not_charging = pulse_get_millis();
+	shutdown_scheduled = 0;
+
+	// Compute the number of mseconds since midnight
+	struct pulse_time_tm now;
+	pulse_get_time_date(&now);
+	uint32_t sec_since_midnight = 0
+		+ now.tm_sec
+		+ now.tm_min * 60
+		+ now.tm_hour * 3600;
+	ms_since_midnight = sec_since_midnight * 1000;
+	epoch_ms = pulse_get_millis();
+	do_load = 0;
+	button_down_ms = 0;
+
+	init();
+}
+
+
+void
+main_app_init(void)
+{
+	// Register a callback for the woke_from_button event
+	pulse_register_callback(
+		ACTION_WOKE_FROM_BUTTON,
+		&wakeup
+	);
+
+	wakeup(0);
+}
+
+
+void
+main_app_handle_button_down(void)
+{
+	// Track when the button was pressed and draw a small
+	// marker to indicate it since my button is twitchy.
+	button_down_ms = pulse_get_millis();
+
+	// Reset the shutdown timer when they hit the button
+	if (not_charging)
+		not_charging = pulse_get_millis();
+
+	fill(
+		COLOR_BLUE,
+		VSCREEN_WIDTH - (2 << VSCREEN_SHIFT),
+		VSCREEN_HEIGHT/2 - (2 << VSCREEN_SHIFT),
+		VSCREEN_WIDTH - 1,
+		VSCREEN_HEIGHT/2 + (2 << VSCREEN_SHIFT)
+	);
+
+	button_down();
+}
+
+
+void
+main_app_handle_button_up(void)
+{
+	const uint32_t now = pulse_get_millis();
+
+	// Stop the power down timer
+	if (not_charging)
+		not_charging = now;
+
+	button_up(now - button_down_ms);
+	button_down_ms = 0;
+
+	fill(
+		COLOR_BLACK,
+		VSCREEN_WIDTH - (2 << VSCREEN_SHIFT),
+		VSCREEN_HEIGHT/2 - (2 << VSCREEN_SHIFT),
+		VSCREEN_WIDTH - 1,
+		VSCREEN_HEIGHT/2 + (2 << VSCREEN_SHIFT)
+	);
+
+	
+}
+
+
+void
+main_app_handle_hardware_update(
+	enum PulseHardwareEvent event
+)
+{
+	switch (event)
+	{
+	case BATTERY_CHARGING:
+		// Erase the last non-charging time to avoid flakey power
+		not_charging = 0;
+		break;
+
+	case BATTERY_NOT_CHARGING:
+		// Record the time that the state was toggled.
+		not_charging = pulse_get_millis();
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+void
+main_app_handle_doz(void)
+{
+	// Fade out
+	for (int i = 100; i >= 0; i -= 6)
+	{
+		pulse_oled_set_brightness(i);
+		pulse_mdelay(60);
+	}
+}
+/** \file
+ * Simplified watch interface.
+ *
+ * The watch program must implement these functions.
+ */
+#ifndef _main_h_
+#define _main_h_
+
+#include <pulse_os.h>
+#include <pulse_types.h>
+#include <app_resources.h>
+#include <stdio.h>
+#include <stdint.h>
+
+
+/** init() is called when the watch wakes up.
+ * The screen will be blank and the OLED will be at 100%.
+ */
+void init(void);
+
+/** draw() is called everytime the app mainloop is invoked.
+ * \todo Frame rate limiting?
+ */
+void draw(uint32_t ms_since_midnight);
+
+/** button_down() is called as soon as the button is pressed */
+void button_down(void);
+
+/** button_up() is called as soon as the button is released
+ * with the length of time in ms that the button was held.
+ */
+void button_up(uint32_t ms_held);
+
+#endif

lunar/resources/fonts.txt

+# This file describes font rendering options
+# 
+# <filename>, <friendly_name>, <px_size>, <clock_font>, <pixel_font>
+#
+# <filename>      - filename of the ttf font to render 
+#                   ('#' is an illegal character in the name and avoid spaces in filenames)
+# <friendly_name> - name that will be used in the header file generated
+#                   ('#' and spaces are illegal characters in the name)
+# <px_size>       - size in px to render the font
+# <clock_font>    - (y/n) only render characters 0 - 9, :, a, p, A, P (for use as a clock)
+# <pixel_font>    - (y/n) don't render grayscale (for pixel fonts)
+#
+# Example:
+# good_times.ttf, good_times_10, 10, n, n
+#Clockopia.ttf, clockopia_32, 32, y, n
+#Clockopia.ttf, clockopia_22, 22, y, n

lunar/sin_table.c

+#include "sin_table.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+static const uint8_t sin_table[] = {
+	0,
+	25,
+	50,
+	74,
+	98,
+	121,
+	142,
+	162,
+	181,
+	198,
+	213,
+	226,
+	237,
+	245,
+	251,
+	255,
+	255 // duplicate to avoid extra code for handling last case
+};
+
+
+int16_t
+sin_lookup(
+	uint16_t theta_in
+)
+{
+	int sign = 1;
+	uint32_t theta = theta_in;
+
+	if (theta < 64ul*256)
+	{
+		// q1, upward slope, normal theta, positive sign
+	} else
+	if (theta < 128ul*256)
+	{
+		theta = 128ul*256 - theta; // downward slope, still positive
+	} else
+	if (theta < 192ul*256)
+	{
+		// q3, downward slope, negative side
+		theta = theta - 128ul*256;
+		sign = -1;
+	} else
+	{
+		// q4, upward slope, negative side
+		theta = 256ul*256 - theta;
+		sign = -1;
+	}
+
+	int32_t s1 = ((uint32_t) sin_table[(theta >> 10) + 0]) << 8;
+	int32_t s2 = ((uint32_t) sin_table[(theta >> 10) + 1]) << 8;
+	int16_t result = (s1 + ((s2 - s1) * (theta & 1023)) / 1024) / 2;
+
+	if (sign == -1)
+		return -result;
+	else
+		return result;
+}
+
+
+#if 0
+int main(void)
+{
+	unsigned theta;
+
+	for (theta = 0 ; theta < 0x100 ; theta++)
+	{
+		double sx = sin_lookup(theta) / 128.0;
+		double sf = sin(theta * 2 * M_PI / 256.0);
+		double cx = cos_lookup(theta) / 128.0;
+		double cf = cos(theta * 2 * M_PI / 256.0);
+
+		printf("%.4f %.4f %.4f\n",
+			cx,
+			cf,
+			cf - cx
+		);
+	}
+}
+#endif

lunar/sin_table.h

+/** \file
+ * Approximate sin function.
+ * theta goes from 0 == 0 Pi to 65536 == 2 Pi
+ */
+#ifndef _sin_table_h_
+#define _sin_table_h_
+
+#include <stdint.h>
+
+extern int16_t sin_lookup(uint16_t theta);
+
+static inline int16_t
+cos_lookup(
+	uint16_t theta
+)
+{
+	return sin_lookup(theta + 16384);
+}
+
+
+#endif
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.