Calculations of the path of the Sun

时间:2015-05-04 19:23:45

标签: java math methods astronomy

I'm writing several methods necessary to calculate the path of the Sun across a specific point. I have written the code using two different sources for my calculations and neither is producing the desired result. The sources are: http://www.pveducation.org/pvcdrom/properties-of-sunlight/suns-position and http://www.esrl.noaa.gov/gmd/grad/solcalc/solareqns.PDF

Note: Degrees to arcminutes is Deg * 60 min.

  1. localSolartime: I have converted the longitude to 'minutes', the local standard time meridian(lstm) derived from the localStandardTimeMeridian method returns a value that is in 'minutes', and the equationOfTime which is also returned in 'minutes'. Using the equation from pveducation, I've calculated the time correction which accounts for the small time variations within a given time zone. When I apply this result and the localTime, each in minutes, to the local solar time (lst) equation, the result is 676.515 (at this moment), which does not make any sense to me. The local solar time, as I understand it, represents the time with respect to the Sun and when it is at its highest point in the sky, locally, is considered solar noon. 676.515 does not make sense. Does anybody understand what might be causing this.

  2. HourAngle: I'm hoping that once I fix the localSolarTime method, this will not need to be corrected.

I've chosen Washington DC for the latitude and longitude. Both the Zenith and Azimuth readings should be positive values, and for my region at this moment, are 66 and 201 respectively.

public class PathOfSun {
    static LocalTime localTime = LocalTime.now();
    static double dcLat = 38.83;
    static double dcLong =  -77.02;
    static DecimalFormat df = new DecimalFormat("#.0");

    public static void main(String [] args) {
        int day = dayOfYear();
        double equationOfTime = equationOfTime(day);
        double lstm = localTimeMeridian();
        double lst = localSolarTime(equationOfTime, dcLong, lstm);
        double declination = declination(day);
        double hourAngle = hourAngle(lst);

        double zenith = zenith(dcLat, declination, hourAngle);
        double azimuth = azimuth(dcLong, declination, zenith, hourAngle); 

    }

    //Longitude of timezone meridian
    public static double localTimeMeridian() {
        TimeZone gmt = TimeZone.getTimeZone("GMT");
        TimeZone est = TimeZone.getTimeZone("EST");
        int td = gmt.getRawOffset() - est.getRawOffset();
        double localStandardTimeMeridian = 15 * (td/(1000*60*60)); //convert td to hours
        //System.out.println("Local Time Meridian: " + localStandardTimeMeridian);
        return localStandardTimeMeridian;
    }

    //Get the number of days since Jan. 1
    public static int dayOfYear() {
        Calendar localCalendar = Calendar.getInstance(TimeZone.getDefault());
        int dayOfYear = localCalendar.get(Calendar.DAY_OF_YEAR); 
        //System.out.println("Day: " + dayOfYear);
        return dayOfYear;
    }

    //Emperical equation to correct the eccentricity of Earth's orbit and axial tilt
    public static double equationOfTime (double day) {
        double d =(360.0/365.0)*(day - 81);
        d = Math.toRadians(d);
        double equationTime = 9.87*sin(2*d)-7.53*cos(d)-1.54*sin(d); 
        //System.out.println("Equation Of Time: " + equationTime);
        return equationTime;
    }
    //The angle between the equator and a line drawn from the center of the Sun(degrees)
    public static double declination(int dayOfYear) {
        double declination = 23.5*sin((Math.toRadians(360.0/365.0))*(dayOfYear - 81));
        //System.out.println("Declination: " + df.format(declination));
        return declination;
    }

    //Add the number of minutes past midnight localtime//
    public static double hourAngle(double localSolarTime) {
        double hourAngle = 15 * (localSolarTime - 13); 
        System.out.println("Hour Angle: " + df.format(hourAngle)); //(degrees)
        return hourAngle;
    }

    //Account for the variation within timezone - increases accuracy
    public static double localSolarTime(double equationOfTime, double longitude, double lstm) { 
        //LocalSolarTime = 4min * (longitude + localStandardTimeMeridian) + equationOfTime
        //Time Correction is time variation within given time zone (minutes)
        //longitude = longitude/60; //convert degrees to arcminutes
        double localStandardTimeMeridian = lstm;
        double timeCorrection = (4 * (longitude + localStandardTimeMeridian) + equationOfTime);
        System.out.println("Time Correction: " + timeCorrection); //(in minutes)
        //localSolarTime represents solar time where noon represents sun's is highest position 
        // in sky and the hour angle is 0 -- hour angle is negative in morning, and positive after solar noon.
        double localSolarTime = (localTime.toSecondOfDay() + (timeCorrection*60)); //(seconds)
        localSolarTime = localSolarTime/(60*60);  //convert from seconds to hours
        //Convert double to Time (HH:mm:ss) for console output
        int hours = (int) Math.floor(localSolarTime);
        int minutes = (int) ((localSolarTime - hours) * 60);
        //-1 for the daylight savings
        Time solarTime = new Time((hours-1), minutes, 0);
        System.out.println("Local Solar Time: " + solarTime); //hours

        return localSolarTime;
    }

