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