diff --git a/utcicalculator.cpp b/utcicalculator.cpp new file mode 100644 index 0000000..6449985 --- /dev/null +++ b/utcicalculator.cpp @@ -0,0 +1,178 @@ +#include "utcicalculator.h" +#include +#include +#include +#include + +UtciCalculator::UtciCalculator(QObject *parent) + : QObject(parent) +{ +} + +/*// Loads coefficients from a simple text file containing one coefficient per line or tab-separated. +// The expected file is the ESM_3 coefficients table exported to a plain text file where the coefficients +// appear in the same order as in the official table (210 coefficients). +bool UtciCalculator::loadCoefficientsFromFile(const QString &path) +{ + QFile f(path); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) + return false; + + QTextStream in(&f); + QVector coeffs; + coeffs.reserve(300); + + while (!in.atEnd()) { + QString line = in.readLine().trimmed(); + if (line.isEmpty()) continue; + // Allow lines with comments or 'term coefficient' headings: + // We try to extract numbers from the line. + QStringList parts = line.split(QRegularExpression("[\\s,\\t]+"), Qt::SkipEmptyParts); + for (const QString &p : parts) { + bool ok = false; + double val = p.toDouble(&ok); + if (ok) coeffs.append(val); + } + } + if (coeffs.isEmpty()) return false; + + m_coeffs = coeffs; + return true; +} +*/ +// Magnus formula to estimate saturation vapour pressure (hPa) then return pa in kPa +double UtciCalculator::vapourPressureFromRH(double Ta, double rh) +{ + // Magnus-Tetens approximation for saturation vapour pressure (hPa) + // valid roughly for -45C..+60C + // constants for water over liquid + double a = 17.27; + double b = 237.7; // °C + double es = 6.112 * qExp((a * Ta) / (Ta + b)); + double ea = es * (rh / 100.0); + return ea / 10.0; // hPa -> kPa +} + +double UtciCalculator::computeUTCI(double Ta, double va, double Tr, double rh) const +{ + double pa = vapourPressureFromRH(Ta, rh); + double tm = Tr - Ta; + double offset = evalOffset(Ta, va, tm, pa); + return Ta + offset; +} + +double UtciCalculator::evalOffset(double Ta, double va, double tm, double pa) const +{ + if (m_coeffs.size() < 210) + return 0.0; + + // Pré-calcul des puissances + double Ta2 = Ta*Ta, Ta3 = Ta2*Ta, Ta4 = Ta3*Ta, Ta5 = Ta4*Ta, Ta6 = Ta5*Ta; + double va2 = va*va, va3 = va2*va, va4 = va3*va, va5 = va4*va, va6 = va5*va; + double tm2 = tm*tm, tm3 = tm2*tm, tm4 = tm3*tm, tm5 = tm4*tm, tm6 = tm5*tm; + double pa2 = pa*pa, pa3 = pa2*pa, pa4 = pa3*pa, pa5 = pa4*pa, pa6 = pa5*pa; + + QVector terms(210, 0.0); + + // Construire les termes dans le même ordre que les coefficients ESM_3 + terms[0] = 1.0; // constante + terms[1] = Ta; + terms[2] = Ta2; + terms[3] = Ta3; + terms[4] = Ta4; + terms[5] = Ta5; + terms[6] = Ta6; + + terms[7] = va; + terms[8] = Ta*va; + terms[9] = Ta2*va; + terms[10] = Ta3*va; + terms[11] = Ta4*va; + terms[12] = Ta5*va; + + terms[13] = va2; + terms[14] = Ta*va2; + terms[15] = Ta2*va2; + terms[16] = Ta3*va2; + terms[17] = Ta4*va2; + + terms[18] = va3; + terms[19] = Ta*va3; + terms[20] = Ta2*va3; + terms[21] = Ta3*va3; + + terms[22] = va4; + terms[23] = Ta*va4; + terms[24] = Ta2*va4; + + terms[25] = va5; + terms[26] = Ta*va5; + + terms[27] = va6; + + terms[28] = tm; + terms[29] = Ta*tm; + terms[30] = Ta2*tm; + terms[31] = Ta3*tm; + terms[32] = Ta4*tm; + terms[33] = Ta5*tm; + terms[34] = Ta6*tm; + + terms[35] = va*tm; + terms[36] = Ta*va*tm; + terms[37] = Ta2*va*tm; + terms[38] = Ta3*va*tm; + terms[39] = Ta4*va*tm; + + terms[40] = va2*tm; + terms[41] = Ta*va2*tm; + terms[42] = Ta2*va2*tm; + terms[43] = Ta3*va2*tm; + + terms[44] = va3*tm; + terms[45] = Ta*va3*tm; + terms[46] = Ta2*va3*tm; + + terms[47] = va4*tm; + terms[48] = Ta*va4*tm; + + terms[49] = va5*tm; + + // tm^2 à tm^6 + terms[50] = tm2; terms[51] = Ta*tm2; terms[52] = Ta2*tm2; terms[53] = Ta3*tm2; terms[54] = Ta4*tm2; + terms[55] = va*tm2; terms[56] = Ta*va*tm2; terms[57] = Ta2*va*tm2; terms[58] = Ta3*va*tm2; + terms[59] = va2*tm2; terms[60] = Ta*va2*tm2; terms[61] = Ta2*va2*tm2; + terms[62] = va3*tm2; + terms[63] = tm3; terms[64] = Ta*tm3; terms[65] = Ta2*tm3; + terms[66] = va*tm3; terms[67] = Ta*va*tm3; terms[68] = va2*tm3; + terms[69] = tm4; terms[70] = Ta*tm4; terms[71] = va*tm4; + terms[72] = tm5; + terms[73] = tm6; + + // pa et combinaisons + terms[74] = pa; terms[75] = Ta*pa; terms[76] = Ta2*pa; terms[77] = Ta3*pa; terms[78] = Ta4*pa; terms[79] = Ta5*pa; + terms[80] = va*pa; terms[81] = Ta*va*pa; terms[82] = Ta2*va*pa; terms[83] = Ta3*va*pa; + terms[84] = va2*pa; terms[85] = Ta*va2*pa; terms[86] = Ta2*va2*pa; + terms[87] = va3*pa; terms[88] = Ta*va3*pa; + terms[89] = va4*pa; + + terms[90] = pa2; terms[91] = Ta*pa2; terms[92] = Ta2*pa2; + + // Remplir le reste avec 0.0 pour simplifier (les 210 coefficients réels peuvent être affectés directement) + for (int i = 93; i < 210; ++i) terms[i] = 0.0; + + // Calcul de l'offset + double offset = 0.0; + for (int i = 0; i < 210; ++i) + offset += m_coeffs[i] * terms[i]; + + return offset; +} + +void UtciCalculator::initCoefficients() +{ + // Coefficients ESM_3 (exemple simplifié, à remplacer par la liste complète 210 valeurs) + m_coeffs = { + 6.07562052E-01, -2.27712343E-02, 8.06470249E-04, -1.54271372E-04, -3.24651735E-06, 7.32602852E-08, 1.35959073E-09, -2.25836520E+00, 8.80326035E-02, 2.16844454E-03, -1.53347087E-05, -5.72983704E-07, -2.55090145E-09, -7.51269505E-01, -4.08350271E-03, -5.21670675E-05, 1.94544667E-06, 1.14099531E-08, 1.58137256E-01, -6.57263143E-05, 2.22697524E-07, -4.16117031E-08, -1.27762753E-02, 9.66891875E-06, 2.52785852E-09, 4.56306672E-04, -1.74202546E-07, -5.91491269E-06, 3.98374029E-01, 1.83945314E-04, -1.73754510E-04, -7.60781159E-07, 3.77830287E-08, 5.43079673E-10, -2.00518269E-02, 8.92859837E-04, 3.45433048E-06, -3.77925774E-07, -1.69699377E-09, 1.69992415E-04, -4.99204314E-05, 2.47417178E-07, 1.07596466E-08, 8.49242932E-05, 1.35191328E-06, -6.21531254E-09, -4.99410301E-06, -1.89489258E-08, 8.15300114E-08, 7.55043090E-04, -3.69476348E-02, 1.62325322E-03, -3.14279680E-05, 2.59835559E-06, -4.77136523E-08, 8.64203390E-03, -6.87405181E-04, -9.13863872E-06, 5.15916806E-07, -3.59217476E-05, 3.28696511E-05, -7.10542454E-07, -1.24382300E-05, -7.38584400E-09, 2.20609296E-07, -7.32469180E-04, -1.87381964E-05, 4.80925239E-06, -8.75492040E-08, 2.77862930E-05, -5.06004592E-06, 1.14325367E-07, 2.53016723E-06, -1.72857035E-08, -3.95079398E-08, -3.59413173E-07, 7.04388046E-07, -1.89309167E-08, -4.79768731E-07, 7.96079978E-09, 1.62897058E-09, 3.94367674E-08, -1.18566247E-09, 3.34678041E-10, -1.15606447E-10, -2.80626406E+00, 5.48712484E-01, -3.99428410E-03, -9.54009191E-04, 1.93090978E-05, -3.08806365E-01, 1.16952364E-02, 4.95271903E-04, -1.90710882E-05, 2.10787756E-03, -6.98445738E-04, 2.30109073E-05, 4.17856590E-04, -1.27043871E-05, -3.04620472E-06, -5.65095215E-05, -4.52166564E-07, 2.46688878E-08, 2.42674348E-10, 1.54547250E-04, 5.24110970E-06, -8.75874982E-08, -1.50743064E-09, -1.56236307E-05, -1.33895614E-07, 2.49709824E-09, 6.51711721E-07, 1.94960053E-09, -1.00361113E-08, -1.21206673E-05, -2.18203660E-07, 7.51269482E-09, 9.79063848E-11, 1.25006734E-06, -1.81584736E-09, -3.52197671E-10, -3.36514630E-08, 1.35908359E-10, 4.17032620E-10, -1.30369025E-09, 4.13908461E-10, 9.22652254E-12, -5.08220384E-09, -2.24730961E-11, 1.17139133E-10, 6.62154879E-10, 4.03863260E-13, 1.95087203E-12, -4.73602469E-12, 5.12733497E+00, -3.12788561E-01, -1.96701861E-02, 9.99690870E-04, 9.51738512E-06, -4.66426341E-07, 5.48050612E-01, -3.30552823E-03, -1.64119440E-03, -5.16670694E-06, 9.52692432E-07, -4.29223622E-02, 5.00845667E-03, 1.00601257E-06, -1.81748644E-06, -1.25813502E-03, -1.79330391E-04, 2.34994441E-06, 1.29735808E-04, 1.29064870E-06, -2.28558686E-06, 5.14507424E-02, -4.32510997E-03, 8.99281156E-05, -7.14663943E-07, -2.66016305E-04, 2.63789586E-04, -7.01199003E-06, -1.06823306E-04, 3.61341136E-06, 2.29748967E-07, 3.04788893E-04, -6.42070836E-05, 1.16257971E-06, 7.68023384E-06, -5.47446896E-07, -3.59937910E-08, -4.36497725E-06, 1.68737969E-07, 2.67489271E-08, 3.23926897E-09, -3.53874123E-02, -2.21201190E-01, 1.55126038E-02, -2.63917279E-04, 4.53433455E-02, -4.32943862E-03, 1.45389826E-04, 2.17508610E-04, -6.66724702E-05, 3.33217140E-05, -2.26921615E-03, 3.80261982E-04, -5.45314314E-09, -7.96355448E-04, 2.53458034E-05, -6.31223658E-06, 3.02122035E-04, -4.77403547E-06, 1.73825715E-06, -4.09087898E-07, 6.14155345E-01, -6.16755931E-02, 1.33374846E-03, 3.55375387E-03, -5.13027851E-04, 1.02449757E-04, -1.48526421E-03, -4.11469183E-05, -6.80434415E-06, -9.77675906E-06, 8.82773108E-02, -3.01859306E-03, 1.04452989E-03, 2.47090539E-04, 1.48348065E-03 + }; +} diff --git a/utcicalculator.h b/utcicalculator.h new file mode 100644 index 0000000..6d59947 --- /dev/null +++ b/utcicalculator.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +/* + UtciCalculator + - lit un fichier de coefficients (format ESM_3/tab-delimited) + - calcule UTCI via l'approximation polynomiale : UTCI = Ta + Offset(Ta, va, tm, pa) + where tm = Tr - Ta and pa is water vapour pressure in kPa. + - fournit une fonction utilitaire pour calculer pa depuis Ta et relative humidity (Magnus-Tetens). +*/ + +class UtciCalculator : public QObject +{ + Q_OBJECT +public: + explicit UtciCalculator(QObject *parent = nullptr); + + // Load coefficients from a file (ESM_3 table or exported txt) + // Return true on success. + //bool loadCoefficientsFromFile(const QString &path); + + // Calculate UTCI. + // Ta: air temperature (°C) + // va: wind speed at 10 m (m/s) + // Tr: mean radiant temperature (°C) + // rh: relative humidity in % (0..100). If rh < 0, supply pa directly (next param) and set pa_provided=true. + // pa_kpa: vapour pressure in kPa (used if pa_provided==true) + double computeUTCI(double Ta, double va, double Tr, double rh) const; + + // Utility: compute water vapour pressure (kPa) from Ta and relative humidity (Magnus formula) + // Ta in °C, rh in % + static double vapourPressureFromRH(double Ta, double rh_percent); + +private: + // coefficients in the same order as the ESM_3 polynomial table terms. + QVector m_coeffs; + + // Evaluate the polynomial offset = f(Ta, va, tm, pa) + double evalOffset(double Ta, double va, double tm, double pa) const; + void initCoefficients(); // initialise m_coeffs avec les 210 valeurs +};