File indexing completed on 2024-04-21 03:45:00

0001 /*
0002     SPDX-FileCopyrightText: 2009 Prakash Mohan <prakash.mohan@kdemail.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "ksalmanac.h"
0008 
0009 #include "geolocation.h"
0010 #include "ksnumbers.h"
0011 #include "kstarsdata.h"
0012 
0013 KSAlmanac::KSAlmanac()
0014 {
0015     KStarsData *data = KStarsData::Instance();
0016 
0017     /*dt  = KStarsDateTime::currentDateTime();
0018     geo = data->geo();
0019     dt.setTime(QTime());
0020     dt  = geo->LTtoUT(dt);*/
0021 
0022     // Jasem 2015-08-24 Do NOT use KStarsDataTime for local time, it is only for UTC
0023     QDateTime midnight = QDateTime(data->lt().date(), QTime());
0024     geo                = data->geo();
0025     dt                 = geo->LTtoUT(KStarsDateTime(midnight));
0026     update();
0027 }
0028 
0029 KSAlmanac::KSAlmanac(const KStarsDateTime &midnight, const GeoLocation *g)
0030 {
0031     geo = g ? g : KStarsData::Instance()->geo();
0032 
0033     dt = midnight.isValid() ?
0034                 midnight.timeSpec() == Qt::LocalTime ?
0035                     geo->LTtoUT(midnight) :
0036                     midnight :
0037                 KStarsData::Instance()->ut();
0038 
0039     update();
0040 }
0041 
0042 void KSAlmanac::update()
0043 {
0044     RiseSetTime(&m_Sun, &SunRise, &SunSet, &SunRiseT, &SunSetT);
0045     RiseSetTime(&m_Moon, &MoonRise, &MoonSet, &MoonRiseT, &MoonSetT);
0046     //    qDebug() << Q_FUNC_INFO << "Sun rise: " << SunRiseT.toString() << " Sun set: " << SunSetT.toString() << " Moon rise: " << MoonRiseT.toString() << " Moon set: " << MoonSetT.toString();
0047     findDawnDusk();
0048     findMoonPhase();
0049 }
0050 
0051 void KSAlmanac::RiseSetTime(SkyObject *o, double *riseTime, double *setTime, QTime *RiseTime, QTime *SetTime)
0052 {
0053     // Compute object rise and set times
0054     const KStarsDateTime today = dt;
0055     const GeoLocation *_geo    = geo;
0056     *RiseTime                  = o->riseSetTime(
0057         today, _geo,
0058         true); // FIXME: Should we add a day here so that we report future rise time? Not doing so produces the right results for the moon. Not sure about the sun.
0059     *SetTime  = o->riseSetTime(today, _geo, false);
0060     *riseTime = -1.0 * RiseTime->secsTo(QTime(0, 0, 0, 0)) / 86400.0;
0061     *setTime  = -1.0 * SetTime->secsTo(QTime(0, 0, 0, 0)) / 86400.0;
0062 
0063     // Check to see if the object is circumpolar
0064     // NOTE: Since we are working on a local copy of the Sun / Moon,
0065     //       we freely change the geolocation / time without setting
0066     //       them back.
0067 
0068     KSNumbers num(dt.djd());
0069     CachingDms LST = geo->GSTtoLST(dt.gst());
0070     o->updateCoords(&num, true, geo->lat(), &LST, true);
0071     if (o->checkCircumpolar(geo->lat()))
0072     {
0073         if (o->alt().Degrees() > 0.0)
0074         {
0075             //Circumpolar, signal it this way:
0076             *riseTime = 0.0;
0077             *setTime  = 1.0;
0078         }
0079         else
0080         {
0081             //never rises, signal it this way:
0082             *riseTime = 0.0;
0083             *setTime  = -1.0;
0084         }
0085     }
0086 }
0087 
0088 void KSAlmanac::findDawnDusk(double altitude)
0089 {
0090     KStarsDateTime today = dt;
0091     KSNumbers num(today.djd());
0092     CachingDms LST = geo->GSTtoLST(today.gst());
0093 
0094     // Relocate our local Sun to this almanac time - local midnight
0095     m_Sun.updateCoords(&num, true, geo->lat(), &LST, true);
0096 
0097     // Granularity
0098     int const h_inc = 5;
0099 
0100     // Compute the altitude of the Sun twelve hours before this almanac time
0101     int const start_h = -1200, end_h = +1200;
0102     double last_alt = findAltitude(&m_Sun, start_h/100.0);
0103 
0104     int dawn = -1300, dusk = -1300, min_alt_time = -1300;
0105     double max_alt = -100.0, min_alt = +100.0;
0106 
0107     // Compute the dawn and dusk times in a [-12,+12] hours around the day midnight of this almanac as well as min and max altitude
0108     // See the header comment about dawn and dusk positions
0109     for (int h = start_h + h_inc; h <= end_h; h += h_inc)
0110     {
0111         // Compute the Sun's altitude in an increasing hour interval
0112         double const alt = findAltitude(&m_Sun, h/100.0);
0113 
0114         // Deduce whether the Sun is rising or setting
0115         bool const rising = alt - last_alt > 0;
0116 
0117         // Extend min/max altitude interval, push minimum time down
0118         if (max_alt < alt)
0119         {
0120             max_alt = alt;
0121         }
0122         else if (alt < min_alt)
0123         {
0124             min_alt = alt;
0125             min_alt_time = h;
0126         }
0127 
0128         // Dawn is when the Sun is rising and crosses an altitude of -18 degrees
0129         if (dawn < 0)
0130             if (rising)
0131                 if (last_alt <= altitude && altitude <= alt)
0132                     dawn = h - h_inc * (alt == last_alt ? 0 : (alt - altitude)/(alt - last_alt));
0133 
0134         // Dusk is when the Sun is setting and crosses an altitude of -18 degrees
0135         if (dusk < 0)
0136             if (!rising)
0137                 if (last_alt >= altitude && altitude >= alt)
0138                     dusk = h - h_inc * (alt == last_alt ? 0 : (alt - altitude)/(alt - last_alt));
0139 
0140         last_alt = alt;
0141     }
0142 
0143     // If the Sun did not cross the astronomical twilight altitude, use the minimal altitude
0144     if (dawn < start_h || dusk < start_h)
0145     {
0146         DawnAstronomicalTwilight = static_cast <double> (min_alt_time) / 2400.0;
0147         DuskAstronomicalTwilight = static_cast <double> (min_alt_time) / 2400.0;
0148     }
0149     // If the Sun did cross the astronomical twilight, use the computed time offsets
0150     else
0151     {
0152         DawnAstronomicalTwilight = static_cast <double> (dawn) / 2400.0;
0153         DuskAstronomicalTwilight = static_cast <double> (dusk) / 2400.0;
0154     }
0155 
0156     SunMaxAlt = max_alt;
0157     SunMinAlt = min_alt;
0158 }
0159 
0160 void KSAlmanac::findMoonPhase()
0161 {
0162     const KStarsDateTime today = dt;
0163     KSNumbers num(today.djd());
0164     CachingDms LST = geo->GSTtoLST(today.gst());
0165 
0166     m_Sun.updateCoords(&num, true, geo->lat(), &LST, true); // We can abuse our own copy of the sun and/or moon
0167     m_Moon.updateCoords(&num, true, geo->lat(), &LST, true);
0168     m_Moon.findPhase(&m_Sun);
0169     MoonPhase = m_Moon.phase().Degrees();
0170 }
0171 
0172 void KSAlmanac::setDate(const KStarsDateTime &utc_midnight)
0173 {
0174     dt = utc_midnight;
0175     update();
0176 }
0177 
0178 void KSAlmanac::setLocation(const GeoLocation *geo_)
0179 {
0180     geo = geo_;
0181     update();
0182 }
0183 
0184 double KSAlmanac::sunZenithAngleToTime(double z) const
0185 {
0186     // TODO: Correct for movement of the sun
0187     double HA       = acos((cos(z * dms::DegToRad) - m_Sun.dec().sin() * geo->lat()->sin()) /
0188                      (m_Sun.dec().cos() * geo->lat()->cos()));
0189     double HASunset = acos((-m_Sun.dec().sin() * geo->lat()->sin()) / (m_Sun.dec().cos() * geo->lat()->cos()));
0190     return SunSet + (HA - HASunset) / 24.0;
0191 }
0192 
0193 double KSAlmanac::findAltitude(const SkyPoint *p, double hour)
0194 {
0195     return SkyPoint::findAltitude(p, dt, geo, hour).Degrees();
0196 }