blob: cfc01a1a7808e6144d75f9c4142f0bfbc8afd7d6 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <hid-parser/units.h>
namespace {
constexpr uint32_t system_mask = 0xF;
// Taken from the Hid document on units. Each unit occupies
// a half byte (Nibble) so the shifts increment by 4 each time.
constexpr int system_shift = 0;
constexpr int length_shift = 4;
constexpr int mass_shift = 8;
constexpr int time_shift = 16;
constexpr int temperature_shift = 20;
constexpr int current_shift = 24;
constexpr int luminous_shift = 28;
constexpr int8_t SignExtendFromNibble(int8_t nibble) {
// Expression taken and simplified from:
// http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
return static_cast<int8_t>(((nibble & 0x0F) ^ 0x08) - 0x08);
}
// Set the exponent of a given unit.
// Since exp will be converted to a nibble its values must range from [-8, 7].
constexpr void SetUnitTypeExp(uint32_t& type, int8_t exp, int unit_shift) {
type = type | ((exp & 0x0F) << unit_shift);
}
// Get the exponent of a given unit.
// Exponents range from [-8, 7].
constexpr int8_t GetUnitTypeExp(uint32_t type, int unit_shift) {
return SignExtendFromNibble(static_cast<int8_t>((type & (0xF << unit_shift)) >> unit_shift));
}
constexpr bool IsSiToEng(hid::unit::System system_in, hid::unit::System system_out) {
return ((system_in == hid::unit::System::si_linear) || (system_in == hid::unit::System::si_rotation)) &&
((system_out == hid::unit::System::eng_linear) || (system_out == hid::unit::System::eng_rotation));
}
constexpr bool IsEngToSi(hid::unit::System system_in, hid::unit::System system_out) {
return ((system_in == hid::unit::System::eng_linear) || (system_in == hid::unit::System::eng_rotation)) &&
((system_out == hid::unit::System::si_linear) || (system_out == hid::unit::System::si_rotation));
}
bool ConvertDistance(double val_in, const hid::Unit& unit_in, double& val_out, const hid::Unit& unit_out) {
int exp = hid::unit::GetLengthExp(unit_in);
auto system_in = hid::unit::GetSystem(unit_in);
auto system_out = hid::unit::GetSystem(unit_out);
if ((exp == 0) || (system_in == system_out)) {
val_out = val_in;
return true;
}
// Centimeters to Inches.
if ((system_in == hid::unit::System::si_linear) &&
(system_out == hid::unit::System::eng_linear)) {
val_out = val_in * pow(0.393701, exp);
return true;
}
// Inches to Centimeters.
if ((system_in == hid::unit::System::eng_linear) &&
(system_out == hid::unit::System::si_linear)) {
val_out = val_in * pow(2.54, exp);
return true;
}
// Degrees to Radians.
if ((system_in == hid::unit::System::eng_rotation) &&
(system_out == hid::unit::System::si_rotation)) {
val_out = val_in * pow((3.14159 / 180.0), exp);
return true;
}
// Radians to Degrees.
if ((system_in == hid::unit::System::si_rotation) &&
(system_out == hid::unit::System::eng_rotation)) {
val_out = val_in * pow((180.0 / 3.14159), exp);
return true;
}
return false;
}
bool ConvertMass(double val_in, const hid::Unit& unit_in, double& val_out, const hid::Unit& unit_out) {
int exp = hid::unit::GetMassExp(unit_in);
auto system_in = hid::unit::GetSystem(unit_in);
auto system_out = hid::unit::GetSystem(unit_out);
if ((exp == 0) || (system_in == system_out)) {
val_out = val_in;
return true;
}
// Grams to Slugs.
if (IsSiToEng(system_in, system_out)) {
val_out = val_in * pow(6.85218e-5, exp);
return true;
}
// Slugs to Grams.
if (IsEngToSi(system_in, system_out)) {
val_out = val_in * pow(14593.9, exp);
return true;
}
return false;
}
bool ConvertTemperature(double val_in, const hid::Unit& unit_in, double& val_out, const hid::Unit& unit_out) {
int exp = hid::unit::GetTemperatureExp(unit_in);
auto system_in = hid::unit::GetSystem(unit_in);
auto system_out = hid::unit::GetSystem(unit_out);
if ((exp == 0) || (system_in == system_out)) {
val_out = val_in;
return true;
}
// Kelvin to Fahrenheit.
if (IsSiToEng(system_in, system_out)) {
val_out = (val_in - 273.15) * (9.0 / 5.0) + 32;
return true;
}
// Fahrenheit to Kelvin.
if (IsEngToSi(system_in, system_out)) {
val_out = (val_in - 32) * (5.0 / 9.0) + 273.15;
return true;
}
return false;
}
} // namespace
namespace hid {
namespace unit {
void SetSystem(Unit& unit, hid::unit::System system) {
SetUnitTypeExp(unit.type, static_cast<int8_t>(system), system_shift);
}
hid::unit::System GetSystem(const Unit& unit) {
int sys = GetUnitTypeExp(unit.type, system_shift);
switch (sys) {
case 1:
return hid::unit::System::si_linear;
case 2:
return hid::unit::System::si_rotation;
case 3:
return hid::unit::System::eng_linear;
case 4:
return hid::unit::System::eng_rotation;
default:
return hid::unit::System::reserved;
}
}
void SetLengthExp(Unit& unit, int8_t exp) {
SetUnitTypeExp(unit.type, exp, length_shift);
}
void SetMassExp(Unit& unit, int8_t exp) {
SetUnitTypeExp(unit.type, exp, mass_shift);
}
void SetTimeExp(Unit& unit, int8_t exp) {
SetUnitTypeExp(unit.type, exp, time_shift);
}
void SetTemperatureExp(Unit& unit, int8_t exp) {
SetUnitTypeExp(unit.type, exp, temperature_shift);
}
void SetCurrentExp(Unit& unit, int8_t exp) {
SetUnitTypeExp(unit.type, exp, current_shift);
}
void SetLuminousExp(Unit& unit, int8_t exp) {
SetUnitTypeExp(unit.type, exp, luminous_shift);
}
int GetLengthExp(const Unit& unit) {
return GetUnitTypeExp(unit.type, length_shift);
}
int GetMassExp(const Unit& unit) {
return GetUnitTypeExp(unit.type, mass_shift);
}
int GetTimeExp(const Unit& unit) {
return GetUnitTypeExp(unit.type, time_shift);
}
int GetTemperatureExp(const Unit& unit) {
return GetUnitTypeExp(unit.type, temperature_shift);
}
int GetCurrentExp(const Unit& unit) {
return GetUnitTypeExp(unit.type, current_shift);
}
int GetLuminousExp(const Unit& unit) {
return GetUnitTypeExp(unit.type, luminous_shift);
}
bool ConvertUnits(const Unit& unit_in, double val_in, const Unit& unit_out, double* val_out) {
// If the units don't have the same measurements it's impossible to do a conversion.
if ((unit_in.type & ~system_mask) != (unit_out.type & ~system_mask)) {
return false;
}
double val = val_in * pow(10.0, unit_in.exp - unit_out.exp);
if (unit_in.type == unit_out.type) {
*val_out = val;
return true;
}
if (!ConvertDistance(val, unit_in, val, unit_out)) {
return false;
}
if (!ConvertMass(val, unit_in, val, unit_out)) {
return false;
}
if (!ConvertTemperature(val, unit_in, val, unit_out)) {
return false;
}
*val_out = val;
return true;
}
} // namespace unit
} // namespace hid