]> Pileus Git - ~andy/rsl/blob - wsr88d_m31.c
9e4c5b198603b7172bafd659ed092e1dba08ec44
[~andy/rsl] / wsr88d_m31.c
1 /*
2     NASA/TRMM, Code 613
3     This is the TRMM Office Radar Software Library.
4     Copyright (C) 2008
5             Bart Kelley
6             SSAI
7             Lanham, Maryland
8
9     This program is free software: you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation, either version 3 of the License, or
12     (at your option) any later version.
13
14     This program 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
17     GNU General Public License for more details.
18
19     You should have received a copy of the GNU General Public License
20     along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23
24 /* 
25  * This file contains routines for processing Message Type 31, the digital
26  * radar message type introduced in WSR-88D Level II Build 10.  For more
27  * information, see the "Interface Control Document for the RDA/RPG" at the
28  * WSR-88D Radar Operations Center web site.
29  */
30
31 #include "rsl.h"
32 #include "wsr88d.h"
33 #include <string.h>
34
35 /* Data descriptions in the following data structures are from the "Interface
36  * Control Document for the RDA/RPG", Build 10.0 Draft, WSR-88D Radar
37  * Operations Center.
38  */
39
40 typedef struct {
41     short rpg[6]; /* 12 bytes inserted by RPG Communications Mgr. Ignored. */
42     unsigned short msg_size;  /* Message size for this segment, in halfwords */
43     unsigned char  channel;   /* RDA Redundant Channel */
44     unsigned char  msg_type;  /* Message type. For example, 31 */
45     unsigned short id_seq;    /* Msg seq num = 0 to 7FFF, then roll over to 0 */
46     unsigned short msg_date;  /* Modified Julian date from 1/1/70 */
47     unsigned int   msg_time;  /* Packet generation time in ms past midnight */
48     unsigned short num_segs;  /* Number of segments for this message */
49     unsigned short seg_num;   /* Number of this segment */
50 } Wsr88d_msg_hdr;
51
52 typedef struct {
53     char radar_id[4];
54     unsigned int   ray_time; /* Data collection time in milliseconds past midnight GMT */
55     unsigned short ray_date; /* Julian date - 2440586.5 (1/01/1970) */
56     unsigned short azm_num ; /* Radial number within elevation scan */
57     float azm;      /* Azimuth angle in degrees (0 to 359.956055) */
58     unsigned char compression_code; /* 0 = uncompressed, 1 = BZIP2, 2 = zlib */
59     unsigned char spare; /* for word alignment */
60     unsigned short radial_len; /* radial length in bytes, including data header block */
61     unsigned char azm_res;
62     unsigned char radial_status;
63     unsigned char elev_num;
64     unsigned char sector_cut_num;
65     float elev;  /* Elevation angle in degrees (-7.0 to 70.0) */
66     unsigned char radial_spot_blanking;
67     unsigned char azm_indexing_mode;
68     unsigned short data_block_count;
69     /* Data Block Indexes */
70     unsigned int vol_const;
71     unsigned int elev_const;
72     unsigned int radial_const;
73     unsigned int field1;
74     unsigned int field2;
75     unsigned int field3;
76     unsigned int field4;
77     unsigned int field5;
78     unsigned int field6;
79 } Ray_header_m31;  /* Called Data Header Block in RDA/RPG document. */
80
81 typedef struct {
82     char dataname[4];
83     unsigned int  reserved;
84     unsigned short ngates;
85     short range_first_gate;
86     short range_samp_interval;
87     short thresh_not_overlayed;
88     short snr_thresh;
89     unsigned char controlflag;
90     unsigned char datasize_bits;
91     float scale;
92     float offset;
93 } Data_moment_hdr;
94
95 #define MAX_RADIAL_LENGTH 14288
96
97 typedef struct {
98     Ray_header_m31 ray_hdr;
99     float unamb_rng;
100     float nyq_vel;
101     unsigned char data[MAX_RADIAL_LENGTH];
102 } Wsr88d_ray_m31;
103
104
105 enum radial_status {START_OF_ELEV, INTERMED_RADIAL, END_OF_ELEV, BEGIN_VOS,
106     END_VOS};
107
108
109 void wsr88d_swap_m31_hdr(Wsr88d_msg_hdr *msghdr)
110 {
111     swap_2_bytes(&msghdr->msg_size);
112     swap_2_bytes(&msghdr->id_seq);
113     swap_2_bytes(&msghdr->msg_date);
114     swap_4_bytes(&msghdr->msg_time);
115     swap_2_bytes(&msghdr->num_segs);
116     swap_2_bytes(&msghdr->seg_num);
117 }
118
119
120 void wsr88d_swap_m31_ray_hdr(Ray_header_m31 *ray_hdr)
121 {
122     int *data_ptr;
123
124     swap_4_bytes(&ray_hdr->ray_time);
125     swap_2_bytes(&ray_hdr->ray_date);
126     swap_2_bytes(&ray_hdr->azm_num);
127     swap_4_bytes(&ray_hdr->azm);
128     swap_2_bytes(&ray_hdr->radial_len);
129     swap_4_bytes(&ray_hdr->elev);
130     swap_2_bytes(&ray_hdr->data_block_count);
131     data_ptr = (int *) &ray_hdr->vol_const;
132     for (; data_ptr <= (int *) &ray_hdr->field6; data_ptr++)
133         swap_4_bytes(data_ptr);
134 }
135
136
137 void wsr88d_swap_data_hdr(Data_moment_hdr *this_field)
138 {
139     short *halfword;
140     halfword = (short *) &this_field->ngates;
141     for (; halfword < (short *) &this_field->controlflag; halfword++)
142         swap_2_bytes(halfword);
143     swap_4_bytes(&this_field->scale);
144     swap_4_bytes(&this_field->offset);
145 }
146
147
148 float wsr88d_get_angle(short bitfield)
149 {
150     short mask = 1;
151     int i;
152     float angle = 0.;
153     float value[13] = {0.043945, 0.08789, 0.17578, 0.35156, .70313, 1.40625,
154         2.8125, 5.625, 11.25, 22.5, 45., 90., 180.};
155
156     /* Find which bits are set and sum corresponding values to get angle. */
157
158     bitfield = bitfield >> 3;  /* 3 least significant bits aren't used. */
159     for (i = 0; i < 13; i++) {
160         if (bitfield & mask) angle += value[i];
161         bitfield = bitfield >> 1;
162     }
163     return angle;
164 }
165
166
167 float wsr88d_get_azim_rate(short bitfield)
168 {
169     short mask = 1;
170     int i;
171     float rate = 0.;
172     float value[12] = {0.0109863, 0.021972656, 0.043945, 0.08789, 0.17578,
173         0.35156, .70313, 1.40625, 2.8125, 5.625, 11.25, 22.5};
174
175     /* Find which bits are set and sum corresponding values to get rate. */
176
177     bitfield = bitfield >> 3;  /* 3 least significant bits aren't used. */
178     for (i = 0; i < 12; i++) {
179         if (bitfield & mask) rate += value[i];
180         bitfield = bitfield >> 1;
181     }
182     if (bitfield >> 15) rate = -rate;
183     return rate;
184 }
185
186 #define WSR88D_MAX_SWEEPS 20
187
188 typedef struct {
189     int vcp;
190     int num_cuts;
191     float vel_res;
192     float fixed_angle[WSR88D_MAX_SWEEPS];
193     float azim_rate[WSR88D_MAX_SWEEPS];
194     int waveform[WSR88D_MAX_SWEEPS];
195     int super_res_ctrl[WSR88D_MAX_SWEEPS];
196     int surveil_prf_num[WSR88D_MAX_SWEEPS];
197     int doppler_prf_num[WSR88D_MAX_SWEEPS];
198 } VCP_data;
199
200 static VCP_data vcp_data;
201
202 void wsr88d_get_vcp_data(short *msgtype5)
203 {
204     short azim_rate, fixed_angle, vel_res;
205     short sres_and_survprf; /* super res ctrl and surveil prf, one byte each */
206     short chconf_and_waveform;
207     int i;
208     
209     vcp_data.vcp = (unsigned short) msgtype5[2];
210     vcp_data.num_cuts = msgtype5[3];
211     if (little_endian()) {
212         swap_2_bytes(&vcp_data.vcp);
213         swap_2_bytes(&vcp_data.num_cuts);
214     }
215     vel_res = msgtype5[5];
216     if (little_endian()) swap_2_bytes(&vel_res);
217     vel_res = vel_res >> 8;
218     if (vel_res == 2) vcp_data.vel_res = 0.5;
219     else if (vel_res == 4) vcp_data.vel_res = 1.0;
220     else vcp_data.vel_res = 0.0;
221     /* Get elevation related information for each sweep. */
222     for (i=0; i < vcp_data.num_cuts; i++) {
223         fixed_angle = msgtype5[11 + i*23];
224         azim_rate = msgtype5[15 + i*23];
225         chconf_and_waveform = msgtype5[12 + i*23];
226         sres_and_survprf = msgtype5[13 + i*23];
227         vcp_data.doppler_prf_num[i] = msgtype5[23 + i*23];
228         if (little_endian()) {
229             swap_2_bytes(&fixed_angle);
230             swap_2_bytes(&azim_rate);
231             swap_2_bytes(&chconf_and_waveform);
232             swap_2_bytes(&sres_and_survprf);
233             swap_2_bytes(&vcp_data.doppler_prf_num[i]);
234         }
235         vcp_data.fixed_angle[i] = wsr88d_get_angle(fixed_angle);
236         vcp_data.azim_rate[i] = wsr88d_get_azim_rate(azim_rate);
237         vcp_data.waveform[i] = chconf_and_waveform & 0xff;
238         vcp_data.super_res_ctrl[i] = sres_and_survprf >> 8;
239         vcp_data.surveil_prf_num[i] = sres_and_survprf & 0xff;
240     }
241 }
242
243
244 void get_wsr88d_unamb_and_nyq_vel(Wsr88d_ray_m31 *wsr88d_ray, float *unamb_rng,
245         float *nyq_vel)
246 {
247     int dindex, found;
248     short nyq_vel_sh, unamb_rng_sh;
249
250     found = 0;
251     dindex = wsr88d_ray->ray_hdr.radial_const;
252     if (strncmp((char *) &wsr88d_ray->data[dindex], "RRAD", 4) == 0) found = 1;
253     else {
254         dindex = wsr88d_ray->ray_hdr.elev_const;
255         if (strncmp((char *) &wsr88d_ray->data[dindex], "RRAD", 4) == 0)
256             found = 1;
257         else {
258             dindex = wsr88d_ray->ray_hdr.vol_const;
259             if (strncmp((char *) &wsr88d_ray->data[dindex], "RRAD", 4) == 0)
260                 found = 1;
261         }
262     }
263     if (found) {
264         memcpy(&unamb_rng_sh, &wsr88d_ray->data[dindex+6], 2);
265         memcpy(&nyq_vel_sh, &wsr88d_ray->data[dindex+16], 2);
266         if (little_endian()) {
267             swap_2_bytes(&unamb_rng_sh);
268             swap_2_bytes(&nyq_vel_sh);
269         }
270         *unamb_rng = unamb_rng_sh / 10.;
271         *nyq_vel = nyq_vel_sh / 100.;
272     } else {
273         *unamb_rng = 0.;
274         *nyq_vel = 0.;
275     }
276 }
277
278
279 int read_wsr88d_ray_m31(Wsr88d_file *wf, int msg_size,
280         Wsr88d_ray_m31 *wsr88d_ray)
281 {
282     int n;
283     float nyq_vel, unamb_rng;
284
285     /* Read wsr88d ray. */
286
287     n = fread(wsr88d_ray->data, msg_size, 1, wf->fptr);
288     if (n < 1) {
289         fprintf(stderr,"read_wsr88d_ray_m31: Read failed.\n");
290         return 0;
291     }
292
293     /* Copy data header block to ray header structure. */
294     memcpy(&wsr88d_ray->ray_hdr, &wsr88d_ray->data, sizeof(Ray_header_m31));
295
296     if (little_endian()) wsr88d_swap_m31_ray_hdr(&wsr88d_ray->ray_hdr);
297
298     /* Retrieve unambiguous range and Nyquist velocity here so that we don't
299      * have to do it for each data moment later.
300      */
301     get_wsr88d_unamb_and_nyq_vel(wsr88d_ray, &unamb_rng, &nyq_vel);
302     wsr88d_ray->unamb_rng = unamb_rng;
303     wsr88d_ray->nyq_vel = nyq_vel;
304
305     return 1;
306 }
307
308
309 void wsr88d_load_ray_hdr(Wsr88d_ray_m31 *wsr88d_ray, Ray *ray)
310 {
311     int month, day, year, hour, minute, sec;
312     float fsec;
313     Wsr88d_ray m1_ray;
314     Ray_header_m31 ray_hdr;
315
316     ray_hdr = wsr88d_ray->ray_hdr;
317     m1_ray.ray_date = ray_hdr.ray_date;
318     m1_ray.ray_time = ray_hdr.ray_time;
319
320     wsr88d_get_date(&m1_ray, &month, &day, &year);
321     wsr88d_get_time(&m1_ray, &hour, &minute, &sec, &fsec);
322     ray->h.year = year + 1900;
323     ray->h.month = month;
324     ray->h.day = day;
325     ray->h.hour = hour;
326     ray->h.minute = minute;
327     ray->h.sec = sec + fsec;
328     ray->h.azimuth = ray_hdr.azm;
329     ray->h.ray_num = ray_hdr.azm_num;
330     ray->h.elev = ray_hdr.elev;
331     ray->h.elev_num = ray_hdr.elev_num;
332     ray->h.unam_rng = wsr88d_ray->unamb_rng;
333     ray->h.nyq_vel = wsr88d_ray->nyq_vel;
334     int elev_index;
335     elev_index = ray_hdr.elev_num - 1;
336     ray->h.azim_rate = vcp_data.azim_rate[elev_index];
337     ray->h.fix_angle = vcp_data.fixed_angle[elev_index];
338     ray->h.vel_res = vcp_data.vel_res;
339     if (ray_hdr.azm_res != 1)
340         ray->h.beam_width = 1.0;
341     else ray->h.beam_width = 0.5;
342
343     /* For convenience, use message type 1 routines to get some values.
344      * First load VCP and elevation numbers into a msg 1 ray.
345      */
346     m1_ray.vol_cpat = vcp_data.vcp;
347     m1_ray.elev_num = ray_hdr.elev_num;
348     m1_ray.unam_rng = (short) (wsr88d_ray->unamb_rng * 10.);
349     /* Get values from message type 1 routines. */
350     ray->h.frequency = wsr88d_get_frequency(&m1_ray);
351     ray->h.pulse_width = wsr88d_get_pulse_width(&m1_ray);
352     ray->h.pulse_count = wsr88d_get_pulse_count(&m1_ray);
353     ray->h.prf = (int) wsr88d_get_prf(&m1_ray);
354     ray->h.wavelength = 0.1071;
355 }
356
357
358 int wsr88d_get_vol_index(char* dataname)
359 {
360     if (strncmp(dataname, "DREF", 4) == 0) return DZ_INDEX;
361     if (strncmp(dataname, "DVEL", 4) == 0) return VR_INDEX;
362     if (strncmp(dataname, "DSW",  3) == 0) return SW_INDEX;
363     if (strncmp(dataname, "DZDR", 4) == 0) return DR_INDEX;
364     if (strncmp(dataname, "DPHI", 4) == 0) return PH_INDEX;
365     if (strncmp(dataname, "DRHO", 4) == 0) return RH_INDEX;
366
367     return -1;
368 }
369
370
371 #define MAXRAYS_M31 800
372 #define MAXSWEEPS 20
373
374 void wsr88d_load_ray_into_radar(Wsr88d_ray_m31 *wsr88d_ray, int isweep,
375         Radar *radar)
376 {
377     /* Load data into ray structure for each data field. */
378
379     int data_index;
380     int *field_offset;
381     int ifield, nfields;
382     int iray;
383     const nconstblocks = 3;
384
385     Data_moment_hdr data_hdr;
386     int ngates, do_swap;
387     int i, hdr_size;
388     unsigned short item;
389     float value, scale, offset;
390     unsigned char *data;
391     Range (*invf)(float x);
392     float (*f)(Range x);
393     Ray *ray;
394     int vol_index, waveform;
395     char *type_str;
396
397     int keep_hi_prf_dz = 0; /* TODO: make this an argument. */
398
399     enum waveforms {surveillance=1, doppler_w_amb_res, doppler_no_amb_res,
400         batch};
401
402     nfields = wsr88d_ray->ray_hdr.data_block_count - nconstblocks;
403     field_offset = (int *) &wsr88d_ray->ray_hdr.radial_const;
404     do_swap = little_endian();
405     iray = wsr88d_ray->ray_hdr.azm_num - 1;
406
407     for (ifield=0; ifield < nfields; ifield++) {
408         field_offset++;
409         data_index = *field_offset;
410         /* Get data moment header. */
411         hdr_size = sizeof(data_hdr);
412         memcpy(&data_hdr, &wsr88d_ray->data[data_index], hdr_size);
413         if (do_swap) wsr88d_swap_data_hdr(&data_hdr);
414         data_index += hdr_size;
415
416         vol_index = wsr88d_get_vol_index(data_hdr.dataname);
417         if (vol_index < 0) {
418             fprintf(stderr,"wsr88d_load_ray_into_radar: Unknown dataname %s.  "
419                     "isweep = %d, iray = %d.\n", data_hdr.dataname, isweep,
420                     iray);
421             return;
422         }
423         switch (vol_index) {
424             case DZ_INDEX: f = DZ_F; invf = DZ_INVF;
425                  type_str = "Reflectivity"; break;
426             case VR_INDEX: f = VR_F; invf = VR_INVF;
427                  type_str = "Velocity"; break;
428             case SW_INDEX: f = SW_F; invf = SW_INVF;
429                  type_str = "Spectrum width"; break;
430             case DR_INDEX: f = DR_F; invf = DR_INVF;
431                  type_str = "Diff. Reflectivity"; break;
432             case PH_INDEX: f = PH_F; invf = PH_INVF;
433                  type_str = "Diff. Phase"; break;
434             case RH_INDEX: f = RH_F; invf = RH_INVF;
435                  type_str = "Correlation Coef (Rho)"; break;
436         }
437
438         waveform = vcp_data.waveform[isweep];
439
440         /* Ignore short-range reflectivity from velocity split cuts unless
441          * merging of split cuts is suppressed.  The indicators for this type of
442          * reflectivity are surveillance mode is 0 and elevation angle is
443          * below 6 degrees.
444          */
445         if (vol_index == DZ_INDEX && (vcp_data.surveil_prf_num[isweep] == 0 &&
446                     vcp_data.fixed_angle[isweep] < 6.0 && !keep_hi_prf_dz))
447             continue;
448
449         /* Load the data for this field. */
450         if (radar->v[vol_index] == NULL) {
451             radar->v[vol_index] = RSL_new_volume(MAXSWEEPS);
452             radar->v[vol_index]->h.f = f;
453             radar->v[vol_index]->h.invf = invf;
454             radar->v[vol_index]->h.type_str = type_str;
455         }
456         if (radar->v[vol_index]->sweep[isweep] == NULL) {
457             radar->v[vol_index]->sweep[isweep] = RSL_new_sweep(MAXRAYS_M31);
458             radar->v[vol_index]->sweep[isweep]->h.f = f;
459             radar->v[vol_index]->sweep[isweep]->h.invf = invf;
460         }
461         ngates = data_hdr.ngates;
462         ray = RSL_new_ray(ngates);
463
464         /* Convert data to float, then use range function to store in ray.
465          * Note: data range is 2-255. 0 means signal is below threshold, and 1
466          * means range folded.
467          */
468
469         offset = data_hdr.offset;
470         scale = data_hdr.scale;
471         if (data_hdr.scale == 0) scale = 1.0; 
472         data = &wsr88d_ray->data[data_index];
473         for (i = 0; i < ngates; i++) {
474             if (data_hdr.datasize_bits != 16) {
475                 item = *data;
476                 data++;
477             } else {
478                 item = *(unsigned short *)data;
479                 if (do_swap) swap_2_bytes(&item);
480                 data += 2;
481             }
482             if (item > 1)
483                 value = (item - offset) / scale;
484             else value = (item == 0) ? BADVAL : RFVAL;
485             ray->range[i] = invf(value);
486             ray->h.f = f;
487             ray->h.invf = invf;
488         }
489         wsr88d_load_ray_hdr(wsr88d_ray, ray);
490         ray->h.range_bin1 = data_hdr.range_first_gate;
491         ray->h.gate_size = data_hdr.range_samp_interval;
492         ray->h.nbins = ngates;
493         radar->v[vol_index]->sweep[isweep]->ray[iray] = ray;
494         radar->v[vol_index]->sweep[isweep]->h.nrays = iray+1;
495     } /* for each data field */
496 }
497
498
499 void wsr88d_load_sweep_header(Radar *radar, int isweep)
500 {
501     int ivolume, nrays;
502     Sweep *sweep;
503     Ray *last_ray;
504
505     for (ivolume=0; ivolume < MAX_RADAR_VOLUMES; ivolume++) {
506         if (radar->v[ivolume] != NULL &&
507                 radar->v[ivolume]->sweep[isweep] != NULL) {
508             sweep = radar->v[ivolume]->sweep[isweep];
509             nrays = sweep->h.nrays;
510             if (nrays == 0) continue;
511             last_ray = sweep->ray[nrays-1];
512             sweep->h.sweep_num = last_ray->h.elev_num;
513             sweep->h.elev = vcp_data.fixed_angle[isweep];
514             sweep->h.beam_width = last_ray->h.beam_width;
515             sweep->h.vert_half_bw = sweep->h.beam_width / 2.;
516             sweep->h.horz_half_bw = sweep->h.beam_width / 2.;
517         }
518     }
519 }
520
521
522 Radar *wsr88d_load_m31_into_radar(Wsr88d_file *wf)
523 {
524     Wsr88d_msg_hdr msghdr;
525     Wsr88d_ray_m31 wsr88d_ray;
526     short non31_seg_remainder[1202]; /* Remainder after message header */
527     int end_of_vos = 0, isweep = 0;
528     int msg_hdr_size, msg_size, n;
529     int sweep_hdrs_written = 0, prev_elev_num = 1, prev_raynum = 0, raynum = 0;
530     Radar *radar = NULL;
531
532     /* Message type 31 is a variable length message.  All other types are made
533      * up of 1 or more segments, where each segment is 2432 bytes in length.
534      * To handle these differences, read the message header and check its type.
535      * If it is 31, use the size given in the message header to determine the
536      * number of bytes to read.  If not, simply read the remainder of the
537      * 2432-byte segment.
538      */
539
540     n = fread(&msghdr, sizeof(Wsr88d_msg_hdr), 1, wf->fptr);
541
542     /* printf("msgtype = %d\n", msghdr.msg_type); */
543     msg_hdr_size = sizeof(Wsr88d_msg_hdr) - sizeof(msghdr.rpg);
544
545     radar = RSL_new_radar(MAX_RADAR_VOLUMES);
546
547     while (! end_of_vos) {
548         if (msghdr.msg_type == 31) {
549             if (little_endian()) wsr88d_swap_m31_hdr(&msghdr);
550
551             /* Get size of the remainder of message.  The given size is in
552              * halfwords, but we want it in bytes, so double it.
553              */
554             msg_size = (int) msghdr.msg_size * 2 - msg_hdr_size;
555
556             n = read_wsr88d_ray_m31(wf, msg_size, &wsr88d_ray);
557             /* Assume error message was issued from read routine */
558             if (n <= 0) return NULL;
559             raynum = wsr88d_ray.ray_hdr.azm_num;
560             if (raynum > MAXRAYS_M31) {
561                 fprintf(stderr,"Error: raynum = %d, exceeds MAXRAYS_M31"
562                         " (%d)\n", raynum, MAXRAYS_M31);
563                 fprintf(stderr,"isweep = %d\n", isweep);
564                 RSL_free_radar(radar);
565                 return NULL;
566             }
567
568             /* Check for an unexpected start of new elevation, and issue a
569              * warning if this has occurred.  This usually means less rays than
570              * expected.  It happens, but rarely.
571              */
572             if (wsr88d_ray.ray_hdr.radial_status == START_OF_ELEV &&
573                     sweep_hdrs_written != prev_elev_num) {
574                 fprintf(stderr,"Warning: Radial status is Start-of-Elevation, "
575                         "but End-of-Elevation was not\n"
576                         "issued for elevation number %d.  Number of rays = %d"
577                         "\n", prev_elev_num, prev_raynum);
578                 wsr88d_load_sweep_header(radar, isweep);
579                 isweep++;
580                 sweep_hdrs_written++;
581                 prev_elev_num = wsr88d_ray.ray_hdr.elev_num - 1;
582             }
583
584             /* Load ray into radar structure. */
585             wsr88d_load_ray_into_radar(&wsr88d_ray, isweep, radar);
586             prev_raynum = raynum;
587         }
588         else { /* msg_type not 31 */
589             n = fread(&non31_seg_remainder, sizeof(non31_seg_remainder), 1,
590                     wf->fptr);
591             if (n < 1) {
592                 fprintf(stderr,"Warning: load_wsr88d_m31_into_radar: ");
593                 if (feof(wf->fptr) != 0)
594                     fprintf(stderr, "Unexpected end of file.\n");
595                 else
596                     fprintf(stderr,"Read failed.\n");
597                 fprintf(stderr,"Current sweep index: %d\n"
598                         "Last ray read: %d\n", isweep, prev_raynum);
599                 wsr88d_load_sweep_header(radar, isweep);
600                 return radar;
601             }
602             if (msghdr.msg_type == 5) {
603                 wsr88d_get_vcp_data(non31_seg_remainder);
604                 radar->h.vcp = vcp_data.vcp;
605             }
606         }
607
608         /* Check for end of sweep */
609         if (wsr88d_ray.ray_hdr.radial_status == END_OF_ELEV) {
610             wsr88d_load_sweep_header(radar, isweep);
611             isweep++;
612             sweep_hdrs_written++;
613             prev_elev_num = wsr88d_ray.ray_hdr.elev_num;
614         }
615
616         /* If not at end of volume scan, read next message header. */
617         if (wsr88d_ray.ray_hdr.radial_status != END_VOS) {
618             n = fread(&msghdr, sizeof(Wsr88d_msg_hdr), 1, wf->fptr);
619             if (n < 1) {
620                 fprintf(stderr,"Warning: load_wsr88d_m31_into_radar: ");
621                 if (feof(wf->fptr) != 0) fprintf(stderr,
622                         "Unexpected end of file.\n");
623                 else fprintf(stderr,"Failed reading msghdr.\n");
624                 fprintf(stderr,"Current sweep index: %d\n"
625                         "Last ray read: %d\n", isweep, prev_raynum);
626                 wsr88d_load_sweep_header(radar, isweep);
627                 return radar;
628             }
629         }
630         else {
631             end_of_vos = 1;
632             wsr88d_load_sweep_header(radar, isweep);
633         }
634         if (feof(wf->fptr) != 0) end_of_vos = 1;
635     }  /* while not end of vos */
636
637     return radar;
638 }