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.
31 extern int radar_verbose_flag;
32 /* Changed old buffer size (16384) for larger dualpol files. BLK 5/18/2011 */
33 typedef short UF_buffer[20000]; /* Some UF files are bigger than 4096
34 * that the UF doc's specify.
40 static float (*f)(Range x);
41 static Range (*invf)(float x);
43 Volume *reset_nsweeps_in_volume(Volume *volume)
46 if (volume == NULL) return NULL;
47 for (i=volume->h.nsweeps; i>0; i--)
48 if (volume->sweep[i-1] != NULL) {
49 volume->h.nsweeps = i;
54 Radar *reset_nsweeps_in_all_volumes(Radar *radar)
57 if (radar == NULL) return NULL;
58 for (i=0; i<radar->h.nvolumes; i++)
59 radar->v[i] = reset_nsweeps_in_volume(radar->v[i]);
63 Volume *copy_sweeps_into_volume(Volume *new_volume, Volume *old_volume)
67 if (old_volume == NULL) return new_volume;
68 if (new_volume == NULL) return new_volume;
69 nsweeps = new_volume->h.nsweeps; /* Save before copying old header. */
70 new_volume->h = old_volume->h;
71 new_volume->h.nsweeps = nsweeps;
72 for (i=0; i<old_volume->h.nsweeps; i++)
73 new_volume->sweep[i] = old_volume->sweep[i]; /* Just copy pointers. */
74 /* Free the old sweep array, not the pointers to sweeps. */
75 free(old_volume->sweep);
79 void swap2(short *buf, int n)
83 while (buf < end_addr) {
89 static void put_start_time_in_radar_header(Radar *radar)
91 /* Get the earliest ray time and store it in radar header.
92 * The search is necessary because rays are not always in chronological order.
93 * For example, we have received data in which rays were apparently sorted by
94 * azimuth in some upstream software. This results in the ray times being out
95 * of order, because a sweep rarely actually begins at zero degrees.
97 * Written by Bart Kelley, SSAI, June 19, 2013
104 int prevdate, thisdate;
105 float prevtime, thistime;
107 /* Get first sweep of first available field. */
108 for (i=0; i < MAX_RADAR_VOLUMES; i++) {
109 if ((sweep = radar->v[i]->sweep[0]) != NULL) break;
111 /* This shouldn't happen. */
112 if (i >= MAX_RADAR_VOLUMES) {
113 printf("put_start_time_in_radar_header: No radar volumes contained "
114 "sweep at index 0.\n");
118 /* Get first ray and its time. */
120 while (!sweep->ray[i] && i < sweep->h.nrays) i++;
122 prevdate = ray->h.year * 10000 + ray->h.month * 100 + ray->h.day;
123 prevtime = ray->h.hour * 10000 + ray->h.minute * 100 + ray->h.sec;
125 /* Compare times of remaining rays for earliest time. */
126 for (i=0; i<sweep->h.nrays; i++) {
128 thisdate = ray->h.year * 10000 + ray->h.month * 100 + ray->h.day;
129 thistime = ray->h.hour * 10000 + ray->h.minute * 100 + ray->h.sec;
130 if (thisdate == prevdate) {
131 if (thistime < prevtime) prevtime = thistime;
133 else if (thisdate < prevdate) {
139 radar->h.year = prevdate / 10000;
140 radar->h.month = prevdate / 100 % 100;
141 radar->h.day = prevdate % 100;
142 radar->h.hour = (int) prevtime / 10000;
143 radar->h.minute = (int) prevtime / 100 % 100;
144 radar->h.sec = fmod(prevtime,100.);
147 /********************************************************************/
148 /*********************************************************************/
152 /* By: John Merritt */
153 /* Space Applications Corporation */
154 /* August 26, 1994 */
155 /*********************************************************************/
156 int uf_into_radar(UF_buffer uf, Radar **the_radar)
159 /* Missing data flag : -32768 when a signed short. */
160 #define UF_NO_DATA 0X8000
162 /* Any convensions may be observed, however, Radial Velocity must be VE. */
164 * DM = Reflectivity (dB(mW)).
165 * DZ = Reflectivity (dBZ).
166 * VR = Radial Velocity.
167 * CZ = Corrected Reflectivity. (Quality controlled: AP removed, etc.)
168 * SW = Spectrum Width.
169 * DR = Differential Reflectivity.
170 * XZ = X-band Reflectivity.
172 * These fields may appear in any order in the UF file.
175 * UF_DONE if we're done with the UF ingest.
176 * UF_MORE if we need more UF data.
179 /* These are pointers to various locations within the UF buffer 'uf'.
180 * They are used to index the different components of the UF structure in
181 * a manor consistant with the UF documentation. For instance, uf_ma[1]
182 * will be equivalenced to the second word (2 bytes/each) of the UF
185 short *uf_ma; /* Mandatory header block. */
186 short *uf_lu; /* Local Use header block. */
187 short *uf_dh; /* Data header. */
188 short *uf_fh; /* Field header. */
189 short *uf_data; /* Data. */
191 /* The length of each header. */
192 int len_data, len_lu;
194 int current_fh_index;
197 int nfields, isweep, ifield, iray, i, j, m;
198 static int pulled_time_from_first_ray = 0;
199 static int need_scan_mode = 1;
200 char *field_type; /* For printing the field type upon error. */
210 extern int rsl_qfield[];
211 extern int *rsl_qsweep; /* See RSL_read_these_sweeps in volume.c */
212 extern int rsl_qsweep_max;
217 * The organization of the Radar structure is by volumes, then sweeps, then
218 * rays, then gates. This is different from the UF file organization.
219 * The UF format is sweeps, rays, then gates for all field types (volumes).
223 /* Set up all the UF pointers. */
225 uf_lu = uf + uf_ma[3] - 1;
226 uf_dh = uf + uf_ma[4] - 1;
229 isweep = uf_ma[9] - 1;
231 if (rsl_qsweep != NULL) {
232 if (isweep > rsl_qsweep_max) return UF_DONE;
233 if (rsl_qsweep[isweep] == 0) return UF_MORE;
237 /* Here is a sticky part. We must make sure that if we encounter any
238 * additional fields that were not previously present, that we are able
239 * to load them. This will require us to copy the entire radar structure
240 * and whack off the old one. But, we must be sure that it really is a
241 * new field. This is not so trivial as a couple of lines of code; I will
242 * have to think about this a little bit more. See STICKYSOLVED below.
245 if (radar == NULL) radar = RSL_new_radar(nfields);
246 /* Sticky solution here. */
249 radar = RSL_new_radar(MAX_RADAR_VOLUMES);
251 pulled_time_from_first_ray = 0;
252 for (i=0; i<MAX_RADAR_VOLUMES; i++)
253 if (rsl_qfield[i]) /* See RSL_select_fields in volume.c */
254 radar->v[i] = RSL_new_volume(20);
259 if (need_scan_mode) {
260 /* PPI and RHI are enum constants defined in rsl.h */
261 if (uf_ma[34] == 1) radar->h.scan_mode = PPI;
262 else if (uf_ma[34] == 3) radar->h.scan_mode = RHI;
264 fprintf(stderr,"Warning: UF sweep mode = %d\n", uf_ma[34]);
265 fprintf(stderr," Expected 1 or 3 (PPI or RHI)\n");
266 fprintf(stderr," Setting radar->h.scan_mode to PPI\n");
267 radar->h.scan_mode = PPI;
272 /* For LITTLE ENDIAN:
273 * WE "UNSWAP" character strings. Because there are so few of them,
274 * it is easier to catch them here. The entire UF buffer is swapped prior
275 * to entry to here, therefore, undo-ing these swaps; sets the
276 * character strings right.
279 for (i=0; i<nfields; i++) {
280 if (little_endian()) swap_2_bytes(&uf_dh[3+2*i]); /* Unswap. */
282 field_type = (char *)&uf_dh[3+2*i];
283 for (j=0; j<MAX_RADAR_VOLUMES; j++) {
284 if (strncmp(field_type, RSL_ftype[j], 2) == 0) {
289 if (ifield < 0) { /* DON'T know how to handle this yet. */
290 fprintf(stderr, "Unknown field type %c%c\n", (char)field_type[0],
291 (char)field_type[1]);
295 f = RSL_f_list[ifield];
296 invf = RSL_invf_list[ifield];
298 /* Do we place the data into this volume? */
299 if (radar->v[ifield] == NULL) continue; /* Nope. */
301 if (isweep >= radar->v[ifield]->h.nsweeps) { /* Exceeded sweep limit.
302 * Allocate more sweeps.
303 * Copy all previous sweeps.
305 if (radar_verbose_flag)
306 fprintf(stderr,"Exceeded sweep allocation of %d. Adding 20 more.\n", isweep);
307 new_volume = RSL_new_volume(radar->v[ifield]->h.nsweeps+20);
308 new_volume = copy_sweeps_into_volume(new_volume, radar->v[ifield]);
309 radar->v[ifield] = new_volume;
312 if (radar->v[ifield]->sweep[isweep] == NULL) {
313 if (radar_verbose_flag)
314 fprintf(stderr,"Allocating new sweep for field %d, isweep %d\n", ifield, isweep);
315 radar->v[ifield]->sweep[isweep] = RSL_new_sweep(1000);
316 radar->v[ifield]->sweep[isweep]->h.nrays = 0; /* Increment this for each
319 radar->v[ifield]->h.f = f;
320 radar->v[ifield]->h.invf = invf;
321 radar->v[ifield]->sweep[isweep]->h.f = f;
322 radar->v[ifield]->sweep[isweep]->h.invf = invf;
323 radar->v[ifield]->sweep[isweep]->h.sweep_num = uf_ma[9];
324 radar->v[ifield]->sweep[isweep]->h.elev = uf_ma[35] / 64.0;
329 current_fh_index = uf_dh[4+2*i];
330 uf_fh = uf + current_fh_index - 1;
331 sweep = radar->v[ifield]->sweep[isweep];
332 iray = sweep->h.nrays;
334 radar->v[ifield]->sweep[isweep]->ray[iray] = RSL_new_ray(nbins);
335 ray = radar->v[ifield]->sweep[isweep]->ray[iray];
341 * ---- Beginning of MANDATORY HEADER BLOCK.
343 ray->h.ray_num = uf_ma[7];
344 if (little_endian()) swap2(&uf_ma[10], 8);
345 memcpy(radar->h.radar_name, &uf_ma[10], 8);
346 if (little_endian()) swap2(&uf_ma[10], 8/2);
347 memcpy(radar->h.name, &uf_ma[14], 8);
348 if (little_endian()) swap2(&uf_ma[14], 8/2);
350 /* All components of lat/lon are the same sign. If not, then
351 * what ever wrote the UF was in error. A simple RSL program
352 * can repair the damage, however, not here.
354 ray->h.lat = uf_ma[18] + uf_ma[19]/60.0 + uf_ma[20]/64.0/3600;
355 ray->h.lon = uf_ma[21] + uf_ma[22]/60.0 + uf_ma[23]/64.0/3600;
356 ray->h.alt = uf_ma[24];
357 ray->h.year = uf_ma[25];
358 if (ray->h.year < 1900) {
360 if (ray->h.year < 1980) ray->h.year += 100; /* Year >= 2000. */
362 ray->h.month = uf_ma[26];
363 ray->h.day = uf_ma[27];
364 ray->h.hour = uf_ma[28];
365 ray->h.minute = uf_ma[29];
366 ray->h.sec = uf_ma[30];
367 ray->h.azimuth = uf_ma[32] / 64.0;
369 /* If Local Use Header is present and contains azimuth, use that
370 * azimuth for VR and SW. This is for WSR-88D, which runs separate
371 * scans for DZ and VR/SW at the lower elevations, which means DZ
372 * VR/SW and have different azimuths in the "same" ray.
374 len_lu = uf_ma[4] - uf_ma[3];
375 if (len_lu == 2 && (ifield == VR_INDEX || ifield == SW_INDEX)) {
376 if (strncmp((char *)uf_lu,"ZA",2) == 0 ||
377 strncmp((char *)uf_lu,"AZ",2) == 0)
378 ray->h.azimuth = uf_lu[1] / 64.0;
380 if (ray->h.azimuth < 0.) ray->h.azimuth += 360.; /* make it 0 to 360. */
381 ray->h.elev = uf_ma[33] / 64.0;
382 ray->h.elev_num = sweep->h.sweep_num;
383 ray->h.fix_angle = sweep->h.elev = uf_ma[35] / 64.0;
384 ray->h.azim_rate = uf_ma[36] / 64.0;
385 ray->h.sweep_rate = ray->h.azim_rate * (60.0/360.0);
386 missing_data = uf_ma[44];
388 if (pulled_time_from_first_ray == 0) {
389 radar->h.height = uf_ma[24];
390 radar->h.latd = uf_ma[18];
391 radar->h.latm = uf_ma[19];
392 radar->h.lats = uf_ma[20] / 64.0;
393 radar->h.lond = uf_ma[21];
394 radar->h.lonm = uf_ma[22];
395 radar->h.lons = uf_ma[23] / 64.0;
396 /* Note that radar header time is now handled at end of ingest by
397 * function put_start_time_in_radar_header(). The values below are
398 * replaced. --BLK, 6/19/13
400 radar->h.year = ray->h.year;
401 radar->h.month = ray->h.month;
402 radar->h.day = ray->h.day;
403 radar->h.hour = ray->h.hour;
404 radar->h.minute = ray->h.minute;
405 radar->h.sec = ray->h.sec;
406 strcpy(radar->h.radar_type, "uf");
407 pulled_time_from_first_ray = 1;
410 * ---- End of MANDATORY HEADER BLOCK.
413 /* ---- Optional header used for MCTEX files. */
414 /* If this is a MCTEX file, the first 4 words following the
415 mandatory header contain the string 'MCTEX'. */
416 memcpy(proj_name, (short *)(uf + uf_ma[2] - 1), 8);
417 if (little_endian()) swap2(proj_name, 4);
420 /* ---- Local Use Header (if present) was checked during Mandatory
421 * Header processing above.
424 /* ---- Begining of FIELD HEADER. */
425 uf_fh = uf+current_fh_index - 1;
426 scale_factor = uf_fh[1];
427 ray->h.range_bin1 = uf_fh[2] * 1000.0 + uf_fh[3];
428 ray->h.gate_size = uf_fh[4];
430 ray->h.nbins = uf_fh[5];
431 ray->h.pulse_width = uf_fh[6]/(RSL_SPEED_OF_LIGHT/1.0e6);
433 if (strncmp((char *)proj_name, "MCTEX", 5) == 0) /* MCTEX? */
435 /* The beamwidth values are not correct in Mctex UF files. */
436 ray->h.beam_width = 1.0;
437 sweep->h.beam_width = ray->h.beam_width;
438 sweep->h.horz_half_bw = ray->h.beam_width/2.0;
439 sweep->h.vert_half_bw = ray->h.beam_width/2.0;
443 ray->h.beam_width = uf_fh[7] / 64.0;
444 sweep->h.beam_width = uf_fh[7] / 64.0;
445 sweep->h.horz_half_bw = uf_fh[7] / 128.0; /* DFF 4/4/95 */
446 sweep->h.vert_half_bw = uf_fh[8] / 128.0; /* DFF 4/4/95 */
448 /* fprintf (stderr, "uf_fh[7] = %d, [8] = %d\n", (int)uf_fh[7], (int)uf_fh[8]); */
449 if((int)uf_fh[7] == -32768) {
450 ray->h.beam_width = 1;
451 sweep->h.beam_width = 1;
452 sweep->h.horz_half_bw = .5;
453 sweep->h.vert_half_bw = .5;
456 frequency = uf_fh[9];
457 /* This corrects an error in v1.43 and earlier where frequency was
458 * multiplied by 64. Correct units for UF are MHz; radar structure
461 if (frequency < 1000.) frequency = frequency/64.;
462 else frequency = frequency/1000.;
463 ray->h.frequency = frequency;
464 ray->h.wavelength = uf_fh[11] / 64.0 / 100.0; /* cm to m. */
465 ray->h.pulse_count = uf_fh[12];
466 if (ifield == DZ_INDEX || ifield == ZT_INDEX) {
467 radar->v[ifield]->h.calibr_const = uf_fh[16] / 100.0;
468 /* uf value scaled by 100 */
471 radar->v[ifield]->h.calibr_const = 0.0;
473 if (uf_fh[17] == (short)UF_NO_DATA) x = 0;
474 else x = uf_fh[17] / 1000000.0; /* PRT in seconds. */
477 ray->h.unam_rng = RSL_SPEED_OF_LIGHT / (2.0 * ray->h.prf * 1000.0);
481 ray->h.unam_rng = 0.0;
484 if (VR_INDEX == ifield || VE_INDEX == ifield) {
485 ray->h.nyq_vel = uf_fh[19] / scale_factor;
488 /* ---- End of FIELD HEADER. */
493 /* ---- Begining of FIELD DATA. */
494 uf_data = uf+uf_fh[0] - 1;
496 len_data = ray->h.nbins; /* Known because of RSL_new_ray. */
497 for (m=0; m<len_data; m++) {
498 if (uf_data[m] == (short)UF_NO_DATA)
499 ray->range[m] = invf(BADVAL); /* BADVAL */
501 if(uf_data[m] == missing_data)
502 ray->range[m] = invf(NOECHO); /* NOECHO */
504 ray->range[m] = invf((float)uf_data[m]/scale_factor);
513 /*********************************************************************/
517 /* By: John Merritt */
518 /* Space Applications Corporation */
519 /* October 4, 1994 */
520 /*********************************************************************/
521 void swap_uf_buffer(UF_buffer uf)
525 addr_end = uf + sizeof(UF_buffer)/sizeof(short);
526 while (uf < addr_end)
530 enum UF_type {NOT_UF, TRUE_UF, TWO_BYTE_UF, FOUR_BYTE_UF};
533 /*********************************************************************/
535 /* RSL_uf_to_radar_fp */
537 /* By: John Merritt */
538 /* Space Applications Corporation */
539 /* September 22, 1995 */
540 /*********************************************************************/
541 Radar *RSL_uf_to_radar_fp(FILE *fp)
552 enum UF_type uf_type;
553 #define NEW_BUFSIZ 16384
557 /* setvbuf(fp,NULL,_IOFBF,(size_t)NEW_BUFSIZ); * Faster i/o? */
558 if (fread(magic.buf, sizeof(char), 6, fp) <= 0) return NULL;
560 * Check for fortran record length delimeters, NCAR kludge.
562 if (strncmp("UF", magic.buf, 2) == 0) uf_type = TRUE_UF;
563 else if (strncmp("UF", &magic.buf[2], 2) == 0) uf_type = TWO_BYTE_UF;
564 else if (strncmp("UF", &magic.buf[4], 2) == 0) uf_type = FOUR_BYTE_UF;
565 else uf_type = NOT_UF;
569 if (radar_verbose_flag) fprintf(stderr,"UF file with 4 byte FORTRAN record delimeters.\n");
570 /* Handle first record specially, since we needed magic information. */
572 if (little_endian()) swap_4_bytes(&nbytes);
573 memcpy(uf, &magic.buf[4], 2);
574 if (fread(&uf[1], sizeof(char), nbytes-2, fp) != nbytes-2)
575 perror("RSL_uf_to_radar_fp: short read");
576 if (little_endian()) swap_uf_buffer(uf);
577 if (fread(&nbytes, sizeof(int), 1, fp) != 1)
578 perror("RSL_uf_to_radar_fp: short read");
579 if (uf_into_radar(uf, &radar) == UF_DONE) break;
580 /* Now the rest of the file. */
581 while(fread(&nbytes, sizeof(int), 1, fp) > 0) {
582 if (little_endian()) swap_4_bytes(&nbytes);
584 if (fread(uf, sizeof(char), nbytes, fp) != nbytes)
585 perror("RSL_uf_to_radar_fp: short read");
586 if (little_endian()) swap_uf_buffer(uf);
588 if (fread(&nbytes, sizeof(int), 1, fp) != 1)
589 perror("RSL_uf_to_radar_fp: short read");
591 if (uf_into_radar(uf, &radar) == UF_DONE) break;
596 if (radar_verbose_flag) fprintf(stderr,"UF file with 2 byte FORTRAN record delimeters.\n");
597 /* Handle first record specially, since we needed magic information. */
598 sbytes = magic.sword;
599 if (little_endian()) swap_2_bytes(&sbytes);
600 memcpy(uf, &magic.buf[2], 4);
601 if (fread(&uf[2], sizeof(char), sbytes-4, fp) != sbytes-4)
602 perror("RSL_uf_to_radar_fp: short read");
603 if (little_endian()) swap_uf_buffer(uf);
604 if (fread(&sbytes, sizeof(short), 1, fp) != 1)
605 perror("RSL_uf_to_radar_fp: short read");
606 uf_into_radar(uf, &radar);
607 /* Now the rest of the file. */
608 while(fread(&sbytes, sizeof(short), 1, fp) > 0) {
609 if (little_endian()) swap_2_bytes(&sbytes);
611 if (fread(uf, sizeof(char), sbytes, fp) != sbytes)
612 perror("RSL_uf_to_radar_fp: short read");
613 if (little_endian()) swap_uf_buffer(uf);
615 if (fread(&sbytes, sizeof(short), 1, fp) != 1)
616 perror("RSL_uf_to_radar_fp: short read");
618 if (uf_into_radar(uf, &radar) == UF_DONE) break;
623 if (radar_verbose_flag) fprintf(stderr,"UF file with no FORTRAN record delimeters. Good.\n");
624 /* Handle first record specially, since we needed magic information. */
625 memcpy(&sbytes, &magic.buf[2], 2); /* Record length is in word #2. */
626 if (little_endian()) swap_2_bytes(&sbytes); /* # of 2 byte words. */
628 memcpy(uf, &magic.buf[0], 6);
629 if (fread(&uf[3], sizeof(short), sbytes-3, fp) != sbytes-3)
630 perror("RSL_uf_to_radar_fp: short read");
631 if (little_endian()) swap_uf_buffer(uf);
632 uf_into_radar(uf, &radar);
633 /* Now the rest of the file. */
634 while(fread(uf, sizeof(short), 2, fp) > 0) {
635 memcpy(&sbytes, &uf[1], 2); /* Record length is in word #2. */
636 if (little_endian()) swap_2_bytes(&sbytes);
638 if (fread(&uf[2], sizeof(short), sbytes-2, fp) != sbytes-2)
639 perror("RSL_uf_to_radar_fp: short read"); /* Have words 1,2. */
640 if (little_endian()) swap_uf_buffer(uf);
642 if (uf_into_radar(uf, &radar) == UF_DONE) break;
646 case NOT_UF: return NULL; break;
648 radar = reset_nsweeps_in_all_volumes(radar);
649 put_start_time_in_radar_header(radar);
650 radar = RSL_prune_radar(radar);
656 /*********************************************************************/
658 /* RSL_uf_to_radar */
660 /* By: John Merritt */
661 /* Space Applications Corporation */
662 /* September 22, 1995 */
663 /*********************************************************************/
664 Radar *RSL_uf_to_radar(char *infile)
667 * This routine ingests a UF file and fills the Radar structure.
668 * This routine allocates space via the system routine malloc.
670 * If *infile is NULL, read from stdin.
676 if (infile == NULL) {
679 fp = fdopen(save_fd, "rb");
680 } else if ((fp = fopen(infile, "rb")) == NULL) {
684 fp = uncompress_pipe(fp); /* Transparently gunzip. */
685 radar = RSL_uf_to_radar_fp(fp);