Moonrise face
This commit is contained in:
		
							parent
							
								
									0ef5e5700e
								
							
						
					
					
						commit
						3f7adcc996
					
				
							
								
								
									
										297
									
								
								movement/lib/moonrise/moonrise.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								movement/lib/moonrise/moonrise.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,297 @@
 | 
			
		||||
// Compute times of moonrise and moonset at a specified latitude and longitude.
 | 
			
		||||
//
 | 
			
		||||
// This software minimizes computational work by performing the full calculation
 | 
			
		||||
// of the lunar position three times, at the beginning, middle, and end of the
 | 
			
		||||
// period of interest.  Three point interpolation is used to predict the position
 | 
			
		||||
// for each hour, and the arithmetic mean is used to predict the half-hour positions.
 | 
			
		||||
//
 | 
			
		||||
// The full computational burden is negligible on modern computers, but the
 | 
			
		||||
// algorithm is effective and still useful for small embedded systems.
 | 
			
		||||
//
 | 
			
		||||
// This software was originally adapted to javascript by Stephen R. Schmitt
 | 
			
		||||
// from a BASIC program from the 'Astronomical Computing' column of Sky & Telescope,
 | 
			
		||||
// April 1989, page 78.
 | 
			
		||||
//
 | 
			
		||||
// Subsequently adapted from Stephen R. Schmitt's javascript to c++ for the Arduino
 | 
			
		||||
// by Cyrus Rahman.
 | 
			
		||||
//
 | 
			
		||||
// Subsequently adapted from Cyrus Rahman's Arduino C++ to C for the Sensor Watch
 | 
			
		||||
// by hueso, this work is subject to Stephen Schmitt's copyright:
 | 
			
		||||
//
 | 
			
		||||
// Copyright 2007 Stephen R. Schmitt  
 | 
			
		||||
// Subsequent work Copyright 2020 Cyrus Rahman
 | 
			
		||||
// 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.
 | 
			
		||||
//
 | 
			
		||||
// Redistributions of this source code must retain this copyright notice.
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include "moonrise.h"
 | 
			
		||||
 | 
			
		||||
#define K1 15*(M_PI/180)*1.0027379
 | 
			
		||||
 | 
			
		||||
// Determine the nearest moon rise or set event previous, and the nearest
 | 
			
		||||
// moon rise or set event subsequent, to the specified time in seconds since the
 | 
			
		||||
// Unix epoch (January 1, 1970) and at the specified latitude and longitude in
 | 
			
		||||
// degrees.
 | 
			
		||||
//
 | 
			
		||||
// We look for events from MR_WINDOW/2 hours in the past to MR_WINDOW/2 hours
 | 
			
		||||
// in the future.
 | 
			
		||||
