kwin/src/plugins/nightcolor/suncalc.cpp
Vlad Zahorodnii 93e0265e4e Move source code to src/ directory
Once in a while, we receive complaints from other fellow KDE developers
about the file organization of kwin. This change addresses some of those
complaints by moving all of source code in a separate directory, src/,
thus making the project structure more traditional. Things such as tests
are kept in their own toplevel directories.

This change may wreak havoc on merge requests that add new files to kwin,
but if a patch modifies an already existing file, git should be smart
enough to figure out that the file has been relocated.

We may potentially split the src/ directory further to make navigating
the source code easier, but hopefully this is good enough already.
2021-02-10 15:31:43 +00:00

161 lines
5 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "suncalc.h"
#include "constants.h"
#include <QDateTime>
#include <QTimeZone>
#include <QtMath>
namespace KWin {
#define TWILIGHT_NAUT -12.0
#define TWILIGHT_CIVIL -6.0
#define SUN_RISE_SET -0.833
#define SUN_HIGH 2.0
static QTime convertToLocalTime(const QDateTime &when, const QTime &utcTime)
{
const QTimeZone timeZone = QTimeZone::systemTimeZone();
const int utcOffset = timeZone.offsetFromUtc(when);
return utcTime.addSecs(utcOffset);
}
QPair<QDateTime, QDateTime> calculateSunTimings(const QDateTime &dateTime, double latitude, double longitude, bool morning)
{
// calculations based on https://aa.quae.nl/en/reken/zonpositie.html
// accuracy: +/- 5min
// positioning
const double rad = M_PI / 180.;
const double earthObliquity = 23.4397; // epsilon
const double lat = latitude; // phi
const double lng = -longitude; // lw
// times
const QDateTime utcDateTime = dateTime.toUTC();
const double juPrompt = utcDateTime.date().toJulianDay(); // J
const double ju2000 = 2451545.; // J2000
// geometry
auto mod360 = [](double number) -> double {
return std::fmod(number, 360.);
};
auto sin = [&rad](double angle) -> double {
return std::sin(angle * rad);
};
auto cos = [&rad](double angle) -> double {
return std::cos(angle * rad);
};
auto asin = [&rad](double val) -> double {
return std::asin(val) / rad;
};
auto acos = [&rad](double val) -> double {
return std::acos(val) / rad;
};
auto anomaly = [&](const double date) -> double { // M
return mod360(357.5291 + 0.98560028 * (date - ju2000));
};
auto center = [&sin](double anomaly) -> double { // C
return 1.9148 * sin(anomaly) + 0.02 * sin(2 * anomaly) + 0.0003 * sin(3 * anomaly);
};
auto ecliptLngMean = [](double anom) -> double { // Mean ecliptical longitude L_sun = Mean Anomaly + Perihelion + 180°
return anom + 282.9372; // anom + 102.9372 + 180°
};
auto ecliptLng = [&](double anom) -> double { // lambda = L_sun + C
return ecliptLngMean(anom) + center(anom);
};
auto declination = [&](const double date) -> double { // delta
const double anom = anomaly(date);
const double eclLng = ecliptLng(anom);
return mod360(asin(sin(earthObliquity) * sin(eclLng)));
};
// sun hour angle at specific angle
auto hourAngle = [&](const double date, double angle) -> double { // H_t
const double decl = declination(date);
const double ret0 = (sin(angle) - sin(lat) * sin(decl)) / (cos(lat) * cos(decl));
double ret = mod360(acos( ret0 ));
if (180. < ret) {
ret = ret - 360.;
}
return ret;
};
/*
* Sun positions
*/
// transit is at noon
auto getTransit = [&](const double date) -> double { // Jtransit
const double juMeanSolTime = juPrompt - ju2000 - 0.0009 - lng / 360.; // n_x = J - J_2000 - J_0 - l_w / 360°
const double juTrEstimate = date + qRound64(juMeanSolTime) - juMeanSolTime; // J_x = J + n - n_x
const double anom = anomaly(juTrEstimate); // M
const double eclLngM = ecliptLngMean(anom); // L_sun
return juTrEstimate + 0.0053 * sin(anom) - 0.0068 * sin(2 * eclLngM);
};
auto getSunMorning = [&hourAngle](const double angle, const double transit) -> double {
return transit - hourAngle(transit, angle) / 360.;
};
auto getSunEvening = [&hourAngle](const double angle, const double transit) -> double {
return transit + hourAngle(transit, angle) / 360.;
};
/*
* Begin calculations
*/
// noon - sun at the highest point
const double juNoon = getTransit(juPrompt);
double begin, end;
if (morning) {
begin = getSunMorning(TWILIGHT_CIVIL, juNoon);
end = getSunMorning(SUN_HIGH, juNoon);
} else {
begin = getSunEvening(SUN_HIGH, juNoon);
end = getSunEvening(TWILIGHT_CIVIL, juNoon);
}
// transform to QDateTime
begin += 0.5;
end += 0.5;
QDateTime dateTimeBegin;
QDateTime dateTimeEnd;
if (!std::isnan(begin)) {
const double dayFraction = begin - int(begin);
const QTime utcTime = QTime::fromMSecsSinceStartOfDay(dayFraction * MSC_DAY);
const QTime localTime = convertToLocalTime(dateTime, utcTime);
dateTimeBegin = QDateTime(dateTime.date(), localTime);
}
if (!std::isnan(end)) {
const double dayFraction = end - int(end);
const QTime utcTime = QTime::fromMSecsSinceStartOfDay(dayFraction * MSC_DAY);
const QTime localTime = convertToLocalTime(dateTime, utcTime);
dateTimeEnd = QDateTime(dateTime.date(), localTime);
}
return { dateTimeBegin, dateTimeEnd };
}
}