001/*
002 * Shredzone Commons - suncalc
003 *
004 * Copyright (C) 2017 Richard "Shred" K├Ârber
005 *   http://commons.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
013 */
014package org.shredzone.commons.suncalc.util;
015
016import static java.lang.Math.*;
017import static org.shredzone.commons.suncalc.util.ExtendedMath.*;
018
019/**
020 * Calculations and constants for the Sun.
021 *
022 * @see "Astronomy on the Personal Computer, 4th edition
023 *      (Oliver Montenbruck, Thomas Pfleger) -
024 *      ISBN 978-3-540-67221-0"
025 */
026public final class Sun {
027
028    private static final double SUN_DISTANCE = 149598000.0;
029    private static final double SUN_MEAN_RADIUS = 695700.0;
030
031    private Sun() {
032        // Utility class without constructor
033    }
034
035    /**
036     * Calculates the equatorial position of the sun.
037     *
038     * @param date
039     *            {@link JulianDate} to be used
040     * @return {@link Vector} containing the sun position
041     */
042    public static Vector positionEquatorial(JulianDate date) {
043        double T = date.getJulianCentury();
044        double M = PI2 * frac(0.993133 + 99.997361 * T);
045        double L = PI2 * frac(0.7859453 + M / PI2
046            + (6893.0 * sin(M) + 72.0 * sin(2.0 * M) + 6191.2 * T) / 1296.0e3);
047
048        double d = SUN_DISTANCE
049            * (1 - 0.016718 * cos(date.getTrueAnomaly()));
050
051        return Vector.ofPolar(L, 0.0, d);
052    }
053
054    /**
055     * Calculates the geocentric position of the sun.
056     *
057     * @param date
058     *            {@link JulianDate} to be used
059     * @return {@link Vector} containing the sun position
060     */
061    public static Vector position(JulianDate date) {
062        Matrix rotateMatrix = equatorialToEcliptical(date).transpose();
063        return rotateMatrix.multiply(positionEquatorial(date));
064    }
065
066    /**
067     * Calculates the horizontal position of the sun.
068     *
069     * @param date
070     *            {@link JulianDate} to be used
071     * @param lat
072     *            Latitude, in radians
073     * @param lng
074     *            Longitute, in radians
075     * @return {@link Vector} of horizontal sun position
076     */
077    public static Vector positionHorizontal(JulianDate date, double lat, double lng) {
078        Vector mc = position(date);
079        double h = date.getGreenwichMeanSiderealTime() + lng - mc.getPhi();
080        return equatorialToHorizontal(h, mc.getTheta(), mc.getR(), lat);
081    }
082
083    /**
084     * Calculates the topocentric position of the sun.
085     * <p>
086     * Atmospheric refraction is <em>not</em> taken into account.
087     *
088     * @param date
089     *            {@link JulianDate} to be used
090     * @param lat
091     *            Latitude, in radians
092     * @param lng
093     *            Longitute, in radians
094     * @param elev
095     *            Elevation, in meters
096     * @return {@link Vector} of topocentric sun position
097     * @since 3.9
098     */
099    public static Vector positionTopocentric(JulianDate date, double lat, double lng, double elev) {
100        Vector pos = positionHorizontal(date, lat, lng);
101        return Vector.ofPolar(
102                pos.getPhi(),
103                pos.getTheta() - parallax(elev, pos.getR()),
104                pos.getR()
105        );
106    }
107
108    /**
109     * Returns the angular radius of the sun.
110     *
111     * @param distance
112     *            Distance of the sun, in kilometers.
113     * @return Angular radius of the sun, in radians.
114     * @see <a href="https://en.wikipedia.org/wiki/Angular_diameter">Wikipedia: Angular
115     *      Diameter</a>
116     */
117    public static double angularRadius(double distance) {
118        return asin(SUN_MEAN_RADIUS / distance);
119    }
120
121}