aboutsummaryrefslogtreecommitdiffstats
path: root/core/units.h
diff options
context:
space:
mode:
Diffstat (limited to 'core/units.h')
-rw-r--r--core/units.h280
1 files changed, 280 insertions, 0 deletions
diff --git a/core/units.h b/core/units.h
new file mode 100644
index 000000000..029bb64fa
--- /dev/null
+++ b/core/units.h
@@ -0,0 +1,280 @@
+#ifndef UNITS_H
+#define UNITS_H
+
+#include <math.h>
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define O2_IN_AIR 209 // permille
+#define N2_IN_AIR 781
+#define O2_DENSITY 1429 // mg/Liter
+#define N2_DENSITY 1251
+#define HE_DENSITY 179
+#define SURFACE_PRESSURE 1013 // mbar
+#define SURFACE_PRESSURE_STRING "1013"
+#define ZERO_C_IN_MKELVIN 273150 // mKelvin
+
+#ifdef __cplusplus
+#define M_OR_FT(_m, _f) ((prefs.units.length == units::METERS) ? ((_m) * 1000) : (feet_to_mm(_f)))
+#else
+#define M_OR_FT(_m, _f) ((prefs.units.length == METERS) ? ((_m) * 1000) : (feet_to_mm(_f)))
+#endif
+
+/* Salinity is expressed in weight in grams per 10l */
+#define SEAWATER_SALINITY 10300
+#define FRESHWATER_SALINITY 10000
+
+#include <stdint.h>
+/*
+ * Some silly typedefs to make our units very explicit.
+ *
+ * Also, the units are chosen so that values can be expressible as
+ * integers, so that we never have FP rounding issues. And they
+ * are small enough that converting to/from imperial units doesn't
+ * really matter.
+ *
+ * We also strive to make '0' a meaningless number saying "not
+ * initialized", since many values are things that may not have
+ * been reported (eg cylinder pressure or temperature from dive
+ * computers that don't support them). But sometimes -1 is an even
+ * more explicit way of saying "not there".
+ *
+ * Thus "millibar" for pressure, for example, or "millikelvin" for
+ * temperatures. Doing temperatures in celsius or fahrenheit would
+ * make for loss of precision when converting from one to the other,
+ * and using millikelvin is SI-like but also means that a temperature
+ * of '0' is clearly just a missing temperature or cylinder pressure.
+ *
+ * Also strive to use units that can not possibly be mistaken for a
+ * valid value in a "normal" system without conversion. If the max
+ * depth of a dive is '20000', you probably didn't convert from mm on
+ * output, or if the max depth gets reported as "0.2ft" it was either
+ * a really boring dive, or there was some missing input conversion,
+ * and a 60-ft dive got recorded as 60mm.
+ *
+ * Doing these as "structs containing value" means that we always
+ * have to explicitly write out those units in order to get at the
+ * actual value. So there is hopefully little fear of using a value
+ * in millikelvin as Fahrenheit by mistake.
+ *
+ * We don't actually use these all yet, so maybe they'll change, but
+ * I made a number of types as guidelines.
+ */
+typedef int64_t timestamp_t;
+
+typedef struct
+{
+ uint32_t seconds; // durations up to 68 yrs
+} duration_t;
+
+typedef struct
+{
+ int32_t seconds; // offsets up to +/- 34 yrs
+} offset_t;
+
+typedef struct
+{
+ int32_t mm;
+} depth_t; // depth to 2000 km
+
+typedef struct
+{
+ int32_t mbar; // pressure up to 2000 bar
+} pressure_t;
+
+typedef struct
+{
+ uint16_t mbar;
+} o2pressure_t; // pressure up to 65 bar
+
+typedef struct
+{
+ int16_t degrees;
+} bearing_t; // compass bearing
+
+typedef struct
+{
+ uint32_t mkelvin; // up to 1750 degrees K (temperatures in K are always positive)
+} temperature_t;
+
+typedef struct
+{
+ int mliter;
+} volume_t;
+
+typedef struct
+{
+ int permille;
+} fraction_t;
+
+typedef struct
+{
+ int grams;
+} weight_t;
+
+typedef struct
+{
+ int udeg;
+} degrees_t;
+
+static inline double udeg_to_radians(int udeg)
+{
+ return (udeg * M_PI) / (1000000.0 * 180.0);
+}
+
+static inline double grams_to_lbs(int grams)
+{
+ return grams / 453.6;
+}
+
+static inline int lbs_to_grams(double lbs)
+{
+ return rint(lbs * 453.6);
+}
+
+static inline double ml_to_cuft(int ml)
+{
+ return ml / 28316.8466;
+}
+
+static inline double cuft_to_l(double cuft)
+{
+ return cuft * 28.3168466;
+}
+
+static inline double mm_to_feet(int mm)
+{
+ return mm * 0.00328084;
+}
+
+static inline double m_to_mile(int m)
+{
+ return m / 1609.344;
+}
+
+static inline unsigned long feet_to_mm(double feet)
+{
+ return rint(feet * 304.8);
+}
+
+static inline int to_feet(depth_t depth)
+{
+ return rint(mm_to_feet(depth.mm));
+}
+
+static inline double mkelvin_to_C(int mkelvin)
+{
+ return (mkelvin - ZERO_C_IN_MKELVIN) / 1000.0;
+}
+
+static inline double mkelvin_to_F(int mkelvin)
+{
+ return mkelvin * 9 / 5000.0 - 459.670;
+}
+
+static inline unsigned long F_to_mkelvin(double f)
+{
+ return rint((f - 32) * 1000 / 1.8 + ZERO_C_IN_MKELVIN);
+}
+
+static inline unsigned long C_to_mkelvin(double c)
+{
+ return rint(c * 1000 + ZERO_C_IN_MKELVIN);
+}
+
+static inline double psi_to_bar(double psi)
+{
+ return psi / 14.5037738;
+}
+
+static inline long psi_to_mbar(double psi)
+{
+ return rint(psi_to_bar(psi) * 1000);
+}
+
+static inline int to_PSI(pressure_t pressure)
+{
+ return rint(pressure.mbar * 0.0145037738);
+}
+
+static inline double bar_to_atm(double bar)
+{
+ return bar / SURFACE_PRESSURE * 1000;
+}
+
+static inline double mbar_to_atm(int mbar)
+{
+ return (double)mbar / SURFACE_PRESSURE;
+}
+
+static inline int mbar_to_PSI(int mbar)
+{
+ pressure_t p = { mbar };
+ return to_PSI(p);
+}
+
+/*
+ * We keep our internal data in well-specified units, but
+ * the input and output may come in some random format. This
+ * keeps track of those units.
+ */
+/* turns out in Win32 PASCAL is defined as a calling convention */
+#ifdef WIN32
+#undef PASCAL
+#endif
+struct units {
+ enum LENGHT {
+ METERS,
+ FEET
+ } length;
+ enum VOLUME {
+ LITER,
+ CUFT
+ } volume;
+ enum PRESSURE {
+ BAR,
+ PSI,
+ PASCAL
+ } pressure;
+ enum TEMPERATURE {
+ CELSIUS,
+ FAHRENHEIT,
+ KELVIN
+ } temperature;
+ enum WEIGHT {
+ KG,
+ LBS
+ } weight;
+ enum TIME {
+ SECONDS,
+ MINUTES
+ } vertical_speed_time;
+};
+
+/*
+ * We're going to default to SI units for input. Yes,
+ * technically the SI unit for pressure is Pascal, but
+ * we default to bar (10^5 pascal), which people
+ * actually use. Similarly, C instead of Kelvin.
+ * And kg instead of g.
+ */
+#define SI_UNITS \
+ { \
+ .length = METERS, .volume = LITER, .pressure = BAR, .temperature = CELSIUS, .weight = KG, .vertical_speed_time = MINUTES \
+ }
+
+#define IMPERIAL_UNITS \
+ { \
+ .length = FEET, .volume = CUFT, .pressure = PSI, .temperature = FAHRENHEIT, .weight = LBS, .vertical_speed_time = MINUTES \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif