Skip to content

Commit c5b618c

Browse files
cliniqueNadahar
andauthored
[astro] Review Eclipses calculations (#20077)
* Instant and Immutable Eclipse Signed-off-by: Gaël L'hopital <gael@lhopital.org> Co-authored-by: Nadahar <Nadahar@users.noreply.github.com>
1 parent 88c9e0b commit c5b618c

37 files changed

Lines changed: 802 additions & 488 deletions

bundles/org.openhab.binding.astro/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ This binding has its own IconProvider and makes available the following list of
3535
| Icon Name | Dynamic | Illustration |
3636
| --------------------------- | ------- | ------------------------------------------------- |
3737
| oh:astro:moon_day | Yes | ![Moon Age](doc/images/moon_day.svg) |
38+
| oh:astro:moon_eclipse | Yes | ![Moon Age](doc/images/moon_eclipse.svg) |
3839
| oh:astro:moon_phase | Yes | ![Moon Phase](doc/images/moon_day.svg) |
3940
| oh:astro:season | Yes | ![Season](doc/images/season.svg) |
4041
| oh:astro:sun_eclipse | Yes | ![Sun Eclipse](doc/images/sun_eclipse.svg) |
Lines changed: 14 additions & 0 deletions
Loading

bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/AstroIconProvider.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,13 @@ public class AstroIconProvider implements IconProvider {
5252
private static final String DEFAULT_LABEL = "Astro Icons";
5353
private static final String DEFAULT_DESCRIPTION = "Icons provided for the Astro Binding";
5454
private static final String MOON_DAY_SET = "moon_day";
55+
private static final String MOON_ECLIPSE_SET = "moon_eclipse";
5556
private static final String MOON_PHASE_SET = "moon_phase";
5657
private static final String SEASON_SET = "season";
5758
private static final String SUN_ECLIPSE_SET = "sun_eclipse";
5859
private static final String ZODIAC_SET = "zodiac";
59-
private static final Set<String> ICON_SETS = Set.of(MOON_DAY_SET, MOON_PHASE_SET, SEASON_SET, SUN_ECLIPSE_SET,
60-
ZODIAC_SET);
60+
private static final Set<String> ICON_SETS = Set.of(MOON_DAY_SET, MOON_ECLIPSE_SET, MOON_PHASE_SET, SEASON_SET,
61+
SUN_ECLIPSE_SET, ZODIAC_SET);
6162

6263
private final Logger logger = LoggerFactory.getLogger(AstroIconProvider.class);
6364
private final TranslationProvider i18nProvider;
@@ -104,7 +105,7 @@ private String getText(String entry, String defaultValue, @Nullable Locale local
104105
String iconState = switch (category) {
105106
case SEASON_SET -> SeasonName.valueOf(state).name();
106107
case ZODIAC_SET -> ZodiacSign.valueOf(state).name();
107-
case SUN_ECLIPSE_SET -> EclipseKind.valueOf(state).name();
108+
case MOON_ECLIPSE_SET, SUN_ECLIPSE_SET -> EclipseKind.valueOf(state).name();
108109
case MOON_PHASE_SET -> {
109110
yield Integer.toString(MoonPhaseName.valueOf(state).getAgeDays());
110111
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.astro.internal.calc;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.openhab.binding.astro.internal.util.DateTimeUtils;
17+
18+
/**
19+
* Calculates the eclipses for the astro object
20+
*
21+
* @author Gerhard Riegler - Initial contribution
22+
* @author Gaël L'hopital - Extracted from MoonCalc
23+
*/
24+
@NonNullByDefault
25+
public class AstroCalc {
26+
protected static double varF(double k, double t) {
27+
return 160.7108 + 390.67050274 * k - .0016341 * t * t - .00000227 * t * t * t + .000000011 * t * t * t * t;
28+
}
29+
30+
protected static double varO(double k, double t) {
31+
return 124.7746 - 1.5637558 * k + .0020691 * t * t + .00000215 * t * t * t;
32+
}
33+
34+
protected static double varE(double t) {
35+
return 1 - .002516 * t - .0000074 * t * t;
36+
}
37+
38+
protected static double varM1(double k, double t) {
39+
return 201.5643 + 385.81693528 * k + .1017438 * t * t + .00001239 * t * t * t - .000000058 * t * t * t * t;
40+
}
41+
42+
protected static double varM(double k, double t) {
43+
return 2.5534 + 29.10535669 * k - .0000218 * t * t - .00000011 * t * t * t;
44+
}
45+
46+
protected static double varJde(double k, double t) {
47+
return 2451550.09765 + 29.530588853 * k + .0001337 * t * t - .00000015 * t * t * t
48+
+ .00000000073 * t * t * t * t;
49+
}
50+
51+
protected static double varK(double jd, double tz) {
52+
return ((jd + tz - DateTimeUtils.JD_2000_01_01) / 365.0) * 12.3685;
53+
}
54+
}

bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/calc/CircadianCalc.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public static Circadian calculate(Calendar calendar, Range riseRange, Range setR
4444

4545
// If we have no rise or no set, there's no point calculating a Circadian Cycle
4646
if (rise == null || set == null || noon == null) {
47-
return Circadian.DEFAULT;
47+
return Circadian.NONE;
4848
}
4949
return calculate(calendar, rise, set, noon);
5050
}
@@ -85,7 +85,7 @@ public static Circadian calculate(Calendar calendar, Calendar rise, Calendar set
8585
long dx = h - x;
8686
if (dx == 0L) {
8787
LOGGER.debug("Degenerate circadian parabola (h == x), returning default values");
88-
return Circadian.DEFAULT;
88+
return Circadian.NONE;
8989
}
9090
double a = (y - k) / (dx * dx);
9191
double percentage = Math.max(Math.min(a * Math.pow(now - h, 2) + k, 100.0), 0.0);
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.astro.internal.calc;
14+
15+
import static org.openhab.binding.astro.internal.util.MathUtils.*;
16+
17+
import java.time.Instant;
18+
import java.util.List;
19+
import java.util.Set;
20+
21+
import org.eclipse.jdt.annotation.NonNullByDefault;
22+
import org.openhab.binding.astro.internal.model.EclipseKind;
23+
import org.openhab.binding.astro.internal.model.Position;
24+
import org.openhab.binding.astro.internal.util.DateTimeUtils;
25+
26+
/**
27+
* Calculates the eclipses for the astro object
28+
*
29+
* @author Gerhard Riegler - Initial contribution
30+
* @author Gaël L'hopital - Extracted from MoonCalc
31+
*/
32+
@NonNullByDefault
33+
public abstract class EclipseCalc extends AstroCalc {
34+
public record Eclipse(EclipseKind kind, double when) {
35+
public LocalizedEclipse withPosition(Position position) {
36+
return new LocalizedEclipse(this, position.getElevationAsDouble());
37+
}
38+
}
39+
40+
public record LocalizedEclipse(Eclipse eclipse, double elevation) {
41+
public Instant when() {
42+
return DateTimeUtils.jdToInstant(eclipse.when);
43+
}
44+
45+
public EclipseKind kind() {
46+
return eclipse.kind;
47+
}
48+
49+
public boolean matches(EclipseKind otherKind) {
50+
return eclipse.kind.equals(otherKind);
51+
}
52+
}
53+
54+
public List<Eclipse> getNextEclipses(double startJd) {
55+
return validEclipses().stream().map(ek -> new Eclipse(ek, calculate(startJd, ek))).toList();
56+
}
57+
58+
public double calculate(double midnightJd, EclipseKind eclipse) {
59+
double tz = 0;
60+
double eclipseJd = 0;
61+
do {
62+
double k = varK(midnightJd, tz);
63+
tz += 1;
64+
eclipseJd = getEclipse(Math.floor(k) + getJDAjust(), eclipse);
65+
} while (eclipseJd <= midnightJd);
66+
return eclipseJd;
67+
}
68+
69+
protected abstract double getJDAjust();
70+
71+
protected abstract double astroAdjust(EclipseKind ek, double e, double m, double m1, double g, double u, double jd);
72+
73+
protected abstract Set<EclipseKind> validEclipses();
74+
75+
/**
76+
* Calculates the eclipse.
77+
*/
78+
protected double getEclipse(double kMod, EclipseKind eclipse) {
79+
double t = kMod / 1236.85;
80+
double f = varF(kMod, t);
81+
82+
if (sinDeg(Math.abs(f)) > .36) {
83+
return 0;
84+
}
85+
86+
double o = varO(kMod, t);
87+
double f1 = f - .02665 * sinDeg(o);
88+
double a1 = 299.77 + .107408 * kMod - .009173 * t * t;
89+
double e = varE(t);
90+
double m = varM(kMod, t);
91+
double m1 = varM1(kMod, t);
92+
double p = .207 * e * sinDeg(m) + .0024 * e * sinDeg(2 * m) - .0392 * sinDeg(m1) + .0116 * sinDeg(2 * m1)
93+
- .0073 * e * sinDeg(m1 + m) + .0067 * e * sinDeg(m1 - m) + .0118 * sinDeg(2 * f1);
94+
double q = 5.2207 - .0048 * e * cosDeg(m) + .002 * e * cosDeg(2 * m) - .3299 * cosDeg(m1)
95+
- .006 * e * cosDeg(m1 + m) + .0041 * e * cosDeg(m1 - m);
96+
double g = (p * cosDeg(f1) + q * sinDeg(f1)) * (1 - .0048 * cosDeg(Math.abs(f1)));
97+
double u = .0059 + .0046 * e * cosDeg(m) - .0182 * cosDeg(m1) + .0004 * cosDeg(2 * m1) - .0005 * cosDeg(m + m1);
98+
double jd = varJde(kMod, t);
99+
jd += .0161 * sinDeg(2 * m1) - .0097 * sinDeg(2 * f1) + .0073 * e * sinDeg(m1 - m) - .005 * e * sinDeg(m1 + m)
100+
- .0023 * sinDeg(m1 - 2 * f1) + .0021 * e * sinDeg(2 * m);
101+
jd += .0012 * sinDeg(m1 + 2 * f1) + .0006 * e * sinDeg(2 * m1 + m) - .0004 * sinDeg(3 * m1)
102+
- .0003 * e * sinDeg(m + 2 * f1) + .0003 * sinDeg(a1) - .0002 * e * sinDeg(m - 2 * f1)
103+
- .0002 * e * sinDeg(2 * m1 - m) - .0002 * sinDeg(o);
104+
return astroAdjust(eclipse, e, m, m1, g, u, jd);
105+
}
106+
}

0 commit comments

Comments
 (0)