1 /*
2 * Copyright (C) 2003 Fabien Chereau
3 * Copyright (C) 2012 Matthew Gates
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "StelCore.hpp"
21
22 #include "StelProjector.hpp"
23 #include "StelProjectorClasses.hpp"
24 #include "StelToneReproducer.hpp"
25 #include "StelApp.hpp"
26 #include "StelUtils.hpp"
27 #include "StelGeodesicGrid.hpp"
28 #include "StelMovementMgr.hpp"
29 #include "StelModuleMgr.hpp"
30 #include "StelPainter.hpp"
31 #include "StelLocationMgr.hpp"
32 #include "StelObserver.hpp"
33 #include "StelObjectMgr.hpp"
34 #include "Planet.hpp"
35 #include "SolarSystem.hpp"
36 #include "LandscapeMgr.hpp"
37 #include "StelTranslator.hpp"
38 #include "StelActionMgr.hpp"
39 #include "StelFileMgr.hpp"
40 #include "StelMainView.hpp"
41 #include "EphemWrapper.hpp"
42 #include "NomenclatureItem.hpp"
43 #include "precession.h"
44
45 #include <QSettings>
46 #include <QDebug>
47 #include <QMetaEnum>
48 #include <QTimeZone>
49 #include <QFile>
50 #include <QDir>
51 #include <QRegularExpression>
52
53 #include <iostream>
54 #include <fstream>
55
56 // Init static transfo matrices
57 // See vsop87.doc:
58 const Mat4d StelCore::matJ2000ToVsop87(Mat4d::xrotation(-23.4392803055555555556*M_PI_180) * Mat4d::zrotation(0.0000275*M_PI_180));
59 const Mat4d StelCore::matVsop87ToJ2000(matJ2000ToVsop87.transpose());
60 const Mat4d StelCore::matJ2000ToGalactic(-0.054875539726, 0.494109453312, -0.867666135858, 0, -0.873437108010, -0.444829589425, -0.198076386122, 0, -0.483834985808, 0.746982251810, 0.455983795705, 0, 0, 0, 0, 1);
61 const Mat4d StelCore::matGalacticToJ2000(matJ2000ToGalactic.transpose());
62 const Mat4d StelCore::matJ2000ToSupergalactic(0.37501548, -0.89832046, 0.22887497, 0, 0.34135896, -0.09572714, -0.93504565, 0, 0.86188018, 0.42878511, 0.27075058, 0, 0, 0, 0, 1);
63 const Mat4d StelCore::matSupergalacticToJ2000(matJ2000ToSupergalactic.transpose());
64 Mat4d StelCore::matJ2000ToJ1875; // gets to be initialized in constructor.
65
66 const double StelCore::JD_SECOND = 0.000011574074074074074074; // 1/(24*60*60)=1/86400
67 const double StelCore::JD_MINUTE = 0.00069444444444444444444; // 1/(24*60) =1/1440
68 const double StelCore::JD_HOUR = 0.041666666666666666666; // 1/24
69 const double StelCore::JD_DAY = 1.;
70 const double StelCore::ONE_OVER_JD_SECOND = 86400; // 86400
71 const double StelCore::TZ_ERA_BEGINNING = 2395996.5; // December 1, 1847
72
StelCore()73 StelCore::StelCore()
74 : skyDrawer(Q_NULLPTR)
75 , movementMgr(Q_NULLPTR)
76 , propMgr(Q_NULLPTR)
77 , geodesicGrid(Q_NULLPTR)
78 , currentProjectionType(ProjectionStereographic)
79 , currentDeltaTAlgorithm(EspenakMeeus)
80 , position(Q_NULLPTR)
81 , flagUseNutation(true)
82 , flagUseAberration(true)
83 , aberrationFactor(1.0)
84 , flagUseTopocentricCoordinates(true)
85 , timeSpeed(JD_SECOND)
86 , JD(0.,0.)
87 , presetSkyTime(0.)
88 , milliSecondsOfLastJDUpdate(0)
89 , jdOfLastJDUpdate(0.)
90 , flagUseDST(true)
91 , flagUseCTZ(false)
92 , deltaTCustomNDot(-26.0)
93 , deltaTCustomYear(1820.0)
94 , deltaTnDot(-26.0)
95 , deltaTdontUseMoon(false)
96 , deltaTfunc(StelUtils::getDeltaTByEspenakMeeus)
97 , deltaTstart(-1999)
98 , deltaTfinish(3000)
99 , de430Available(false)
100 , de431Available(false)
101 , de430Active(false)
102 , de431Active(false)
103 , de440Available(false)
104 , de441Available(false)
105 , de440Active(false)
106 , de441Active(false)
107 {
108 setObjectName("StelCore");
109 registerMathMetaTypes();
110
111 toneReproducer = new StelToneReproducer();
112 milliSecondsOfLastJDUpdate = QDateTime::currentMSecsSinceEpoch();
113
114 QSettings* conf = StelApp::getInstance().getSettings();
115 // Create and initialize the default projector params
116 QString tmpstr = conf->value("projection/viewport").toString();
117 currentProjectorParams.maskType = StelProjector::stringToMaskType(tmpstr);
118 const int viewport_width = conf->value("projection/viewport_width", currentProjectorParams.viewportXywh[2]).toInt();
119 const int viewport_height = conf->value("projection/viewport_height", currentProjectorParams.viewportXywh[3]).toInt();
120 const int viewport_x = conf->value("projection/viewport_x", 0).toInt();
121 const int viewport_y = conf->value("projection/viewport_y", 0).toInt();
122 currentProjectorParams.viewportXywh.set(viewport_x,viewport_y,viewport_width,viewport_height);
123
124 const qreal viewportCenterX = conf->value("projection/viewport_center_x",0.5*viewport_width).toDouble();
125 const qreal viewportCenterY = conf->value("projection/viewport_center_y",0.5*viewport_height).toDouble();
126 currentProjectorParams.viewportCenter.set(viewportCenterX, viewportCenterY);
127 const qreal viewportCenterOffsetX = conf->value("projection/viewport_center_offset_x",0.).toDouble();
128 const qreal viewportCenterOffsetY = conf->value("projection/viewport_center_offset_y",0.).toDouble();
129 currentProjectorParams.viewportCenterOffset.set(viewportCenterOffsetX, viewportCenterOffsetY);
130
131 currentProjectorParams.viewportFovDiameter = conf->value("projection/viewport_fov_diameter", qMin(viewport_width,viewport_height)).toDouble();
132 currentProjectorParams.flipHorz = conf->value("projection/flip_horz",false).toBool();
133 currentProjectorParams.flipVert = conf->value("projection/flip_vert",false).toBool();
134
135 currentProjectorParams.gravityLabels = conf->value("viewing/flag_gravity_labels").toBool();
136
137 currentProjectorParams.devicePixelsPerPixel = StelApp::getInstance().getDevicePixelsPerPixel();
138
139 flagUseNutation=conf->value("astro/flag_nutation", true).toBool();
140 flagUseAberration=conf->value("astro/flag_aberration", true).toBool();
141 aberrationFactor=conf->value("astro/aberration_factor", 1.0).toDouble();
142 flagUseTopocentricCoordinates=conf->value("astro/flag_topocentric_coordinates", true).toBool();
143 flagUseDST=conf->value("localization/flag_dst", true).toBool();
144
145 // Initialize matJ2000ToJ1875 matrix
146 double eps1875, chi1875, omega1875, psi1875;
147 const double jdB1875 = StelUtils::getJDFromBesselianEpoch(1875.0);
148 getPrecessionAnglesVondrak(jdB1875, &eps1875, &chi1875, &omega1875, &psi1875);
149 matJ2000ToJ1875 = Mat4d::xrotation(84381.406*1./3600.*M_PI/180.) * Mat4d::zrotation(-psi1875) * Mat4d::xrotation(-omega1875) * Mat4d::zrotation(chi1875);
150 matJ2000ToJ1875 = matJ2000ToJ1875.transpose();
151 }
152
153
~StelCore()154 StelCore::~StelCore()
155 {
156 delete toneReproducer; toneReproducer=Q_NULLPTR;
157 delete geodesicGrid; geodesicGrid=Q_NULLPTR;
158 delete skyDrawer; skyDrawer=Q_NULLPTR;
159 delete position; position=Q_NULLPTR;
160 }
161
162 /*************************************************************************
163 Load core data and initialize with default values
164 *************************************************************************/
init()165 void StelCore::init()
166 {
167 QSettings* conf = StelApp::getInstance().getSettings();
168
169 if (conf->childGroups().contains("location_run_once"))
170 defaultLocationID = "stellarium_cli";
171 else
172 defaultLocationID = conf->value("init_location/location", "auto").toString();
173 bool ok;
174 StelLocationMgr* locationMgr = &StelApp::getInstance().getLocationMgr();
175 StelLocation location=locationMgr->getLastResortLocation(); // first location: Paris. Required if no IP connection on first launch!
176 if (defaultLocationID == "auto")
177 {
178 locationMgr->locationFromIP();
179 }
180 else if (defaultLocationID == "stellarium_cli")
181 {
182 location = locationMgr->locationFromCLI();
183 }
184 else if (defaultLocationID.startsWith("GPS", Qt::CaseInsensitive))
185 {
186 // The location is obtained already from init_location/last_resort_location
187 location.name = conf->value("init_location/location").toString();
188 }
189 else
190 {
191 location = locationMgr->locationForString(defaultLocationID);
192 }
193
194 if (!location.isValid())
195 {
196 qWarning() << "Warning: location" << defaultLocationID << "is unknown.";
197 location = locationMgr->getLastResortLocation();
198 }
199 position = new StelObserver(location);
200
201 QString ctz = conf->value("localization/time_zone", "").toString();
202 if (!ctz.isEmpty())
203 setUseCustomTimeZone(true);
204 else
205 ctz = getCurrentLocation().ianaTimeZone;
206 setCurrentTimeZone(ctz);
207
208 // Delta-T stuff
209 // Define default algorithm for time correction (Delta T)
210 QString tmpDT = conf->value("navigation/time_correction_algorithm", "EspenakMeeus").toString();
211 setCurrentDeltaTAlgorithmKey(tmpDT);
212
213 // Define variables of custom equation for calculation of Delta T
214 // Default: ndot = -26.0 "/cy/cy; year = 1820; DeltaT = -20 + 32*u^2, where u = (currentYear-1820)/100
215 setDeltaTCustomYear(conf->value("custom_time_correction/year", 1820.0).toDouble());
216 setDeltaTCustomNDot(conf->value("custom_time_correction/ndot", -26.0).toDouble());
217 setDeltaTCustomEquationCoefficients(Vec3d(conf->value("custom_time_correction/coefficients", "-20,0,32").toString()));
218
219 // Time stuff
220 setTimeNow();
221
222 // We want to be able to handle the old style preset time, recorded as a double
223 // jday, or as a more human readable string...
224 QString presetTimeStr = conf->value("navigation/preset_sky_time",2451545.).toString();
225 presetSkyTime = presetTimeStr.toDouble(&ok);
226 if (ok)
227 {
228 qDebug() << "navigation/preset_sky_time is a double - treating as jday:" << QString::number(presetSkyTime, 'f', 5);
229 }
230 else
231 {
232 qDebug() << "navigation/preset_sky_time was not a double, treating as string date:" << presetTimeStr;
233 presetSkyTime = StelUtils::qDateTimeToJd(QDateTime::fromString(presetTimeStr));
234 }
235 setInitTodayTime(QTime::fromString(conf->value("navigation/today_time", "22:00").toString()));
236 startupTimeMode = conf->value("navigation/startup_time_mode", "actual").toString().toLower();
237 if (startupTimeMode=="preset")
238 setJD(presetSkyTime - static_cast<double>(getUTCOffset(presetSkyTime)) * JD_HOUR);
239 else if (startupTimeMode=="today")
240 setTodayTime(getInitTodayTime());
241
242 // Compute transform matrices between coordinates systems
243 updateTransformMatrices();
244
245 movementMgr = new StelMovementMgr(this);
246 movementMgr->init();
247 currentProjectorParams.fov = static_cast<float>(movementMgr->getInitFov());
248 StelApp::getInstance().getModuleMgr().registerModule(movementMgr);
249
250 skyDrawer = new StelSkyDrawer(this);
251 skyDrawer->init();
252
253 propMgr = StelApp::getInstance().getStelPropertyManager();
254 propMgr->registerObject(skyDrawer);
255 propMgr->registerObject(this);
256
257 setCurrentProjectionTypeKey(getDefaultProjectionTypeKey());
258 updateMaximumFov();
259
260 // activate DE430/431
261 initEphemeridesFunctions();
262
263 // Register all the core actions.
264 QString timeGroup = N_("Date and Time");
265 QString movementGroup = N_("Movement and Selection");
266 QString displayGroup = N_("Display Options");
267 StelActionMgr* actionsMgr = StelApp::getInstance().getStelActionManager();
268 actionsMgr->addAction("actionIncrease_Time_Speed", timeGroup, N_("Increase time speed"), this, "increaseTimeSpeed()", "L");
269 actionsMgr->addAction("actionDecrease_Time_Speed", timeGroup, N_("Decrease time speed"), this, "decreaseTimeSpeed()", "J");
270 actionsMgr->addAction("actionIncrease_Time_Speed_Less", timeGroup, N_("Increase time speed (a little)"), this, "increaseTimeSpeedLess()", "Shift+L");
271 actionsMgr->addAction("actionDecrease_Time_Speed_Less", timeGroup, N_("Decrease time speed (a little)"), this, "decreaseTimeSpeedLess()", "Shift+J");
272 actionsMgr->addAction("actionSet_Real_Time_Speed", timeGroup, N_("Set normal time rate"), this, "toggleRealTimeSpeed()", "K");
273 actionsMgr->addAction("actionSet_Time_Rate_Zero", timeGroup, N_("Set time rate to zero"), this, "setZeroTimeSpeed()", "7");
274 actionsMgr->addAction("actionSet_Time_Reverse", timeGroup, N_("Set reverse time direction"), this, "revertTimeDirection()", "0");
275 actionsMgr->addAction("actionReturn_To_Current_Time", timeGroup, N_("Set time to now"), this, "setTimeNow()", "8");
276 actionsMgr->addAction("actionAdd_Solar_Minute", timeGroup, N_("Add 1 solar minute"), this, "addMinute()");
277 actionsMgr->addAction("actionAdd_Solar_Hour", timeGroup, N_("Add 1 solar hour"), this, "addHour()", "Ctrl+=");
278 actionsMgr->addAction("actionAdd_Solar_Day", timeGroup, N_("Add 1 solar day"), this, "addDay()", "=");
279 actionsMgr->addAction("actionAdd_Solar_Week", timeGroup, N_("Add 7 solar days"), this, "addWeek()", "]");
280 actionsMgr->addAction("actionSubtract_Solar_Minute", timeGroup, N_("Subtract 1 solar minute"), this, "subtractMinute()");
281 actionsMgr->addAction("actionSubtract_Solar_Hour", timeGroup, N_("Subtract 1 solar hour"), this, "subtractHour()", "Ctrl+-");
282 actionsMgr->addAction("actionSubtract_Solar_Day", timeGroup, N_("Subtract 1 solar day"), this, "subtractDay()", "-");
283 actionsMgr->addAction("actionSubtract_Solar_Week", timeGroup, N_("Subtract 7 solar days"), this, "subtractWeek()", "[");
284 actionsMgr->addAction("actionAdd_Sidereal_Day", timeGroup, N_("Add 1 sidereal day"), this, "addSiderealDay()", "Alt+=");
285 actionsMgr->addAction("actionAdd_Sidereal_Week", timeGroup, N_("Add 7 sidereal days"), this, "addSiderealWeek()");
286 actionsMgr->addAction("actionAdd_Sidereal_Year", timeGroup, N_("Add 1 sidereal year"), this, "addSiderealYear()", "Ctrl+Alt+Shift+]");
287 actionsMgr->addAction("actionAdd_Sidereal_Century", timeGroup, N_("Add 100 sidereal years"), this, "addSiderealYears()");
288 actionsMgr->addAction("actionAdd_Synodic_Month", timeGroup, N_("Add 1 synodic month"), this, "addSynodicMonth()");
289 actionsMgr->addAction("actionAdd_Saros", timeGroup, N_("Add 1 saros"), this, "addSaros()");
290 actionsMgr->addAction("actionAdd_Draconic_Month", timeGroup, N_("Add 1 draconic month"), this, "addDraconicMonth()");
291 actionsMgr->addAction("actionAdd_Draconic_Year", timeGroup, N_("Add 1 draconic year"), this, "addDraconicYear()");
292 actionsMgr->addAction("actionAdd_Anomalistic_Month", timeGroup, N_("Add 1 anomalistic month"), this, "addAnomalisticMonth()");
293 actionsMgr->addAction("actionAdd_Anomalistic_Year", timeGroup, N_("Add 1 anomalistic year"), this, "addAnomalisticYear()");
294 actionsMgr->addAction("actionAdd_Anomalistic_Century", timeGroup, N_("Add 100 anomalistic years"), this, "addAnomalisticYears()");
295 actionsMgr->addAction("actionAdd_Mean_Tropical_Month", timeGroup, N_("Add 1 mean tropical month"), this, "addMeanTropicalMonth()");
296 actionsMgr->addAction("actionAdd_Mean_Tropical_Year", timeGroup, N_("Add 1 mean tropical year"), this, "addMeanTropicalYear()");
297 actionsMgr->addAction("actionAdd_Mean_Tropical_Century", timeGroup, N_("Add 100 mean tropical years"), this, "addMeanTropicalYears()");
298 actionsMgr->addAction("actionAdd_Tropical_Year", timeGroup, N_("Add 1 tropical year"), this, "addTropicalYear()");
299 actionsMgr->addAction("actionAdd_Julian_Year", timeGroup, N_("Add 1 Julian year"), this, "addJulianYear()");
300 actionsMgr->addAction("actionAdd_Julian_Century", timeGroup, N_("Add 1 Julian century"), this, "addJulianYears()");
301 actionsMgr->addAction("actionAdd_Gaussian_Year", timeGroup, N_("Add 1 Gaussian year"), this, "addGaussianYear()");
302 actionsMgr->addAction("actionAdd_Calendric_Month", timeGroup, N_("Add 1 calendric month"), this, "addCalendricMonth()");
303 actionsMgr->addAction("actionSubtract_Sidereal_Day", timeGroup, N_("Subtract 1 sidereal day"), this, "subtractSiderealDay()", "Alt+-");
304 actionsMgr->addAction("actionSubtract_Sidereal_Week", timeGroup, N_("Subtract 7 sidereal days"), this, "subtractSiderealWeek()");
305 actionsMgr->addAction("actionSubtract_Sidereal_Year", timeGroup, N_("Subtract 1 sidereal year"), this, "subtractSiderealYear()", "Ctrl+Alt+Shift+[");
306 actionsMgr->addAction("actionSubtract_Sidereal_Century", timeGroup, N_("Subtract 100 sidereal years"), this, "subtractSiderealYears()");
307 actionsMgr->addAction("actionSubtract_Synodic_Month", timeGroup, N_("Subtract 1 synodic month"), this, "subtractSynodicMonth()");
308 actionsMgr->addAction("actionSubtract_Saros", timeGroup, N_("Subtract 1 saros"), this, "subtractSaros()");
309 actionsMgr->addAction("actionSubtract_Draconic_Month", timeGroup, N_("Subtract 1 draconic month"), this, "subtractDraconicMonth()");
310 actionsMgr->addAction("actionSubtract_Draconic_Year", timeGroup, N_("Subtract 1 draconic year"), this, "subtractDraconicYear()");
311 actionsMgr->addAction("actionSubtract_Anomalistic_Month", timeGroup, N_("Subtract 1 anomalistic month"), this, "subtractAnomalisticMonth()");
312 actionsMgr->addAction("actionSubtract_Anomalistic_Year", timeGroup, N_("Subtract 1 anomalistic year"), this, "subtractAnomalisticYear()");
313 actionsMgr->addAction("actionSubtract_Anomalistic_Century", timeGroup, N_("Subtract 100 anomalistic years"), this, "subtractAnomalisticYears()");
314 actionsMgr->addAction("actionSubtract_Mean_Tropical_Month", timeGroup, N_("Subtract 1 mean tropical month"), this, "subtractMeanTropicalMonth()");
315 actionsMgr->addAction("actionSubtract_Mean_Tropical_Year", timeGroup, N_("Subtract 1 mean tropical year"), this, "subtractMeanTropicalYear()");
316 actionsMgr->addAction("actionSubtract_Mean_Tropical_Century", timeGroup, N_("Subtract 100 mean tropical years"), this, "subtractMeanTropicalYears()");
317 actionsMgr->addAction("actionSubtract_Tropical_Year", timeGroup, N_("Subtract 1 tropical year"), this, "subtractTropicalYear()");
318 actionsMgr->addAction("actionSubtract_Julian_Year", timeGroup, N_("Subtract 1 Julian year"), this, "subtractJulianYear()");
319 actionsMgr->addAction("actionSubtract_Julian_Century", timeGroup, N_("Subtract 1 Julian century"), this, "subtractJulianYears()");
320 actionsMgr->addAction("actionSubtract_Gaussian_Year", timeGroup, N_("Subtract 1 Gaussian year"), this, "subtractGaussianYear()");
321 actionsMgr->addAction("actionSubtract_Calendric_Month", timeGroup, N_("Subtract 1 calendric month"), this, "subtractCalendricMonth()");
322
323 actionsMgr->addAction("actionSet_Home_Planet_To_Selected", movementGroup, N_("Set home planet to selected planet"), this, "moveObserverToSelected()", "Ctrl+G");
324 actionsMgr->addAction("actionGo_Home_Global", movementGroup, N_("Go to home"), this, "returnToHome()", "Ctrl+H");
325
326 actionsMgr->addAction("actionHorizontal_Flip", displayGroup, N_("Flip scene horizontally"), this, "flipHorz", "Ctrl+Shift+H", "", true);
327 actionsMgr->addAction("actionVertical_Flip", displayGroup, N_("Flip scene vertically"), this, "flipVert", "Ctrl+Shift+V", "", true);
328 }
329
getDefaultProjectionTypeKey() const330 QString StelCore::getDefaultProjectionTypeKey() const
331 {
332 QSettings* conf = StelApp::getInstance().getSettings();
333 return conf->value("projection/type", "ProjectionStereographic").toString();
334 }
335
336 // Get the shared instance of StelGeodesicGrid.
337 // The returned instance is garanteed to allow for at least maxLevel levels
getGeodesicGrid(int maxLevel) const338 const StelGeodesicGrid* StelCore::getGeodesicGrid(int maxLevel) const
339 {
340 if (geodesicGrid==Q_NULLPTR)
341 {
342 geodesicGrid = new StelGeodesicGrid(maxLevel);
343 }
344 else if (maxLevel>geodesicGrid->getMaxLevel())
345 {
346 delete geodesicGrid;
347 geodesicGrid = new StelGeodesicGrid(maxLevel);
348 }
349 return geodesicGrid;
350 }
351
getProjection2d() const352 StelProjectorP StelCore::getProjection2d() const
353 {
354 StelProjectorP prj(new StelProjector2d());
355 prj->init(currentProjectorParams);
356 return prj;
357 }
358
getProjection(StelProjector::ModelViewTranformP modelViewTransform,ProjectionType projType) const359 StelProjectorP StelCore::getProjection(StelProjector::ModelViewTranformP modelViewTransform, ProjectionType projType) const
360 {
361 if (projType==1000)
362 projType = currentProjectionType;
363
364 StelProjectorP prj;
365 switch (projType)
366 {
367 case ProjectionPerspective:
368 prj = StelProjectorP(new StelProjectorPerspective(modelViewTransform));
369 break;
370 case ProjectionEqualArea:
371 prj = StelProjectorP(new StelProjectorEqualArea(modelViewTransform));
372 break;
373 case ProjectionStereographic:
374 prj = StelProjectorP(new StelProjectorStereographic(modelViewTransform));
375 break;
376 case ProjectionFisheye:
377 prj = StelProjectorP(new StelProjectorFisheye(modelViewTransform));
378 break;
379 case ProjectionHammer:
380 prj = StelProjectorP(new StelProjectorHammer(modelViewTransform));
381 break;
382 case ProjectionCylinder:
383 prj = StelProjectorP(new StelProjectorCylinder(modelViewTransform));
384 break;
385 case ProjectionMercator:
386 prj = StelProjectorP(new StelProjectorMercator(modelViewTransform));
387 break;
388 case ProjectionOrthographic:
389 prj = StelProjectorP(new StelProjectorOrthographic(modelViewTransform));
390 break;
391 case ProjectionSinusoidal:
392 prj = StelProjectorP(new StelProjectorSinusoidal(modelViewTransform));
393 break;
394 case ProjectionMiller:
395 prj = StelProjectorP(new StelProjectorMiller(modelViewTransform));
396 break;
397 default:
398 qWarning() << "Unknown projection type: " << static_cast<int>(projType) << "using ProjectionStereographic instead";
399 prj = StelProjectorP(new StelProjectorStereographic(modelViewTransform));
400 Q_ASSERT(0);
401 }
402 prj->init(currentProjectorParams);
403 return prj;
404 }
405
406 // Get an instance of projector using the current display parameters from Navigation, StelMovementMgr
getProjection(FrameType frameType,RefractionMode refractionMode) const407 StelProjectorP StelCore::getProjection(FrameType frameType, RefractionMode refractionMode) const
408 {
409 switch (frameType)
410 {
411 case FrameAltAz:
412 return getProjection(getAltAzModelViewTransform(refractionMode));
413 case FrameHeliocentricEclipticJ2000:
414 return getProjection(getHeliocentricEclipticModelViewTransform(refractionMode));
415 case FrameObservercentricEclipticJ2000:
416 return getProjection(getObservercentricEclipticJ2000ModelViewTransform(refractionMode));
417 case FrameObservercentricEclipticOfDate:
418 return getProjection(getObservercentricEclipticOfDateModelViewTransform(refractionMode));
419 case FrameEquinoxEqu:
420 return getProjection(getEquinoxEquModelViewTransform(refractionMode));
421 case FrameJ2000:
422 return getProjection(getJ2000ModelViewTransform(refractionMode));
423 case FrameGalactic:
424 return getProjection(getGalacticModelViewTransform(refractionMode));
425 case FrameSupergalactic:
426 return getProjection(getSupergalacticModelViewTransform(refractionMode));
427 default:
428 qDebug() << "Unknown reference frame type: " << static_cast<int>(frameType) << ".";
429 }
430 Q_ASSERT(0);
431 return getProjection2d();
432 }
433
getVisibleSkyArea() const434 SphericalCap StelCore::getVisibleSkyArea() const
435 {
436 const LandscapeMgr* landscapeMgr = GETSTELMODULE(LandscapeMgr);
437 Vec3d up(0, 0, 1);
438 up = altAzToJ2000(up, RefractionOff);
439
440 // Limit star drawing to above landscape's minimal altitude (was const=-0.035, Bug lp:1469407)
441 if (landscapeMgr->getIsLandscapeFullyVisible())
442 {
443 return SphericalCap(up, landscapeMgr->getLandscapeSinMinAltitudeLimit());
444 }
445 return SphericalCap(up, -1.);
446 }
447
448 // Handle the resizing of the window
windowHasBeenResized(qreal x,qreal y,qreal width,qreal height)449 void StelCore::windowHasBeenResized(qreal x, qreal y, qreal width, qreal height)
450 {
451 // Maximize display when resized since it invalidates previous options anyway
452 currentProjectorParams.viewportXywh.set(qRound(x), qRound(y), qRound(width), qRound(height));
453 currentProjectorParams.viewportCenter.set(x+(0.5+currentProjectorParams.viewportCenterOffset.v[0])*width, y+(0.5+currentProjectorParams.viewportCenterOffset.v[1])*height);
454 currentProjectorParams.viewportFovDiameter = qMin(width,height);
455 }
456
457 /*************************************************************************
458 Update all the objects in function of the time
459 *************************************************************************/
update(double deltaTime)460 void StelCore::update(double deltaTime)
461 {
462 // Update the position of observation and time and recompute planet positions etc...
463 updateTime(deltaTime);
464
465 // Transform matrices between coordinates systems
466 updateTransformMatrices();
467
468 // Update direction of vision/Zoom level
469 movementMgr->updateMotion(deltaTime);
470
471 currentProjectorParams.fov = static_cast<float>(movementMgr->getCurrentFov());
472
473 skyDrawer->update(deltaTime);
474 }
475
476
477 /*************************************************************************
478 Execute all the pre-drawing functions
479 *************************************************************************/
preDraw()480 void StelCore::preDraw()
481 {
482 // Init openGL viewing with fov, screen size and clip planes
483 currentProjectorParams.zNear = 0.000001;
484 currentProjectorParams.zFar = 500.;
485
486 // Clear the render buffer.
487 // Here we can set a sky background color if really wanted (art
488 // applications. Astronomical sky should be 0/0/0/0)
489 Vec3f backColor = StelMainView::getInstance().getSkyBackgroundColor();
490 QOpenGLFunctions* gl = QOpenGLContext::currentContext()->functions();
491 gl->glClearColor(backColor[0], backColor[1], backColor[2], 0.f);
492 gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
493
494 skyDrawer->preDraw();
495 }
496
497
498 /*************************************************************************
499 Update core state after drawing modules
500 *************************************************************************/
postDraw()501 void StelCore::postDraw()
502 {
503 StelPainter sPainter(getProjection(StelCore::FrameJ2000));
504 sPainter.drawViewportShape();
505 }
506
updateMaximumFov()507 void StelCore::updateMaximumFov()
508 {
509 const float savedFov = currentProjectorParams.fov;
510 currentProjectorParams.fov = 0.0001f; // Avoid crash
511 const float newMaxFov = getProjection(StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(Mat4d::identity())))->getMaxFov();
512 movementMgr->setMaxFov(static_cast<double>(newMaxFov));
513 currentProjectorParams.fov = qMin(newMaxFov, savedFov);
514 }
515
setCurrentProjectionType(ProjectionType type)516 void StelCore::setCurrentProjectionType(ProjectionType type)
517 {
518 if(type!=currentProjectionType)
519 {
520 currentProjectionType=type;
521 updateMaximumFov();
522
523 emit currentProjectionTypeChanged(type);
524 emit currentProjectionTypeKeyChanged(getCurrentProjectionTypeKey());
525 emit currentProjectionNameI18nChanged(getCurrentProjectionNameI18n());
526 }
527 }
528
getCurrentProjectionType() const529 StelCore::ProjectionType StelCore::getCurrentProjectionType() const
530 {
531 return currentProjectionType;
532 }
533
534 //! Set the current projection type to use
setCurrentProjectionTypeKey(QString key)535 void StelCore::setCurrentProjectionTypeKey(QString key)
536 {
537 const QMetaEnum& en = metaObject()->enumerator(metaObject()->indexOfEnumerator("ProjectionType"));
538 ProjectionType newType = static_cast<ProjectionType>(en.keyToValue(key.toLatin1().data()));
539 if (newType<0)
540 {
541 qWarning() << "Unknown projection type: " << key << "setting \"ProjectionStereographic\" instead";
542 newType = ProjectionStereographic;
543 }
544 setCurrentProjectionType(newType);
545 }
546
547 //! Get the current Mapping used by the Projection
getCurrentProjectionTypeKey(void) const548 QString StelCore::getCurrentProjectionTypeKey(void) const
549 {
550 return metaObject()->enumerator(metaObject()->indexOfEnumerator("ProjectionType")).key(currentProjectionType);
551 }
552
getCurrentProjectionNameI18n() const553 QString StelCore::getCurrentProjectionNameI18n() const
554 {
555 return projectionTypeKeyToNameI18n(getCurrentProjectionTypeKey());
556 }
557
558 //! Get the list of all the available projections
getAllProjectionTypeKeys() const559 QStringList StelCore::getAllProjectionTypeKeys() const
560 {
561 const QMetaEnum& en = metaObject()->enumerator(metaObject()->indexOfEnumerator("ProjectionType"));
562 QStringList l;
563 for (int i=0;i<en.keyCount();++i)
564 l << en.key(i);
565 return l;
566 }
567
setMaskType(StelProjector::StelProjectorMaskType m)568 void StelCore::setMaskType(StelProjector::StelProjectorMaskType m)
569 {
570 currentProjectorParams.maskType = m;
571 }
572
setFlagGravityLabels(bool gravity)573 void StelCore::setFlagGravityLabels(bool gravity)
574 {
575 currentProjectorParams.gravityLabels = gravity;
576 emit flagGravityLabelsChanged(gravity);
577 }
578
getFlagGravityLabels() const579 bool StelCore::getFlagGravityLabels() const
580 {
581 return currentProjectorParams.gravityLabels;
582 }
583
setDefaultAngleForGravityText(float a)584 void StelCore::setDefaultAngleForGravityText(float a)
585 {
586 currentProjectorParams.defaultAngleForGravityText = a;
587 }
588
setFlipHorz(bool flip)589 void StelCore::setFlipHorz(bool flip)
590 {
591 if (currentProjectorParams.flipHorz != flip)
592 {
593 currentProjectorParams.flipHorz = flip;
594 emit flipHorzChanged(flip);
595 }
596 }
597
setFlipVert(bool flip)598 void StelCore::setFlipVert(bool flip)
599 {
600 if (currentProjectorParams.flipVert != flip)
601 {
602 currentProjectorParams.flipVert = flip;
603 emit flipVertChanged(flip);
604 }
605 }
606
getFlipHorz(void) const607 bool StelCore::getFlipHorz(void) const
608 {
609 return currentProjectorParams.flipHorz;
610 }
611
getFlipVert(void) const612 bool StelCore::getFlipVert(void) const
613 {
614 return currentProjectorParams.flipVert;
615 }
616
617 // Get current value for horizontal viewport offset [-50...50]
getViewportHorizontalOffset(void) const618 double StelCore::getViewportHorizontalOffset(void) const
619 {
620 return (currentProjectorParams.viewportCenterOffset[0] * 100.0);
621 }
622 // Set horizontal viewport offset. Argument will be clamped to be inside [-50...50]
setViewportHorizontalOffset(double newOffsetPct)623 void StelCore::setViewportHorizontalOffset(double newOffsetPct)
624 {
625 currentProjectorParams.viewportCenterOffset[0]=0.01* qBound(-50., newOffsetPct, 50.);
626 currentProjectorParams.viewportCenter.set(currentProjectorParams.viewportXywh[0]+(0.5+currentProjectorParams.viewportCenterOffset.v[0])*currentProjectorParams.viewportXywh[2],
627 currentProjectorParams.viewportXywh[1]+(0.5+currentProjectorParams.viewportCenterOffset.v[1])*currentProjectorParams.viewportXywh[3]);
628 }
629
630 // Get current value for vertical viewport offset [-50...50]
getViewportVerticalOffset(void) const631 double StelCore::getViewportVerticalOffset(void) const
632 {
633 return (currentProjectorParams.viewportCenterOffset[1] * 100.0);
634 }
635 // Set vertical viewport offset. Argument will be clamped to be inside [-50...50]
setViewportVerticalOffset(double newOffsetPct)636 void StelCore::setViewportVerticalOffset(double newOffsetPct)
637 {
638 currentProjectorParams.viewportCenterOffset[1]=0.01* qBound(-50., newOffsetPct, 50.);
639 currentProjectorParams.viewportCenter.set(currentProjectorParams.viewportXywh[0]+(0.5+currentProjectorParams.viewportCenterOffset.v[0])*currentProjectorParams.viewportXywh[2],
640 currentProjectorParams.viewportXywh[1]+(0.5+currentProjectorParams.viewportCenterOffset.v[1])*currentProjectorParams.viewportXywh[3]);
641 }
642
643 // Set both viewport offsets. Arguments will be clamped to be inside [-50...50]. I (GZ) hope this will avoid some of the shaking.
setViewportOffset(double newHorizontalOffsetPct,double newVerticalOffsetPct)644 void StelCore::setViewportOffset(double newHorizontalOffsetPct, double newVerticalOffsetPct)
645 {
646 currentProjectorParams.viewportCenterOffset[0]=0.01* qBound(-50., newHorizontalOffsetPct, 50.);
647 currentProjectorParams.viewportCenterOffset[1]=0.01* qBound(-50., newVerticalOffsetPct, 50.);
648 currentProjectorParams.viewportCenter.set(currentProjectorParams.viewportXywh[0]+(0.5+currentProjectorParams.viewportCenterOffset.v[0])*currentProjectorParams.viewportXywh[2],
649 currentProjectorParams.viewportXywh[1]+(0.5+currentProjectorParams.viewportCenterOffset.v[1])*currentProjectorParams.viewportXywh[3]);
650 }
651
setViewportStretch(float stretch)652 void StelCore::setViewportStretch(float stretch)
653 {
654 currentProjectorParams.widthStretch=static_cast<qreal>(qMax(0.001f, stretch));
655 }
656
getDefaultLocationID() const657 QString StelCore::getDefaultLocationID() const
658 {
659 return defaultLocationID;
660 }
661
projectionTypeKeyToNameI18n(const QString & key) const662 QString StelCore::projectionTypeKeyToNameI18n(const QString& key) const
663 {
664 const QMetaEnum& en = metaObject()->enumerator(metaObject()->indexOfEnumerator("ProjectionType"));
665 QString s(getProjection(StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(Mat4d::identity())), static_cast<ProjectionType>(en.keyToValue(key.toLatin1())))->getNameI18());
666 return s;
667 }
668
projectionNameI18nToTypeKey(const QString & nameI18n) const669 QString StelCore::projectionNameI18nToTypeKey(const QString& nameI18n) const
670 {
671 const QMetaEnum& en = metaObject()->enumerator(metaObject()->indexOfEnumerator("ProjectionType"));
672 for (int i=0;i<en.keyCount();++i)
673 {
674 if (getProjection(StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(Mat4d::identity())), static_cast<ProjectionType>(i))->getNameI18()==nameI18n)
675 return en.valueToKey(i);
676 }
677 // Unknown translated name
678 Q_ASSERT(0);
679 return en.valueToKey(ProjectionStereographic);
680 }
681
getCurrentStelProjectorParams() const682 StelProjector::StelProjectorParams StelCore::getCurrentStelProjectorParams() const
683 {
684 return currentProjectorParams;
685 }
686
setCurrentStelProjectorParams(const StelProjector::StelProjectorParams & newParams)687 void StelCore::setCurrentStelProjectorParams(const StelProjector::StelProjectorParams& newParams)
688 {
689 currentProjectorParams=newParams;
690 }
691
lookAtJ2000(const Vec3d & pos,const Vec3d & aup)692 void StelCore::lookAtJ2000(const Vec3d& pos, const Vec3d& aup)
693 {
694 Vec3d f(j2000ToAltAz(pos, RefractionOff));
695 Vec3d up(j2000ToAltAz(aup, RefractionOff));
696 f.normalize();
697 up.normalize();
698
699 // Update the model view matrix
700 Vec3d s(f^up); // y vector
701 s.normalize();
702 Vec3d u(s^f); // Up vector in AltAz coordinates
703 u.normalize();
704 matAltAzModelView.set(s[0],u[0],-f[0],0.,
705 s[1],u[1],-f[1],0.,
706 s[2],u[2],-f[2],0.,
707 0.,0.,0.,1.);
708 invertMatAltAzModelView = matAltAzModelView.inverse();
709 }
710
setMatAltAzModelView(const Mat4d & mat)711 void StelCore::setMatAltAzModelView(const Mat4d& mat)
712 {
713 matAltAzModelView = mat;
714 invertMatAltAzModelView = matAltAzModelView.inverse();
715 }
716
altAzToEquinoxEqu(const Vec3d & v,RefractionMode refMode) const717 Vec3d StelCore::altAzToEquinoxEqu(const Vec3d& v, RefractionMode refMode) const
718 {
719 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
720 return matAltAzToEquinoxEqu*v;
721 Vec3d r(v);
722 skyDrawer->getRefraction().backward(r);
723 r.transfo4d(matAltAzToEquinoxEqu);
724 return r;
725 }
726
equinoxEquToAltAz(const Vec3d & v,RefractionMode refMode) const727 Vec3d StelCore::equinoxEquToAltAz(const Vec3d& v, RefractionMode refMode) const
728 {
729 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
730 return matEquinoxEquToAltAz*v;
731 Vec3d r(v);
732 r.transfo4d(matEquinoxEquToAltAz);
733 skyDrawer->getRefraction().forward(r);
734 return r;
735 }
736
altAzToJ2000(const Vec3d & v,RefractionMode refMode) const737 Vec3d StelCore::altAzToJ2000(const Vec3d& v, RefractionMode refMode) const
738 {
739 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
740 return matEquinoxEquDateToJ2000*matAltAzToEquinoxEqu*v;
741 Vec3d r(v);
742 skyDrawer->getRefraction().backward(r);
743 r.transfo4d(matEquinoxEquDateToJ2000*matAltAzToEquinoxEqu);
744 return r;
745 }
746
j2000ToAltAz(const Vec3d & v,RefractionMode refMode) const747 Vec3d StelCore::j2000ToAltAz(const Vec3d& v, RefractionMode refMode) const
748 {
749 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
750 return matJ2000ToAltAz*v;
751 Vec3d r(v);
752 r.transfo4d(matJ2000ToAltAz);
753 skyDrawer->getRefraction().forward(r);
754 return r;
755 }
756
galacticToJ2000(const Vec3d & v) const757 Vec3d StelCore::galacticToJ2000(const Vec3d& v) const
758 {
759 return matGalacticToJ2000*v;
760 }
761
supergalacticToJ2000(const Vec3d & v) const762 Vec3d StelCore::supergalacticToJ2000(const Vec3d& v) const
763 {
764 return matSupergalacticToJ2000*v;
765 }
766
equinoxEquToJ2000(const Vec3d & v,RefractionMode refMode) const767 Vec3d StelCore::equinoxEquToJ2000(const Vec3d& v, RefractionMode refMode) const
768 {
769 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
770 return matEquinoxEquDateToJ2000*v;
771 Vec3d r(v);
772 r.transfo4d(matEquinoxEquToAltAz);
773 skyDrawer->getRefraction().backward(r);
774 r.transfo4d(matAltAzToJ2000);
775 return r;
776 }
777
j2000ToEquinoxEqu(const Vec3d & v,RefractionMode refMode) const778 Vec3d StelCore::j2000ToEquinoxEqu(const Vec3d& v, RefractionMode refMode) const
779 {
780 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
781 return matJ2000ToEquinoxEqu*v;
782 Vec3d r(v);
783 r.transfo4d(matJ2000ToAltAz);
784 skyDrawer->getRefraction().forward(r);
785 r.transfo4d(matAltAzToEquinoxEqu);
786 return r;
787 }
788
j2000ToJ1875(const Vec3d & v) const789 Vec3d StelCore::j2000ToJ1875(const Vec3d& v) const
790 {
791 return matJ2000ToJ1875*v;
792 }
793
j2000ToGalactic(const Vec3d & v) const794 Vec3d StelCore::j2000ToGalactic(const Vec3d& v) const
795 {
796 return matJ2000ToGalactic*v;
797 }
798
j2000ToSupergalactic(const Vec3d & v) const799 Vec3d StelCore::j2000ToSupergalactic(const Vec3d& v) const
800 {
801 return matJ2000ToSupergalactic*v;
802 }
803
804 //! Transform vector from heliocentric ecliptic coordinate to altazimuthal
heliocentricEclipticToAltAz(const Vec3d & v,RefractionMode refMode) const805 Vec3d StelCore::heliocentricEclipticToAltAz(const Vec3d& v, RefractionMode refMode) const
806 {
807 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
808 return matHeliocentricEclipticJ2000ToAltAz*v;
809 Vec3d r(v);
810 r.transfo4d(matHeliocentricEclipticJ2000ToAltAz);
811 skyDrawer->getRefraction().forward(r);
812 return r;
813 }
814
815 //! Transform from heliocentric coordinate to equatorial at current equinox (for the planet where the observer stands)
heliocentricEclipticToEquinoxEqu(const Vec3d & v) const816 Vec3d StelCore::heliocentricEclipticToEquinoxEqu(const Vec3d& v) const
817 {
818 return matHeliocentricEclipticToEquinoxEqu*v;
819 }
820
821 /*
822 //! Transform vector from heliocentric coordinate to false equatorial : equatorial
823 //! coordinate but centered on the observer position (useful for objects close to earth)
824 //! Unused as of V0.13
825 Vec3d StelCore::heliocentricEclipticToEarthPosEquinoxEqu(const Vec3d& v) const
826 {
827 return matAltAzToEquinoxEqu*matHeliocentricEclipticToAltAz*v;
828 }
829 */
830
getHeliocentricEclipticModelViewTransform(RefractionMode refMode) const831 StelProjector::ModelViewTranformP StelCore::getHeliocentricEclipticModelViewTransform(RefractionMode refMode) const
832 {
833 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
834 return StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(matAltAzModelView*matHeliocentricEclipticJ2000ToAltAz));
835 Refraction* refr = new Refraction(skyDrawer->getRefraction());
836 // The pretransform matrix will convert from input coordinates to AltAz needed by the refraction function.
837 refr->setPreTransfoMat(matHeliocentricEclipticJ2000ToAltAz);
838 refr->setPostTransfoMat(matAltAzModelView);
839 return StelProjector::ModelViewTranformP(refr);
840 }
841
842 //! Get the modelview matrix for observer-centric ecliptic J2000 (Vsop87A) drawing
getObservercentricEclipticJ2000ModelViewTransform(RefractionMode refMode) const843 StelProjector::ModelViewTranformP StelCore::getObservercentricEclipticJ2000ModelViewTransform(RefractionMode refMode) const
844 {
845 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
846 return StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(matAltAzModelView*matJ2000ToAltAz*matVsop87ToJ2000));
847 Refraction* refr = new Refraction(skyDrawer->getRefraction());
848 // The pretransform matrix will convert from input coordinates to AltAz needed by the refraction function.
849 refr->setPreTransfoMat(matJ2000ToAltAz*matVsop87ToJ2000);
850 refr->setPostTransfoMat(matAltAzModelView);
851 return StelProjector::ModelViewTranformP(refr);
852 }
853
854 //! Get the modelview matrix for observer-centric ecliptic-of-date drawing
getObservercentricEclipticOfDateModelViewTransform(RefractionMode refMode) const855 StelProjector::ModelViewTranformP StelCore::getObservercentricEclipticOfDateModelViewTransform(RefractionMode refMode) const
856 {
857 double eps_A=getPrecessionAngleVondrakCurrentEpsilonA();
858 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
859 return StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(matAltAzModelView*matEquinoxEquToAltAz* Mat4d::xrotation(eps_A)));
860 Refraction* refr = new Refraction(skyDrawer->getRefraction());
861 // The pretransform matrix will convert from input coordinates to AltAz needed by the refraction function.
862 refr->setPreTransfoMat(matEquinoxEquToAltAz* Mat4d::xrotation(eps_A));
863 refr->setPostTransfoMat(matAltAzModelView);
864 return StelProjector::ModelViewTranformP(refr);
865 }
866
867 //! Get the modelview matrix for observer-centric equatorial at equinox drawing
getEquinoxEquModelViewTransform(RefractionMode refMode) const868 StelProjector::ModelViewTranformP StelCore::getEquinoxEquModelViewTransform(RefractionMode refMode) const
869 {
870 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
871 return StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(matAltAzModelView*matEquinoxEquToAltAz));
872 Refraction* refr = new Refraction(skyDrawer->getRefraction());
873 // The pretransform matrix will convert from input coordinates to AltAz needed by the refraction function.
874 refr->setPreTransfoMat(matEquinoxEquToAltAz);
875 refr->setPostTransfoMat(matAltAzModelView);
876 return StelProjector::ModelViewTranformP(refr);
877 }
878
879 //! Get the modelview matrix for observer-centric altazimuthal drawing
getAltAzModelViewTransform(RefractionMode refMode) const880 StelProjector::ModelViewTranformP StelCore::getAltAzModelViewTransform(RefractionMode refMode) const
881 {
882 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
883 {
884 // Catch problem with improperly initialized matAltAzModelView
885 Q_ASSERT(matAltAzModelView[0]==matAltAzModelView[0]);
886 return StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(matAltAzModelView));
887 }
888 Refraction* refr = new Refraction(skyDrawer->getRefraction());
889 // The pretransform matrix will convert from input coordinates to AltAz needed by the refraction function.
890 refr->setPostTransfoMat(matAltAzModelView);
891 return StelProjector::ModelViewTranformP(refr);
892 }
893
894 //! Get the modelview matrix for observer-centric J2000 equatorial drawing
getJ2000ModelViewTransform(RefractionMode refMode) const895 StelProjector::ModelViewTranformP StelCore::getJ2000ModelViewTransform(RefractionMode refMode) const
896 {
897 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
898 return StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(matAltAzModelView*matEquinoxEquToAltAz*matJ2000ToEquinoxEqu));
899 Refraction* refr = new Refraction(skyDrawer->getRefraction());
900 // The pretransform matrix will convert from input coordinates to AltAz needed by the refraction function.
901 refr->setPreTransfoMat(matEquinoxEquToAltAz*matJ2000ToEquinoxEqu);
902 refr->setPostTransfoMat(matAltAzModelView);
903 return StelProjector::ModelViewTranformP(refr);
904 }
905
906 //! Get the modelview matrix for observer-centric Galactic equatorial drawing
getGalacticModelViewTransform(RefractionMode refMode) const907 StelProjector::ModelViewTranformP StelCore::getGalacticModelViewTransform(RefractionMode refMode) const
908 {
909 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
910 return StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(matAltAzModelView*matEquinoxEquToAltAz*matJ2000ToEquinoxEqu*matGalacticToJ2000));
911 Refraction* refr = new Refraction(skyDrawer->getRefraction());
912 // The pretransform matrix will convert from input coordinates to AltAz needed by the refraction function.
913 refr->setPreTransfoMat(matEquinoxEquToAltAz*matJ2000ToEquinoxEqu*matGalacticToJ2000);
914 refr->setPostTransfoMat(matAltAzModelView);
915 return StelProjector::ModelViewTranformP(refr);
916 }
917
918 //! Get the modelview matrix for observer-centric Supergalactic equatorial drawing
getSupergalacticModelViewTransform(RefractionMode refMode) const919 StelProjector::ModelViewTranformP StelCore::getSupergalacticModelViewTransform(RefractionMode refMode) const
920 {
921 if (refMode==RefractionOff || skyDrawer==Q_NULLPTR || (refMode==RefractionAuto && skyDrawer->getFlagHasAtmosphere()==false))
922 return StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(matAltAzModelView*matEquinoxEquToAltAz*matJ2000ToEquinoxEqu*matSupergalacticToJ2000));
923 Refraction* refr = new Refraction(skyDrawer->getRefraction());
924 // The pretransform matrix will convert from input coordinates to AltAz needed by the refraction function.
925 refr->setPreTransfoMat(matEquinoxEquToAltAz*matJ2000ToEquinoxEqu*matSupergalacticToJ2000);
926 refr->setPostTransfoMat(matAltAzModelView);
927 return StelProjector::ModelViewTranformP(refr);
928 }
929
930 // GZ: One of the most important functions, totally void of doc. :-(
931 // called in update() (for every frame)
updateTransformMatrices()932 void StelCore::updateTransformMatrices()
933 {
934 matAltAzToEquinoxEqu = position->getRotAltAzToEquatorial(getJD(), getJDE());
935 matEquinoxEquToAltAz = matAltAzToEquinoxEqu.transpose();
936
937 // multiply static J2000 earth axis tilt (eclipticalJ2000<->equatorialJ2000)
938 // in effect, this matrix transforms from VSOP87 ecliptical J2000 to planet-based equatorial coordinates.
939 // For earth, matJ2000ToEquinoxEqu is the precession matrix.
940 matEquinoxEquDateToJ2000 = matVsop87ToJ2000 * position->getRotEquatorialToVsop87();
941 matJ2000ToEquinoxEqu = matEquinoxEquDateToJ2000.transpose();
942 matJ2000ToAltAz = matEquinoxEquToAltAz*matJ2000ToEquinoxEqu;
943 matAltAzToJ2000 = matJ2000ToAltAz.transpose();
944
945 matHeliocentricEclipticToEquinoxEqu = matJ2000ToEquinoxEqu * matVsop87ToJ2000 * Mat4d::translation(-position->getCenterVsop87Pos());
946
947 // These two next have to take into account the position of the observer on the earth/planet of observation.
948 Mat4d matAltAzToVsop87 = matJ2000ToVsop87 * matEquinoxEquDateToJ2000 * matAltAzToEquinoxEqu;
949 //Mat4d tmp1 = matJ2000ToVsop87 * matEquinoxEquDateToJ2000;
950
951 // Before 0.14 getDistanceFromCenter assumed spherical planets. Now uses rectangular coordinates for observer!
952 // In series 0.14 and 0.15, this was erroneous: offset by distance rho, but in the wrong direction.
953 // Attempt to fix LP:1275092. This improves the situation, but is still not perfect.
954 // Please keep the commented stuff until situation is really solved.
955 if (flagUseTopocentricCoordinates)
956 {
957 const Vec4d offset=position->getTopographicOffsetFromCenter(); // [rho cosPhi', rho sinPhi', phi'_rad, rho]
958 const double sigma=static_cast<double>(position->getCurrentLocation().latitude)*M_PI/180.0 - offset.v[2];
959 const double rho=offset.v[3];
960
961 matAltAzToHeliocentricEclipticJ2000 = Mat4d::translation(position->getCenterVsop87Pos()) * matAltAzToVsop87 *
962 Mat4d::translation(Vec3d(rho*sin(sigma), 0., rho*cos(sigma) ));
963
964 matHeliocentricEclipticJ2000ToAltAz =
965 Mat4d::translation(Vec3d(-rho*sin(sigma), 0., -rho*cos(sigma))) * matAltAzToVsop87.transpose() *
966 Mat4d::translation(-position->getCenterVsop87Pos());
967
968 // Here I tried to split tmp matrix. This does not work:
969 // matAltAzToHeliocentricEclipticJ2000 = Mat4d::translation(position->getCenterVsop87Pos()) * tmp1 *
970 // Mat4d::translation(Vec3d(rho*sin(sigma), 0., rho*cos(sigma) )) * matAltAzToEquinoxEqu;
971
972 // matHeliocentricEclipticJ2000ToAltAz =
973 // matEquinoxEquToAltAz *
974 // Mat4d::translation(Vec3d(-rho*sin(sigma), 0., -rho*cos(sigma))) * tmp1.transpose() *
975 // Mat4d::translation(-position->getCenterVsop87Pos());
976
977
978 // matAltAzToHeliocentricEclipticJ2000 = Mat4d::translation(position->getCenterVsop87Pos()) * tmp *
979 // Mat4d::translation(Vec3d(0.,0., position->getDistanceFromCenter()));
980
981 // matHeliocentricEclipticJ2000ToAltAz = Mat4d::translation(Vec3d(0.,0.,-position->getDistanceFromCenter())) * tmp.transpose() *
982 // Mat4d::translation(-position->getCenterVsop87Pos());
983 }
984 else
985 {
986 matAltAzToHeliocentricEclipticJ2000 = Mat4d::translation(position->getCenterVsop87Pos()) * matAltAzToVsop87;
987 matHeliocentricEclipticJ2000ToAltAz = matAltAzToVsop87.transpose() * Mat4d::translation(-position->getCenterVsop87Pos());
988 }
989 }
990
991 // Return the observer heliocentric position
getObserverHeliocentricEclipticPos() const992 Vec3d StelCore::getObserverHeliocentricEclipticPos() const
993 {
994 return Vec3d(matAltAzToHeliocentricEclipticJ2000[12], matAltAzToHeliocentricEclipticJ2000[13], matAltAzToHeliocentricEclipticJ2000[14]);
995 }
996
997 // Set the location to use by default at startup
setDefaultLocationID(const QString & id)998 void StelCore::setDefaultLocationID(const QString& id)
999 {
1000 StelLocation location = StelApp::getInstance().getLocationMgr().locationForString(id);
1001 if (!location.isValid())
1002 {
1003 qWarning() << "Trying to set an invalid location" << id;
1004 return;
1005 }
1006 defaultLocationID = id;
1007 QSettings* conf = StelApp::getInstance().getSettings();
1008 Q_ASSERT(conf);
1009 conf->setValue("init_location/location", id);
1010 }
1011
returnToDefaultLocation()1012 void StelCore::returnToDefaultLocation()
1013 {
1014 StelLocationMgr& locationMgr = StelApp::getInstance().getLocationMgr();
1015 StelLocation loc = locationMgr.locationForString(defaultLocationID);
1016 if (loc.isValid())
1017 moveObserverTo(loc, 0.);
1018 }
1019
returnToHome()1020 void StelCore::returnToHome()
1021 {
1022 // Using returnToDefaultLocation() and getCurrentLocation() introduce issue, because for flying
1023 // between planets using SpaceShip and second method give does not exist data
1024 StelLocationMgr& locationMgr = StelApp::getInstance().getLocationMgr();
1025 StelLocation loc;
1026 if (defaultLocationID == "auto")
1027 {
1028 locationMgr.locationFromIP();
1029 loc = locationMgr.getLastResortLocation();
1030 }
1031 else
1032 loc = locationMgr.locationForString(defaultLocationID);
1033
1034 if (loc.isValid())
1035 moveObserverTo(loc, 0.);
1036
1037 PlanetP p = GETSTELMODULE(SolarSystem)->searchByEnglishName(loc.planetName);
1038 QSettings* conf = StelApp::getInstance().getSettings();
1039
1040 LandscapeMgr* landscapeMgr = GETSTELMODULE(LandscapeMgr);
1041 landscapeMgr->setCurrentLandscapeID(landscapeMgr->getDefaultLandscapeID());
1042 landscapeMgr->setFlagAtmosphere(p->hasAtmosphere() && conf->value("landscape/flag_atmosphere", true).toBool());
1043 landscapeMgr->setFlagFog(p->hasAtmosphere() && conf->value("landscape/flag_fog", true).toBool());
1044 landscapeMgr->setFlagLandscape(!p->getEnglishName().contains("observer", Qt::CaseInsensitive) && conf->value("landscape/flag_landscape", true).toBool());
1045
1046 GETSTELMODULE(StelObjectMgr)->unSelect();
1047
1048 StelMovementMgr* smmgr = getMovementMgr();
1049 smmgr->setViewDirectionJ2000(altAzToJ2000(smmgr->getInitViewingDirection(), StelCore::RefractionOff));
1050 smmgr->zoomTo(smmgr->getInitFov(), 1.);
1051 }
1052
getJDOfLastJDUpdate() const1053 double StelCore::getJDOfLastJDUpdate() const
1054 {
1055 return jdOfLastJDUpdate;
1056 }
1057
setMilliSecondsOfLastJDUpdate(qint64 millis)1058 void StelCore::setMilliSecondsOfLastJDUpdate(qint64 millis)
1059 {
1060 milliSecondsOfLastJDUpdate = millis;
1061 }
1062
getMilliSecondsOfLastJDUpdate() const1063 qint64 StelCore::getMilliSecondsOfLastJDUpdate() const
1064 {
1065 return milliSecondsOfLastJDUpdate;
1066 }
1067
setJD(double newJD)1068 void StelCore::setJD(double newJD)
1069 {
1070 JD.first=newJD;
1071 JD.second=computeDeltaT(newJD);
1072 resetSync();
1073 }
1074
getJD() const1075 double StelCore::getJD() const
1076 {
1077 return JD.first;
1078 }
1079
setJDE(double newJDE)1080 void StelCore::setJDE(double newJDE)
1081 {
1082 // nitpickerish this is not exact, but as good as it gets...
1083 JD.second=computeDeltaT(newJDE);
1084 JD.first=newJDE-JD.second/86400.0;
1085 resetSync();
1086 }
1087
getJDE() const1088 double StelCore::getJDE() const
1089 {
1090 return JD.first+JD.second/86400.0;
1091 }
1092
1093
setMJDay(double MJD)1094 void StelCore::setMJDay(double MJD)
1095 {
1096 setJD(MJD+2400000.5);
1097 }
1098
getMJDay() const1099 double StelCore::getMJDay() const
1100 {
1101 return JD.first-2400000.5;
1102 }
1103
getPresetSkyTime() const1104 double StelCore::getPresetSkyTime() const
1105 {
1106 return presetSkyTime;
1107 }
1108
setPresetSkyTime(double d)1109 void StelCore::setPresetSkyTime(double d)
1110 {
1111 presetSkyTime=d;
1112 }
1113
setTimeRate(double ts)1114 void StelCore::setTimeRate(double ts)
1115 {
1116 timeSpeed=ts;
1117 resetSync();
1118 emit timeRateChanged(timeSpeed);
1119 }
1120
getTimeRate() const1121 double StelCore::getTimeRate() const
1122 {
1123 return timeSpeed;
1124 }
1125
revertTimeDirection(void)1126 void StelCore::revertTimeDirection(void)
1127 {
1128 setTimeRate(-1*getTimeRate());
1129 }
1130
moveObserverToSelected()1131 void StelCore::moveObserverToSelected()
1132 {
1133 StelObjectMgr* objmgr = GETSTELMODULE(StelObjectMgr);
1134 Q_ASSERT(objmgr);
1135 if (objmgr->getWasSelected())
1136 {
1137 Planet* pl = dynamic_cast<Planet*>(objmgr->getSelectedObject()[0].data());
1138 if (pl)
1139 {
1140 // We need to move to the selected planet. Try to generate a location from the current one
1141 StelLocation loc = getCurrentLocation();
1142 if (loc.planetName != pl->getEnglishName())
1143 {
1144 loc.planetName = pl->getEnglishName();
1145 loc.name = "landing site";
1146 loc.state = "";
1147
1148 // Let's try guess name of location...
1149 LocationMap results = StelApp::getInstance().getLocationMgr().pickLocationsNearby(loc.planetName, loc.longitude, loc.latitude, 1.0f);
1150 if (results.size()>0)
1151 loc = results.value(results.firstKey()); // ...and use it!
1152
1153 moveObserverTo(loc);
1154 }
1155 }
1156 else
1157 {
1158 NomenclatureItem* ni = dynamic_cast<NomenclatureItem*>(objmgr->getSelectedObject()[0].data());
1159 if (ni)
1160 {
1161 // We need to move to the nomenclature item's host planet.
1162 StelLocation loc; // = getCurrentLocation();
1163 loc.planetName = ni->getPlanet()->getEnglishName();
1164 loc.name=ni->getEnglishName();
1165 loc.state = "";
1166 loc.longitude=ni->getLongitude();
1167 loc.latitude=ni->getLatitude();
1168 loc.bortleScaleIndex=1;
1169
1170 moveObserverTo(loc);
1171 objmgr->unSelect(); // no use to keep it: Marker will flicker around the screen.
1172 }
1173 }
1174 }
1175 StelMovementMgr* mmgr = GETSTELMODULE(StelMovementMgr);
1176 Q_ASSERT(mmgr);
1177 mmgr->setFlagTracking(false);
1178 }
1179
1180 // Get the informations on the current location
getCurrentLocation() const1181 const StelLocation& StelCore::getCurrentLocation() const
1182 {
1183 return position->getCurrentLocation();
1184 }
1185
getCurrentPlanet() const1186 const QSharedPointer<Planet> StelCore::getCurrentPlanet() const
1187 {
1188 return position->getHomePlanet();
1189 }
1190
getCurrentObserver() const1191 const StelObserver *StelCore::getCurrentObserver() const
1192 {
1193 return position;
1194 }
1195
setObserver(StelObserver * obs)1196 void StelCore::setObserver(StelObserver *obs)
1197 {
1198 delete position;
1199 position = obs;
1200 if (!getUseCustomTimeZone() && obs->getCurrentLocation().ianaTimeZone.length()>0)
1201 setCurrentTimeZone(obs->getCurrentLocation().ianaTimeZone);
1202 }
1203
1204 // Smoothly move the observer to the given location
moveObserverTo(const StelLocation & target,double duration,double durationIfPlanetChange)1205 void StelCore::moveObserverTo(const StelLocation& target, double duration, double durationIfPlanetChange)
1206 {
1207 double d = (getCurrentLocation().planetName==target.planetName) ? duration : durationIfPlanetChange;
1208 if (d>0.)
1209 {
1210 StelLocation curLoc = getCurrentLocation();
1211 if (position->isTraveling())
1212 {
1213 // Avoid using a temporary location name to create another temporary one (otherwise it looks like loc1 -> loc2 -> loc3 etc..)
1214 curLoc.name = ".";
1215 }
1216 SpaceShipObserver* newObs = new SpaceShipObserver(curLoc, target, d);
1217 setObserver(newObs);
1218 newObs->update(0);
1219 }
1220 else
1221 {
1222 setObserver(new StelObserver(target));
1223 }
1224 emit targetLocationChanged(target);
1225 emit locationChanged(getCurrentLocation());
1226 }
1227
getUTCOffset(const double JD) const1228 double StelCore::getUTCOffset(const double JD) const
1229 {
1230 int year, month, day, hour, minute, second;
1231 StelUtils::getDateFromJulianDay(JD, &year, &month, &day);
1232 StelUtils::getTimeFromJulianDay(JD, &hour, &minute, &second);
1233 // as analogous to second statement in getJDFromDate, nkerr
1234 if ( year <= 0 )
1235 {
1236 year = year - 1;
1237 }
1238 //getTime/DateFromJulianDay returns UTC time, not local time
1239 QDateTime universal(QDate(year, month, day), QTime(hour, minute, second), Qt::UTC);
1240 if (!universal.isValid())
1241 {
1242 //qWarning() << "JD " << QString("%1").arg(JD) << " out of bounds of QT help with GMT shift, using current datetime";
1243 // Assumes the GMT shift was always the same before year -4710
1244 // NOTE: QDateTime has no year 0, and therefore likely different leap year rules.
1245 // Under which circumstances do we get invalid universal?
1246 universal = QDateTime(QDate(-4710, month, day), QTime(hour, minute, second), Qt::UTC);
1247 }
1248
1249 #if defined(Q_OS_WIN)
1250 if (abs(year)<3)
1251 {
1252 // Mitigate a QTBUG on Windows (GH #594).
1253 // This bug causes offset to be MIN_INT in January to March, 1AD.
1254 // We assume a constant offset in this remote history,
1255 // so we construct yet another date to get a valid offset.
1256 // Application of the named time zones is inappropriate in any case.
1257 universal = QDateTime(QDate(3, month, day), QTime(hour, minute, second), Qt::UTC);
1258 }
1259 #endif
1260 StelLocation loc = getCurrentLocation();
1261 QString tzName = getCurrentTimeZone();
1262 QTimeZone tz(tzName.toUtf8());
1263 if (!tz.isValid() && !QString("LMST LTST system_default").contains(tzName))
1264 {
1265 qWarning() << "Invalid timezone: " << tzName;
1266 }
1267
1268 qint64 shiftInSeconds = 0;
1269 if (tzName=="system_default" || (loc.planetName=="Earth" && !tz.isValid() && !QString("LMST LTST").contains(tzName)))
1270 {
1271 QDateTime local = universal.toLocalTime();
1272 //Both timezones should be interpreted as UTC because secsTo() converts both
1273 //times to UTC if their zones have different daylight saving time rules.
1274 local.setTimeSpec(Qt::UTC);
1275 shiftInSeconds = universal.secsTo(local);
1276 if (abs(shiftInSeconds)>50000 || shiftInSeconds==INT_MIN)
1277 {
1278 qDebug() << "TZ system_default or invalid, At JD" << QString::number(JD, 'g', 11) << ", shift:" << shiftInSeconds;
1279 }
1280 }
1281 else
1282 {
1283 // The first adoption of a standard time was on December 1, 1847 in Great Britain
1284 if (tz.isValid() && loc.planetName=="Earth" && (JD>=StelCore::TZ_ERA_BEGINNING || getUseCustomTimeZone()))
1285 {
1286 if (getUseDST())
1287 shiftInSeconds = tz.offsetFromUtc(universal);
1288 else
1289 shiftInSeconds = tz.standardTimeOffset(universal);
1290 if (abs(shiftInSeconds)>500000 || shiftInSeconds==INT_MIN)
1291 {
1292 // Something very strange has happened. The Windows-only clause above already mitigated GH #594.
1293 // Trigger this with a named custom TZ like Europe/Stockholm.
1294 // Then try to wheel back some date in January-March from year 10 to 0. Instead of year 1, it jumps to 70,
1295 // an offset of INT_MIN
1296 qWarning() << "ERROR TRAPPED! --- Please submit a bug report with this logfile attached.";
1297 qWarning() << "TZ" << tz << "valid, but at JD" << QString::number(JD, 'g', 11) << ", shift:" << shiftInSeconds;
1298 qWarning() << "Universal reference date: " << universal.toString();
1299 }
1300 }
1301 else
1302 {
1303 shiftInSeconds = qRound((loc.longitude/15.f)*3600.f); // Local Mean Solar Time
1304 }
1305 if (tzName=="LTST")
1306 shiftInSeconds += getSolutionEquationOfTime(JD)*60;
1307 }
1308 #ifdef Q_OS_WIN
1309 // A dirty hack for report: https://github.com/Stellarium/stellarium/issues/686
1310 // TODO: switch to IANA TZ on all operating systems
1311 if (tzName=="Europe/Volgograd")
1312 shiftInSeconds = 4*3600; // UTC+04:00
1313 #endif
1314
1315 // Extraterrestrial: Either use the configured Terrestrial timezone, or even a pseudo-LMST based on planet's rotation speed?
1316 if (loc.planetName!="Earth")
1317 {
1318 if (tz.isValid() && (JD>=StelCore::TZ_ERA_BEGINNING || getUseCustomTimeZone()))
1319 {
1320 if (getUseDST())
1321 shiftInSeconds = tz.offsetFromUtc(universal);
1322 else
1323 shiftInSeconds = tz.standardTimeOffset(universal);
1324 if (shiftInSeconds==INT_MIN) // triggered error
1325 {
1326 // Something very strange has happened. The Windows-only clause above already mitigated GH #594.
1327 // Trigger this with a named custom TZ like Europe/Stockholm.
1328 // Then try to wheel back some date in January-March from year 10 to 0. Instead of year 1, it jumps to 70,
1329 // an offset of INT_MIN
1330 qWarning() << "ERROR TRAPPED! --- Please submit a bug report with this logfile attached.";
1331 qWarning() << "TZ" << tz << "valid, but at JD" << QString::number(JD, 'g', 11) << ", shift:" << shiftInSeconds;
1332 qWarning() << "Universal reference date: " << universal.toString();
1333 }
1334 }
1335 else
1336 {
1337 // TODO: This should give "mean solar time" for any planet.
1338 // Combine rotation and orbit, or (for moons) rotation and orbit of parent planet.
1339 // LTST is even worse, needs equation of time for other planets.
1340 shiftInSeconds = 0; // For now, give UT
1341 }
1342 }
1343
1344 return shiftInSeconds / 3600.0;
1345 }
1346
getCurrentTimeZone() const1347 QString StelCore::getCurrentTimeZone() const
1348 {
1349 return currentTimeZone;
1350 }
1351
setCurrentTimeZone(const QString & tz)1352 void StelCore::setCurrentTimeZone(const QString& tz)
1353 {
1354 if (StelApp::getInstance().getLocationMgr().getAllTimezoneNames().contains(tz))
1355 {
1356 currentTimeZone = tz;
1357 emit(currentTimeZoneChanged(tz));
1358 }
1359 else
1360 {
1361 qWarning() << "StelCore: Invalid timezone name:" << tz << " -- not setting timezone.";
1362 }
1363 }
1364
getUseDST() const1365 bool StelCore::getUseDST() const
1366 {
1367 return flagUseDST;
1368 }
1369
setUseDST(const bool b)1370 void StelCore::setUseDST(const bool b)
1371 {
1372 flagUseDST = b;
1373 StelApp::getInstance().getSettings()->setValue("localization/flag_dst", b);
1374 emit flagUseDSTChanged(b);
1375 }
1376
getUseCustomTimeZone() const1377 bool StelCore::getUseCustomTimeZone() const
1378 {
1379 return flagUseCTZ;
1380 }
1381
setUseCustomTimeZone(const bool b)1382 void StelCore::setUseCustomTimeZone(const bool b)
1383 {
1384 flagUseCTZ = b;
1385 emit useCustomTimeZoneChanged(b);
1386 }
1387
getSolutionEquationOfTime(const double JDE) const1388 double StelCore::getSolutionEquationOfTime(const double JDE) const
1389 {
1390 double tau = (JDE - 2451545.0)/365250.0;
1391 double sunMeanLongitude = 280.4664567 + tau*(360007.6892779 + tau*(0.03032028 + tau*(1./49931. - tau*(1./15300. - tau/2000000.))));
1392
1393 // reduce the angle
1394 sunMeanLongitude = std::fmod(sunMeanLongitude, 360.);
1395 // force it to be the positive remainder, so that 0 <= angle < 360
1396 sunMeanLongitude = std::fmod(sunMeanLongitude + 360., 360.);
1397
1398 Vec3d pos = GETSTELMODULE(StelObjectMgr)->searchByName("Sun")->getEquinoxEquatorialPos(this);
1399 double ra, dec;
1400 StelUtils::rectToSphe(&ra, &dec, pos);
1401
1402 // covert radians to degrees and reduce the angle
1403 double alpha = std::fmod(ra*M_180_PI, 360.);
1404 // force it to be the positive remainder, so that 0 <= angle < 360
1405 alpha = std::fmod(alpha + 360., 360.);
1406
1407 double deltaPsi, deltaEps;
1408 getNutationAngles(JDE, &deltaPsi, &deltaEps); // these are radians!
1409 //double equation = 4*(sunMeanLongitude - 0.0057183 - alpha + get_nutation_longitude(JDE)*cos(get_mean_ecliptical_obliquity(JDE)));
1410 double equation = 4*(sunMeanLongitude - 0.0057183 - alpha + deltaPsi*M_180_PI*cos(getPrecessionAngleVondrakEpsilon(JDE)));
1411 // The equation of time is always smaller 20 minutes in absolute value
1412 if (qAbs(equation)>20)
1413 {
1414 // If absolute value of the equation of time appears to be too large, add 24 hours (1440 minutes) to or subtract it from our result
1415 if (equation>0.)
1416 equation -= 1440.;
1417 else
1418 equation += 1440.;
1419 }
1420
1421 return equation;
1422 }
1423
1424 //! Set stellarium time to current real world time
setTimeNow()1425 void StelCore::setTimeNow()
1426 {
1427 setJD(StelUtils::getJDFromSystem());
1428 // Force emit dateChanged
1429 emit dateChanged();
1430 }
1431
setTodayTime(const QTime & target)1432 void StelCore::setTodayTime(const QTime& target)
1433 {
1434 QDateTime dt = QDateTime::currentDateTime();
1435 if (target.isValid())
1436 {
1437 dt.setTime(target);
1438 // don't forget to adjust for timezone / daylight savings.
1439 double JD = StelUtils::qDateTimeToJd(dt)-(static_cast<double>(getUTCOffset(StelUtils::getJDFromSystem())) * JD_HOUR);
1440 setJD(JD);
1441 }
1442 else
1443 {
1444 qWarning() << "WARNING - time passed to StelCore::setTodayTime is not valid. The system time will be used." << target;
1445 setTimeNow();
1446 }
1447 }
1448
1449 //! Get whether the current stellarium time is the real world time
getIsTimeNow(void) const1450 bool StelCore::getIsTimeNow(void) const
1451 {
1452 // cache last time to prevent to much slow system call
1453 static double lastJD = getJD();
1454 static bool previousResult = (fabs(getJD()-(StelUtils::getJDFromSystem()))<JD_SECOND);
1455 if (fabs(lastJD-getJD())>JD_SECOND/4)
1456 {
1457 lastJD = getJD();
1458 previousResult = (fabs(getJD()-(StelUtils::getJDFromSystem()))<JD_SECOND);
1459 }
1460 return previousResult;
1461 }
1462
getInitTodayTime(void) const1463 QTime StelCore::getInitTodayTime(void) const
1464 {
1465 return initTodayTime;
1466 }
1467
setInitTodayTime(const QTime & time)1468 void StelCore::setInitTodayTime(const QTime& time)
1469 {
1470 initTodayTime=time;
1471 }
1472
setPresetSkyTime(QDateTime dateTime)1473 void StelCore::setPresetSkyTime(QDateTime dateTime)
1474 {
1475 setPresetSkyTime(StelUtils::qDateTimeToJd(dateTime));
1476 }
1477
addMinute()1478 void StelCore::addMinute()
1479 {
1480 addSolarDays(JD_MINUTE);
1481 }
1482
addHour()1483 void StelCore::addHour()
1484 {
1485 addSolarDays(JD_HOUR);
1486 }
1487
addDay()1488 void StelCore::addDay()
1489 {
1490 addSolarDays(1.0);
1491 }
1492
addWeek()1493 void StelCore::addWeek()
1494 {
1495 addSolarDays(7.0);
1496 }
1497
addSiderealDay()1498 void StelCore::addSiderealDay()
1499 {
1500 addSiderealDays(1.0);
1501 }
1502
addSiderealWeek()1503 void StelCore::addSiderealWeek()
1504 {
1505 addSiderealDays(7.0);
1506 }
1507
addSiderealYear()1508 void StelCore::addSiderealYear()
1509 {
1510 addSiderealYears(1.);
1511 }
1512
addSiderealYears(double n)1513 void StelCore::addSiderealYears(double n)
1514 {
1515 double days = 365.256363004;
1516 double sidereal = getLocalSiderealYearLength();
1517 Planet::PlanetType ptype = getCurrentPlanet()->getPlanetType();
1518 if (ptype!=Planet::isObserver && ptype!=Planet::isArtificial && sidereal>0.)
1519 days = sidereal;
1520
1521 addSolarDays(days*n);
1522 }
1523
addSynodicMonth()1524 void StelCore::addSynodicMonth()
1525 {
1526 addSolarDays(29.530588853);
1527 }
1528
addSaros()1529 void StelCore::addSaros()
1530 {
1531 // 223 synodic months
1532 addSolarDays(223*29.530588853);
1533 }
1534
addDraconicMonth()1535 void StelCore::addDraconicMonth()
1536 {
1537 addSolarDays(27.212220817);
1538 }
1539
addMeanTropicalMonth()1540 void StelCore::addMeanTropicalMonth()
1541 {
1542 addSolarDays(27.321582241);
1543 }
1544
addCalendricMonth()1545 void StelCore::addCalendricMonth()
1546 {
1547 double cjd = getJD();
1548 int year, month, day, hour, minute, second;
1549 StelUtils::getDateFromJulianDay(cjd, &year, &month, &day);
1550 StelUtils::getTimeFromJulianDay(cjd, &hour, &minute, &second);
1551 month++;
1552 if (month>12)
1553 {
1554 month = 1;
1555 year++;
1556 }
1557 StelUtils::getJDFromDate(&cjd, year, month, day, hour, minute, second);
1558 setJD(cjd);
1559 }
1560
addAnomalisticMonth()1561 void StelCore::addAnomalisticMonth()
1562 {
1563 addSolarDays(27.554549878);
1564 }
1565
addAnomalisticYear()1566 void StelCore::addAnomalisticYear()
1567 {
1568 addAnomalisticYears(1.);
1569 }
1570
addAnomalisticYears(double n)1571 void StelCore::addAnomalisticYears(double n)
1572 {
1573 addSolarDays(365.259636*n);
1574 }
1575
addDraconicYear()1576 void StelCore::addDraconicYear()
1577 {
1578 addSolarDays(346.620075883);
1579 }
1580
addMeanTropicalYear()1581 void StelCore::addMeanTropicalYear()
1582 {
1583 addMeanTropicalYears(1.0);
1584 }
1585
addTropicalYear()1586 void StelCore::addTropicalYear()
1587 {
1588 // Source: J. Meeus. More Mathematical Astronomy Morsels. 2002, p. 358.
1589 // Meeus, J. & Savoie, D. The history of the tropical year. Journal of the British Astronomical Association, vol.102, no.1, p.40-42
1590 // http://articles.adsabs.harvard.edu//full/1992JBAA..102...40M
1591 const double T = (getJD()-2451545.0)/365250.0;
1592 addSolarDays(365.242189623 - T*(0.000061522 - T*(0.0000000609 + T*0.00000026525)));
1593 }
1594
addMeanTropicalYears(double n)1595 void StelCore::addMeanTropicalYears(double n)
1596 {
1597 // Source: https://en.wikipedia.org/wiki/Tropical_year#Mean_tropical_year_current_value
1598 addSolarDays(365.2421897*n); // The mean tropical year on January 1, 2000
1599 }
1600
addJulianYear()1601 void StelCore::addJulianYear()
1602 {
1603 addJulianYears(1.);
1604 }
1605
addGaussianYear()1606 void StelCore::addGaussianYear()
1607 {
1608 addSolarDays(365.2568983);
1609 }
1610
addJulianYears(double n)1611 void StelCore::addJulianYears(double n)
1612 {
1613 addSolarDays(365.25*n);
1614 }
1615
subtractMinute()1616 void StelCore::subtractMinute()
1617 {
1618 addSolarDays(-JD_MINUTE);
1619 }
1620
subtractHour()1621 void StelCore::subtractHour()
1622 {
1623 addSolarDays(-JD_HOUR);
1624 }
1625
subtractDay()1626 void StelCore::subtractDay()
1627 {
1628 addSolarDays(-1.0);
1629 }
1630
subtractWeek()1631 void StelCore::subtractWeek()
1632 {
1633 addSolarDays(-7.0);
1634 }
1635
subtractSiderealDay()1636 void StelCore::subtractSiderealDay()
1637 {
1638 addSiderealDays(-1.0);
1639 }
1640
subtractSiderealWeek()1641 void StelCore::subtractSiderealWeek()
1642 {
1643 addSiderealDays(-7.0);
1644 }
1645
subtractSiderealYear()1646 void StelCore::subtractSiderealYear()
1647 {
1648 addSiderealYears(-1.);
1649 }
1650
subtractSiderealYears(double n)1651 void StelCore::subtractSiderealYears(double n)
1652 {
1653 addSiderealYears(-n);
1654 }
1655
subtractSynodicMonth()1656 void StelCore::subtractSynodicMonth()
1657 {
1658 addSolarDays(-29.530588853);
1659 }
1660
subtractSaros()1661 void StelCore::subtractSaros()
1662 {
1663 // 223 synodic months
1664 addSolarDays(-223*29.530588853);
1665 }
1666
subtractDraconicMonth()1667 void StelCore::subtractDraconicMonth()
1668 {
1669 addSolarDays(-27.212220817);
1670 }
1671
subtractMeanTropicalMonth()1672 void StelCore::subtractMeanTropicalMonth()
1673 {
1674 addSolarDays(-27.321582241);
1675 }
1676
subtractCalendricMonth()1677 void StelCore::subtractCalendricMonth()
1678 {
1679 double cjd = getJD();
1680 int year, month, day, hour, minute, second;
1681 StelUtils::getDateFromJulianDay(cjd, &year, &month, &day);
1682 StelUtils::getTimeFromJulianDay(cjd, &hour, &minute, &second);
1683 month--;
1684 if (month<1)
1685 {
1686 month = 12;
1687 year--;
1688 }
1689 StelUtils::getJDFromDate(&cjd, year, month, day, hour, minute, second);
1690 setJD(cjd);
1691 }
1692
subtractAnomalisticMonth()1693 void StelCore::subtractAnomalisticMonth()
1694 {
1695 addSolarDays(-27.554549878);
1696 }
1697
subtractAnomalisticYear()1698 void StelCore::subtractAnomalisticYear()
1699 {
1700 subtractAnomalisticYears(1.);
1701 }
1702
subtractAnomalisticYears(double n)1703 void StelCore::subtractAnomalisticYears(double n)
1704 {
1705 addSolarDays(-365.259636*n);
1706 }
1707
subtractDraconicYear()1708 void StelCore::subtractDraconicYear()
1709 {
1710 addSolarDays(-346.620075883);
1711 }
1712
subtractTropicalYear()1713 void StelCore::subtractTropicalYear()
1714 {
1715 // Source: J. Meeus. More Mathematical Astronomy Morsels. 2002, p. 358.
1716 double T = (getJD()-2451545.0)/365250.0;
1717 addSolarDays(-(365.242189623 - T*(0.000061522 - T*(0.0000000609 + T*0.00000026525))));
1718 }
1719
subtractMeanTropicalYear()1720 void StelCore::subtractMeanTropicalYear()
1721 {
1722 addMeanTropicalYears(-1.0);
1723 }
1724
subtractMeanTropicalYears(double n)1725 void StelCore::subtractMeanTropicalYears(double n)
1726 {
1727 addMeanTropicalYears(-1.0*n);
1728 }
1729
subtractJulianYear()1730 void StelCore::subtractJulianYear()
1731 {
1732 addSolarDays(-365.25);
1733 }
1734
subtractGaussianYear()1735 void StelCore::subtractGaussianYear()
1736 {
1737 addSolarDays(-365.2568983);
1738 }
1739
subtractJulianYears(double n)1740 void StelCore::subtractJulianYears(double n)
1741 {
1742 addSolarDays(-365.25*n);
1743 }
1744
addSolarDays(double d)1745 void StelCore::addSolarDays(double d)
1746 {
1747 const PlanetP& home = getCurrentPlanet();
1748 Planet::PlanetType ptype = home->getPlanetType();
1749 if (ptype!=Planet::isArtificial && ptype!=Planet::isObserver)
1750 d *= home->getMeanSolarDay();
1751
1752 setJD(getJD() + d);
1753
1754 if (qAbs(d)>0.99) // WTF: qAbs(d)>=1.0 not working here!
1755 emit dateChanged();
1756 }
1757
addSiderealDays(double d)1758 void StelCore::addSiderealDays(double d)
1759 {
1760 const PlanetP& home = getCurrentPlanet();
1761 Planet::PlanetType ptype = home->getPlanetType();
1762 if (ptype!=Planet::isArtificial && ptype!=Planet::isObserver)
1763 d *= home->getSiderealDay();
1764
1765 setJD(getJD() + d);
1766 }
1767
1768 // Get the sidereal time of the prime meridian (i.e. Rotation Angle) shifted by the observer longitude
getLocalSiderealTime() const1769 double StelCore::getLocalSiderealTime() const
1770 {
1771 // On Earth, this requires UT deliberately with all its faults, on other planets we use the more regular TT.
1772 return (getCurrentPlanet()->getSiderealTime(getJD(), getJDE())+static_cast<double>(position->getCurrentLocation().longitude))*M_PI/180.;
1773 }
1774
1775 //! Get the duration of a sidereal day for the current observer in day.
getLocalSiderealDayLength() const1776 double StelCore::getLocalSiderealDayLength() const
1777 {
1778 return getCurrentPlanet()->getSiderealDay();
1779 }
1780
1781 //! Get the duration of a sidereal year for the current observer in days.
getLocalSiderealYearLength() const1782 double StelCore::getLocalSiderealYearLength() const
1783 {
1784 return getCurrentPlanet()->getSiderealPeriod();
1785 }
1786
getStartupTimeMode() const1787 QString StelCore::getStartupTimeMode() const
1788 {
1789 return startupTimeMode;
1790 }
1791
1792 //! Increase the time speed
increaseTimeSpeed()1793 void StelCore::increaseTimeSpeed()
1794 {
1795 double s = getTimeRate();
1796 if (s>=JD_SECOND) s*=10.;
1797 else if (s<-JD_SECOND) s/=10.;
1798 else if (s>=0.) s=JD_SECOND;
1799 else s=0.;
1800 setTimeRate(s);
1801 }
1802
1803 //! Decrease the time speed
decreaseTimeSpeed()1804 void StelCore::decreaseTimeSpeed()
1805 {
1806 double s = getTimeRate();
1807 if (s>JD_SECOND) s/=10.;
1808 else if (s<=-JD_SECOND) s*=10.;
1809 else if (s<=0.) s=-JD_SECOND;
1810 else s=0.;
1811 setTimeRate(s);
1812 }
1813
increaseTimeSpeedLess()1814 void StelCore::increaseTimeSpeedLess()
1815 {
1816 double s = getTimeRate();
1817 if (s>=JD_SECOND) s*=2.;
1818 else if (s<-JD_SECOND) s/=2.;
1819 else if (s>=0.) s=JD_SECOND;
1820 else s=0.;
1821 setTimeRate(s);
1822 }
1823
decreaseTimeSpeedLess()1824 void StelCore::decreaseTimeSpeedLess()
1825 {
1826 double s = getTimeRate();
1827 if (s>JD_SECOND) s/=2.;
1828 else if (s<=-JD_SECOND) s*=2.;
1829 else if (s<=0.) s=-JD_SECOND;
1830 else s=0.;
1831 setTimeRate(s);
1832 }
1833
setZeroTimeSpeed()1834 void StelCore::setZeroTimeSpeed()
1835 {
1836 setTimeRate(0);
1837 }
1838
setRealTimeSpeed()1839 void StelCore::setRealTimeSpeed()
1840 {
1841 setTimeRate(JD_SECOND);
1842 }
1843
toggleRealTimeSpeed()1844 void StelCore::toggleRealTimeSpeed()
1845 {
1846 (!getRealTimeSpeed()) ? setRealTimeSpeed() : setZeroTimeSpeed();
1847 }
1848
getRealTimeSpeed() const1849 bool StelCore::getRealTimeSpeed() const
1850 {
1851 return (fabs(timeSpeed-JD_SECOND)<0.0000001);
1852 }
1853
1854 ////////////////////////////////////////////////////////////////////////////////
1855 // Increment time
updateTime(double deltaTime)1856 void StelCore::updateTime(double deltaTime)
1857 {
1858 if (getRealTimeSpeed())
1859 {
1860 JD.first = jdOfLastJDUpdate + (QDateTime::currentMSecsSinceEpoch() - milliSecondsOfLastJDUpdate) / 1000.0 * JD_SECOND;
1861 }
1862 else
1863 {
1864 JD.first = jdOfLastJDUpdate + (QDateTime::currentMSecsSinceEpoch() - milliSecondsOfLastJDUpdate) / 1000.0 * timeSpeed;
1865 }
1866
1867 // Fix time limits to -100000 to +100000 to prevent bugs
1868 if (JD.first>38245309.499988) JD.first = 38245309.499988;
1869 if (JD.first<-34803211.500012) JD.first = -34803211.500012;
1870 JD.second=computeDeltaT(JD.first);
1871
1872 if (position->isObserverLifeOver())
1873 {
1874 // Unselect if the new home planet is the previously selected object
1875 StelObjectMgr* objmgr = GETSTELMODULE(StelObjectMgr);
1876 Q_ASSERT(objmgr);
1877 if (objmgr->getWasSelected() && objmgr->getSelectedObject()[0].data()==getCurrentPlanet())
1878 {
1879 objmgr->unSelect();
1880 }
1881 StelObserver* newObs = position->getNextObserver();
1882 delete position;
1883 position = newObs;
1884 }
1885 if (position->update(deltaTime))
1886 emit(locationChanged(getCurrentLocation()));
1887
1888 // Position of sun and all the satellites (ie planets)
1889 // GZ maybe setting this static can speedup a bit?
1890 static SolarSystem* solsystem = static_cast<SolarSystem*>(StelApp::getInstance().getModuleMgr().getModule("SolarSystem"));
1891 // Likely the most important location where we need JDE:
1892 solsystem->computePositions(getJDE(), getCurrentPlanet());
1893 }
1894
resetSync()1895 void StelCore::resetSync()
1896 {
1897 jdOfLastJDUpdate = getJD();
1898 //use currentMsecsSinceEpoch directly instead of StelApp::getTotalRuntime,
1899 //because the StelApp::startMSecs gets subtracted anyways in update()
1900 //also changed to qint64 to increase precision
1901 milliSecondsOfLastJDUpdate = QDateTime::currentMSecsSinceEpoch();
1902 emit timeSyncOccurred(jdOfLastJDUpdate);
1903 }
1904
registerMathMetaTypes()1905 void StelCore::registerMathMetaTypes()
1906 {
1907 //enables use of these types in QVariant, StelProperty, signals and slots
1908 qRegisterMetaType<Vec2d>();
1909 qRegisterMetaType<Vec2f>();
1910 qRegisterMetaType<Vec2i>();
1911 qRegisterMetaType<Vec3d>();
1912 qRegisterMetaType<Vec3f>();
1913 qRegisterMetaType<Vec3i>();
1914 qRegisterMetaType<Vec4d>();
1915 qRegisterMetaType<Vec4f>();
1916 qRegisterMetaType<Vec4i>();
1917 qRegisterMetaType<Mat4d>();
1918 qRegisterMetaType<Mat4f>();
1919 qRegisterMetaType<Mat3d>();
1920 qRegisterMetaType<Mat3f>();
1921
1922 //registers the QDataStream operators, so that QVariants with these types can be saved
1923 qRegisterMetaTypeStreamOperators<Vec2d>();
1924 qRegisterMetaTypeStreamOperators<Vec2f>();
1925 qRegisterMetaTypeStreamOperators<Vec2i>();
1926 qRegisterMetaTypeStreamOperators<Vec3d>();
1927 qRegisterMetaTypeStreamOperators<Vec3f>();
1928 qRegisterMetaTypeStreamOperators<Vec3i>();
1929 qRegisterMetaTypeStreamOperators<Vec4d>();
1930 qRegisterMetaTypeStreamOperators<Vec4f>();
1931 qRegisterMetaTypeStreamOperators<Vec4i>();
1932 qRegisterMetaTypeStreamOperators<Mat4d>();
1933 qRegisterMetaTypeStreamOperators<Mat4f>();
1934 qRegisterMetaTypeStreamOperators<Mat3d>();
1935 qRegisterMetaTypeStreamOperators<Mat3f>();
1936
1937 //for debugging QVariants with these types, it helps if we register the string converters
1938 QMetaType::registerConverter(&Vec2d::toString);
1939 QMetaType::registerConverter(&Vec2f::toString);
1940 QMetaType::registerConverter(&Vec2i::toString);
1941 QMetaType::registerConverter(&Vec3d::toString);
1942 QMetaType::registerConverter(&Vec3f::toString);
1943 QMetaType::registerConverter(&Vec3i::toString);
1944 QMetaType::registerConverter(&Vec4d::toString);
1945 QMetaType::registerConverter(&Vec4f::toString);
1946 QMetaType::registerConverter(&Vec4i::toString);
1947 }
1948
setStartupTimeMode(const QString & s)1949 void StelCore::setStartupTimeMode(const QString& s)
1950 {
1951 startupTimeMode = s;
1952 }
1953
1954 // return precomputed DeltaT in seconds. Public.
getDeltaT() const1955 double StelCore::getDeltaT() const
1956 {
1957 return JD.second;
1958 }
1959
1960
1961 // compute and return DeltaT in seconds. Try not to call it directly, current DeltaT, JD, and JDE are available.
computeDeltaT(const double JD)1962 double StelCore::computeDeltaT(const double JD)
1963 {
1964 double DeltaT = 0.;
1965 if (currentDeltaTAlgorithm==Custom)
1966 {
1967 // User defined coefficients for quadratic equation for DeltaT may change frequently.
1968 deltaTnDot = deltaTCustomNDot; // n.dot = custom value "/cy/cy
1969 int year, month, day;
1970 StelUtils::getDateFromJulianDay(JD, &year, &month, &day);
1971 double u = (StelUtils::yearFraction(year,month,day)-getDeltaTCustomYear())/100.;
1972 DeltaT = deltaTCustomEquationCoeff[0] + u*(deltaTCustomEquationCoeff[1] + u*deltaTCustomEquationCoeff[2]);
1973 }
1974
1975 else
1976 {
1977 Q_ASSERT(deltaTfunc);
1978 DeltaT=deltaTfunc(JD);
1979 }
1980
1981 if (!deltaTdontUseMoon)
1982 DeltaT += StelUtils::getMoonSecularAcceleration(JD, deltaTnDot, ((de440Active&&EphemWrapper::jd_fits_de440(JD)) ||
1983 (de441Active&&EphemWrapper::jd_fits_de441(JD)) ||
1984 (de430Active&&EphemWrapper::jd_fits_de430(JD)) ||
1985 (de431Active&&EphemWrapper::jd_fits_de431(JD))));
1986
1987 return DeltaT;
1988 }
1989
1990 // set a function pointer here. This should make the actual computation simpler by just calling the function.
setCurrentDeltaTAlgorithm(DeltaTAlgorithm algorithm)1991 void StelCore::setCurrentDeltaTAlgorithm(DeltaTAlgorithm algorithm)
1992 {
1993 currentDeltaTAlgorithm=algorithm;
1994 deltaTdontUseMoon = false; // most algorithms will use it!
1995 switch (currentDeltaTAlgorithm)
1996 {
1997 case WithoutCorrection:
1998 // Without correction, DeltaT is disabled
1999 deltaTfunc = StelUtils::getDeltaTwithoutCorrection;
2000 deltaTnDot = -26.; // n.dot = -26.0"/cy/cy OR WHAT SHALL WE DO HERE?
2001 deltaTdontUseMoon = true;
2002 deltaTstart = INT_MIN;
2003 deltaTfinish = INT_MAX;
2004 break;
2005 case Schoch:
2006 // Schoch (1931) algorithm for DeltaT
2007 deltaTnDot = -29.68; // n.dot = -29.68"/cy/cy
2008 deltaTfunc = StelUtils::getDeltaTBySchoch;
2009 deltaTstart = -300;
2010 deltaTfinish = 1980;
2011 break;
2012 case Clemence:
2013 // Clemence (1948) algorithm for DeltaT
2014 deltaTnDot = -22.44; // n.dot = -22.44 "/cy/cy
2015 deltaTfunc = StelUtils::getDeltaTByClemence;
2016 deltaTstart = 1681;
2017 deltaTfinish = 1900;
2018 break;
2019 case IAU:
2020 // IAU (1952) algorithm for DeltaT, based on observations by Spencer Jones (1939)
2021 deltaTnDot = -22.44; // n.dot = -22.44 "/cy/cy
2022 deltaTfunc = StelUtils::getDeltaTByIAU;
2023 deltaTstart = 1681;
2024 deltaTfinish = 1936; // Details in http://adsabs.harvard.edu/abs/1939MNRAS..99..541S
2025 break;
2026 case AstronomicalEphemeris:
2027 // Astronomical Ephemeris (1960) algorithm for DeltaT
2028 deltaTnDot = -22.44; // n.dot = -22.44 "/cy/cy
2029 deltaTfunc = StelUtils::getDeltaTByAstronomicalEphemeris;
2030 // GZ: What is the source of "1681..1900"? Expl.Suppl.AE 1961-p87 says "...over periods extending back to ancient times"
2031 // I changed to what I estimate.
2032 deltaTstart = -500; // 1681;
2033 deltaTfinish = 2000; // 1900;
2034 break;
2035 case TuckermanGoldstine:
2036 // Tuckerman (1962, 1964) & Goldstine (1973) algorithm for DeltaT
2037 //FIXME: n.dot
2038 deltaTnDot = -22.44; // n.dot = -22.44 "/cy/cy ???
2039 deltaTfunc = StelUtils::getDeltaTByTuckermanGoldstine;
2040 deltaTstart = -600;
2041 deltaTfinish = 1649;
2042 break;
2043 case MullerStephenson:
2044 // Muller & Stephenson (1975) algorithm for DeltaT
2045 deltaTnDot = -37.5; // n.dot = -37.5 "/cy/cy
2046 deltaTfunc = StelUtils::getDeltaTByMullerStephenson;
2047 deltaTstart = -1375;
2048 deltaTfinish = 1975;
2049 break;
2050 case Stephenson1978:
2051 // Stephenson (1978) algorithm for DeltaT
2052 deltaTnDot = -30.0; // n.dot = -30.0 "/cy/cy
2053 deltaTfunc = StelUtils::getDeltaTByStephenson1978;
2054 deltaTstart = INT_MIN; // Range unknown!
2055 deltaTfinish = INT_MAX;
2056 break;
2057 case SchmadelZech1979:
2058 // Schmadel & Zech (1979) algorithm for DeltaT
2059 deltaTnDot = -23.8946; // n.dot = -23.8946 "/cy/cy
2060 deltaTfunc = StelUtils::getDeltaTBySchmadelZech1979;
2061 deltaTstart = 1800;
2062 deltaTfinish = 1975;
2063 break;
2064 case MorrisonStephenson1982:
2065 // Morrison & Stephenson (1982) algorithm for DeltaT (used by RedShift)
2066 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2067 deltaTfunc = StelUtils::getDeltaTByMorrisonStephenson1982;
2068 // FIXME: Is it valid range?
2069 deltaTstart = -4000;
2070 deltaTfinish = 2800;
2071 break;
2072 case StephensonMorrison1984:
2073 // Stephenson & Morrison (1984) algorithm for DeltaT
2074 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2075 deltaTfunc = StelUtils::getDeltaTByStephensonMorrison1984;
2076 deltaTstart = -391;
2077 deltaTfinish = 1600;
2078 break;
2079 case StephensonHoulden:
2080 // Stephenson & Houlden (1986) algorithm for DeltaT. The limits are implicitly given by the tabulated values.
2081 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2082 deltaTfunc = StelUtils::getDeltaTByStephensonHoulden;
2083 deltaTstart = -600;
2084 deltaTfinish = 1650;
2085 break;
2086 case Espenak:
2087 // Espenak (1987, 1989) algorithm for DeltaT
2088 //FIXME: n.dot
2089 deltaTnDot = -23.8946; // n.dot = -23.8946 "/cy/cy ???
2090 deltaTfunc = StelUtils::getDeltaTByEspenak;
2091 deltaTstart = 1950;
2092 deltaTfinish = 2100;
2093 break;
2094 case Borkowski:
2095 // Borkowski (1988) algorithm for DeltaT, relates to ELP2000-85!
2096 deltaTnDot = -23.895; // GZ: I see -23.895 in the paper, not -23.859; (?) // n.dot = -23.859 "/cy/cy
2097 deltaTfunc = StelUtils::getDeltaTByBorkowski;
2098 deltaTstart = -2136;
2099 deltaTfinish = 1715;
2100 break;
2101 case SchmadelZech1988:
2102 // Schmadel & Zech (1988) algorithm for DeltaT
2103 //FIXME: n.dot
2104 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy ???
2105 deltaTfunc = StelUtils::getDeltaTBySchmadelZech1988;
2106 deltaTstart = 1800;
2107 deltaTfinish = 1988;
2108 break;
2109 case ChaprontTouze:
2110 // Chapront-Touzé & Chapront (1991) algorithm for DeltaT
2111 deltaTnDot = -23.8946; // n.dot = -23.8946 "/cy/cy
2112 deltaTfunc = StelUtils::getDeltaTByChaprontTouze;
2113 // FIXME: Is it valid range?
2114 deltaTstart = -4000;
2115 deltaTfinish = 8000;
2116 break;
2117 case StephensonMorrison1995:
2118 // Stephenson & Morrison (1995) algorithm for DeltaT
2119 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2120 deltaTfunc = StelUtils::getDeltaTByStephensonMorrison1995;
2121 deltaTstart = -700;
2122 deltaTfinish = 1600;
2123 break;
2124 case Stephenson1997:
2125 // Stephenson (1997) algorithm for DeltaT
2126 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2127 deltaTfunc = StelUtils::getDeltaTByStephenson1997;
2128 deltaTstart = -500;
2129 deltaTfinish = 1600;
2130 break;
2131 case ChaprontMeeus:
2132 // Chapront, Chapront-Touze & Francou (1997) & Meeus (1998) algorithm for DeltaT
2133 deltaTnDot = -25.7376; // n.dot = -25.7376 "/cy/cy
2134 deltaTfunc = StelUtils::getDeltaTByChaprontMeeus;
2135 deltaTstart = -400; // 1800; // not explicitly given, but guess based on his using ChaprontFrancou which is cited elsewhere in a similar term with -391.
2136 deltaTfinish = 2150; // 1997;
2137 break;
2138 case JPLHorizons:
2139 // JPL Horizons algorithm for DeltaT
2140 deltaTnDot = -25.7376; // n.dot = -25.7376 "/cy/cy
2141 deltaTfunc = StelUtils::getDeltaTByJPLHorizons;
2142 deltaTstart = -2999;
2143 deltaTfinish = 1620;
2144 break;
2145 case MeeusSimons:
2146 // Meeus & Simons (2000) algorithm for DeltaT
2147 deltaTnDot = -25.7376; // n.dot = -25.7376 "/cy/cy
2148 deltaTfunc = StelUtils::getDeltaTByMeeusSimons;
2149 deltaTstart = 1620;
2150 deltaTfinish = 2000;
2151 break;
2152 case ReingoldDershowitz:
2153 // Reingold & Dershowitz (2002, 2007, 2018) algorithm for DeltaT
2154 // FIXME: n.dot
2155 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy ???
2156 deltaTfunc = StelUtils::getDeltaTByReingoldDershowitz;
2157 // GZ: while not original work, it's based on Meeus and therefore the full implementation covers likewise approximately:
2158 // AW: limits from 4th edition:
2159 deltaTstart = -500; //1620;
2160 deltaTfinish = 2150; //2019;
2161 break;
2162 case MontenbruckPfleger:
2163 // Montenbruck & Pfleger (2000) algorithm for DeltaT
2164 // NOTE: book does not contain n.dot value
2165 // FIXME: n.dot
2166 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy ???
2167 deltaTfunc = StelUtils::getDeltaTByMontenbruckPfleger;
2168 deltaTstart = 1825;
2169 deltaTfinish = 2005;
2170 break;
2171 case MorrisonStephenson2004:
2172 // Morrison & Stephenson (2004, 2005) algorithm for DeltaT
2173 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2174 deltaTfunc = StelUtils::getDeltaTByMorrisonStephenson2004;
2175 deltaTstart = -1000;
2176 deltaTfinish = 2000;
2177 break;
2178 case Reijs:
2179 // Reijs (2006) algorithm for DeltaT
2180 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2181 deltaTfunc = StelUtils::getDeltaTByReijs;
2182 deltaTstart = -1500; // -500; // GZ: It models long-term variability, so we should reflect this. Not sure on the begin, though.
2183 deltaTfinish = 1100; // not 1620; // GZ: Not applicable for telescopic era, and better not after 1100 (pers.comm.)
2184 break;
2185 case EspenakMeeus:
2186 // Espenak & Meeus (2006) algorithm for DeltaT
2187 deltaTnDot = -25.858; // n.dot = -25.858 "/cy/cy
2188 deltaTfunc = StelUtils::getDeltaTByEspenakMeeus;
2189 deltaTstart = -1999;
2190 deltaTfinish = 3000;
2191 break;
2192 case EspenakMeeusZeroMoonAccel:
2193 // This is a trying area. Something is wrong with DeltaT, maybe ndot is still not applied correctly.
2194 // Espenak & Meeus (2006) algorithm for DeltaT
2195 deltaTnDot = -25.858; // n.dot = -25.858 "/cy/cy
2196 deltaTdontUseMoon = true;
2197 deltaTfunc = StelUtils::getDeltaTByEspenakMeeus;
2198 deltaTstart = -1999;
2199 deltaTfinish = 3000;
2200 break;
2201 case Banjevic:
2202 // Banjevic (2006) algorithm for DeltaT
2203 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2204 deltaTfunc = StelUtils::getDeltaTByBanjevic;
2205 deltaTstart = -2020;
2206 deltaTfinish = 1620;
2207 break;
2208 case IslamSadiqQureshi:
2209 // Islam, Sadiq & Qureshi (2008 + revisited 2013) algorithm for DeltaT (6 polynomials)
2210 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2211 deltaTfunc = StelUtils::getDeltaTByIslamSadiqQureshi;
2212 deltaTdontUseMoon = true; // Seems this solutions doesn't use value of secular acceleration of the Moon
2213 deltaTstart = 1620;
2214 deltaTfinish = 2007;
2215 break;
2216 case KhalidSultanaZaidi:
2217 // M. Khalid, Mariam Sultana and Faheem Zaidi polinomial approximation of time period 1620-2013 (2014)
2218 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2219 deltaTfunc = StelUtils::getDeltaTByKhalidSultanaZaidi;
2220 deltaTdontUseMoon = true; // Seems this solutions doesn't use value of secular acceleration of the Moon
2221 deltaTstart = 1620;
2222 deltaTfinish = 2013;
2223 break;
2224 case StephensonMorrisonHohenkerk2016:
2225 deltaTnDot = -25.82; // n.dot = -25.82 "/cy/cy
2226 deltaTfunc=StelUtils::getDeltaTByStephensonMorrisonHohenkerk2016;
2227 deltaTstart = -720;
2228 deltaTfinish = 2015;
2229 break;
2230 case Henriksson2017:
2231 // Henriksson solution (2017) for Schoch formula for DeltaT (1931)
2232 // Source: The Acceleration of the Moon and the Universe – the Mass of the Graviton.
2233 // Henriksson, G.
2234 // Advances in Astrophysics, Vol. 2, No. 3, August 2017
2235 // https://doi.org/10.22606/adap.2017.23004
2236 deltaTnDot = -30.128; // n.dot = -30.128"/cy/cy
2237 deltaTfunc = StelUtils::getDeltaTBySchoch;
2238 deltaTstart = -4000;
2239 deltaTfinish = 2000;
2240 break;
2241 case Custom:
2242 // User defined coefficients for quadratic equation for DeltaT. These can change, and we don't use the function pointer here.
2243 deltaTnDot = deltaTCustomNDot; // n.dot = custom value "/cy/cy
2244 deltaTfunc=Q_NULLPTR;
2245 deltaTstart = INT_MIN; // Range unknown!
2246 deltaTfinish = INT_MAX;
2247 break;
2248 default:
2249 deltaTnDot = -26.0; // n.dot = -26.0 "/cy/cy
2250 deltaTfunc=Q_NULLPTR;
2251 deltaTstart = INT_MIN; // Range unknown!
2252 deltaTfinish = INT_MAX;
2253 qCritical() << "StelCore: unknown DeltaT algorithm selected (" << currentDeltaTAlgorithm << ")! (setting nDot=-26., but no function!!)";
2254 }
2255 Q_ASSERT((currentDeltaTAlgorithm==Custom) || (deltaTfunc!=Q_NULLPTR));
2256 }
2257
2258 //! Set the current algorithm for time correction to use
setCurrentDeltaTAlgorithmKey(QString key)2259 void StelCore::setCurrentDeltaTAlgorithmKey(QString key)
2260 {
2261 const QMetaEnum& en = metaObject()->enumerator(metaObject()->indexOfEnumerator("DeltaTAlgorithm"));
2262 DeltaTAlgorithm algo = static_cast<DeltaTAlgorithm>(en.keyToValue(key.toLatin1().data()));
2263 if (algo<0)
2264 {
2265 qWarning() << "Unknown DeltaT algorithm: " << key << "setting \"WithoutCorrection\" instead";
2266 algo = WithoutCorrection;
2267 }
2268 setCurrentDeltaTAlgorithm(algo);
2269 }
2270
2271 //! Get the current algorithm used by the DeltaT
getCurrentDeltaTAlgorithmKey(void) const2272 QString StelCore::getCurrentDeltaTAlgorithmKey(void) const
2273 {
2274 return metaObject()->enumerator(metaObject()->indexOfEnumerator("DeltaTAlgorithm")).key(currentDeltaTAlgorithm);
2275 }
2276
2277 //! Get description of the current algorithm for time correction
getCurrentDeltaTAlgorithmDescription(void) const2278 QString StelCore::getCurrentDeltaTAlgorithmDescription(void) const
2279 {
2280 // GZ remarked where more info would be desirable. Generally, a full citation and the math. term would be nice to have displayed if it's just one formula, and the range of valid dates.
2281 QString description;
2282 double jd = 0;
2283 QString marker;
2284 switch (getCurrentDeltaTAlgorithm())
2285 {
2286 case WithoutCorrection:
2287 description = q_("Correction is disabled. Use only if you know what you are doing!");
2288 break;
2289 case Schoch: // historical value.
2290 description = q_("This historical formula was obtained by C. Schoch in 1931 and was used by G. Henriksson in his article <em>Einstein's Theory of Relativity Confirmed by Ancient Solar Eclipses</em> (%1). See for more info %2here%3.").arg("2009").arg("<a href='http://journalofcosmology.com/AncientAstronomy123.html'>").arg("</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2291 break;
2292 case Clemence: // historical value.
2293 description = q_("This empirical equation was published by G. M. Clemence in the article <em>On the system of astronomical constants</em> (%1).").arg("<a href='http://adsabs.harvard.edu/abs/1948AJ.....53..169C'>1948</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2294 break;
2295 case IAU: // historical value.
2296 description = q_("This formula is based on a study of post-1650 observations of the Sun, the Moon and the planets by Spencer Jones (%1) and used by Jean Meeus in his <em>Astronomical Formulae for Calculators</em>. It was also adopted in the PC program SunTracker Pro.").arg("<a href='http://adsabs.harvard.edu/abs/1939MNRAS..99..541S'>1939</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2297 // find year of publication of AFFC
2298 break;
2299 case AstronomicalEphemeris: // historical value.
2300 description = q_("This is a slightly modified version of the IAU (1952) formula which was adopted in the <em>Astronomical Ephemeris</em> and in the <em>Canon of Solar Eclipses</em> by Mucke & Meeus (1983).").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2301 // TODO: expand the sentence: ... adopted ... from 19xx-19yy?
2302 break;
2303 case TuckermanGoldstine: // historical value.
2304 description = q_("The tables of Tuckerman (1962, 1964) list the positions of the Sun, the Moon and the planets at 5- and 10-day intervals from 601 BCE to 1649 CE. The same relation was also implicitly adopted in the syzygy tables of Goldstine (1973).").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2305 // TODO: These tables are sometimes found cited, but I have no details. Maybe add "based on ... " ?
2306 break;
2307 case MullerStephenson:
2308 description = q_("This equation was published by P. M. Muller and F. R. Stephenson in the article <em>The accelerations of the earth and moon from early astronomical observations</em> (%1).").arg("<a href='http://adsabs.harvard.edu/abs/1975grhe.conf..459M'>1975</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2309 break;
2310 case Stephenson1978:
2311 description = q_("This equation was published by F. R. Stephenson in the article <em>Pre-Telescopic Astronomical Observations</em> (%1).").arg("<a href='http://adsabs.harvard.edu/abs/1978tfer.conf....5S'>1978</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2312 break;
2313 case SchmadelZech1979: // outdated data fit, historical value?
2314 description = q_("This 12th-order polynomial equation (outdated and superseded by Schmadel & Zech (1988)) was published by L. D. Schmadel and G. Zech in the article <em>Polynomial approximations for the correction delta T E.T.-U.T. in the period 1800-1975</em> (%1) as fit through data published by Brouwer (1952).").arg("<a href='http://adsabs.harvard.edu/abs/1979AcA....29..101S'>1979</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2315 break;
2316 case MorrisonStephenson1982:
2317 description = q_("This algorithm was adopted in P. Bretagnon & L. Simon's <em>Planetary Programs and Tables from -4000 to +2800</em> (1986) and in the PC planetarium program RedShift.").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2318 break;
2319 case StephensonMorrison1984: // PRIMARY SOURCE
2320 description = q_("This formula was published by F. R. Stephenson and L. V. Morrison in the article <em>Long-term changes in the rotation of the earth - 700 B.C. to A.D. 1980</em> (%1).").arg("<a href='http://adsabs.harvard.edu/abs/1984RSPTA.313...47S'>1984</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2321 break;
2322 case StephensonHoulden:
2323 description = q_("This algorithm is used in the PC planetarium program Guide 7.").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2324 break;
2325 case Espenak: // limited range, but wide availability?
2326 description = q_("This algorithm was given by F. Espenak in his <em>Fifty Year Canon of Solar Eclipses: 1986-2035</em> (1987) and in his <em>Fifty Year Canon of Lunar Eclipses: 1986-2035</em> (1989).").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2327 break;
2328 case Borkowski: // Linked to ELP2000-85, so it's important...
2329 description = q_("This formula was obtained by K.M. Borkowski (%1) from an analysis of 31 solar eclipse records dating between 2137 BCE and 1715 CE.").arg("<a href='http://adsabs.harvard.edu/abs/1988A&A...205L...8B'>1988</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2330 break;
2331 case SchmadelZech1988: // data fit through Stephenson&Morrison1984, which itself is important. Unclear who uses this version?
2332 description = q_("This 12th-order polynomial equation was published by L. D. Schmadel and G. Zech in the article <em>Empirical Transformations from U.T. to E.T. for the Period 1800-1988</em> (%1) as data fit through values given by Stephenson & Morrison (1984).").arg("<a href='http://adsabs.harvard.edu/abs/1988AN....309..219S'>1988</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2333 break;
2334 case ChaprontTouze:
2335 description = q_("This formula was adopted by M. Chapront-Touze & J. Chapront in the shortened version of the ELP 2000-85 lunar theory in their <em>Lunar Tables and Programs from 4000 B.C. to A.D. 8000</em> (1991).").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2336 break;
2337 case StephensonMorrison1995:
2338 description = q_("This equation was published by F. R. Stephenson and L. V. Morrison in the article <em>Long-Term Fluctuations in the Earth's Rotation: 700 BC to AD 1990</em> (%1).").arg("<a href='http://adsabs.harvard.edu/abs/1995RSPTA.351..165S'>1995</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2339 break;
2340 case Stephenson1997:
2341 description = q_("F. R. Stephenson published this formula in his book <em>Historical Eclipses and Earth's Rotation</em> (%1).").arg("<a href='http://ebooks.cambridge.org/ebook.jsf?bid=CBO9780511525186'>1997</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2342 break;
2343 case ChaprontMeeus:
2344 description = q_("From J. Meeus, <em>Astronomical Algorithms</em> (2nd ed., 1998), and widely used. Table for 1620..2000, and includes a variant of Chapront, Chapront-Touze & Francou (1997) for dates outside 1620..2000.").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2345 break;
2346 case JPLHorizons:
2347 description = q_("The JPL Solar System Dynamics Group of the NASA Jet Propulsion Laboratory use this formula in their interactive website %1JPL Horizons%2.").arg("<a href='http://ssd.jpl.nasa.gov/?horizons'>").arg("</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2348 break;
2349 case MeeusSimons:
2350 description = q_("This polynome was published by J. Meeus and L. Simons in article <em>Polynomial approximations to Delta T, 1620-2000 AD</em> (%1).").arg("<a href='http://adsabs.harvard.edu/abs/2000JBAA..110..323M'>2000</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2351 break;
2352 case MontenbruckPfleger: // uninspired
2353 description = q_("The fourth edition of O. Montenbruck & T. Pfleger's <em>Astronomy on the Personal Computer</em> (2000) provides simple 3rd-order polynomial data fits for the recent past.").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2354 break;
2355 case ReingoldDershowitz: //
2356 description = q_("E. M. Reingold & N. Dershowitz present this polynomial data fit in <em>Calendrical Calculations</em> (4th ed. 2018) and in their <em>Calendrical Tabulations</em> (2002). It is based on Jean Meeus' <em>Astronomical Algorithms</em> (1991).").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2357 break;
2358 case MorrisonStephenson2004: // PRIMARY SOURCE
2359 description = q_("This important solution was published by L. V. Morrison and F. R. Stephenson in article <em>Historical values of the Earth's clock error %1T and the calculation of eclipses</em> (%2) with addendum in (%3).").arg(QChar(0x0394)).arg("<a href='http://adsabs.harvard.edu/abs/2004JHA....35..327M'>2004</a>").arg("<a href='http://adsabs.harvard.edu/abs/2005JHA....36..339M'>2005</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2360 break;
2361 case Reijs:
2362 description = q_("From the Length of Day (LOD; as determined by Stephenson & Morrison (%2)), Victor Reijs derived a %1T formula by using a Simplex optimisation with a cosine and square function. This is based on a possible periodicy described by Stephenson (%2). See for more info %3here%4.").arg(QChar(0x0394)).arg("<a href='http://adsabs.harvard.edu/abs/2004JHA....35..327M'>2004</a>").arg("<a href='http://www.iol.ie/~geniet/eng/DeltaTeval.htm'>").arg("</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2363 break;
2364 case EspenakMeeus: // GENERAL SOLUTION
2365 description = q_("This solution by F. Espenak and J. Meeus, based on Morrison & Stephenson (2004) and a polynomial fit through tabulated values for 1600-2000, is used for the %1NASA Eclipse Web Site%2 and in their <em>Five Millennium Canon of Solar Eclipses: -1900 to +3000</em> (2006). This formula is also used in the solar, lunar and planetary ephemeris program SOLEX.").arg("<a href='http://eclipse.gsfc.nasa.gov/eclipse.html'>").arg("</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker)).append(" <em>").append(q_("Used by default.")).append("</em>");
2366 break;
2367 case EspenakMeeusZeroMoonAccel: // PATCHED SOLUTION. Experimental, it may not make sense to keep it in V1.0.
2368 description = QString("%1 %2").arg(q_("PATCHED VERSION WITHOUT ADDITIONAL LUNAR ACCELERATION.")).arg(q_("This solution by F. Espenak and J. Meeus, based on Morrison & Stephenson (2004) and a polynomial fit through tabulated values for 1600-2000, is used for the %1NASA Eclipse Web Site%2 and in their <em>Five Millennium Canon of Solar Eclipses: -1900 to +3000</em> (2006). This formula is also used in the solar, lunar and planetary ephemeris program SOLEX.").arg("<a href='http://eclipse.gsfc.nasa.gov/eclipse.html'>").arg("</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker)).append(" <em>").append("</em>"));
2369 break;
2370 case Banjevic:
2371 description = q_("This solution by B. Banjevic, based on Stephenson & Morrison (1984), was published in article <em>Ancient eclipses and dating the fall of Babylon</em> (%1).").arg("<a href='http://adsabs.harvard.edu/abs/2006POBeo..80..251B'>2006</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2372 break;
2373 case IslamSadiqQureshi:
2374 description = q_("This solution by S. Islam, M. Sadiq and M. S. Qureshi, based on Meeus & Simons (2000), was published in article <em>Error Minimization of Polynomial Approximation of DeltaT</em> (%1) and revisited by Sana Islam in 2013.").arg("<a href='http://www.ias.ac.in/article/fulltext/joaa/029/03-04/0363-0366'>2008</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2375 break;
2376 case KhalidSultanaZaidi:
2377 description = q_("This polynomial approximation with 0.6 seconds of accuracy by M. Khalid, Mariam Sultana and Faheem Zaidi was published in <em>Delta T: Polynomial Approximation of Time Period 1620-2013</em> (%1).").arg("<a href='https://doi.org/10.1155/2014/480964'>2014</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2378 break;
2379 case StephensonMorrisonHohenkerk2016: // PRIMARY SOURCE, SEEMS VERY IMPORTANT
2380 description = q_("This solution by F. R. Stephenson, L. V. Morrison and C. Y. Hohenkerk (2016) was published in <em>Measurement of the Earth’s rotation: 720 BC to AD 2015</em> (%1). Outside of the named range (modelled with a spline fit) it provides values from an approximate parabola.").arg("<a href='https://doi.org/10.1098/rspa.2016.0404'>2016</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2381 break;
2382 case Henriksson2017:
2383 description = q_("This solution by G. Henriksson was published in the article <em>The Acceleration of the Moon and the Universe - the Mass of the Graviton</em> (%1) and based on C. Schoch's formula (1931).").arg("<a href='https://doi.org/10.22606/adap.2017.23004'>2017</a>").append(getCurrentDeltaTAlgorithmValidRangeDescription(jd, &marker));
2384 break;
2385 case Custom:
2386 description = q_("This is a quadratic formula for calculation of %1T with coefficients defined by the user.").arg(QChar(0x0394));
2387 break;
2388 default:
2389 description = q_("Error");
2390 }
2391
2392 // Put n-dot value info
2393 if (getCurrentDeltaTAlgorithm()!=WithoutCorrection)
2394 {
2395 QString fp = QString("%1\"/cy%2").arg(QString::number(getDeltaTnDot(), 'f', 4)).arg(QChar(0x00B2));
2396 QString sp = QString("n%1").arg(QChar(0x2032));
2397 description.append(" " + q_("The solution's value of %1 for %2 (secular acceleration of the Moon) requires an adaptation, see Guide for details.").arg(fp).arg(sp));
2398 }
2399
2400 return description;
2401 }
2402
getCurrentDeltaTAlgorithmValidRangeDescription(const double JD,QString * marker) const2403 QString StelCore::getCurrentDeltaTAlgorithmValidRangeDescription(const double JD, QString *marker) const
2404 {
2405 QString validRange = "";
2406 QString validRangeAppendix = "";
2407 *marker = "";
2408 int year, month, day;
2409
2410 StelUtils::getDateFromJulianDay(JD, &year, &month, &day);
2411 switch (currentDeltaTAlgorithm)
2412 {
2413 // These models provide extrapolated values outside their specified/recommended range.
2414 case WithoutCorrection: // and
2415 case Schoch: // and
2416 case Clemence: // and
2417 case IAU: // and
2418 case AstronomicalEphemeris: // and
2419 case TuckermanGoldstine: // and
2420 case StephensonHoulden: // and
2421 case MullerStephenson: // and
2422 case Stephenson1978: // and
2423 case MorrisonStephenson1982: // and
2424 case StephensonMorrison1984: // and
2425 case Espenak: // and
2426 case Borkowski: // and
2427 case StephensonMorrison1995: // and
2428 case Stephenson1997: // and
2429 case ChaprontMeeus: // and
2430 case ReingoldDershowitz: // and
2431 case MorrisonStephenson2004: // and
2432 case Reijs: // and
2433 case EspenakMeeus: // the default, range stated in the Canon, p. 14. ... and
2434 case EspenakMeeusZeroMoonAccel: // and
2435 case StephensonMorrisonHohenkerk2016: // and
2436 case Henriksson2017:
2437 break;
2438 case JPLHorizons: // and
2439 case MeeusSimons:
2440 validRangeAppendix = q_("with zero values outside this range");
2441 break;
2442 case MontenbruckPfleger:
2443 validRangeAppendix = q_("with a typical 1-second accuracy and zero values outside this range");
2444 break;
2445 case SchmadelZech1988:
2446 validRangeAppendix = q_("with a mean error of less than one second, max. error 1.9s, and values for the limit years outside this range");
2447 break;
2448 case SchmadelZech1979: // and
2449 case ChaprontTouze: // and
2450 case Banjevic: // and
2451 case IslamSadiqQureshi: // and
2452 case KhalidSultanaZaidi:
2453 validRangeAppendix = q_("with values for the limit years outside this range");
2454 break;
2455 case Custom:
2456 // Valid range unknown
2457 break;
2458 }
2459
2460 if (deltaTstart > INT_MIN) // limits declared?
2461 {
2462 if (validRangeAppendix!="")
2463 validRange = q_("Valid range of usage: between years %1 and %2, %3.").arg(deltaTstart).arg(deltaTfinish).arg(validRangeAppendix);
2464 else
2465 validRange = q_("Valid range of usage: between years %1 and %2.").arg(deltaTstart).arg(deltaTfinish);
2466 if ((year < deltaTstart) || (deltaTfinish < year))
2467 *marker = "*"; // mark "outside designed range, possibly wrong"
2468 }
2469 else
2470 *marker = "?"; // mark "no range given"
2471
2472 return QString(" %1").arg(validRange);
2473 }
2474
2475 // return if sky plus atmosphere is bright enough from sunlight so that e.g. screen labels should be rendered dark.
2476 // DONE: Simply ask the atmosphere for its surrounding brightness
2477 // TODO2: This could be moved to the SkyDrawer or even some GUI class, as it is used to decide a GUI thing.
isBrightDaylight() const2478 bool StelCore::isBrightDaylight() const
2479 {
2480 if (propMgr->getStelPropertyValue("Oculars.ocularDisplayed", true).toBool())
2481 return false;
2482 SolarSystem* ssys = GETSTELMODULE(SolarSystem);
2483 if (!ssys->getFlagPlanets())
2484 return false;
2485 if (!getSkyDrawer()->getFlagHasAtmosphere())
2486 return false;
2487 if (ssys->getSolarEclipseFactor(this).first<=0.01) // Total solar eclipse
2488 return false;
2489
2490 // immediately decide upon sky background brightness...
2491 return (GETSTELMODULE(LandscapeMgr)->getAtmosphereAverageLuminance() > static_cast<float>(getSkyDrawer()->getDaylightLabelThreshold()));
2492 }
2493
getCurrentEpoch() const2494 double StelCore::getCurrentEpoch() const
2495 {
2496 return 2000.0 + (getJD() - 2451545.0)/365.25;
2497 }
2498
2499 // DE430/DE431 handling.
2500 // NOTE: When DE431 is not available, DE430 installed, but date outside, de430IsActive() may be true but computations still go back to VSOP!
2501 // To test whether DE43x is really currently in use, test (EphemWrapper::use_de430(jd) || EphemWrapper::use_de431(jd))
2502
de430IsAvailable()2503 bool StelCore::de430IsAvailable()
2504 {
2505 return de430Available;
2506 }
2507
de431IsAvailable()2508 bool StelCore::de431IsAvailable()
2509 {
2510 return de431Available;
2511 }
2512
de430IsActive()2513 bool StelCore::de430IsActive()
2514 {
2515 return de430Active;
2516 }
2517
de431IsActive()2518 bool StelCore::de431IsActive()
2519 {
2520 return de431Active;
2521 }
2522
setDe430Active(bool status)2523 void StelCore::setDe430Active(bool status)
2524 {
2525 de430Active = de430Available && status;
2526 }
2527
setDe431Active(bool status)2528 void StelCore::setDe431Active(bool status)
2529 {
2530 de431Active = de431Available && status;
2531 }
2532
2533
de440IsAvailable()2534 bool StelCore::de440IsAvailable()
2535 {
2536 return de440Available;
2537 }
2538
de441IsAvailable()2539 bool StelCore::de441IsAvailable()
2540 {
2541 return de441Available;
2542 }
2543
de440IsActive()2544 bool StelCore::de440IsActive()
2545 {
2546 return de440Active;
2547 }
2548
de441IsActive()2549 bool StelCore::de441IsActive()
2550 {
2551 return de441Active;
2552 }
2553
setDe440Active(bool status)2554 void StelCore::setDe440Active(bool status)
2555 {
2556 de440Active = de440Available && status;
2557 }
2558
setDe441Active(bool status)2559 void StelCore::setDe441Active(bool status)
2560 {
2561 de441Active = de441Available && status;
2562 }
2563
initEphemeridesFunctions()2564 void StelCore::initEphemeridesFunctions()
2565 {
2566 QSettings* conf = StelApp::getInstance().getSettings();
2567
2568 QString de430ConfigPath = conf->value("astro/de430_path").toString();
2569 QString de431ConfigPath = conf->value("astro/de431_path").toString();
2570 QString de440ConfigPath = conf->value("astro/de440_path").toString();
2571 QString de441ConfigPath = conf->value("astro/de441_path").toString();
2572
2573 QString de430FilePath;
2574 QString de431FilePath;
2575 QString de440FilePath;
2576 QString de441FilePath;
2577
2578 //<-- DE430 -->
2579 if(de430ConfigPath.remove(QChar('"')).isEmpty())
2580 de430FilePath = StelFileMgr::findFile("ephem/" + QString(DE430_FILENAME), StelFileMgr::File);
2581 else
2582 de430FilePath = StelFileMgr::findFile(de430ConfigPath, StelFileMgr::File);
2583
2584 de430Available=!de430FilePath.isEmpty();
2585 if(de430Available)
2586 {
2587 qDebug() << "DE430 at: " << de430FilePath;
2588 EphemWrapper::init_de430(de430FilePath.toStdString().c_str());
2589 }
2590 setDe430Active(de430Available && conf->value("astro/flag_use_de430", false).toBool());
2591
2592 //<-- DE431 -->
2593 if(de431ConfigPath.remove(QChar('"')).isEmpty())
2594 de431FilePath = StelFileMgr::findFile("ephem/" + QString(DE431_FILENAME), StelFileMgr::File);
2595 else
2596 de431FilePath = StelFileMgr::findFile(de431ConfigPath, StelFileMgr::File);
2597
2598 de431Available=!de431FilePath.isEmpty();
2599 if(de431Available)
2600 {
2601 qDebug() << "DE431 at: " << de431FilePath;
2602 EphemWrapper::init_de431(de431FilePath.toStdString().c_str());
2603 }
2604 setDe431Active(de431Available && conf->value("astro/flag_use_de431", false).toBool());
2605
2606 //<-- DE440 -->
2607 if(de440ConfigPath.remove(QChar('"')).isEmpty())
2608 de440FilePath = StelFileMgr::findFile("ephem/" + QString(DE440_FILENAME), StelFileMgr::File);
2609 else
2610 de440FilePath = StelFileMgr::findFile(de440ConfigPath, StelFileMgr::File);
2611
2612 de440Available=!de440FilePath.isEmpty();
2613 if(de440Available)
2614 {
2615 qDebug() << "DE440 at: " << de440FilePath;
2616 EphemWrapper::init_de440(de440FilePath.toStdString().c_str());
2617 }
2618 setDe440Active(de440Available && conf->value("astro/flag_use_de440", false).toBool());
2619
2620 //<-- DE441 -->
2621 if(de441ConfigPath.remove(QChar('"')).isEmpty())
2622 de441FilePath = StelFileMgr::findFile("ephem/" + QString(DE441_FILENAME), StelFileMgr::File);
2623 else
2624 de441FilePath = StelFileMgr::findFile(de441ConfigPath, StelFileMgr::File);
2625
2626 de441Available=!de441FilePath.isEmpty();
2627 if(de441Available)
2628 {
2629 qDebug() << "DE441 at: " << de441FilePath;
2630 EphemWrapper::init_de441(de441FilePath.toStdString().c_str());
2631 }
2632 setDe441Active(de441Available && conf->value("astro/flag_use_de441", false).toBool());
2633 }
2634
2635 // Methods for finding constellation from J2000 position.
2636 typedef struct iau_constline{
2637 double RAlow; // low value of 1875.0 right ascension segment, HH.dddd
2638 double RAhigh; // high value of 1875.0 right ascension segment, HH.dddd
2639 double decLow; // declination 1875.0 of southern border, DD.dddd
2640 QString constellation; // 3-letter code of constellation
2641 } iau_constelspan;
2642
2643 static QVector<iau_constelspan> iau_constlineVec;
2644 static bool iau_constlineVecInitialized=false;
2645
2646 // File iau_constellations_spans.dat is converted from file data.dat from ADC catalog VI/42.
2647 // We converted back to HH:MM:SS format to avoid the inherent rounding errors present in that file (Bug LP:#1690615).
getIAUConstellation(const Vec3d positionEqJnow) const2648 QString StelCore::getIAUConstellation(const Vec3d positionEqJnow) const
2649 {
2650 // Precess positionJ2000 to 1875.0
2651 const Vec3d pos1875=j2000ToJ1875(equinoxEquToJ2000(positionEqJnow, RefractionOff));
2652 double RA1875;
2653 double dec1875;
2654 StelUtils::rectToSphe(&RA1875, &dec1875, pos1875);
2655 RA1875 *= 12./M_PI; // hours
2656 if (RA1875 <0.) RA1875+=24.;
2657 dec1875 *= M_180_PI; // degrees
2658 Q_ASSERT(RA1875>=0.0);
2659 Q_ASSERT(RA1875<=24.0);
2660 Q_ASSERT(dec1875<=90.0);
2661 Q_ASSERT(dec1875>=-90.0);
2662
2663 // read file into structure.
2664 if (!iau_constlineVecInitialized)
2665 {
2666 //struct iau_constline line;
2667 QFile file(StelFileMgr::findFile("data/constellations_spans.dat"));
2668
2669 if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
2670 {
2671 qWarning() << "IAU constellation line data file data/constellations_spans.dat not found.";
2672 return "err";
2673 }
2674 iau_constelspan span;
2675 QRegularExpression emptyLine("^\\s*$");
2676 QTextStream in(&file);
2677 while (!in.atEnd())
2678 {
2679 // Build list of entries. The checks can certainly become more robust. Actually the file must have 4-part lines.
2680 QString line = in.readLine();
2681 if (line.length()==0) continue;
2682 if (emptyLine.match(line).hasMatch()) continue;
2683 if (line.at(0)=='#') continue; // skip comment lines.
2684 //QStringList list = line.split(QRegularExpression("\\b\\s+\\b"));
2685 QStringList list = line.trimmed().split(QRegularExpression("\\s+"));
2686 if (list.count() != 4)
2687 {
2688 qWarning() << "IAU constellation file constellations_spans.dat has bad line:" << line << "with" << list.count() << "elements";
2689 continue;
2690 }
2691 //qDebug() << "Creating span for decl=" << list.at(2) << " from RA=" << list.at(0) << "to" << list.at(1) << ": " << list.at(3);
2692 QStringList numList=list.at(0).split(QRegularExpression(":"));
2693 span.RAlow= atof(numList.at(0).toLatin1()) + atof(numList.at(1).toLatin1())/60. + atof(numList.at(2).toLatin1())/3600.;
2694 numList=list.at(1).split(QRegularExpression(":"));
2695 span.RAhigh=atof(numList.at(0).toLatin1()) + atof(numList.at(1).toLatin1())/60. + atof(numList.at(2).toLatin1())/3600.;
2696 numList=list.at(2).split(QRegularExpression(":"));
2697 span.decLow=atof(numList.at(0).toLatin1());
2698 if (span.decLow<0.0)
2699 span.decLow -= atof(numList.at(1).toLatin1())/60.;
2700 else
2701 span.decLow += atof(numList.at(1).toLatin1())/60.;
2702 span.constellation=list.at(3);
2703 iau_constlineVec.append(span);
2704 }
2705 file.close();
2706 iau_constlineVecInitialized=true;
2707 }
2708
2709 // iterate through vector, find entry where declination is lower.
2710 int entry=0;
2711 while (iau_constlineVec.at(entry).decLow > dec1875)
2712 entry++;
2713 while (entry<iau_constlineVec.size())
2714 {
2715 while (iau_constlineVec.at(entry).RAhigh <= RA1875)
2716 entry++;
2717 while (iau_constlineVec.at(entry).RAlow >= RA1875)
2718 entry++;
2719 if (iau_constlineVec.at(entry).RAhigh > RA1875)
2720 return iau_constlineVec.at(entry).constellation;
2721 else
2722 entry++;
2723 }
2724 qDebug() << "getIAUconstellation error: Cannot determine, algorithm failed.";
2725 return "(?)";
2726 }
2727
getMouseJ2000Pos() const2728 Vec3d StelCore::getMouseJ2000Pos() const
2729 {
2730 const StelProjectorP prj = getProjection(StelCore::FrameJ2000, StelCore::RefractionAuto);
2731 float ppx = static_cast<float>(getCurrentStelProjectorParams().devicePixelsPerPixel);
2732
2733 QPoint p = StelMainView::getInstance().getMousePos(); // get screen coordinates of mouse cursor
2734 Vec3d mousePosition;
2735 const float wh = prj->getViewportWidth()*0.5f; // get half of width of the screen
2736 const float hh = prj->getViewportHeight()*0.5f; // get half of height of the screen
2737 const float mx = p.x()*ppx-wh; // point 0 in center of the screen, axis X directed to right
2738 const float my = p.y()*ppx-hh; // point 0 in center of the screen, axis Y directed to bottom
2739 // calculate position of mouse cursor via position of center of the screen (and invert axis Y)
2740 // If coordinates are invalid, don't draw them.
2741 bool coordsValid = prj->unProject(static_cast<double>(prj->getViewportPosX()+wh+mx), static_cast<double>(prj->getViewportPosY()+hh+1-my), mousePosition);
2742 if (coordsValid)
2743 { // Nick Fedoseev patch
2744 Vec3d win;
2745 prj->project(mousePosition,win);
2746 float dx = prj->getViewportPosX()+wh+mx - static_cast<float>(win.v[0]);
2747 float dy = prj->getViewportPosY()+hh+1-my - static_cast<float>(win.v[1]);
2748 prj->unProject(static_cast<double>(prj->getViewportPosX()+wh+mx+dx), static_cast<double>(prj->getViewportPosY()+hh+1-my+dy), mousePosition);
2749 }
2750 // if (getUseAberration() && getCurrentPlanet())
2751 // {
2752 // Vec3d vel=getCurrentPlanet()->getHeliocentricEclipticVelocity();
2753 // vel=StelCore::matVsop87ToJ2000*vel * getAberrationFactor()*(AU/(86400.0*SPEED_OF_LIGHT));
2754 // mousePosition-=vel;
2755 // mousePosition.normalize();
2756 // }
2757 return mousePosition;
2758 }
2759