Compare commits

...

4 commits

Author SHA1 Message Date
9ecaefc708
Fix what I wanted to not have to fix 2024-09-26 18:05:17 +02:00
cab4f1530e
Experimental support for polar cycles
In tests this is 2 days inaccurate. Better than nothing though.
I should just add getting sun's position on the sky
2024-09-26 18:01:44 +02:00
f25741f689
Update MockTwilightTimeTest to use the new format 2024-09-26 18:00:06 +02:00
bdb3404c3d
Duration -> LocalDateTime in Twilight
more appropriate for polar seasons
2024-09-26 17:59:45 +02:00
5 changed files with 93 additions and 41 deletions

View file

@ -2,14 +2,15 @@ package eu.m724.wtapi.object;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* @param date The date this object contains times for
* @param sunrise Time of sunrise relative to UTC midnight of the date
* @param sunset Time of sunset relative to UTC midnight of the date
* @param sunrise Time of sunrise
* @param sunset Time of sunset
*/
public record Twilight(LocalDate date, Duration sunrise, Duration sunset) {
public record Twilight(LocalDate date, LocalDateTime sunrise, LocalDateTime sunset) {
public Duration getDayLength() {
return sunset.minus(sunrise);
return Duration.between(sunrise, sunset);
}
}

View file

@ -4,7 +4,6 @@ import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import eu.m724.wtapi.provider.twilight.CacheableTwilightTimeProvider;
import java.time.Duration;
import java.time.LocalDate;
import static java.lang.Math.*;
@ -22,10 +21,22 @@ public class ApproximateTwilightTimeProvider extends CacheableTwilightTimeProvid
double equationOfTime = cache.equationOfTime();
double latRad = toRadians(coordinates.latitude);
// 90.833 deg = 1.5853349194640094 rad
double n1 = cos(1.5853349194640094) / (cos(latRad) * cos(declination));
double n2 = tan(latRad) * tan(declination);
double hourAngle = acos(n1 - n2);
double hourAngle = hourAngle(latRad, declination);
System.out.println(hourAngle);
if (Double.isNaN(hourAngle)) {
LocalDate sunriseDate = step(cache.date().minusDays(1), latRad, -1);
LocalDate sunsetDate = step(cache.date().plusDays(1), latRad, 1);
Twilight backwardTwilight = calculateTwilightTime(sunriseDate, coordinates);
Twilight forwardTwilight = calculateTwilightTime(sunsetDate, coordinates);
return new Twilight(
cache.date(),
backwardTwilight.sunrise(),
forwardTwilight.sunset()
);
}
double longitudeEffect = 4 * toDegrees(hourAngle);
@ -37,34 +48,64 @@ public class ApproximateTwilightTimeProvider extends CacheableTwilightTimeProvid
return new Twilight(
cache.date(),
Duration.ofMillis((long) (sunrise * 60000)),
Duration.ofMillis((long) (sunset * 60000))
cache.date().atStartOfDay().plusMinutes((long) sunrise),
cache.date().atStartOfDay().plusMinutes((long) sunset)
);
}
private LocalDate step(LocalDate date, double latRad, int direction) {
double declination = getDeclination(getFractionalYear(date));
System.out.println("Step date: " + date);
double hourAngle = hourAngle(latRad, declination);
if (Double.isNaN(hourAngle)) {
return step(date.plusDays(direction), latRad, direction);
}
System.out.println("this the one");
return date;
}
private double hourAngle(double latRad, double declination) {
// 90.833 deg = 1.5853349194640094 rad
double n1 = cos(1.5853349194640094) / (cos(latRad) * cos(declination));
double n2 = tan(latRad) * tan(declination);
System.out.println(n1 - n2);
return acos(n1 - n2);
}
@Override
public ApproximateTwilightTimeCache initializeCache(LocalDate date) {
int dayOfYear = date.getDayOfYear() - 1; // -1 because we have to start from zero
// 2 * PI / 365 = 0.01721420632103996
double fractionalYear = 0.01721420632103996 * dayOfYear;
double fractionalYear = getFractionalYear(date);
double equationOfTime = 229.18 * (0.000075
+ 0.001868 * cos(fractionalYear)
- 0.032077 * sin(fractionalYear)
- 0.014615 * cos(2 * fractionalYear)
- 0.040849 * sin(2 * fractionalYear));
double declination = 0.006918
return new ApproximateTwilightTimeCache(
date,
equationOfTime,
getDeclination(fractionalYear)
);
}
private double getFractionalYear(LocalDate date) {
return getFractionalYear(date.getDayOfYear() - 1); // -1 because we have to start from zero
}
private double getFractionalYear(int dayOfYear) {
return 0.01721420632103996 * dayOfYear;
}
private double getDeclination(double fractionalYear) {
return 0.006918
- 0.399912 * cos(fractionalYear)
+ 0.070257 * sin(fractionalYear)
- 0.006758 * cos(2 * fractionalYear)
+ 0.000907 * sin(2 * fractionalYear)
- 0.002697 * cos(3 * fractionalYear)
+ 0.00148 * sin(3 * fractionalYear);
return new ApproximateTwilightTimeCache(
date,
equationOfTime,
declination
);
}
}

View file

