[astro] Switching radiation to immutable and Instant (#19949)

* Switching radiation to immutable and Instant

Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital
2026-01-04 23:13:32 +01:00
committed by GitHub
parent 1663f77a1d
commit 120b077d1f
7 changed files with 93 additions and 76 deletions
@@ -51,9 +51,10 @@ import org.slf4j.LoggerFactory;
public class AstroActions implements ThingActions { public class AstroActions implements ThingActions {
private final Logger logger = LoggerFactory.getLogger(AstroActions.class); private final Logger logger = LoggerFactory.getLogger(AstroActions.class);
private @Nullable AstroThingHandler handler;
private final TimeZoneProvider timeZoneProvider; private final TimeZoneProvider timeZoneProvider;
private @Nullable AstroThingHandler handler;
@Activate @Activate
public AstroActions(@Reference TimeZoneProvider timeZoneProvider) { public AstroActions(@Reference TimeZoneProvider timeZoneProvider) {
this.timeZoneProvider = timeZoneProvider; this.timeZoneProvider = timeZoneProvider;
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2010-2026 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 java.time.LocalDate;
import java.time.ZonedDateTime;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.model.Radiation;
import org.openhab.binding.astro.internal.util.MathUtils;
/**
* Calculates the color temperature and brightness depending upon sun positional information
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class RadiationCalc {
private static final double SC = 1367; // Solar constant in W/m²
public static Radiation calculate(ZonedDateTime now, double elevation, @Nullable Double altitude) {
double altitudeRatio = (altitude != null) ? 1 / Math.pow((1 - (6.5 / 288) * (altitude / 1000.0)), 5.256) : 1;
double sinAlpha = MathUtils.sinDeg(elevation);
LocalDate date = now.toLocalDate();
int dayOfYear = date.getDayOfYear();
int daysInYear = date.lengthOfYear(); // 365 or 366
// Direct Solar Radiation (in W/m²) at the atmosphere entry
// At sunrise/sunset - calculations limits are reached
double rOut = (elevation > 3 ? SC * (0.034 * Math.cos(MathUtils.TWO_PI * dayOfYear / daysInYear) + 1) : 0)
* sinAlpha;
// 0.6 = transmissivity coefficient
double m = Math.pow(0.6, (Math.sqrt(1229 + Math.pow(614 * sinAlpha, 2)) - 614 * sinAlpha) * altitudeRatio);
// Direct radiation after atmospheric layer
double rDir = rOut * m;
// Diffuse Radiation
double rDiff = rOut * (0.271 - 0.294 * m);
return new Radiation(rDir, rDiff);
}
}
@@ -28,7 +28,6 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.model.Eclipse; import org.openhab.binding.astro.internal.model.Eclipse;
import org.openhab.binding.astro.internal.model.EclipseType; import org.openhab.binding.astro.internal.model.EclipseType;
import org.openhab.binding.astro.internal.model.Position; import org.openhab.binding.astro.internal.model.Position;
import org.openhab.binding.astro.internal.model.Radiation;
import org.openhab.binding.astro.internal.model.Range; import org.openhab.binding.astro.internal.model.Range;
import org.openhab.binding.astro.internal.model.Season; import org.openhab.binding.astro.internal.model.Season;
import org.openhab.binding.astro.internal.model.Sun; import org.openhab.binding.astro.internal.model.Sun;
@@ -45,9 +44,6 @@ import org.openhab.binding.astro.internal.util.MathUtils;
*/ */
@NonNullByDefault @NonNullByDefault
public class SunCalc { public class SunCalc {
private static final double J2000 = 2451545.0;
private static final double SC = 1367; // Solar constant in W/m²
private static final double M0 = Math.toRadians(357.5291); private static final double M0 = Math.toRadians(357.5291);
private static final double M1 = Math.toRadians(0.98560028); private static final double M1 = Math.toRadians(0.98560028);
private static final double J0 = 0.0009; private static final double J0 = 0.0009;
@@ -93,37 +89,6 @@ public class SunCalc {
position.setAzimuth(azimuth + 180); position.setAzimuth(azimuth + 180);
position.setElevation(elevation); position.setElevation(elevation);
position.setShadeLength(shadeLength); position.setShadeLength(shadeLength);
setRadiationInfo(calendar, elevation, altitude, sun);
}
/**
* Calculates sun radiation data.
*/
private void setRadiationInfo(Calendar calendar, double elevation, @Nullable Double altitude, Sun sun) {
double sinAlpha = MathUtils.sinDeg(elevation);
int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
int daysInYear = calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
// Direct Solar Radiation (in W/m²) at the atmosphere entry
// At sunrise/sunset - calculations limits are reached
double rOut = (elevation > 3) ? SC * (0.034 * MathUtils.cosDeg(360 * dayOfYear / daysInYear) + 1) : 0;
double altitudeRatio = (altitude != null) ? 1 / Math.pow((1 - (6.5 / 288) * (altitude / 1000.0)), 5.256) : 1;
double m = (Math.sqrt(1229 + Math.pow(614 * sinAlpha, 2)) - 614 * sinAlpha) * altitudeRatio;
// Direct radiation after atmospheric layer
// 0.6 = Coefficient de transmissivité
double rDir = rOut * Math.pow(0.6, m) * sinAlpha;
// Diffuse Radiation
double rDiff = rOut * (0.271 - 0.294 * Math.pow(0.6, m)) * sinAlpha;
double rTot = rDir + rDiff;
Radiation radiation = sun.getRadiation();
radiation.setDirect(rDir);
radiation.setDiffuse(rDiff);
radiation.setTotal(rTot);
} }
/** /**
@@ -306,15 +271,15 @@ public class SunCalc {
// all the following methods are translated to java based on the javascript // all the following methods are translated to java based on the javascript
// calculations of http://www.suncalc.net // calculations of http://www.suncalc.net
private double getJulianCycle(double j, double lw) { private double getJulianCycle(double j, double lw) {
return Math.round(j - J2000 - J0 - lw / MathUtils.TWO_PI); return Math.round(j - DateTimeUtils.JD_J2000 - J0 - lw / MathUtils.TWO_PI);
} }
private double getApproxSolarTransit(double ht, double lw, double n) { private double getApproxSolarTransit(double ht, double lw, double n) {
return J2000 + J0 + (ht + lw) / MathUtils.TWO_PI + n; return DateTimeUtils.JD_J2000 + J0 + (ht + lw) / MathUtils.TWO_PI + n;
} }
private double getSolarMeanAnomaly(double js) { private double getSolarMeanAnomaly(double js) {
return M0 + M1 * (js - J2000); return M0 + M1 * (js - DateTimeUtils.JD_J2000);
} }
private double getEquationOfCenter(double m) { private double getEquationOfCenter(double m) {
@@ -338,7 +303,7 @@ public class SunCalc {
} }
private double getSiderealTime(double j, double lw) { private double getSiderealTime(double j, double lw) {
return TH0 + TH1 * (j - J2000) - lw; return TH0 + TH1 * (j - DateTimeUtils.JD_J2000) - lw;
} }
private double getAzimuth(double th, double a, double phi, double d) { private double getAzimuth(double th, double a, double phi, double d) {
@@ -19,6 +19,7 @@ import static org.openhab.core.types.RefreshType.REFRESH;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.Arrays; import java.util.Arrays;
@@ -344,7 +345,9 @@ public abstract class AstroThingHandler extends BaseThingHandler {
public void schedule(Job job, Instant eventAt) { public void schedule(Job job, Instant eventAt) {
long sleepTime = Instant.now().until(eventAt, ChronoUnit.MILLIS); long sleepTime = Instant.now().until(eventAt, ChronoUnit.MILLIS);
schedule(job, sleepTime); schedule(job, sleepTime);
logger.debug("Scheduled {} in {}ms (at {})", job, sleepTime, eventAt); if (logger.isDebugEnabled()) {
logger.debug("Scheduled {} in {}ms (at {})", job, sleepTime, eventAt.atZone(ZoneId.systemDefault()));
}
} }
private void tidyScheduledFutures() { private void tidyScheduledFutures() {
@@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.astro.internal.handler; package org.openhab.binding.astro.internal.handler;
import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Calendar; import java.util.Calendar;
@@ -22,6 +23,7 @@ import java.util.TimeZone;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.astro.internal.calc.CircadianCalc; import org.openhab.binding.astro.internal.calc.CircadianCalc;
import org.openhab.binding.astro.internal.calc.RadiationCalc;
import org.openhab.binding.astro.internal.calc.SunCalc; import org.openhab.binding.astro.internal.calc.SunCalc;
import org.openhab.binding.astro.internal.job.DailyJobSun; import org.openhab.binding.astro.internal.job.DailyJobSun;
import org.openhab.binding.astro.internal.job.Job; import org.openhab.binding.astro.internal.job.Job;
@@ -68,12 +70,14 @@ public class SunHandler extends AstroThingHandler {
Double longitude = thingConfig.longitude; Double longitude = thingConfig.longitude;
Double altitude = thingConfig.altitude; Double altitude = thingConfig.altitude;
Calendar calendar = Calendar.getInstance(zone, locale); Calendar calendar = Calendar.getInstance(zone, locale);
ZonedDateTime now = Instant.now().atZone(zoneId);
sunCalc.setPositionalInfo(calendar, latitude != null ? latitude : 0, longitude != null ? longitude : 0, sunCalc.setPositionalInfo(calendar, latitude != null ? latitude : 0, longitude != null ? longitude : 0,
altitude != null ? altitude : 0, sun); altitude != null ? altitude : 0, sun);
sun.getEclipse().setElevations(this, timeZoneProvider); sun.getEclipse().setElevations(this, timeZoneProvider);
sun.setCircadian(CircadianCalc.calculate(calendar, sun.getRise(), sun.getSet(), sun.getNoon())); sun.setCircadian(CircadianCalc.calculate(calendar, sun.getRise(), sun.getSet(), sun.getNoon()));
sun.setRadiation(RadiationCalc.calculate(now, sun.getPosition().getElevationAsDouble(), altitude));
this.sun = sun; this.sun = sun;
@@ -138,6 +142,6 @@ public class SunHandler extends AstroThingHandler {
public @Nullable Radiation getRadiationAt(ZonedDateTime date) { public @Nullable Radiation getRadiationAt(ZonedDateTime date) {
Sun localSun = getPositionedSunAt(date); Sun localSun = getPositionedSunAt(date);
return localSun.getRadiation(); return RadiationCalc.calculate(date, localSun.getPosition().getElevationAsDouble(), thingConfig.altitude);
} }
} }
@@ -13,9 +13,12 @@
package org.openhab.binding.astro.internal.model; package org.openhab.binding.astro.internal.model;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.dimension.Intensity; import org.openhab.core.library.dimension.Intensity;
import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units; import org.openhab.core.library.unit.Units;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/** /**
* Holds the calculated direct, diffuse and total * Holds the calculated direct, diffuse and total
@@ -25,59 +28,39 @@ import org.openhab.core.library.unit.Units;
*/ */
@NonNullByDefault @NonNullByDefault
public class Radiation { public class Radiation {
public static final Radiation NULL = new Radiation();
private double direct; private final double direct;
private double diffuse; private final double diffuse;
private double total;
public Radiation() { private Radiation() {
this(Double.NaN, Double.NaN);
} }
public Radiation(double direct, double diffuse, double total) { public Radiation(double direct, double diffuse) {
this.direct = direct; this.direct = direct;
this.diffuse = diffuse; this.diffuse = diffuse;
this.total = total;
}
/**
* Sets the direct radiation.
*/
public void setDirect(double direct) {
this.direct = direct;
}
/**
* Sets the diffuse radiation.
*/
public void setDiffuse(double diffuse) {
this.diffuse = diffuse;
}
/**
* Sets the total radiation.
*/
public void setTotal(double total) {
this.total = total;
} }
/** /**
* Returns the total radiation. * Returns the total radiation.
*/ */
public QuantityType<Intensity> getTotal() { public @Nullable QuantityType<Intensity> getTotal() {
return new QuantityType<>(total, Units.IRRADIANCE); return Double.isNaN(direct) || Double.isNaN(diffuse) ? null
: new QuantityType<>(direct + diffuse, Units.IRRADIANCE);
} }
/** /**
* Returns the direct radiation. * Returns the direct radiation.
*/ */
public QuantityType<Intensity> getDirect() { public State getDirect() {
return new QuantityType<>(direct, Units.IRRADIANCE); return Double.isNaN(direct) ? UnDefType.UNDEF : new QuantityType<>(direct, Units.IRRADIANCE);
} }
/** /**
* Returns the diffuse radiation. * Returns the diffuse radiation.
*/ */
public QuantityType<Intensity> getDiffuse() { public State getDiffuse() {
return new QuantityType<>(diffuse, Units.IRRADIANCE); return Double.isNaN(diffuse) ? UnDefType.UNDEF : new QuantityType<>(diffuse, Units.IRRADIANCE);
} }
} }
@@ -36,7 +36,7 @@ public class Sun extends RiseSet implements Planet {
private Eclipse eclipse = new Eclipse(EclipseKind.PARTIAL, EclipseKind.TOTAL, EclipseKind.RING); private Eclipse eclipse = new Eclipse(EclipseKind.PARTIAL, EclipseKind.TOTAL, EclipseKind.RING);
private Radiation radiation = new Radiation(); private Radiation radiation = Radiation.NULL;
private SunPhase phase = new SunPhase(); private SunPhase phase = new SunPhase();
@@ -239,6 +239,10 @@ public class Sun extends RiseSet implements Planet {
return radiation; return radiation;
} }
public void setRadiation(Radiation radiation) {
this.radiation = radiation;
}
/** /**
* Sets the sun position. * Sets the sun position.
*/ */