    public static double azimuth(double lat, double declination, double zenith, double hourAngle) {
        double azimuthDegree = 0;
        double elevation = 90 - zenith;
        elevation = Math.toRadians(elevation);
        zenith = Math.toRadians(zenith);
        lat = Math.toRadians(lat);
        declination = Math.toRadians(declination);
        hourAngle = Math.round(hourAngle);
        hourAngle = Math.toRadians(hourAngle);

        //double azimuthRadian = -sin(hourAngle)*cos(declination) / cos(elevation);
        double azimuthRadian = ((sin(declination)*cos(lat)) - (cos(hourAngle)*cos(declination)*
                sin(lat)))/cos(elevation);

        //Account for time quadrants
        Calendar cal = Calendar.getInstance();
        int hour = cal.get(Calendar.HOUR_OF_DAY);
        if(hour > 0 && hour < 6) {
        azimuthDegree =  Math.toDegrees(acos(azimuthRadian));
        }
        else if(hour >= 6 && hour < 12) {
            azimuthDegree = Math.toDegrees(acos(azimuthRadian));
            azimuthDegree = 180 - azimuthDegree;
        } else if (hour >= 12 && hour < 18) {
            azimuthDegree = Math.toDegrees(acos(azimuthRadian));
            azimuthDegree = azimuthDegree - 180;
        } else if (hour >= 18 && hour < 24) {
            azimuthDegree = Math.toDegrees(acos(azimuthRadian));
            azimuthDegree = 360 - azimuthDegree;
        }

        System.out.println("Azimuth: " + df.format(azimuthDegree));
        return azimuthDegree;
    }

    public static double zenith(double lat, double declination, double hourAngle) {
        lat = Math.toRadians(lat);
        declination = Math.toRadians(declination);
        hourAngle = Math.round(hourAngle);
        hourAngle = Math.toRadians(hourAngle);
        //Solar Zenith Angle 
        double zenith = Math.toDegrees(acos(sin(lat)*sin(declination) + (cos(lat)*cos(declination)*cos(hourAngle))));
        //Solar Elevation Angle
        double elevation = Math.toDegrees(asin(sin(lat)*sin(declination) + (cos(lat)*cos(declination)*cos(hourAngle))));
        System.out.println("Elevation: " + df.format(elevation));
        System.out.println("Zenith: " + df.format(zenith));
        return zenith;
    }
}

Just to reiterate, the day, local time meridian are exactly correct, and the equation of time and declination are accurate but not exact. ----UPDATE OUTPUT---- new output

sensor program

-----UPDATE----- Used the scatterchart to display the sun's elevation/azimuth throughout day. I am still having trouble figuring out the azimuth output. It is correct for long time, but then it will change from increasing and start to decrease (~270-->0). I will be sure to update the code once I finally get the output right.

1 个答案:

答案 0 :(得分:3)

您将经度传递给localSolarTime()作为度数,然后将其除以60,并附上一条评论,声称这是为了转换为弧分。这是错的;你以后的计算需要度数,即使你需要几分钟的弧度,你也要乘以60而不是除数。

这种误差导致经度为-1.3°,当您找到当地时间子午线与您的位置之间的角度时,您会得到一个大角度(约75°)。它应该是一个小角度,通常为±7.5°。大角度会导致大量时间校正,并将所有内容都抛弃。

更新:azimuth()方法的更新版本中,象限选择应基于太阳的小时角,或等效地,基于当地太阳时,而不是标准挂钟时间。并且,所有计算中使用的小时角度不应四舍五入。该方法可能如下所示,而不是测试四个不同的象限:

public static double azimuth(double lat, double declination, double zenith, double hourAngle)
{
  double elevation = Math.toRadians(90 - zenith);
  lat = Math.toRadians(lat);
  declination = Math.toRadians(declination);
  hourAngle = Math.toRadians(hourAngle);
  double azimuthRadian = acos(((sin(declination) * cos(lat)) - (cos(hourAngle) * cos(declination) * sin(lat))) / cos(elevation));
  double azimuthDegree = Math.toDegrees(azimuthRadian);
  if (hourAngle > 0)
    azimuthDegree = 360 - azimuthDegree;
  System.out.println("Azimuth: " + df.format(azimuthDegree));
  return azimuthDegree;
}

最后,您将dcLong作为lat方法的azimuth()参数传递;这应该是dcLat

我建议在内部使用弧度,并且只在输入和输出上转换和转换。这有助于防止错误,并减少舍入错误和不必要的混乱。