]> Pileus Git - ~andy/rsl/blob - doc/programmers_guide.html
Initial import
[~andy/rsl] / doc / programmers_guide.html
1 <!doctype html public "-//w3c//dtd html 4.0 transitional//en">
2 <html>
3 <head>
4    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
5    <meta name="GENERATOR" content="Mozilla/4.5 [en] (X11; U; Linux 2.0.32 i686) [Netscape]">
6 </head>
7 <body>
8 <a href="index.html"><img SRC="rsl.gif" height=100 width=100></a>
9 <hr>
10 <br>&nbsp;
11 <br>&nbsp;
12 <h1>
13 Programming Guide for RSL.</h1>
14
15 <h2>
16 Design philosophy:</h2>
17 The design philosophy is simple. Routines are functions. Routines are short
18 and simple. And, higher level routines are built upon lower level routines;
19 a hierarchical modular design.
20 <p>Each procedure in the RSL is a function that returns a pointer to an
21 object that it is designed, or self documented, to return. In the case
22 of scalar functions, scalar values are returned. There are exceptions to
23 this rule wherein we have procedures that modify their arguments, but,
24 these are few. Also, each function does one thing well. This makes the
25 implementation of each function simple and easy to understand and, therefore,
26 easy to maintain. The power of the library has come from building higher
27 level functions from many lower level functions. Eventually, the highest
28 level function takes a filename as an argument and returns a Radar pointer.
29 <p>The Radar data structure is designed to contain values and header information
30 making it a superset of all the radar data formats currently encountered.
31 Currently RSL can ingest the following file formats: nexrad, lassen both
32 1.3 and 1.4 file format versions (MCTEX too), UF, toga, new sigment (nsig)
33 both version 1 and version 2 file formats, mcgill, kwajalein and rapic
34 (Berrimah data).
35 <br>&nbsp;
36 <h2>
37 <a href="whats_new.html">What's New?</a></h2>
38
39 <h2>
40 Future plans.</h2>
41 There are no plans for added functionality to the library. However, there
42 are always new functions that pop-up. The documentation must be improved.
43 Other than that, bug fixes and optimizations will be incorporated. Therefore
44 all furture releases are expected to be minor.
45 <h2>
46 Can read compressed files automatically (new since v0.41)</h2>
47 Transparent, to all ingest routines the capability of filtering the input
48 data file through the GNU gzip program for decompressing the file has been
49 added. This feature does not appear to slow the I/O time and, in some cases,
50 especially on 486 pc's, improves overall throughput time. Two generic,
51 internal routines, have been added: <b>compress_pipe</b> and <b>uncompress_pipe</b>.
52 Each routine takes a <b>FILE *</b> and redirects it through <b>gzip</b>.
53 Each routine returns a new <b>FILE *</b>. Wsr88d files occupy 1/10 the
54 disk space when compressed and the TRMM Office plans to compress wsr88d
55 files to CDROM and 8mm tape for overall throughput savings for the production
56 system. It will no longer be necessary to decompress the data before processing
57 level 1.
58 <p>Similiarly, UF output can be saved using the gzip filter. The new routine
59 added is <a href="RSL_radar_to_uf.html">RSL_radar_to_uf_gzip</a> which
60 utilizes the gzip compression filter.
61 <h2>
62 Can read stdin (new since v0.39)</h2>
63 A new routine is provided called <a href="RSL_uf_to_radar.html">RSL_uf_to_radar_fp</a>
64 which takes an open file descriptor and returns a radar pointer. That file
65 pointer can be <b>stdin</b>. This special interface syntax, for the UF
66 ingest, is an exception to the interfaces that the other ingest routines
67 have. All other ingest routines, including <a href="RSL_uf_to_radar.html">RSL_uf_to_radar</a>,
68 have been modified to read stdin when the input filename is NULL. The complete
69 list of routines is: <a href="RSL_wsr88d_to_radar.html">RSL_wsr88d_to_radar</a>,
70 <a href="RSL_nsig_to_radar.html">RSL_nsig_to_radar</a>,
71 <a href="RSL_lassen_to_radar.html">RSL_lassen_to_radar</a>,
72 <a href="RSL_uf_to_radar.html">RSL_uf_to_radar</a>,
73 <a href="RSL_mcgill_to_radar.html">RSL_mcgill_to_radar</a>
74 and <a href="RSL_toga_to_radar.html">RSL_toga_to_radar</a>. The only routine
75 that will not accept stdin will be <a href="RSL_anyformat_to_radar.html">RSL_anyformat_to_radar</a>.
76 That routine will not be able to handle reading stdin because it needs
77 to read the first few bytes of the file to determine which ingest routine
78 to call. If you plan to make a filter program, you'll just have to know
79 what file format you expect: UF, nsig, wsr88d, etc.
80 <h2>
81 Verbose print mode:</h2>
82 The function <a href="RSL_radar_verbose.html">RSL_radar_verbose_on()</a>
83 and <a href="RSL_radar_verbose.html">RSL_radar_verbose_off()</a> control
84 whether or not the radar library prints diagnostic messages during execution.
85 These routines simply toggle an externally defined variable, <b>radar_verbose_flag</b>.
86 If you're writing a new library function and you want the user (another
87 programmer) to control the printing of diagnostic messages or not, you
88 simply place print statement as the consequence of testing the variable
89 <b>radar_verbose_flag</b>.
90 For example:
91 <pre>extern int radar_verbose_flag; /* Define before usage. */
92 &nbsp;/* Somewhere in the code. */
93 &nbsp;if (radar_verbose_flag) printf("I'm here now. Whatever.\n");</pre>
94
95 <h2>
96 Writing methods (structure specific interfaces for routines):</h2>
97 The natural hierarchy of Radar makes it easy to construct routines that
98 have interfaces that work on each of the substructures. When writing a
99 function that manipulates the Ray data, it is easy to provide the interface
100 for the Sweep, Volume and Radar without much extra effort. I will illustrate
101 with an example that adds two Rays.
102 <pre>Ray *add_rays(Ray *ray1, Ray *ray2)
103 {
104 int i;
105 Ray *ray_new;
106 if (ray1 == NULL) return NULL;
107 if (ray2 == NULL) return NULL;
108 ray_new = RSL_new_ray(ray1->h.nbins); /* Allocate the new ray. */
109 if (ray_new == NULL) return NULL;
110 ray_new->h = ray_new->h; /* Copy the header information. */&nbsp;
111 for (i=0; i &lt;= ray_new->h.nbins; i++) /* Add each range element. */
112 &nbsp; ray_new->range[i] = ray1->range[i] + ray2->range[i];
113 return ray_new; /* Return this new ray. */
114 }</pre>
115 It is valid for the routine to accept NULL's. This should be checked and
116 appropriate values returned. In the case above, we return NULL. We could
117 easily return the ray that is not NULL, too. The choice is up to you. The
118 routine is a function and returns a structure pointer similar to those
119 it manipulates; or self documented to return. Space is allocated and header
120 information is copied. You might ask, "Why don't we call RSL_copy_ray,
121 then call RSL_clear_ray?". This is a viable approach and it will work well
122 at the ray level, however, for the sweep, volume, and radar interfaces
123 we don't want to recursively copy all substructures because it is more
124 troublesome to copy, clear and set all the levels, than to simply allocate
125 and set at each level. To construct the interfaces for Sweeps and Volumes
126 is easy. For the Sweep interface we recognize that a Sweep is an array
127 of Rays. You will notice that this routine is coded almost identically
128 to the ray routine.
129 <pre>Sweep *add_sweeps(Sweep *s1, Sweep *s2)
130 {
131 int i;
132 Sweep *s_new;
133 if (s1 == NULL) return NULL;
134 if (s2 == NULL) return NULL;
135 s_new = RSL_new_sweep(s1->h.nrays);
136 s_new->h = s1->h;
137 for (i=0; i &lt;= s1->h.nrays; i++)
138 &nbsp; s_new->ray[i] = add_rays(s1->ray[i], s2->ray[i]);
139 return s_new;</pre>
140
141 <pre>}</pre>
142 The pattern continues for making the Volume interface.
143 <pre>Volume *add_volumes(Volume *v1, Volume *v2)
144 {
145 int i;
146 Volume *v_new;
147 if (v1 == NULL) return NULL;
148 if (v2 == NULL) return NULL;
149 v_new = RSL_new_volume(v1->h.nsweeps);
150 v_new->h = v1->h;
151 for (i=0; i &lt;= v1->h.nsweeps; i++)
152 &nbsp; v_new->sweep[i] = add_sweeps(v1->sweep[i], v2->sweep[i]);
153 return v_new;
154 }</pre>
155 And finally, for Radar.
156 <pre>Radar *add_radars(Radar *r1, Radar *r2)
157 {
158 int i;
159 Radar *r_new;
160 if (r1 == NULL) return NULL;
161 if (r2 == NULL) return NULL;
162 r_new = RSL_new_radar(r1->h.nvolumes);
163 r_new->h = r1->h;
164 for (i=0; i &lt;= r1->h.nvolumes; i++)
165 &nbsp; r_new->v[i] = add_volumes(r1->v[i], r2->v[i]);
166 return r_new;
167 }</pre>
168 These four functions allow us to add two radars, add two volumes, add two
169 sweeps or add two rays. The development was simple. For the radar routine
170 we loop on the number of volumes and call the volume routine. For the volume
171 routine we loop on the number of sweeps and call the sweep routine. For
172 the sweep routine we loop on the number of rays and call the ray routine.
173 For the ray routine we loop on the number of bins and perform the work.
174 Simple. This is how many of the RSL routines were written.
175 <h2>
176 Using h.f and h.invf and defining your own function:</h2>
177 The header members <b>f</b> and <b>invf</b> are provided to allow you to
178 define the conversion function for internal/float storage. These header
179 members are in the structures <a href="RSL_volume_struct.html">Volume</a>,
180 <a href="RSL_sweep_struct.html">Sweep</a>,
181 and <a href="RSL_ray_struct.html">Ray</a>.
182 <b>f</b> takes a value of type
183 <b>Range</b>
184 and returns a value of type
185 <b>float</b>. <b>invf</b> takes a value of
186 type <b>float</b> and returns a value of type <b>Range</b>.
187 <b>f</b> and
188 <b>invf</b> should be inverse functions. That is, c == invf(f(c)) and x
189 == f(invf(x)). <b>Range</b> is a datatype that represents the type of data
190 for internal storage. Typically,
191 <b>Range</b> will be <b>unsigned char</b>
192 or <b>unsigned short</b> depending on the configuration option USE_TWO_BYTE_PRECISION
193 the file makefile during the build and install step of RSL. There are several
194 predefined functions and they are based on the WSR88D, or NEXRAD, encoding.
195 There is a function for each field type: reflectivity, velocity, spectral
196 width, etc. However, only two distinct functions are provided because the
197 other field types can use these functions as well: DZ_F (and DZ_INVF) and
198 VR_F (and VR_INVF). DZ_F and DZ_INVF apply to reflectivity data and VR_F
199 and VR_INVF apply to all remaining field types by default. There are no
200 restrictions for
201 <b>f</b> and <b>invf</b>. Typically, you either pick the
202 default functions DZ_F, DZ_INVF, VR_F and VR_INVF and assign them to the
203 header member <b>h.f</b> and <b>h.invf</b>, appropriately, in the routine
204 that is ingesting data from disk and constructing the Radar data structure.
205 You can define your own encoding functions and assign them to <b>h.f</b>
206 and <b>h.invf</b>. To illustrate:
207 <pre>static Range invf(float x) {
208 &nbsp;return (Range) (x/2 + 10);
209 }
210
211 static float f (Range x) {
212 &nbsp;return (float) (x - 10)*2; /* Not quite a perfect inverse function. */
213 }
214
215 Volume *volume_routine(Volume *v)
216 {
217 &nbsp;v->h.f = f; /* Assign the float function. */
218 &nbsp;v->h.invf = invf; /* Assign the Range function. */
219 &nbsp;return v;
220 }</pre>
221 So far in the construction of RSL, the only location where you assign h.f
222 and h.invf is in the ingest routine for a particular data format. The routines
223 <a href="RSL_uf_to_radar.html">RSL_uf_to_radar</a>,
224 <a href="RSL_lassen_to_radar.html">RSL_lassen_to_radar</a>,
225 <a href="RSL_wsr88d_to_radar.html">RSL_wsr88d_to_radar</a>,
226 <a href="RSL_nsig_to_radar.html">RSL_nsig_to_radar</a>,
227 <a href="RSL_toga_to_radar.html">RSL_toga_to_radar</a>,
228 and <a href="RSL_mcgill_to_radar.html">RSL_mcgill_to_radar</a> each define
229 the invf and f functions; in some cases the default functions are used.
230 The file <b>volume.c</b> contains the default specification for DZ_F, DZ_INVF,
231 etc.
232 <h2>
233 Image generation:</h2>
234 The image generation section was initially written to test and debug the
235 development of the library. However, some of the image generation capabilities
236 are becoming a permanent feature of RSL. There are two parts to image generation.
237 One, defining the color table and two, making PPM images. Assigning colors
238 is critical to making color images, otherwise the resultant images will
239 be black. The color table in RSL is global to the image functions and is
240 statically allocated so that once the color table is defined, that's it.
241 There are several routines that load color tables, but the main idea is
242 that there is a red table, a green table and a blue table. Each of these
243 tables is a separate file on disk stored in <b>/usr/local/trmm/lib/colors</b>
244 The <b>/usr/local/trmm</b> prefix may be different on your system. These
245 are the default color files and so you can use any file you like. Each
246 file can contain up to 256 bytes, each byte represents a color intensity
247 in the range 0 - 255. The color are ordered, in the file, by color index.
248 The first byte is for color index 0, the second byte for color index 1,
249 etc. There is only one type of image made, PPM. Well, I can make PBM and
250 PGM too, but I consider them similar to PPM. To make other types of images
251 like GIF, PICT, etc. I pipe the output into the ppmtogif, ppmtopict, command
252 appropriately. I do this because I don't want to include all the different
253 conversion sources in RSL. If you don't have the pbmplus software on your
254 system, you can still use these image functions by either making your own
255 ppmtogif command which is a csh script with the sole command 'cat' in it
256 or modify the RSL library routine to not pass the data through the pipe.
257 Also, when making PGM files, I pipe them into gzip so that the output files
258 are small. The image generation functions are designed to meet our specific
259 needs and I cannot say that they will be suitable for your needs.
260 <h2>
261 Writing a new ingest routine:</h2>
262 Here we explain how to interface a new file format into RSL. This means
263 we read the file format and assign values to the appropriate structure
264 members. The interface will return a pointer to Radar, allocating all memory.
265 Or, it will return NULL indicating an error.
266 <pre>Radar *RSL_something_to_radar(char *infile);</pre>
267 The file added to RSL will be called something.c. All the code needed to
268 ingest the file and create the Radar structure will be in that file. It
269 may be necessary to rely on another library that can ingest the data file
270 utilizing a nicer interface. That is ok, go ahead and use it. This new
271 interface definition will be placed in the prototype section of rsl.h so
272 that other routines are aware of it. Also, code to automatically determine
273 the type of input file must be placed in anyformat.c. If it is not possible
274 to automatically determine the type of file, then this must be documented
275 as such and an explanation that when anyformat_to_radar returns NULL, it
276 may be necessary to call RSL_something_to_radar directly. Memory allocation
277 routines you will need are: <a href="RSL_new.html">RSL_new_radar</a>, <a href="RSL_new.html">RSL_new_volume</a>,
278 <a href="RSL_new.html">RSL_new_sweep</a>
279 and <a href="RSL_new.html">RSL_new_ray</a>. You may not know ahead of time
280 just how many substructures to allocate, so allocate some reasonable maximum.
281 You can reset the value of nvolumes, nsweeps, or nrays after the ingest
282 is complete.
283 <pre>radar = RSL_new_radar(20);
284 ...load radar->v[i] ...
285 ...keep track of the max index for v[i] ...
286 radar->h.nvolumes = max_vol_index;
287 return radar;</pre>
288 You don't have to worry about deallocating the extra memory. Output routines
289 -- to disk -- will ignore it. And, after re-reading it, the right amount
290 of memory will be allocated.
291 <p>New in v0.41 is the implementation of the radar structure member <b>radar_type</b>.
292 Assign a descriptive string indicating the origin or the format type of
293 the data. Currently used strings are: "wsr88d", "lassen", "nsig", "uf",
294 "mcgill", "toga", "kwajalein". The string may not exceed 50 characters,
295 including the null character.
296 <p>Common problems that have occurred are:
297 <ol>
298 <li>
299 Not setting the beamwidth member in the sweep header. This affects image
300 generation because the pixel size relies on it.</li>
301
302 <li>
303 The number of rays, sweeps, or volumes is not set or exceeds the amount
304 allocated.</li>
305
306 <li>
307 Not calling RSL_rebin_velocity_... before generating velocity images.</li>
308
309 <li>
310 Not calling the RSL_load_..._color_table(). This results in black images.</li>
311 </ol>
312
313 <h2>
314 RSL_copy versus RSL_new:</h2>
315 It is important to note the differences between these two routines. RSL_copy_{radar,
316 volume, sweep, ray} returns a complete duplicate of the input structure.
317 It duplicates all of the substructures. This means that it allocates the
318 exact amount of memory that the argument occupies. If a Radar occupies
319 15 Mbytes of RAM, then a RSL_copy_radar will result in 15Mbytes of new
320 memory allocated. The total memory occupied will be 30 Mbytes: 15 for the
321 original and 15 for the copy.
322 <p>On the other hand, RSL_new_{radar, volume, sweep, ray} allocates memory
323 for the header structure and only the pointers to the substructure. You
324 have to allocate the substructure yourself. For instance, if you call
325 <pre>RSL_new_radar(4); /* Four volumes. */</pre>
326 you will have to call RSL_new_volume four times. Do that with,
327 <pre>for (i=0; i&lt;=radar->h.nvolumes; i++) {
328 &nbsp; radar->v[i] = RSL_new_volume(40); /* 40 sweep pointers. */
329 &nbsp;&nbsp; ... construct the volume ...
330 }</pre>
331 Here only 40 pointers for the sweeps are allocated and not the space for
332 the entire sweep which would include all the rays. For each sweep you must
333 use the code,
334 <pre>for (i=0; i&lt;volume->h.nsweeps; i++) {
335 &nbsp; volume->sweep[i] = RSL_new_sweep(400);&nbsp; /* 400 is a good max. */
336
337 &nbsp; ... construct the sweep ...
338
339 }</pre>
340
341 <h2>
342 Using RSL_new to copy instead of RSL_copy:</h2>
343 Let's say you want to create the triplet, or quadruplet, set of routines
344 that function like the RSL_copy function in that they allocate new memory
345 and return a pointer to the appropriate structure, but, the routines are
346 executing an algorithm on the data and so the result is a mathematical
347 manipulation of the input structure. In other words:
348 <pre>my_new_volume = RSL_some_function_of_volume(volume);</pre>
349 The natural hierarchical construction will be:
350 <pre>Radar *RSL_some_function_of_radar(radar);
351 Volume *RSL_some_function_of_volume(volume);
352 Sweep *RSL_some_function_of_sweep(sweep);
353 Ray *RSL_some_function_of_ray(ray);</pre>
354 As I've stated before, the radar routine will loop on the number of volumes
355 and call the volume routine. The volume routine will loop on the number
356 of sweeps and call the sweep routine. The sweep routine will loop on the
357 number of rays and call the ray routine. The ray routine will perform the
358 actual function. One approach might be to define the radar routine as:
359 <pre>Radar *RSL_some_function_of_radar(Radar *radar)
360 {
361 &nbsp; int i;
362 &nbsp; Volume *volume;
363 &nbsp; Radar *new_radar;
364 &nbsp; if (radar == NULL) return NULL;
365 &nbsp; new_radar = RSL_copy_radar(radar);&nbsp; /* This is bad, bad, bad. */
366
367 &nbsp; for (i=0; i&lt;=radar->h.nvolumes; i++)
368 &nbsp;&nbsp;&nbsp; new_radar->v[i] = RSL_some_function_of_volume(radar->v[i]);
369
370 &nbsp; return new_radar;
371 }</pre>
372 Now, why did I place the comment /* bad, bad, bad */ in the code? At first,
373 it seems, it may not be all that bad. But, remember that RSL_copy_radar
374 will copy all the volumes and sweeps and rays and bins. The line that assigns
375 new_radar->v[i] is allocating space for a volume too. Therefore, we are
376 more than doubling the memory requirements, if we code the volume and sweep
377 routines similarly. The amount of memory allocated will be nvolumes*space_allocated_to_a_volume
378 + nsweeps*space_allocated_to_a_sweep + nrays*space_allocated_to_a_ray.
379 That is a tremendous amount of memory, you're talking 3*15*15Mbytes (675Mbytes).
380 In order to use RSL_copy_{radar,volume,sweep,ray} you will need to clear
381 out all the volumes, sweeps and rays with a call to RSL_clear_{volume,sweep,ray}
382 and you will have to pass the target structure to the routine which will
383 make it an argument that is modified. This goes against the design of the
384 typical RSL interface where a pointer to an object is returned by a function.
385 A better solution is to allocate a new radar and copy the header information.
386 Instead of writing,
387 <pre>new_radar = RSL_copy_radar(radar);</pre>
388 you write,
389 <pre>new_radar = RSL_new_radar(radar->h.nvolumes);
390 new_radar->h = radar->h;</pre>
391 The volume, sweep and ray routine can be coded similarly. For example,
392 here is the sweep routine:
393 <pre>Sweep *RSL_some_function_of_sweep(Sweep *sweep)
394 {
395 int i;
396 Ray *ray;
397 Sweep *new_sweep;
398 if (sweep == NULL) return NULL;
399 new_sweep = RSL_new_sweep(sweep->h.nrays);
400 new_sweep->h = sweep->h;
401
402 for (i=0; i&lt;=sweep->h.nrays; i++)
403 &nbsp; new_sweep->ray[i] = RSL_some_function_of_ray(sweep->ray[i]);&nbsp;
404
405 return new_sweep;
406 }</pre>
407
408 </body>
409 </html>