2020-11-24 07:31:16 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
2023-11-19 00:43:20 -05:00
|
|
|
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
|
|
|
// written by Christian Daniel //
|
|
|
|
// Copyright (C) 2015-2016, 2018-2019 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
|
|
|
// Copyright (C) 2020-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
2020-11-24 07:31:16 -05:00
|
|
|
// //
|
|
|
|
// This program is free software; you can redistribute it and/or modify //
|
|
|
|
// it under the terms of the GNU General Public License as published by //
|
|
|
|
// the Free Software Foundation as version 3 of the License, or //
|
|
|
|
// (at your option) any later version. //
|
|
|
|
// //
|
|
|
|
// This program is distributed in the hope that it will be useful, //
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
|
|
// GNU General Public License V3 for more details. //
|
|
|
|
// //
|
|
|
|
// You should have received a copy of the GNU General Public License //
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#ifndef INCLUDE_UNITS_H
|
|
|
|
#define INCLUDE_UNITS_H
|
|
|
|
|
|
|
|
#include <cmath>
|
2021-01-13 12:10:18 -05:00
|
|
|
#include <QString>
|
|
|
|
#include <QStringList>
|
|
|
|
#include <QRegExp>
|
|
|
|
#include <QDebug>
|
2020-11-24 07:31:16 -05:00
|
|
|
|
|
|
|
#include "export.h"
|
|
|
|
|
|
|
|
// Unit conversions
|
|
|
|
class SDRBASE_API Units
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
static inline float feetToMetres(float feet)
|
|
|
|
{
|
|
|
|
return feet * 0.3048f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int feetToIntegerMetres(float feet)
|
|
|
|
{
|
|
|
|
return (int)std::round(feetToMetres(feet));
|
|
|
|
}
|
|
|
|
|
2021-05-20 12:15:14 -04:00
|
|
|
static inline float metresToFeet(float metres)
|
|
|
|
{
|
|
|
|
return metres * 3.28084f;
|
|
|
|
}
|
|
|
|
|
ADS-B: Add support for decoding Comm-B replies in Mode-S frames. This adds TAS, IAS, Mach, Selelected Alt, Selected Heading, Turn rate, Roll angle, Autopilot enabled, Vertical and lateral navigation modes, Baro setting, Headwind, OAT, Wind speed and direction, Pressure, Static air temperature, Humidity.
2022-08-26 07:35:12 -04:00
|
|
|
static inline int metresToIntegerFeet(float metres)
|
|
|
|
{
|
|
|
|
return (int)std::round(metresToFeet(metres));
|
|
|
|
}
|
|
|
|
|
2020-11-24 07:31:16 -05:00
|
|
|
static inline float nauticalMilesToMetres(float nauticalMiles)
|
|
|
|
{
|
|
|
|
return nauticalMiles * 1855.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int nauticalMilesToIntegerMetres(float nauticalMiles)
|
|
|
|
{
|
|
|
|
return (int)std::round(nauticalMilesToMetres(nauticalMiles));
|
|
|
|
}
|
|
|
|
|
|
|
|
static float knotsToKPH(float knots)
|
|
|
|
{
|
|
|
|
return knots * 1.852f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int knotsToIntegerKPH(float knots)
|
|
|
|
{
|
|
|
|
return (int)std::round(knotsToKPH(knots));
|
|
|
|
}
|
|
|
|
|
2021-04-02 16:14:49 -04:00
|
|
|
static float knotsToMPH(float knots)
|
|
|
|
{
|
|
|
|
return knots * 1.15078f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int knotsToIntegerMPH(float knots)
|
|
|
|
{
|
|
|
|
return (int)std::round(knotsToMPH(knots));
|
|
|
|
}
|
|
|
|
|
ADS-B: Add support for decoding Comm-B replies in Mode-S frames. This adds TAS, IAS, Mach, Selelected Alt, Selected Heading, Turn rate, Roll angle, Autopilot enabled, Vertical and lateral navigation modes, Baro setting, Headwind, OAT, Wind speed and direction, Pressure, Static air temperature, Humidity.
2022-08-26 07:35:12 -04:00
|
|
|
static float knotsToMetresPerSecond(float knots)
|
|
|
|
{
|
|
|
|
return knots * 0.514444f;
|
|
|
|
}
|
|
|
|
|
2021-02-26 15:25:48 -05:00
|
|
|
static float kmpsToKPH(float kps)
|
|
|
|
{
|
|
|
|
return kps * (60.0 * 60.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int kmpsToIntegerKPH(float kps)
|
|
|
|
{
|
|
|
|
return (int)std::round(kmpsToKPH(kps));
|
|
|
|
}
|
|
|
|
|
2020-11-24 07:31:16 -05:00
|
|
|
static float feetPerMinToMetresPerSecond(float fpm)
|
|
|
|
{
|
|
|
|
return fpm * 0.00508f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int feetPerMinToIntegerMetresPerSecond(float fpm)
|
|
|
|
{
|
|
|
|
return (int)std::round(feetPerMinToMetresPerSecond(fpm));
|
|
|
|
}
|
|
|
|
|
2021-01-29 07:57:58 -05:00
|
|
|
template <class T>
|
|
|
|
static T degreesToRadians(T degrees)
|
2020-11-24 07:31:16 -05:00
|
|
|
{
|
2021-01-29 07:57:58 -05:00
|
|
|
return degrees * ((T)M_PI) / 180.0f;
|
2020-11-24 07:31:16 -05:00
|
|
|
}
|
|
|
|
|
2021-01-29 07:57:58 -05:00
|
|
|
template <class T>
|
|
|
|
static T radiansToDegrees(T radians)
|
2020-11-24 07:31:16 -05:00
|
|
|
{
|
2021-01-29 07:57:58 -05:00
|
|
|
return radians * 180.0f / ((T)M_PI);
|
2020-11-24 07:31:16 -05:00
|
|
|
}
|
|
|
|
|
2021-01-13 12:10:18 -05:00
|
|
|
static float fahrenheitToCelsius(float fahrenheit)
|
|
|
|
{
|
|
|
|
return (fahrenheit - 32.0f) * (5.0f/9.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static float celsiusToKelvin(float celsius)
|
|
|
|
{
|
|
|
|
return celsius + 273.15f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static float inchesToMilimetres(float inches)
|
|
|
|
{
|
|
|
|
return inches * 25.4f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static float milesToKilometres(float miles)
|
|
|
|
{
|
|
|
|
return miles * 1.60934f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static float degreesMinutesSecondsToDecimal(int degrees, int minutes, float seconds)
|
|
|
|
{
|
|
|
|
float dec = std::abs(degrees) + minutes * 1.0f/60.0f + seconds * 1.0f/(60.0f*60.0f);
|
|
|
|
if (degrees < 0)
|
|
|
|
return -dec;
|
|
|
|
else
|
|
|
|
return dec;
|
|
|
|
}
|
|
|
|
|
|
|
|
static float hoursMinutesSecondsToDecimal(int hours, int minutes, float seconds)
|
|
|
|
{
|
|
|
|
return hours + minutes * 1.0f/60.0f + seconds * 1.0f/(60.0f*60.0f);
|
|
|
|
}
|
|
|
|
|
2021-05-24 07:34:38 -04:00
|
|
|
// Also supports decimal degrees
|
|
|
|
static bool degreeMinuteAndSecondsToDecimalDegrees(const QString& string, float& degrees)
|
|
|
|
{
|
|
|
|
QRegExp decimal("(-?[0-9]+(\\.[0-9]+)?)");
|
|
|
|
if (decimal.exactMatch(string))
|
|
|
|
{
|
|
|
|
degrees = decimal.capturedTexts()[1].toFloat();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
QRegExp dms(QString("(-)?([0-9]+)[%1d](([0-9]+)['m](([0-9]+(\\.[0-9]+)?)[\"s])?)?").arg(QChar(0xb0)));
|
|
|
|
if (dms.exactMatch(string))
|
|
|
|
{
|
|
|
|
float d = 0.0f;
|
|
|
|
bool neg = false;
|
|
|
|
for (int i = 0; i < dms.captureCount(); i++) {
|
|
|
|
qDebug() << dms.capturedTexts()[i];
|
|
|
|
}
|
|
|
|
if (dms.captureCount() >= 1) {
|
|
|
|
neg = dms.capturedTexts()[1] == "-";
|
|
|
|
}
|
|
|
|
if (dms.captureCount() >= 3) {
|
|
|
|
d = dms.capturedTexts()[2].toFloat();
|
|
|
|
}
|
|
|
|
float m = 0.0f;
|
|
|
|
if (dms.captureCount() >= 5) {
|
|
|
|
m = dms.capturedTexts()[4].toFloat();
|
|
|
|
}
|
|
|
|
float s = 0.0f;
|
|
|
|
if (dms.captureCount() >= 7) {
|
|
|
|
s = dms.capturedTexts()[6].toFloat();
|
|
|
|
}
|
|
|
|
qDebug() << neg << d << m << s;
|
|
|
|
degrees = d + m/60.0 + s/(60.0*60.0);
|
|
|
|
if (neg) {
|
|
|
|
degrees = -degrees;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-13 12:10:18 -05:00
|
|
|
static QString decimalDegreesToDegreeMinutesAndSeconds(float decimal, int secondsFieldWidth=5)
|
|
|
|
{
|
2021-05-24 07:34:38 -04:00
|
|
|
double v, d, m, s;
|
2021-01-13 12:10:18 -05:00
|
|
|
int neg;
|
|
|
|
|
|
|
|
v = decimal;
|
|
|
|
neg = v < 0.0f;
|
|
|
|
v = fabs(v);
|
|
|
|
d = floor(v);
|
|
|
|
v -= d;
|
|
|
|
v *= 60.0;
|
|
|
|
m = floor(v);
|
|
|
|
v -= m;
|
|
|
|
v *= 60.0;
|
|
|
|
s = v;
|
|
|
|
return QString("%1%2%3%4'%5\"").arg(neg ? "-" : "").arg((int)d).arg(QChar(0xb0)).arg((int)m, 2, 10, QChar('0')).arg(s, secondsFieldWidth, 'f', 2, QChar('0'));
|
|
|
|
}
|
|
|
|
|
|
|
|
static QString decimalDegreesToDegreesAndMinutes(float decimal)
|
|
|
|
{
|
2021-05-24 07:34:38 -04:00
|
|
|
double v, d, m;
|
2021-01-13 12:10:18 -05:00
|
|
|
int neg;
|
|
|
|
|
|
|
|
v = decimal;
|
|
|
|
neg = v < 0.0f;
|
|
|
|
v = fabs(v);
|
|
|
|
d = floor(v);
|
|
|
|
v -= d;
|
|
|
|
v *= 60.0;
|
|
|
|
m = round(v);
|
2021-05-24 07:34:38 -04:00
|
|
|
if (m == 60)
|
|
|
|
{
|
|
|
|
if (neg) {
|
|
|
|
d--;
|
|
|
|
} else {
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
m = 0;
|
|
|
|
}
|
2021-01-13 12:10:18 -05:00
|
|
|
return QString("%1%2%3%4'").arg(neg ? "-" : "").arg((int)d).arg(QChar(0xb0)).arg((int)m, 2, 10, QChar('0'));
|
|
|
|
}
|
|
|
|
|
|
|
|
static QString decimalDegreesToDegrees(float decimal)
|
|
|
|
{
|
2021-05-24 07:34:38 -04:00
|
|
|
double v, d;
|
2021-01-13 12:10:18 -05:00
|
|
|
int neg;
|
|
|
|
|
|
|
|
v = decimal;
|
|
|
|
neg = v < 0.0f;
|
|
|
|
v = fabs(v);
|
|
|
|
d = round(v);
|
|
|
|
return QString("%1%2%3").arg(neg ? "-" : "").arg((int)d).arg(QChar(0xb0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static QString decimalHoursToHoursMinutesAndSeconds(float decimal, int precision=2)
|
|
|
|
{
|
2021-05-24 07:34:38 -04:00
|
|
|
double v, h, m, s;
|
2021-01-13 12:10:18 -05:00
|
|
|
|
|
|
|
v = decimal;
|
|
|
|
v = fabs(v);
|
|
|
|
h = floor(v);
|
|
|
|
v -= h;
|
|
|
|
v *= 60.0;
|
|
|
|
m = floor(v);
|
|
|
|
v -= m;
|
|
|
|
v *= 60.0;
|
|
|
|
s = v;
|
|
|
|
return QString("%1h%2m%3s").arg((int)h).arg((int)m, 2, 10, QChar('0')).arg(s, 2, 'f', precision, QChar('0'));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to convert a string to latitude and longitude. Returns false if not recognised format.
|
|
|
|
// https://en.wikipedia.org/wiki/ISO_6709 specifies a standard syntax
|
|
|
|
// We support both decimal and DMS formats
|
|
|
|
static bool stringToLatitudeAndLongitude(const QString& string, float& latitude, float& longitude)
|
|
|
|
{
|
2023-03-21 06:54:42 -04:00
|
|
|
QRegExp decimal("(-?[0-9]+(\\.[0-9]+)?) *,? *(-?[0-9]+(\\.[0-9]+)?)");
|
2021-01-13 12:10:18 -05:00
|
|
|
if (decimal.exactMatch(string))
|
|
|
|
{
|
|
|
|
latitude = decimal.capturedTexts()[1].toFloat();
|
2023-03-21 06:54:42 -04:00
|
|
|
longitude = decimal.capturedTexts()[3].toFloat();
|
2021-01-13 12:10:18 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
QRegExp dms(QString("([0-9]+)[%1d]([0-9]+)['m]([0-9]+(\\.[0-9]+)?)[\"s]([NS]) *,? *([0-9]+)[%1d]([0-9]+)['m]([0-9]+(\\.[0-9]+)?)[\"s]([EW])").arg(QChar(0xb0)));
|
|
|
|
if (dms.exactMatch(string))
|
|
|
|
{
|
|
|
|
float latD = dms.capturedTexts()[1].toFloat();
|
|
|
|
float latM = dms.capturedTexts()[2].toFloat();
|
|
|
|
float latS = dms.capturedTexts()[3].toFloat();
|
|
|
|
bool north = dms.capturedTexts()[5] == "N";
|
|
|
|
float lonD = dms.capturedTexts()[6].toFloat();
|
|
|
|
float lonM = dms.capturedTexts()[7].toFloat();
|
|
|
|
float lonS = dms.capturedTexts()[8].toFloat();
|
|
|
|
bool east = dms.capturedTexts()[10] == "E";
|
|
|
|
latitude = latD + latM/60.0 + latS/(60.0*60.0);
|
|
|
|
if (!north)
|
|
|
|
latitude = -latitude;
|
2023-03-03 12:14:56 -05:00
|
|
|
longitude = lonD + lonM/60.0 + lonS/(60.0*60.0);
|
|
|
|
if (!east)
|
|
|
|
longitude = -longitude;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
QRegExp dms2(QString("([0-9]+)([NS])([0-9]{2})([0-9]{2}) *,?([0-9]+)([EW])([0-9]{2})([0-9]{2})"));
|
|
|
|
if (dms2.exactMatch(string))
|
|
|
|
{
|
|
|
|
float latD = dms2.capturedTexts()[1].toFloat();
|
|
|
|
bool north = dms2.capturedTexts()[2] == "N";
|
|
|
|
float latM = dms2.capturedTexts()[3].toFloat();
|
|
|
|
float latS = dms2.capturedTexts()[4].toFloat();
|
|
|
|
float lonD = dms2.capturedTexts()[5].toFloat();
|
|
|
|
bool east = dms2.capturedTexts()[6] == "E";
|
|
|
|
float lonM = dms2.capturedTexts()[7].toFloat();
|
|
|
|
float lonS = dms2.capturedTexts()[8].toFloat();
|
|
|
|
latitude = latD + latM/60.0 + latS/(60.0*60.0);
|
|
|
|
if (!north)
|
|
|
|
latitude = -latitude;
|
2023-03-21 06:54:42 -04:00
|
|
|
longitude = lonD + lonM/60.0 + lonS/(60.0*60.0);
|
|
|
|
if (!east)
|
|
|
|
longitude = -longitude;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// 512255.5900N 0024400.6105W as used on aviation charts
|
|
|
|
QRegExp dms3(QString("(\\d{2})(\\d{2})((\\d{2})(\\.\\d+)?)([NS]) *,?(\\d{3})(\\d{2})((\\d{2})(\\.\\d+)?)([EW])"));
|
|
|
|
if (dms3.exactMatch(string))
|
|
|
|
{
|
|
|
|
float latD = dms3.capturedTexts()[1].toFloat();
|
|
|
|
float latM = dms3.capturedTexts()[2].toFloat();
|
|
|
|
float latS = dms3.capturedTexts()[3].toFloat();
|
|
|
|
bool north = dms3.capturedTexts()[6] == "N";
|
|
|
|
float lonD = dms3.capturedTexts()[7].toFloat();
|
|
|
|
float lonM = dms3.capturedTexts()[8].toFloat();
|
|
|
|
float lonS = dms3.capturedTexts()[9].toFloat();
|
|
|
|
bool east = dms3.capturedTexts()[12] == "E";
|
|
|
|
latitude = latD + latM/60.0 + latS/(60.0*60.0);
|
|
|
|
if (!north)
|
|
|
|
latitude = -latitude;
|
2021-01-13 12:10:18 -05:00
|
|
|
longitude = lonD + lonM/60.0 + lonS/(60.0*60.0);
|
|
|
|
if (!east)
|
|
|
|
longitude = -longitude;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-02-14 08:20:33 -05:00
|
|
|
// Try to convert a string to Right Ascension (RA) and Declination. Returns false if not recognised format.
|
|
|
|
// This supports HMS/DMS or decimal.
|
|
|
|
// E.g.:
|
|
|
|
// 12 05 12.23 +17 06 21.0
|
|
|
|
// 12:05:12.23 -17:06:21.0
|
|
|
|
// 12h05m12.23s +17d06m21.0s
|
|
|
|
// 107.1324 -34.233
|
|
|
|
static bool stringToRADec(const QString& string, float& ra, float& dec)
|
|
|
|
{
|
|
|
|
QRegExp dms("([0-9]+)[ :h]([0-9]+)[ :m]([0-9]+(\\.[0-9]+)?)s? *,? *([+-]?[0-9]+)[ :d]([0-9]+)[ :m]([0-9]+(\\.[0-9]+)?)s?");
|
|
|
|
if (dms.exactMatch(string))
|
|
|
|
{
|
|
|
|
int raHours = dms.capturedTexts()[1].toInt();
|
|
|
|
int raMins = dms.capturedTexts()[2].toInt();
|
|
|
|
float raSecs = dms.capturedTexts()[3].toFloat();
|
|
|
|
ra = raHours + raMins / 60.0f + raSecs / (60.0f * 60.0f);
|
|
|
|
qDebug() << ra << raHours << raMins << raSecs;
|
|
|
|
int decDegs = dms.capturedTexts()[5].toInt();
|
|
|
|
int decMins = dms.capturedTexts()[6].toInt();
|
|
|
|
float decSecs = dms.capturedTexts()[7].toFloat();
|
|
|
|
bool neg = decDegs < 0;
|
|
|
|
dec = abs(decDegs) + decMins / 60.0f + decSecs / (60.0f * 60.0f);
|
|
|
|
if (neg) {
|
|
|
|
dec = -dec;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
QRegExp decimal("([0-9]+(\\.[0-9]+)?) *,? *([+-]?[0-9]+(\\.[0-9]+)?)");
|
|
|
|
if (decimal.exactMatch(string))
|
|
|
|
{
|
|
|
|
ra = decimal.capturedTexts()[1].toFloat();
|
|
|
|
dec = decimal.capturedTexts()[3].toFloat();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-22 11:57:47 -05:00
|
|
|
static float solarFluxUnitsToJansky(float sfu)
|
|
|
|
{
|
|
|
|
return sfu * 10000.0f;
|
|
|
|
}
|
|
|
|
|
2021-01-29 07:57:58 -05:00
|
|
|
template <class T>
|
|
|
|
static T solarFluxUnitsToWattsPerMetrePerHertz(T sfu)
|
2021-01-22 11:57:47 -05:00
|
|
|
{
|
|
|
|
return sfu * 1e-22f;
|
|
|
|
}
|
|
|
|
|
2021-10-12 06:31:14 -04:00
|
|
|
template <class T>
|
|
|
|
static T wattsPerMetrePerHertzToSolarFluxUnits(T w)
|
|
|
|
{
|
|
|
|
return w / 1e-22f;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
static T wattsPerMetrePerHertzToJansky(T w)
|
|
|
|
{
|
|
|
|
return w / 1e-26f;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
static T noiseFigureToNoiseTemp(T nfdB, T refTempK=T(290.0))
|
|
|
|
{
|
|
|
|
return refTempK * (std::pow(T(10.0), nfdB/T(10.0)) - T(1.0));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
static T noiseTempToNoiseFigureTo(T tempK, T refTempK=T(290.0))
|
|
|
|
{
|
|
|
|
return T(10.0) * std::log10(tempK/refTempK+T(1.0));
|
|
|
|
}
|
|
|
|
|
2020-11-24 07:31:16 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif // INCLUDE_UNITS_H
|