Statistics
| Branch: | Revision:

root / src / common / unitconversion.cc @ master

History | View | Annotate | Download (7.88 KB)

1 01873262 Georg Kunz
//=========================================================================
2
//  UNITCONVERSION.CC - part of
3
//                  OMNeT++/OMNEST
4
//           Discrete System Simulation in C++
5
//
6
//  Author: Andras Varga
7
//
8
//=========================================================================
9
10
/*--------------------------------------------------------------*
11
  Copyright (C) 2006-2008 OpenSim Ltd.
12

13
  This file is distributed WITHOUT ANY WARRANTY. See the file
14
  `license' for details on this and other legal matters.
15
*--------------------------------------------------------------*/
16
17
#include <stdio.h>
18
#include <assert.h>
19
#include <locale.h>
20
#include "opp_ctype.h"
21
#include "stringutil.h"
22
#include "platmisc.h"  //strcasecmp
23
#include "unitconversion.h"
24
25
USING_NAMESPACE
26
27
const double K = 1024.0;
28
29
UnitConversion::UnitDesc UnitConversion::unitTable[] = {    //TODO mile,foot,yard,kmph, mps, mph
30
    { "s",       1, "s",    "second" },
31
    { "d",   86400, "s",    "day" },
32
    { "h",    3600, "s",    "hour" },
33
    { "min",    60, "s",    "minute" }, // "m" is meter
34
    { "ms",   1e-3, "s",    "millisecond" },
35
    { "us",   1e-6, "s",    "microsecond" },
36
    { "ns",   1e-9, "s",    "nanosecond" },
37
    { "ps",  1e-12, "s",    "picosecond" },
38
    { "bps",     1, "bps",  "bit/sec" },
39
    { "Kbps",  1e3, "bps",  "kilobit/sec" },
40
    { "Mbps",  1e6, "bps",  "megabit/sec" },
41
    { "Gbps",  1e9, "bps",  "gigabit/sec" },
42
    { "Tbps", 1e12, "bps",  "terabit/sec" },
43
    { "B",       1, "B",    "byte" },
44
    { "KB",      K, "B",    "kilobyte" },
45
    { "MB",    K*K, "B",    "megabyte" },
46
    { "GB",  K*K*K, "B",    "gigabyte" },
47
    { "TB",K*K*K*K, "B",    "terabyte" },
48
    { "b",       1, "b",    "bit" },
49
    { "m",       1, "m",    "meter" },
50
    { "km",    1e3, "m",    "kilometer" },
51
    { "cm",   1e-2, "m",    "centimeter" },
52
    { "mm",   1e-3, "m",    "millimeter" },
53
    { "W",       1, "W",    "watt" },
54
    { "mW",   1e-3, "W",    "milliwatt" },
55
    { "Hz",      1, "Hz",   "herz" },
56
    { "kHz",   1e3, "Hz",   "kiloherz" },
57
    { "MHz",   1e6, "Hz",   "megaherz" },
58
    { "GHz",   1e9, "Hz",   "gigaherz" },
59
    { "kg",      1, "kg",   "kilogram" },
60
    { "g",    1e-3, "kg",   "gram" },
61
    { "J",       1, "J",    "joule" },
62
    { "kJ",    1e3, "J",    "kilojoule" },
63
    { "MJ",    1e6, "J",    "megajoule" },
64
    { "V",       1, "V",    "volt" },
65
    { "kV",    1e3, "V",    "kilovolt" },
66
    { "mV",   1e-3, "V",    "millivolt" },
67
    { "A",       1, "A",    "amper" },
68
    { "mA",   1e-3, "A",    "milliamper" },
69
    { "uA",   1e-6, "A",    "microamper" },
70
    // this many should be enough
71
    { NULL,      0, NULL,   NULL }
72
};
73
74
UnitConversion::UnitDesc *UnitConversion::lookupUnit(const char *unit)
75
{
76
    // short name ("Hz", "mW")
77
    for (int i=0; unitTable[i].unit; i++)
78
        if (!strcmp(unitTable[i].unit, unit))
79
            return unitTable+i;
80
81
    // long name, case insensitive ("herz", "milliwatt")
82
    for (int i=0; unitTable[i].unit; i++)
83
        if (!strcasecmp(unitTable[i].longName, unit))
84
            return unitTable+i;
85
86
    // long name in plural, case insensitive ("milliwatts")
87
    if (unit[strlen(unit)-1]=='s') {
88
        std::string tmp = std::string(unit, strlen(unit)-1);
89
        for (int i=0; unitTable[i].unit; i++)
90
            if (!strcasecmp(unitTable[i].longName, tmp.c_str()))
91
                return unitTable+i;
92
    }
93
    return NULL;
94
}
95
96
bool UnitConversion::readNumber(const char *&s, double& number)
97
{
98
    while (opp_isspace(*s)) s++;
99
100
    int len;
101
    setlocale(LC_NUMERIC, "C");
102
    if (sscanf(s, "%lf%n", &number, &len) <= 0)
103
        return false; // no number read
104
    s+=len;
105
106
//FIXME use this code instead:
107
//    char *endp;
108
//    setlocale(LC_NUMERIC, "C");
109
//    number = strtod(s, &endp);
110
//    if (s==endp)
111
//        return false; // no number read
112
//    if (errno==ERANGE)
113
//        throw opp_runtime_error("overflow or underflow during conversion of `%s'", s);
114
//    s = endp;
115
116
    while (opp_isspace(*s)) s++;
117
    return true; // OK
118
}
119
120
bool UnitConversion::readUnit(const char *&s, std::string& unit)
121
{
122
    unit = "";
123
    while (opp_isspace(*s)) s++;
124
    while (opp_isalpha(*s))
125
        unit.append(1, *s++);
126
    while (opp_isspace(*s)) s++;
127
    return !unit.empty();
128
}
129
130
double UnitConversion::parseQuantity(const char *str, const char *expectedUnit)
131
{
132
    std::string unit;
133
    double d = parseQuantity(str, unit);
134
    return convertUnit(d, unit.c_str(), expectedUnit);
135
}
136
137
double UnitConversion::parseQuantity(const char *str, std::string& unit)
138
{
139
    double result = 0;
140
    unit = "";
141
    const char *s = str;
142
143
    // read first number and unit
144
    if (!readNumber(s, result))
145
        throw opp_runtime_error("Syntax error parsing quantity '%s': must begin with a number", str);
146
    if (!readUnit(s, unit)) {
147
        // special case: plain number without unit
148
        if (*s)
149
            throw opp_runtime_error("Syntax error parsing quantity '%s': garbage after first number", str);
150
        return result;
151
    }
152
153
    // now process the rest: [<number> <unit>]*
154
    while (*s)
155
    {
156
        // read number and unit
157
        double d;
158
        if (!readNumber(s, d))
159
            break;
160
        std::string tmpUnit;
161
        if (!readUnit(s, tmpUnit))
162
            throw opp_runtime_error("syntax error parsing quantity '%s': missing unit", str);
163
164
        // check unit
165
        double factor = getConversionFactor(unit.c_str(), tmpUnit.c_str());
166
        if (factor == 0)
167
            throw opp_runtime_error("error in quantity '%s': unit %s does not match %s",
168
                    str, getUnitDescription(tmpUnit.c_str()).c_str(), getUnitDescription(unit.c_str()).c_str());
169
170
        // do the conversion
171
        result = result * factor + d;
172
        unit = tmpUnit;
173
    }
174
175
    // must be at the end of the input string
176
    if (*s)
177
        throw opp_runtime_error("syntax error parsing quantity '%s'", str);
178
179
    // success
180
    return result;
181
}
182
183
std::string UnitConversion::formatQuantity(double d, const char *unit)
184
{
185
    return opp_stringf("%g%s", d, opp_nulltoempty(unit));
186
}
187
188
std::string UnitConversion::getUnitDescription(const char *unit)
189
{
190
    UnitDesc *desc = lookupUnit(unit);
191
    std::string result = std::string("'")+unit+"'";
192
    if (desc)
193
        result += std::string(" (") + desc->longName + ")";
194
    return result;
195
}
196
197
double UnitConversion::getConversionFactor(const char *unit, const char *targetUnit)
198
{
199
    // if there are no units or if units are the same, no conversion is needed
200
    if (unit==targetUnit || opp_strcmp(unit, targetUnit)==0)
201
        return 1.0;
202
203
    // if only one unit is given, that's an error
204
    if (opp_isempty(unit) || opp_isempty(targetUnit))
205
        return 0; // cannot convert
206
207
    // we'll need to convert
208
    UnitDesc *unitDesc = lookupUnit(unit);
209
    UnitDesc *targetUnitDesc = lookupUnit(targetUnit);
210
    if (unitDesc==NULL || targetUnitDesc==NULL || strcmp(unitDesc->baseUnit, targetUnitDesc->baseUnit)!=0)
211
        return 0; // cannot convert
212
213
    // the solution
214
    return unitDesc->mult / targetUnitDesc->mult;
215
}
216
217
double UnitConversion::convertUnit(double d, const char *unit, const char *targetUnit)
218
{
219
    if (d == 0 && opp_isempty(unit))
220
        return 0; // accept 0 without unit
221
222
    double factor = getConversionFactor(unit, targetUnit);
223
    if (factor == 0)
224
        throw opp_runtime_error("Cannot convert unit %s to %s",
225
                (opp_isempty(unit) ? "none" : getUnitDescription(unit).c_str()),
226
                (opp_isempty(targetUnit) ? "none" : getUnitDescription(targetUnit).c_str()));
227
    return factor * d;
228
}
229
230
const char *UnitConversion::getLongName(const char *unit)
231
{
232
    UnitDesc *unitDesc = lookupUnit(unit);
233
    return unitDesc ? unitDesc->longName : NULL;
234
}
235
236
const char *UnitConversion::getBaseUnit(const char *unit)
237
{
238
    UnitDesc *unitDesc = lookupUnit(unit);
239
    return unitDesc ? unitDesc->baseUnit: NULL;
240
}
241
242
std::vector<const char *> UnitConversion::getAllUnits()
243
{
244
    std::vector<const char *> result;
245
    for (int i=0; unitTable[i].unit; i++)
246
        result.push_back(unitTable[i].unit);
247
    return result;
248
}