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.
24 * Volume functions coded in this file:
26 * Default DZ_F and DZ_INVF. For VR, SW, CZ, ZT, DR and LR too.
27 * Volume *RSL_new_volume(int max_sweeps);
28 * Sweep *RSL_new_sweep(int max_rays);
29 * Ray *RSL_new_ray(int max_bins);
30 * Ray *RSL_clear_ray(Ray *r);
31 * Sweep *RSL_clear_sweep(Sweep *s);
32 * Volume *RSL_clear_volume(Volume *v);
33 * void RSL_free_ray(Ray *r);
34 * void RSL_free_sweep(Sweep *s);
35 * void RSL_free_volume(Volume *v);
36 * Ray *RSL_copy_ray(Ray *r);
37 * Sweep *RSL_copy_sweep(Sweep *s);
38 * Volume *RSL_copy_volume(Volume *v);
39 * Ray *RSL_get_ray_from_sweep(Sweep *s, float azim);
40 * float RSL_get_value_from_sweep(Sweep *s, float azim, float r);
41 * float RSL_get_value_from_ray(Ray *ray, float r);
42 * float RSL_get_value_at_h(Volume *v, float azim, float grnd_r, float h);
43 * Sweep *RSL_get_sweep(Volume *v, float elev);
44 * Ray *RSL_get_ray(Volume *v, float elev, float azimuth);
45 * float RSL_get_value(Volume *v, float elev, float azimuth, float range);
46 * Ray *RSL_get_ray_above(Volume *v, Ray *current_ray);
47 * Ray *RSL_get_ray_below(Volume *v, Ray *current_ray);
48 * Ray *RSL_get_matching_ray(Volume *v, Ray *ray);
49 * int RSL_get_sweep_index_from_volume
51 * See image_gen.c for the Volume image generation functions.
53 * See doc/rsl_index.html for HTML formatted documentation.
55 * All routines herein coded, unless otherwise stated, by:
57 * Space Applications Corporation
60 * Dennis Flanigan, Jr.
61 * flanigan@lance.colostate.edu
68 int strcasecmp(const char *s1, const char *s2);
72 #define bin_azimuth(x, dx) (float)((float)x/dx)
73 #define bin_elevation(x, dx) (float)((float)x/dx)
74 #define bin_range(x, dx) (float)((float)x/dx)
76 extern int radar_verbose_flag;
78 /* Internal storage conversion functions. These may be any conversion and
79 * may be dynamically defined; based on the input data conversion. If you
80 * change any of the reserved values, ie. values that cannot be converted
81 * from/to internal storage (BADVAL, APFLAG, etc), be sure to change all
82 * functions, both XX_F and XX_INVF to handle it. It is best to have the
83 * reserved values stored at 0, 1, 2, on up. That way you merely need to
84 * provide an offset to the actual conversion function. See 'rsl.h'
85 * for the definition of the reserved values. Currently: BADVAL, RFVAL,
88 * The conversion functions may NOT be macros.
91 #ifdef USE_TWO_BYTE_PRECISION
92 #define F_FACTOR 100.0
93 #define F_DR_FACTOR 1000.0
94 #define F_DZ_RANGE_OFFSET 50
97 #define F_DR_FACTOR 10.0
98 #define F_DZ_RANGE_OFFSET 32
101 /* #define F_VR_OFFSET 63.5 */
102 #define F_VR_OFFSET 127.0
103 #define F_DR_OFFSET 12.0
105 /* IMPORTANT: This is the offset from reserved values. This
106 * number must be exactly (or >=) the number of
107 * reserved values in XX_F and XX_INVF.
109 * You must change nsig_to_radar.c where F_OFFSET is used for optimization.
113 char *RSL_ftype[] = {"DZ", "VR", "SW", "CZ", "ZT", "DR",
114 "LR", "ZD", "DM", "RH", "PH", "XZ",
115 "CD", "MZ", "MD", "ZE", "VE", "KD",
116 "TI", "DX", "CH", "AH", "CV", "AV",
117 "SQ", "VS", "VL", "VG", "VT", "NP",
118 "HC", "VC", "V2", "S2", "V3", "S3",
119 "CR", "CC", "PR", "SD", "ZZ", "RD"};
121 float (*RSL_f_list[])(Range x) = {DZ_F, VR_F, SW_F, CZ_F, ZT_F, DR_F,
122 LR_F, ZD_F, DM_F, RH_F, PH_F, XZ_F,
123 CD_F, MZ_F, MD_F, ZE_F, VE_F, KD_F,
124 TI_F, DX_F, CH_F, AH_F, CV_F, AV_F,
125 SQ_F, VS_F, VL_F, VG_F, VT_F, NP_F,
126 HC_F, VC_F, VR_F, SW_F, VR_F, SW_F,
127 DZ_F, CZ_F, PH_F, SD_F, DZ_F, DZ_F};
129 Range (*RSL_invf_list[])(float x)
130 = {DZ_INVF, VR_INVF, SW_INVF, CZ_INVF, ZT_INVF, DR_INVF,
131 LR_INVF, ZD_INVF, DM_INVF, RH_INVF, PH_INVF, XZ_INVF,
132 CD_INVF, MZ_INVF, MD_INVF, ZE_INVF, VE_INVF, KD_INVF,
133 TI_INVF, DX_INVF, CH_INVF, AH_INVF, CV_INVF, AV_INVF,
134 SQ_INVF, VS_INVF, VL_INVF, VG_INVF, VT_INVF, NP_INVF,
135 HC_INVF, VC_INVF, VR_INVF, SW_INVF, VR_INVF, SW_INVF,
136 DZ_INVF, CZ_INVF, PH_INVF, SD_INVF, DZ_INVF, DZ_INVF};
138 float DZ_F(Range x) {
139 if (x >= F_OFFSET) /* This test works when Range is unsigned. */
140 return (((float)x-F_OFFSET)/F_FACTOR - F_DZ_RANGE_OFFSET); /* Default wsr88d. */
141 if (x == 0) return BADVAL;
142 if (x == 1) return RFVAL;
143 if (x == 2) return APFLAG;
144 if (x == 3) return NOECHO;
145 return BADVAL; /* Can't get here, but quiets the compiler. */
148 float VR_F(Range x) {
150 if (x >= F_OFFSET) { /* This test works when Range is unsigned. */
151 val = (((float)x-F_OFFSET)/F_FACTOR - F_VR_OFFSET); /* Default wsr88d coding. */
152 /* fprintf(stderr, "x=%d, val=%f\n", x, val); */
155 if (x == 0) return BADVAL;
156 if (x == 1) return RFVAL;
157 if (x == 2) return APFLAG;
158 if (x == 3) return NOECHO;
159 return BADVAL; /* Can't get here, but quiets the compiler. */
162 float DR_F(Range x) { /* Differential reflectivity */
164 if (x >= F_OFFSET) { /* This test works when Range is unsigned. */
165 val = (((float)x-F_OFFSET)/F_DR_FACTOR - F_DR_OFFSET);
168 if (x == 0) return BADVAL;
169 if (x == 1) return RFVAL;
170 if (x == 2) return APFLAG;
171 if (x == 3) return NOECHO;
172 return BADVAL; /* Can't get here, but quiets the compiler. */
175 float LR_F(Range x) {/* From MCTEX */
176 if (x >= F_OFFSET) /* This test works when Range is unsigned. */
177 return (float) (x - 250.)/6.;
178 if (x == 0) return BADVAL;
179 if (x == 1) return RFVAL;
180 if (x == 2) return APFLAG;
181 if (x == 3) return NOECHO;
185 float HC_F(Range x) { /* HydroClass (Sigmet) */
186 if (x == 0) return BADVAL;
190 /****************************
191 Sigmet RhoHV : one_byte values
192 > RohHV = sqrt((N-1)/253)
199 *******************************/
200 float RH_F(Range x) {
201 if (x == 0) return BADVAL;
202 /* return (float)(sqrt((double)((x-1.0)/253.0))); */
203 return (float)(x-1) / 65533.;
206 /*****************************
207 Sigmet PhiDP : one_byte values
208 > PhiDP (mod 180) = 180 * ((N-1)/254)
209 > The range is from 0 to 180 degrees in steps of 0.71 as follows.
215 ******************************/
216 float PH_F(Range x) {
217 if (x == 0) return BADVAL;
218 /*return (float)(180.0*((x-1.0)/254.0));*/
219 return (360.*(x-1.))/65534.;
222 /* TODO: Should this be 5. cm. instead of 0.5? Or maybe 10. cm.? */
223 float rsl_kdp_wavelen = 0.5; /* Default radar wavelen = .5 cm. See
224 * nsig_to_radar.c for the code that sets this.
226 /* KD_F for 1 or 2 byte. */
229 /****** Commented-out code for 1-byte Sigmet native data format.
232 if (rsl_kdp_wavelen == 0.0) return BADVAL;
235 -0.25 * pow((double)600.0,(double)((127-x)/126.0))
239 0.25 * pow((double)600.0,(double)((x-129)/126.0))
244 if (x == 1) return RFVAL;
245 if (x == 2) return APFLAG;
246 if (x == 3) return NOECHO;
250 if (x == 0) return BADVAL;
251 return (x-32768.)/100.;
254 /* Normalized Coherent Power (DORADE) */
257 if (x == 0) return BADVAL;
258 return (float)(x - 1) / 100.;
261 /* Standard Deviation (for Dual-pole QC testing.) */
264 if (x == 0) return BADVAL;
265 return (float)x / 100.;
268 /* Signal Quality Index */
271 if (x == 0) return BADVAL;
272 return (float)(x-1) / 65533.;
277 if (x >= F_OFFSET) return (float)(x);
278 if (x == 0) return BADVAL;
279 if (x == 1) return RFVAL;
280 if (x == 2) return APFLAG;
281 if (x == 3) return NOECHO;
285 float SW_F(Range x) { return VR_F(x); }
286 float CZ_F(Range x) { return DZ_F(x); }
287 float ZT_F(Range x) { return DZ_F(x); }
288 float ZD_F(Range x) { return DR_F(x); } /* Differential reflectivity */
289 float CD_F(Range x) { return DR_F(x); } /* Differential reflectivity */
290 float XZ_F(Range x) { return DZ_F(x); }
291 float MZ_F(Range x) { return (float)x; } /* DZ Mask */
292 float MD_F(Range x) { return MZ_F(x); } /* ZD Mask */
293 float ZE_F(Range x) { return DZ_F(x); }
294 float VE_F(Range x) { return VR_F(x); }
295 float DM_F(Range x) { return DZ_F(x); }
296 float DX_F(Range x) { return DZ_F(x); }
297 float CH_F(Range x) { return DZ_F(x); }
298 float AH_F(Range x) { return DZ_F(x); }
299 float CV_F(Range x) { return DZ_F(x); }
300 float AV_F(Range x) { return DZ_F(x); }
301 float VS_F(Range x) { return VR_F(x); }
302 float VL_F(Range x) { return VR_F(x); }
303 float VG_F(Range x) { return VR_F(x); }
304 float VT_F(Range x) { return VR_F(x); }
305 float VC_F(Range x) { return VR_F(x); }
310 /* Unfortunately, floats are stored differently than ints/shorts. So,
311 * we cannot simply set up a switch statement and we must test for
312 * all the special cases first. We must test for exactness.
314 Range DZ_INVF(float x)
316 if (x == BADVAL) return (Range)0;
317 if (x == RFVAL) return (Range)1;
318 if (x == APFLAG) return (Range)2;
319 if (x == NOECHO) return (Range)3;
320 if (x < -F_DZ_RANGE_OFFSET) return (Range)0;
321 return (Range)(F_FACTOR*(x+F_DZ_RANGE_OFFSET)+.5 + F_OFFSET); /* Default wsr88d. */
324 Range VR_INVF(float x)
326 if (x == BADVAL) return (Range)0;
327 if (x == RFVAL) return (Range)1;
328 if (x == APFLAG) return (Range)2;
329 if (x == NOECHO) return (Range)3;
330 if (x < -F_VR_OFFSET) return (Range)0;
331 return (Range)(F_FACTOR*(x+F_VR_OFFSET)+.5 + F_OFFSET); /* Default wsr88d coding. */
334 Range DR_INVF(float x) /* Differential reflectivity */
336 if (x == BADVAL) return (Range)0;
337 if (x == RFVAL) return (Range)1;
338 if (x == APFLAG) return (Range)2;
339 if (x == NOECHO) return (Range)3;
340 if (x < -F_DR_OFFSET) return (Range)0;
341 return (Range)(F_DR_FACTOR*(x + F_DR_OFFSET) + F_OFFSET + 0.5);
344 Range HC_INVF(float x) /* HydroClass (Sigmet) */
346 if (x == BADVAL) return (Range)0;
347 return (Range)(x + 0.5); /* Round */
350 Range LR_INVF(float x) /* MCTEX */
352 if (x == BADVAL) return (Range)0;
353 if (x == RFVAL) return (Range)1;
354 if (x == APFLAG) return (Range)2;
355 if (x == NOECHO) return (Range)3;
356 return (Range)((6.*x + 250) + 0.5); /* Round */
359 /**************************
360 Sigmet RhoHV : one_byte values
361 > RohHV = sqrt((N-1)/253)
368 ****************************/
369 /* RH_INVF for 1 or 2 byte data. */
370 Range RH_INVF(float x) {
371 if (x == BADVAL) return (Range)0;
372 /* return (Range)(x * x * 253.0 + 1.0 + 0.5); */
373 return (Range)(x * 65533. + 1. +.5);
376 /******************************
377 Sigmet PhiDP : one_byte values
378 > PhiDP (mod 180) = 180 * ((N-1)/254)
379 > The range is from 0 to 180 degrees in steps of 0.71 as follows.
385 *******************************/
386 Range PH_INVF(float x) {
387 if (x == BADVAL) return (Range)0;
388 /* return (Range)((x / 180.0) * 254.0 + 1.0 + 0.5); */
389 return (Range)(x*65534./360. + 1.0 + 0.5);
393 /* KD_INVF for 1 or 2 byte data. */
394 Range KD_INVF(float x) {
395 if (x == BADVAL) return (Range)0;
396 return (Range)(x * 100. + 32768. + 0.5);
397 /****** Old code for 1-byte Sigmet native data format commented-out:
398 if (x == RFVAL) return (Range)1;
399 if (x == APFLAG) return (Range)2;
400 if (x == NOECHO) return (Range)3;
401 if (rsl_kdp_wavelen == 0.0) return (Range)0;
404 126 * (log((double)-x) - log((double)(0.25/rsl_kdp_wavelen))) /
409 126 * (log((double)x) - log((double)0.25/rsl_kdp_wavelen)) /
419 /* Standard Deviation (for Dual-pole QC testing.) */
420 Range SD_INVF(float x)
422 if (x == BADVAL) return (Range)0;
423 return (Range)(x * 100.);
426 /* Signal Quality Index */
427 Range SQ_INVF(float x)
429 if (x == BADVAL) return (Range)0;
430 return (Range)(x * 65533. + 1. +.5);
433 /* Normalized Coherent Power (DORADE) */
434 Range NP_INVF(float x)
436 if (x == BADVAL) return (0);
437 return (Range)(x * 100. + 1.);
440 Range TI_INVF(float x) /* MCTEX */
442 if (x == BADVAL) return (Range)0;
443 if (x == RFVAL) return (Range)1;
444 if (x == APFLAG) return (Range)2;
445 if (x == NOECHO) return (Range)3;
450 Range SW_INVF(float x) { return VR_INVF(x); }
451 Range CZ_INVF(float x) { return DZ_INVF(x); }
452 Range ZT_INVF(float x) { return DZ_INVF(x); }
453 Range ZD_INVF(float x) { return DR_INVF(x); } /* Differential reflectivity */
454 Range CD_INVF(float x) { return DR_INVF(x); } /* Differential reflectivity */
455 Range XZ_INVF(float x) { return DZ_INVF(x); }
456 Range MZ_INVF(float x) { return (Range)x; } /* DZ Mask */
457 Range MD_INVF(float x) { return MZ_INVF(x); } /* ZD Mask */
458 Range ZE_INVF(float x) { return DZ_INVF(x); }
459 Range VE_INVF(float x) { return VR_INVF(x); }
460 Range DM_INVF(float x) { return DZ_INVF(x); }
461 Range DX_INVF(float x) { return DZ_INVF(x); }
462 Range CH_INVF(float x) { return DZ_INVF(x); }
463 Range AH_INVF(float x) { return DZ_INVF(x); }
464 Range CV_INVF(float x) { return DZ_INVF(x); }
465 Range AV_INVF(float x) { return DZ_INVF(x); }
466 Range VS_INVF(float x) { return VR_INVF(x); }
467 Range VL_INVF(float x) { return VR_INVF(x); }
468 Range VG_INVF(float x) { return VR_INVF(x); }
469 Range VT_INVF(float x) { return VR_INVF(x); }
470 Range VC_INVF(float x) { return VR_INVF(x); }
474 /**********************************************************************/
475 /* M E M O R Y M A N A G E M E N T R O U T I N E S */
476 /**********************************************************************/
477 /**********************************************************************/
483 /**********************************************************************/
484 Volume *RSL_new_volume(int max_sweeps)
487 * A volume consists of a header section and an array of sweeps.
490 v = (Volume *)calloc(1, sizeof(Volume));
491 if (v == NULL) perror("RSL_new_volume");
492 v->sweep = (Sweep **) calloc(max_sweeps, sizeof(Sweep*));
493 if (v->sweep == NULL) perror("RSL_new_volume, Sweep*");
494 v->h.nsweeps = max_sweeps; /* A default setting. */
499 * The 'Sweep_list' structure is internal to RSL. It maintains a list
500 * of sweeps allocated and it contains pointers to a hash table of Rays
501 * separately for each sweep. There is no reason to access this internal
502 * structure except when optimizing new RSL routines that access Rays.
503 * Otherwise, the RSL interfaces should suffice.
505 * The hash table is a means of finding rays, by azimuth, quickly.
506 * To find a ray is simple: use the hash function to get close
507 * to the ray, if not right on it the first time. Collisions of rays in
508 * the hash table are handled by a link list of rays from a hash entry.
509 * Typically, the first ray of the sweep is not the ray with the smallest
510 * azimuth angle. We are confident that the order of Rays in the Sweep
511 * is by azimuth angle, but that cannot be guarenteed. Therefore, this
512 * hash scheme is required.
514 * The 'Sweep_list' contains the address of the memory allocated to
515 * sweep. The list is sorted by addresses. There is no
516 * memory limit to the number of sweeps. If the number of sweeps exceeds
517 * the current allocation for the Sweep_list, then a new Sweep_list is
518 * allocated, which is bigger, and the old list copied to it.
520 * Sweep_list is at least as long as the number of sweeps allocated.
529 * By design of RSL, this should be "#define STATIC static"
531 * It is OK to "#define STATIC static", but, if you do, then
532 * the examples (run by run_tests in examples/) will fail for
533 * those programs that test these data structures. I normally,
534 * don't set this #define, for that reason.
538 STATIC int RSL_max_sweeps = 0; /* Initial allocation for sweep_list.
539 * RSL_new_sweep will allocate the space first
542 STATIC int RSL_nsweep_addr = 0; /* A count of sweeps in the table. */
543 STATIC Sweep_list *RSL_sweep_list = NULL;
544 STATIC int RSL_nextents = 0;
546 void FREE_HASH_NODE(Azimuth_hash *node)
548 if (node == NULL) return;
549 FREE_HASH_NODE(node->next); /* Tail recursive link list removal. */
553 void FREE_HASH_TABLE(Hash_table *table)
556 if (table == NULL) return;
557 for (i=0; i<table->nindexes; i++)
558 FREE_HASH_NODE(table->indexes[i]); /* A possible linked list of Rays. */
559 free(table->indexes);
563 void REMOVE_SWEEP(Sweep *s)
567 /* Find where it goes, split the list and slide the tail down one. */
568 for (i=0; i<RSL_nsweep_addr; i++)
569 if (s == RSL_sweep_list[i].s_addr) break;
571 if (i == RSL_nsweep_addr) return; /* Not found. */
572 /* This sweep is at 'i'. */
573 /* Deallocate the memory for the hash table. */
574 FREE_HASH_TABLE(RSL_sweep_list[i].hash);
577 for (j=i; j<RSL_nsweep_addr; j++)
578 RSL_sweep_list[j] = RSL_sweep_list[j+1];
580 RSL_sweep_list[RSL_nsweep_addr].s_addr = NULL;
581 RSL_sweep_list[RSL_nsweep_addr].hash = NULL;
585 int INSERT_SWEEP(Sweep *s)
587 Sweep_list *new_list;
590 if (RSL_nsweep_addr >= RSL_max_sweeps) { /* Current list is too small. */
592 new_list = (Sweep_list *) calloc(100*RSL_nextents, sizeof(Sweep_list));
593 if (new_list == NULL) {
594 perror("INSERT_SWEEP");
597 /* Copy the old list to the new one. */
598 for (i=0; i<RSL_max_sweeps; i++) new_list[i] = RSL_sweep_list[i];
599 RSL_max_sweeps = 100*RSL_nextents;
600 free(RSL_sweep_list);
601 RSL_sweep_list = new_list;
603 /* Find where it goes, split the list and slide the tail down one. */
604 for (i=0; i<RSL_nsweep_addr; i++)
605 if (s < RSL_sweep_list[i].s_addr) break;
607 /* This sweep goes at 'i'. But first we must split the list. */
608 for (j=RSL_nsweep_addr; j>i; j--)
609 RSL_sweep_list[j] = RSL_sweep_list[j-1];
611 RSL_sweep_list[i].s_addr = s;
612 RSL_sweep_list[i].hash = NULL;
617 int SWEEP_INDEX(Sweep *s)
619 /* Locate the sweep in the RSL_sweep_list. Return the index. */
620 /* Simple linear search; but this will be a binary search. */
622 for (i=0; i<RSL_nsweep_addr; i++)
623 if (s == RSL_sweep_list[i].s_addr) return i;
627 Sweep *RSL_new_sweep(int max_rays)
630 * A sweep consists of a header section and an array of rays.
633 s = (Sweep *)calloc(1, sizeof(Sweep));
634 if (s == NULL) perror("RSL_new_sweep");
636 s->ray = (Ray **) calloc(max_rays, sizeof(Ray*));
637 if (s->ray == NULL) perror("RSL_new_sweep, Ray*");
638 s->h.nrays = max_rays; /* A default setting. */
640 s->h.azimuth = -999.;
644 Ray *RSL_new_ray(int max_bins)
647 * A ray consists of a header section and an array of Range types (floats).
650 r = (Ray *)calloc(1, sizeof(Ray));
651 if (r == NULL) perror("RSL_new_ray");
652 r->range = (Range *) calloc(max_bins, sizeof(Range));
653 if (r->range == NULL) perror("RSL_new_ray, Range");
654 r->h.nbins = max_bins; /* A default setting. */
655 /* fprintf(stderr,"range[0] = %x, range[%d] = %x\n", &r->range[0], max_bins-1, &r->range[max_bins-1]);*/
659 /**********************************************************************/
665 /**********************************************************************/
666 Ray *RSL_clear_ray(Ray *r)
668 if (r == NULL) return r;
669 memset(r->range, 0, sizeof(Range)*r->h.nbins);
672 Sweep *RSL_clear_sweep(Sweep *s)
675 if (s == NULL) return s;
676 for (i=0; i<s->h.nrays; i++) {
677 RSL_clear_ray(s->ray[i]);
681 Volume *RSL_clear_volume(Volume *v)
684 if (v == NULL) return v;
685 for (i=0; i<v->h.nsweeps; i++) {
686 RSL_clear_sweep(v->sweep[i]);
690 /**********************************************************************/
696 /**********************************************************************/
697 void RSL_free_ray(Ray *r)
699 if (r == NULL) return;
700 if (r->range) free(r->range);
703 void RSL_free_sweep(Sweep *s)
706 if (s == NULL) return;
707 for (i=0; i<s->h.nrays; i++) {
708 RSL_free_ray(s->ray[i]);
710 if (s->ray) free(s->ray);
711 REMOVE_SWEEP(s); /* Remove from internal Sweep list. */
714 void RSL_free_volume(Volume *v)
717 if (v == NULL) return;
719 for (i=0; i<v->h.nsweeps; i++)
721 RSL_free_sweep(v->sweep[i]);
723 if (v->sweep) free(v->sweep);
727 /**********************************************************************/
733 /**********************************************************************/
734 Ray *RSL_copy_ray(Ray *r)
738 if (r == NULL) return NULL;
739 new_ray = RSL_new_ray(r->h.nbins);
741 memcpy(new_ray->range, r->range, r->h.nbins*sizeof(Range));
744 Sweep *RSL_copy_sweep(Sweep *s)
749 if (s == NULL) return NULL;
750 n_sweep = RSL_new_sweep(s->h.nrays);
751 if (n_sweep == NULL) return NULL;
754 for (i=0; i<s->h.nrays; i++) {
755 n_sweep->ray[i] = RSL_copy_ray(s->ray[i]);
762 Volume *RSL_copy_volume(Volume *v)
767 if (v == NULL) return NULL;
768 new_vol = RSL_new_volume(v->h.nsweeps);
771 for (i=0; i<v->h.nsweeps; i++) {
772 new_vol->sweep[i] = RSL_copy_sweep(v->sweep[i]);
778 /**********************************************************************/
779 /**********************************************************************/
780 /* G E N E R A L F U N C T I O N S */
781 /**********************************************************************/
782 /**********************************************************************/
784 double angle_diff(float x, float y)
787 d = fabs((double)(x - y));
788 if (d > 180) d = 360 - d;
792 /**********************************************************************/
794 /* RSL_get_next_cwise_ray */
795 /* Dennis Flanigan */
796 /* Mods by John Merritt 10/20/95 */
797 /**********************************************************************/
798 Ray *RSL_get_next_cwise_ray(Sweep *s, Ray *ray)
800 /* The fastest way to do this is to gain access to the hash table
801 * which maintains a linked list of sorted rays.
803 Hash_table *hash_table;
804 Azimuth_hash *closest;
808 if (s == NULL) return NULL;
809 if (ray == NULL) return NULL;
810 /* Find a non-NULL index close to hindex that we want. */
811 hash_table = hash_table_for_sweep(s);
812 if (hash_table == NULL) return NULL; /* Nada. */
813 ray_angle = ray->h.azimuth;
814 hindex = hash_bin(hash_table,ray_angle);
816 /* Find hash entry with closest Ray */
817 closest = the_closest_hash(hash_table->indexes[hindex],ray_angle);
819 return closest->ray_high->ray;
822 /**********************************************************************/
824 /* RSL_get_next_ccwise_ray */
826 /**********************************************************************/
827 Ray *RSL_get_next_ccwise_ray(Sweep *s, Ray *ray)
829 /* The fastest way to do this is to gain access to the hash table
830 * which maintains a linked list of sorted rays.
832 Hash_table *hash_table;
833 Azimuth_hash *closest;
837 if (s == NULL) return NULL;
838 if (ray == NULL) return NULL;
839 /* Find a non-NULL index close to hindex that we want. */
840 hash_table = hash_table_for_sweep(s);
841 if (hash_table == NULL) return NULL; /* Nada. */
842 ray_angle = ray->h.azimuth;
843 hindex = hash_bin(hash_table,ray_angle);
845 /* Find hash entry with closest Ray */
846 closest = the_closest_hash(hash_table->indexes[hindex],ray_angle);
848 return closest->ray_low->ray;
852 /******************************************
856 * Dennis Flanigan,Jr. 5/17/95 *
857 ******************************************/
858 double cwise_angle_diff(float x,float y)
860 /* Returns the clockwise angle difference of x to y.
861 * If x = 345 and y = 355 return 10.
862 * If x = 345 and y = 335 return 350
871 /******************************************
873 * ccwise_angle_diff *
875 * Dennis Flanigan,Jr. 5/17/95 *
876 ******************************************/
877 double ccwise_angle_diff(float x,float y)
879 /* Returns the counterclockwise angle differnce of x to y.
880 * If x = 345 and y = 355 return 350.
881 * If x = 345 and y = 335 return 10
890 /*****************************************
894 * Dennis Flanigan,Jr. 4/29/95 *
895 *****************************************/
896 Azimuth_hash *the_closest_hash(Azimuth_hash *hash, float ray_angle)
898 /* Return the hash pointer with the minimum ray angle difference. */
900 double clow,chigh,cclow;
901 Azimuth_hash *high,*low;
903 if (hash == NULL) return NULL;
905 /* Set low pointer to hash index with ray angle just below
906 * requested angle and high pointer to just above requested
910 /* set low and high pointers to initial search locations*/
912 high = hash->ray_high;
914 /* Search until clockwise angle to high is less then clockwise
918 clow = cwise_angle_diff(ray_angle,low->ray->h.azimuth);
919 chigh = cwise_angle_diff(ray_angle,high->ray->h.azimuth);
920 cclow = ccwise_angle_diff(ray_angle,low->ray->h.azimuth);
922 while((chigh > clow) && (clow != 0))
927 high = low->ray_high; /* Not the same low as line before ! */
932 high = low->ray_high; /* Not the same low as line before ! */
935 clow = cwise_angle_diff(ray_angle,low->ray->h.azimuth);
936 chigh = cwise_angle_diff(ray_angle,high->ray->h.azimuth);
937 cclow = ccwise_angle_diff(ray_angle,low->ray->h.azimuth);
951 /*******************************************************************/
953 /* get_closest_sweep_index */
955 /* Dennis Flanigan, Jr. 5/15/95 */
956 /*******************************************************************/
957 int get_closest_sweep_index(Volume *v,float sweep_angle)
961 float delta_angle = 91;
964 if(v == NULL) return -1;
968 for (i=0; i<v->h.nsweeps; i++)
971 if (s == NULL) continue;
972 check_angle = fabs((double)(s->h.elev - sweep_angle));
974 if(check_angle <= delta_angle)
976 delta_angle = check_angle;
988 /********************************************************************/
990 /* RSL_get_closest_sweep */
992 /* Dennis Flanigan, Jr. 5/15/95 */
993 /********************************************************************/
994 Sweep *RSL_get_closest_sweep(Volume *v,float sweep_angle,float limit)
996 /* Find closest sweep to requested angle. Assume PPI sweep for
997 * now. Meaning: sweep_angle represents elevation angle from
1004 if (v == NULL) return NULL;
1006 if((ci = get_closest_sweep_index(v,sweep_angle)) < 0)
1013 delta_angle = fabs((double)(s->h.elev - sweep_angle));
1015 if( delta_angle <= limit)
1025 /**********************************************************************/
1026 /* These are more specific routines to make coding hierarchical. */
1028 /* done 4/7/95 Ray *RSL_get_ray_from_sweep */
1029 /* done 3/31 float RSL_get_value_from_sweep */
1030 /* done 3/31 float RSL_get_value_from_ray */
1031 /* done 4/1 float RSL_get_value_at_h */
1033 /**********************************************************************/
1034 Ray *RSL_get_ray_from_sweep(Sweep *s, float ray_angle)
1036 /* Locate the Ray * for ray_angle in the sweep. */
1038 /* Sanity checks. */
1039 if (s == NULL) return NULL;
1040 if (ray_angle < 0) ray_angle += 360.0; /* Only positive angles. */
1041 if (ray_angle >= 360) ray_angle -= 360;
1043 return RSL_get_closest_ray_from_sweep(s,ray_angle,s->h.horz_half_bw);
1046 /**********************************************
1050 * Dennis Flanigan, Jr. 4/27/95 *
1051 **********************************************/
1052 int hash_bin(Hash_table *table,float angle)
1054 /* Internal Routine to calculate the hashing bin index
1060 res = 360.0/table->nindexes;
1061 hash = (int)(angle/res + res/2.0);/*Centered about bin.*/
1063 if(hash >= table->nindexes) hash = hash - table->nindexes;
1065 /* Could test see which direction is closer, but
1068 while(table->indexes[hash] == NULL) {
1070 if(hash >= table->nindexes) hash = 0;
1076 Hash_table *hash_table_for_sweep(Sweep *s)
1081 if (i==-1) { /* Obviously, an unregistered sweep. Most likely the
1082 * result of pointer assignments.
1084 i = INSERT_SWEEP(s);
1087 if (RSL_sweep_list[i].hash == NULL) { /* First time. Construct the table. */
1088 RSL_sweep_list[i].hash = construct_sweep_hash_table(s);
1091 return RSL_sweep_list[i].hash;
1094 /*********************************************************************/
1096 /* RSL_get_closest_ray_from_sweep */
1098 /* Dennis Flanigan 4/30/95 */
1099 /*********************************************************************/
1100 Ray *RSL_get_closest_ray_from_sweep(Sweep *s,float ray_angle, float limit)
1103 * Return closest Ray in Sweep within limit (angle) specified
1104 * in parameter list. Assume PPI mode.
1107 Hash_table *hash_table;
1108 Azimuth_hash *closest;
1111 if (s == NULL) return NULL;
1112 /* Find a non-NULL index close to hindex that we want. */
1113 hash_table = hash_table_for_sweep(s);
1114 if (hash_table == NULL) return NULL; /* Nada. */
1116 hindex = hash_bin(hash_table,ray_angle);
1118 /* Find hash entry with closest Ray */
1119 closest = the_closest_hash(hash_table->indexes[hindex],ray_angle);
1121 /* Is closest ray within limit parameter ? If
1122 * so return ray, else return NULL.
1125 close_diff = angle_diff(ray_angle,closest->ray->h.azimuth);
1127 if(close_diff <= limit) return closest->ray;
1133 /*********************************************************************/
1135 /* Rsl_get_value_from_sweep */
1137 /*********************************************************************/
1138 float RSL_get_value_from_sweep(Sweep *s, float azim, float r)
1140 /* Locate the polar point (r,azim) in the sweep. */
1142 if (s == NULL) return BADVAL;
1143 ray = RSL_get_ray_from_sweep(s, azim);
1144 if (ray == NULL) return BADVAL;
1145 return RSL_get_value_from_ray(ray, r);
1149 /*********************************************************************/
1151 /* RSL_get_range_of_range_index */
1152 /* D. Flanigan 8/18/95 */
1153 /*********************************************************************/
1154 float RSL_get_range_of_range_index(Ray *ray, int index)
1156 if (ray == NULL) return 0.0;
1157 if (index >= ray->h.nbins) return 0.0;
1158 return ray->h.range_bin1/1000.0 + index*ray->h.gate_size/1000.0;
1162 /************************************/
1163 /* RSL_get_value_from_ray */
1165 /* Updated 4/4/95 D. Flanigan */
1167 /************************************/
1168 float RSL_get_value_from_ray(Ray *ray, float r)
1175 if (ray == NULL) return BADVAL;
1177 if(ray->h.gate_size == 0)
1179 if(radar_verbose_flag)
1181 fprintf(stderr,"RSL_get_value_from_ray: ray->h.gate_size == 0\n");
1186 /* range_bin1 is range to center of first bin */
1187 bin_index = (int)(((rm - ray->h.range_bin1)/ray->h.gate_size) + 0.5);
1189 /* Bin indexes go from 0 to nbins - 1 */
1190 if (bin_index >= ray->h.nbins || bin_index < 0) return BADVAL;
1192 return ray->h.f(ray->range[bin_index]);
1196 /*********************************************************************/
1198 /* RSL_get_value_at_h */
1200 /*********************************************************************/
1201 float RSL_get_value_at_h(Volume *v, float azim, float grnd_r, float h)
1205 RSL_get_slantr_and_elev(grnd_r, h, &r, &elev);
1206 return RSL_get_value(v, elev, azim, r);
1210 /**********************************************************************/
1211 /* These take a Volume and return the appropriate structure. */
1213 /* done 4/21/95 Sweep *RSL_get_sweep */
1214 /* done 4/1 Ray *RSL_get_ray */
1215 /* done 4/1 float *RSL_get_value */
1216 /* done 5/3 Ray *RSL_get_ray_above */
1217 /* done 5/3 Ray *RSL_get_ray_below */
1218 /* done 5/12 Ray *RSL_get_ray_from_other_volume */
1220 /**********************************************************************/
1224 /*********************************************************************/
1228 /* Updated 5/15/95 Dennis Flanigan, Jr. */
1229 /*********************************************************************/
1230 Sweep *RSL_get_sweep(Volume *v, float sweep_angle)
1232 /* Return a sweep with +/- 1/2 beam_width of 'elev', if found. */
1235 if (v == NULL) return NULL;
1236 while(v->sweep[i] == NULL) i++;
1238 return RSL_get_closest_sweep(v,sweep_angle,v->sweep[i]->h.vert_half_bw);
1242 /*********************************************************************/
1246 /*********************************************************************/
1247 Ray *RSL_get_ray(Volume *v, float elev, float azimuth)
1249 /* Locate 'elev' and 'azimuth' in the Volume v by a simple +/- epsilon on
1250 * the elevation angle and azimuth angle.
1254 * 1. Locate sweep using azimuth; call RSL_get_sweep.
1255 * 2. Call RSL_get_ray_from_sweep
1258 return RSL_get_ray_from_sweep( RSL_get_sweep( v, elev ), azimuth );
1261 /*********************************************************************/
1265 /*********************************************************************/
1266 float RSL_get_value(Volume *v, float elev, float azimuth, float range)
1268 /* Locate 'elev' and 'azimuth' and '<range' in the Volume v
1269 * by a simple +/- epsilon on the elevation angle and azimuth angle
1274 * 1. Locate sweep using 'elev'.
1275 * 2. Call RSL_get_value_from_sweep
1277 return RSL_get_value_from_sweep ( RSL_get_sweep (v, elev), azimuth, range );
1280 /*********************************************************************/
1282 /* RSL_get_ray_above */
1284 /* Updated 5/15/95, Dennis Flanigan, Jr. */
1285 /*********************************************************************/
1286 Ray *RSL_get_ray_above(Volume *v, Ray *current_ray)
1290 if (v == NULL) return NULL;
1291 if (current_ray == NULL) return NULL;
1293 /* Find index of current Sweep */
1294 if(( i = get_closest_sweep_index(v,current_ray->h.elev)) < 0) return NULL;
1297 while( i < v->h.nsweeps)
1299 if(v->sweep[i] != NULL) break;
1303 if(i >= v->h.nsweeps) return NULL;
1305 return RSL_get_ray_from_sweep(v->sweep[i], current_ray->h.azimuth);
1309 /*********************************************************************/
1311 /* RSL_get_ray_below */
1313 /* Updated 5/15/95, Dennis Flanigan, Jr. */
1314 /*********************************************************************/
1315 Ray *RSL_get_ray_below(Volume *v, Ray *current_ray)
1319 if (v == NULL) return NULL;
1320 if (current_ray == NULL) return NULL;
1322 /* Find index of current Sweep */
1323 if(( i = get_closest_sweep_index(v,current_ray->h.elev)) < 0) return NULL;
1328 if(v->sweep[i] != NULL) break;
1332 if(i < 0) return NULL;
1334 return RSL_get_ray_from_sweep(v->sweep[i], current_ray->h.azimuth);
1337 /*********************************************************************/
1339 /* RSL_get_matching_ray */
1341 /*********************************************************************/
1342 Ray *RSL_get_matching_ray(Volume *v, Ray *ray)
1346 * Locate the closest matching ray in the Volume 'v' to 'ray'.
1347 * Typically, use this function when finding a similiar ray in another
1350 if (v == NULL) return NULL;
1351 if (ray == NULL) return NULL;
1353 return RSL_get_ray(v, ray->h.elev, ray->h.azimuth);
1356 /*********************************************************************/
1358 /* RSL_get_first_ray_of_sweep */
1359 /* RSL_get_first_ray_of_volume */
1361 /*********************************************************************/
1362 Ray *RSL_get_first_ray_of_sweep(Sweep *s)
1364 /* Because a sorting of azimuth angles may have been performed,
1365 * we need to test on the ray_num member and look for the smallest
1370 int smallest_ray_num;
1373 smallest_ray_num = 9999999;
1374 if (s == NULL) return r;
1375 for (i=0; i<s->h.nrays; i++)
1377 if (s->ray[i]->h.ray_num <= 1) return s->ray[i];
1378 if (s->ray[i]->h.ray_num < smallest_ray_num) {
1380 smallest_ray_num = r->h.ray_num;
1386 Ray *RSL_get_first_ray_of_volume(Volume *v)
1389 if (v == NULL) return NULL;
1390 for (i=0; i<v->h.nsweeps; i++)
1391 if (v->sweep[i]) return RSL_get_first_ray_of_sweep(v->sweep[i]);
1395 /*********************************************************************/
1397 /* RSL_get_first_sweep_of_volume */
1399 /*********************************************************************/
1400 Sweep *RSL_get_first_sweep_of_volume(Volume *v)
1403 if (v == NULL) return NULL;
1404 for (i=0; i<v->h.nsweeps; i++)
1405 if (RSL_get_first_ray_of_sweep(v->sweep[i])) return v->sweep[i];
1409 #define N_SPECIAL_NAMES 2
1411 * Unfortunately in C, there is no way around initializing static
1412 * arrays by specifying repetition.
1414 * There is another solution and that is to have RSL_new_radar set
1415 * a flag indicating if the application has called 'RSL_select_fields'
1416 * prior to calling the ingest routine. I choose the static = {...}; method
1420 /* Could be static and force use of 'rsl_query_field' */
1421 int rsl_qfield[MAX_RADAR_VOLUMES] = {
1434 /*********************************************************************/
1436 /* RSL_select_fields */
1438 /*********************************************************************/
1439 void RSL_select_fields(char *field_type, ...)
1443 * field_type = Case insensitive:
1444 * "all" - default, if never this routine is never called.
1445 * "none" - No fields are ingestd. Useful for getting header
1447 * "dz" - Ingest DZ volume.
1448 * "vr" - Ingest VR volume.
1449 * ... - Just list additional fields.
1451 * The last argument must be NULL. This signals this routine
1452 * when to stop parsing the field types.
1454 * Action or side-effect:
1455 * A second call to this fuction overrides any previous settings.
1456 * In other words, multiple calls are not additive. So, to get both
1457 * DZ and VR volumes, use:
1458 * RSL_select_fields("dz", "vr"); - Read both DZ and VR.
1460 * RSL_select_fields("dz"); - Read only DZ.
1461 * RSL_select_fields("vr"); - Read only VR, no DZ.
1463 * An RSL hidden array is set to flag which fields are selected.
1464 * This array is examined inside all ingest code. It is not available
1465 * to the application.
1472 for (i=0; i<MAX_RADAR_VOLUMES; i++) rsl_qfield[i] = 0;
1474 /* # arguments, should be <= MAX_RADAR_VOLUMES, but we can handle
1475 * typo's and redundancies. Each is processed in the order they
1479 c_field = field_type;
1480 va_start(ap, field_type);
1482 if (radar_verbose_flag) fprintf(stderr,"Selected fields for ingest:");
1484 /* CHECK EACH FIELD. This is a fancier case statement than C provides. */
1485 if (radar_verbose_flag) fprintf(stderr," %s", c_field);
1486 if (strcasecmp(c_field, "all") == 0) {
1487 for (i=0; i<MAX_RADAR_VOLUMES; i++) rsl_qfield[i] = 1;
1488 } else if (strcasecmp(c_field, "none") == 0) {
1489 for (i=0; i<MAX_RADAR_VOLUMES; i++) rsl_qfield[i] = 0;
1492 for (i=0; i<MAX_RADAR_VOLUMES; i++)
1493 if (strcasecmp(c_field, RSL_ftype[i]) == 0) {
1495 break; /* Break the for loop. */
1498 if (i == MAX_RADAR_VOLUMES) {
1499 if (radar_verbose_flag)
1500 fprintf(stderr, "\nRSL_select_fields: Invalid field name <<%s>> specified.\n", c_field);
1503 c_field = va_arg(ap, char *);
1506 if (radar_verbose_flag) fprintf(stderr,"\n");
1512 int rsl_query_field(char *c_field)
1516 * RSL interface, for library code developers, to rsl ingest code,
1517 * which is intended to be part of RSL ingest code, which access
1518 * the hidden array 'rsl_qfield' and reports if that field is to
1521 * Return 1 if YES, meaning yes ingest this field type.
1526 * All ingest code is meant to use this routine to decide whether
1527 * or not to allocate memory for a field type. For data formats
1528 * that are very large, this will help optimize the ingest on
1529 * small memory machines and hopefully avoid unnessary swapping.
1531 * LASSEN is a good example where there may be 10 or 12 input field
1532 * types, but the application only wants 2 or 3 of them.
1534 * The application interface is RSL_select_fields.
1538 /* Quiet the compilier when -pedantic. :-) */
1539 RSL_f_list[0] = RSL_f_list[0];
1540 RSL_invf_list[0] = RSL_invf_list[0];
1542 for (i=0; i<MAX_RADAR_VOLUMES; i++)
1543 if (strcasecmp(c_field, RSL_ftype[i]) == 0) {
1545 break; /* Break the for loop. */
1548 if (i == MAX_RADAR_VOLUMES) { /* We should never see this message for
1549 * properly written ingest code.
1551 fprintf(stderr, "rsl_query_field: Invalid field name <<%s>> specified.\n", c_field);
1554 /* 'i' is the index. Is it set? */
1555 return rsl_qfield[i];
1559 /* Could be static and force use of 'rsl_query_sweep' */
1560 int *rsl_qsweep = NULL; /* If NULL, then read all sweeps. Otherwise,
1561 * read what is on the list.
1563 #define RSL_MAX_QSWEEP 500 /* It'll be rediculious to have more. :-) */
1564 int rsl_qsweep_max = RSL_MAX_QSWEEP;
1566 /*********************************************************************/
1568 /* RSL_read_these_sweeps */
1570 /*********************************************************************/
1571 void RSL_read_these_sweeps(char *csweep, ...)
1577 /* "all", "none", "0", "1", "2", "3", ... */
1579 /* # arguments, should be <= 'max # sweeps expected', but, what is it?
1580 * We can handle typo's and redundancies. Each is processed in the
1581 * order they appear.
1585 va_start(ap, csweep);
1587 rsl_qsweep_max = -1;
1588 if (rsl_qsweep == NULL)
1589 rsl_qsweep = (int *)calloc(RSL_MAX_QSWEEP, sizeof(int));
1591 /* else Clear the array - a second call to this function over-rides
1592 * any previous settings. This holds even if the second call has
1596 for(i = 0;i< RSL_MAX_QSWEEP; i++)
1600 if (radar_verbose_flag) fprintf(stderr,"Selected sweeps for ingest:");
1601 for (;c_sweep; c_sweep = va_arg(ap, char *))
1603 /* CHECK EACH FIELD. This is a fancier case statement than C provides. */
1604 if (radar_verbose_flag) fprintf(stderr," %s", c_sweep);
1605 if (strcasecmp(c_sweep, "all") == 0) {
1606 for (i=0; i<RSL_MAX_QSWEEP; i++) rsl_qsweep[i] = 1;
1607 rsl_qsweep_max = RSL_MAX_QSWEEP;
1608 } else if (strcasecmp(c_sweep, "none") == 0) {
1609 /* Commented this out to save runtime -GJW
1610 * rsl_qsweep[] already initialized to 0 above.
1612 * for (i=0; i<RSL_MAX_QSWEEP; i++) rsl_qsweep[i] = 0;
1613 * rsl_qsweep_max = -1;
1616 i = sscanf(c_sweep,"%d", &isweep);
1617 if (i == 0) { /* No match, bad argument. */
1618 if (radar_verbose_flag) fprintf(stderr,"\nRSL_read_these_sweeps: bad parameter %s. Ignoring.\n", c_sweep);
1622 if (isweep < 0 || isweep > RSL_MAX_QSWEEP) {
1623 if (radar_verbose_flag) fprintf(stderr,"\nRSL_read_these_sweeps: parameter %s not in [0,%d). Ignoring.\n", c_sweep, RSL_MAX_QSWEEP);
1627 if (isweep > rsl_qsweep_max) rsl_qsweep_max = isweep;
1628 rsl_qsweep[isweep] = 1;
1632 if (radar_verbose_flag) fprintf(stderr,"\n");
1637 void RSL_fix_time (Ray *ray)
1641 /* Fixes possible overflow values in month, day, year, hh, mm, ss */
1642 /* Normally, ss should be the overflow. This code ensures end of
1643 * month, year and century are handled correctly by using the Unix
1646 if (ray == NULL) return;
1647 memset(&the_time, 0, sizeof(struct tm));
1648 the_time.tm_sec = ray->h.sec;
1649 fsec = ray->h.sec - the_time.tm_sec;
1650 the_time.tm_min = ray->h.minute;
1651 the_time.tm_hour = ray->h.hour;
1652 the_time.tm_mon = ray->h.month - 1;
1653 the_time.tm_year = ray->h.year - 1900;
1654 the_time.tm_mday = ray->h.day;
1655 the_time.tm_isdst = -1;
1656 (void) mktime(&the_time);
1657 /* The time is fixed. */
1658 ray->h.sec = the_time.tm_sec;
1660 ray->h.minute = the_time.tm_min;
1661 ray->h.hour = the_time.tm_hour;
1662 ray->h.month = the_time.tm_mon + 1;
1663 ray->h.year = the_time.tm_year + 1900;
1664 ray->h.day = the_time.tm_mday;
1668 /*********************************************************************/
1670 /* RSL_add_dbz_offset_to_ray */
1672 /*********************************************************************/
1674 Add the calibration factor 'dbz_offset' to each ray bin which
1675 contains a valid value.
1677 void RSL_add_dbz_offset_to_ray(Ray *r, float dbz_offset)
1682 if (r == NULL) return;
1683 for (ibin=0; ibin<r->h.nbins; ibin++)
1685 val = r->h.f(r->range[ibin]);
1686 if ( val >= (float)NOECHO ) continue; /* Invalid value */
1687 r->range[ibin] = r->h.invf(val + dbz_offset);
1691 /*********************************************************************/
1693 /* RSL_add_dbz_offset_to_sweep */
1695 /*********************************************************************/
1696 void RSL_add_dbz_offset_to_sweep(Sweep *s, float dbz_offset)
1699 if (s == NULL) return;
1700 for (iray=0; iray<s->h.nrays; iray++)
1701 RSL_add_dbz_offset_to_ray(s->ray[iray], dbz_offset);
1704 /*********************************************************************/
1706 /* RSL_add_dbz_offset_to_volume */
1708 /*********************************************************************/
1709 void RSL_add_dbz_offset_to_volume(Volume *v, float dbz_offset)
1712 if (v == NULL) return;
1713 for (isweep=0; isweep<v->h.nsweeps; isweep++)
1714 RSL_add_dbz_offset_to_sweep(v->sweep[isweep], dbz_offset);