mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[astro] Add Circadian light (#19789)
* Adding solar midnight and Circadian light Signed-off-by: gael@lhopital.org <gael@lhopital.org>
This commit is contained in:
@@ -78,6 +78,11 @@ This binding has its own IconProvider and makes available the following list of
|
||||
- **group** `phase`
|
||||
- **channel**
|
||||
- `name` (String), values: `SUN_RISE, ASTRO_DAWN, NAUTIC_DAWN, CIVIL_DAWN, CIVIL_DUSK, NAUTIC_DUSK, ASTRO_DUSK, SUN_SET, DAYLIGHT, NOON, NIGHT`
|
||||
- **group** `circadian`: provides automatically calculated values that follow a daily circadian rhythm based on the position of the sun.
|
||||
- **channel**
|
||||
- `brightness` (Dimmer): represents a recommended light brightness level as a percentage. It ranges from **0–100%**, where 0% is fully off and 100% is maximum brightness. The value follows the solar cycle, generally increasing towards **solar noon** and decreasing towards **midnight**.
|
||||
- `temperature` (Number:Temperature): represents a recommended color temperature for white light in **Kelvin**, ranging from **2500 K** (warm white) to **5500 K** (cool white). Around solar noon the value tends towards the higher, cooler temperatures, while during the night and around midnight it shifts towards lower, warmer temperatures.
|
||||
|
||||
- **thing** `moon`
|
||||
- **group** `rise, set`
|
||||
- **channel**
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.calc;
|
||||
|
||||
import static org.openhab.binding.astro.internal.model.Circadian.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.model.Circadian;
|
||||
import org.openhab.binding.astro.internal.model.Range;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Calculates the color temperature and brightness depending upon sun positional information
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
* @implNote based on the calculations of
|
||||
* https://github.com/claytonjn/hass-circadian_lighting/blob/ed03e159b9a1db8f08a94b7de8c3b6b73fa0eb92/README.md
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CircadianCalc {
|
||||
private static final long DELTA_TEMP = MAX_COLOR_TEMP - MIN_COLOR_TEMP;
|
||||
private static final long TWELVE_HOURS_MS = 12 * 60 * 60 * 1000;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CircadianCalc.class);
|
||||
|
||||
public static Circadian calculate(Calendar calendar, Range riseRange, Range setRange, @Nullable Range noonRange) {
|
||||
var rise = riseRange.getStart();
|
||||
var set = setRange.getStart();
|
||||
var noon = noonRange != null ? noonRange.getStart() : null;
|
||||
|
||||
// If we have no rise or no set, there's no point calculating a Circadian Cycle
|
||||
if (rise == null || set == null || noon == null) {
|
||||
return Circadian.DEFAULT;
|
||||
}
|
||||
return calculate(calendar, rise, set, noon);
|
||||
}
|
||||
|
||||
public static Circadian calculate(Calendar calendar, Calendar rise, Calendar set, Calendar noon) {
|
||||
// Figure out where we are in time so we know which half of the parabola to calculate.
|
||||
// We're generating a different sunset-sunrise parabola for before and after solar midnight,
|
||||
// because solar midnight might not be exactly halfway between sunrise and sunset.
|
||||
// We're also generating a different parabola for sunrise-sunset.
|
||||
|
||||
var now = calendar.getTimeInMillis();
|
||||
var sunRise = rise.getTimeInMillis();
|
||||
var sunSet = set.getTimeInMillis();
|
||||
|
||||
long h, x;
|
||||
double k;
|
||||
|
||||
if (sunRise < now && now < sunSet) {
|
||||
// Sunrise -> Sunset parabola
|
||||
k = 100.0;
|
||||
h = noon.getTimeInMillis();
|
||||
// parabola before solar_noon else after solar_noon
|
||||
x = now < h ? sunRise : sunSet;
|
||||
} else {
|
||||
k = -100.0;
|
||||
if (now < sunRise) {
|
||||
// Before sunrise we are still in the sunset -> sunrise cycle of the previous day
|
||||
h = noon.getTimeInMillis() - TWELVE_HOURS_MS;
|
||||
x = sunRise;
|
||||
} else {
|
||||
// sunset -> sunrise parabola
|
||||
h = noon.getTimeInMillis() + TWELVE_HOURS_MS;
|
||||
x = sunSet;
|
||||
}
|
||||
}
|
||||
|
||||
double y = 0.0;
|
||||
long dx = h - x;
|
||||
if (dx == 0L) {
|
||||
logger.debug("Degenerate circadian parabola (h == x), returning default values");
|
||||
return Circadian.DEFAULT;
|
||||
}
|
||||
double a = (y - k) / (dx * dx);
|
||||
double percentage = a * Math.pow(now - h, 2) + k;
|
||||
double colorTemp = percentage > 0 ? (DELTA_TEMP * percentage / 100) + MIN_COLOR_TEMP : MIN_COLOR_TEMP;
|
||||
|
||||
logger.debug("Percentage: {}, ColorTemp: {}", percentage, colorTemp);
|
||||
|
||||
return new Circadian(Math.min(100, Math.abs(percentage)), colorTemp);
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -65,7 +65,6 @@ public class SunCalc {
|
||||
private static final double H1 = Math.toRadians(-6.0); // nautical twilight angle
|
||||
private static final double H2 = Math.toRadians(-12.0); // astronomical twilight angle
|
||||
private static final double H3 = Math.toRadians(-18.0); // darkness angle
|
||||
private static final double MINUTES_PER_DAY = 60 * 24;
|
||||
private static final int CURVE_TIME_INTERVAL = 20; // 20 minutes
|
||||
private static final double JD_ONE_MINUTE_FRACTION = 1.0 / 60 / 24;
|
||||
|
||||
@@ -130,9 +129,11 @@ public class SunCalc {
|
||||
* Returns true, if the sun is up all day (no rise and set).
|
||||
*/
|
||||
private boolean isSunUpAllDay(Calendar calendar, double latitude, double longitude, @Nullable Double altitude) {
|
||||
Calendar cal = DateTimeUtils.truncateToMidnight(calendar);
|
||||
Sun sun = new Sun();
|
||||
for (int minutes = 0; minutes <= MINUTES_PER_DAY; minutes += CURVE_TIME_INTERVAL) {
|
||||
Calendar start = DateTimeUtils.truncateToMidnight(calendar);
|
||||
Calendar cal = (Calendar) start.clone();
|
||||
var numberOfSamples = 24 * 60 / CURVE_TIME_INTERVAL;
|
||||
for (int i = 0; i <= numberOfSamples; i++) {
|
||||
setPositionalInfo(cal, latitude, longitude, altitude, sun);
|
||||
if (sun.getPosition().getElevationAsDouble() < SUN_ANGLE) {
|
||||
return false;
|
||||
|
||||
+7
-2
@@ -21,6 +21,7 @@ import java.util.TimeZone;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.astro.internal.calc.CircadianCalc;
|
||||
import org.openhab.binding.astro.internal.calc.SunCalc;
|
||||
import org.openhab.binding.astro.internal.job.DailyJobSun;
|
||||
import org.openhab.binding.astro.internal.job.Job;
|
||||
@@ -66,10 +67,14 @@ public class SunHandler extends AstroThingHandler {
|
||||
Double latitude = thingConfig.latitude;
|
||||
Double longitude = thingConfig.longitude;
|
||||
Double altitude = thingConfig.altitude;
|
||||
sunCalc.setPositionalInfo(Calendar.getInstance(zone, locale), latitude != null ? latitude : 0,
|
||||
longitude != null ? longitude : 0, altitude != null ? altitude : 0, sun);
|
||||
Calendar calendar = Calendar.getInstance(zone, locale);
|
||||
sunCalc.setPositionalInfo(calendar, latitude != null ? latitude : 0, longitude != null ? longitude : 0,
|
||||
altitude != null ? altitude : 0, sun);
|
||||
|
||||
sun.getEclipse().setElevations(this, timeZoneProvider);
|
||||
|
||||
sun.setCircadian(CircadianCalc.calculate(calendar, sun.getRise(), sun.getSet(), sun.getNoon()));
|
||||
|
||||
this.sun = sun;
|
||||
|
||||
publishPlanet();
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
|
||||
/**
|
||||
* Holds the calculated brightness and color temperature.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public record Circadian(int brightness, int temperature) {
|
||||
|
||||
public static final long MIN_COLOR_TEMP = 2500;
|
||||
public static final long MAX_COLOR_TEMP = 5500;
|
||||
|
||||
public static final Circadian DEFAULT = new Circadian(0, MIN_COLOR_TEMP);
|
||||
|
||||
public Circadian {
|
||||
if (brightness < 0 || brightness > 100) {
|
||||
throw new IllegalArgumentException("Brightness level out of range");
|
||||
}
|
||||
}
|
||||
|
||||
public Circadian(double percentage, double colorTemp) {
|
||||
this((int) Math.round(percentage), (int) colorTemp);
|
||||
}
|
||||
|
||||
public QuantityType<?> getTemperature() {
|
||||
return new QuantityType<>(temperature, Units.KELVIN);
|
||||
}
|
||||
|
||||
public PercentType getBrightness() {
|
||||
return new PercentType(brightness);
|
||||
}
|
||||
}
|
||||
+10
@@ -42,6 +42,8 @@ public class Sun extends RiseSet implements Planet {
|
||||
|
||||
private SunPhase phase = new SunPhase();
|
||||
|
||||
private Circadian circadian = Circadian.DEFAULT;
|
||||
|
||||
/**
|
||||
* Returns the astro dawn range.
|
||||
*/
|
||||
@@ -308,4 +310,12 @@ public class Sun extends RiseSet implements Planet {
|
||||
public Map<SunPhaseName, Range> getAllRanges() {
|
||||
return ranges;
|
||||
}
|
||||
|
||||
public Circadian getCircadian() {
|
||||
return circadian;
|
||||
}
|
||||
|
||||
public void setCircadian(Circadian circadian) {
|
||||
this.circadian = circadian;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ thing-type.config.astro.sunconfig.useMeteorologicalSeason.description = Uses met
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.astro.circadian.label = Circadian Cycle
|
||||
channel-group-type.astro.circadian.description = Circadian cycle computed values
|
||||
channel-group-type.astro.distance.label = Distance
|
||||
channel-group-type.astro.distance.description = Distance data
|
||||
channel-group-type.astro.moonEclipse.label = Eclipses
|
||||
|
||||
@@ -293,6 +293,16 @@
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<!-- Circadian cycle -->
|
||||
<channel-group-type id="circadian">
|
||||
<label>Circadian Cycle</label>
|
||||
<description>Circadian cycle computed values</description>
|
||||
<channels>
|
||||
<channel id="brightness" typeId="system.brightness"/>
|
||||
<channel id="temperature" typeId="system.color-temperature-abs"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="total">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Total Eclipse</label>
|
||||
|
||||
@@ -70,8 +70,13 @@
|
||||
<channel-group id="season" typeId="season"/>
|
||||
<channel-group id="eclipse" typeId="sunEclipse"/>
|
||||
<channel-group id="phase" typeId="sunPhase"/>
|
||||
<channel-group id="circadian" typeId="circadian"/>
|
||||
</channel-groups>
|
||||
|
||||
<properties>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>geolocation</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:astro:sunconfig"/>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
|
||||
|
||||
<thing-type uid="astro:sun">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="brightness" groupIds="circadian">
|
||||
<type>system:brightness</type>
|
||||
</add-channel>
|
||||
<add-channel id="temperature" groupIds="circadian">
|
||||
<type>system:color-temperature-abs</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
</update:update-descriptions>
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.astro.internal.calc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.astro.internal.model.Circadian;
|
||||
import org.openhab.binding.astro.internal.model.Range;
|
||||
|
||||
/**
|
||||
* Tests for {@link CircadianCalc}.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
public class CircadianCalcTest {
|
||||
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
@Test
|
||||
public void calculateUsesPreviousSolarMidnightBeforeSunrise() {
|
||||
Calendar noon = newCalendar(2024, Calendar.JANUARY, 1, 13, 0);
|
||||
Calendar sunrise = newCalendar(2024, Calendar.JANUARY, 1, 7, 0);
|
||||
Calendar sunset = newCalendar(2024, Calendar.JANUARY, 1, 19, 0);
|
||||
|
||||
Circadian beforeSunrise = CircadianCalc.calculate(newCalendar(2024, Calendar.JANUARY, 1, 5, 0), sunrise, sunset,
|
||||
noon);
|
||||
assertEquals(56, beforeSunrise.brightness());
|
||||
Circadian afterSunset = CircadianCalc.calculate(newCalendar(2024, Calendar.JANUARY, 1, 21, 0), sunrise, sunset,
|
||||
noon);
|
||||
assertEquals(afterSunset, beforeSunrise);
|
||||
|
||||
assertEquals(2500, beforeSunrise.temperature());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateUsesRiseAndSetRangeStarts() {
|
||||
Calendar noon = newCalendar(2024, Calendar.JANUARY, 1, 13, 0);
|
||||
Calendar sunrise = newCalendar(2024, Calendar.JANUARY, 1, 7, 0);
|
||||
Calendar sunset = newCalendar(2024, Calendar.JANUARY, 1, 19, 0);
|
||||
Calendar now = newCalendar(2024, Calendar.JANUARY, 1, 21, 0);
|
||||
|
||||
Range riseRange = new Range(sunrise, newCalendar(2024, Calendar.JANUARY, 1, 8, 0));
|
||||
Range setRange = new Range(sunset, newCalendar(2024, Calendar.JANUARY, 1, 20, 0));
|
||||
Range noonRange = new Range(noon, newCalendar(2024, Calendar.JANUARY, 1, 14, 0));
|
||||
|
||||
Circadian expected = CircadianCalc.calculate(now, sunrise, sunset, noon);
|
||||
Circadian actual = CircadianCalc.calculate(now, riseRange, setRange, noonRange);
|
||||
|
||||
assertEquals(expected, actual);
|
||||
assertNotEquals(CircadianCalc.calculate(now, sunrise, sunrise, noon), actual);
|
||||
|
||||
actual = CircadianCalc.calculate(now, new Range(), new Range(), null);
|
||||
assertEquals(Circadian.DEFAULT, actual);
|
||||
}
|
||||
|
||||
private static Calendar newCalendar(int year, int month, int day, int hour, int minute) {
|
||||
Calendar calendar = new GregorianCalendar(UTC);
|
||||
calendar.clear();
|
||||
calendar.set(year, month, day, hour, minute, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
return calendar;
|
||||
}
|
||||
}
|
||||
+80
-44
@@ -14,6 +14,7 @@ package org.openhab.binding.astro.internal.calc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
@@ -45,12 +46,21 @@ import org.openhab.binding.astro.internal.util.DateTimeUtils;
|
||||
*/
|
||||
public class SunCalcTest {
|
||||
|
||||
private static final TimeZone TIME_ZONE = TimeZone.getTimeZone("Europe/Amsterdam");
|
||||
private static final Calendar FEB_27_2019 = SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 1, 0, TIME_ZONE);
|
||||
private static final TimeZone AMSTERDAM_TIME_ZONE = TimeZone.getTimeZone("Europe/Amsterdam");
|
||||
private static final Calendar FEB_27_2019 = SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 1, 0,
|
||||
AMSTERDAM_TIME_ZONE);
|
||||
private static final double AMSTERDAM_LATITUDE = 52.367607;
|
||||
private static final double AMSTERDAM_LONGITUDE = 4.8978293;
|
||||
private static final double AMSTERDAM_ALTITUDE = 0.0;
|
||||
private static final int ACCURACY_IN_MILLIS = 3 * 60 * 1000;
|
||||
private static final TimeZone BARROW_TIME_ZONE = TimeZone.getTimeZone("America/Anchorage");
|
||||
private static final double BARROW_LATITUDE = 71.2906;
|
||||
private static final double BARROW_LONGITUDE = -156.7886;
|
||||
private static final double BARROW_ALTITUDE = 0.0;
|
||||
private static final Calendar SUMMER_SOLSTICE_AMSTERDAM = SunCalcTest.newCalendar(2024, Calendar.JUNE, 21, 12, 0,
|
||||
AMSTERDAM_TIME_ZONE);
|
||||
private static final Calendar SUMMER_SOLSTICE_BARROW = SunCalcTest.newCalendar(2024, Calendar.JUNE, 21, 12, 0,
|
||||
BARROW_TIME_ZONE);
|
||||
|
||||
private SunCalc sunCalc;
|
||||
|
||||
@@ -62,7 +72,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testGetSunInfoForOldDate() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
assertNotNull(sun.getNight());
|
||||
|
||||
@@ -91,150 +101,157 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testGetSunInfoForAstronomicalDawnAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAstroDawn();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 05:39 till 06:18
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 5, 39, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 5, 39, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
cal = range.getEnd();
|
||||
assertNotNull(cal);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 18, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 18, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForNauticDawnAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getNauticDawn();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 06:18 till 06:58
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 18, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 18, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
cal = range.getEnd();
|
||||
assertNotNull(cal);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 58, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 58, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForCivilDawnAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getCivilDawn();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 06:58 till 07:32
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 58, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 6, 58, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
cal = range.getEnd();
|
||||
assertNotNull(cal);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 7, 32, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 7, 32, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForRiseAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getRise();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 07:32
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 7, 32, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 7, 32, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForSunNoonAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getNoon();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 12:54
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 12, 54, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(
|
||||
SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 12, 54, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForSetAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getSet();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 18:15
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 15, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(
|
||||
SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 15, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForCivilDuskAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getCivilDusk();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 18:15 till 18:50
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 15, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(
|
||||
SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 15, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
cal = range.getEnd();
|
||||
assertNotNull(cal);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 50, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(
|
||||
SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 50, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForNauticDuskAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getNauticDusk();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 18:50 till 19:29
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 50, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(
|
||||
SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 18, 50, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
cal = range.getEnd();
|
||||
assertNotNull(cal);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 19, 29, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(
|
||||
SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 19, 29, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSunInfoForAstronomicalDuskAccuracy() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAstroDusk();
|
||||
assertNotNull(range);
|
||||
Calendar cal = range.getStart();
|
||||
assertNotNull(cal);
|
||||
// expected result from haevens-above.com is 27 Feb 2019 19:29 till 20:09
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 19, 29, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(
|
||||
SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 19, 29, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
cal = range.getEnd();
|
||||
assertNotNull(cal);
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 20, 9, TIME_ZONE).getTimeInMillis(),
|
||||
assertEquals(SunCalcTest.newCalendar(2019, Calendar.FEBRUARY, 27, 20, 9, AMSTERDAM_TIME_ZONE).getTimeInMillis(),
|
||||
cal.getTimeInMillis(), ACCURACY_IN_MILLIS);
|
||||
}
|
||||
|
||||
@@ -242,7 +259,7 @@ public class SunCalcTest {
|
||||
@Disabled
|
||||
public void testRangesForCoherenceBetweenNightEndAndAstroDawnStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.NIGHT);
|
||||
assertNotNull(range);
|
||||
@@ -254,7 +271,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenMorningNightEndAndAstroDawnStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.MORNING_NIGHT);
|
||||
assertNotNull(range);
|
||||
@@ -266,7 +283,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenAstroDownEndAndNauticDawnStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN);
|
||||
assertNotNull(range);
|
||||
@@ -278,7 +295,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenNauticDawnEndAndCivilDawnStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN);
|
||||
assertNotNull(range);
|
||||
@@ -290,7 +307,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenCivilDawnEndAndSunRiseStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN);
|
||||
assertNotNull(range);
|
||||
@@ -302,7 +319,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenSunRiseEndAndDaylightStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.SUN_RISE);
|
||||
assertNotNull(range);
|
||||
@@ -314,7 +331,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenDaylightEndAndSunSetStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.DAYLIGHT);
|
||||
assertNotNull(range);
|
||||
@@ -326,7 +343,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenSunSetEndAndCivilDuskStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.SUN_SET);
|
||||
assertNotNull(range);
|
||||
@@ -338,7 +355,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenCivilDuskEndAndNauticDuskStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK);
|
||||
assertNotNull(range);
|
||||
@@ -350,7 +367,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenNauticDuskEndAndAstroDuskStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK);
|
||||
assertNotNull(range);
|
||||
@@ -362,7 +379,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenAstroDuskEndAndNightStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK);
|
||||
assertNotNull(range);
|
||||
@@ -374,7 +391,7 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testRangesForCoherenceBetweenAstroDuskEndAndEveningNightStart() {
|
||||
Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE, false,
|
||||
TIME_ZONE, Locale.ROOT);
|
||||
AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Range range = sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK);
|
||||
assertNotNull(range);
|
||||
@@ -388,7 +405,7 @@ public class SunCalcTest {
|
||||
TimeZone tZone = TimeZone.getTimeZone("Europe/London");
|
||||
Calendar tDate = SunCalcTest.newCalendar(2020, Calendar.MAY, 13, 5, 12, tZone);
|
||||
|
||||
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, TIME_ZONE, Locale.ROOT);
|
||||
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
assertEquals(SunPhaseName.CIVIL_DAWN, sun.getPhase().getName());
|
||||
}
|
||||
|
||||
@@ -399,7 +416,7 @@ public class SunCalcTest {
|
||||
Calendar tDate = SunCalcTest.newCalendar(2020, Calendar.MAY, 13, 5, 13, tZone);
|
||||
tDate.set(Calendar.SECOND, 4);
|
||||
|
||||
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, TIME_ZONE, Locale.ROOT);
|
||||
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
assertEquals(SunPhaseName.SUN_RISE, sun.getPhase().getName());
|
||||
}
|
||||
|
||||
@@ -408,7 +425,7 @@ public class SunCalcTest {
|
||||
TimeZone tZone = TimeZone.getTimeZone("Europe/London");
|
||||
Calendar tDate = SunCalcTest.newCalendar(2020, Calendar.MAY, 13, 5, 18, tZone);
|
||||
|
||||
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, TIME_ZONE, Locale.ROOT);
|
||||
Sun sun = sunCalc.getSunInfo(tDate, 53.524695, -2.4, 0.0, true, AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
assertEquals(SunPhaseName.DAYLIGHT, sun.getPhase().getName());
|
||||
}
|
||||
|
||||
@@ -445,9 +462,9 @@ public class SunCalcTest {
|
||||
@Test
|
||||
public void testAstroAndMeteoSeasons() {
|
||||
Sun meteoSun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE,
|
||||
true, TIME_ZONE, Locale.ROOT);
|
||||
true, AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
Sun equiSun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE,
|
||||
false, TIME_ZONE, Locale.ROOT);
|
||||
false, AMSTERDAM_TIME_ZONE, Locale.ROOT);
|
||||
|
||||
Calendar cal = meteoSun.getSeason().getSpring();
|
||||
assertNotNull(cal);
|
||||
@@ -458,4 +475,23 @@ public class SunCalcTest {
|
||||
assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertFalse(cal.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSunUpAllDayForBarrowAlaska() throws Exception {
|
||||
Method isSunUpAllDay = SunCalc.class.getDeclaredMethod("isSunUpAllDay", Calendar.class, double.class,
|
||||
double.class, Double.class);
|
||||
isSunUpAllDay.setAccessible(true);
|
||||
|
||||
// At summer solstice in Barrow, Alaska, sun stays up all day
|
||||
boolean result = (boolean) isSunUpAllDay.invoke(sunCalc, SUMMER_SOLSTICE_BARROW, BARROW_LATITUDE,
|
||||
BARROW_LONGITUDE, BARROW_ALTITUDE);
|
||||
|
||||
assertTrue(result);
|
||||
|
||||
// It's not the case in Amsterdam
|
||||
result = (boolean) isSunUpAllDay.invoke(sunCalc, SUMMER_SOLSTICE_AMSTERDAM, AMSTERDAM_LATITUDE,
|
||||
AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE);
|
||||
|
||||
assertFalse(result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user