MoonRise MoonRise_calculate(double latitude, double longitude, time_t t) {
 | 
			
		||||
  MoonRise self;
 | 
			
		||||
  skyCoordinates moonPosition[3];
 | 
			
		||||
  double offsetDays;
 | 
			
		||||
  
 | 
			
		||||
  self.queryTime = t;
 | 
			
		||||
  offsetDays = julianDate(t) - 2451545L;     // Days since Jan 1, 2000, 1200UTC.
 | 
			
		||||
  // Begin testing (MR_WINDOW / 2) hours before requested time.
 | 
			
		||||
  offsetDays -= (double)MR_WINDOW / (2 * 24) ;	
 | 
			
		||||
 | 
			
		||||
  // Calculate coordinates at start, middle, and end of search period.
 | 
			
		||||
  for (int i = 0; i < 3; i++) {
 | 
			
		||||
    moonPosition[i] = moon(offsetDays + i * (double)MR_WINDOW / (2 * 24));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If the RA wraps around during this period, unwrap it to keep the
 | 
			
		||||
  // sequence smooth for interpolation.
 | 
			
		||||
  if (moonPosition[1].RA <= moonPosition[0].RA)
 | 
			
		||||
    moonPosition[1].RA += 2 * M_PI;
 | 
			
		||||
  if (moonPosition[2].RA <= moonPosition[1].RA)
 | 
			
		||||
    moonPosition[2].RA += 2 * M_PI;
 | 
			
		||||
 | 
			
		||||
  // Initialize interpolation array.
 | 
			
		||||
  skyCoordinates mpWindow[3];
 | 
			
		||||
  mpWindow[0].RA  = moonPosition[0].RA;
 | 
			
		||||
  mpWindow[0].declination = moonPosition[0].declination;
 | 
			
		||||
  mpWindow[0].distance = moonPosition[0].distance;
 | 
			
		||||
 | 
			
		||||
  for (int k = 0; k < MR_WINDOW; k++) {	    // Check each interval of search period
 | 
			
		||||
    float ph = (float)(k + 1)/MR_WINDOW;
 | 
			
		||||
        
 | 
			
		||||
    mpWindow[2].RA = interpolate(moonPosition[0].RA,
 | 
			
		||||
				 moonPosition[1].RA,
 | 
			
		||||
				 moonPosition[2].RA, ph);
 | 
			
		||||
    mpWindow[2].declination = interpolate(moonPosition[0].declination,
 | 
			
		||||
					  moonPosition[1].declination,
 | 
			
		||||
					  moonPosition[2].declination, ph);
 | 
			
		||||
    mpWindow[2].distance = moonPosition[2].distance;
 | 
			
		||||
 | 
			
		||||
    // Look for moonrise/set events during this interval.
 | 
			
		||||
    {
 | 
			
		||||
        double ha[3], VHz[3];
 | 
			
		||||
        double lSideTime;
 | 
			
		||||
 | 
			
		||||
        // Get (local_sidereal_time - MR_WINDOW / 2) hours in radians.
 | 
			
		||||
        lSideTime = localSiderealTime(offsetDays, longitude) * 2* M_PI / 360;
 | 
			
		||||
 | 
			
		||||
        // Calculate Hour Angle.
 | 
			
		||||
        ha[0] = lSideTime - mpWindow[0].RA + k*K1;
 | 
			
		||||
        ha[2] = lSideTime - mpWindow[2].RA + k*K1 + K1;
 | 
			
		||||
 | 
			
		||||
        // Hour Angle and declination at half hour.
 | 
			
		||||
        ha[1]  = (ha[2] + ha[0])/2;
 | 
			
		||||
        mpWindow[1].declination = (mpWindow[2].declination + mpWindow[0].declination)/2;
 | 
			
		||||
 | 
			
		||||
        double s = sin(M_PI / 180 * latitude);
 | 
			
		||||
        double c = cos(M_PI / 180 * latitude);
 | 
			
		||||
 | 
			
		||||
        // refraction + semidiameter at horizon + distance correction
 | 
			
		||||
        double z = cos(M_PI / 180 * (90.567 - 41.685 / mpWindow[0].distance));
 | 
			
		||||
 | 
			
		||||
        VHz[0] = s * sin(mpWindow[0].declination) + c * cos(mpWindow[0].declination) * cos(ha[0]) - z;
 | 
			
		||||
        VHz[2] = s * sin(mpWindow[2].declination) + c * cos(mpWindow[2].declination) * cos(ha[2]) - z;
 | 
			
		||||
 | 
			
		||||
        if (signbit(VHz[0]) == signbit(VHz[2]))
 | 
			
		||||
        goto noevent;			    // No event this hour.
 | 
			
		||||
 | 
			
		||||
        VHz[1] = s * sin(mpWindow[1].declination) + c * cos(mpWindow[1].declination) * cos(ha[1]) - z;
 | 
			
		||||
 | 
			
		||||
        double a, b, d, e, time;
 | 
			
		||||
        a = 2 * VHz[2] - 4 * VHz[1] + 2 * VHz[0];
 | 
			
		||||
        b = 4 * VHz[1] - 3 * VHz[0] - VHz[2];
 | 
			
		||||
        d = b * b - 4 * a * VHz[0];
 | 
			
		||||
 | 
			
		||||
        if (d < 0)
 | 
			
		||||
        goto noevent;			    // No event this hour.
 | 
			
		||||
 | 
			
		||||
        d = sqrt(d);
 | 
			
		||||
        e = (-b + d) / (2 * a);
 | 
			
		||||
        if ((e < 0) || (e > 1))
 | 
			
		||||
        e = (-b - d) / (2 * a);
 | 
			
		||||
        time = k + e + 1 / 120;	    // Time since k=0 of event (in hours).
 | 
			
		||||
 | 
			
		||||
        // The time we started searching + the time from the start of the search to the
 | 
			
		||||
        // event is the time of the event.  Add (time since k=0) - window/2 hours.
 | 
			
		||||
        time_t eventTime;
 | 
			
		||||
        eventTime = self.queryTime + (time - MR_WINDOW / 2) *60 *60;
 | 
			
		||||
 | 
			
		||||
        double hz, nz, dz, az;
 | 
			
		||||
        hz = ha[0] + e * (ha[2] - ha[0]);	    // Azimuth of the moon at the event.
 | 
			
		||||
        nz = -cos(mpWindow[1].declination) * sin(hz);
 | 
			
		||||
        dz = c * sin(mpWindow[1].declination) - s * cos(mpWindow[1].declination) * cos(hz);
 | 
			
		||||
        az = atan2(nz, dz) / (M_PI / 180);
 | 
			
		||||
        if (az < 0)
 | 
			
		||||
        az += 360;
 | 
			
		||||
 | 
			
		||||
        // If there is no previously recorded event of this type, save this event.
 | 
			
		||||
        //
 | 
			
		||||
        // If this event is previous to queryTime, and is the nearest event to queryTime
 | 
			
		||||
        // of events of its type previous to queryType, save this event, replacing the
 | 
			
		||||
        // previously recorded event of its type.  Events subsequent to queryTime are
 | 
			
		||||
        // treated similarly, although since events are tested in chronological order
 | 
			
		||||
        // no replacements will occur as successive events will be further from
 | 
			
		||||
        // queryTime.
 | 
			
		||||
        //
 | 
			
		||||
        // If this event is subsequent to queryTime and there is an event of its type
 | 
			
		||||
        // previous to queryTime, then there is an event of the other type between the
 | 
			
		||||
        // two events of this event's type.  If the event of the other type is
 | 
			
		||||
        // previous to queryTime, then it is the nearest event to queryTime that is
 | 
			
		||||
        // previous to queryTime.  In this case save the current event, replacing
 | 
			
		||||
        // the previously recorded event of its type.  Otherwise discard the current
 | 
			
		||||
        // event.
 | 
			
		||||
        //
 | 
			
		||||
        if ((VHz[0] < 0) && (VHz[2] > 0)) {
 | 
			
		||||
            if (!self.hasRise ||
 | 
			
		||||
            ((self.riseTime < self.queryTime) == (eventTime < self.queryTime) &&
 | 
			
		||||
            llabs(self.riseTime - self.queryTime) > llabs(eventTime - self.queryTime)) ||
 | 
			
		||||
            ((self.riseTime < self.queryTime) != (eventTime < self.queryTime) &&
 | 
			
		||||
            (self.hasSet && 
 | 
			
		||||
            (self.riseTime < self.queryTime) == (self.setTime < self.queryTime)))) {
 | 
			
		||||
            self.riseTime = eventTime;
 | 
			
		||||
            self.riseAz = az;
 | 
			
		||||
            self.hasRise = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ((VHz[0] > 0) && (VHz[2] < 0)) {
 | 
			
		||||
            if (!self.hasSet ||
 | 
			
		||||
            ((self.setTime < self.queryTime) == (eventTime < self.queryTime) &&
 | 
			
		||||
            llabs(self.setTime - self.queryTime) > llabs(eventTime - self.queryTime)) ||
 | 
			
		||||
            ((self.setTime < self.queryTime) != (eventTime < self.queryTime) &&
 | 
			
		||||
            (self.hasRise && 
 | 
			
		||||
            (self.setTime < self.queryTime) == (self.riseTime < self.queryTime)))) {
 | 
			
		||||
            self.setTime = eventTime;
 | 
			
		||||
            self.setAz = az;
 | 
			
		||||
            self.hasSet = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        noevent:
 | 
			
		||||
        // There are obscure cases in the polar regions that require extra logic.
 | 
			
		||||
        if (!self.hasRise && !self.hasSet)
 | 
			
		||||
        self.isVisible = !signbit(VHz[2]);
 | 
			
		||||
        else if (self.hasRise && !self.hasSet)
 | 
			
		||||
        self.isVisible = (self.queryTime > self.riseTime);
 | 
			
		||||
        else if (!self.hasRise && self.hasSet)
 | 
			
		||||
        self.isVisible = (self.queryTime < self.setTime);
 | 
			
		||||
        else
 | 
			
		||||
        self.isVisible = ((self.riseTime < self.setTime && self.riseTime < self.queryTime && self.setTime > self.queryTime) ||
 | 
			
		||||
        (self.riseTime > self.setTime && (self.riseTime < self.queryTime || self.setTime > self.queryTime)));
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    mpWindow[0] = mpWindow[2];		    // Advance to next interval.
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Moon position using fundamental arguments 
 | 
			
		||||
// (Van Flandern & Pulkkinen, 1979)
 | 
			
		||||
skyCoordinates moon(double dayOffset) {
 | 
			
		||||
  double l = 0.606434 + 0.03660110129 * dayOffset;
 | 
			
		||||
  double m = 0.374897 + 0.03629164709 * dayOffset;
 | 
			
		||||
  double f = 0.259091 + 0.03674819520 * dayOffset;
 | 
			
		||||
  double d = 0.827362 + 0.03386319198 * dayOffset;
 | 
			
		||||
  double n = 0.347343 - 0.00014709391 * dayOffset;
 | 
			
		||||
  double g = 0.993126 + 0.00273777850 * dayOffset;
 | 
			
		||||
 | 
			
		||||
  l = 2 * M_PI * (l - floor(l));
 | 
			
		||||
  m = 2 * M_PI * (m - floor(m));
 | 
			
		||||
  f = 2 * M_PI * (f - floor(f));
 | 
			
		||||
  d = 2 * M_PI * (d - floor(d));
 | 
			
		||||
  n = 2 * M_PI * (n - floor(n));
 | 
			
		||||
  g = 2 * M_PI * (g - floor(g));
 | 
			
		||||
 | 
			
		||||
  double v, u, w;
 | 
			
		||||
  v = 0.39558 * sin(f + n)
 | 
			
		||||
    + 0.08200 * sin(f)
 | 
			
		||||
    + 0.03257 * sin(m - f - n)
 | 
			
		||||
    + 0.01092 * sin(m + f + n)
 | 
			
		||||
    + 0.00666 * sin(m - f)
 | 
			
		||||
    - 0.00644 * sin(m + f - 2*d + n)
 | 
			
		||||
    - 0.00331 * sin(f - 2*d + n)
 | 
			
		||||
    - 0.00304 * sin(f - 2*d)
 | 
			
		||||
    - 0.00240 * sin(m - f - 2*d - n)
 | 
			
		||||
    + 0.00226 * sin(m + f)
 | 
			
		||||
    - 0.00108 * sin(m + f - 2*d)
 | 
			
		||||
    - 0.00079 * sin(f - n)
 | 
			
		||||
    + 0.00078 * sin(f + 2*d + n);
 | 
			
		||||
    
 | 
			
		||||
  u = 1
 | 
			
		||||
    - 0.10828 * cos(m)
 | 
			
		||||
    - 0.01880 * cos(m - 2*d)
 | 
			
		||||
    - 0.01479 * cos(2*d)
 | 
			
		||||
    + 0.00181 * cos(2*m - 2*d)
 | 
			
		||||
    - 0.00147 * cos(2*m)
 | 
			
		||||
    - 0.00105 * cos(2*d - g)
 | 
			
		||||
    - 0.00075 * cos(m - 2*d + g);
 | 
			
		||||
    
 | 
			
		||||
  w = 0.10478 * sin(m)
 | 
			
		||||
    - 0.04105 * sin(2*f + 2*n)
 | 
			
		||||
    - 0.02130 * sin(m - 2*d)
 | 
			
		||||
    - 0.01779 * sin(2*f + n)
 | 
			
		||||
    + 0.01774 * sin(n)
 | 
			
		||||
    + 0.00987 * sin(2*d)
 | 
			
		||||
    - 0.00338 * sin(m - 2*f - 2*n)
 | 
			
		||||
    - 0.00309 * sin(g)
 | 
			
		||||
    - 0.00190 * sin(2*f)
 | 
			
		||||
    - 0.00144 * sin(m + n)
 | 
			
		||||
    - 0.00144 * sin(m - 2*f - n)
 | 
			
		||||
    - 0.00113 * sin(m + 2*f + 2*n)
 | 
			
		||||
    - 0.00094 * sin(m - 2*d + g)
 | 
			
		||||
    - 0.00092 * sin(2*m - 2*d);
 | 
			
		||||
 | 
			
		||||
  double s;
 | 
			
		||||
  skyCoordinates sc;
 | 
			
		||||
  s = w / sqrt(u - v*v);
 | 
			
		||||
  sc.RA = l + atan(s / sqrt(1 - s*s));		      // Right ascension
 | 
			
		||||
 | 
			
		||||
  s = v / sqrt(u);
 | 
			
		||||
  sc.declination = atan(s / sqrt(1 - s*s));	      // Declination
 | 
			
		||||
  sc.distance = 60.40974 * sqrt(u);		      // Distance
 | 
			
		||||
  return(sc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 3-point interpolation
 | 
			
		||||
double interpolate(double f0, double f1, double f2, double p) {
 | 
			
		||||
    double a = f1 - f0;
 | 
			
		||||
    double b = f2 - f1 - a;
 | 
			
		||||
    return(f0 + p * (2*a + b * (2*p - 1)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Determine Julian date from Unix time.
 | 
			
		||||
// Provides marginally accurate results with Arduino 4-byte double.
 | 
			
		||||
double julianDate(time_t t) {
 | 
			
		||||
  return (t / 86400.0L + 2440587.5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Local Sidereal Time
 | 
			
		||||
// Provides local sidereal time in degrees, requires longitude in degrees
 | 
			
		||||
// and time in fractional Julian days since Jan 1, 2000, 1200UTC (e.g. the
 | 
			
		||||
// Julian date - 2451545).
 | 
			
		||||
// cf. USNO Astronomical Almanac and
 | 
			
		||||
// https://astronomy.stackexchange.com/questions/24859/local-sidereal-time
 | 
			
		||||
double localSiderealTime(double offsetDays, double longitude) {
 | 
			
		||||
  double lSideTime = (15.0L * (6.697374558L + 0.06570982441908L * offsetDays +
 | 
			
		||||
			       remainder(offsetDays, 1) * 24 + 12 +
 | 
			
		||||
			       0.000026 * (offsetDays / 36525) * (offsetDays / 36525))
 | 
			
		||||
		      + longitude) / 360;
 | 
			
		||||
  lSideTime -= floor(lSideTime);
 | 
			
		||||
  lSideTime *= 360;			  // Convert to degrees.
 | 
			
		||||
  return(lSideTime);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										42
									
								
								movement/lib/moonrise/moonrise.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								movement/lib/moonrise/moonrise.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
#ifndef MoonRise_h
 | 
			
		||||
#define MoonRise_h
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
// Size of event search window in hours.
 | 
			
		||||
// Events further away from the search time than MR_WINDOW/2 will not be
 | 
			
		||||
// found.  At higher latitudes the moon rise/set intervals become larger, so if
 | 
			
		||||
// you want to find the nearest events this will need to increase.  Larger
 | 
			
		||||
// windows will increase interpolation error.  Useful values are probably from
 | 
			
		||||
// 12 - 48 but will depend upon your application.
 | 
			
		||||
 | 
			
		||||
#define MR_WINDOW   72	    // Even integer
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  double RA;		    // Right ascension
 | 
			
		||||
  double declination;	// Declination
 | 
			
		||||
  double distance;	    // Distance
 | 
			
		||||
} skyCoordinates;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    time_t queryTime;
 | 
			
		||||
    time_t riseTime;
 | 
			
		||||
    time_t setTime;
 | 
			
		||||
    float riseAz;
 | 
			
		||||
    float setAz;
 | 
			
		||||
    bool hasRise;
 | 
			
		||||
    bool hasSet;
 | 
			
		||||
    bool isVisible;
 | 
			
		||||
} MoonRise;
 | 
			
		||||
 | 
			
		||||
MoonRise MoonRise_calculate(double latitude, double longitude, time_t t);
 | 
			
		||||
 | 
			
		||||
//  private:
 | 
			
		||||
void testMoonRiseSet(MoonRise *self, int i, double offsetDays, double latitude, double longitude,
 | 
			
		||||
		 skyCoordinates *mp);
 | 
			
		||||
skyCoordinates moon(double dayOffset);
 | 
			
		||||
double interpolate(double f0, double f1, double f2, double p);
 | 
			
		||||
double julianDate(time_t t);
 | 
			
		||||
double localSiderealTime(double offsetDays, double longitude);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@ -25,6 +25,7 @@ INCLUDES += \
 | 
			
		||||
  -I../lib/astrolib/ \
 | 
			
		||||
  -I../lib/morsecalc/ \
 | 
			
		||||
  -I../lib/smallchesslib/ \
 | 
			
		||||
  -I../lib/moonrise/ \
 | 
			
		||||
 | 
			
		||||
# If you add any other source files you wish to compile, add them after ../app.c
 | 
			
		||||
# Note that you will need to add a backslash at the end of any line you wish to continue, i.e.
 | 
			
		||||
@ -40,6 +41,7 @@ SRCS += \
 | 
			
		||||
  ../lib/TOTP/TOTP.c \
 | 
			
		||||
  ../lib/base32/base32.c \
 | 
			
		||||
  ../lib/sunriset/sunriset.c \
 | 
			
		||||
  ../lib/moonrise/moonrise.c \
 | 
			
		||||
  ../lib/vsop87/vsop87a_milli.c \
 | 
			
		||||
  ../lib/astrolib/astrolib.c \
 | 
			
		||||
  ../lib/morsecalc/calc.c \
 | 
			
		||||
@ -149,6 +151,7 @@ SRCS += \
 | 
			
		||||
  ../watch_faces/sensor/accel_interrupt_count_face.c \
 | 
			
		||||
  ../watch_faces/complication/metronome_face.c \
 | 
			
		||||
  ../watch_faces/complication/smallchess_face.c \
 | 
			
		||||
  ../watch_faces/complication/moonrise_face.c \
 | 
			
		||||
# New watch faces go above this line.
 | 
			
		||||
 | 
			
		||||
# Leave this line at the bottom of the file; it has all the targets for making your project.
 | 
			
		||||
 | 
			
		||||
@ -123,6 +123,7 @@
 | 
			
		||||
#include "accel_interrupt_count_face.h"
 | 
			
		||||
#include "metronome_face.h"
 | 
			
		||||
#include "smallchess_face.h"
 | 
			
		||||
#include "moonrise_face.h"
 | 
			
		||||
// New includes go above this line.
 | 
			
		||||
 | 
			
		||||
#endif // MOVEMENT_FACES_H_
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										438
									
								
								movement/watch_faces/complication/moonrise_face.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										438
									
								
								movement/watch_faces/complication/moonrise_face.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,438 @@
 | 
			
		||||
/*
 | 
			
		||||
 * MIT License
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2025 hueso
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
 * copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
 * SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include "moonrise_face.h"
 | 
			
		||||
#include "sunrise_sunset_face.h"
 | 
			
		||||
#include "watch.h"
 | 
			
		||||
#include "watch_utility.h"
 | 
			
		||||
#include "moonrise.h"
 | 
			
		||||
 | 
			
		||||
#if __EMSCRIPTEN__
 | 
			
		||||
#include <emscripten.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static const uint8_t _location_count = sizeof(longLatPresets) / sizeof(long_lat_presets_t);
 | 
			
		||||
 | 
			
		||||
static void _moonrise_set_expiration(moonrise_state_t *state, watch_date_time next_rise_set) {
 | 
			
		||||
    uint32_t timestamp = watch_utility_date_time_to_unix_time(next_rise_set, 0);
 | 
			
		||||
    state->rise_set_expires = watch_utility_date_time_from_unix_time(timestamp + 60, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _moonrise_face_update(movement_settings_t *settings, moonrise_state_t *state) {
 | 
			
		||||
    char buf[14];
 | 
			
		||||
    bool show_next_match = false;
 | 
			
		||||
    movement_location_t movement_location;
 | 
			
		||||
    struct tm rise, set;
 | 
			
		||||
 | 
			
		||||
    if (state->longLatToUse == 0 || _location_count <= 1)
 | 
			
		||||
        movement_location = (movement_location_t) watch_get_backup_data(1);
 | 
			
		||||
    else{
 | 
			
		||||
        movement_location.bit.latitude = longLatPresets[state->longLatToUse].latitude;
 | 
			
		||||
        movement_location.bit.longitude = longLatPresets[state->longLatToUse].longitude;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (movement_location.reg == 0) {
 | 
			
		||||
        watch_display_string("MR  no Loc", 0);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
 | 
			
		||||
    watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC
 | 
			
		||||
    watch_date_time scratch_time; // scratchpad, contains different values at different times
 | 
			
		||||
    scratch_time.reg = utc_now.reg;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Weird quirky unsigned things were happening when I tried to cast these directly to doubles below.
 | 
			
		||||
    // it looks redundant, but extracting them to local int16's seemed to fix it.
 | 
			
		||||
    int16_t lat_centi = (int16_t)movement_location.bit.latitude;
 | 
			
		||||
    int16_t lon_centi = (int16_t)movement_location.bit.longitude;
 | 
			
		||||
 | 
			
		||||
    double lat = (double)lat_centi / 100.0;
 | 
			
		||||
    double lon = (double)lon_centi / 100.0;
 | 
			
		||||
 | 
			
		||||
    for(int i = 0; i < 2; i++) {
 | 
			
		||||
        uint8_t result = 0;
 | 
			
		||||
        struct tm time_tm = {
 | 
			
		||||
            .tm_year = scratch_time.unit.year - 1900 + WATCH_RTC_REFERENCE_YEAR,
 | 
			
		||||
            .tm_mon = scratch_time.unit.month - 1,     // June
 | 
			
		||||
            .tm_mday = scratch_time.unit.day,
 | 
			
		||||
            .tm_hour = scratch_time.unit.hour,
 | 
			
		||||
            .tm_min = scratch_time.unit.minute,
 | 
			
		||||
            .tm_sec = 0
 | 
			
		||||
        };
 | 
			
		||||
        time_t t = timegm(&time_tm);
 | 
			
		||||
 | 
			
		||||
        MoonRise mr = MoonRise_calculate(lat, lon, t);
 | 
			
		||||
 | 
			
		||||
        localtime_r(&mr.riseTime, &rise);
 | 
			
		||||
        localtime_r(&mr.setTime, &set);
 | 
			
		||||
 | 
			
		||||
        if (!mr.hasRise && !mr.hasSet) {
 | 
			
		||||
            watch_clear_colon();
 | 
			
		||||
            watch_clear_indicator(WATCH_INDICATOR_PM);
 | 
			
		||||
            watch_clear_indicator(WATCH_INDICATOR_24H);
 | 
			
		||||
            sprintf(buf, "%s%2d none ", (result == 1) ? "M_" : "M^", scratch_time.unit.day);
 | 
			
		||||
            watch_display_string(buf, 0);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        watch_set_colon();
 | 
			
		||||
        if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) 
 | 
			
		||||
            watch_set_indicator(WATCH_INDICATOR_24H);
 | 
			
		||||
 | 
			
		||||
        scratch_time.unit.hour = rise.tm_hour;
 | 
			
		||||
        scratch_time.unit.minute = rise.tm_min;
 | 
			
		||||
 | 
			
		||||
        if (date_time.reg < scratch_time.reg) _moonrise_set_expiration(state, scratch_time);
 | 
			
		||||
 | 
			
		||||
        if (date_time.reg < scratch_time.reg || show_next_match) {
 | 
			
		||||
            if (state->rise_index == 0 || show_next_match) {
 | 
			
		||||
                bool set_leading_zero = false;
 | 
			
		||||
                if (!settings->bit.clock_mode_24h) {
 | 
			
		||||
                    if (watch_utility_convert_to_12_hour(&scratch_time)) watch_set_indicator(WATCH_INDICATOR_PM);
 | 
			
		||||
                    else watch_clear_indicator(WATCH_INDICATOR_PM);
 | 
			
		||||
                } else if (settings->bit.clock_24h_leading_zero && scratch_time.unit.hour < 10) {
 | 
			
		||||
                    set_leading_zero = true;
 | 
			
		||||
                }
 | 
			
		||||
                sprintf(buf, "M %2d%2d%02d%s", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute,longLatPresets[state->longLatToUse].name);
 | 
			
		||||
                watch_display_string(buf, 0);
 | 
			
		||||
                watch_set_pixel(0,11);
 | 
			
		||||
                if (set_leading_zero)
 | 
			
		||||
                    watch_display_string("0", 4);
 | 
			
		||||
                return;
 | 
			
		||||
            } else {
 | 
			
		||||
                show_next_match = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        scratch_time.unit.hour = set.tm_hour;
 | 
			
		||||
        scratch_time.unit.minute = set.tm_min;
 | 
			
		||||
 | 
			
		||||
        if (date_time.reg < scratch_time.reg) _moonrise_set_expiration(state, scratch_time);
 | 
			
		||||
 | 
			
		||||
        if (date_time.reg < scratch_time.reg || show_next_match) {
 | 
			
		||||
            if (state->rise_index == 0 || show_next_match) {
 | 
			
		||||
                bool set_leading_zero = false;
 | 
			
		||||
                if (!settings->bit.clock_mode_24h) {
 | 
			
		||||
                    if (watch_utility_convert_to_12_hour(&scratch_time)) watch_set_indicator(WATCH_INDICATOR_PM);
 | 
			
		||||
                    else watch_clear_indicator(WATCH_INDICATOR_PM);
 | 
			
		||||
                } else if (settings->bit.clock_24h_leading_zero && scratch_time.unit.hour < 10) {
 | 
			
		||||
                    set_leading_zero = true;
 | 
			
		||||
                }
 | 
			
		||||
                sprintf(buf, "M %2d%2d%02d%s", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute, longLatPresets[state->longLatToUse].name);
 | 
			
		||||
                watch_display_string(buf, 0);
 | 
			
		||||
                watch_set_pixel(2,11);
 | 
			
		||||
                if (set_leading_zero)
 | 
			
		||||
                    watch_display_string("0", 4);
 | 
			
		||||
                return;
 | 
			
		||||
            } else {
 | 
			
		||||
                show_next_match = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // it's after sunset. we need to display sunrise/sunset for tomorrow.
 | 
			
		||||
        uint32_t timestamp = watch_utility_date_time_to_unix_time(utc_now, 0);
 | 
			
		||||
        timestamp += 86400;
 | 
			
		||||
        scratch_time = watch_utility_date_time_from_unix_time(timestamp, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int16_t _moonrise_face_latlon_from_struct(moonrise_lat_lon_settings_t val) {
 | 
			
		||||
    int16_t retval = (val.sign ? -1 : 1) *
 | 
			
		||||
                        (
 | 
			
		||||
                            val.hundreds * 10000 +
 | 
			
		||||
                            val.tens * 1000 +
 | 
			
		||||
                            val.ones * 100 +
 | 
			
		||||
                            val.tenths * 10 +
 | 
			
		||||
                            val.hundredths
 | 
			
		||||
                        );
 | 
			
		||||
    return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static moonrise_lat_lon_settings_t _moonrise_face_struct_from_latlon(int16_t val) {
 | 
			
		||||
    moonrise_lat_lon_settings_t retval;
 | 
			
		||||
 | 
			
		||||
    retval.sign = val < 0;
 | 
			
		||||
    val = abs(val);
 | 
			
		||||
    retval.hundredths = val % 10;
 | 
			
		||||
    val /= 10;
 | 
			
		||||
    retval.tenths = val % 10;
 | 
			
		||||
    val /= 10;
 | 
			
		||||
    retval.ones = val % 10;
 | 
			
		||||
    val /= 10;
 | 
			
		||||
    retval.tens = val % 10;
 | 
			
		||||
    val /= 10;
 | 
			
		||||
    retval.hundreds = val % 10;
 | 
			
		||||
 | 
			
		||||
    return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _moonrise_face_update_location_register(moonrise_state_t *state) {
 | 
			
		||||
    if (state->location_changed) {
 | 
			
		||||
        movement_location_t movement_location;
 | 
			
		||||
        int16_t lat = _moonrise_face_latlon_from_struct(state->working_latitude);
 | 
			
		||||
        int16_t lon = _moonrise_face_latlon_from_struct(state->working_longitude);
 | 
			
		||||
        movement_location.bit.latitude = lat;
 | 
			
		||||
        movement_location.bit.longitude = lon;
 | 
			
		||||
        watch_store_backup_data(movement_location.reg, 1);
 | 
			
		||||
        state->location_changed = false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _moonrise_face_update_settings_display(movement_event_t event, moonrise_state_t *state) {
 | 
			
		||||
    char buf[12];
 | 
			
		||||
 | 
			
		||||
    switch (state->page) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            return;
 | 
			
		||||
        case 1:
 | 
			
		||||
            sprintf(buf, "LA  %c %04d", state->working_latitude.sign ? '-' : '+', abs(_moonrise_face_latlon_from_struct(state->working_latitude)));
 | 
			
		||||
            break;
 | 
			
		||||
        case 2:
 | 
			
		||||
            sprintf(buf, "LO  %c%05d", state->working_longitude.sign ? '-' : '+', abs(_moonrise_face_latlon_from_struct(state->working_longitude)));
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    if (event.subsecond % 2) {
 | 
			
		||||
        buf[state->active_digit + 4] = ' ';
 | 
			
		||||
    }
 | 
			
		||||
    watch_display_string(buf, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _moonrise_face_advance_digit(moonrise_state_t *state) {
 | 
			
		||||
    state->location_changed = true;
 | 
			
		||||
    switch (state->page) {
 | 
			
		||||
        case 1: // latitude
 | 
			
		||||
            switch (state->active_digit) {
 | 
			
		||||
                case 0:
 | 
			
		||||
                    state->working_latitude.sign++;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 1:
 | 
			
		||||
                    // we skip this digit
 | 
			
		||||
                    break;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    state->working_latitude.tens = (state->working_latitude.tens + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_latitude)) > 9000) {
 | 
			
		||||
                        // prevent latitude from going over ±90.
 | 
			
		||||
                        // TODO: perform these checks when advancing the digit?
 | 
			
		||||
                        state->working_latitude.ones = 0;
 | 
			
		||||
                        state->working_latitude.tenths = 0;
 | 
			
		||||
                        state->working_latitude.hundredths = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case 3:
 | 
			
		||||
                    state->working_latitude.ones = (state->working_latitude.ones + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_latitude)) > 9000) state->working_latitude.ones = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 4:
 | 
			
		||||
                    state->working_latitude.tenths = (state->working_latitude.tenths + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_latitude)) > 9000) state->working_latitude.tenths = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 5:
 | 
			
		||||
                    state->working_latitude.hundredths = (state->working_latitude.hundredths + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_latitude)) > 9000) state->working_latitude.hundredths = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 2: // longitude
 | 
			
		||||
            switch (state->active_digit) {
 | 
			
		||||
                case 0:
 | 
			
		||||
                    state->working_longitude.sign++;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 1:
 | 
			
		||||
                    state->working_longitude.hundreds = (state->working_longitude.hundreds + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) {
 | 
			
		||||
                        // prevent longitude from going over ±180
 | 
			
		||||
                        state->working_longitude.tens = 8;
 | 
			
		||||
                        state->working_longitude.ones = 0;
 | 
			
		||||
                        state->working_longitude.tenths = 0;
 | 
			
		||||
                        state->working_longitude.hundredths = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    state->working_longitude.tens = (state->working_longitude.tens + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) state->working_longitude.tens = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 3:
 | 
			
		||||
                    state->working_longitude.ones = (state->working_longitude.ones + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) state->working_longitude.ones = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 4:
 | 
			
		||||
                    state->working_longitude.tenths = (state->working_longitude.tenths + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) state->working_longitude.tenths = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 5:
 | 
			
		||||
                    state->working_longitude.hundredths = (state->working_longitude.hundredths + 1) % 10;
 | 
			
		||||
                    if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) state->working_longitude.hundredths = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void moonrise_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
 | 
			
		||||
    (void) settings;
 | 
			
		||||
    (void) watch_face_index;
 | 
			
		||||
    if (*context_ptr == NULL) {
 | 
			
		||||
        *context_ptr = malloc(sizeof(moonrise_state_t));
 | 
			
		||||
        memset(*context_ptr, 0, sizeof(moonrise_state_t));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void moonrise_face_activate(movement_settings_t *settings, void *context) {
 | 
			
		||||
    (void) settings;
 | 
			
		||||
    if (watch_tick_animation_is_running()) watch_stop_tick_animation();
 | 
			
		||||
 | 
			
		||||
#if __EMSCRIPTEN__
 | 
			
		||||
    int16_t browser_lat = EM_ASM_INT({
 | 
			
		||||
        return lat;
 | 
			
		||||
    });
 | 
			
		||||
    int16_t browser_lon = EM_ASM_INT({
 | 
			
		||||
        return lon;
 | 
			
		||||
    });
 | 
			
		||||
    if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
 | 
			
		||||
        movement_location_t browser_loc;
 | 
			
		||||
        browser_loc.bit.latitude = browser_lat;
 | 
			
		||||
        browser_loc.bit.longitude = browser_lon;
 | 
			
		||||
        watch_store_backup_data(browser_loc.reg, 1);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    moonrise_state_t *state = (moonrise_state_t *)context;
 | 
			
		||||
    movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
 | 
			
		||||
    state->working_latitude = _moonrise_face_struct_from_latlon(movement_location.bit.latitude);
 | 
			
		||||
    state->working_longitude = _moonrise_face_struct_from_latlon(movement_location.bit.longitude);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool moonrise_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
 | 
			
		||||
    moonrise_state_t *state = (moonrise_state_t *)context;
 | 
			
		||||
 | 
			
		||||
    switch (event.event_type) {
 | 
			
		||||
        case EVENT_ACTIVATE:
 | 
			
		||||
            _moonrise_face_update(settings, state);
 | 
			
		||||
            break;
 | 
			
		||||
        case EVENT_LOW_ENERGY_UPDATE:
 | 
			
		||||
        case EVENT_TICK:
 | 
			
		||||
            if (state->page == 0) {
 | 
			
		||||
                // if entering low energy mode, start tick animation
 | 
			
		||||
                if (event.event_type == EVENT_LOW_ENERGY_UPDATE && !watch_tick_animation_is_running()) watch_start_tick_animation(1000);
 | 
			
		||||
                // check if we need to update the display
 | 
			
		||||
                watch_date_time date_time = watch_rtc_get_date_time();
 | 
			
		||||
                if (date_time.reg >= state->rise_set_expires.reg) {
 | 
			
		||||
                    // and on the off chance that this happened before EVENT_TIMEOUT snapped us back to rise/set 0, go back now
 | 
			
		||||
                    state->rise_index = 0;
 | 
			
		||||
                    _moonrise_face_update(settings, state);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                _moonrise_face_update_settings_display(event, state);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case EVENT_LIGHT_BUTTON_DOWN:
 | 
			
		||||
            if (state->page) {
 | 
			
		||||
                state->active_digit++;
 | 
			
		||||
                if (state->page == 1 && state->active_digit == 1) state->active_digit++; // max latitude is +- 90, no hundreds place
 | 
			
		||||
                if (state->active_digit > 5) {
 | 
			
		||||
                    state->active_digit = 0;
 | 
			
		||||
                    state->page = (state->page + 1) % 3;
 | 
			
		||||
                    _moonrise_face_update_location_register(state);
 | 
			
		||||
                }
 | 
			
		||||
                _moonrise_face_update_settings_display(event, context);
 | 
			
		||||
            } else if (_location_count <= 1) {
 | 
			
		||||
                movement_illuminate_led();
 | 
			
		||||
            }
 | 
			
		||||
            if (state->page == 0) {
 | 
			
		||||
                movement_request_tick_frequency(1);
 | 
			
		||||
                _moonrise_face_update(settings, state);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case EVENT_LIGHT_LONG_PRESS:
 | 
			
		||||
            if (_location_count <= 1) break;
 | 
			
		||||
            else if (!state->page) movement_illuminate_led();
 | 
			
		||||
            break;
 | 
			
		||||
        case EVENT_LIGHT_BUTTON_UP:
 | 
			
		||||
            if (state->page == 0 && _location_count > 1) {
 | 
			
		||||
                state->longLatToUse = (state->longLatToUse + 1) % _location_count;
 | 
			
		||||
                _moonrise_face_update(settings, state);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case EVENT_ALARM_BUTTON_UP:
 | 
			
		||||
            if (state->page) {
 | 
			
		||||
                _moonrise_face_advance_digit(state);
 | 
			
		||||
                _moonrise_face_update_settings_display(event, context);
 | 
			
		||||
            } else {
 | 
			
		||||
                state->rise_index = (state->rise_index + 1) % 2;
 | 
			
		||||
                _moonrise_face_update(settings, state);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case EVENT_ALARM_LONG_PRESS:
 | 
			
		||||
            if (state->page == 0) {
 | 
			
		||||
            if (state->longLatToUse != 0) {
 | 
			
		||||
                state->longLatToUse = 0;
 | 
			
		||||
                _moonrise_face_update(settings, state);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
                state->page++;
 | 
			
		||||
                state->active_digit = 0;
 | 
			
		||||
                watch_clear_display();
 | 
			
		||||
                movement_request_tick_frequency(4);
 | 
			
		||||
                _moonrise_face_update_settings_display(event, context);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                state->active_digit = 0;
 | 
			
		||||
                state->page = 0;
 | 
			
		||||
                _moonrise_face_update_location_register(state);
 | 
			
		||||
                _moonrise_face_update(settings, state);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case EVENT_TIMEOUT:
 | 
			
		||||
            if (watch_get_backup_data(1) == 0) {
 | 
			
		||||
                // if no location set, return home
 | 
			
		||||
                movement_move_to_face(0);
 | 
			
		||||
            } else if (state->page || state->rise_index) {
 | 
			
		||||
                // otherwise on timeout, exit settings mode and return to the next sunrise or sunset
 | 
			
		||||
                state->page = 0;
 | 
			
		||||
                state->rise_index = 0;
 | 
			
		||||
                movement_request_tick_frequency(1);
 | 
			
		||||
                _moonrise_face_update(settings, state);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            return movement_default_loop_handler(event, settings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void moonrise_face_resign(movement_settings_t *settings, void *context) {
 | 
			
		||||
    (void) settings;
 | 
			
		||||
    moonrise_state_t *state = (moonrise_state_t *)context;
 | 
			
		||||
    state->page = 0;
 | 
			
		||||
    state->active_digit = 0;
 | 
			
		||||
    state->rise_index = 0;
 | 
			
		||||
    _moonrise_face_update_location_register(state);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								movement/watch_faces/complication/moonrise_face.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								movement/watch_faces/complication/moonrise_face.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
			
		||||
/*
 | 
			
		||||
 * MIT License
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2022 Joey Castillo
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
 * copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
 * SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef MOONRISE_FACE_H_
 | 
			
		||||
#define MOONRISE_FACE_H_
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * SUNRISE & SUNSET FACE
 | 
			
		||||
 *
 | 
			
		||||
 * The Sunrise/Sunset face is designed to display the next sunrise or sunset
 | 
			
		||||
 * for a given location. It also functions as an interface for setting the
 | 
			
		||||
 * location register, which other watch faces can use for various purposes.
 | 
			
		||||
 *
 | 
			
		||||
 * Refer to the wiki for usage instructions:
 | 
			
		||||
 *  https://www.sensorwatch.net/docs/watchfaces/complication/#sunrisesunset
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "movement.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint8_t sign: 1;    // 0-1
 | 
			
		||||
    uint8_t hundreds: 1;    // 0-1, ignored for latitude
 | 
			
		||||
    uint8_t tens: 4;        // 0-9 (must wrap at 10)
 | 
			
		||||
    uint8_t ones: 4;        // 0-9 (must wrap at 10)
 | 
			
		||||
    uint8_t tenths: 4;      // 0-9 (must wrap at 10)
 | 
			
		||||
    uint8_t hundredths: 4;  // 0-9 (must wrap at 10)
 | 
			
		||||
} moonrise_lat_lon_settings_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint8_t page;
 | 
			
		||||
    uint8_t rise_index;
 | 
			
		||||
    uint8_t active_digit;
 | 
			
		||||
    bool location_changed;
 | 
			
		||||
    watch_date_time rise_set_expires;
 | 
			
		||||
    moonrise_lat_lon_settings_t working_latitude;
 | 
			
		||||
    moonrise_lat_lon_settings_t working_longitude;
 | 
			
		||||
    uint8_t longLatToUse;
 | 
			
		||||
} moonrise_state_t;
 | 
			
		||||
 | 
			
		||||
void moonrise_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
 | 
			
		||||
void moonrise_face_activate(movement_settings_t *settings, void *context);
 | 
			
		||||
bool moonrise_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
 | 
			
		||||
void moonrise_face_resign(movement_settings_t *settings, void *context);
 | 
			
		||||
 | 
			
		||||
#define moonrise_face ((const watch_face_t){ \
 | 
			
		||||
    moonrise_face_setup, \
 | 
			
		||||
    moonrise_face_activate, \
 | 
			
		||||
    moonrise_face_loop, \
 | 
			
		||||
    moonrise_face_resign, \
 | 
			
		||||
    NULL, \
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
typedef struct {
 | 
			
		||||
    char name[2];
 | 
			
		||||
    int16_t latitude;
 | 
			
		||||
    int16_t longitude;
 | 
			
		||||
} long_lat_presets_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const long_lat_presets_t longLatPresets[] =
 | 
			
		||||
{
 | 
			
		||||
    { .name = "  "},  // Default, the long and lat get replaced by what's set in the watch
 | 
			
		||||
//    { .name = "Ny", .latitude = 4072, .longitude = -7401 },  // New York City, NY
 | 
			
		||||
//    { .name = "LA", .latitude = 3405, .longitude = -11824 },  // Los Angeles, CA
 | 
			
		||||
//    { .name = "dE", .latitude = 4221, .longitude = -8305 },  // Detroit, MI
 | 
			
		||||
};
 | 
			
		||||
*/
 | 
			
		||||
#endif // MOONRISE_FACE_H_
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user