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 /* Modification to properly convert DM field (DEK 21 Nov 2012) */
93 #ifdef USE_TWO_BYTE_PRECISION
94 #define F_FACTOR 100.0
95 #define F_DR_FACTOR 1000.0
96 #define F_DZ_RANGE_OFFSET 50
97 #define F_DM_RANGE_OFFSET 130 /* DEK */
100 #define F_DR_FACTOR 10.0
101 #define F_DZ_RANGE_OFFSET 32
104 /* #define F_VR_OFFSET 63.5 */
105 #define F_VR_OFFSET 127.0
106 #define F_DR_OFFSET 12.0
108 /* IMPORTANT: This is the offset from reserved values. This
109 * number must be exactly (or >=) the number of
110 * reserved values in XX_F and XX_INVF.
112 * You must change nsig_to_radar.c where F_OFFSET is used for optimization.
116 char *RSL_ftype[] = {"DZ", "VR", "SW", "CZ", "ZT", "DR",
117 "LR", "ZD", "DM", "RH", "PH", "XZ",
118 "CD", "MZ", "MD", "ZE", "VE", "KD",
119 "TI", "DX", "CH", "AH", "CV", "AV",
120 "SQ", "VS", "VL", "VG", "VT", "NP",
121 "HC", "VC", "V2", "S2", "V3", "S3",
122 "CR", "CC", "PR", "SD", "ZZ", "RD"};
124 float (*RSL_f_list[])(Range x) = {DZ_F, VR_F, SW_F, CZ_F, ZT_F, DR_F,
125 LR_F, ZD_F, DM_F, RH_F, PH_F, XZ_F,
126 CD_F, MZ_F, MD_F, ZE_F, VE_F, KD_F,
127 TI_F, DX_F, CH_F, AH_F, CV_F, AV_F,
128 SQ_F, VS_F, VL_F, VG_F, VT_F, NP_F,
129 HC_F, VC_F, VR_F, SW_F, VR_F, SW_F,
130 DZ_F, CZ_F, PH_F, SD_F, DZ_F, DZ_F};
132 Range (*RSL_invf_list[])(float x)
133 = {DZ_INVF, VR_INVF, SW_INVF, CZ_INVF, ZT_INVF, DR_INVF,
134 LR_INVF, ZD_INVF, DM_INVF, RH_INVF, PH_INVF, XZ_INVF,
135 CD_INVF, MZ_INVF, MD_INVF, ZE_INVF, VE_INVF, KD_INVF,
136 TI_INVF, DX_INVF, CH_INVF, AH_INVF, CV_INVF, AV_INVF,
137 SQ_INVF, VS_INVF, VL_INVF, VG_INVF, VT_INVF, NP_INVF,
138 HC_INVF, VC_INVF, VR_INVF, SW_INVF, VR_INVF, SW_INVF,
139 DZ_INVF, CZ_INVF, PH_INVF, SD_INVF, DZ_INVF, DZ_INVF};
141 float DZ_F(Range x) {
142 if (x >= F_OFFSET) /* This test works when Range is unsigned. */
143 return (((float)x-F_OFFSET)/F_FACTOR - F_DZ_RANGE_OFFSET); /* Default wsr88d. */
144 if (x == 0) return BADVAL;
145 if (x == 1) return RFVAL;
146 if (x == 2) return APFLAG;
147 if (x == 3) return NOECHO;
148 return BADVAL; /* Can't get here, but quiets the compiler. */
152 float DM_F(Range x) {
153 if (x >= F_OFFSET) /* This test works when Range is unsigned. */
154 return (((float)x-F_OFFSET)/F_FACTOR - F_DM_RANGE_OFFSET); /* DEK */
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. */
163 float VR_F(Range x) {
165 if (x >= F_OFFSET) { /* This test works when Range is unsigned. */
166 val = (((float)x-F_OFFSET)/F_FACTOR - F_VR_OFFSET); /* Default wsr88d coding. */
167 /* fprintf(stderr, "x=%d, val=%f\n", x, val); */
170 if (x == 0) return BADVAL;
171 if (x == 1) return RFVAL;
172 if (x == 2) return APFLAG;
173 if (x == 3) return NOECHO;
174 return BADVAL; /* Can't get here, but quiets the compiler. */
177 float DR_F(Range x) { /* Differential reflectivity */
179 if (x >= F_OFFSET) { /* This test works when Range is unsigned. */
180 val = (((float)x-F_OFFSET)/F_DR_FACTOR - F_DR_OFFSET);
183 if (x == 0) return BADVAL;
184 if (x == 1) return RFVAL;
185 if (x == 2) return APFLAG;
186 if (x == 3) return NOECHO;
187 return BADVAL; /* Can't get here, but quiets the compiler. */
190 float LR_F(Range x) {/* From MCTEX */
191 if (x >= F_OFFSET) /* This test works when Range is unsigned. */
192 return (float) (x - 250.)/6.;
193 if (x == 0) return BADVAL;
194 if (x == 1) return RFVAL;
195 if (x == 2) return APFLAG;
196 if (x == 3) return NOECHO;
200 float HC_F(Range x) { /* HydroClass (Sigmet) */
201 if (x == 0) return BADVAL;
205 /****************************
206 Sigmet RhoHV : one_byte values
207 > RohHV = sqrt((N-1)/253)
214 *******************************/
215 float RH_F(Range x) {
216 if (x == 0) return BADVAL;
217 /* return (float)(sqrt((double)((x-1.0)/253.0))); */
218 return (float)x / 100.;
221 /*****************************
222 Sigmet PhiDP : one_byte values
223 > PhiDP (mod 180) = 180 * ((N-1)/254)
224 > The range is from 0 to 180 degrees in steps of 0.71 as follows.
230 ******************************/
231 float PH_F(Range x) {
232 if (x == 0) return BADVAL;
233 /*return (float)(180.0*((x-1.0)/254.0));*/
237 /* TODO: Should this be 5. cm. instead of 0.5? Or maybe 10. cm.? */
238 float rsl_kdp_wavelen = 0.5; /* Default radar wavelen = .5 cm. See
239 * nsig_to_radar.c for the code that sets this.
241 /* KD_F for 1 or 2 byte. */
244 if (x == 0) return BADVAL;
248 /* Normalized Coherent Power (DORADE) */
251 if (x == 0) return BADVAL;
252 return (float)(x - 1) / 100.;
255 /* Standard Deviation (for Dual-pole QC testing.) */
258 if (x == 0) return BADVAL;
259 return (float)x / 100.;
262 /* Signal Quality Index */
265 if (x == 0) return BADVAL;
266 return (float)(x-1) / 65533.;
271 if (x >= F_OFFSET) return (float)(x);
272 if (x == 0) return BADVAL;
273 if (x == 1) return RFVAL;
274 if (x == 2) return APFLAG;
275 if (x == 3) return NOECHO;
279 float SW_F(Range x) { return VR_F(x); }
280 float CZ_F(Range x) { return DZ_F(x); }
281 float ZT_F(Range x) { return DZ_F(x); }
282 float ZD_F(Range x) { return DR_F(x); } /* Differential reflectivity */
283 float CD_F(Range x) { return DR_F(x); } /* Differential reflectivity */
284 float XZ_F(Range x) { return DZ_F(x); }
285 float MZ_F(Range x) { return (float)x; } /* DZ Mask */
286 float MD_F(Range x) { return MZ_F(x); } /* ZD Mask */
287 float ZE_F(Range x) { return DZ_F(x); }
288 float VE_F(Range x) { return VR_F(x); }
289 /* float DM_F(Range x) { return DZ_F(x); } DEK */
290 float DX_F(Range x) { return DZ_F(x); }
291 float CH_F(Range x) { return DZ_F(x); }
292 float AH_F(Range x) { return DZ_F(x); }
293 float CV_F(Range x) { return DZ_F(x); }
294 float AV_F(Range x) { return DZ_F(x); }
295 float VS_F(Range x) { return VR_F(x); }
296 float VL_F(Range x) { return VR_F(x); }
297 float VG_F(Range x) { return VR_F(x); }
298 float VT_F(Range x) { return VR_F(x); }
299 float VC_F(Range x) { return VR_F(x); }
304 /* Unfortunately, floats are stored differently than ints/shorts. So,
305 * we cannot simply set up a switch statement and we must test for
306 * all the special cases first. We must test for exactness.
308 Range DZ_INVF(float x)
310 if (x == BADVAL) return (Range)0;
311 if (x == RFVAL) return (Range)1;
312 if (x == APFLAG) return (Range)2;
313 if (x == NOECHO) return (Range)3;
314 if (x < -F_DZ_RANGE_OFFSET) return (Range)0;
315 return (Range)(F_FACTOR*(x+F_DZ_RANGE_OFFSET)+.5 + F_OFFSET); /* Default wsr88d. */
318 Range DM_INVF(float x)
320 if (x == BADVAL) return (Range)0;
321 if (x == RFVAL) return (Range)1;
322 if (x == APFLAG) return (Range)2;
323 if (x == NOECHO) return (Range)3;
324 return (Range)(F_FACTOR*(x+F_DM_RANGE_OFFSET)+.5 + F_OFFSET); /* DEK */
328 Range VR_INVF(float x)
330 if (x == BADVAL) return (Range)0;
331 if (x == RFVAL) return (Range)1;
332 if (x == APFLAG) return (Range)2;
333 if (x == NOECHO) return (Range)3;
334 if (x < -F_VR_OFFSET) return (Range)0;
335 return (Range)(F_FACTOR*(x+F_VR_OFFSET)+.5 + F_OFFSET); /* Default wsr88d coding. */
338 Range DR_INVF(float x) /* Differential reflectivity */
340 if (x == BADVAL) return (Range)0;
341 if (x == RFVAL) return (Range)1;
342 if (x == APFLAG) return (Range)2;
343 if (x == NOECHO) return (Range)3;
344 if (x < -F_DR_OFFSET) return (Range)0;
345 return (Range)(F_DR_FACTOR*(x + F_DR_OFFSET) + F_OFFSET + 0.5);
348 Range HC_INVF(float x) /* HydroClass (Sigmet) */
350 if (x == BADVAL) return (Range)0;
351 return (Range)(x + 0.5); /* Round */
354 Range LR_INVF(float x) /* MCTEX */
356 if (x == BADVAL) return (Range)0;
357 if (x == RFVAL) return (Range)1;
358 if (x == APFLAG) return (Range)2;
359 if (x == NOECHO) return (Range)3;
360 return (Range)((6.*x + 250) + 0.5); /* Round */
363 /**************************
364 Sigmet RhoHV : one_byte values
365 > RohHV = sqrt((N-1)/253)
372 ****************************/
373 /* RH_INVF for 1 or 2 byte data. */
374 Range RH_INVF(float x) {
375 if (x == BADVAL) return (Range)0;
376 /* return (Range)(x * x * 253.0 + 1.0 + 0.5); */
377 return (Range)(x * 100. +.5);
380 /******************************
381 Sigmet PhiDP : one_byte values
382 > PhiDP (mod 180) = 180 * ((N-1)/254)
383 > The range is from 0 to 180 degrees in steps of 0.71 as follows.
389 *******************************/
390 Range PH_INVF(float x) {
391 if (x == BADVAL) return (Range)0;
392 /* return (Range)((x / 180.0) * 254.0 + 1.0 + 0.5); */
393 return (Range)(x*10.+ 0.5);
397 /* KD_INVF for 1 or 2 byte data. */
398 Range KD_INVF(float x) {
399 if (x == BADVAL) return (Range)0;
400 return (Range)(x * 100. + 0.5);
403 /* Standard Deviation (for Dual-pole QC testing.) */
404 Range SD_INVF(float x)
406 if (x == BADVAL) return (Range)0;
407 return (Range)(x * 100.);
410 /* Signal Quality Index */
411 Range SQ_INVF(float x)
413 if (x == BADVAL) return (Range)0;
414 return (Range)(x * 65533. + 1. +.5);
417 /* Normalized Coherent Power (DORADE) */
418 Range NP_INVF(float x)
420 if (x == BADVAL) return (0);
421 return (Range)(x * 100. + 1.);
424 Range TI_INVF(float x) /* MCTEX */
426 if (x == BADVAL) return (Range)0;
427 if (x == RFVAL) return (Range)1;
428 if (x == APFLAG) return (Range)2;
429 if (x == NOECHO) return (Range)3;
434 Range SW_INVF(float x) { return VR_INVF(x); }
435 Range CZ_INVF(float x) { return DZ_INVF(x); }
436 Range ZT_INVF(float x) { return DZ_INVF(x); }
437 Range ZD_INVF(float x) { return DR_INVF(x); } /* Differential reflectivity */
438 Range CD_INVF(float x) { return DR_INVF(x); } /* Differential reflectivity */
439 Range XZ_INVF(float x) { return DZ_INVF(x); }
440 Range MZ_INVF(float x) { return (Range)x; } /* DZ Mask */
441 Range MD_INVF(float x) { return MZ_INVF(x); } /* ZD Mask */
442 Range ZE_INVF(float x) { return DZ_INVF(x); }
443 Range VE_INVF(float x) { return VR_INVF(x); }
444 /* Range DM_INVF(float x) { return DZ_INVF(x); } DEK */
445 Range DX_INVF(float x) { return DZ_INVF(x); }
446 Range CH_INVF(float x) { return DZ_INVF(x); }
447 Range AH_INVF(float x) { return DZ_INVF(x); }
448 Range CV_INVF(float x) { return DZ_INVF(x); }
449 Range AV_INVF(float x) { return DZ_INVF(x); }
450 Range VS_INVF(float x) { return VR_INVF(x); }
451 Range VL_INVF(float x) { return VR_INVF(x); }
452 Range VG_INVF(float x) { return VR_INVF(x); }
453 Range VT_INVF(float x) { return VR_INVF(x); }
454 Range VC_INVF(float x) { return VR_INVF(x); }
458 /**********************************************************************/
459 /* M E M O R Y M A N A G E M E N T R O U T I N E S */
460 /**********************************************************************/
461 /**********************************************************************/
467 /**********************************************************************/
468 Volume *RSL_new_volume(int max_sweeps)
471 * A volume consists of a header section and an array of sweeps.
474 v = (Volume *)calloc(1, sizeof(Volume));
475 if (v == NULL) perror("RSL_new_volume");
476 v->sweep = (Sweep **) calloc(max_sweeps, sizeof(Sweep*));
477 if (v->sweep == NULL) perror("RSL_new_volume, Sweep*");
478 v->h.nsweeps = max_sweeps; /* A default setting. */
483 * The 'Sweep_list' structure is internal to RSL. It maintains a list
484 * of sweeps allocated and it contains pointers to a hash table of Rays
485 * separately for each sweep. There is no reason to access this internal
486 * structure except when optimizing new RSL routines that access Rays.
487 * Otherwise, the RSL interfaces should suffice.
489 * The hash table is a means of finding rays, by azimuth, quickly.
490 * To find a ray is simple: use the hash function to get close
491 * to the ray, if not right on it the first time. Collisions of rays in
492 * the hash table are handled by a link list of rays from a hash entry.
493 * Typically, the first ray of the sweep is not the ray with the smallest
494 * azimuth angle. We are confident that the order of Rays in the Sweep
495 * is by azimuth angle, but that cannot be guarenteed. Therefore, this
496 * hash scheme is required.
498 * The 'Sweep_list' contains the address of the memory allocated to
499 * sweep. The list is sorted by addresses. There is no
500 * memory limit to the number of sweeps. If the number of sweeps exceeds
501 * the current allocation for the Sweep_list, then a new Sweep_list is
502 * allocated, which is bigger, and the old list copied to it.
504 * Sweep_list is at least as long as the number of sweeps allocated.
513 * By design of RSL, this should be "#define STATIC static"
515 * It is OK to "#define STATIC static", but, if you do, then
516 * the examples (run by run_tests in examples/) will fail for
517 * those programs that test these data structures. I normally,
518 * don't set this #define, for that reason.
522 STATIC int RSL_max_sweeps = 0; /* Initial allocation for sweep_list.
523 * RSL_new_sweep will allocate the space first
526 STATIC int RSL_nsweep_addr = 0; /* A count of sweeps in the table. */
527 STATIC Sweep_list *RSL_sweep_list = NULL;
528 STATIC int RSL_nextents = 0;
530 void FREE_HASH_NODE(Azimuth_hash *node)
532 if (node == NULL) return;
533 FREE_HASH_NODE(node->next); /* Tail recursive link list removal. */
537 void FREE_HASH_TABLE(Hash_table *table)
540 if (table == NULL) return;
541 for (i=0; i<table->nindexes; i++)
542 FREE_HASH_NODE(table->indexes[i]); /* A possible linked list of Rays. */
543 free(table->indexes);
547 void REMOVE_SWEEP(Sweep *s)
551 /* Find where it goes, split the list and slide the tail down one. */
552 for (i=0; i<RSL_nsweep_addr; i++)
553 if (s == RSL_sweep_list[i].s_addr) break;
555 if (i == RSL_nsweep_addr) return; /* Not found. */
556 /* This sweep is at 'i'. */
557 /* Deallocate the memory for the hash table. */
558 FREE_HASH_TABLE(RSL_sweep_list[i].hash);
561 for (j=i; j<RSL_nsweep_addr; j++)
562 RSL_sweep_list[j] = RSL_sweep_list[j+1];
564 RSL_sweep_list[RSL_nsweep_addr].s_addr = NULL;
565 RSL_sweep_list[RSL_nsweep_addr].hash = NULL;
569 int INSERT_SWEEP(Sweep *s)
571 Sweep_list *new_list;
574 if (RSL_nsweep_addr >= RSL_max_sweeps) { /* Current list is too small. */
576 new_list = (Sweep_list *) calloc(100*RSL_nextents, sizeof(Sweep_list));
577 if (new_list == NULL) {
578 perror("INSERT_SWEEP");
581 /* Copy the old list to the new one. */
582 for (i=0; i<RSL_max_sweeps; i++) new_list[i] = RSL_sweep_list[i];
583 RSL_max_sweeps = 100*RSL_nextents;
584 free(RSL_sweep_list);
585 RSL_sweep_list = new_list;
587 /* Find where it goes, split the list and slide the tail down one. */
588 for (i=0; i<RSL_nsweep_addr; i++)
589 if (s < RSL_sweep_list[i].s_addr) break;
591 /* This sweep goes at 'i'. But first we must split the list. */
592 for (j=RSL_nsweep_addr; j>i; j--)
593 RSL_sweep_list[j] = RSL_sweep_list[j-1];
595 RSL_sweep_list[i].s_addr = s;
596 RSL_sweep_list[i].hash = NULL;
601 int SWEEP_INDEX(Sweep *s)
603 /* Locate the sweep in the RSL_sweep_list. Return the index. */
604 /* Simple linear search; but this will be a binary search. */
606 for (i=0; i<RSL_nsweep_addr; i++)
607 if (s == RSL_sweep_list[i].s_addr) return i;
611 Sweep *RSL_new_sweep(int max_rays)
614 * A sweep consists of a header section and an array of rays.
617 s = (Sweep *)calloc(1, sizeof(Sweep));
618 if (s == NULL) perror("RSL_new_sweep");
620 s->ray = (Ray **) calloc(max_rays, sizeof(Ray*));
621 if (s->ray == NULL) perror("RSL_new_sweep, Ray*");
622 s->h.nrays = max_rays; /* A default setting. */
624 s->h.azimuth = -999.;
628 Ray *RSL_new_ray(int max_bins)
631 * A ray consists of a header section and an array of Range types (floats).
634 r = (Ray *)calloc(1, sizeof(Ray));
635 if (r == NULL) perror("RSL_new_ray");
636 r->range = (Range *) calloc(max_bins, sizeof(Range));
637 if (r->range == NULL) perror("RSL_new_ray, Range");
638 r->h.nbins = max_bins; /* A default setting. */
639 /* fprintf(stderr,"range[0] = %x, range[%d] = %x\n", &r->range[0], max_bins-1, &r->range[max_bins-1]);*/
643 /**********************************************************************/
649 /**********************************************************************/
650 Ray *RSL_clear_ray(Ray *r)
652 if (r == NULL) return r;
653 memset(r->range, 0, sizeof(Range)*r->h.nbins);
656 Sweep *RSL_clear_sweep(Sweep *s)
659 if (s == NULL) return s;
660 for (i=0; i<s->h.nrays; i++) {
661 RSL_clear_ray(s->ray[i]);
665 Volume *RSL_clear_volume(Volume *v)
668 if (v == NULL) return v;
669 for (i=0; i<v->h.nsweeps; i++) {
670 RSL_clear_sweep(v->sweep[i]);
674 /**********************************************************************/
680 /**********************************************************************/
681 void RSL_free_ray(Ray *r)
683 if (r == NULL) return;
684 if (r->range) free(r->range);
687 void RSL_free_sweep(Sweep *s)
690 if (s == NULL) return;
691 for (i=0; i<s->h.nrays; i++) {
692 RSL_free_ray(s->ray[i]);
694 if (s->ray) free(s->ray);
695 REMOVE_SWEEP(s); /* Remove from internal Sweep list. */
698 void RSL_free_volume(Volume *v)
701 if (v == NULL) return;
703 for (i=0; i<v->h.nsweeps; i++)
705 RSL_free_sweep(v->sweep[i]);
707 if (v->sweep) free(v->sweep);
711 /**********************************************************************/
717 /**********************************************************************/
718 Ray *RSL_copy_ray(Ray *r)
722 if (r == NULL) return NULL;
723 new_ray = RSL_new_ray(r->h.nbins);
725 memcpy(new_ray->range, r->range, r->h.nbins*sizeof(Range));
728 Sweep *RSL_copy_sweep(Sweep *s)
733 if (s == NULL) return NULL;
734 n_sweep = RSL_new_sweep(s->h.nrays);
735 if (n_sweep == NULL) return NULL;
738 for (i=0; i<s->h.nrays; i++) {
739 n_sweep->ray[i] = RSL_copy_ray(s->ray[i]);
746 Volume *RSL_copy_volume(Volume *v)
751 if (v == NULL) return NULL;
752 new_vol = RSL_new_volume(v->h.nsweeps);
755 for (i=0; i<v->h.nsweeps; i++) {
756 new_vol->sweep[i] = RSL_copy_sweep(v->sweep[i]);
762 /**********************************************************************/
763 /**********************************************************************/
764 /* G E N E R A L F U N C T I O N S */
765 /**********************************************************************/
766 /**********************************************************************/
768 double angle_diff(float x, float y)
771 d = fabs((double)(x - y));
772 if (d > 180) d = 360 - d;
776 /**********************************************************************/
778 /* RSL_get_next_cwise_ray */
779 /* Dennis Flanigan */
780 /* Mods by John Merritt 10/20/95 */
781 /**********************************************************************/
782 Ray *RSL_get_next_cwise_ray(Sweep *s, Ray *ray)
784 /* The fastest way to do this is to gain access to the hash table
785 * which maintains a linked list of sorted rays.
787 Hash_table *hash_table;
788 Azimuth_hash *closest;
792 if (s == NULL) return NULL;
793 if (ray == NULL) return NULL;
794 /* Find a non-NULL index close to hindex that we want. */
795 hash_table = hash_table_for_sweep(s);
796 if (hash_table == NULL) return NULL; /* Nada. */
797 ray_angle = ray->h.azimuth;
798 hindex = hash_bin(hash_table,ray_angle);
800 /* Find hash entry with closest Ray */
801 closest = the_closest_hash(hash_table->indexes[hindex],ray_angle);
803 return closest->ray_high->ray;
806 /**********************************************************************/
808 /* RSL_get_next_ccwise_ray */
810 /**********************************************************************/
811 Ray *RSL_get_next_ccwise_ray(Sweep *s, Ray *ray)
813 /* The fastest way to do this is to gain access to the hash table
814 * which maintains a linked list of sorted rays.
816 Hash_table *hash_table;
817 Azimuth_hash *closest;
821 if (s == NULL) return NULL;
822 if (ray == NULL) return NULL;
823 /* Find a non-NULL index close to hindex that we want. */
824 hash_table = hash_table_for_sweep(s);
825 if (hash_table == NULL) return NULL; /* Nada. */
826 ray_angle = ray->h.azimuth;
827 hindex = hash_bin(hash_table,ray_angle);
829 /* Find hash entry with closest Ray */
830 closest = the_closest_hash(hash_table->indexes[hindex],ray_angle);
832 return closest->ray_low->ray;
836 /******************************************
840 * Dennis Flanigan,Jr. 5/17/95 *
841 ******************************************/
842 double cwise_angle_diff(float x,float y)
844 /* Returns the clockwise angle difference of x to y.
845 * If x = 345 and y = 355 return 10.
846 * If x = 345 and y = 335 return 350
855 /******************************************
857 * ccwise_angle_diff *
859 * Dennis Flanigan,Jr. 5/17/95 *
860 ******************************************/
861 double ccwise_angle_diff(float x,float y)
863 /* Returns the counterclockwise angle differnce of x to y.
864 * If x = 345 and y = 355 return 350.
865 * If x = 345 and y = 335 return 10
874 /*****************************************
878 * Dennis Flanigan,Jr. 4/29/95 *
879 *****************************************/
880 Azimuth_hash *the_closest_hash(Azimuth_hash *hash, float ray_angle)
882 /* Return the hash pointer with the minimum ray angle difference. */
884 double clow,chigh,cclow;
885 Azimuth_hash *high,*low;
887 if (hash == NULL) return NULL;
889 /* Set low pointer to hash index with ray angle just below
890 * requested angle and high pointer to just above requested
894 /* set low and high pointers to initial search locations*/
896 high = hash->ray_high;
898 /* Search until clockwise angle to high is less then clockwise
902 clow = cwise_angle_diff(ray_angle,low->ray->h.azimuth);
903 chigh = cwise_angle_diff(ray_angle,high->ray->h.azimuth);
904 cclow = ccwise_angle_diff(ray_angle,low->ray->h.azimuth);
906 while((chigh > clow) && (clow != 0))
911 high = low->ray_high; /* Not the same low as line before ! */
916 high = low->ray_high; /* Not the same low as line before ! */
919 clow = cwise_angle_diff(ray_angle,low->ray->h.azimuth);
920 chigh = cwise_angle_diff(ray_angle,high->ray->h.azimuth);
921 cclow = ccwise_angle_diff(ray_angle,low->ray->h.azimuth);
935 /*******************************************************************/
937 /* get_closest_sweep_index */
939 /* Dennis Flanigan, Jr. 5/15/95 */
940 /*******************************************************************/
941 int get_closest_sweep_index(Volume *v,float sweep_angle)
945 float delta_angle = 91;
948 if(v == NULL) return -1;
952 for (i=0; i<v->h.nsweeps; i++)
955 if (s == NULL) continue;
956 check_angle = fabs((double)(s->h.elev - sweep_angle));
958 if(check_angle <= delta_angle)
960 delta_angle = check_angle;
972 /********************************************************************/
974 /* RSL_get_closest_sweep */
976 /* Dennis Flanigan, Jr. 5/15/95 */
977 /********************************************************************/
978 Sweep *RSL_get_closest_sweep(Volume *v,float sweep_angle,float limit)
980 /* Find closest sweep to requested angle. Assume PPI sweep for
981 * now. Meaning: sweep_angle represents elevation angle from
988 if (v == NULL) return NULL;
990 if((ci = get_closest_sweep_index(v,sweep_angle)) < 0)
997 delta_angle = fabs((double)(s->h.elev - sweep_angle));
999 if( delta_angle <= limit)
1009 /**********************************************************************/
1010 /* These are more specific routines to make coding hierarchical. */
1012 /* done 4/7/95 Ray *RSL_get_ray_from_sweep */
1013 /* done 3/31 float RSL_get_value_from_sweep */
1014 /* done 3/31 float RSL_get_value_from_ray */
1015 /* done 4/1 float RSL_get_value_at_h */
1017 /**********************************************************************/
1018 Ray *RSL_get_ray_from_sweep(Sweep *s, float ray_angle)
1020 /* Locate the Ray * for ray_angle in the sweep. */
1022 /* Sanity checks. */
1023 if (s == NULL) return NULL;
1024 if (ray_angle < 0) ray_angle += 360.0; /* Only positive angles. */
1025 if (ray_angle >= 360) ray_angle -= 360;
1027 return RSL_get_closest_ray_from_sweep(s,ray_angle,s->h.horz_half_bw);
1030 /**********************************************
1034 * Dennis Flanigan, Jr. 4/27/95 *
1035 **********************************************/
1036 int hash_bin(Hash_table *table,float angle)
1038 /* Internal Routine to calculate the hashing bin index
1044 res = 360.0/table->nindexes;
1045 hash = (int)(angle/res + res/2.0);/*Centered about bin.*/
1047 if(hash >= table->nindexes) hash = hash - table->nindexes;
1049 /* Could test see which direction is closer, but
1052 while(table->indexes[hash] == NULL) {
1054 if(hash >= table->nindexes) hash = 0;
1060 Hash_table *hash_table_for_sweep(Sweep *s)
1065 if (i==-1) { /* Obviously, an unregistered sweep. Most likely the
1066 * result of pointer assignments.
1068 i = INSERT_SWEEP(s);
1071 if (RSL_sweep_list[i].hash == NULL) { /* First time. Construct the table. */
1072 RSL_sweep_list[i].hash = construct_sweep_hash_table(s);
1075 return RSL_sweep_list[i].hash;
1078 /*********************************************************************/
1080 /* RSL_get_closest_ray_from_sweep */
1082 /* Dennis Flanigan 4/30/95 */
1083 /*********************************************************************/
1084 Ray *RSL_get_closest_ray_from_sweep(Sweep *s,float ray_angle, float limit)
1087 * Return closest Ray in Sweep within limit (angle) specified
1088 * in parameter list. Assume PPI mode.
1091 Hash_table *hash_table;
1092 Azimuth_hash *closest;
1095 if (s == NULL) return NULL;
1096 /* Find a non-NULL index close to hindex that we want. */
1097 hash_table = hash_table_for_sweep(s);
1098 if (hash_table == NULL) return NULL; /* Nada. */
1100 hindex = hash_bin(hash_table,ray_angle);
1102 /* Find hash entry with closest Ray */
1103 closest = the_closest_hash(hash_table->indexes[hindex],ray_angle);
1105 /* Is closest ray within limit parameter ? If
1106 * so return ray, else return NULL.
1109 close_diff = angle_diff(ray_angle,closest->ray->h.azimuth);
1111 if(close_diff <= limit) return closest->ray;
1117 /*********************************************************************/
1119 /* Rsl_get_value_from_sweep */
1121 /*********************************************************************/
1122 float RSL_get_value_from_sweep(Sweep *s, float azim, float r)
1124 /* Locate the polar point (r,azim) in the sweep. */
1126 if (s == NULL) return BADVAL;
1127 ray = RSL_get_ray_from_sweep(s, azim);
1128 if (ray == NULL) return BADVAL;
1129 return RSL_get_value_from_ray(ray, r);
1133 /*********************************************************************/
1135 /* RSL_get_range_of_range_index */
1136 /* D. Flanigan 8/18/95 */
1137 /*********************************************************************/
1138 float RSL_get_range_of_range_index(Ray *ray, int index)
1140 if (ray == NULL) return 0.0;
1141 if (index >= ray->h.nbins) return 0.0;
1142 return ray->h.range_bin1/1000.0 + index*ray->h.gate_size/1000.0;
1146 /************************************/
1147 /* RSL_get_value_from_ray */
1149 /* Updated 4/4/95 D. Flanigan */
1151 /************************************/
1152 float RSL_get_value_from_ray(Ray *ray, float r)
1159 if (ray == NULL) return BADVAL;
1161 if(ray->h.gate_size == 0)
1163 if(radar_verbose_flag)
1165 fprintf(stderr,"RSL_get_value_from_ray: ray->h.gate_size == 0\n");
1170 /* range_bin1 is range to center of first bin */
1171 bin_index = (int)(((rm - ray->h.range_bin1)/ray->h.gate_size) + 0.5);
1173 /* Bin indexes go from 0 to nbins - 1 */
1174 if (bin_index >= ray->h.nbins || bin_index < 0) return BADVAL;
1176 return ray->h.f(ray->range[bin_index]);
1180 /*********************************************************************/
1182 /* RSL_get_value_at_h */
1184 /*********************************************************************/
1185 float RSL_get_value_at_h(Volume *v, float azim, float grnd_r, float h)
1189 RSL_get_slantr_and_elev(grnd_r, h, &r, &elev);
1190 return RSL_get_value(v, elev, azim, r);
1194 /**********************************************************************/
1195 /* These take a Volume and return the appropriate structure. */
1197 /* done 4/21/95 Sweep *RSL_get_sweep */
1198 /* done 4/1 Ray *RSL_get_ray */
1199 /* done 4/1 float *RSL_get_value */
1200 /* done 5/3 Ray *RSL_get_ray_above */
1201 /* done 5/3 Ray *RSL_get_ray_below */
1202 /* done 5/12 Ray *RSL_get_ray_from_other_volume */
1204 /**********************************************************************/
1208 /*********************************************************************/
1212 /* Updated 5/15/95 Dennis Flanigan, Jr. */
1213 /*********************************************************************/
1214 Sweep *RSL_get_sweep(Volume *v, float sweep_angle)
1216 /* Return a sweep with +/- 1/2 beam_width of 'elev', if found. */
1219 if (v == NULL) return NULL;
1220 while(v->sweep[i] == NULL) i++;
1222 return RSL_get_closest_sweep(v,sweep_angle,v->sweep[i]->h.vert_half_bw);
1226 /*********************************************************************/
1230 /*********************************************************************/
1231 Ray *RSL_get_ray(Volume *v, float elev, float azimuth)
1233 /* Locate 'elev' and 'azimuth' in the Volume v by a simple +/- epsilon on
1234 * the elevation angle and azimuth angle.
1238 * 1. Locate sweep using azimuth; call RSL_get_sweep.
1239 * 2. Call RSL_get_ray_from_sweep
1242 return RSL_get_ray_from_sweep( RSL_get_sweep( v, elev ), azimuth );
1245 /*********************************************************************/
1249 /*********************************************************************/
1250 float RSL_get_value(Volume *v, float elev, float azimuth, float range)
1252 /* Locate 'elev' and 'azimuth' and '<range' in the Volume v
1253 * by a simple +/- epsilon on the elevation angle and azimuth angle
1258 * 1. Locate sweep using 'elev'.
1259 * 2. Call RSL_get_value_from_sweep
1261 return RSL_get_value_from_sweep ( RSL_get_sweep (v, elev), azimuth, range );
1264 /*********************************************************************/
1266 /* RSL_get_ray_above */
1268 /* Updated 5/15/95, Dennis Flanigan, Jr. */
1269 /*********************************************************************/
1270 Ray *RSL_get_ray_above(Volume *v, Ray *current_ray)
1274 if (v == NULL) return NULL;
1275 if (current_ray == NULL) return NULL;
1277 /* Find index of current Sweep */
1278 if(( i = get_closest_sweep_index(v,current_ray->h.elev)) < 0) return NULL;
1281 while( i < v->h.nsweeps)
1283 if(v->sweep[i] != NULL) break;
1287 if(i >= v->h.nsweeps) return NULL;
1289 return RSL_get_ray_from_sweep(v->sweep[i], current_ray->h.azimuth);
1293 /*********************************************************************/
1295 /* RSL_get_ray_below */
1297 /* Updated 5/15/95, Dennis Flanigan, Jr. */
1298 /*********************************************************************/
1299 Ray *RSL_get_ray_below(Volume *v, Ray *current_ray)
1303 if (v == NULL) return NULL;
1304 if (current_ray == NULL) return NULL;
1306 /* Find index of current Sweep */
1307 if(( i = get_closest_sweep_index(v,current_ray->h.elev)) < 0) return NULL;
1312 if(v->sweep[i] != NULL) break;
1316 if(i < 0) return NULL;
1318 return RSL_get_ray_from_sweep(v->sweep[i], current_ray->h.azimuth);
1321 /*********************************************************************/
1323 /* RSL_get_matching_ray */
1325 /*********************************************************************/
1326 Ray *RSL_get_matching_ray(Volume *v, Ray *ray)
1330 * Locate the closest matching ray in the Volume 'v' to 'ray'.
1331 * Typically, use this function when finding a similiar ray in another
1334 if (v == NULL) return NULL;
1335 if (ray == NULL) return NULL;
1337 return RSL_get_ray(v, ray->h.elev, ray->h.azimuth);
1340 /*********************************************************************/
1342 /* RSL_get_first_ray_of_sweep */
1343 /* RSL_get_first_ray_of_volume */
1345 /*********************************************************************/
1346 Ray *RSL_get_first_ray_of_sweep(Sweep *s)
1348 /* Because a sorting of azimuth angles may have been performed,
1349 * we need to test on the ray_num member and look for the smallest
1354 int smallest_ray_num;
1357 smallest_ray_num = 9999999;
1358 if (s == NULL) return r;
1359 for (i=0; i<s->h.nrays; i++)
1361 if (s->ray[i]->h.ray_num <= 1) return s->ray[i];
1362 if (s->ray[i]->h.ray_num < smallest_ray_num) {
1364 smallest_ray_num = r->h.ray_num;
1370 Ray *RSL_get_first_ray_of_volume(Volume *v)
1373 if (v == NULL) return NULL;
1374 for (i=0; i<v->h.nsweeps; i++)
1375 if (v->sweep[i]) return RSL_get_first_ray_of_sweep(v->sweep[i]);
1379 /*********************************************************************/
1381 /* RSL_get_first_sweep_of_volume */
1383 /*********************************************************************/
1384 Sweep *RSL_get_first_sweep_of_volume(Volume *v)
1387 if (v == NULL) return NULL;
1388 for (i=0; i<v->h.nsweeps; i++)
1389 if (RSL_get_first_ray_of_sweep(v->sweep[i])) return v->sweep[i];
1393 #define N_SPECIAL_NAMES 2
1395 * Unfortunately in C, there is no way around initializing static
1396 * arrays by specifying repetition.
1398 * There is another solution and that is to have RSL_new_radar set
1399 * a flag indicating if the application has called 'RSL_select_fields'
1400 * prior to calling the ingest routine. I choose the static = {...}; method
1404 /* Could be static and force use of 'rsl_query_field' */
1405 int rsl_qfield[MAX_RADAR_VOLUMES] = {
1418 /*********************************************************************/
1420 /* RSL_select_fields */
1422 /*********************************************************************/
1423 void RSL_select_fields(char *field_type, ...)
1427 * field_type = Case insensitive:
1428 * "all" - default, if never this routine is never called.
1429 * "none" - No fields are ingestd. Useful for getting header
1431 * "dz" - Ingest DZ volume.
1432 * "vr" - Ingest VR volume.
1433 * ... - Just list additional fields.
1435 * The last argument must be NULL. This signals this routine
1436 * when to stop parsing the field types.
1438 * Action or side-effect:
1439 * A second call to this fuction overrides any previous settings.
1440 * In other words, multiple calls are not additive. So, to get both
1441 * DZ and VR volumes, use:
1442 * RSL_select_fields("dz", "vr"); - Read both DZ and VR.
1444 * RSL_select_fields("dz"); - Read only DZ.
1445 * RSL_select_fields("vr"); - Read only VR, no DZ.
1447 * An RSL hidden array is set to flag which fields are selected.
1448 * This array is examined inside all ingest code. It is not available
1449 * to the application.
1456 for (i=0; i<MAX_RADAR_VOLUMES; i++) rsl_qfield[i] = 0;
1458 /* # arguments, should be <= MAX_RADAR_VOLUMES, but we can handle
1459 * typo's and redundancies. Each is processed in the order they
1463 c_field = field_type;
1464 va_start(ap, field_type);
1466 if (radar_verbose_flag) fprintf(stderr,"Selected fields for ingest:");
1468 /* CHECK EACH FIELD. This is a fancier case statement than C provides. */
1469 if (radar_verbose_flag) fprintf(stderr," %s", c_field);
1470 if (strcasecmp(c_field, "all") == 0) {
1471 for (i=0; i<MAX_RADAR_VOLUMES; i++) rsl_qfield[i] = 1;
1472 } else if (strcasecmp(c_field, "none") == 0) {
1473 for (i=0; i<MAX_RADAR_VOLUMES; i++) rsl_qfield[i] = 0;
1476 for (i=0; i<MAX_RADAR_VOLUMES; i++)
1477 if (strcasecmp(c_field, RSL_ftype[i]) == 0) {
1479 break; /* Break the for loop. */
1482 if (i == MAX_RADAR_VOLUMES) {
1483 if (radar_verbose_flag)
1484 fprintf(stderr, "\nRSL_select_fields: Invalid field name <<%s>> specified.\n", c_field);
1487 c_field = va_arg(ap, char *);
1490 if (radar_verbose_flag) fprintf(stderr,"\n");
1496 int rsl_query_field(char *c_field)
1500 * RSL interface, for library code developers, to rsl ingest code,
1501 * which is intended to be part of RSL ingest code, which access
1502 * the hidden array 'rsl_qfield' and reports if that field is to
1505 * Return 1 if YES, meaning yes ingest this field type.
1510 * All ingest code is meant to use this routine to decide whether
1511 * or not to allocate memory for a field type. For data formats
1512 * that are very large, this will help optimize the ingest on
1513 * small memory machines and hopefully avoid unnessary swapping.
1515 * LASSEN is a good example where there may be 10 or 12 input field
1516 * types, but the application only wants 2 or 3 of them.
1518 * The application interface is RSL_select_fields.
1522 /* Quiet the compilier when -pedantic. :-) */
1523 RSL_f_list[0] = RSL_f_list[0];
1524 RSL_invf_list[0] = RSL_invf_list[0];
1526 for (i=0; i<MAX_RADAR_VOLUMES; i++)
1527 if (strcasecmp(c_field, RSL_ftype[i]) == 0) {
1529 break; /* Break the for loop. */
1532 if (i == MAX_RADAR_VOLUMES) { /* We should never see this message for
1533 * properly written ingest code.
1535 fprintf(stderr, "rsl_query_field: Invalid field name <<%s>> specified.\n", c_field);
1538 /* 'i' is the index. Is it set? */
1539 return rsl_qfield[i];
1543 /* Could be static and force use of 'rsl_query_sweep' */
1544 int *rsl_qsweep = NULL; /* If NULL, then read all sweeps. Otherwise,
1545 * read what is on the list.
1547 #define RSL_MAX_QSWEEP 500 /* It'll be rediculious to have more. :-) */
1548 int rsl_qsweep_max = RSL_MAX_QSWEEP;
1550 /*********************************************************************/
1552 /* RSL_read_these_sweeps */
1554 /*********************************************************************/
1555 void RSL_read_these_sweeps(char *csweep, ...)
1561 /* "all", "none", "0", "1", "2", "3", ... */
1563 /* # arguments, should be <= 'max # sweeps expected', but, what is it?
1564 * We can handle typo's and redundancies. Each is processed in the
1565 * order they appear.
1569 va_start(ap, csweep);
1571 rsl_qsweep_max = -1;
1572 if (rsl_qsweep == NULL)
1573 rsl_qsweep = (int *)calloc(RSL_MAX_QSWEEP, sizeof(int));
1575 /* else Clear the array - a second call to this function over-rides
1576 * any previous settings. This holds even if the second call has
1580 for(i = 0;i< RSL_MAX_QSWEEP; i++)
1584 if (radar_verbose_flag) fprintf(stderr,"Selected sweeps for ingest:");
1585 for (;c_sweep; c_sweep = va_arg(ap, char *))
1587 /* CHECK EACH FIELD. This is a fancier case statement than C provides. */
1588 if (radar_verbose_flag) fprintf(stderr," %s", c_sweep);
1589 if (strcasecmp(c_sweep, "all") == 0) {
1590 for (i=0; i<RSL_MAX_QSWEEP; i++) rsl_qsweep[i] = 1;
1591 rsl_qsweep_max = RSL_MAX_QSWEEP;
1592 } else if (strcasecmp(c_sweep, "none") == 0) {
1593 /* Commented this out to save runtime -GJW
1594 * rsl_qsweep[] already initialized to 0 above.
1596 * for (i=0; i<RSL_MAX_QSWEEP; i++) rsl_qsweep[i] = 0;
1597 * rsl_qsweep_max = -1;
1600 i = sscanf(c_sweep,"%d", &isweep);
1601 if (i == 0) { /* No match, bad argument. */
1602 if (radar_verbose_flag) fprintf(stderr,"\nRSL_read_these_sweeps: bad parameter %s. Ignoring.\n", c_sweep);
1606 if (isweep < 0 || isweep > RSL_MAX_QSWEEP) {
1607 if (radar_verbose_flag) fprintf(stderr,"\nRSL_read_these_sweeps: parameter %s not in [0,%d). Ignoring.\n", c_sweep, RSL_MAX_QSWEEP);
1611 if (isweep > rsl_qsweep_max) rsl_qsweep_max = isweep;
1612 rsl_qsweep[isweep] = 1;
1616 if (radar_verbose_flag) fprintf(stderr,"\n");
1621 void RSL_fix_time (Ray *ray)
1625 /* Fixes possible overflow values in month, day, year, hh, mm, ss */
1626 /* Normally, ss should be the overflow. This code ensures end of
1627 * month, year and century are handled correctly by using the Unix
1630 if (ray == NULL) return;
1631 memset(&the_time, 0, sizeof(struct tm));
1632 the_time.tm_sec = ray->h.sec;
1633 fsec = ray->h.sec - the_time.tm_sec;
1634 the_time.tm_min = ray->h.minute;
1635 the_time.tm_hour = ray->h.hour;
1636 the_time.tm_mon = ray->h.month - 1;
1637 the_time.tm_year = ray->h.year - 1900;
1638 the_time.tm_mday = ray->h.day;
1639 the_time.tm_isdst = -1;
1640 (void) mktime(&the_time);
1641 /* The time is fixed. */
1642 ray->h.sec = the_time.tm_sec;
1644 ray->h.minute = the_time.tm_min;
1645 ray->h.hour = the_time.tm_hour;
1646 ray->h.month = the_time.tm_mon + 1;
1647 ray->h.year = the_time.tm_year + 1900;
1648 ray->h.day = the_time.tm_mday;
1652 /*********************************************************************/
1654 /* RSL_add_dbz_offset_to_ray */
1656 /*********************************************************************/
1658 Add the calibration factor 'dbz_offset' to each ray bin which
1659 contains a valid value.
1661 void RSL_add_dbz_offset_to_ray(Ray *r, float dbz_offset)
1666 if (r == NULL) return;
1667 for (ibin=0; ibin<r->h.nbins; ibin++)
1669 val = r->h.f(r->range[ibin]);
1670 if ( val >= (float)NOECHO ) continue; /* Invalid value */
1671 r->range[ibin] = r->h.invf(val + dbz_offset);
1675 /*********************************************************************/
1677 /* RSL_add_dbz_offset_to_sweep */
1679 /*********************************************************************/
1680 void RSL_add_dbz_offset_to_sweep(Sweep *s, float dbz_offset)
1683 if (s == NULL) return;
1684 for (iray=0; iray<s->h.nrays; iray++)
1685 RSL_add_dbz_offset_to_ray(s->ray[iray], dbz_offset);
1688 /*********************************************************************/
1690 /* RSL_add_dbz_offset_to_volume */
1692 /*********************************************************************/
1693 void RSL_add_dbz_offset_to_volume(Volume *v, float dbz_offset)
1696 if (v == NULL) return;
1697 for (isweep=0; isweep<v->h.nsweeps; isweep++)
1698 RSL_add_dbz_offset_to_sweep(v->sweep[isweep], dbz_offset);