Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions src/MoonPhase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Calculate the phase and position of the moon for a given date.
// The algorithm is simple and adequate for many purposes.
//
// This software was originally adapted to javascript by Stephen R. Schmitt
// from a BASIC program from the 'Astronomical Computing' column of Sky & Telescope,
// April 1994, page 86, written by Bradley E. Schaefer.
//
// Subsequently adapted from Stephen R. Schmitt's javascript to c++ for the Arduino
// by Cyrus Rahman, this work is subject to Stephen Schmitt's copyright:
//
// Copyright 2004 Stephen R. Schmitt
// You may use or modify this source code in any way you find useful, provided
// that you agree that the author(s) have no warranty, obligations or liability. You
// must determine the suitability of this source code for your use.

#include <math.h>
#include "MoonPhase.h"

// Names of lunar phases
static const char *phaseNames[] = {"New", "Evening Crescent", "First Quarter",
"Waxing Gibbous", "Full", "Waning Gibbous",
"Last Quarter", "Morning Crescent"};
// Names of Zodiac constellations
static const char *zodiacNames[] = {"Pisces", "Aries", "Taurus", "Gemini", "Cancer",
"Leo", "Virgo", "Libra", "Scorpio", "Sagittarius",
"Capricorn", "Aquarius"};
// Ecliptic angles of Zodiac constellations
static const float zodiacAngles[] = {33.18, 51.16, 93.44, 119.48, 135.30, 173.34,
224.17, 242.57, 271.26, 302.49, 311.72, 348.58};

// Constructor initialization.
MoonPhase::MoonPhase() {
jDate = 0;
phase = 0;
age = 0;
fraction = 0;
distance = 0;
latitude = 0;
longitude = 0;
phaseName = zodiacName = "";
}

// Determine the Moon Phase and orbital positions for the specified time.
void
MoonPhase::calculate(time_t t) {
jDate = julianDate(t);

// Calculate illumination (synodic) phase.
// From number of days since new moon on Julian date MOON_SYNODIC_OFFSET
// (1815UTC January 6, 2000), determine remainder of incomplete cycle.
phase = (jDate - MOON_SYNODIC_OFFSET) / MOON_SYNODIC_PERIOD;
phase -= floor(phase);

// Calculate age and illumination fraction.
age = phase * MOON_SYNODIC_PERIOD;
fraction = (1.0 - cos(2 * M_PI * phase)) * 0.5;
phaseName = phaseNames[(int)(phase * 8 + 0.5) % 8];

// Calculate distance from anomalistic phase.
double distancePhase = (jDate - MOON_DISTANCE_OFFSET) / MOON_DISTANCE_PERIOD;
distancePhase -= floor(distancePhase);
distance = 60.4 - 3.3 * cos(2 * M_PI * distancePhase)
- 0.6 * cos(2 * 2 * M_PI * phase - 2 * M_PI * distancePhase)
- 0.5 * cos(2 * 2 * M_PI * phase);

// Calculate ecliptic latitude from nodal (draconic) phase.
double latPhase = (jDate - MOON_LATITUDE_OFFSET) / MOON_LATITUDE_PERIOD;
latPhase -= floor(latPhase);
latitude = 5.1 * sin(2 * M_PI * latPhase);

// Calculate ecliptic longitude from sidereal motion.
double longPhase = (jDate - MOON_LONGITUDE_OFFSET) / MOON_LONGITUDE_PERIOD;
longPhase -= floor(longPhase);
longitude = 360 * longPhase
+ 6.3 * sin(2 * M_PI * distancePhase)
+ 1.3 * sin(2 * 2 * M_PI * phase - 2 * M_PI * distancePhase)
+ 0.7 * sin(2 * 2 * M_PI * phase);
if (longitude > 360)
longitude -= 360;

// Select the Zodiac name.
zodiacName = zodiacNames[0];
for (int i = 0; i < sizeof(zodiacAngles) / sizeof(float); i++) {
if (longitude < zodiacAngles[i]) {
zodiacName = zodiacNames[i];
break;
}
}
}

