Browse Source

Fixed Night Time Calculations and handling

Fixed a bug in ACalc::intermediatePointsOnGreatCircle() where appending instead of prepending to a Vector lead to a route between two points being returned backwards

Optimised and fixed ACalc::calculateNightTime() to also account for local flights (where departure and destination airfield are identical) as well as avoid unnecessary conversions from radians to degrees and vice versa

Added the NightTimeValues struct which encapsulates various calculations with regards to Day/Night that are needed by the UI to properly update and provides convenient access.

Added overloads for blocktime and blockminutes calculations to also accept string inputs

Continued Work on NewNewFlightDialog
Felix Turo 3 years ago
parent
commit
9806bbe1bd

+ 14 - 2
mainwindow.cpp

@@ -27,10 +27,22 @@
 
 // Quick and dirty Debug area
 #include "src/gui/dialogues/newnewflightdialog.h"
+#include "src/functions/adatetime.h"
 void MainWindow::doDebugStuff()
 {
-    NewNewFlightDialog nf(completionData, this);
-    nf.exec();
+    //NewNewFlightDialog nf(completionData, this);
+    //nf.exec();
+    QString dept = "KJFK";
+    QString dest = "EGLL";
+    QDateTime date_time = ADateTime::fromString(QDate::currentDate().toString(Qt::ISODate) + "22:00");
+    int tblk = 660;
+    int night_angle = -6;
+
+    //LOG << "Berlin Local" << date_time.toString() << "Total:" << tblk;
+    //LOG << "Night time:" << ACalc::calculateNightTime(dept, dest, date_time, tblk, night_angle);
+    //dest = "EDDB";
+    LOG << "JFK to LHR" << date_time.toString() << "Total:" << tblk;
+    LOG << "Night Time:" << ACalc::calculateNightTime(dept, dest, date_time, tblk, night_angle);
 }
 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)

+ 39 - 23
src/functions/acalc.cpp

@@ -20,6 +20,7 @@
 #include "src/database/adatabase.h"
 #include "src/classes/asettings.h"
 #include "src/opl.h"
+#include "src/functions/atime.h"
 
 using namespace ACalc;
 
