00001
00077 #include <stdlib.h>
00078 #include <string.h>
00079 #include <stdio.h>
00080 #include <errno.h>
00081 #include <ctype.h>
00082 #include <math.h>
00083
00084 #include "platform.h"
00085 #include "output.h"
00086 #include "unit.h"
00087 #include "exception.h"
00088 #include "globals.h"
00089 #include "find.h"
00090
00091
00092 static double c = 2.997925e8;
00093 static double e = 1.602189246e-19;
00094 static double h = 6.62617636e-34;
00095 static double k = 1.38066244e-23;
00096 static double m = 0.910953447e-30;
00097 static double s = 1.233270e4;
00098 static int precision = 10;
00099 static UNIT *unit_list = NULL;
00100 static char filepath[1024] = "unitfile.txt";
00101 static int linenum = 0;
00102
00103 typedef struct s_unitscalar UNITSCALAR;
00104 struct s_unitscalar {
00105 char name[3];
00106 unsigned char len;
00107 int scalar;
00108 UNITSCALAR *next;
00109 };
00110 UNITSCALAR *scalar_list = NULL;
00111
00112
00113
00114
00115 UNIT *unit_find_raw(char *unit){
00116 UNIT *p;
00117
00118 for (p = unit_list; p != NULL; p = p->next){
00119 if (strcmp(p->name,unit) == 0){
00120 return p;
00121 }
00122 }
00123 return NULL;
00124 }
00125 UNIT *unit_primary(char *name,double c,double e,double h,double k,double m,double s,double a,double b,int prec);
00126
00127
00128 UNIT *unit_find_underived(char *unit)
00129 {
00130 UNIT *p;
00131 UNITSCALAR *s;
00132
00133
00134 p = unit_find_raw(unit);
00135 if(p != NULL){
00136
00137 return p;
00138 }
00139
00140
00141 for(s = scalar_list; s != NULL; s = s->next){
00142 if(strncmp(unit, s->name, s->len) == 0){
00143 break;
00144 }
00145 }
00146 if(s == NULL){
00147 return NULL;
00148 }
00149
00150
00151 p = unit_find_raw(unit+s->len);
00152 if(p == NULL){
00153 output_error("compound unit '%s' could not be correctly identified", unit);
00154
00155
00156
00157
00158 return NULL;
00159 }
00160
00161
00162 return unit_primary(unit, p->c, p->e, p->h, p->k, p->m, p->s, p->a * pow(10, s->scalar), p->b, p->prec);
00163 }
00164
00165
00166 int unit_scalar(char *name,int scalar)
00167 {
00168 UNITSCALAR *ptr = (UNITSCALAR *)malloc(sizeof(UNITSCALAR));
00169 if (ptr == NULL){
00170 throw_exception("%s(%d): scalar definitinon of '%s' failed: %s", filepath, linenum, name, strerror(errno));
00171
00172
00173
00174 }
00175 strncpy(ptr->name, name, sizeof(ptr->name));
00176 ptr->scalar = scalar;
00177 ptr->len = (unsigned char)strlen(ptr->name);
00178 ptr->next = scalar_list;
00179 scalar_list = ptr;
00180 return 1;
00181 }
00182
00183
00184
00185 int unit_constant(char name, double value)
00186 {
00187 char buffer[2] = {name,'\0'};
00188 if(unit_find_underived(buffer) != NULL){
00189 throw_exception("%s(%d): constant definition of '%s' failed; unit already defined", filepath, linenum, buffer);
00190
00191
00192
00193
00194 }
00195 switch (name) {
00196 case 'c':
00197 c=value;
00198 break;
00199 case 'e':
00200 e=value;
00201 break;
00202 case 'h':
00203 h=value;
00204 break;
00205 case 'k':
00206 k=value;
00207 break;
00208 case 'm':
00209 m=value;
00210 break;
00211 case 's':
00212 s=value;
00213 break;
00214 default:
00215 throw_exception("%s(%d): constant '%c' is not valid", filepath,linenum,name);
00216
00217
00218
00219 return 0;
00220 }
00221 return 1;
00222 }
00223
00224
00225 UNIT *unit_primary(char *name, double c, double e, double h, double k, double m, double s, double a, double b, int prec){
00226 UNIT *p = unit_find_raw(name);
00227 #if 0
00228 ){
00229 throw_exception("raw unit '%s' was not found", name);
00230
00231
00232
00233
00234 }
00235 if
00236 #endif
00237 if(p != NULL && (c != p->c || e != p->e || h != p->h || k != p->k || m != p->m || s != p->s || a != p->a || b != p->b || prec != p->prec)){
00238 throw_exception("%s(%d): primary definition of '%s' failed; unit already defined with a different parameter set", filepath, linenum, name);
00239
00240
00241
00242
00243
00244 }
00245 p = (UNIT *)malloc(sizeof(UNIT));
00246 if(p == NULL)
00247 {
00248 throw_exception("%s(%d): memory allocation failed",filepath,linenum);
00249
00250
00251
00252
00253 return 0;
00254 }
00255 strncpy(p->name, name, sizeof(p->name) - 1);
00256 p->c = c;
00257 p->e = e;
00258 p->h = h;
00259 p->k = k;
00260 p->m = m;
00261 p->s = s;
00262 p->a = a;
00263 p->b = b;
00264 p->prec = prec;
00265 p->next = unit_list;
00266 unit_list = p;
00267 return p;
00268 }
00269
00270
00271 int unit_precision(char *term)
00272 {
00273 char* p;
00274 char mant[256];
00275
00276
00277 if ((p=strchr(term,'e')) != NULL || (p=strchr(term,'E')) != NULL) {
00278
00279
00280 strncpy(mant, term, p-term);
00281 } else {
00282 strcpy(mant, term);
00283 }
00284
00285 return (int)(strlen(mant) - (strchr(mant,'.') != NULL ? 1 : 0));
00286 }
00287
00288
00289 int unit_derived(char *name,char *derivation)
00290 {
00291 double c = 0, e = 0, h = 0, k = 0, m = 0, s = 0, a = 0, b = 0;
00292 int prec = 0;
00293 double scalar = 1.0;
00294 char lastOp = '\0', nextOp = '\0';
00295 UNIT *lastUnit = NULL;
00296 char *p = derivation;
00297
00298 if (unit_find_raw(name) != NULL){
00299 throw_exception("%s(%d): derived definition of '%s' failed; unit already defined", filepath, linenum, name);
00300
00301
00302
00303 }
00304
00305
00306 if (sscanf(p, "%lf", &scalar) == 1){
00307
00308 if ((p = strchr(p,' ')) != NULL){
00309 p++;
00310 }
00311
00312 else {
00313 p = derivation;
00314 }
00315 }
00316
00317
00318 while (*p != '\0')
00319 {
00320 char term[32];
00321 UNIT *pUnit;
00322
00323
00324 if (sscanf(p,"%[^-*/^+]",term)!=1){
00325 throw_exception("%s(%d): unable to read unit '%s'", filepath,linenum,p);
00326
00327
00328
00329
00330 }
00331
00332
00333 if (nextOp != '^'){
00334
00335 pUnit = unit_find_underived(term);
00336
00337 if (pUnit == NULL){
00338 double val;
00339 if (sscanf(term,"%lf",&val) == 1){
00340 static UNIT local;
00341 if (nextOp =='+' || nextOp == '-'){
00342 UNIT bias = {"", 0, 0, 0, 0, 0, 0, 0, val, unit_precision(term)};
00343 local = bias;
00344 } else {
00345 UNIT offset = {"", 0, 0, 0, 0, 0, 0, val, 0, unit_precision(term)};
00346 local = offset;
00347 }
00348 pUnit = &local;
00349 } else {
00350 throw_exception("%s(%d): unable to find or derive unit '%s'", filepath, linenum, p);
00351
00352
00353
00354
00355
00356 }
00357 }
00358 }
00359
00360
00361 p += strlen(term);
00362
00363
00364 switch(nextOp){
00365 case '\0':
00366
00367 if (a == 0)
00368 {
00369 c = pUnit->c;
00370 e = pUnit->e;
00371 h = pUnit->h;
00372 k = pUnit->k;
00373 m = pUnit->m;
00374 s = pUnit->s;
00375 a = pUnit->a * scalar;
00376 b = pUnit->b;
00377 prec = pUnit->prec;
00378 nextOp = '*';
00379 }
00380 break;
00381 case '*':
00382 c += pUnit->c;
00383 e += pUnit->e;
00384 h += pUnit->h;
00385 k += pUnit->k;
00386 m += pUnit->m;
00387 s += pUnit->s;
00388 a *= pUnit->a;
00389 prec = min(prec,pUnit->prec);
00390 break;
00391 case '/':
00392 c -= pUnit->c;
00393 e -= pUnit->e;
00394 h -= pUnit->h;
00395 k -= pUnit->k;
00396 m -= pUnit->m;
00397 s -= pUnit->s;
00398 a /= pUnit->a;
00399 prec = min(prec, pUnit->prec);
00400 break;
00401 case '+':
00402 b += pUnit->b;
00403 break;
00404 case '-':
00405 b -= pUnit->b;
00406 break;
00407 case '^':
00408 if (lastUnit!=NULL){
00409 int repeat = 0;
00410 if (sscanf(term, "%d", &repeat) != 1 || repeat < 2){
00411 throw_exception("%s(%d): syntax error at '%s', exponent must be greater than 1", filepath,linenum,term);
00412
00413
00414
00415
00416 }
00417 repeat--;
00418 switch(lastOp) {
00419 case '*':
00420 c += lastUnit->c * repeat;
00421 e += lastUnit->e * repeat;
00422 h += lastUnit->h * repeat;
00423 k += lastUnit->k * repeat;
00424 m += lastUnit->m * repeat;
00425 s += lastUnit->s * repeat;
00426 a *= pow(lastUnit->a, repeat);
00427 prec = min(prec, lastUnit->prec);
00428 break;
00429 case '/':
00430 c -= lastUnit->c * repeat;
00431 e -= lastUnit->e * repeat;
00432 h -= lastUnit->h * repeat;
00433 k -= lastUnit->k * repeat;
00434 m -= lastUnit->m * repeat;
00435 s -= lastUnit->s * repeat;
00436 a /= pow(lastUnit->a, repeat);
00437 prec = min(prec, lastUnit->prec);
00438 break;
00439 default:
00440 throw_exception("%s(%d): ^ not allowed after '%c' at '%s'", filepath, linenum, lastOp, term);
00441
00442
00443
00444 return 0;
00445 }
00446 }
00447 break;
00448 default:
00449 throw_exception("%s(%d): '%c' is not recognized at '%s'", filepath,linenum,lastOp,term);
00450
00451
00452
00453 return 0;
00454 break;
00455
00456 }
00457 lastOp = nextOp;
00458 if((nextOp = *p) != '\0'){
00459 p++;
00460 }
00461 lastUnit = pUnit;
00462 }
00463
00464 return (unit_primary(name, c, e, h, k, m, s, a, b, prec) != NULL);
00465 }
00466
00469 void unit_init(void)
00470 {
00471 static int tried=0;
00472 char *glpath = getenv("GLPATH");
00473 FILE *fp = NULL;
00474 char *tpath = find_file(filepath, NULL, 4);
00475
00476 if (tried)
00477 return;
00478 else
00479 tried = 1;
00480
00481 if(tpath != NULL)
00482 fp = fopen(tpath, "r");
00483
00484
00485 if (fp == NULL && glpath != NULL){
00486 char envbuf[1024];
00487 char *dir;
00488 strcpy(envbuf, glpath);
00489 dir = strtok(envbuf, ";");
00490 while(dir != NULL){
00491 strcpy(filepath, dir);
00492 strcat(filepath, "/unitfile.txt");
00493 fp = fopen(filepath, "r");
00494 if (fp != NULL){
00495 break;
00496 }
00497 dir = strtok(NULL, ";");
00498 }
00499 }
00500
00501
00502 if (fp == NULL)
00503 {
00504 output_error("%s: %s (GLPATH=%s)", filepath, strerror(errno), glpath);
00505
00506
00507
00508
00509 return;
00510 }
00511
00512
00513 while (!feof(fp) && !ferror(fp))
00514 {
00515 char buffer[1024], *p;
00516 if (fgets(buffer, sizeof(buffer), fp) == NULL){
00517 break;
00518 }
00519 linenum++;
00520
00521
00522 p = strchr(buffer,';');
00523 if (p != NULL){
00524 *p = '\0';
00525 }
00526
00527
00528 p = buffer + strlen(buffer) - 1;
00529 while (iswspace(*p) && p>buffer){
00530 *p-- = '\0';
00531 }
00532
00533
00534 if (buffer[0] == '\0' || iswspace(buffer[0])){
00535 continue;
00536 }
00537
00538
00539 if (buffer[0] == '#') {
00540 double value = 0.0;
00541 char name;
00542 if (sscanf(buffer + 1,"%c=%lf", &name, &value) != 2){
00543 output_error("%s(%d): constant definition '%s' is not valid", filepath, linenum, buffer);
00544 } else {
00545 unit_constant(name, value);
00546 }
00547 continue;
00548 }
00549
00550
00551 if (buffer[0] == '*'){
00552 char name[8];
00553 int scalar;
00554 if (sscanf(buffer+1, "%7[^=]=%d", name, &scalar) == 2){
00555 unit_scalar(name, scalar);
00556 } else {
00557 output_error("%s(%d): scalar '%s' is not valid", filepath, linenum, buffer);
00558 }
00559 continue;
00560 }
00561
00562
00563 {
00564 char name[256];
00565 double c, e, h, k, m, s, a, b;
00566 int prec;
00567 if (sscanf(buffer, "%[^=]=%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d",name, &c, &e, &h, &k, &m, &s, &a, &b, &prec) == 10){
00568 unit_primary(name, c, e, h, k, m, s, a, b, prec);
00569 continue;
00570 }
00571 }
00572
00573
00574 {
00575 char name[256];
00576 char derivation[256];
00577 if (sscanf(buffer, "%[^=]=%[-+/*^0-9. %A-Za-z]", name, derivation) == 2){
00578 unit_derived(name, derivation);
00579 continue;
00580 }
00581 }
00582
00583
00584 throw_exception("%s(%d): unit '%s' is not recognized", filepath, linenum, buffer);
00585
00586
00587
00588 }
00589
00590
00591 if (ferror(fp)){
00592 throw_exception("unitfile: %s", strerror(errno));
00593
00594
00595
00596 } else {
00597 output_verbose("%s loaded ok", filepath);
00598 }
00599
00600
00601 fclose(fp);
00602 }
00603
00607 int unit_convert(char *from, char *to, double *pValue){
00608 UNIT *pFrom = unit_find(from);
00609 UNIT *pTo = unit_find(to);
00610 if (pFrom != NULL){
00611 if(pTo != NULL){
00612 return unit_convert_ex(pFrom, pTo, pValue);
00613 } else {
00614 output_error("could not find 'to' unit %s for unit_convert", to);
00615
00616
00617
00618 }
00619 } else {
00620 output_error("could not find 'from' unit %s for unit_convert", from);
00621
00622
00623
00624 }
00625 return 0;
00626 }
00627
00631 int unit_convert_ex(UNIT *pFrom, UNIT *pTo, double *pValue)
00632 {
00633 if(pFrom == NULL || pTo == NULL || pValue == NULL){
00634 output_error("could not run unit_convert_ex due to null arguement");
00635
00636
00637
00638
00639
00640 return 0;
00641 }
00642 if (pTo->c == pFrom->c && pTo->e == pFrom->e && pTo->h == pFrom->h && pTo->k == pFrom->k && pTo->m == pFrom->m && pTo->s == pFrom->s)
00643 {
00644 *pValue = (*pValue - pFrom->b) * (pFrom->a / pTo->a) + pTo->b;
00645 return 1;
00646 } else {
00647 output_error("could not convert units from %s to %s, mismatched constant values", pFrom->name, pTo->name);
00648 return 0;
00649 }
00650 }
00651
00655 int unit_convert_complex(UNIT *pFrom, UNIT *pTo, complex *pValue)
00656 {
00657 if(pFrom == NULL || pTo == NULL || pValue == NULL){
00658 output_error("could not run unit_convert_complex due to null arguement");
00659
00660
00661
00662
00663
00664 return 0;
00665 }
00666
00667 if (pTo->c == pFrom->c && pTo->e == pFrom->e && pTo->h == pFrom->h && pTo->k == pFrom->k && pTo->m == pFrom->m && pTo->s == pFrom->s){
00668 pValue->r = (pValue->r - pFrom->b) * (pFrom->a / pTo->a) + pTo->b;
00669 pValue->i = (pValue->i - pFrom->b) * (pFrom->a / pTo->a) + pTo->b;
00670 return 1;
00671 } else {
00672 output_error("could not convert units from %s to %s, mismatched constant values", pFrom->name, pTo->name);
00673 return 0;
00674 }
00675 }
00676
00680 UNIT *unit_find(char *unit)
00681 {
00682 UNIT *p;
00683
00684 TRY {
00685
00686 if (unit_list==NULL) unit_init();
00687 } CATCH (char *msg) {
00688 output_error("unit_find(char *unit='%s'): %s", unit,msg);
00689 return NULL;
00690 }
00691 ENDCATCH;
00692
00693
00694 p = unit_find_raw(unit);
00695 if (p != NULL){
00696 return p;
00697 }
00698
00699
00700 if (unit_derived(unit,unit)){
00701 return unit_list;
00702 } else {
00703 output_error("could not find unit \'%s\'", unit);
00704 return NULL;
00705 }
00706 }
00707
00711 int unit_test(void)
00712 {
00713 typedef struct {double value; char *unit;} VALUE;
00714 typedef struct {VALUE from; VALUE to; double precision;} TEST;
00715 #ifndef PI
00716 #define PI 3.141592635
00717 #endif
00718 TEST test[] = {
00719 {1, "ratio", 1, "unit"},
00720 {1, "%", 0.01, "unit"},
00721 {1, "pu", 1, "1/unit"},
00722 {1, "/%", 1, "1/%"},
00723
00724 {PI, "unit", 3.141592635, "unit"},
00725 {2*PI, "rad", 1, "unit"},
00726 {360, "deg", 1, "unit"},
00727 {400, "grad", 1, "unit"},
00728 {4, "quad", 1, "unit"},
00729 {4*PI, "sr", 1, "unit"},
00730
00731 {1, "R", 5.0/9.0, "K", 0.001},
00732 {0, "degC", 273.14, "K", 0.001},
00733 {0, "degF", 459.65, "R", 0.001},
00734 {1, "kg", 1000, "g"},
00735 {1, "N", 1, "m*kg/s^2"},
00736 {1, "J", 1, "N*m"},
00737
00738 {1, "min", 60,"s"},
00739 {1, "h", 60,"min"},
00740 {1, "day", 24,"h"},
00741 {1, "yr", 365,"day"},
00742 {1, "syr", 365.24,"day"},
00743
00744 {1, "cm", 0.01, "m"},
00745 {1, "mm", 0.1, "cm"},
00746 {1, "km", 1000,"m"},
00747 {1, "in", 2.54, "cm"},
00748 {1, "ft", 12, "in"},
00749 {1, "yd", 3, "ft"},
00750 {1, "mile", 5280, "ft"},
00751
00752 {1, "sf", 1,"ft^2"},
00753 {1, "sy", 1,"yd^2"},
00754
00755 {1, "cf", 1,"ft^3"},
00756 {1, "cy", 1,"yd^3"},
00757 {1, "l", 0.001,"m^3"},
00758 {1, "gal", 3.785412, "l"},
00759
00760 {2.2046, "lb", 1, "kg"},
00761 {1, "tonne", 1000, "kg"},
00762
00763 {1, "mph", 1, "mile/h"},
00764 {1, "fps", 1, "ft/s"},
00765 {1, "fpm", 1, "ft/min"},
00766 {1, "gps", 1, "gal/s"},
00767 {1, "gpm", 1, "gal/min"},
00768 {1, "gph", 1, "gal/h"},
00769 {1, "cfm", 1, "ft^3/min"},
00770 {1, "ach", 1, "1/h"},
00771
00772 {1, "Hz", 1, "1/s"},
00773
00774 {1, "W", 1, "J/s"},
00775 {1, "kW", 1000, "W"},
00776 {1, "MW", 1000, "kW"},
00777 {1, "Wh", 1, "W*h"},
00778 {1, "kWh", 1000, "Wh"},
00779 {1, "MWh", 1000, "kWh"},
00780 {1, "Btu", 0.293, "Wh"},
00781 {1, "kBtu", 1000, "Btu"},
00782 {1, "MBtu", 1000, "kBtu"},
00783 {1, "ton", 12000, "Btu/h"},
00784 {1, "tons", 1, "ton*s"},
00785 {1, "tonh", 1, "ton*h"},
00786 {1, "hp", 746, "W"},
00787 {1, "W", 1, "V*A"},
00788 {1, "C", 1, "A*s"},
00789 {1, "F", 1, "C/V"},
00790 {1, "Ohm", 1,"V/A"},
00791 {1, "H", 1,"Ohm*s"},
00792 };
00793 int n, failed = 0, succeeded = 0;
00794 output_test("\nBEGIN: units tests");
00795 for (n = 0; n < sizeof(test)/sizeof(test[0]); n++){
00796 double v = test[n].from.value;
00797 if (test[n].precision == 0)
00798 test[n].precision = 1e-4;
00799
00800
00801 if (!unit_convert(test[n].from.unit, test[n].to.unit, &v)) {
00802 output_test("FAILED: conversion from %s to %s not possible", test[n].from.unit,test[n].to.unit);
00803 failed++;
00804 } else if (fabs(v-test[n].to.value) > test[n].precision) {
00805 output_test("FAILED: incorrect unit conversion %g %s -> %g %s (got %g %s instead)",
00806 test[n].from.value,test[n].from.unit,
00807 test[n].to.value,test[n].to.unit,
00808 v, test[n].to.unit);
00809 failed++;
00810 } else if (!unit_convert(test[n].to.unit,test[n].from.unit, &v)) {
00811 output_test("FAILED: conversion from %s to %s not possible", test[n].to.unit, test[n].from.unit);
00812 failed++;
00813 } else if (fabs(v - test[n].from.value) > test[n].precision) {
00814 output_test("FAILED: incorrect unit conversion %g %s -> %g %s (got %g %s instead)",
00815 test[n].to.value,test[n].to.unit,
00816 test[n].from.value,test[n].from.unit,
00817 v, test[n].from.unit);
00818 failed++;
00819 } else {
00820 output_test("SUCCESS: %g %s = %g %s",
00821 test[n].from.value, test[n].from.unit,
00822 test[n].to.value, test[n].to.unit);
00823 succeeded++;
00824 }
00825 }
00826 output_test("END: %d units tested", n);
00827 output_verbose("units tested: %d ok, %d failed (see '%s' for details).", succeeded, failed, global_testoutputfile);
00828 return failed;
00829 }
00830