// Determine Julian date from Unix time.
// Provides marginally accurate results with older Arduino 4-byte double.
double
MoonPhase::julianDate(time_t t) {
return (t / 86400.0L + 2440587.5);
}
33 changes: 33 additions & 0 deletions src/MoonPhase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef MoonPhase_h
#define MoonPhase_h

#include <time.h>

#define MOON_SYNODIC_PERIOD 29.530588853 // Period of moon cycle in days.
#define MOON_SYNODIC_OFFSET 2451550.26 // Reference cycle offset in days.
#define MOON_DISTANCE_PERIOD 27.55454988 // Period of distance oscillation
#define MOON_DISTANCE_OFFSET 2451562.2
#define MOON_LATITUDE_PERIOD 27.212220817 // Latitude oscillation
#define MOON_LATITUDE_OFFSET 2451565.2
#define MOON_LONGITUDE_PERIOD 27.321582241 // Longitude oscillation
#define MOON_LONGITUDE_OFFSET 2451555.8

class MoonPhase {
public:
double jDate;
double phase; // 0 - 1, 0.5 = full
double age; // Age in days of current cycle
double fraction; // Fraction of illuminated disk
double distance; // Moon distance in earth radii
double latitude; // Moon ecliptic latitude
double longitude; // Moon ecliptic longitude
const char *phaseName; // New, Full, etc.
const char *zodiacName; // Constellation

MoonPhase();
void calculate(time_t);

private:
double julianDate(time_t);
};
#endif
79 changes: 71 additions & 8 deletions src/Watchy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ RTC_DATA_ATTR bool USB_PLUGGED_IN = false;
RTC_DATA_ATTR tmElements_t bootTime;
RTC_DATA_ATTR uint32_t lastIPAddress;
RTC_DATA_ATTR char lastSSID[30];
RTC_DATA_ATTR MoonPhase mp;

void Watchy::init(String datetime) {
esp_sleep_wakeup_cause_t wakeup_reason;
Expand All @@ -39,7 +40,7 @@ void Watchy::init(String datetime) {
RTC.init();
// Init the display since is almost sure we will use it
display.epd2.initWatchy();

mp = MoonPhase();
switch (wakeup_reason) {
#ifdef ARDUINO_ESP32S3_DEV
case ESP_SLEEP_WAKEUP_TIMER: // RTC Alarm
Expand Down Expand Up @@ -97,6 +98,9 @@ void Watchy::init(String datetime) {
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
break;
}

time_t epoch = makeTime(currentTime);
mp.calculate(epoch);
deepSleep();
}
void Watchy::deepSleep() {
Expand Down Expand Up @@ -167,6 +171,8 @@ void Watchy::handleButtonPress() {
case 6:
showSyncNTP();
break;
case 7:
showMoonPhase();
default:
break;
}
Expand Down Expand Up @@ -249,6 +255,8 @@ void Watchy::handleButtonPress() {
case 6:
showSyncNTP();
break;
case 7:
showMoonPhase();
default:
break;
}
Expand Down Expand Up @@ -302,9 +310,9 @@ void Watchy::showMenu(byte menuIndex, bool partialRefresh) {
const char *menuItems[] = {
"About Watchy", "Vibrate Motor", "Show Accelerometer",
"Set Time", "Setup WiFi", "Update Firmware",
"Sync NTP"};
"Sync NTP", "Moon Phase"};
for (int i = 0; i < MENU_LENGTH; i++) {
yPos = MENU_HEIGHT + (MENU_HEIGHT * i);
yPos = MENU_HEIGHT/2 + (MENU_HEIGHT * i);
display.setCursor(0, yPos);
if (i == menuIndex) {
display.getTextBounds(menuItems[i], 0, yPos, &x1, &y1, &w, &h);
Expand Down Expand Up @@ -335,9 +343,9 @@ void Watchy::showFastMenu(byte menuIndex) {
const char *menuItems[] = {
"About Watchy", "Vibrate Motor", "Show Accelerometer",
"Set Time", "Setup WiFi", "Update Firmware",
"Sync NTP"};
"Sync NTP", "Moon Phase"};
for (int i = 0; i < MENU_LENGTH; i++) {
yPos = MENU_HEIGHT + (MENU_HEIGHT * i);
yPos = MENU_HEIGHT/2 + (MENU_HEIGHT * i);
display.setCursor(0, yPos);
if (i == menuIndex) {
display.getTextBounds(menuItems[i], 0, yPos, &x1, &y1, &w, &h);
Expand Down Expand Up @@ -404,6 +412,57 @@ void Watchy::showAbout() {
guiState = APP_STATE;
}

void Watchy::showMoonPhase() {

RTC.read(currentTime);
time_t epoch = makeTime(currentTime);
mp.calculate(epoch);

display.setFullWindow();
display.fillScreen(GxEPD_BLACK);
display.setFont(&FreeMonoBold9pt7b);
display.setTextColor(GxEPD_WHITE);
display.setCursor(0, 10);

display.setCursor(0, MENU_HEIGHT);

display.print("Date: ");
display.println(mp.jDate);

display.print("Phase: ");
display.println(mp.phase);

display.print("Age: ");
display.print(mp.age);
display.println(" days");

display.print("Visibility: ");
display.print(mp.fraction);
display.println("%");

display.print("Distance: ");
display.print(mp.distance);
display.println(" er");

display.print("Latitude: ");
display.print(mp.latitude);
display.println("°");

display.print("Longitude: ");
display.print(mp.longitude);
display.println("°");

display.print("Ph.: ");
display.println(mp.phaseName);

display.print("Zodiac: ");
display.println(mp.zodiacName);

display.display(true); // full refresh

guiState = APP_STATE;
}

void Watchy::showBuzz() {
display.setFullWindow();
display.fillScreen(GxEPD_BLACK);
Expand Down Expand Up @@ -443,7 +502,7 @@ void Watchy::setTime() {
int8_t hour = currentTime.Hour;
int8_t day = currentTime.Day;
int8_t month = currentTime.Month;
int8_t year = currentTime.Year; //tmYearToY2k(currentTime.Year);
int8_t year = tmYearToY2k(currentTime.Year);
#endif
int8_t gmt = gmtOffset / 3600;

Expand Down Expand Up @@ -580,7 +639,7 @@ void Watchy::setTime() {
if (setIndex == SET_YEAR) { // blink minute digits
display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK);
}
display.print(year);
display.print(1970 + year);

display.setTextColor(GxEPD_WHITE);
display.print("/");
Expand Down Expand Up @@ -613,7 +672,7 @@ void Watchy::setTime() {
#ifdef ARDUINO_ESP32S3_DEV
tm.Year = year;
#else
tm.Year = year; //y2kYearToTm(year);
tm.Year = y2kYearToTm(year);
#endif
tm.Hour = hour;
tm.Minute = minute;
Expand Down Expand Up @@ -1200,5 +1259,9 @@ bool Watchy::syncNTP(long gmt, String ntpServer) {
tmElements_t tm;
breakTime((time_t)timeClient.getEpochTime(), tm);
RTC.set(tm);
//Update also moon calendar.
RTC.read(currentTime);
time_t epoch = makeTime(currentTime);
mp.calculate(epoch);
return true;
}
2 changes: 2 additions & 0 deletions src/Watchy.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "config.h"
#include "esp_chip_info.h"
#include "TimezonesGMT.h"
#include "MoonPhase.h"
#ifdef ARDUINO_ESP32S3_DEV
#include "Watchy32KRTC.h"
#include "soc/rtc.h"
Expand Down Expand Up @@ -86,6 +87,7 @@ class Watchy {
void showMenu(byte menuIndex, bool partialRefresh);
void showFastMenu(byte menuIndex);
void showAbout();
void showMoonPhase();
void showBuzz();
void showAccelerometer();
void showUpdateFW();
Expand Down
4 changes: 2 additions & 2 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@
#define MAIN_MENU_STATE 0
#define APP_STATE 1
#define FW_UPDATE_STATE 2
#define MENU_HEIGHT 25
#define MENU_LENGTH 7
#define MENU_HEIGHT 24
#define MENU_LENGTH 8
// set time
#define SET_HOUR 0
#define SET_MINUTE 1
Expand Down