@@ -136,7 +137,10 @@ double ACalc::greatCircleDistanceBetweenAirports(const QString &dept, const QStr
 QVector<QVector<double>> ACalc::intermediatePointsOnGreatCircle(double lat1, double lon1,
                                                                double lat2, double lon2, int tblk)
 {
-    double d = greatCircleDistance(lat1, lon1, lat2, lon2); //Calculate distance (radians)
+    double d = greatCircleDistance(lat1, lon1, lat2, lon2); //Calculate distance (Haversine)
+    // where d is the great circle distance in radians, to get KM take * 6371 (radius of earth).
+    //DEB << "Distance (nm): " << (d*6371.0088) * 0.5399568; // km->nm = *0.54
+
     // Converting Latitude and Longitude to Radians
     lat1 = degToRad(lat1);
     lon1 = degToRad(lon1);
@@ -157,8 +161,8 @@ QVector<QVector<double>> ACalc::intermediatePointsOnGreatCircle(double lat1, dou
         double lat = atan2(z, sqrt( pow(x, 2) + pow(y, 2) ));
         double lon = atan2(y, x);
 
-        QVector<double> coordinate = {lat, lon};
-        coordinates.append(coordinate);
+        QVector<double> coordinate = {radToDeg(lat), radToDeg(lon)};
+        coordinates.prepend(coordinate);
     }
     return coordinates;
 }
@@ -166,8 +170,7 @@ QVector<QVector<double>> ACalc::intermediatePointsOnGreatCircle(double lat1, dou
 
 double ACalc::solarElevation(QDateTime utc_time_point, double lat, double lon)
 {
-    double Alt =
-        11; // I am taking 11 kilometers as an average cruising height for a commercial passenger airplane.
+    double Alt = 11; // Assuming 11 kilometers as an average cruising height for a commercial passenger airplane.
     // convert current DateTime Object to a J2000 value used in the Calculation
     double d = utc_time_point.date().toJulianDay() - 2451544 + utc_time_point.time().hour() / 24.0 +
                utc_time_point.time().minute() / 1440.0;
@@ -213,7 +216,7 @@ double ACalc::solarElevation(QDateTime utc_time_point, double lat, double lon)
     double HA = (sid_time * 15 - RA);
     // convert to rectangular coordinate system
     x = cos(HA * (M_PI / 180)) * cos(delta * (M_PI / 180));
-    y = sin(HA * (M_PI / 180)) * cos(delta * (M_PI / 180));
+    //y = sin(HA * (M_PI / 180)) * cos(delta * (M_PI / 180)); //value of y not needed
     double z = sin(delta * (M_PI / 180));
     // rotate this along an axis going east - west.
     double zhor = x * sin((90 - lat) * (M_PI / 180)) + z * cos((90 - lat) * (M_PI / 180));
@@ -224,7 +227,7 @@ double ACalc::solarElevation(QDateTime utc_time_point, double lat, double lon)
 }
 
 
-int ACalc::calculateNightTime(const QString &dept, const QString &dest, QDateTime departureTime, int tblk, int nightAngle)
+int ACalc::calculateNightTime(const QString &dept, const QString &dest, QDateTime departureTime, int tblk, int night_angle)
 {
 
     const QString statement = QLatin1String("SELECT lat, long FROM airports WHERE icao = '")
@@ -233,23 +236,37 @@ int ACalc::calculateNightTime(const QString &dept, const QString &dest, QDateTim
             + QLatin1String("'");
     auto lat_lon = aDB->customQuery(statement, 2);
 
-    if (lat_lon.length() != 4) {
+    double dept_lat;
+    double dept_lon;
+    double dest_lat;
+    double dest_lon;
+    int night_time = 0;
+
+    if (lat_lon.length() == 4) { // normal flight from A to B
+        dept_lat = lat_lon[0].toDouble();
+        dept_lon = lat_lon[1].toDouble();
+        dest_lat = lat_lon[2].toDouble();
+        dest_lon = lat_lon[3].toDouble();
+    } else if (lat_lon.length() == 2 ) { // Dept == Dest, i.e. local flight
+        for (int i = 0; i < tblk; i++) {
+            dept_lat = lat_lon[0].toDouble();
+            dept_lon = lat_lon[1].toDouble();
+            if (solarElevation(departureTime.addSecs(60 * i), dept_lat, dept_lon) < night_angle)
+
+                night_time++;
+        }
+        return night_time;
+    } else {
         DEB << "Invalid input. Aborting.";
         return 0;
     }
 
-    double dept_lat = degToRad(lat_lon[0].toDouble());
-    double dept_lon = degToRad(lat_lon[1].toDouble());
-    double dest_lat = degToRad(lat_lon[2].toDouble());
-    double dest_lon = degToRad(lat_lon[3].toDouble());
-
     QVector<QVector<double>> route = intermediatePointsOnGreatCircle(dept_lat, dept_lon,
                                                                      dest_lat, dest_lon,
                                                                      tblk);
-    int night_time = 0;
     for (int i = 0; i < tblk ; i++) {
-        if (solarElevation(departureTime.addSecs(60 * i), radToDeg(route[i][0]),
-                           radToDeg(route[i][1])) < nightAngle) {
+        if (solarElevation(departureTime.addSecs(60 * i), route[i][0],
+                           route[i][1]) < night_angle) {
             night_time ++;
         }
     }
@@ -306,19 +323,19 @@ void ACalc::updateAutoTimes(int acft_id)
                 && acft_data.value(Opl::Db::TAILS_MULTIENGINE) == 0) {
             DEB << "SPSE";
             flight_data.insert(Opl::Db::FLIGHTS_TSPSE, flight_data.value(Opl::Db::FLIGHTS_TBLK));
-            flight_data.insert(Opl::Db::FLIGHTS_TSPME, QStringLiteral(""));
-            flight_data.insert(Opl::Db::FLIGHTS_TMP, QStringLiteral(""));
+            flight_data.insert(Opl::Db::FLIGHTS_TSPME, QString());
+            flight_data.insert(Opl::Db::FLIGHTS_TMP, QString());
         } else if ((acft_data.value(Opl::Db::TAILS_MULTIPILOT) == 0
                     && acft.getData().value(Opl::Db::TAILS_MULTIENGINE) == 1)) {
             DEB << "SPME";
             flight_data.insert(Opl::Db::FLIGHTS_TSPME, flight_data.value(Opl::Db::FLIGHTS_TBLK));
-            flight_data.insert(Opl::Db::FLIGHTS_TSPSE, QStringLiteral(""));
-            flight_data.insert(Opl::Db::FLIGHTS_TMP, QStringLiteral(""));
+            flight_data.insert(Opl::Db::FLIGHTS_TSPSE, QString());
+            flight_data.insert(Opl::Db::FLIGHTS_TMP, QString());
         } else if ((acft_data.value(Opl::Db::TAILS_MULTIPILOT) == 1)) {
             DEB << "MPME";
             flight_data.insert(Opl::Db::FLIGHTS_TMP, flight_data.value(Opl::Db::FLIGHTS_TBLK));
-            flight_data.insert(Opl::Db::FLIGHTS_TSPSE, QStringLiteral(""));
-            flight_data.insert(Opl::Db::FLIGHTS_TSPME, QStringLiteral(""));
+            flight_data.insert(Opl::Db::FLIGHTS_TSPSE, QString());
+            flight_data.insert(Opl::Db::FLIGHTS_TSPME, QString());
         }
         flight.setData(flight_data);
         aDB->commit(flight);
@@ -359,4 +376,3 @@ void ACalc::updateNightTimes()
         aDB->commit(flt);
     }
 }
-

+ 56 - 4
src/functions/acalc.h

@@ -25,6 +25,7 @@
 #include <QDateTime>
 #include <QDebug>
 #include "src/functions/alog.h"
+#include "src/functions/atime.h"
 /*!
  * \brief The ACalc namespace provides various functions for calculations that are performed
  * outside of the database. This includes tasks like converting different units and formats,
@@ -101,8 +102,8 @@ QT_DEPRECATED
 inline int QTimeToMinutes(QTime time)
 {
     QString timestring = time.toString("hh:mm");
-    int minutes = (timestring.left(2).toInt()) * 60;
-    minutes += timestring.right(2).toInt();
+    int minutes = (timestring.leftRef(2).toInt()) * 60;
+    minutes += timestring.rightRef(2).toInt();
     return minutes;
 }
 
@@ -114,8 +115,8 @@ inline int QTimeToMinutes(QTime time)
 QT_DEPRECATED
 inline int stringToMinutes(QString timestring)
 {
-    int minutes = (timestring.left(2).toInt()) * 60;
-    minutes += timestring.right(2).toInt();
+    int minutes = (timestring.leftRef(2).toInt()) * 60;
+    minutes += timestring.rightRef(2).toInt();
     timestring = QString::number(minutes);
     return minutes;
 }
@@ -224,6 +225,57 @@ QString formatTimeInput(QString user_input);
 void updateAutoTimes(int acft_id);
 
 void updateNightTimes();
+
+/*!
+ * \brief The NightTimeValues struct encapsulates values relating to night time that are needed by the NewFlightDialog
+ */
+struct NightTimeValues{
+    NightTimeValues(const QString& dept, const QString& dest, const QDateTime& departure_time, int block_minutes, int night_angle)
+    {
+        nightMinutes = calculateNightTime(dept, dest, departure_time, block_minutes, night_angle);
+
+        nightTime = ATime::fromMinutes(nightMinutes);
+        totalTime = ATime::fromMinutes(block_minutes);
+        LOG << "Total: " << totalTime;
+        LOG << "Night: " << nightTime;
+        LOG << "Is Night Dept" << isNight(dept, departure_time, night_angle);
+        LOG << "Is Night Dest" << isNight(dest, departure_time.addSecs(block_minutes * 60), night_angle);
+
+        if (nightMinutes == 0) { // all day
+            takeOffNight = false;
+            landingNight  = false;
+        }
+        else if (nightMinutes == block_minutes) { // all night
+            takeOffNight = true;
+            landingNight  = true;
+        } else {
+            if(isNight(dept, departure_time,  night_angle))
+                takeOffNight = true;
+            else
+                takeOffNight = false;
+            if(isNight(dest, departure_time.addSecs(block_minutes * 60), night_angle))
+                landingNight = true;
+            else
+                landingNight = false;
+        }
+
+
+    };
+    NightTimeValues(bool to_night, bool ldg_night, int night_minutes, QTime night_time, QTime total_time)
+        : takeOffNight(to_night), landingNight(ldg_night), nightMinutes(night_minutes), nightTime(night_time), totalTime(total_time){};
+    bool takeOffNight;
+    bool landingNight;
+    int nightMinutes;
+    QTime nightTime;
+    QTime totalTime;
+
+    inline bool isAllDay()      {return (!takeOffNight && !landingNight);}
+    inline bool isAllNight()    {return (takeOffNight && landingNight);}
+    inline bool isDayToNight()  {return (!takeOffNight && landingNight);}
+    inline bool isNightToDay()  {return (takeOffNight && !landingNight);}
+};
+
+
 } // namespace ACalc
 
 #endif // ACALC_H

+ 8 - 1
src/functions/adatetime.h

@@ -26,7 +26,7 @@ namespace ADateTime {
  * \brief toString formats a QDateTime object into a string in a uniform way.
  * \return
  */
-inline const QString toString (const QDateTime date_time, Opl::Datetime::DateTimeFormat format) {
+inline const QString toString (const QDateTime& date_time, Opl::Datetime::DateTimeFormat format) {
     switch (format) {
     case Opl::Datetime::Default:
         return date_time.toString(Qt::ISODate);
@@ -37,6 +37,13 @@ inline const QString toString (const QDateTime date_time, Opl::Datetime::DateTim
     }
 }
 
+inline QDateTime fromString(const QString& date_time_string)
+{
+    auto date_time = QDateTime::fromString(date_time_string, QStringLiteral("yyyy-MM-ddhh:mm"));
+    date_time.setTimeZone(QTimeZone::utc());
+    return date_time;
+}
+
 }
 
 #endif // ADATETIME_H

+ 35 - 0
src/functions/atime.h

@@ -141,6 +141,41 @@ inline QTime blocktime(const QTime &tofb, const QTime &tonb)
     return blocktime_out;
 }
 
+inline QTime blocktime(const QString& tofb, const QString& tonb)
+{
+    QTime t_tofb = ATime::fromString(tofb);
+    QTime t_tonb = ATime::fromString(tonb);
+    return blocktime(t_tofb, t_tonb);
+}
+
+/*!
+ * \brief blockMinutes calculates the total amount of minutes elapsed between
+ * tofb and tonb
+ */
+inline int blockMinutes(const QString& tofb, const QString& tonb)
+{
+    const QTime t_tofb = ATime::fromString(tofb);
+    const QTime t_tonb = ATime::fromString(tonb);
+    if (t_tofb.isValid() && t_tonb.isValid()) {
+        const auto tblk = ATime::blocktime(t_tofb, t_tonb);
+        return ATime::toMinutes(tblk);
+    } else
+        return 0;
+}
+
+/*!
+ * \brief blockMinutes calculates the total amount of minutes elapsed between
+ * tofb and tonb
+ */
+inline int blockMinutes(const QTime& tofb, const QTime& tonb)
+{
+    if (tofb.isValid() && tonb.isValid()) {
+        const auto tblk = ATime::blocktime(tofb, tonb);
+        return ATime::toMinutes(tblk);
+    } else
+        return 0;
+}
+
 /*!
  * \brief verifies user input and formats to hh:mm
  * if the output is not a valid time, an empty string is returned. Accepts

+ 28 - 46
src/gui/dialogues/newnewflightdialog.cpp

@@ -5,6 +5,7 @@
 #include "src/functions/adate.h"
 #include "src/classes/asettings.h"
 #include "src/functions/acalc.h"
+#include "src/functions/adatetime.h"
 #include <QDateTime>
 #include <QCompleter>
 #include <QKeyEvent>
@@ -108,15 +109,12 @@ void NewNewFlightDialog::onGoodInputReceived(QLineEdit *line_edit)
 
     if (mandatoryLineEdits.contains(line_edit))
         validationState.validate(mandatoryLineEdits.indexOf(line_edit));
+
     if (validationState.timesValid()) {
-        DEB << "All mandatory Line Edits valid!";
-        // Update Block Time Label
-        QTime tblk = calculateBlockTime();
-        ui->tblkDisplayLabel->setText(ATime::toString(tblk));
+        updateBlockTimeLabel();
         if (validationState.allValid())
             updateNightCheckBoxes();
     }
-
         validationState.printValidationStatus();
 }
 
@@ -132,63 +130,47 @@ void NewNewFlightDialog::onBadInputReceived(QLineEdit *line_edit)
     validationState.printValidationStatus();
 }
 
-QTime NewNewFlightDialog::calculateBlockTime()
+void NewNewFlightDialog::updateBlockTimeLabel()
 {
-    if (!validationState.timesValid())
-        return {};
-    const auto tofb = ATime::fromString(ui->tofbTimeLineEdit->text());
-    const auto tonb = ATime::fromString(ui->tonbTimeLineEdit->text());
-    return ATime::blocktime(tofb, tonb);
+    QTime tblk = ATime::blocktime(ui->tofbTimeLineEdit->text(), ui->tonbTimeLineEdit->text());
+    ui->tblkDisplayLabel->setText(ATime::toString(tblk));
 }
 
+
 /*!
  * \brief NewNewFlightDialog::updateNightCheckBoxes updates the check boxes for take-off and landing
  * at night. Returns the number of minutes of night time.
  * \return
  */
-int NewNewFlightDialog::updateNightCheckBoxes()
+void NewNewFlightDialog::updateNightCheckBoxes()
 {
     if (!validationState.allValid())
-        return 0;
+        return;
 
-    // Calculate Block Time
-    const auto tofb = ATime::fromString(ui->tofbTimeLineEdit->text());
-    const auto tonb = ATime::fromString(ui->tonbTimeLineEdit->text());
-    const auto tblk = ATime::blocktime(tofb, tonb);
-    const auto block_minutes = ATime::toMinutes(tblk);
     // Calculate Night Time
-
-    const QString dept_date = ui->doftLineEdit->text() + 'T'
-            + ATime::toString(tofb);
-    const auto dept_date_time = QDateTime::fromString(dept_date, QStringLiteral("yyyy-MM-ddThh:mm"));
+    const QString dept_date = (ui->doftLineEdit->text() + ui->tofbTimeLineEdit->text());
+    const auto dept_date_time = ADateTime::fromString(dept_date);
+    const int block_minutes = ATime::blockMinutes(ui->tofbTimeLineEdit->text(), ui->tonbTimeLineEdit->text());
     const int night_angle = ASettings::read(ASettings::FlightLogging::NightAngle).toInt();
-    const auto night_time = ATime::fromMinutes(ACalc::calculateNightTime(
-                                             ui->deptLocationLineEdit->text(),
-                                             ui->destLocationLineEdit->text(),
-                                             dept_date_time,
-                                             block_minutes,
-                                             night_angle));
-    const auto night_minutes = ATime::toMinutes(night_time);
-
+    const auto night_values = ACalc::NightTimeValues(
+                ui->deptLocationLineEdit->text(),
+                ui->destLocationLineEdit->text(),
+                dept_date_time,
+                block_minutes,
+                night_angle);
     // set check boxes
-    if (night_minutes == 0) {
-        ui->toNightCheckBox->setCheckState(Qt::Unchecked);
-        ui->ldgNightCheckBox->setCheckState(Qt::Unchecked);
-    }
-    else if (night_minutes == ATime::toMinutes(calculateBlockTime())) {
+    DEB << "toN" << night_values.takeOffNight;
+    DEB << "ldgN" << night_values.landingNight;
+
+    if (night_values.takeOffNight)
         ui->toNightCheckBox->setCheckState(Qt::Checked);
-        ui->ldgNightCheckBox->setCheckState(Qt::Checked);
-    } else {
-        if(ACalc::isNight(ui->deptLocationLineEdit->text(), dept_date_time,  night_angle)) {
-            ui->toNightCheckBox->setCheckState(Qt::Checked);
-            ui->ldgNightCheckBox->setCheckState(Qt::Unchecked);
-        } else {
-            ui->toNightCheckBox->setCheckState(Qt::Unchecked);
-            ui->ldgNightCheckBox->setCheckState(Qt::Checked);
-        }
-    }
+    else
+        ui->toNightCheckBox->setCheckState(Qt::Unchecked);
 
-    return ATime::toMinutes(night_time);
+    if (night_values.landingNight)
+        ui->ldgNightCheckBox->setCheckState(Qt::Checked);
+    else
+        ui->ldgNightCheckBox->setCheckState(Qt::Unchecked);
 }
 
 // # Slots

+ 2 - 2
src/gui/dialogues/newnewflightdialog.h

@@ -107,9 +107,9 @@ private:
     void onGoodInputReceived(QLineEdit *line_edit);
     void onBadInputReceived(QLineEdit *line_edit);
 
-    QTime calculateBlockTime();
-    int updateNightCheckBoxes();
+    void updateNightCheckBoxes();
     void setNightCheckboxes();
+    void updateBlockTimeLabel();
 
 
 private slots: