@@ -284,11 +284,13 @@ private static double getEquationOfTime(double julianCenturies) {
284
284
double geomMeanAnomalySun = getSunGeometricMeanAnomaly (julianCenturies );
285
285
double y = Math .tan (Math .toRadians (epsilon ) / 2.0 );
286
286
y *= y ;
287
- double sin2l0 = Math .sin (2.0 * Math .toRadians (geomMeanLongSun ));
288
- double sinm = Math .sin (Math .toRadians (geomMeanAnomalySun ));
289
- double cos2l0 = Math .cos (2.0 * Math .toRadians (geomMeanLongSun ));
290
- double sin4l0 = Math .sin (4.0 * Math .toRadians (geomMeanLongSun ));
291
- double sin2m = Math .sin (2.0 * Math .toRadians (geomMeanAnomalySun ));
287
+ double geomMeanLongSunRad = Math .toRadians (geomMeanLongSun );
288
+ double geomMeanAnomalySunRad = Math .toRadians (geomMeanAnomalySun );
289
+ double sin2l0 = Math .sin (2.0 * geomMeanLongSunRad );
290
+ double sinm = Math .sin (geomMeanAnomalySunRad );
291
+ double cos2l0 = Math .cos (2.0 * geomMeanLongSunRad );
292
+ double sin4l0 = Math .sin (4.0 * geomMeanLongSunRad );
293
+ double sin2m = Math .sin (2.0 * geomMeanAnomalySunRad );
292
294
double equationOfTime = y * sin2l0 - 2.0 * eccentricityEarthOrbit * sinm + 4.0 * eccentricityEarthOrbit * y
293
295
* sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * eccentricityEarthOrbit * eccentricityEarthOrbit * sin2m ;
294
296
return Math .toDegrees (equationOfTime ) * 4.0 ;
@@ -311,7 +313,8 @@ private static double getEquationOfTime(double julianCenturies) {
311
313
private static double getSunHourAngle (double latitude , double solarDeclination , double zenith , SolarEvent solarEvent ) {
312
314
double latRad = Math .toRadians (latitude );
313
315
double sdRad = Math .toRadians (solarDeclination );
314
- double hourAngle = (Math .acos (Math .cos (Math .toRadians (zenith )) / (Math .cos (latRad ) * Math .cos (sdRad ))
316
+ double zRad = Math .toRadians (zenith );
317
+ double hourAngle = (Math .acos (Math .cos (zRad ) / (Math .cos (latRad ) * Math .cos (sdRad ))
315
318
- Math .tan (latRad ) * Math .tan (sdRad )));
316
319
317
320
if (solarEvent == SolarEvent .SUNSET ) {
@@ -493,19 +496,166 @@ private static double getSunRiseSetUTC(Calendar calendar, double latitude, doubl
493
496
double equationOfTime = getEquationOfTime (tnoon );
494
497
double solarDeclination = getSunDeclination (tnoon );
495
498
double hourAngle = getSunHourAngle (latitude , solarDeclination , zenith , solarEvent );
499
+ if (Double .isNaN (hourAngle )) {
500
+ hourAngle = interpolateHourAngle1 (julianDay , latitude , longitude , zenith , solarEvent );
501
+ if (Double .isNaN (hourAngle )) {
502
+ return Double .NaN ;
503
+ }
504
+ }
496
505
double delta = longitude - Math .toDegrees (hourAngle );
497
506
double timeDiff = 4 * delta ;
498
507
double timeUTC = 720 + timeDiff - equationOfTime ;
499
508
500
509
// Second pass includes fractional Julian Day in gamma calc
501
510
double newt = getJulianCenturiesFromJulianDay (julianDay + timeUTC / 1440.0 );
502
511
equationOfTime = getEquationOfTime (newt );
503
-
512
+
504
513
solarDeclination = getSunDeclination (newt );
505
514
hourAngle = getSunHourAngle (latitude , solarDeclination , zenith , solarEvent );
515
+ if (Double .isNaN (hourAngle )) {
516
+ hourAngle = interpolateHourAngle2 (julianDay , latitude , longitude , zenith , solarEvent );
517
+ if (Double .isNaN (hourAngle )) {
518
+ return Double .NaN ;
519
+ }
520
+ }
506
521
delta = longitude - Math .toDegrees (hourAngle );
507
522
timeDiff = 4 * delta ;
508
523
timeUTC = 720 + timeDiff - equationOfTime ;
509
524
return timeUTC ;
510
525
}
526
+
527
+ private static double getSunHourAngle1 (double julianDay , double latitude , double longitude , double zenith , SolarEvent solarEvent ) {
528
+ double noonmin = getSolarNoonMidnightUTC (julianDay , longitude , SolarEvent .NOON );
529
+ double tnoon = getJulianCenturiesFromJulianDay (julianDay + noonmin / 1440.0 );
530
+ double solarDeclination = getSunDeclination (tnoon );
531
+ return getSunHourAngle (latitude , solarDeclination , zenith , solarEvent );
532
+ }
533
+
534
+ private static double getSunHourAngle2 (double julianDay , double latitude , double longitude , double zenith , SolarEvent solarEvent ) {
535
+ double noonmin = getSolarNoonMidnightUTC (julianDay , longitude , SolarEvent .NOON );
536
+ double tnoon = getJulianCenturiesFromJulianDay (julianDay + noonmin / 1440.0 );
537
+
538
+ // First calculates sunrise and approximate length of day
539
+ double equationOfTime = getEquationOfTime (tnoon );
540
+ double solarDeclination = getSunDeclination (tnoon );
541
+ double hourAngle = getSunHourAngle (latitude , solarDeclination , zenith , solarEvent );
542
+ if (Double .isNaN (hourAngle )) {
543
+ return Double .NaN ;
544
+ }
545
+ double delta = longitude - Math .toDegrees (hourAngle );
546
+ double timeDiff = 4 * delta ;
547
+ double timeUTC = 720 + timeDiff - equationOfTime ;
548
+
549
+ // Second pass includes fractional Julian Day in gamma calc
550
+ double newt = getJulianCenturiesFromJulianDay (julianDay + timeUTC / 1440.0 );
551
+
552
+ solarDeclination = getSunDeclination (newt );
553
+ return getSunHourAngle (latitude , solarDeclination , zenith , solarEvent );
554
+ }
555
+
556
+ /**
557
+ * Use linear interpolation to calculate a missing angle.
558
+ */
559
+ private static double interpolateHourAngle1 (double julianDay , double latitude , double longitude , double zenith , SolarEvent solarEvent ) {
560
+ double hourAngle ;
561
+ double x1 = 0 ;
562
+ double y1 = 0 ;
563
+ double x2 = 0 ;
564
+ double y2 = 0 ;
565
+
566
+ double d = julianDay - 1 ;
567
+ final double dayFirst = Math .max (julianDay - 366 , 1 );
568
+ while (d >= dayFirst ) {
569
+ hourAngle = getSunHourAngle1 (d , latitude , longitude , zenith , solarEvent );
570
+
571
+ if (!Double .isNaN (hourAngle )) {
572
+ x1 = d ;
573
+ y1 = hourAngle ;
574
+ break ;
575
+ }
576
+ d --;
577
+ }
578
+
579
+ d = julianDay + 1 ;
580
+ final double dayLast = julianDay + 366 ;
581
+ while (d <= dayLast ) {
582
+ hourAngle = getSunHourAngle1 (d , latitude , longitude , zenith , solarEvent );
583
+
584
+ if (!Double .isNaN (hourAngle )) {
585
+ if (x1 == 0 ) {
586
+ x1 = d ;
587
+ y1 = hourAngle ;
588
+ d ++;
589
+ continue ;
590
+ }
591
+ x2 = d ;
592
+ y2 = hourAngle ;
593
+ break ;
594
+ }
595
+ d ++;
596
+ }
597
+
598
+ if ((x1 == 0 ) || (x2 == 0 )) {
599
+ return Double .NaN ;
600
+ }
601
+ double dx = x2 - x1 ;
602
+ if (dx == 0 ) {
603
+ return Double .NaN ;
604
+ }
605
+ double dy = y2 - y1 ;
606
+ return y1 + ((julianDay - x1 ) * dy / dx );
607
+ }
608
+
609
+ /**
610
+ * Use linear interpolation to calculate a missing angle.
611
+ */
612
+ private static double interpolateHourAngle2 (double julianDay , double latitude , double longitude , double zenith , SolarEvent solarEvent ) {
613
+ double hourAngle ;
614
+ double x1 = 0 ;
615
+ double y1 = 0 ;
616
+ double x2 = 0 ;
617
+ double y2 = 0 ;
618
+
619
+ double d = julianDay - 1 ;
620
+ final double dayFirst = Math .max (julianDay - 366 , 1 );
621
+ while (d >= dayFirst ) {
622
+ hourAngle = getSunHourAngle2 (d , latitude , longitude , zenith , solarEvent );
623
+
624
+ if (!Double .isNaN (hourAngle )) {
625
+ x1 = d ;
626
+ y1 = hourAngle ;
627
+ break ;
628
+ }
629
+ d --;
630
+ }
631
+
632
+ d = julianDay + 1 ;
633
+ final double dayLast = julianDay + 366 ;
634
+ while (d <= dayLast ) {
635
+ hourAngle = getSunHourAngle2 (d , latitude , longitude , zenith , solarEvent );
636
+
637
+ if (!Double .isNaN (hourAngle )) {
638
+ if (x1 == 0 ) {
639
+ x1 = d ;
640
+ y1 = hourAngle ;
641
+ d ++;
642
+ continue ;
643
+ }
644
+ x2 = d ;
645
+ y2 = hourAngle ;
646
+ break ;
647
+ }
648
+ d ++;
649
+ }
650
+
651
+ if ((x1 == 0 ) || (x2 == 0 )) {
652
+ return Double .NaN ;
653
+ }
654
+ double dx = x2 - x1 ;
655
+ if (dx == 0 ) {
656
+ return Double .NaN ;
657
+ }
658
+ double dy = y2 - y1 ;
659
+ return y1 + ((julianDay - x1 ) * dy / dx );
660
+ }
511
661
}
0 commit comments