/*-------------------------------------------------------------------\ | Thermal Index Calculator (ti) Version 1.70 | | written by Kevin Ford 2-21-96 | | version 1 written Feb. 1992 | | | | VAX/VMS Compatibility added 06/30/93 by Kim Reniska | | Text graphics added 12/13/95 by Gary Helmstetter (GH) | | | | Six modes of operation: | | Interactive: | | ti read from .uac file (text) | | ti -1 read from .uac file | | ti -3 read from archived file | | Batch | | ti filename station hightemp maxz ; read from .uac (text) | | ti -1 filename station hightemp maxz ; read from .uac file | | ti -3 filename date hightemp maxz ; read from archive file | \-------------------------------------------------------------------*/ /* must use cc ti.c -lm to compile */ #include #include #include #include #ifdef VMS #define O_RDONLY 0 #include #else #include #endif #define t0 (273.15) /* convert from Kelvin to Celcius */ #define RG (29.286) #define GD (-0.00974) /* dry adiabatic lapse rate, deg. C per meter */ #define DALR (-.0029687) /*GH same rate, deg. C per foot */ #define GSCA (40) /*GH width of graph in characters */ #define G0 (-0.0065) /* standard lapse rate */ #define CFM (0.3048) /* convert from feet to meters GH:by multication */ #define spdcvt (1.9458) /* convert from m/s to knots */ /* stored data structure for .uac files */ typedef struct { char ident[12], loc[12], loc2[12]; long idcode; float lat,lon; long ml,sl,wl; /* num. of mand. levels, sig. levels, wind levels */ float mp[20], mh[20], mt[20], mdp[20], mdir[20], mspd[20]; float sp[50], st[50], sdp[50]; float wp[50], wdir[50], wspd[50]; } filerec; filerec rec; /* raw data from the file, for each level */ float p[100], /* pressure in millibars */ t[100], /* temperature in degrees Kelvin */ dp[100]; /* dewpoint in degrees Kelvin */ int ddir[100], /* wind direction, degrees */ dspd[100]; /* wind speed in knots */ /* auxilliary 1000 foot interval wind data from the file */ int winddir[160], /* wind direction, degrees */ windspd[160]; /* wind speed, knots */ /* calculated data for each pressure level */ float h[100], /* height of level in meters MSL */ tv[100]; /* virtual temperature in degrees Celcius */ /* These arrays contain calculated data at 500 foot intervals */ float ti[300], /* thermal index */ ta[300], /*GH temp at altitude (replaces single variable) */ alt[300]; /* altimeter reading */ int dir[300], /* wind direction, degrees */ spd[300]; /* wind speed in knots */ float Gmin,Gmax,Glow,Gsca;/*GH lowest/highest tv - to get base & scale for: */ int Gtmp,Glr; /*GH Glow-based vtemp offsets, in scaled degrees C */ int Ghh,Gdd,Gmm,Gyy; /*GH to parse time&date from input filename */ char *Gptr,*Gptr1; /*GH parse pointers */ char *Gmon[] = /*GH pretty date */ {"JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"}; char Gch; /*GH char for drawing graph */ char Graph[GSCA+2]; /*GH current line of graph */ char Graphend=0; /*GH null to end full graph lines */ float Gx; /*GH scratch float */ int levels; /* total number of pressure levels */ /* calculate the virtual temp given the temp, dewpoint and pressure */ float vtemp(t,dp,p) float t,dp,p; { float e; e=6.11 * exp(5423.0*(1.0/t0 - 1.0/dp)); return(t/(1.0-.378*e/p)); } /* altitude for a given pressure in the standard atmosphere */ float stdz(p) float p; { return((-288.0/G0) * (1.0- pow(p/1013.25,-RG*G0))); } /* convert a string to an integer */ int strtoint(buf) char *buf; { int d; char w; d=0; while ((w= *buf++)>0) d=10*d+w-'0'; return(d); } /* decode a coded temperature/dewpoint from an text data file */ tempcvt(n,t,dp) int n; /* coded form */ float *t, *dp; /* decoded temp & dewpoint */ { int q; float h; q=n/100; h=((float) q)/10.0; if (q%2==0) *t=t0+h; else *t=t0-h; q=n%100; h=(float) q; if (q<56) *dp = *t - h/10.0; else if (q<99) *dp = *t - h+50.0; else *dp = t0 - 99.9; } /* virtual temperature at altitude a */ float tempalt(a) float a; { int i; if (a<=h[0]) return(tv[0]); if (a >= h[levels-1]) return(tv[levels-1]); for (i=1; h[i]1) { if (strcmp(argv[1],"-3")==0) { ti3=1; i++; } if (strcmp(argv[1],"-1")==0) { ti1=1; i++; } } if (argc>i) { if (argc=fname; Gptr--) if (*Gptr=='/') break; Gptr++; sscanf(Gptr,"%4d%2d%2d%2d",&Gyy,&Gmm,&Gdd,&Ghh); /*GH*/ #endif /* def VMS */ /*GH*/ } /* if not ti3 */ /*--------- Get date/station and read data for text data file --------------*/ if (!ti1) { if (!batch) { if (ti3) { printf("Enter date (YYMMDDHH) : "); scanf("%s",date); } else { printf("Enter station : "); scanf("%s",station); } } found=0; while (!found) { if (fscanf(fp,"%s",buf)==EOF) found=2; if ((strncmp(buf,"DATE",4)==0) && (ti3)) { fscanf(fp,"%s",date2); if (strncmp(date2,date,8)==0) found=1; } if ((!ti3) && (strncmp(buf,station,3) == 0)) found=1; } if (found!=1) { if (ti3) printf("ti error: no data for %s.\n",date); else printf("ti error: no data for %s.\n",station); exit(-1); } if (ti3) fscanf(fp,"%s",station); for (i=0; block[i-1][0]!='$'; i++) fscanf(fp,"%s",block[i]); fclose(fp); } /* if !ti1 */ /*--------- Get station id and read upper air data from .uac file -------*/ else { lseek(fd,4284L,0); nid=0; while ((read(fd,&rec,sizeof(filerec))) == sizeof(filerec)) strcpy(block[nid++],rec.ident); if (nid==0) { printf("ti error: file %s contains no sounding data.\n",fname); exit(-1); } if (!batch) { printf("Stations:\n\n"); for (i=0; i='a')&&(station[i]<='z')) station[i] -= ('a'-'A'); for (j=0; j='a')&&(station[i]<='z')) station[i] -= ('a'-'A'); for (j=0; jdspd[j]))) { ftemp=p[i];p[i]=p[j];p[j]=ftemp; ftemp=t[i];t[i]=t[j];t[j]=ftemp; ftemp=dp[i];dp[i]=dp[j];dp[j]=ftemp; itemp=dspd[i];dspd[i]=dspd[j];dspd[j]=itemp; itemp=ddir[i];ddir[i]=ddir[j];ddir[j]=itemp; } p[levels]=9999.0; for (i=0; i 10.0) { printf("ti error: surface data missing\n"); exit(-1); } for (i=0; i= levels) hd=dp[0]; else { ftemp = h[i] - h[i-1]; a = dp[i-1]*(h[i]-(h[0]+500.0))/ftemp + dp[i]*(500.0+h[0]-h[i-1])/ftemp; z += (500.0+h[0]-h[i-1]) * (a + dp[i-1]) / 2.0; hd = z / 500.0; } if (!batch) { printf("\nEnter forecast high temp (F) : "); scanf("%d",&ht); printf("Enter maximum altitude to calculate TI : "); scanf("%f",&maxz); } ftemp = (float) (ht-32.0)/1.8+t0; if (ftemp 333.0) ftemp=t[0]; if (maxz*CFMftemp) hd=ftemp; cb = (int) ((ftemp - hd)*4.0 + .5) * 100; /* est. cloud base */ htt = vtemp(ftemp,hd,p[0]); if (Gdd) printf(" %2d-%3s-%4d %2d UTC ", Gdd, Gmon[Gmm-1], Gyy, Ghh); else printf(" "); printf("Soaring report from %s upper air data.\n", station); if (ht < 150) { printf(" Forecast high:%3d F; est. base of any clouds:%5d feet AGL.", ht,cb); /* printf("Surface virtual temp: %4.1f C\n", htt-t0+.05); */ } else printf("No forecast high or estimated cloud base."); printf("\n\n"); for (i=0; i<199; i++) {dir[i]= -999.0; spd[i]= -999.0; } /*------------------ calculate thermal index ---------------------------*/ lo=(int) (h[0]/CFM/500.0) + 1; hi=(int) ((maxz+250.0)/500.0); if (hi>120) hi=120; /* don't go over 60,000 feet MSL */ if (hi=0) && (dspd[i]>=0)) { j = (int) ((h[i]/CFM+250.0)/500.0); dir[j] = ddir[i]; spd[j] = dspd[i]; } for (Gmin=999.0, Gmax=0.0, i=lo; i<=hi; i++) { k=500.0 * CFM * i; if ((i%2==0)&&(winddir[i/2]>=0)) { dir[i]=winddir[i/2]; spd[i]=windspd[i/2]; } itemp = ht; if (ht >= 150) itemp = (int) ((t[0]-t0)*1.8+32.5); for (j=1; j Gmax) Gmax = ta[i]; } /*GH Gsca = chars per deg. K of extremes of overlapped (actual,DALR) ranges. */ /*GH Glow = lowest temp in ranges in deg. K, for setting min point of graph. */ Glow = ta[hi] - ti[hi]; /*GH min of DALR range */ if (Glow > Gmin) Glow = Gmin; /*GH min of actual range */ Gx = ta[lo] - ti[lo]; /*GH max of DALR range */ if (Gx < Gmax) Gx = Gmax; /*GH max of actual range */ Gsca = (float) GSCA / (Gx - Glow); /* printf( "Gmax %5.1f Gmin %5.1f hiLR %5.1f @%5d loLR %5.1f @%5d\n", Gmax, Gmin, ta[lo], lo*500, ta[lo] + (float)(500*DALR*(hi-lo)), hi*500); GH debug*/ /* printf("Glow %5.1f K %6.1f F Range %5.1f C Gsca %5.3f\n", Glow, ((Glow-t0)*1.8)+32., Gx - Glow, Gsca); GH debug*/ /*---------------------- print out tables ----------------------------*/ printf(" === Raw Upper-Air Data ==="); j = (levels < 10) ? levels : 10; printf("\nFeet MSL:"); for (i=0; i < j; i++) printf("%7d", (int) (h[i]/CFM+.5) ); printf("\nPres mb: "); for (i=0; i < j; i++) printf("%7.0f", p[i] ); printf("\nTemp C: "); for (i=0; i < j; i++) printf("%7.1f", t[i]-t0+.01 ); printf("\nVirT C: "); for (i=0; i < j; i++) printf("%7.1f", tv[i]-t0+.01 ); printf("\nDewPt C: "); for (i=0; i < j; i++) if (dp[i] > 180.0) printf("%7.1f", dp[i]-t0+.01 ); else printf(" "); printf("\nWdir@kts:"); for (i=0; i < j; i++) if (dspd[i] >= 0) printf("%4d%3d", ddir[i], dspd[i] ); else printf(" "); if (p[0]<500.0) { printf("Insufficient upper air data for thermal index calculations.\n"); exit(-1); } for (i=0; i<=GSCA; i++) Graph[i] = '-'; Graph[GSCA+1]=0; printf("\n\n "); printf("=== Interpolations (temps in deg. F, altitudes in feet MSL) ===\n"); printf(" MSL *TI* Wdir@kts trig "); printf(" VirT %2.1f degrees/division (\"`\": Dry Adiabatic)\n",1.8/Gsca); printf("----- ---- -------- ---- . ---- %s\n", Graph); for (i=hi; i>=lo; i--) { printf("%5d", i*500); if (ht<150) printf("%6.1f", ti[i]+.05); else printf(" "); if ((spd[i]<0)||(dir[i]<0)) printf(" "); else printf("%5d%4d",dir[i],spd[i]); printf("%5d |%5.1f ", trig[i], ((ta[i]-t0)*1.8)+32.0); for (j=0; j=0) && (Glr <= GSCA)) Graph[Glr] = '`'; Gtmp = ( ta[i] - Glow) * Gsca; /* printf("\nGlr:%5d Gtmp:%5d ",Glr,Gtmp); GH debug*/ Gx = ta[i+1] - ta [i]; /* if (Gx < -1.5) Gch = '\\'; else if (Gx > 1.5) Gch = '/'; else Gch = ':'; */ Gch=':'; if ((Gtmp >=0) && (Gtmp <= GSCA)) Graph[Gtmp] = Gch; if (Glr > Gtmp) Gtmp=Glr; Graph[(int) ++Gtmp] = '\0'; printf("%s\n",Graph); } printf("\n\n === Complete Upper Air Data ===\n\n"); printf(" P(mb) H(ft) Tv(C) T(C) DP(C) "); printf("wind dir wind spd\n\n"); for (i=0; i180.0) printf("%9.1f",dp[i]-t0+.01); else printf(" "); if (dspd[i]>=0) printf("%9d%9d",ddir[i], dspd[i]); printf("\n"); } printf("Additional wind data:\n\n"); printf(" H(ft) dir spd\n"); printf(" ------ --- ---\n"); for (i=1; i<= 150; i++) if (windspd[i]>0) printf(" %2d000 %3d %3d\n",i,winddir[i],windspd[i]); printf("\n"); }