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