3 This is the TRMM Office Radar Software Library.
4 Copyright (C) 1996, 1997
6 Space Applications Corporation
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with this library; if not, write to the Free
21 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 extern int radar_verbose_flag;
31 typedef short UF_buffer[16384]; /* Some UF files are bigger than 4096
32 * that the UF doc's specify.
38 static float (*f)(Range x);
39 static Range (*invf)(float x);
41 Volume *reset_nsweeps_in_volume(Volume *volume)
44 if (volume == NULL) return NULL;
45 for (i=volume->h.nsweeps; i>0; i--)
46 if (volume->sweep[i-1] != NULL) {
47 volume->h.nsweeps = i;
52 Radar *reset_nsweeps_in_all_volumes(Radar *radar)
55 if (radar == NULL) return NULL;
56 for (i=0; i<radar->h.nvolumes; i++)
57 radar->v[i] = reset_nsweeps_in_volume(radar->v[i]);
61 Volume *copy_sweeps_into_volume(Volume *new_volume, Volume *old_volume)
65 if (old_volume == NULL) return new_volume;
66 if (new_volume == NULL) return new_volume;
67 nsweeps = new_volume->h.nsweeps; /* Save before copying old header. */
68 new_volume->h = old_volume->h;
69 new_volume->h.nsweeps = nsweeps;
70 for (i=0; i<old_volume->h.nsweeps; i++)
71 new_volume->sweep[i] = old_volume->sweep[i]; /* Just copy pointers. */
72 /* Free the old sweep array, not the pointers to sweeps. */
73 free(old_volume->sweep);
77 void swap2(short *buf, int n)
81 while (buf < end_addr) {
88 /********************************************************************/
89 /*********************************************************************/
93 /* By: John Merritt */
94 /* Space Applications Corporation */
96 /*********************************************************************/
97 int uf_into_radar(UF_buffer uf, Radar **the_radar)
100 /* Missing data flag : -32768 when a signed short. */
101 #define UF_NO_DATA 0X8000
103 /* Any convensions may be observed, however, Radial Velocity must be VE. */
105 * DM = Reflectivity (dB(mW)).
106 * DZ = Reflectivity (dBZ).
107 * VR = Radial Velocity.
108 * CZ = Corrected Reflectivity. (Quality controlled: AP removed, etc.)
109 * SW = Spectrum Width.
110 * DR = Differential Reflectivity.
111 * XZ = X-band Reflectivity.
113 * These fields may appear in any order in the UF file.
116 * UF_DONE if we're done with the UF ingest.
117 * UF_MORE if we need more UF data.
120 /* These are pointers to various locations within the UF buffer 'uf'.
121 * They are used to index the different components of the UF structure in
122 * a manor consistant with the UF documentation. For instance, uf_ma[1]
123 * will be equivalenced to the second word (2 bytes/each) of the UF
126 short *uf_ma; /* Mandatory header block. */
127 short *uf_lu; /* Local Use header block. */
128 short *uf_dh; /* Data header. */
129 short *uf_fh; /* Field header. */
130 short *uf_data; /* Data. */
132 /* The length of each header. */
133 int len_data, len_lu;
135 int current_fh_index;
138 int nfields, isweep, ifield, iray, i, m;
139 static int pulled_time_from_first_ray = 0;
140 char *field_type; /* For printing the field type upon error. */
149 extern int rsl_qfield[];
150 extern int *rsl_qsweep; /* See RSL_read_these_sweeps in volume.c */
151 extern int rsl_qsweep_max;
156 * The organization of the Radar structure is by volumes, then sweeps, then
157 * rays, then gates. This is different from the UF file organization.
158 * The UF format is sweeps, rays, then gates for all field types (volumes).
162 /* Set up all the UF pointers. */
164 uf_lu = uf + uf_ma[3] - 1;
165 uf_dh = uf + uf_ma[4] - 1;
168 isweep = uf_ma[9] - 1;
170 if (rsl_qsweep != NULL) {
171 if (isweep > rsl_qsweep_max) return UF_DONE;
172 if (rsl_qsweep[isweep] == 0) return UF_MORE;
176 /* Here is a sticky part. We must make sure that if we encounter any
177 * additional fields that were not previously present, that we are able
178 * to load them. This will require us to copy the entire radar structure
179 * and whack off the old one. But, we must be sure that it really is a
180 * new field. This is not so trivial as a couple of lines of code; I will
181 * have to think about this a little bit more. See STICKYSOLVED below.
184 if (radar == NULL) radar = RSL_new_radar(nfields);
185 /* Sticky solution here. */
188 radar = RSL_new_radar(MAX_RADAR_VOLUMES);
190 pulled_time_from_first_ray = 0;
191 for (i=0; i<MAX_RADAR_VOLUMES; i++)
192 if (rsl_qfield[i]) /* See RSL_select_fields in volume.c */
193 radar->v[i] = RSL_new_volume(20);
197 /* For LITTLE ENDIAN:
198 * WE "UNSWAP" character strings. Because there are so few of them,
199 * it is easier to catch them here. The entire UF buffer is swapped prior
200 * to entry to here, therefore, undo-ing these swaps; sets the
201 * character strings right.
204 for (i=0; i<nfields; i++) {
205 if (little_endian()) swap_2_bytes(&uf_dh[3+2*i]); /* Unswap. */
206 if (strncmp((char *)&uf_dh[3+2*i], "DZ", 2) == 0) ifield = DZ_INDEX;
207 else if (strncmp((char *)&uf_dh[3+2*i], "VR", 2) == 0) ifield = VR_INDEX;
208 else if (strncmp((char *)&uf_dh[3+2*i], "SW", 2) == 0) ifield = SW_INDEX;
209 else if (strncmp((char *)&uf_dh[3+2*i], "CZ", 2) == 0) ifield = CZ_INDEX;
210 else if (strncmp((char *)&uf_dh[3+2*i], "UZ", 2) == 0) ifield = ZT_INDEX;
211 else if (strncmp((char *)&uf_dh[3+2*i], "ZT", 2) == 0) ifield = ZT_INDEX;
212 else if (strncmp((char *)&uf_dh[3+2*i], "DR", 2) == 0) ifield = DR_INDEX;
213 else if (strncmp((char *)&uf_dh[3+2*i], "ZD", 2) == 0) ifield = ZD_INDEX;
214 else if (strncmp((char *)&uf_dh[3+2*i], "DM", 2) == 0) ifield = DM_INDEX;
215 else if (strncmp((char *)&uf_dh[3+2*i], "RH", 2) == 0) ifield = RH_INDEX;
216 else if (strncmp((char *)&uf_dh[3+2*i], "PH", 2) == 0) ifield = PH_INDEX;
217 else if (strncmp((char *)&uf_dh[3+2*i], "XZ", 2) == 0) ifield = XZ_INDEX;
218 else if (strncmp((char *)&uf_dh[3+2*i], "CD", 2) == 0) ifield = CD_INDEX;
219 else if (strncmp((char *)&uf_dh[3+2*i], "MZ", 2) == 0) ifield = MZ_INDEX;
220 else if (strncmp((char *)&uf_dh[3+2*i], "MD", 2) == 0) ifield = MD_INDEX;
221 else if (strncmp((char *)&uf_dh[3+2*i], "ZE", 2) == 0) ifield = ZE_INDEX;
222 else if (strncmp((char *)&uf_dh[3+2*i], "VE", 2) == 0) ifield = VE_INDEX;
223 else if (strncmp((char *)&uf_dh[3+2*i], "KD", 2) == 0) ifield = KD_INDEX;
224 else if (strncmp((char *)&uf_dh[3+2*i], "TI", 2) == 0) ifield = TI_INDEX;
225 else if (strncmp((char *)&uf_dh[3+2*i], "DX", 2) == 0) ifield = DX_INDEX;
226 else if (strncmp((char *)&uf_dh[3+2*i], "CH", 2) == 0) ifield = CH_INDEX;
227 else if (strncmp((char *)&uf_dh[3+2*i], "AH", 2) == 0) ifield = AH_INDEX;
228 else if (strncmp((char *)&uf_dh[3+2*i], "CV", 2) == 0) ifield = CV_INDEX;
229 else if (strncmp((char *)&uf_dh[3+2*i], "AV", 2) == 0) ifield = AV_INDEX;
230 else if (strncmp((char *)&uf_dh[3+2*i], "SQ", 2) == 0) ifield = SQ_INDEX;
231 else { /* etc. DON'T know how to handle this yet. */
232 field_type = (char *)&uf_dh[3+2*i];
233 fprintf(stderr, "Unknown field type %c%c\n", (char)field_type[0], (char)field_type[1]);
237 case DZ_INDEX: f = DZ_F; invf = DZ_INVF; break;
238 case VR_INDEX: f = VR_F; invf = VR_INVF; break;
239 case SW_INDEX: f = SW_F; invf = SW_INVF; break;
240 case CZ_INDEX: f = CZ_F; invf = CZ_INVF; break;
241 case ZT_INDEX: f = ZT_F; invf = ZT_INVF; break;
242 case DR_INDEX: f = DR_F; invf = DR_INVF; break;
243 case ZD_INDEX: f = ZD_F; invf = ZD_INVF; break;
244 case DM_INDEX: f = DM_F; invf = DM_INVF; break;
245 case RH_INDEX: f = RH_F; invf = RH_INVF; break;
246 case PH_INDEX: f = PH_F; invf = PH_INVF; break;
247 case XZ_INDEX: f = XZ_F; invf = XZ_INVF; break;
248 case CD_INDEX: f = CD_F; invf = CD_INVF; break;
249 case MZ_INDEX: f = MZ_F; invf = MZ_INVF; break;
250 case MD_INDEX: f = MD_F; invf = MD_INVF; break;
251 case ZE_INDEX: f = ZE_F; invf = ZE_INVF; break;
252 case VE_INDEX: f = VE_F; invf = VE_INVF; break;
253 case KD_INDEX: f = KD_F; invf = KD_INVF; break;
254 case TI_INDEX: f = TI_F; invf = TI_INVF; break;
255 case DX_INDEX: f = DX_F; invf = DX_INVF; break;
256 case CH_INDEX: f = CH_F; invf = CH_INVF; break;
257 case AH_INDEX: f = AH_F; invf = AH_INVF; break;
258 case CV_INDEX: f = CV_F; invf = CV_INVF; break;
259 case AV_INDEX: f = AV_F; invf = AV_INVF; break;
260 case SQ_INDEX: f = SQ_F; invf = SQ_INVF; break;
261 default: f = DZ_F; invf = DZ_INVF; break;
264 /* Do we place the data into this volume? */
265 if (radar->v[ifield] == NULL) continue; /* Nope. */
267 if (isweep >= radar->v[ifield]->h.nsweeps) { /* Exceeded sweep limit.
268 * Allocate more sweeps.
269 * Copy all previous sweeps.
271 if (radar_verbose_flag)
272 fprintf(stderr,"Exceeded sweep allocation of %d. Adding 20 more.\n", isweep);
273 new_volume = RSL_new_volume(radar->v[ifield]->h.nsweeps+20);
274 new_volume = copy_sweeps_into_volume(new_volume, radar->v[ifield]);
275 radar->v[ifield] = new_volume;
278 if (radar->v[ifield]->sweep[isweep] == NULL) {
279 if (radar_verbose_flag)
280 fprintf(stderr,"Allocating new sweep for field %d, isweep %d\n", ifield, isweep);
281 radar->v[ifield]->sweep[isweep] = RSL_new_sweep(1000);
282 radar->v[ifield]->sweep[isweep]->h.nrays = 0; /* Increment this for each
285 radar->v[ifield]->h.f = f;
286 radar->v[ifield]->h.invf = invf;
287 radar->v[ifield]->sweep[isweep]->h.f = f;
288 radar->v[ifield]->sweep[isweep]->h.invf = invf;
289 radar->v[ifield]->sweep[isweep]->h.sweep_num = uf_ma[9];
290 radar->v[ifield]->sweep[isweep]->h.elev = uf_ma[35] / 64.0;
295 current_fh_index = uf_dh[4+2*i];
296 uf_fh = uf + current_fh_index - 1;
297 sweep = radar->v[ifield]->sweep[isweep];
298 iray = sweep->h.nrays;
300 radar->v[ifield]->sweep[isweep]->ray[iray] = RSL_new_ray(nbins);
301 ray = radar->v[ifield]->sweep[isweep]->ray[iray];
307 * ---- Beginning of MANDATORY HEADER BLOCK.
309 ray->h.ray_num = uf_ma[7];
310 if (little_endian()) swap2(&uf_ma[10], 8);
311 memcpy(radar->h.radar_name, &uf_ma[10], 8);
312 if (little_endian()) swap2(&uf_ma[10], 8/2);
313 memcpy(radar->h.name, &uf_ma[14], 8);
314 if (little_endian()) swap2(&uf_ma[14], 8/2);
316 /* All components of lat/lon are the same sign. If not, then
317 * what ever wrote the UF was in error. A simple RSL program
318 * can repair the damage, however, not here.
320 ray->h.lat = uf_ma[18] + uf_ma[19]/60.0 + uf_ma[20]/64.0/3600;
321 ray->h.lon = uf_ma[21] + uf_ma[22]/60.0 + uf_ma[23]/64.0/3600;
322 ray->h.alt = uf_ma[24];
323 ray->h.year = uf_ma[25];
324 if (ray->h.year < 1900) {
326 if (ray->h.year < 1980) ray->h.year += 100; /* Year >= 2000. */
328 ray->h.month = uf_ma[26];
329 ray->h.day = uf_ma[27];
330 ray->h.hour = uf_ma[28];
331 ray->h.minute = uf_ma[29];
332 ray->h.sec = uf_ma[30];
333 ray->h.azimuth = uf_ma[32] / 64.0;
335 /* If Local Use Header is present and contains azimuth, use that
336 * azimuth for VR and SW. This is for WSR-88D, which runs separate
337 * scans for DZ and VR/SW at the lower elevations, which means DZ
338 * VR/SW and have different azimuths in the "same" ray.
340 len_lu = uf_ma[4] - uf_ma[3];
341 if (len_lu == 2 && (ifield == VR_INDEX || ifield == SW_INDEX)) {
342 if (strncmp((char *)uf_lu,"ZA",2) == 0 ||
343 strncmp((char *)uf_lu,"AZ",2) == 0)
344 ray->h.azimuth = uf_lu[1] / 64.0;
346 if (ray->h.azimuth < 0.) ray->h.azimuth += 360.; /* make it 0 to 360. */
347 ray->h.elev = uf_ma[33] / 64.0;
348 ray->h.elev_num = sweep->h.sweep_num;
349 ray->h.fix_angle = sweep->h.elev = uf_ma[35] / 64.0;
350 ray->h.azim_rate = uf_ma[36] / 64.0;
351 ray->h.sweep_rate = ray->h.azim_rate * (60.0/360.0);
352 missing_data = uf_ma[44];
354 if (pulled_time_from_first_ray == 0) {
355 radar->h.height = uf_ma[24];
356 radar->h.latd = uf_ma[18];
357 radar->h.latm = uf_ma[19];
358 radar->h.lats = uf_ma[20] / 64.0;
359 radar->h.lond = uf_ma[21];
360 radar->h.lonm = uf_ma[22];
361 radar->h.lons = uf_ma[23] / 64.0;
362 radar->h.year = ray->h.year;
363 radar->h.month = ray->h.month;
364 radar->h.day = ray->h.day;
365 radar->h.hour = ray->h.hour;
366 radar->h.minute = ray->h.minute;
367 radar->h.sec = ray->h.sec;
368 strcpy(radar->h.radar_type, "uf");
369 pulled_time_from_first_ray = 1;
372 * ---- End of MANDATORY HEADER BLOCK.
375 /* ---- Optional header used for MCTEX files. */
376 /* If this is a MCTEX file, the first 4 words following the
377 mandatory header contain the string 'MCTEX'. */
378 memcpy(proj_name, (short *)(uf + uf_ma[2] - 1), 8);
379 if (little_endian()) swap2(proj_name, 4);
382 /* ---- Local Use Header (if present) was checked during Mandatory
383 * Header processing above.
386 /* ---- Begining of FIELD HEADER. */
387 uf_fh = uf+current_fh_index - 1;
388 scale_factor = uf_fh[1];
389 ray->h.range_bin1 = uf_fh[2] * 1000.0 + uf_fh[3];
390 ray->h.gate_size = uf_fh[4];
392 ray->h.nbins = uf_fh[5];
393 ray->h.pulse_width = uf_fh[6]/(RSL_SPEED_OF_LIGHT/1.0e6);
395 if (strncmp((char *)proj_name, "MCTEX", 5) == 0) /* MCTEX? */
397 /* The beamwidth values are not correct in Mctex UF files. */
398 ray->h.beam_width = 1.0;
399 sweep->h.beam_width = ray->h.beam_width;
400 sweep->h.horz_half_bw = ray->h.beam_width/2.0;
401 sweep->h.vert_half_bw = ray->h.beam_width/2.0;
405 ray->h.beam_width = uf_fh[7] / 64.0;
406 sweep->h.beam_width = uf_fh[7] / 64.0;
407 sweep->h.horz_half_bw = uf_fh[7] / 128.0; /* DFF 4/4/95 */
408 sweep->h.vert_half_bw = uf_fh[8] / 128.0; /* DFF 4/4/95 */
410 /* fprintf (stderr, "uf_fh[7] = %d, [8] = %d\n", (int)uf_fh[7], (int)uf_fh[8]); */
411 if((int)uf_fh[7] == -32768) {
412 ray->h.beam_width = 1;
413 sweep->h.beam_width = 1;
414 sweep->h.horz_half_bw = .5;
415 sweep->h.vert_half_bw = .5;
418 ray->h.frequency = uf_fh[9] / 64.0;
419 ray->h.wavelength = uf_fh[11] / 64.0 / 100.0; /* cm to m. */
420 ray->h.pulse_count = uf_fh[12];
421 if (ifield == DZ_INDEX || ifield == ZT_INDEX) {
422 radar->v[ifield]->h.calibr_const = uf_fh[16] / 100.0;
423 /* uf value scaled by 100 */
426 radar->v[ifield]->h.calibr_const = 0.0;
428 if (uf_fh[17] == (short)UF_NO_DATA) x = 0;
429 else x = uf_fh[17] / 1000000.0; /* PRT in seconds. */
432 ray->h.unam_rng = RSL_SPEED_OF_LIGHT / (2.0 * ray->h.prf * 1000.0);
436 ray->h.unam_rng = 0.0;
439 if (VR_INDEX == ifield || VE_INDEX == ifield) {
440 ray->h.nyq_vel = uf_fh[19] / scale_factor;
443 /* ---- End of FIELD HEADER. */
448 /* ---- Begining of FIELD DATA. */
449 uf_data = uf+uf_fh[0] - 1;
451 len_data = ray->h.nbins; /* Known because of RSL_new_ray. */
452 for (m=0; m<len_data; m++) {
453 if (uf_data[m] == (short)UF_NO_DATA)
454 ray->range[m] = invf(BADVAL); /* BADVAL */
456 if(uf_data[m] == missing_data)
457 ray->range[m] = invf(NOECHO); /* NOECHO */
459 ray->range[m] = invf((float)uf_data[m]/scale_factor);
468 /*********************************************************************/
472 /* By: John Merritt */
473 /* Space Applications Corporation */
474 /* October 4, 1994 */
475 /*********************************************************************/
476 void swap_uf_buffer(UF_buffer uf)
480 addr_end = uf + sizeof(UF_buffer)/sizeof(short);
481 while (uf < addr_end)
485 enum UF_type {NOT_UF, TRUE_UF, TWO_BYTE_UF, FOUR_BYTE_UF};
488 /*********************************************************************/
490 /* RSL_uf_to_radar_fp */
492 /* By: John Merritt */
493 /* Space Applications Corporation */
494 /* September 22, 1995 */
495 /*********************************************************************/
496 Radar *RSL_uf_to_radar_fp(FILE *fp)
507 enum UF_type uf_type;
508 #define NEW_BUFSIZ 16384
512 setvbuf(fp,NULL,_IOFBF,(size_t)NEW_BUFSIZ); /* Faster i/o? */
513 if (fread(magic.buf, sizeof(char), 6, fp) <= 0) return NULL;
515 * Check for fortran record length delimeters, NCAR kludge.
517 if (strncmp("UF", magic.buf, 2) == 0) uf_type = TRUE_UF;
518 else if (strncmp("UF", &magic.buf[2], 2) == 0) uf_type = TWO_BYTE_UF;
519 else if (strncmp("UF", &magic.buf[4], 2) == 0) uf_type = FOUR_BYTE_UF;
520 else uf_type = NOT_UF;
524 if (radar_verbose_flag) fprintf(stderr,"UF file with 4 byte FORTRAN record delimeters.\n");
525 /* Handle first record specially, since we needed magic information. */
527 if (little_endian()) swap_4_bytes(&nbytes);
528 memcpy(uf, &magic.buf[4], 2);
529 (void)fread(&uf[1], sizeof(char), nbytes-2, fp);
530 if (little_endian()) swap_uf_buffer(uf);
531 (void)fread(&nbytes, sizeof(int), 1, fp);
532 if (uf_into_radar(uf, &radar) == UF_DONE) break;
533 /* Now the rest of the file. */
534 while(fread(&nbytes, sizeof(int), 1, fp) > 0) {
535 if (little_endian()) swap_4_bytes(&nbytes);
537 (void)fread(uf, sizeof(char), nbytes, fp);
538 if (little_endian()) swap_uf_buffer(uf);
540 (void)fread(&nbytes, sizeof(int), 1, fp);
542 if (uf_into_radar(uf, &radar) == UF_DONE) break;
547 if (radar_verbose_flag) fprintf(stderr,"UF file with 2 byte FORTRAN record delimeters.\n");
548 /* Handle first record specially, since we needed magic information. */
549 sbytes = magic.sword;
550 if (little_endian()) swap_2_bytes(&sbytes);
551 memcpy(uf, &magic.buf[2], 4);
552 (void)fread(&uf[2], sizeof(char), sbytes-4, fp);
553 if (little_endian()) swap_uf_buffer(uf);
554 (void)fread(&sbytes, sizeof(short), 1, fp);
555 uf_into_radar(uf, &radar);
556 /* Now the rest of the file. */
557 while(fread(&sbytes, sizeof(short), 1, fp) > 0) {
558 if (little_endian()) swap_2_bytes(&sbytes);
560 (void)fread(uf, sizeof(char), sbytes, fp);
561 if (little_endian()) swap_uf_buffer(uf);
563 (void)fread(&sbytes, sizeof(short), 1, fp);
565 if (uf_into_radar(uf, &radar) == UF_DONE) break;
570 if (radar_verbose_flag) fprintf(stderr,"UF file with no FORTRAN record delimeters. Good.\n");
571 /* Handle first record specially, since we needed magic information. */
572 memcpy(&sbytes, &magic.buf[2], 2); /* Record length is in word #2. */
573 if (little_endian()) swap_2_bytes(&sbytes); /* # of 2 byte words. */
575 memcpy(uf, &magic.buf[0], 6);
576 (void)fread(&uf[3], sizeof(short), sbytes-3, fp);
577 if (little_endian()) swap_uf_buffer(uf);
578 uf_into_radar(uf, &radar);
579 /* Now the rest of the file. */
580 while(fread(uf, sizeof(short), 2, fp) > 0) {
581 memcpy(&sbytes, &uf[1], 2); /* Record length is in word #2. */
582 if (little_endian()) swap_2_bytes(&sbytes);
584 (void)fread(&uf[2], sizeof(short), sbytes-2, fp); /* Have words 1,2. */
585 if (little_endian()) swap_uf_buffer(uf);
587 if (uf_into_radar(uf, &radar) == UF_DONE) break;
591 case NOT_UF: return NULL; break;
593 radar = reset_nsweeps_in_all_volumes(radar);
594 radar = RSL_prune_radar(radar);
600 /*********************************************************************/
602 /* RSL_uf_to_radar */
604 /* By: John Merritt */
605 /* Space Applications Corporation */
606 /* September 22, 1995 */
607 /*********************************************************************/
608 Radar *RSL_uf_to_radar(char *infile)
611 * This routine ingests a UF file and fills the Radar structure.
612 * This routine allocates space via the system routine malloc.
614 * If *infile is NULL, read from stdin.
620 if (infile == NULL) {
623 fp = fdopen(save_fd, "r");
624 } else if ((fp = fopen(infile, "r")) == NULL) {
628 fp = uncompress_pipe(fp); /* Transparently gunzip. */
629 radar = RSL_uf_to_radar_fp(fp);