00001
00076 #include <stdlib.h>
00077 #include <string.h>
00078 #include <stdio.h>
00079 #include <errno.h>
00080 #include <ctype.h>
00081 #include <math.h>
00082
00083 #include "platform.h"
00084 #include "output.h"
00085 #include "unit.h"
00086 #include "exception.h"
00087 #include "globals.h"
00088 #include "find.h"
00089
00090
00091 static double c = 2.997925e8;
00092 static double e = 1.602189246e-19;
00093 static double h = 6.62617636e-34;
00094 static double k = 1.38066244e-23;
00095 static double m = 0.910953447e-30;
00096 static double s = 1.233270e4;
00097 static int precision = 10;
00098 static UNIT *unit_list = NULL;
00099 static char filepath[1024] = "unitfile.txt";
00100 static int linenum=0;
00101
00102
00103 UNIT *unit_find_underived(char *unit)
00104 {
00105 UNIT *p;
00106
00107
00108 for (p=unit_list; p!=NULL; p=p->next)
00109 {
00110 if (strcmp(p->name,unit)==0)
00111 return p;
00112 }
00113 return NULL;
00114 }
00115
00116
00117 int unit_constant(char name, double value)
00118 {
00119 char buffer[2] = {name,'\0'};
00120 if (unit_find_underived(buffer)!=NULL)
00121 throw_exception("%s(%d): constant definition of '%s' failed; unit already defined", filepath, linenum, buffer);
00122 switch (name) {
00123 case 'c':
00124 c=value;
00125 break;
00126 case 'e':
00127 e=value;
00128 break;
00129 case 'h':
00130 h=value;
00131 break;
00132 case 'k':
00133 k=value;
00134 break;
00135 case 'm':
00136 m=value;
00137 break;
00138 case 's':
00139 s=value;
00140 break;
00141 default:
00142 throw_exception("%s(%d): constant '%c' is not valid", filepath,linenum,name);
00143 return 0;
00144 }
00145 return 1;
00146 }
00147
00148
00149 int unit_primary(char *name,double c,double e,double h,double k,double m,double s,double a,double b,int prec)
00150 {
00151 UNIT *p = (UNIT*)malloc(sizeof(UNIT));
00152 if (unit_find_underived(name)!=NULL)
00153 throw_exception("%s(%d): primary definition of '%s' failed; unit already defined", filepath, linenum, name);
00154 if (p==NULL)
00155 {
00156 throw_exception("%s(%d): memory allocation failed",filepath,linenum);
00157 return 0;
00158 }
00159 strncpy(p->name,name,sizeof(p->name)-1);
00160 p->c = c;
00161 p->e = e;
00162 p->h = h;
00163 p->k = k;
00164 p->m = m;
00165 p->s = s;
00166 p->a = a;
00167 p->b = b;
00168 p->prec = prec;
00169 p->next = unit_list;
00170 unit_list = p;
00171 return 1;
00172 }
00173
00174
00175 int unit_precision(char *term)
00176 {
00177 char* p;
00178 char mant[256];
00179
00180
00181 if ((p=strchr(term,'e'))!=NULL || (p=strchr(term,'E'))!=NULL) {
00182
00183
00184 strncpy(mant, term, p-term);
00185 }
00186 else {
00187 strcpy(mant, term);
00188 }
00189
00190 return (int)(strlen(mant) - (strchr(mant,'.')!=NULL ? 1 : 0));
00191 }
00192
00193
00194 int unit_derived(char *name,char *derivation)
00195 {
00196 double c=0,e=0,h=0,k=0,m=0,s=0,a=0,b=0;
00197 int prec=0;
00198 double scalar=1.0;
00199 char lastOp = '\0', nextOp = '\0';
00200 UNIT *lastUnit = NULL;
00201 char *p=derivation;
00202 if (unit_find_underived(name)!=NULL)
00203 throw_exception("%s(%d): derived definition of '%s' failed; unit already defined", filepath, linenum, name);
00204
00205
00206 if (sscanf(p,"%lf",&scalar)==1)
00207 {
00208
00209 if ((p=strchr(p,' '))!=NULL)
00210 p++;
00211
00212
00213 else
00214 p=derivation;
00215 }
00216
00217
00218 while (*p!='\0')
00219 {
00220 char term[32];
00221 UNIT *pUnit;
00222
00223
00224 if (sscanf(p,"%[^-*/^+]",term)!=1)
00225 throw_exception("%s(%d): unable to read unit '%s'", filepath,linenum,p);
00226
00227
00228 if (nextOp!='^')
00229 {
00230
00231 pUnit = unit_find_underived(term);
00232
00233 if (pUnit==NULL)
00234 {
00235 double val;
00236 if (sscanf(term,"%lf",&val)==1)
00237 {
00238 static UNIT local;
00239 if (nextOp=='+' || nextOp=='-')
00240 {
00241 UNIT bias = {"",0,0,0,0,0,0,0,val,unit_precision(term)};
00242 local = bias;
00243 }
00244 else
00245 {
00246 UNIT offset = {"",0,0,0,0,0,0,val,0,unit_precision(term)};
00247 local = offset;
00248 }
00249 pUnit = &local;
00250 }
00251 else
00252 throw_exception("%s(%d): unable to find or derive unit '%s'", filepath,linenum,p);
00253 }
00254 }
00255
00256
00257 p += strlen(term);
00258
00259
00260 switch (nextOp) {
00261 case '\0':
00262
00263 if (a==0)
00264 {
00265 c = pUnit->c;
00266 e = pUnit->e;
00267 h = pUnit->h;
00268 k = pUnit->k;
00269 m = pUnit->m;
00270 s = pUnit->s;
00271 a = pUnit->a * scalar;
00272 b = pUnit->b;
00273 prec = pUnit->prec;
00274 nextOp = '*';
00275 }
00276 break;
00277 case '*':
00278 c += pUnit->c;
00279 e += pUnit->e;
00280 h += pUnit->h;
00281 k += pUnit->k;
00282 m += pUnit->m;
00283 s += pUnit->s;
00284 a *= pUnit->a;
00285 prec = min(prec,pUnit->prec);
00286 break;
00287 case '/':
00288 c -= pUnit->c;
00289 e -= pUnit->e;
00290 h -= pUnit->h;
00291 k -= pUnit->k;
00292 m -= pUnit->m;
00293 s -= pUnit->s;
00294 a /= pUnit->a;
00295 prec = min(prec,pUnit->prec);
00296 break;
00297 case '+':
00298 b += pUnit->b;
00299 break;
00300 case '-':
00301 b -= pUnit->b;
00302 break;
00303 case '^':
00304 if (lastUnit!=NULL)
00305 {
00306 int repeat = 0;
00307 if (sscanf(term,"%d",&repeat)!=1 || repeat<2)
00308 throw_exception("%s(%d): syntax error at '%s'", filepath,linenum,term);
00309 repeat--;
00310 switch(lastOp) {
00311 case '*':
00312 c += lastUnit->c*repeat;
00313 e += lastUnit->e*repeat;
00314 h += lastUnit->h*repeat;
00315 k += lastUnit->k*repeat;
00316 m += lastUnit->m*repeat;
00317 s += lastUnit->s*repeat;
00318 a *= pow(lastUnit->a,repeat);
00319 prec = min(prec,lastUnit->prec);
00320 break;
00321 case '/':
00322 c -= lastUnit->c*repeat;
00323 e -= lastUnit->e*repeat;
00324 h -= lastUnit->h*repeat;
00325 k -= lastUnit->k*repeat;
00326 m -= lastUnit->m*repeat;
00327 s -= lastUnit->s*repeat;
00328 a /= pow(lastUnit->a,repeat);
00329 prec = min(prec,lastUnit->prec);
00330 break;
00331 default:
00332 throw_exception("%s(%d): ^ not allowed after '%c' at '%s'", filepath,linenum,lastOp,term);
00333 return 0;
00334 }
00335 }
00336 break;
00337 default:
00338 throw_exception("%s(%d): '%c' is not recognized at '%s'", filepath,linenum,lastOp,term);
00339 return 0;
00340 break;
00341 }
00342 lastOp = nextOp;
00343 if ((nextOp=*p)!='\0')
00344 p++;
00345 lastUnit = pUnit;
00346 }
00347
00348 return unit_primary(name,c,e,h,k,m,s,a,b,prec);
00349 }
00350
00353 void unit_init(void)
00354 {
00355 static int tried=0;
00356 char *glpath = getenv("GLPATH");
00357 FILE *fp = NULL;
00358 char *tpath = find_file(filepath, NULL, 4);
00359
00360 if (tried)
00361 return;
00362 else
00363 tried = 1;
00364
00365 if(tpath != NULL)
00366 fp = fopen(tpath, "r");
00367
00368
00369 if (fp==NULL && glpath!=NULL)
00370 {
00371 char envbuf[1024];
00372 char *dir;
00373 strcpy(envbuf,glpath);
00374 dir = strtok(envbuf,";");
00375 while (dir!=NULL)
00376 {
00377 strcpy(filepath,dir);
00378 strcat(filepath,"/unitfile.txt");
00379 fp = fopen(filepath,"r");
00380 if (fp!=NULL)
00381 break;
00382 dir = strtok(NULL,";");
00383 }
00384 }
00385
00386
00387 if (fp==NULL)
00388 {
00389 output_error("%s: %s (GLPATH=%s)", filepath, strerror(errno),glpath);
00390 return;
00391 }
00392
00393
00394 while (!feof(fp) && !ferror(fp))
00395 {
00396 char buffer[1024], *p;
00397 if (fgets(buffer,sizeof(buffer),fp)==NULL)
00398 break;
00399 linenum++;
00400
00401
00402 p = strchr(buffer,';');
00403 if (p!=NULL)
00404 *p = '\0';
00405
00406
00407 p = buffer+strlen(buffer)-1;
00408 while (iswspace(*p) && p>buffer)
00409 *p--='\0';
00410
00411
00412 if (buffer[0]=='\0' || iswspace(buffer[0]))
00413 continue;
00414
00415
00416 if (buffer[0]=='#') {
00417 double value=0.0;
00418 char name;
00419 if (sscanf(buffer+1,"%c=%lf",&name,&value)!=2)
00420 output_error("%s(%d): constant definition '%s' is not valid", filepath,linenum,buffer);
00421 else
00422 unit_constant(name,value);
00423 continue;
00424 }
00425
00426
00427 if (buffer[0]=='*')
00428 {
00430 output_warning("%s(%d): scalar '%s' ignored (not supported yet)", filepath,linenum,buffer+1);
00431 continue;
00432 }
00433
00434
00435 {
00436 char name[256];
00437 double c, e, h, k, m, s, a, b;
00438 int prec;
00439 if (sscanf(buffer,"%[^=]=%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d",name,&c,&e,&h,&k,&m,&s,&a,&b,&prec)==10)
00440 {
00441 unit_primary(name,c,e,h,k,m,s,a,b,prec);
00442 continue;
00443 }
00444 }
00445
00446
00447 {
00448 char name[256];
00449 char derivation[256];
00450 if (sscanf(buffer,"%[^=]=%[-+/*^0-9. %A-Za-z]",name,derivation)==2)
00451 {
00452 unit_derived(name,derivation);
00453 continue;
00454 }
00455 }
00456
00457
00458 throw_exception("%s(%d): unit '%s' is not recognized", filepath,linenum,buffer);
00459 }
00460
00461
00462 if (ferror(fp))
00463 throw_exception("unitfile: %s", strerror(errno));
00464 else
00465 output_verbose("%s loaded ok", filepath);
00466
00467
00468 fclose(fp);
00469 }
00470
00474 int unit_convert(char *from, char *to, double *pValue)
00475 {
00476 UNIT *pFrom = unit_find(from);
00477 UNIT *pTo = unit_find(to);
00478 if (pFrom!=NULL && pTo!=NULL)
00479 return unit_convert_ex(pFrom,pTo,pValue);
00480 return 0;
00481 }
00482
00486 int unit_convert_ex(UNIT *pFrom, UNIT *pTo, double *pValue)
00487 {
00488 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)
00489 {
00490 *pValue = (*pValue-pFrom->b)*pFrom->a/pTo->a + pTo->b;
00491 return 1;
00492 }
00493 else
00494 return 0;
00495 }
00496
00500 UNIT *unit_find(char *unit)
00501 {
00502 UNIT *p;
00503
00504
00505 if (unit_list==NULL) unit_init();
00506
00507
00508 for (p=unit_list; p!=NULL; p=p->next)
00509 {
00510 if (strcmp(p->name,unit)==0)
00511 return p;
00512 }
00513
00514
00515 if (unit_derived(unit,unit))
00516 return unit_list;
00517 else
00518 return NULL;
00519 }
00520
00524 int unit_test(void)
00525 {
00526 typedef struct {double value; char *unit;} VALUE;
00527 typedef struct {VALUE from; VALUE to; double precision;} TEST;
00528 #ifndef PI
00529 #define PI 3.141592635
00530 #endif
00531 TEST test[] = {
00532 {1, "ratio", 1, "unit"},
00533 {1, "%", 0.01, "unit"},
00534 {1, "pu", 1, "1/unit"},
00535 {1, "/%", 1, "1/%"},
00536
00537 {PI, "unit", 3.141592635, "unit"},
00538 {2*PI, "rad", 1, "unit"},
00539 {360, "deg", 1, "unit"},
00540 {400, "grad", 1, "unit"},
00541 {4, "quad", 1, "unit"},
00542 {4*PI, "sr", 1, "unit"},
00543
00544 {1, "R", 5.0/9.0, "K", 0.001},
00545 {0, "degC", 273.14, "K", 0.001},
00546 {0, "degF", 459.65, "R", 0.001},
00547 {1, "kg", 1000, "g"},
00548 {1, "N", 1, "m*kg/s^2"},
00549 {1, "J", 1, "N*m"},
00550
00551 {1, "min", 60,"s"},
00552 {1, "h", 60,"min"},
00553 {1, "day", 24,"h"},
00554 {1, "yr", 365,"day"},
00555 {1, "syr", 365.24,"day"},
00556
00557 {1, "cm", 0.01, "m"},
00558 {1, "mm", 0.1, "cm"},
00559 {1, "km", 1000,"m"},
00560 {1, "in", 2.54, "cm"},
00561 {1, "ft", 12, "in"},
00562 {1, "yd", 3, "ft"},
00563 {1, "mile", 5280, "ft"},
00564
00565 {1, "sf", 1,"ft^2"},
00566 {1, "sy", 1,"yd^2"},
00567
00568 {1, "cf", 1,"ft^3"},
00569 {1, "cy", 1,"yd^3"},
00570 {1,"l", 0.001,"m^3"},
00571 {1,"gal", 3.785412, "l"},
00572
00573 {2.2046, "lb", 1, "kg"},
00574 {1, "tonne", 1000, "kg"},
00575
00576 {1, "mph", 1, "mile/h"},
00577 {1, "fps", 1, "ft/s"},
00578 {1, "fpm", 1, "ft/min"},
00579 {1, "gps", 1, "gal/s"},
00580 {1, "gpm", 1, "gal/min"},
00581 {1, "gph", 1, "gal/h"},
00582 {1, "cfm", 1, "ft^3/min"},
00583 {1, "ach", 1, "1/h"},
00584
00585 {1, "Hz", 1, "1/s"},
00586
00587 {1, "W", 1, "J/s"},
00588 {1, "kW", 1000, "W"},
00589 {1, "MW", 1000, "kW"},
00590 {1, "Wh", 1, "W*h"},
00591 {1, "kWh", 1000, "Wh"},
00592 {1, "MWh", 1000, "kWh"},
00593 {1, "Btu", 0.293, "Wh"},
00594 {1, "kBtu", 1000, "Btu"},
00595 {1, "MBtu", 1000, "kBtu"},
00596 {1, "ton", 12000, "Btu/h"},
00597 {1, "tons", 1, "ton*s"},
00598 {1, "tonh", 1, "ton*h"},
00599 {1, "hp", 746, "W"},
00600 {1, "W", 1, "V*A"},
00601 {1, "C", 1, "A*s"},
00602 {1, "F", 1, "C/V"},
00603 {1, "ohm", 1,"V/A"},
00604 {1, "H", 1,"ohm*s"},
00605 };
00606 int n, failed=0, succeeded=0;
00607 output_verbose("performing units tests");
00608 output_test("BEGIN: units tests");
00609 for (n=0; n<sizeof(test)/sizeof(test[0]); n++)
00610 {
00611 double v = test[n].from.value;
00612 if (test[n].precision==0)
00613 test[n].precision = 1e-4;
00614
00615
00616 if (!unit_convert(test[n].from.unit,test[n].to.unit,&v))
00617 {
00618 output_test("FAILED: conversion from %s to %s not possible", test[n].from.unit,test[n].to.unit);
00619 failed++;
00620 }
00621 else if (fabs(v-test[n].to.value)>test[n].precision)
00622 {
00623 output_test("FAILED: incorrect unit conversion %g %s -> %g %s (got %g %s instead)",
00624 test[n].from.value,test[n].from.unit,
00625 test[n].to.value,test[n].to.unit,
00626 v,test[n].to.unit);
00627 failed++;
00628 }
00629
00630
00631 else if (!unit_convert(test[n].to.unit,test[n].from.unit,&v))
00632 {
00633 output_test("FAILED: conversion from %s to %s not possible", test[n].to.unit,test[n].from.unit);
00634 failed++;
00635 }
00636 else if (fabs(v-test[n].from.value)>test[n].precision)
00637 {
00638 output_test("FAILED: incorrect unit conversion %g %s -> %g %s (got %g %s instead)",
00639 test[n].to.value,test[n].to.unit,
00640 test[n].from.value,test[n].from.unit,
00641 v,test[n].from.unit);
00642 failed++;
00643 }
00644
00645 else
00646 {
00647 output_test("SUCCESS: %g %s = %g %s",
00648 test[n].from.value,test[n].from.unit,
00649 test[n].to.value,test[n].to.unit);
00650 succeeded++;
00651 }
00652 }
00653 output_test("END: %d units tested", n);
00654 output_debug("units tested: %d ok, %d failed (see '%s' for details).", succeeded, failed, global_testoutputfile);
00655 return failed;
00656 }
00657