File indexing completed on 2024-04-21 03:44:53
0001 /* 0002 SPDX-FileCopyrightText: 2007 Jason Harris <kstars@30doradus.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "modcalcvizequinox.h" 0008 0009 #include "dms.h" 0010 #include "kstarsdata.h" 0011 #include "ksnotification.h" 0012 #include "skyobjects/kssun.h" 0013 #include "widgets/dmsbox.h" 0014 0015 #include <KLineEdit> 0016 #include <KPlotAxis> 0017 #include <KPlotObject> 0018 #include <KPlotPoint> 0019 0020 #include <cmath> 0021 #include <QtMath> 0022 0023 modCalcEquinox::modCalcEquinox(QWidget *parentSplit) : QFrame(parentSplit), dSpring(), dSummer(), dAutumn(), dWinter() 0024 { 0025 setupUi(this); 0026 0027 connect(Year, SIGNAL(valueChanged(int)), this, SLOT(slotCompute())); 0028 connect(InputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles())); 0029 connect(OutputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles())); 0030 connect(RunButtonBatch, SIGNAL(clicked()), this, SLOT(slotRunBatch())); 0031 connect(ViewButtonBatch, SIGNAL(clicked()), this, SLOT(slotViewBatch())); 0032 0033 Plot->axis(KPlotWidget::LeftAxis)->setLabel(i18n("Sun's Declination")); 0034 Plot->setTopPadding(40); 0035 //Don't draw Top & Bottom axes; custom axes drawn as plot objects 0036 Plot->axis(KPlotWidget::BottomAxis)->setVisible(false); 0037 Plot->axis(KPlotWidget::TopAxis)->setVisible(false); 0038 0039 //This will call slotCompute(): 0040 Year->setValue(KStarsData::Instance()->lt().date().year()); 0041 0042 RunButtonBatch->setEnabled(false); 0043 ViewButtonBatch->setEnabled(false); 0044 0045 show(); 0046 } 0047 0048 double modCalcEquinox::dmonth(int i) 0049 { 0050 Q_ASSERT(i >= 0 && i < 12 && "Month must be in 0 .. 11 range"); 0051 return DMonth[i]; 0052 } 0053 0054 void modCalcEquinox::slotCheckFiles() 0055 { 0056 RunButtonBatch->setEnabled(!InputFileBatch->lineEdit()->text().isEmpty() && 0057 !OutputFileBatch->lineEdit()->text().isEmpty()); 0058 } 0059 0060 void modCalcEquinox::slotRunBatch() 0061 { 0062 QString inputFileName = InputFileBatch->url().toLocalFile(); 0063 0064 if (QFile::exists(inputFileName)) 0065 { 0066 QFile f(inputFileName); 0067 if (!f.open(QIODevice::ReadOnly)) 0068 { 0069 KSNotification::sorry(i18n("Could not open file %1.", f.fileName()), i18n("Could Not Open File")); 0070 inputFileName.clear(); 0071 return; 0072 } 0073 0074 QTextStream istream(&f); 0075 processLines(istream); 0076 0077 ViewButtonBatch->setEnabled(true); 0078 0079 f.close(); 0080 } 0081 else 0082 { 0083 KSNotification::sorry(i18n("Invalid file: %1", inputFileName), i18n("Invalid file")); 0084 inputFileName.clear(); 0085 return; 0086 } 0087 } 0088 0089 void modCalcEquinox::processLines(QTextStream &istream) 0090 { 0091 QFile fOut(OutputFileBatch->url().toLocalFile()); 0092 fOut.open(QIODevice::WriteOnly); 0093 QTextStream ostream(&fOut); 0094 int originalYear = Year->value(); 0095 0096 //Write header to output file 0097 ostream << i18n("# Timing of Equinoxes and Solstices\n") << i18n("# computed by KStars\n#\n") 0098 << i18n("# Vernal Equinox\t\tSummer Solstice\t\t\tAutumnal Equinox\t\tWinter Solstice\n#\n"); 0099 0100 while (!istream.atEnd()) 0101 { 0102 QString line = istream.readLine(); 0103 bool ok = false; 0104 int year = line.toInt(&ok); 0105 0106 //for now I will simply change the value of the Year widget to trigger 0107 //computation of the Equinoxes and Solstices. 0108 if (ok) 0109 { 0110 //triggers slotCompute(), which sets values of dSpring et al.: 0111 Year->setValue(year); 0112 0113 //Write to output file 0114 ostream << QLocale().toString(dSpring.date(), QLocale::LongFormat) << "\t" 0115 << QLocale().toString(dSummer.date(), QLocale::LongFormat) << "\t" 0116 << QLocale().toString(dAutumn.date(), QLocale::LongFormat) << "\t" 0117 << QLocale().toString(dWinter.date(), QLocale::LongFormat) << '\n'; 0118 } 0119 } 0120 0121 if (Year->value() != originalYear) 0122 Year->setValue(originalYear); 0123 } 0124 0125 void modCalcEquinox::slotViewBatch() 0126 { 0127 QFile fOut(OutputFileBatch->url().toLocalFile()); 0128 fOut.open(QIODevice::ReadOnly); 0129 QTextStream istream(&fOut); 0130 QStringList text; 0131 0132 while (!istream.atEnd()) 0133 text.append(istream.readLine()); 0134 0135 fOut.close(); 0136 0137 KMessageBox::informationList(nullptr, i18n("Results of Sidereal time calculation"), text, 0138 OutputFileBatch->url().toLocalFile()); 0139 } 0140 0141 void modCalcEquinox::slotCompute() 0142 { 0143 KStarsData *data = KStarsData::Instance(); 0144 KSSun Sun; 0145 int year0 = Year->value(); 0146 0147 KStarsDateTime dt(QDate(year0, 1, 1), QTime(0, 0, 0)); 0148 long double jd0 = dt.djd(); //save JD on Jan 1st 0149 for (int imonth = 0; imonth < 12; imonth++) 0150 { 0151 KStarsDateTime kdt(QDate(year0, imonth + 1, 1), QTime(0, 0, 0)); 0152 DMonth[imonth] = kdt.djd() - jd0; 0153 } 0154 0155 Plot->removeAllPlotObjects(); 0156 0157 //Add the celestial equator, just a single line bisecting the plot horizontally 0158 KPlotObject *ce = new KPlotObject(data->colorScheme()->colorNamed("EqColor"), KPlotObject::Lines, 2.0); 0159 ce->addPoint(0.0, 0.0); 0160 ce->addPoint(366.0, 0.0); 0161 Plot->addPlotObject(ce); 0162 0163 // Tropic of cancer 0164 KPlotObject *tcan = new KPlotObject(data->colorScheme()->colorNamed("EqColor"), KPlotObject::Lines, 2.0); 0165 tcan->addPoint(0.0, 23.5); 0166 tcan->addPoint(366.0, 23.5); 0167 Plot->addPlotObject(tcan); 0168 0169 // Tropic of capricorn 0170 KPlotObject *tcap = new KPlotObject(data->colorScheme()->colorNamed("EqColor"), KPlotObject::Lines, 2.0); 0171 tcap->addPoint(0.0, -23.5); 0172 tcap->addPoint(366.0, -23.5); 0173 Plot->addPlotObject(tcap); 0174 0175 //Add Ecliptic. This is more complicated than simply incrementing the 0176 //ecliptic longitude, because we want the x-axis to be time, not RA. 0177 //For each day in the year, compute the Sun's position. 0178 KPlotObject *ecl = new KPlotObject(data->colorScheme()->colorNamed("EclColor"), KPlotObject::Lines, 2); 0179 ecl->setLinePen(QPen(ecl->pen().color(), 4)); 0180 0181 Plot->setLimits(1.0, double(dt.date().daysInYear()), -30.0, 30.0); 0182 0183 //Add top and bottom axis lines, and custom tickmarks at each month 0184 addDateAxes(); 0185 0186 for (int i = 1; i <= dt.date().daysInYear(); i++) 0187 { 0188 KSNumbers num(dt.djd()); 0189 Sun.findPosition(&num); 0190 ecl->addPoint(double(i), Sun.dec().Degrees()); 0191 0192 dt = dt.addDays(1); 0193 } 0194 Plot->addPlotObject(ecl); 0195 0196 // Calculate dates for all solstices and equinoxes 0197 findSolsticeAndEquinox(Year->value()); 0198 0199 //Display the Date/Time of each event in the text fields 0200 VEquinox->setText(QLocale().toString(dSpring, QLocale::LongFormat)); 0201 SSolstice->setText(QLocale().toString(dSummer, QLocale::LongFormat)); 0202 AEquinox->setText(QLocale().toString(dAutumn, QLocale::LongFormat)); 0203 WSolstice->setText(QLocale().toString(dWinter, QLocale::LongFormat)); 0204 0205 //Add vertical dotted lines at times of the equinoxes and solstices 0206 KPlotObject *poSpring = new KPlotObject(Qt::white, KPlotObject::Lines, 1); 0207 poSpring->setLinePen(QPen(Qt::white, 1.0, Qt::DotLine)); 0208 poSpring->addPoint(dSpring.djd() - jd0, Plot->dataRect().top()); 0209 poSpring->addPoint(dSpring.djd() - jd0, Plot->dataRect().bottom()); 0210 Plot->addPlotObject(poSpring); 0211 KPlotObject *poSummer = new KPlotObject(Qt::white, KPlotObject::Lines, 1); 0212 poSummer->setLinePen(QPen(Qt::white, 1.0, Qt::DotLine)); 0213 poSummer->addPoint(dSummer.djd() - jd0, Plot->dataRect().top()); 0214 poSummer->addPoint(dSummer.djd() - jd0, Plot->dataRect().bottom()); 0215 Plot->addPlotObject(poSummer); 0216 KPlotObject *poAutumn = new KPlotObject(Qt::white, KPlotObject::Lines, 1); 0217 poAutumn->setLinePen(QPen(Qt::white, 1.0, Qt::DotLine)); 0218 poAutumn->addPoint(dAutumn.djd() - jd0, Plot->dataRect().top()); 0219 poAutumn->addPoint(dAutumn.djd() - jd0, Plot->dataRect().bottom()); 0220 Plot->addPlotObject(poAutumn); 0221 KPlotObject *poWinter = new KPlotObject(Qt::white, KPlotObject::Lines, 1); 0222 poWinter->setLinePen(QPen(Qt::white, 1.0, Qt::DotLine)); 0223 poWinter->addPoint(dWinter.djd() - jd0, Plot->dataRect().top()); 0224 poWinter->addPoint(dWinter.djd() - jd0, Plot->dataRect().bottom()); 0225 Plot->addPlotObject(poWinter); 0226 } 0227 0228 //Add custom top/bottom axes with tickmarks for each month 0229 void modCalcEquinox::addDateAxes() 0230 { 0231 KPlotObject *poTopAxis = new KPlotObject(Qt::white, KPlotObject::Lines, 1); 0232 poTopAxis->addPoint(0.0, Plot->dataRect().bottom()); //y-axis is reversed! 0233 poTopAxis->addPoint(366.0, Plot->dataRect().bottom()); 0234 Plot->addPlotObject(poTopAxis); 0235 0236 KPlotObject *poBottomAxis = new KPlotObject(Qt::white, KPlotObject::Lines, 1); 0237 poBottomAxis->addPoint(0.0, Plot->dataRect().top() + 0.02); 0238 poBottomAxis->addPoint(366.0, Plot->dataRect().top() + 0.02); 0239 Plot->addPlotObject(poBottomAxis); 0240 0241 //Tick mark for each month 0242 for (int imonth = 0; imonth < 12; imonth++) 0243 { 0244 KPlotObject *poMonth = new KPlotObject(Qt::white, KPlotObject::Lines, 1); 0245 poMonth->addPoint(dmonth(imonth), Plot->dataRect().top()); 0246 poMonth->addPoint(dmonth(imonth), Plot->dataRect().top() + 1.4); 0247 Plot->addPlotObject(poMonth); 0248 poMonth = new KPlotObject(Qt::white, KPlotObject::Lines, 1); 0249 poMonth->addPoint(dmonth(imonth), Plot->dataRect().bottom()); 0250 poMonth->addPoint(dmonth(imonth), Plot->dataRect().bottom() - 1.4); 0251 Plot->addPlotObject(poMonth); 0252 } 0253 } 0254 0255 // Calculate and store dates for all solstices and equinoxes 0256 void modCalcEquinox::findSolsticeAndEquinox(uint32_t year) 0257 { 0258 // All the magic numbers are taken from Meeus - 1991 chapter 27 0259 qreal m, jdme[4]; 0260 if(year > 1000) 0261 { 0262 m = (year-2000.0) / 1000.0; 0263 jdme[0] = (-0.00057 * qPow(m, 4)) + (-0.00411 * qPow(m, 3)) + (0.05169 * qPow(m, 2)) + (365242.37404 * m) + 2451623.80984; 0264 jdme[1] = (-0.0003 * qPow(m, 4)) + (0.00888 * qPow(m, 3)) + (0.00325 * qPow(m, 2)) + (365241.62603 * m) + 2451716.56767; 0265 jdme[2] = (0.00078 * qPow(m, 4)) + (0.00337 * qPow(m, 3)) + (-0.11575 * qPow(m, 2)) + (365242.01767 * m) + 2451810.21715; 0266 jdme[3] = (0.00032 * qPow(m, 4)) + (-0.00823 * qPow(m, 3)) + (-0.06223 * qPow(m, 2)) + (365242.74049 * m) + 2451900.05952; 0267 } 0268 else 0269 { 0270 m = year / 1000.0; 0271 jdme[0] = (-0.00071 * qPow(m, 4)) + (0.00111 * qPow(m, 3)) + (0.06134 * qPow(m, 2)) + (365242.13740 * m) + 1721139.29189; 0272 jdme[1] = (0.00025 * qPow(m, 4)) + (0.00907 * qPow(m, 3)) + (-0.05323 * qPow(m, 2)) + (365241.72562 * m) + 1721233.25401; 0273 jdme[2] = (0.00074 * qPow(m, 4)) + (-0.00297 * qPow(m, 3)) + (-0.11677 * qPow(m, 2)) + (365242.49558 * m) + 1721325.70455; 0274 jdme[3] = (-0.00006 * qPow(m, 4)) + (-0.00933 * qPow(m, 3)) + (-0.00769 * qPow(m, 2)) + (365242.88257 * m) + 1721414.39987; 0275 } 0276 0277 qreal t[4]; 0278 for(int i = 0; i < 4; i++) 0279 { 0280 t[i] = (jdme[i] - 2451545)/36525; 0281 } 0282 0283 qreal a[4][24] = {{485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45, 44, 29, 18, 17, 16, 14, 12, 12, 12, 9, 8}, {485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45, 44, 29, 18, 17, 16, 14, 12, 12, 12, 9, 8}, {485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45, 44, 29, 18, 17, 16, 14, 12, 12, 12, 9, 8}, {485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45, 44, 29, 18, 17, 16, 14, 12, 12, 12, 9, 8}}; 0284 0285 qreal b[4][24] = {{324.96, 337.23, 342.08, 27.85, 73.14, 171.52, 222.54, 296.72, 243.58, 119.81, 297.17, 21.02, 247.54, 325.15, 60.93, 155.12, 288.79, 198.04, 199.76, 95.39, 287.11, 320.81, 227.73, 15.45}, {324.96, 337.23, 342.08, 27.85, 73.14, 171.52, 222.54, 296.72, 243.58, 119.81, 297.17, 21.02, 247.54, 325.15, 60.93, 155.12, 288.79, 198.04, 199.76, 95.39, 287.11, 320.81, 227.73, 15.45}, {324.96, 337.23, 342.08, 27.85, 73.14, 171.52, 222.54, 296.72, 243.58, 119.81, 297.17, 21.02, 247.54, 325.15, 60.93, 155.12, 288.79, 198.04, 199.76, 95.39, 287.11, 320.81, 227.73, 15.45}, {324.96, 337.23, 342.08, 27.85, 73.14, 171.52, 222.54, 296.72, 243.58, 119.81, 297.17, 21.02, 247.54, 325.15, 60.93, 155.12, 288.79, 198.04, 199.76, 95.39, 287.11, 320.81, 227.73, 15.45}}; 0286 0287 qreal c[] = {1934.136, 32964.467, 20.186, 445267.112, 45036.886, 22518.443, 65928.934, 3034.906, 9037.513, 33718.147, 150.678, 2281.226, 29929.562, 31555.956, 4443.417, 67555.328, 4562.452, 62894.029, 31436.921, 14577.848, 31931.756, 34777.259, 1222.114, 16859.074}; 0288 0289 qreal d[4][24]; 0290 0291 for (int i = 0; i < 4; i++) 0292 { 0293 for (int j = 0; j < 24; j++) 0294 { 0295 d[i][j] = c[j] * t[i]; 0296 } 0297 } 0298 0299 for (int i = 0; i < 4; i++) 0300 { 0301 for (int j = 0; j < 24; j++) 0302 { 0303 d[i][j] = qCos(qDegreesToRadians(b[i][j] + d[i][j])); 0304 } 0305 } 0306 0307 for (int i = 0; i < 4; i++) 0308 { 0309 for (int j = 0; j < 24; j++) 0310 { 0311 d[i][j] = d[i][j] * a[i][j]; 0312 } 0313 } 0314 0315 qreal e[4], f[4], g[4], jd[4]; 0316 0317 for (int i = 0; i < 4; i++) 0318 { 0319 e[i] = 0; 0320 for (int j = 0; j < 24; j++) 0321 { 0322 e[i] += d[i][j]; 0323 } 0324 } 0325 0326 for (int i = 0; i < 4; i++) 0327 { 0328 f[i] = (35999.373*t[i]-2.47); 0329 } 0330 0331 for (int i = 0; i < 4; i++) 0332 { 0333 g[i] = 1 + (0.0334 * qCos(qDegreesToRadians(f[i]))) + (0.0007 * qCos(qDegreesToRadians(2*f[i]))); 0334 } 0335 0336 for (int i = 0; i < 4; i++) 0337 { 0338 jd[i] = jdme[i] + (0.00001*e[i]/g[i]); 0339 } 0340 0341 // Get correction 0342 qreal correction = FindCorrection(year); 0343 0344 for (int i = 0; i < 4; i++) 0345 { 0346 KStarsDateTime dt(jd[i]); 0347 0348 // Apply correction 0349 dt = dt.addSecs(correction); 0350 0351 switch(i) 0352 { 0353 case 0 : 0354 dSpring = dt; 0355 break; 0356 case 1 : 0357 dSummer = dt; 0358 break; 0359 case 2 : 0360 dAutumn = dt; 0361 break; 0362 case 3 : 0363 dWinter = dt; 0364 break; 0365 } 0366 } 0367 } 0368 0369 qreal modCalcEquinox::FindCorrection(uint32_t year) 0370 { 0371 uint32_t tblFirst = 1620, tblLast = 2002; 0372 0373 // Corrections taken from Meeus -1991 chapter 10 0374 qreal tbl[] = {/*1620*/ 121,112,103, 95, 88, 82, 77, 72, 68, 63, 60, 56, 53, 51, 48, 46, 44, 42, 40, 38, 0375 /*1660*/ 35, 33, 31, 29, 26, 24, 22, 20, 18, 16, 14, 12, 11, 10, 9, 8, 7, 7, 7, 7, 0376 /*1700*/ 7, 7, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 0377 /*1740*/ 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 0378 /*1780*/ 16, 16, 16, 16, 16, 16, 15, 15, 14, 13, 0379 /*1800*/ 13.1, 12.5, 12.2, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 11.9, 11.6, 11.0, 10.2, 9.2, 8.2, 0380 /*1830*/ 7.1, 6.2, 5.6, 5.4, 5.3, 5.4, 5.6, 5.9, 6.2, 6.5, 6.8, 7.1, 7.3, 7.5, 7.6, 0381 /*1860*/ 7.7, 7.3, 6.2, 5.2, 2.7, 1.4, -1.2, -2.8, -3.8, -4.8, -5.5, -5.3, -5.6, -5.7, -5.9, 0382 /*1890*/ -6.0, -6.3, -6.5, -6.2, -4.7, -2.8, -0.1, 2.6, 5.3, 7.7, 10.4, 13.3, 16.0, 18.2, 20.2, 0383 /*1920*/ 21.1, 22.4, 23.5, 23.8, 24.3, 24.0, 23.9, 23.9, 23.7, 24.0, 24.3, 25.3, 26.2, 27.3, 28.2, 0384 /*1950*/ 29.1, 30.0, 30.7, 31.4, 32.2, 33.1, 34.0, 35.0, 36.5, 38.3, 40.2, 42.2, 44.5, 46.5, 48.5, 0385 /*1980*/ 50.5, 52.5, 53.8, 54.9, 55.8, 56.9, 58.3, 60.0, 61.6, 63.0, 63.8, 64.3}; 0386 0387 qreal deltaT = 0; 0388 qreal t = (year - 2000.0)/100.0; 0389 0390 if(year >= tblFirst && year <= tblLast) 0391 { 0392 if(year % 2) 0393 { 0394 deltaT = (tbl[(year-tblFirst-1)/2] + tbl[(year-tblFirst+1)/2]) / 2; 0395 } 0396 else 0397 { 0398 deltaT = tbl[(year-tblFirst)/2]; 0399 } 0400 } 0401 else if(year < 948) 0402 { 0403 deltaT = 2177 + 497*t + 44.1*qPow(t, 2); 0404 } 0405 else if(year >= 948) 0406 { 0407 deltaT = 102 + 102*t + 25.3*qPow(t, 2); 0408 if (year>=2000 && year <=2100) 0409 { 0410 // Special correction to avoid discontinuity in 2000 0411 deltaT += 0.37 * ( year - 2100.0 ); 0412 } 0413 } 0414 return -deltaT; 0415 }