Skip to content

Commit

Permalink
Add new is_day function with tests; final cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
wapcaplet committed Apr 29, 2020
1 parent 7aca668 commit d4bccca
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 66 deletions.
13 changes: 10 additions & 3 deletions src/calendar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ moon_phase get_moon_phase( const time_point &p )
}

// TODO: Refactor sunrise / sunset
// The only difference between them is the start_hours array
time_point sunrise( const time_point &p )
{
static_assert( static_cast<int>( SPRING ) == 0,
Expand All @@ -95,7 +96,6 @@ time_point sunrise( const time_point &p )
return midnight + time_duration::from_minutes( static_cast<int>( time * 60 ) );
}

// TODO: Refactor sunrise / sunset
time_point sunset( const time_point &p )
{
static_assert( static_cast<int>( SPRING ) == 0,
Expand Down Expand Up @@ -127,8 +127,6 @@ time_point daylight_time( const time_point &p )
return sunrise( p ) + 15_minutes;
}

// TODO: bool is_day( const time_point &p ) ?

bool is_night( const time_point &p )
{
const time_duration now = time_past_midnight( p );
Expand All @@ -138,6 +136,15 @@ bool is_night( const time_point &p )
return now >= sunset + twilight_duration || now <= sunrise;
}

bool is_day( const time_point &p )
{
const time_duration now = time_past_midnight( p );
const time_duration sunrise = time_past_midnight( ::sunrise( p ) );
const time_duration sunset = time_past_midnight( ::sunset( p ) );

return now >= sunrise + twilight_duration && now <= sunset;
}

bool is_dusk( const time_point &p )
{
const time_duration now = time_past_midnight( p );
Expand Down
2 changes: 2 additions & 0 deletions src/calendar.h
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ time_point daylight_time( const time_point &p );
time_point night_time( const time_point &p );
/** Returns true if it's currently night time - after dusk and before dawn. */
bool is_night( const time_point &p );
/** Returns true if it's currently day time - after dawn and before dusk. */
bool is_day( const time_point &p );
/** Returns true if it's currently dusk - between sunset and and twilight_duration after sunset. */
bool is_dusk( const time_point &p );
/** Returns true if it's currently dawn - between sunrise and twilight_duration after sunrise. */
Expand Down
194 changes: 131 additions & 63 deletions tests/sun_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,51 @@

// SUN TESTS

// is_night, is_dawn, is_dusk
// The 24-hour solar cycle is divided into four parts, as returned by four calendar.cpp functions:
//
// is_night : from the end of dusk, until the following sunrise (start of dawn)
// is_dawn : begins at sunrise, lasts twilight_duration (1 hour)
// is_day : from the end of dawn, until sunset (start of dusk)
// is_dusk : begins at sunset, lasts twilight_duration (1 hour)
//
// These are inclusive at their endpoints; in other words, each overlaps with the next like so:
//
// 00:00 is_night
// : is_night
// 06:00 is_night && is_dawn ( sunrise )
// : is_dawn ( sunrise + twilight )
// 07:00 is_dawn && is_day
// : is_day
// 19:00 is_day && is_dusk ( sunset )
// : is_dusk ( sunset + twilight )
// 20:00 is_dusk && is_night
// : is_night
// 23:59 is_night
//
// The times of sunrise and sunset will naturally depend on the current time of year; this aspect is
// covered by the "sunrise and sunset" and solstice/equinox tests later in this file. Here we simply
// use the first day of spring as a baseline.
//
// This test covers is_night, is_dawn, is_day, is_dusk, and their behavior in relation to time of day.
TEST_CASE( "daily solar cycle", "[sun][night][dawn][day][dusk]" )
{
// Use sunrise/sunset on the first day (spring equinox)
static const time_point midnight = calendar::turn_zero;
static const time_point noon = calendar::turn_zero + 12_hours;
static const time_point today_sunrise = sunrise( midnight );
static const time_point today_sunset = sunset( midnight );

REQUIRE( "Year 1, Spring, day 1 6:00:00 AM" == to_string( today_sunrise ) );
REQUIRE( "Year 1, Spring, day 1 7:00:00 PM" == to_string( today_sunset ) );

// 00:00 is_night
// : is_night
// 06:00 is_night && is_dawn (sunrise time)
// : is_dawn (sunrise + twilight )
// 07:00 is_dawn && is_day
// : is_day
// 19:00 is_day && is_dusk (sunset time)
// : is_dusk (sunset + twilight )
// 20:00 is_dusk && is_night
// : is_night
// 23:59 is_night

// First, at the risk of stating the obvious
CHECK( is_night( midnight ) );
// And while we're at it
CHECK_FALSE( is_day( midnight ) );
CHECK_FALSE( is_dawn( midnight ) );
CHECK_FALSE( is_dusk( midnight ) );

// Yep, still dark
CHECK( is_night( midnight + 1_seconds ) );
CHECK( is_night( midnight + 2_hours ) );
CHECK( is_night( midnight + 3_hours ) );
Expand All @@ -38,24 +59,56 @@ TEST_CASE( "daily solar cycle", "[sun][night][dawn][day][dusk]" )
CHECK( is_dawn( today_sunrise ) );

// Dawn
CHECK_FALSE( is_night( today_sunrise + 1_seconds ) );
CHECK( is_dawn( today_sunrise + 1_seconds ) );
CHECK( is_dawn( today_sunrise + 30_minutes ) );
CHECK( is_dawn( today_sunrise + 59_minutes ) );
CHECK( is_dawn( today_sunrise + 1_hours - 1_seconds ) );
CHECK_FALSE( is_day( today_sunrise + 1_hours - 1_seconds ) );

// The endpoint of dawn is both "dawn" and "day"
CHECK( is_dawn( today_sunrise + 1_hours ) );
//CHECK( is_day( today_sunrise + 1_hours ) );

// FIXME: No function to tell when it is daytime
CHECK( is_day( today_sunrise + 1_hours ) );

// Breakfast
CHECK_FALSE( is_dawn( today_sunrise + 1_hours + 1_seconds ) );
CHECK( is_day( today_sunrise + 1_hours + 1_seconds ) );
CHECK( is_day( today_sunrise + 2_hours ) );
// Second breakfast
CHECK( is_day( today_sunrise + 3_hours ) );
CHECK( is_day( today_sunrise + 4_hours ) );
// Luncheon
CHECK( is_day( noon - 3_hours ) );
CHECK( is_day( noon - 2_hours ) );
// Elevenses
CHECK( is_day( noon - 1_hours ) );

// Noon
CHECK( is_day( noon ) );
CHECK_FALSE( is_dawn( noon ) );
CHECK_FALSE( is_dusk( noon ) );
CHECK_FALSE( is_night( noon ) );

// Afternoon tea
CHECK( is_day( noon + 1_hours ) );
CHECK( is_day( noon + 2_hours ) );
// Dinner
CHECK( is_day( noon + 3_hours ) );
CHECK( is_day( today_sunset - 2_hours ) );
// Supper
CHECK( is_day( today_sunset - 1_hours ) );
CHECK( is_day( today_sunset - 1_seconds ) );
CHECK_FALSE( is_dusk( today_sunset - 1_seconds ) );

// The beginning of sunset is both "day" and "dusk"
//CHECK( is_day( today_sunset ) );
CHECK( is_day( today_sunset ) );
CHECK( is_dusk( today_sunset ) );

// Dusk
CHECK_FALSE( is_day( today_sunset + 1_seconds ) );
CHECK( is_dusk( today_sunset + 1_seconds ) );
CHECK( is_dusk( today_sunset + 30_minutes ) );
CHECK( is_dusk( today_sunset + 59_minutes ) );
CHECK( is_dusk( today_sunset + 1_hours - 1_seconds ) );
CHECK_FALSE( is_night( today_sunset + 1_hours - 1_seconds ) );

// The point when dusk ends is both "dusk" and "night"
CHECK( is_dusk( today_sunset + 1_hours ) );
Expand All @@ -68,22 +121,6 @@ TEST_CASE( "daily solar cycle", "[sun][night][dawn][day][dusk]" )
CHECK( is_night( today_sunset + 4_hours ) );
}

// current_daylight_level returns seasonally-adjusted maximum daylight level
TEST_CASE( "current daylight level", "[sun][daylight]" )
{
static const time_duration one_season = calendar::season_length();
static const time_point spring = calendar::turn_zero;
static const time_point summer = spring + one_season;
static const time_point autumn = summer + one_season;
static const time_point winter = autumn + one_season;

// For ~Boston: solstices are +/- 25% sunlight intensity from equinoxes
CHECK( 100.0f == current_daylight_level( spring ) );
CHECK( 125.0f == current_daylight_level( summer ) );
CHECK( 100.0f == current_daylight_level( autumn ) );
CHECK( 75.0f == current_daylight_level( winter ) );
}

// The calendar `sunlight` function returns light level for both sun and moon.
TEST_CASE( "sunlight and moonlight", "[sun][sunlight][moonlight]" )
{
Expand All @@ -92,43 +129,43 @@ TEST_CASE( "sunlight and moonlight", "[sun][sunlight][moonlight]" )
static const time_point today_sunrise = sunrise( midnight );
static const time_point today_sunset = sunset( midnight );

// Tests assume 100.0f maximum
REQUIRE( default_daylight_level() == 100.0f );
// Expected numbers below assume 100.0f maximum daylight level
// (maximum daylight is different at other times of year - see [daylight] tests)
REQUIRE( 100.0f == default_daylight_level() );

SECTION( "sunlight" ) {
// Before dawn
CHECK( sunlight( midnight ) == 1.0f );
CHECK( sunlight( today_sunrise ) == 1.0f );
CHECK( 1.0f == sunlight( midnight ) );
CHECK( 1.0f == sunlight( today_sunrise ) );
// Dawn
CHECK( sunlight( today_sunrise + 1_seconds ) == Approx( 1.0275f ) );
CHECK( sunlight( today_sunrise + 1_minutes ) == Approx( 2.65f ) );
CHECK( sunlight( today_sunrise + 15_minutes ) == 25.75f );
CHECK( sunlight( today_sunrise + 15_minutes ) == 25.75f );
CHECK( sunlight( today_sunrise + 30_minutes ) == 50.50f );
CHECK( sunlight( today_sunrise + 45_minutes ) == 75.25f );
CHECK( 1.0275f == sunlight( today_sunrise + 1_seconds ) );
CHECK( 2.65f == sunlight( today_sunrise + 1_minutes ) );
CHECK( 25.75f == sunlight( today_sunrise + 15_minutes ) );
CHECK( 50.50f == sunlight( today_sunrise + 30_minutes ) );
CHECK( 75.25f == sunlight( today_sunrise + 45_minutes ) );
// 1 second before full daylight
CHECK( sunlight( today_sunrise + 1_hours - 1_seconds ) == Approx( 99.9725f ) );
CHECK( sunlight( today_sunrise + 1_hours ) == 100.0f );
CHECK( 99.9725f == sunlight( today_sunrise + 1_hours - 1_seconds ) );
CHECK( 100.0f == sunlight( today_sunrise + 1_hours ) );
// End of dawn, full light all day
CHECK( sunlight( today_sunrise + 2_hours ) == 100.0f );
CHECK( sunlight( today_sunrise + 3_hours ) == 100.0f );
CHECK( 100.0f == sunlight( today_sunrise + 2_hours ) );
CHECK( 100.0f == sunlight( today_sunrise + 3_hours ) );
// Noon
CHECK( sunlight( midnight + 12_hours ) == 100.0f );
CHECK( sunlight( midnight + 13_hours ) == 100.0f );
CHECK( sunlight( midnight + 14_hours ) == 100.0f );
CHECK( 100.0f == sunlight( midnight + 12_hours ) );
CHECK( 100.0f == sunlight( midnight + 13_hours ) );
CHECK( 100.0f == sunlight( midnight + 14_hours ) );
// Dusk begins
CHECK( sunlight( today_sunset ) == 100.0f );
CHECK( 100.0f == sunlight( today_sunset ) );
// 1 second after dusk begins
CHECK( sunlight( today_sunset + 1_seconds ) == Approx( 99.9725f ) );
CHECK( sunlight( today_sunset + 15_minutes ) == 75.25f );
CHECK( sunlight( today_sunset + 30_minutes ) == 50.50f );
CHECK( sunlight( today_sunset + 45_minutes ) == 25.75f );
CHECK( 99.9725f == sunlight( today_sunset + 1_seconds ) );
CHECK( 75.25f == sunlight( today_sunset + 15_minutes ) );
CHECK( 50.50f == sunlight( today_sunset + 30_minutes ) );
CHECK( 25.75f == sunlight( today_sunset + 45_minutes ) );
// 1 second before full night
CHECK( sunlight( today_sunset + 1_hours - 1_seconds ) == Approx( 1.0275f ) );
CHECK( sunlight( today_sunset + 1_hours ) == 1.0f );
CHECK( 1.0275f == sunlight( today_sunset + 1_hours - 1_seconds ) );
CHECK( 1.0f == sunlight( today_sunset + 1_hours ) );
// After dusk
CHECK( sunlight( today_sunset + 2_hours ) == 1.0f );
CHECK( sunlight( today_sunset + 3_hours ) == 1.0f );
CHECK( 1.0f == sunlight( today_sunset + 2_hours ) );
CHECK( 1.0f == sunlight( today_sunset + 3_hours ) );
}

// This moonlight test is intentionally simple, only checking new moon (minimal light) and full
Expand All @@ -142,19 +179,50 @@ TEST_CASE( "sunlight and moonlight", "[sun][sunlight][moonlight]" )
WHEN( "the moon is new" ) {
REQUIRE( get_moon_phase( new_moon ) == MOON_NEW );
THEN( "moonlight is 1.0" ) {
CHECK( sunlight( new_moon ) == 1.0f );
CHECK( 1.0f == sunlight( new_moon ) );
}
}

WHEN( "the moon is full" ) {
REQUIRE( get_moon_phase( full_moon ) == MOON_FULL );
THEN( "moonlight is 10.0" ) {
CHECK( sunlight( full_moon ) == 10.0f );
CHECK( 10.0f == sunlight( full_moon ) );
}
}
}
}

// current_daylight_level returns seasonally-adjusted maximum daylight level
TEST_CASE( "current daylight level", "[sun][daylight][equinox][solstice]" )
{
static const time_duration one_season = calendar::season_length();
static const time_point spring = calendar::turn_zero;
static const time_point summer = spring + one_season;
static const time_point autumn = summer + one_season;
static const time_point winter = autumn + one_season;

SECTION( "baseline 100 daylight on the spring and autumn equinoxes" ) {
CHECK( 100.0f == current_daylight_level( spring ) );
CHECK( 100.0f == current_daylight_level( autumn ) );
}

SECTION( "25 percent more daylight on the summer solstice" ) {
CHECK( 125.0f == current_daylight_level( summer ) );
}

SECTION( "25 percent less daylight on the winter solstice" ) {
CHECK( 75.0f == current_daylight_level( winter ) );
}

// Many other times of day have peak daylight level, but noon is for sure
SECTION( "noon is peak daylight level" ) {
CHECK( 100.0f == sunlight( spring + 12_hours ) );
CHECK( 125.0f == sunlight( summer + 12_hours ) );
CHECK( 100.0f == sunlight( autumn + 12_hours ) );
CHECK( 75.0f == sunlight( winter + 12_hours ) );
}
}

// The times of sunrise and sunset vary throughout the year. For simplicity, equinoxes occur on the
// first day of spring and autumn, and solstices occur on the first day of summer and winter.
TEST_CASE( "sunrise and sunset", "[sun][sunrise][sunset][equinox][solstice]" )
Expand Down

0 comments on commit d4bccca

Please sign in to comment.