@ -6,43 +6,48 @@ import eu.m724.wtapi.provider.twilight.impl.approximate.ApproximateTwilightTimeP
import eu.m724.wtapi.provider.twilight.TwilightTimeProvider;
import org.junit.Test;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
// TODO also test cached
public class ApproximateTwilightTimeTest {
/**
* Acceptable discrepancy in minutes
* Acceptable discrepancy in seconds
* 600 is 10 minutes
*/
public static final int ACCEPTABLE_DIFFERENCE = 10;
public static int ACCEPTABLE_DIFFERENCE = 600;
@Test
public void approximateTest() {
TwilightTimeProvider provider = new ApproximateTwilightTimeProvider();
// used https://gml.noaa.gov/grad/solcalc/index.html for reference values
testLocation(provider, 26, 6, 2023, 53.123394, 23.0864867, 122, 1139);
testLocation(provider, 13, 11, 2040, 45.432427, -122.3899276, 907, 1481);
testLocation(provider, 23, 3, 2021, 55.5024161, 9.6801853, 315, 1061);
testLocation(provider, 6, 8,1990, -72.012117, 2.5240873, 600, 832);
// TODO this is broken so fix
//testLocation(provider, 6, 9,2021, 82.498665, -62.3458366, 74, 1351); // date not on purpose as this was the first sunset in that location. first sunset since 5th april
testLocation(provider, 26, 6, 2023, 53.123394, 23.0864867, LocalDateTime.of(2023, 6, 26, 2, 2), LocalDateTime.of(2023, 6, 26, 18, 59));
testLocation(provider, 13, 11, 2040, 45.432427, -122.3899276, LocalDateTime.of(2040, 11, 13, 15, 7), LocalDateTime.of(2040, 11, 14, 0, 41));
testLocation(provider, 23, 3, 2021, 55.5024161, 9.6801853, LocalDateTime.of(2021, 3, 23, 5, 15), LocalDateTime.of(2021, 3, 23, 17, 41));
testLocation(provider, 6, 8,1990, -72.012117, 2.5240873, LocalDateTime.of(1990, 8, 6, 10, 0), LocalDateTime.of(1990, 8, 6, 13, 52));
// TODO this is broken (very inaccurate) so fix
ACCEPTABLE_DIFFERENCE = 172800; // 2 days
testLocation(provider, 5, 9,2021, 82.498665, -62.3458366, LocalDateTime.of(2021, 4, 5, 20, 55), LocalDateTime.of(2021, 9, 6, 18, 22)); // the reference values might be incorrect
}
private void testLocation(TwilightTimeProvider provider, int day, int month, int year, double latitude, double longitude, int actualSunrise, int actualSunset) {
private void testLocation(TwilightTimeProvider provider, int day, int month, int year, double latitude, double longitude, LocalDateTime actualSunrise, LocalDateTime actualSunset) {
LocalDate date = LocalDate.of(year, month, day);
Coordinates coordinates = new Coordinates(latitude, longitude);
Twilight twilight = provider.calculateTwilightTime(date, coordinates);
System.out.println();
System.out.println(date);
System.out.println(coordinates.latitude + " " + coordinates.longitude);
System.out.println("Calculated sunrise: " + date.atStartOfDay().plus(twilight.sunrise()));
System.out.println("Actual sunrise: " + date.atStartOfDay().plusMinutes(actualSunrise));
assert Math.abs(twilight.sunrise().toMinutes() - actualSunrise) < ACCEPTABLE_DIFFERENCE;
System.out.println("Calculated sunrise: " + twilight.sunrise());
System.out.println("Actual sunrise: " + actualSunrise);
assert Duration.between(twilight.sunrise(), actualSunrise).abs().getSeconds() < ACCEPTABLE_DIFFERENCE;
System.out.println("Calculated sunset: " + date.atStartOfDay().plus(twilight.sunset()));
System.out.println("Actual sunset: " + date.atStartOfDay().plusMinutes(actualSunset));
assert Math.abs(twilight.sunset().toMinutes() - actualSunset) < ACCEPTABLE_DIFFERENCE;
System.out.println("Calculated sunset: " + twilight.sunset());
System.out.println("Actual sunset: " + actualSunset);
assert Duration.between(twilight.sunset(), actualSunset).abs().getSeconds() < ACCEPTABLE_DIFFERENCE;
System.out.println();
}
}

View file

@ -6,6 +6,8 @@ import eu.m724.wtapi.provider.twilight.TwilightTimeProvider;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class MockTwilightTimeProvider extends TwilightTimeProvider {
@Override
@ -13,8 +15,8 @@ public class MockTwilightTimeProvider extends TwilightTimeProvider {
int time = (int) (coordinates.latitude + coordinates.longitude);
return new Twilight(
date,
Duration.ofMinutes(-time),
Duration.ofMinutes(time)
date.atStartOfDay().plusSeconds(-time),
date.atStartOfDay().plusSeconds(time)
);
}
}

View file

@ -17,7 +17,10 @@ public class MockTwilightTimeTest {
Twilight twilight = provider.calculateTwilightTime(date, coordinates);
assert twilight.date().equals(date);
assert twilight.sunrise().getSeconds() == -6840;
assert twilight.sunset().getSeconds() == 6840;
// this might make no sense but just a test remember
assert twilight.sunrise().getDayOfMonth() == 19;
assert twilight.sunrise().toLocalTime().toSecondOfDay() == 86286;
assert twilight.sunset().getDayOfMonth() == 20;
assert twilight.sunset().toLocalTime().toSecondOfDay() == 114;
}
}