太阳路径的计算

11
我正在编写计算太阳经过特定点路径所需的几种方法。我已使用两个不同的来源进行计算,但都未得到期望的结果。这些来源是:http://www.pveducation.org/pvcdrom/properties-of-sunlight/suns-positionhttp://www.esrl.noaa.gov/gmd/grad/solcalc/solareqns.PDF 注意:度数转换为弧分是Deg * 60 min。
  1. 本地太阳时间:我已将经度转换为“分钟”,从localStandardTimeMeridian方法中得出的本地标准时间子午线(lstm)返回一个“分钟”值,而equationOfTime也以“分钟”返回。使用pveducation的公式,我计算了时间修正值,它考虑了给定时区内的小时间变化。当我将此结果和本地时间(每个都以分钟为单位)应用于本地太阳时间(lst)方程式时,结果是676.515(此时此刻),这对我来说毫无意义。据我理解,本地太阳时间代表相对于太阳的时间,并且当太阳在天空中达到最高点时,被视为太阳正午。676.515没有意义。有人明白可能是什么原因吗。

  2. 时角:我希望一旦我修复了localSolarTime方法,就不需要进行更正。

我选择了华盛顿特区的纬度和经度。天顶角和方位角读数都应该是正值,在我所在地区此时此刻分别为66和201。

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;
    }
}

再次强调,日期、当地时间子午线是完全正确的,而时间方程和赤纬是准确但不精确的。

----更新输出---- new output

sensor program

-----更新----- 使用散点图显示太阳在一天中的高度/方位角。我仍然有困难弄清楚方位角输出。它长时间正确,但然后它会从增加变为减少(~270-->0)。一旦我最终获得正确的输出,我一定会更新代码。


1
由于措辞不清,很难理解确切的问题所在。你说它应该生成分钟,并以分钟为单位给出结果;为什么呢?你说这两个值应该是正数,然后给出了正数的结果;为什么呢?你是否调试过这个程序来关联输入和输出? - ChiefTwoPencils
你的zenith()和azimuth()函数不符合你提供的引文中给出的公式。你是否尝试简化它们了? - BadZen
另外,请注意,您需要对结果进行模运算[0,2*Pi],以获得一个“标准化”的(非负)角度。 - BadZen
@BadZen,抱歉,我想我一定是重写了方程式。我会更新帖子以澄清。 - wellington
这个任务还有其他方法可以解决,比如计算太阳在地平线下/上X度时的时间,同时这个也可能会有所帮助根据日出/日落时间计算是否接近黎明/黄昏? - Spektre
1个回答

3
你需要将经度作为角度传递到 localSolarTime() 方法中,然后将其除以60,注释称这是为了转换为弧度的分钟。这是错误的;你后面的计算需要的是角度,即使你需要弧度的分钟,你也应该乘以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;
}

最后,在`azimuth()`方法中作为`lat`参数传递了`dcLong`,应该要换成`dcLat`。
我建议在内部使用弧度,并只在输入和输出时进行角度转换。这样将有助于防止错误,并减少四舍五入误差和不必要的混乱。

感谢Erickson指出这一点。我会调整代码,希望它能产生更令人愉悦的结果! - wellington
我已经处理了这个问题,现在方法都正确了,除了最终的计算——方位角不正确。在早上,方位角范围是0-180度(从0到12点),下午是180-360度(从12到24点)。鉴于此,我需要调整acos()内部的值来考虑单位圆的每个象限中符号的变化。我尝试使用了两个不同的方程,都来自http://en.wikipedia.org/wiki/Solar_azimuth_angle,但第二个方程的输出产生了一个“?”。第一个方程非常接近,但与实际相差约10度。 - wellington
@wellington 如果您需要额外的帮助,请在您的帖子中添加更新,包括您当前的方位角方法、样本输入和期望结果。 - erickson
你有什么建议可以帮助我减少代码量吗?我感觉自己像个穴居人,我的编码没有太多的技巧。 :) 我感激你的帮助。 - wellington
1
你最后的问题是在将经度传递给方位角方法时,而不是预期的纬度。至于减少代码量,你正在做一些不必要的工作。例如,方位角中的四个if-else情况应该是return (hour > 0) ? 360 - azimuth : azimuth;并且你应该在内部处理所有弧度,仅在需要时将其转换为度数进行输入和输出。始终使用java.time API:OffsetDateTimeLocalTimeDuration。编写一些单元测试。编写测试以使用硬编码时间而不是now() - erickson
是的,我发现了使用错误时间值(即hourAngle)来计算方位角度的错误。这确实有助于产生更好的值。目前,我使用180而不是360,以获得与实际匹配的方位值。这可能是因为方向的原因吧?:http://www.pveducation.org/pvcdrom/properties-of-sunlight/azimuth-angle 到目前为止,这个方法还有效,但在关键时刻(当hourAngle < 0时)我们会再看看。 只是进行度数转换以进行故障排除。 Erickson提供的信息真的很有帮助,非常感谢你的帮助! - wellington

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接