1 /***************************************************************************
2  * msrutils.c:
3  *
4  * Generic routines to operate on Mini-SEED records.
5  *
6  * Written by Chad Trabant
7  *   ORFEUS/EC-Project MEREDIAN
8  *   IRIS Data Management Center
9  *
10  * modified: 2016.283
11  ***************************************************************************/
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17 
18 #include "libmseed.h"
19 
20 /***************************************************************************
21  * msr_init:
22  *
23  * Initialize and return an MSRecord struct, allocating memory if
24  * needed.  If memory for the fsdh and datasamples fields has been
25  * allocated the pointers will be retained for reuse.  If a blockette
26  * chain is present all associated memory will be released.
27  *
28  * Returns a pointer to a MSRecord struct on success or NULL on error.
29  ***************************************************************************/
30 MSRecord *
msr_init(MSRecord * msr)31 msr_init (MSRecord *msr)
32 {
33   void *fsdh        = 0;
34   void *datasamples = 0;
35 
36   if (!msr)
37   {
38     msr = (MSRecord *)malloc (sizeof (MSRecord));
39   }
40   else
41   {
42     fsdh        = msr->fsdh;
43     datasamples = msr->datasamples;
44 
45     if (msr->blkts)
46       msr_free_blktchain (msr);
47 
48     if (msr->ststate)
49       free (msr->ststate);
50   }
51 
52   if (msr == NULL)
53   {
54     ms_log (2, "msr_init(): Cannot allocate memory\n");
55     return NULL;
56   }
57 
58   memset (msr, 0, sizeof (MSRecord));
59 
60   msr->fsdh        = fsdh;
61   msr->datasamples = datasamples;
62 
63   msr->reclen    = -1;
64   msr->samplecnt = -1;
65   msr->byteorder = -1;
66   msr->encoding  = -1;
67 
68   return msr;
69 } /* End of msr_init() */
70 
71 /***************************************************************************
72  * msr_free:
73  *
74  * Free all memory associated with a MSRecord struct.
75  ***************************************************************************/
76 void
msr_free(MSRecord ** ppmsr)77 msr_free (MSRecord **ppmsr)
78 {
79   if (ppmsr != NULL && *ppmsr != 0)
80   {
81     /* Free fixed section header if populated */
82     if ((*ppmsr)->fsdh)
83       free ((*ppmsr)->fsdh);
84 
85     /* Free blockette chain if populated */
86     if ((*ppmsr)->blkts)
87       msr_free_blktchain (*ppmsr);
88 
89     /* Free datasamples if present */
90     if ((*ppmsr)->datasamples)
91       free ((*ppmsr)->datasamples);
92 
93     /* Free stream processing state if present */
94     if ((*ppmsr)->ststate)
95       free ((*ppmsr)->ststate);
96 
97     free (*ppmsr);
98 
99     *ppmsr = NULL;
100   }
101 } /* End of msr_free() */
102 
103 /***************************************************************************
104  * msr_free_blktchain:
105  *
106  * Free all memory associated with a blockette chain in a MSRecord
107  * struct and set MSRecord->blkts to NULL.  Also reset the shortcut
108  * blockette pointers.
109  ***************************************************************************/
110 void
msr_free_blktchain(MSRecord * msr)111 msr_free_blktchain (MSRecord *msr)
112 {
113   if (msr)
114   {
115     if (msr->blkts)
116     {
117       BlktLink *bc = msr->blkts;
118       BlktLink *nb = NULL;
119 
120       while (bc)
121       {
122         nb = bc->next;
123 
124         if (bc->blktdata)
125           free (bc->blktdata);
126 
127         free (bc);
128 
129         bc = nb;
130       }
131 
132       msr->blkts = 0;
133     }
134 
135     msr->Blkt100  = 0;
136     msr->Blkt1000 = 0;
137     msr->Blkt1001 = 0;
138   }
139 } /* End of msr_free_blktchain() */
140 
141 /***************************************************************************
142  * msr_addblockette:
143  *
144  * Add a blockette to the blockette chain of an MSRecord.  'blktdata'
145  * should be the body of the blockette type 'blkttype' of 'length'
146  * bytes without the blockette header (type and next offsets).  The
147  * 'chainpos' value controls which end of the chain the blockette is
148  * added to.  If 'chainpos' is 0 the blockette will be added to the
149  * end of the chain (last blockette), other wise it will be added to
150  * the beginning of the chain (first blockette).
151  *
152  * Returns a pointer to the BlktLink added to the chain on success and
153  * NULL on error.
154  ***************************************************************************/
155 BlktLink *
msr_addblockette(MSRecord * msr,char * blktdata,int length,int blkttype,int chainpos)156 msr_addblockette (MSRecord *msr, char *blktdata, int length, int blkttype,
157                   int chainpos)
158 {
159   BlktLink *blkt;
160 
161   if (!msr)
162     return NULL;
163 
164   blkt = msr->blkts;
165 
166   if (blkt)
167   {
168     if (chainpos != 0)
169     {
170       blkt = (BlktLink *)malloc (sizeof (BlktLink));
171 
172       blkt->next = msr->blkts;
173       msr->blkts = blkt;
174     }
175     else
176     {
177       /* Find the last blockette */
178       while (blkt && blkt->next)
179       {
180         blkt = blkt->next;
181       }
182 
183       blkt->next = (BlktLink *)malloc (sizeof (BlktLink));
184 
185       blkt       = blkt->next;
186       blkt->next = 0;
187     }
188 
189     if (blkt == NULL)
190     {
191       ms_log (2, "msr_addblockette(): Cannot allocate memory\n");
192       return NULL;
193     }
194   }
195   else
196   {
197     msr->blkts = (BlktLink *)malloc (sizeof (BlktLink));
198 
199     if (msr->blkts == NULL)
200     {
201       ms_log (2, "msr_addblockette(): Cannot allocate memory\n");
202       return NULL;
203     }
204 
205     blkt       = msr->blkts;
206     blkt->next = 0;
207   }
208 
209   blkt->blktoffset = 0;
210   blkt->blkt_type  = blkttype;
211   blkt->next_blkt  = 0;
212 
213   blkt->blktdata = (char *)malloc (length);
214 
215   if (blkt->blktdata == NULL)
216   {
217     ms_log (2, "msr_addblockette(): Cannot allocate memory\n");
218     return NULL;
219   }
220 
221   memcpy (blkt->blktdata, blktdata, length);
222   blkt->blktdatalen = length;
223 
224   /* Setup the shortcut pointer for common blockettes */
225   switch (blkttype)
226   {
227   case 100:
228     msr->Blkt100 = blkt->blktdata;
229     break;
230   case 1000:
231     msr->Blkt1000 = blkt->blktdata;
232     break;
233   case 1001:
234     msr->Blkt1001 = blkt->blktdata;
235     break;
236   }
237 
238   return blkt;
239 } /* End of msr_addblockette() */
240 
241 /***************************************************************************
242  * msr_normalize_header:
243  *
244  * Normalize header values between the MSRecord struct and the
245  * associated fixed-section of the header and blockettes.  Essentially
246  * this updates the SEED structured data in the MSRecord.fsdh struct
247  * and MSRecord.blkts chain with values stored at the MSRecord level.
248  *
249  * Returns the header length in bytes on success or -1 on error.
250  ***************************************************************************/
251 int
msr_normalize_header(MSRecord * msr,flag verbose)252 msr_normalize_header (MSRecord *msr, flag verbose)
253 {
254   struct blkt_link_s *cur_blkt;
255   hptime_t hptimems;
256   int8_t usecoffset;
257   char seqnum[7];
258   int offset    = 0;
259   int blktcnt   = 0;
260   int reclenexp = 0;
261   int reclenfind;
262 
263   if (!msr)
264     return -1;
265 
266   /* Get start time rounded to tenths of milliseconds and microsecond offset */
267   ms_hptime2tomsusecoffset (msr->starttime, &hptimems, &usecoffset);
268 
269   /* Update values in fixed section of data header */
270   if (msr->fsdh)
271   {
272     if (verbose > 2)
273       ms_log (1, "Normalizing fixed section of data header\n");
274 
275     /* Roll-over sequence number if necessary */
276     if (msr->sequence_number > 999999)
277       msr->sequence_number = 1;
278 
279     /* Update values in the MSRecord.fsdh struct */
280     snprintf (seqnum, 7, "%06d", msr->sequence_number);
281     memcpy (msr->fsdh->sequence_number, seqnum, 6);
282     msr->fsdh->dataquality = msr->dataquality;
283     msr->fsdh->reserved    = ' ';
284     ms_strncpopen (msr->fsdh->network, msr->network, 2);
285     ms_strncpopen (msr->fsdh->station, msr->station, 5);
286     ms_strncpopen (msr->fsdh->location, msr->location, 2);
287     ms_strncpopen (msr->fsdh->channel, msr->channel, 3);
288     ms_hptime2btime (hptimems, &(msr->fsdh->start_time));
289 
290     /* Determine the factor and multipler for sample rate */
291     if (ms_genfactmult (msr->samprate,
292                         &(msr->fsdh->samprate_fact),
293                         &(msr->fsdh->samprate_mult)))
294     {
295       if (verbose > 1)
296         ms_log (1, "Sampling rate out of range, cannot generate factor & multiplier: %g\n",
297                 msr->samprate);
298       msr->fsdh->samprate_fact = 0;
299       msr->fsdh->samprate_mult = 0;
300     }
301 
302     offset += 48;
303 
304     if (msr->blkts)
305       msr->fsdh->blockette_offset = offset;
306     else
307       msr->fsdh->blockette_offset = 0;
308   }
309 
310   /* Traverse blockette chain and perform necessary updates */
311   cur_blkt = msr->blkts;
312 
313   if (cur_blkt && verbose > 2)
314     ms_log (1, "Normalizing blockette chain\n");
315 
316   while (cur_blkt)
317   {
318     offset += 4;
319 
320     if (cur_blkt->blkt_type == 100 && msr->Blkt100)
321     {
322       msr->Blkt100->samprate = (float)msr->samprate;
323       offset += sizeof (struct blkt_100_s);
324     }
325     else if (cur_blkt->blkt_type == 1000 && msr->Blkt1000)
326     {
327       msr->Blkt1000->byteorder = msr->byteorder;
328       msr->Blkt1000->encoding  = msr->encoding;
329 
330       /* Calculate the record length as an exponent of 2 */
331       for (reclenfind = 1, reclenexp = 1; reclenfind <= MAXRECLEN; reclenexp++)
332       {
333         reclenfind *= 2;
334         if (reclenfind == msr->reclen)
335           break;
336       }
337 
338       if (reclenfind != msr->reclen)
339       {
340         ms_log (2, "msr_normalize_header(): Record length %d is not a power of 2\n",
341                 msr->reclen);
342         return -1;
343       }
344 
345       msr->Blkt1000->reclen = reclenexp;
346 
347       offset += sizeof (struct blkt_1000_s);
348     }
349 
350     else if (cur_blkt->blkt_type == 1001)
351     {
352       msr->Blkt1001->usec = usecoffset;
353       offset += sizeof (struct blkt_1001_s);
354     }
355 
356     blktcnt++;
357     cur_blkt = cur_blkt->next;
358   }
359 
360   if (msr->fsdh)
361     msr->fsdh->numblockettes = blktcnt;
362 
363   return offset;
364 } /* End of msr_normalize_header() */
365 
366 /***************************************************************************
367  * msr_duplicate:
368  *
369  * Duplicate an MSRecord struct including the fixed-section data
370  * header and blockette chain.  If the datadup flag is true and the
371  * source MSRecord has associated data samples copy them as well.
372  *
373  * Returns a pointer to a new MSRecord on success and NULL on error.
374  ***************************************************************************/
375 MSRecord *
msr_duplicate(MSRecord * msr,flag datadup)376 msr_duplicate (MSRecord *msr, flag datadup)
377 {
378   MSRecord *dupmsr = 0;
379   int samplesize   = 0;
380 
381   if (!msr)
382     return NULL;
383 
384   /* Allocate target MSRecord structure */
385   if ((dupmsr = msr_init (NULL)) == NULL)
386     return NULL;
387 
388   /* Copy MSRecord structure */
389   memcpy (dupmsr, msr, sizeof (MSRecord));
390 
391   /* Copy fixed-section data header structure */
392   if (msr->fsdh)
393   {
394     /* Allocate memory for new FSDH structure */
395     if ((dupmsr->fsdh = (struct fsdh_s *)malloc (sizeof (struct fsdh_s))) == NULL)
396     {
397       ms_log (2, "msr_duplicate(): Error allocating memory\n");
398       free (dupmsr);
399       return NULL;
400     }
401 
402     /* Copy the contents */
403     memcpy (dupmsr->fsdh, msr->fsdh, sizeof (struct fsdh_s));
404   }
405 
406   /* Copy the blockette chain */
407   if (msr->blkts)
408   {
409     BlktLink *blkt = msr->blkts;
410     BlktLink *next = NULL;
411 
412     dupmsr->blkts = 0;
413     while (blkt)
414     {
415       next = blkt->next;
416 
417       /* Add blockette to chain of new MSRecord */
418       if (msr_addblockette (dupmsr, blkt->blktdata, blkt->blktdatalen,
419                             blkt->blkt_type, 0) == NULL)
420       {
421         ms_log (2, "msr_duplicate(): Error adding blockettes\n");
422         msr_free (&dupmsr);
423         return NULL;
424       }
425 
426       blkt = next;
427     }
428   }
429 
430   /* Copy data samples if requested and available */
431   if (datadup && msr->datasamples)
432   {
433     /* Determine size of samples in bytes */
434     samplesize = ms_samplesize (msr->sampletype);
435 
436     if (samplesize == 0)
437     {
438       ms_log (2, "msr_duplicate(): unrecognized sample type: '%c'\n",
439               msr->sampletype);
440       free (dupmsr);
441       return NULL;
442     }
443 
444     /* Allocate memory for new data array */
445     if ((dupmsr->datasamples = (void *)malloc ((size_t) (msr->numsamples * samplesize))) == NULL)
446     {
447       ms_log (2, "msr_duplicate(): Error allocating memory\n");
448       free (dupmsr);
449       return NULL;
450     }
451 
452     /* Copy the data array */
453     memcpy (dupmsr->datasamples, msr->datasamples, ((size_t) (msr->numsamples * samplesize)));
454   }
455   /* Otherwise make sure the sample array and count are zero */
456   else
457   {
458     dupmsr->datasamples = 0;
459     dupmsr->numsamples  = 0;
460   }
461 
462   return dupmsr;
463 } /* End of msr_duplicate() */
464 
465 /***************************************************************************
466  * msr_samprate:
467  *
468  * Calculate and return a double precision sample rate for the
469  * specified MSRecord.  If a Blockette 100 was included and parsed,
470  * the "Actual sample rate" (field 3) will be returned, otherwise a
471  * nominal sample rate will be calculated from the sample rate factor
472  * and multiplier in the fixed section data header.
473  *
474  * Returns the positive sample rate on success and -1.0 on error.
475  ***************************************************************************/
476 double
msr_samprate(MSRecord * msr)477 msr_samprate (MSRecord *msr)
478 {
479   if (!msr)
480     return -1.0;
481 
482   if (msr->Blkt100)
483     return (double)msr->Blkt100->samprate;
484   else
485     return msr_nomsamprate (msr);
486 } /* End of msr_samprate() */
487 
488 /***************************************************************************
489  * msr_nomsamprate:
490  *
491  * Calculate a double precision nominal sample rate from the sample
492  * rate factor and multiplier in the FSDH struct of the specified
493  * MSRecord.
494  *
495  * Returns the positive sample rate on success and -1.0 on error.
496  ***************************************************************************/
497 double
msr_nomsamprate(MSRecord * msr)498 msr_nomsamprate (MSRecord *msr)
499 {
500   if (!msr)
501     return -1.0;
502 
503   return ms_nomsamprate (msr->fsdh->samprate_fact, msr->fsdh->samprate_mult);
504 } /* End of msr_nomsamprate() */
505 
506 /***************************************************************************
507  * msr_starttime:
508  *
509  * Convert a btime struct of a FSDH struct of a MSRecord (the record
510  * start time) into a high precision epoch time and apply time
511  * corrections if any are specified in the header and bit 1 of the
512  * activity flags indicates that it has not already been applied.  If
513  * a Blockette 1001 is included and has been parsed the microseconds
514  * of field 4 are also applied.
515  *
516  * Returns a high precision epoch time on success and HPTERROR on
517  * error.
518  ***************************************************************************/
519 hptime_t
msr_starttime(MSRecord * msr)520 msr_starttime (MSRecord *msr)
521 {
522   hptime_t starttime = msr_starttime_uc (msr);
523 
524   if (!msr || starttime == HPTERROR)
525     return HPTERROR;
526 
527   /* Check if a correction is included and if it has been applied,
528      bit 1 of activity flags indicates if it has been appiled */
529 
530   if (msr->fsdh->time_correct != 0 &&
531       !(msr->fsdh->act_flags & 0x02))
532   {
533     starttime += (hptime_t)msr->fsdh->time_correct * (HPTMODULUS / 10000);
534   }
535 
536   /* Apply microsecond precision in a parsed Blockette 1001 */
537   if (msr->Blkt1001)
538   {
539     starttime += (hptime_t)msr->Blkt1001->usec * (HPTMODULUS / 1000000);
540   }
541 
542   return starttime;
543 } /* End of msr_starttime() */
544 
545 /***************************************************************************
546  * msr_starttime_uc:
547  *
548  * Convert a btime struct of a FSDH struct of a MSRecord (the record
549  * start time) into a high precision epoch time.  This time has no
550  * correction(s) applied to it.
551  *
552  * Returns a high precision epoch time on success and HPTERROR on
553  * error.
554  ***************************************************************************/
555 hptime_t
msr_starttime_uc(MSRecord * msr)556 msr_starttime_uc (MSRecord *msr)
557 {
558   if (!msr)
559     return HPTERROR;
560 
561   if (!msr->fsdh)
562     return HPTERROR;
563 
564   return ms_btime2hptime (&msr->fsdh->start_time);
565 } /* End of msr_starttime_uc() */
566 
567 /***************************************************************************
568  * msr_endtime:
569  *
570  * Calculate the time of the last sample in the record; this is the
571  * actual last sample time and *not* the time "covered" by the last
572  * sample.
573  *
574  * On the epoch time scale the value of a leap second is the same as
575  * the second following the leap second, without external information
576  * the values are ambiguous.
577  *
578  * Leap second handling: when a record completely contains a leap
579  * second, starts before and ends after, the calculated end time will
580  * be adjusted (reduced) by one second.
581  *
582  * Returns the time of the last sample as a high precision epoch time
583  * on success and HPTERROR on error.
584  ***************************************************************************/
585 hptime_t
msr_endtime(MSRecord * msr)586 msr_endtime (MSRecord *msr)
587 {
588   hptime_t span      = 0;
589   LeapSecond *lslist = leapsecondlist;
590 
591   if (!msr)
592     return HPTERROR;
593 
594   if (msr->samprate > 0.0 && msr->samplecnt > 0)
595     span = (hptime_t) (((double)(msr->samplecnt - 1) / msr->samprate * HPTMODULUS) + 0.5);
596 
597   /* Check if the record contains a leap second, if list is available */
598   if (lslist)
599   {
600     while (lslist)
601     {
602       if (lslist->leapsecond > msr->starttime &&
603           lslist->leapsecond <= (msr->starttime + span - HPTMODULUS))
604       {
605         span -= HPTMODULUS;
606         break;
607       }
608 
609       lslist = lslist->next;
610     }
611   }
612   else
613   {
614     /* If a positive leap second occurred during this record as denoted by
615      * bit 4 of the activity flags being set, reduce the end time to match
616      * the now shifted UTC time. */
617     if (msr->fsdh)
618       if (msr->fsdh->act_flags & 0x10)
619         span -= HPTMODULUS;
620   }
621 
622   return (msr->starttime + span);
623 } /* End of msr_endtime() */
624 
625 /***************************************************************************
626  * msr_srcname:
627  *
628  * Generate a source name string for a specified MSRecord in the
629  * format: 'NET_STA_LOC_CHAN' or, if the quality flag is true:
630  * 'NET_STA_LOC_CHAN_QUAL'.  The passed srcname must have enough room
631  * for the resulting string.
632  *
633  * Returns a pointer to the resulting string or NULL on error.
634  ***************************************************************************/
635 char *
msr_srcname(MSRecord * msr,char * srcname,flag quality)636 msr_srcname (MSRecord *msr, char *srcname, flag quality)
637 {
638   char *src = srcname;
639   char *cp  = srcname;
640 
641   if (!msr || !srcname)
642     return NULL;
643 
644   /* Build the source name string */
645   cp = msr->network;
646   while (*cp)
647   {
648     *src++ = *cp++;
649   }
650   *src++ = '_';
651   cp     = msr->station;
652   while (*cp)
653   {
654     *src++ = *cp++;
655   }
656   *src++ = '_';
657   cp     = msr->location;
658   while (*cp)
659   {
660     *src++ = *cp++;
661   }
662   *src++ = '_';
663   cp     = msr->channel;
664   while (*cp)
665   {
666     *src++ = *cp++;
667   }
668 
669   if (quality)
670   {
671     *src++ = '_';
672     *src++ = msr->dataquality;
673   }
674 
675   *src = '\0';
676 
677   return srcname;
678 } /* End of msr_srcname() */
679 
680 /***************************************************************************
681  * msr_print:
682  *
683  * Prints header values in an MSRecord struct, if 'details' is greater
684  * than 0 then detailed information about each blockette is printed.
685  * If 'details' is greater than 1 very detailed information is
686  * printed.  If no FSDH (msr->fsdh) is present only a single line with
687  * basic information is printed.
688  ***************************************************************************/
689 void
msr_print(MSRecord * msr,flag details)690 msr_print (MSRecord *msr, flag details)
691 {
692   double nomsamprate;
693   char srcname[50];
694   char time[25];
695   char b;
696   int idx;
697 
698   if (!msr)
699     return;
700 
701   /* Generate a source name string */
702   srcname[0] = '\0';
703   msr_srcname (msr, srcname, 0);
704 
705   /* Generate a start time string */
706   ms_hptime2seedtimestr (msr->starttime, time, 1);
707 
708   /* Report information in the fixed header */
709   if (details > 0 && msr->fsdh)
710   {
711     nomsamprate = msr_nomsamprate (msr);
712 
713     ms_log (0, "%s, %06d, %c\n", srcname, msr->sequence_number, msr->dataquality);
714     ms_log (0, "             start time: %s\n", time);
715     ms_log (0, "      number of samples: %d\n", msr->fsdh->numsamples);
716     ms_log (0, "     sample rate factor: %d  (%.10g samples per second)\n",
717             msr->fsdh->samprate_fact, nomsamprate);
718     ms_log (0, " sample rate multiplier: %d\n", msr->fsdh->samprate_mult);
719 
720     if (details > 1)
721     {
722       /* Activity flags */
723       b = msr->fsdh->act_flags;
724       ms_log (0, "         activity flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
725               bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
726               bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
727       if (b & 0x01)
728         ms_log (0, "                         [Bit 0] Calibration signals present\n");
729       if (b & 0x02)
730         ms_log (0, "                         [Bit 1] Time correction applied\n");
731       if (b & 0x04)
732         ms_log (0, "                         [Bit 2] Beginning of an event, station trigger\n");
733       if (b & 0x08)
734         ms_log (0, "                         [Bit 3] End of an event, station detrigger\n");
735       if (b & 0x10)
736         ms_log (0, "                         [Bit 4] A positive leap second happened in this record\n");
737       if (b & 0x20)
738         ms_log (0, "                         [Bit 5] A negative leap second happened in this record\n");
739       if (b & 0x40)
740         ms_log (0, "                         [Bit 6] Event in progress\n");
741       if (b & 0x80)
742         ms_log (0, "                         [Bit 7] Undefined bit set\n");
743 
744       /* I/O and clock flags */
745       b = msr->fsdh->io_flags;
746       ms_log (0, "    I/O and clock flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
747               bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
748               bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
749       if (b & 0x01)
750         ms_log (0, "                         [Bit 0] Station volume parity error possibly present\n");
751       if (b & 0x02)
752         ms_log (0, "                         [Bit 1] Long record read (possibly no problem)\n");
753       if (b & 0x04)
754         ms_log (0, "                         [Bit 2] Short record read (record padded)\n");
755       if (b & 0x08)
756         ms_log (0, "                         [Bit 3] Start of time series\n");
757       if (b & 0x10)
758         ms_log (0, "                         [Bit 4] End of time series\n");
759       if (b & 0x20)
760         ms_log (0, "                         [Bit 5] Clock locked\n");
761       if (b & 0x40)
762         ms_log (0, "                         [Bit 6] Undefined bit set\n");
763       if (b & 0x80)
764         ms_log (0, "                         [Bit 7] Undefined bit set\n");
765 
766       /* Data quality flags */
767       b = msr->fsdh->dq_flags;
768       ms_log (0, "     data quality flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
769               bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
770               bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
771       if (b & 0x01)
772         ms_log (0, "                         [Bit 0] Amplifier saturation detected\n");
773       if (b & 0x02)
774         ms_log (0, "                         [Bit 1] Digitizer clipping detected\n");
775       if (b & 0x04)
776         ms_log (0, "                         [Bit 2] Spikes detected\n");
777       if (b & 0x08)
778         ms_log (0, "                         [Bit 3] Glitches detected\n");
779       if (b & 0x10)
780         ms_log (0, "                         [Bit 4] Missing/padded data present\n");
781       if (b & 0x20)
782         ms_log (0, "                         [Bit 5] Telemetry synchronization error\n");
783       if (b & 0x40)
784         ms_log (0, "                         [Bit 6] A digital filter may be charging\n");
785       if (b & 0x80)
786         ms_log (0, "                         [Bit 7] Time tag is questionable\n");
787     }
788 
789     ms_log (0, "   number of blockettes: %d\n", msr->fsdh->numblockettes);
790     ms_log (0, "        time correction: %ld\n", (long int)msr->fsdh->time_correct);
791     ms_log (0, "            data offset: %d\n", msr->fsdh->data_offset);
792     ms_log (0, " first blockette offset: %d\n", msr->fsdh->blockette_offset);
793   }
794   else
795   {
796     ms_log (0, "%s, %06d, %c, %d, %" PRId64 " samples, %-.10g Hz, %s\n",
797             srcname, msr->sequence_number, msr->dataquality,
798             msr->reclen, msr->samplecnt, msr->samprate, time);
799   }
800 
801   /* Report information in the blockette chain */
802   if (details > 0 && msr->blkts)
803   {
804     BlktLink *cur_blkt = msr->blkts;
805 
806     while (cur_blkt)
807     {
808       if (cur_blkt->blkt_type == 100)
809       {
810         struct blkt_100_s *blkt_100 = (struct blkt_100_s *)cur_blkt->blktdata;
811 
812         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
813                 ms_blktdesc (cur_blkt->blkt_type));
814         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
815         ms_log (0, "          actual sample rate: %.10g\n", blkt_100->samprate);
816 
817         if (details > 1)
818         {
819           b = blkt_100->flags;
820           ms_log (0, "             undefined flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
821                   bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
822                   bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
823 
824           ms_log (0, "          reserved bytes (3): %u,%u,%u\n",
825                   blkt_100->reserved[0], blkt_100->reserved[1], blkt_100->reserved[2]);
826         }
827       }
828 
829       else if (cur_blkt->blkt_type == 200)
830       {
831         struct blkt_200_s *blkt_200 = (struct blkt_200_s *)cur_blkt->blktdata;
832 
833         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
834                 ms_blktdesc (cur_blkt->blkt_type));
835         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
836         ms_log (0, "            signal amplitude: %g\n", blkt_200->amplitude);
837         ms_log (0, "               signal period: %g\n", blkt_200->period);
838         ms_log (0, "         background estimate: %g\n", blkt_200->background_estimate);
839 
840         if (details > 1)
841         {
842           b = blkt_200->flags;
843           ms_log (0, "       event detection flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
844                   bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
845                   bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
846           if (b & 0x01)
847             ms_log (0, "                         [Bit 0] 1: Dilatation wave\n");
848           else
849             ms_log (0, "                         [Bit 0] 0: Compression wave\n");
850           if (b & 0x02)
851             ms_log (0, "                         [Bit 1] 1: Units after deconvolution\n");
852           else
853             ms_log (0, "                         [Bit 1] 0: Units are digital counts\n");
854           if (b & 0x04)
855             ms_log (0, "                         [Bit 2] Bit 0 is undetermined\n");
856           ms_log (0, "               reserved byte: %u\n", blkt_200->reserved);
857         }
858 
859         ms_btime2seedtimestr (&blkt_200->time, time);
860         ms_log (0, "           signal onset time: %s\n", time);
861         ms_log (0, "               detector name: %.24s\n", blkt_200->detector);
862       }
863 
864       else if (cur_blkt->blkt_type == 201)
865       {
866         struct blkt_201_s *blkt_201 = (struct blkt_201_s *)cur_blkt->blktdata;
867 
868         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
869                 ms_blktdesc (cur_blkt->blkt_type));
870         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
871         ms_log (0, "            signal amplitude: %g\n", blkt_201->amplitude);
872         ms_log (0, "               signal period: %g\n", blkt_201->period);
873         ms_log (0, "         background estimate: %g\n", blkt_201->background_estimate);
874 
875         b = blkt_201->flags;
876         ms_log (0, "       event detection flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
877                 bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
878                 bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
879         if (b & 0x01)
880           ms_log (0, "                         [Bit 0] 1: Dilation wave\n");
881         else
882           ms_log (0, "                         [Bit 0] 0: Compression wave\n");
883 
884         if (details > 1)
885           ms_log (0, "               reserved byte: %u\n", blkt_201->reserved);
886         ms_btime2seedtimestr (&blkt_201->time, time);
887         ms_log (0, "           signal onset time: %s\n", time);
888         ms_log (0, "                  SNR values: ");
889         for (idx = 0; idx < 6; idx++)
890           ms_log (0, "%u  ", blkt_201->snr_values[idx]);
891         ms_log (0, "\n");
892         ms_log (0, "              loopback value: %u\n", blkt_201->loopback);
893         ms_log (0, "              pick algorithm: %u\n", blkt_201->pick_algorithm);
894         ms_log (0, "               detector name: %.24s\n", blkt_201->detector);
895       }
896 
897       else if (cur_blkt->blkt_type == 300)
898       {
899         struct blkt_300_s *blkt_300 = (struct blkt_300_s *)cur_blkt->blktdata;
900 
901         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
902                 ms_blktdesc (cur_blkt->blkt_type));
903         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
904         ms_btime2seedtimestr (&blkt_300->time, time);
905         ms_log (0, "      calibration start time: %s\n", time);
906         ms_log (0, "      number of calibrations: %u\n", blkt_300->numcalibrations);
907 
908         b = blkt_300->flags;
909         ms_log (0, "           calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
910                 bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
911                 bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
912         if (b & 0x01)
913           ms_log (0, "                         [Bit 0] First pulse is positive\n");
914         if (b & 0x02)
915           ms_log (0, "                         [Bit 1] Calibration's alternate sign\n");
916         if (b & 0x04)
917           ms_log (0, "                         [Bit 2] Calibration was automatic\n");
918         if (b & 0x08)
919           ms_log (0, "                         [Bit 3] Calibration continued from previous record(s)\n");
920 
921         ms_log (0, "               step duration: %u\n", blkt_300->step_duration);
922         ms_log (0, "           interval duration: %u\n", blkt_300->interval_duration);
923         ms_log (0, "            signal amplitude: %g\n", blkt_300->amplitude);
924         ms_log (0, "        input signal channel: %.3s", blkt_300->input_channel);
925         if (details > 1)
926           ms_log (0, "               reserved byte: %u\n", blkt_300->reserved);
927         ms_log (0, "         reference amplitude: %u\n", blkt_300->reference_amplitude);
928         ms_log (0, "                    coupling: %.12s\n", blkt_300->coupling);
929         ms_log (0, "                     rolloff: %.12s\n", blkt_300->rolloff);
930       }
931 
932       else if (cur_blkt->blkt_type == 310)
933       {
934         struct blkt_310_s *blkt_310 = (struct blkt_310_s *)cur_blkt->blktdata;
935 
936         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
937                 ms_blktdesc (cur_blkt->blkt_type));
938         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
939         ms_btime2seedtimestr (&blkt_310->time, time);
940         ms_log (0, "      calibration start time: %s\n", time);
941         if (details > 1)
942           ms_log (0, "               reserved byte: %u\n", blkt_310->reserved1);
943 
944         b = blkt_310->flags;
945         ms_log (0, "           calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
946                 bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
947                 bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
948         if (b & 0x04)
949           ms_log (0, "                         [Bit 2] Calibration was automatic\n");
950         if (b & 0x08)
951           ms_log (0, "                         [Bit 3] Calibration continued from previous record(s)\n");
952         if (b & 0x10)
953           ms_log (0, "                         [Bit 4] Peak-to-peak amplitude\n");
954         if (b & 0x20)
955           ms_log (0, "                         [Bit 5] Zero-to-peak amplitude\n");
956         if (b & 0x40)
957           ms_log (0, "                         [Bit 6] RMS amplitude\n");
958 
959         ms_log (0, "        calibration duration: %u\n", blkt_310->duration);
960         ms_log (0, "               signal period: %g\n", blkt_310->period);
961         ms_log (0, "            signal amplitude: %g\n", blkt_310->amplitude);
962         ms_log (0, "        input signal channel: %.3s", blkt_310->input_channel);
963         if (details > 1)
964           ms_log (0, "               reserved byte: %u\n", blkt_310->reserved2);
965         ms_log (0, "         reference amplitude: %u\n", blkt_310->reference_amplitude);
966         ms_log (0, "                    coupling: %.12s\n", blkt_310->coupling);
967         ms_log (0, "                     rolloff: %.12s\n", blkt_310->rolloff);
968       }
969 
970       else if (cur_blkt->blkt_type == 320)
971       {
972         struct blkt_320_s *blkt_320 = (struct blkt_320_s *)cur_blkt->blktdata;
973 
974         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
975                 ms_blktdesc (cur_blkt->blkt_type));
976         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
977         ms_btime2seedtimestr (&blkt_320->time, time);
978         ms_log (0, "      calibration start time: %s\n", time);
979         if (details > 1)
980           ms_log (0, "               reserved byte: %u\n", blkt_320->reserved1);
981 
982         b = blkt_320->flags;
983         ms_log (0, "           calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
984                 bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
985                 bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
986         if (b & 0x04)
987           ms_log (0, "                         [Bit 2] Calibration was automatic\n");
988         if (b & 0x08)
989           ms_log (0, "                         [Bit 3] Calibration continued from previous record(s)\n");
990         if (b & 0x10)
991           ms_log (0, "                         [Bit 4] Random amplitudes\n");
992 
993         ms_log (0, "        calibration duration: %u\n", blkt_320->duration);
994         ms_log (0, "      peak-to-peak amplitude: %g\n", blkt_320->ptp_amplitude);
995         ms_log (0, "        input signal channel: %.3s", blkt_320->input_channel);
996         if (details > 1)
997           ms_log (0, "               reserved byte: %u\n", blkt_320->reserved2);
998         ms_log (0, "         reference amplitude: %u\n", blkt_320->reference_amplitude);
999         ms_log (0, "                    coupling: %.12s\n", blkt_320->coupling);
1000         ms_log (0, "                     rolloff: %.12s\n", blkt_320->rolloff);
1001         ms_log (0, "                  noise type: %.8s\n", blkt_320->noise_type);
1002       }
1003 
1004       else if (cur_blkt->blkt_type == 390)
1005       {
1006         struct blkt_390_s *blkt_390 = (struct blkt_390_s *)cur_blkt->blktdata;
1007 
1008         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
1009                 ms_blktdesc (cur_blkt->blkt_type));
1010         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1011         ms_btime2seedtimestr (&blkt_390->time, time);
1012         ms_log (0, "      calibration start time: %s\n", time);
1013         if (details > 1)
1014           ms_log (0, "               reserved byte: %u\n", blkt_390->reserved1);
1015 
1016         b = blkt_390->flags;
1017         ms_log (0, "           calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
1018                 bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
1019                 bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
1020         if (b & 0x04)
1021           ms_log (0, "                         [Bit 2] Calibration was automatic\n");
1022         if (b & 0x08)
1023           ms_log (0, "                         [Bit 3] Calibration continued from previous record(s)\n");
1024 
1025         ms_log (0, "        calibration duration: %u\n", blkt_390->duration);
1026         ms_log (0, "            signal amplitude: %g\n", blkt_390->amplitude);
1027         ms_log (0, "        input signal channel: %.3s", blkt_390->input_channel);
1028         if (details > 1)
1029           ms_log (0, "               reserved byte: %u\n", blkt_390->reserved2);
1030       }
1031 
1032       else if (cur_blkt->blkt_type == 395)
1033       {
1034         struct blkt_395_s *blkt_395 = (struct blkt_395_s *)cur_blkt->blktdata;
1035 
1036         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
1037                 ms_blktdesc (cur_blkt->blkt_type));
1038         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1039         ms_btime2seedtimestr (&blkt_395->time, time);
1040         ms_log (0, "        calibration end time: %s\n", time);
1041         if (details > 1)
1042           ms_log (0, "          reserved bytes (2): %u,%u\n",
1043                   blkt_395->reserved[0], blkt_395->reserved[1]);
1044       }
1045 
1046       else if (cur_blkt->blkt_type == 400)
1047       {
1048         struct blkt_400_s *blkt_400 = (struct blkt_400_s *)cur_blkt->blktdata;
1049 
1050         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
1051                 ms_blktdesc (cur_blkt->blkt_type));
1052         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1053         ms_log (0, "      beam azimuth (degrees): %g\n", blkt_400->azimuth);
1054         ms_log (0, "  beam slowness (sec/degree): %g\n", blkt_400->slowness);
1055         ms_log (0, "               configuration: %u\n", blkt_400->configuration);
1056         if (details > 1)
1057           ms_log (0, "          reserved bytes (2): %u,%u\n",
1058                   blkt_400->reserved[0], blkt_400->reserved[1]);
1059       }
1060 
1061       else if (cur_blkt->blkt_type == 405)
1062       {
1063         struct blkt_405_s *blkt_405 = (struct blkt_405_s *)cur_blkt->blktdata;
1064 
1065         ms_log (0, "          BLOCKETTE %u: (%s, incomplete)\n", cur_blkt->blkt_type,
1066                 ms_blktdesc (cur_blkt->blkt_type));
1067         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1068         ms_log (0, "           first delay value: %u\n", blkt_405->delay_values[0]);
1069       }
1070 
1071       else if (cur_blkt->blkt_type == 500)
1072       {
1073         struct blkt_500_s *blkt_500 = (struct blkt_500_s *)cur_blkt->blktdata;
1074 
1075         ms_log (0, "          BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
1076                 ms_blktdesc (cur_blkt->blkt_type));
1077         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1078         ms_log (0, "              VCO correction: %g%%\n", blkt_500->vco_correction);
1079         ms_btime2seedtimestr (&blkt_500->time, time);
1080         ms_log (0, "           time of exception: %s\n", time);
1081         ms_log (0, "                        usec: %d\n", blkt_500->usec);
1082         ms_log (0, "           reception quality: %u%%\n", blkt_500->reception_qual);
1083         ms_log (0, "             exception count: %u\n", blkt_500->exception_count);
1084         ms_log (0, "              exception type: %.16s\n", blkt_500->exception_type);
1085         ms_log (0, "                 clock model: %.32s\n", blkt_500->clock_model);
1086         ms_log (0, "                clock status: %.128s\n", blkt_500->clock_status);
1087       }
1088 
1089       else if (cur_blkt->blkt_type == 1000)
1090       {
1091         struct blkt_1000_s *blkt_1000 = (struct blkt_1000_s *)cur_blkt->blktdata;
1092         int recsize;
1093         char order[40];
1094 
1095         /* Calculate record size in bytes as 2^(blkt_1000->rec_len) */
1096         recsize = (unsigned int)1 << blkt_1000->reclen;
1097 
1098         /* Big or little endian? */
1099         if (blkt_1000->byteorder == 0)
1100           strncpy (order, "Little endian", sizeof (order) - 1);
1101         else if (blkt_1000->byteorder == 1)
1102           strncpy (order, "Big endian", sizeof (order) - 1);
1103         else
1104           strncpy (order, "Unknown value", sizeof (order) - 1);
1105 
1106         ms_log (0, "         BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
1107                 ms_blktdesc (cur_blkt->blkt_type));
1108         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1109         ms_log (0, "                    encoding: %s (val:%u)\n",
1110                 (char *)ms_encodingstr (blkt_1000->encoding), blkt_1000->encoding);
1111         ms_log (0, "                  byte order: %s (val:%u)\n",
1112                 order, blkt_1000->byteorder);
1113         ms_log (0, "               record length: %d (val:%u)\n",
1114                 recsize, blkt_1000->reclen);
1115 
1116         if (details > 1)
1117           ms_log (0, "               reserved byte: %u\n", blkt_1000->reserved);
1118       }
1119 
1120       else if (cur_blkt->blkt_type == 1001)
1121       {
1122         struct blkt_1001_s *blkt_1001 = (struct blkt_1001_s *)cur_blkt->blktdata;
1123 
1124         ms_log (0, "         BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
1125                 ms_blktdesc (cur_blkt->blkt_type));
1126         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1127         ms_log (0, "              timing quality: %u%%\n", blkt_1001->timing_qual);
1128         ms_log (0, "                micro second: %d\n", blkt_1001->usec);
1129 
1130         if (details > 1)
1131           ms_log (0, "               reserved byte: %u\n", blkt_1001->reserved);
1132 
1133         ms_log (0, "                 frame count: %u\n", blkt_1001->framecnt);
1134       }
1135 
1136       else if (cur_blkt->blkt_type == 2000)
1137       {
1138         struct blkt_2000_s *blkt_2000 = (struct blkt_2000_s *)cur_blkt->blktdata;
1139         char order[40];
1140 
1141         /* Big or little endian? */
1142         if (blkt_2000->byteorder == 0)
1143           strncpy (order, "Little endian", sizeof (order) - 1);
1144         else if (blkt_2000->byteorder == 1)
1145           strncpy (order, "Big endian", sizeof (order) - 1);
1146         else
1147           strncpy (order, "Unknown value", sizeof (order) - 1);
1148 
1149         ms_log (0, "         BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
1150                 ms_blktdesc (cur_blkt->blkt_type));
1151         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1152         ms_log (0, "            blockette length: %u\n", blkt_2000->length);
1153         ms_log (0, "                 data offset: %u\n", blkt_2000->data_offset);
1154         ms_log (0, "               record number: %u\n", blkt_2000->recnum);
1155         ms_log (0, "                  byte order: %s (val:%u)\n",
1156                 order, blkt_2000->byteorder);
1157         b = blkt_2000->flags;
1158         ms_log (0, "                  data flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
1159                 bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
1160                 bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
1161 
1162         if (details > 1)
1163         {
1164           if (b & 0x01)
1165             ms_log (0, "                         [Bit 0] 1: Stream oriented\n");
1166           else
1167             ms_log (0, "                         [Bit 0] 0: Record oriented\n");
1168           if (b & 0x02)
1169             ms_log (0, "                         [Bit 1] 1: Blockette 2000s may NOT be packaged\n");
1170           else
1171             ms_log (0, "                         [Bit 1] 0: Blockette 2000s may be packaged\n");
1172           if (!(b & 0x04) && !(b & 0x08))
1173             ms_log (0, "                      [Bits 2-3] 00: Complete blockette\n");
1174           else if (!(b & 0x04) && (b & 0x08))
1175             ms_log (0, "                      [Bits 2-3] 01: First blockette in span\n");
1176           else if ((b & 0x04) && (b & 0x08))
1177             ms_log (0, "                      [Bits 2-3] 11: Continuation blockette in span\n");
1178           else if ((b & 0x04) && !(b & 0x08))
1179             ms_log (0, "                      [Bits 2-3] 10: Final blockette in span\n");
1180           if (!(b & 0x10) && !(b & 0x20))
1181             ms_log (0, "                      [Bits 4-5] 00: Not file oriented\n");
1182           else if (!(b & 0x10) && (b & 0x20))
1183             ms_log (0, "                      [Bits 4-5] 01: First blockette of file\n");
1184           else if ((b & 0x10) && !(b & 0x20))
1185             ms_log (0, "                      [Bits 4-5] 10: Continuation of file\n");
1186           else if ((b & 0x10) && (b & 0x20))
1187             ms_log (0, "                      [Bits 4-5] 11: Last blockette of file\n");
1188         }
1189 
1190         ms_log (0, "           number of headers: %u\n", blkt_2000->numheaders);
1191 
1192         /* Crude display of the opaque data headers */
1193         if (details > 1)
1194           ms_log (0, "                     headers: %.*s\n",
1195                   (blkt_2000->data_offset - 15), blkt_2000->payload);
1196       }
1197 
1198       else
1199       {
1200         ms_log (0, "         BLOCKETTE %u: (%s, not parsed)\n", cur_blkt->blkt_type,
1201                 ms_blktdesc (cur_blkt->blkt_type));
1202         ms_log (0, "              next blockette: %u\n", cur_blkt->next_blkt);
1203       }
1204 
1205       cur_blkt = cur_blkt->next;
1206     }
1207   }
1208 } /* End of msr_print() */
1209 
1210 /***************************************************************************
1211  * msr_host_latency:
1212  *
1213  * Calculate the latency based on the host time in UTC accounting for
1214  * the time covered using the number of samples and sample rate; in
1215  * other words, the difference between the host time and the time of
1216  * the last sample in the specified Mini-SEED record.
1217  *
1218  * Double precision is returned, but the true precision is dependent
1219  * on the accuracy of the host system clock among other things.
1220  *
1221  * Returns seconds of latency or 0.0 on error (indistinguishable from
1222  * 0.0 latency).
1223  ***************************************************************************/
1224 double
msr_host_latency(MSRecord * msr)1225 msr_host_latency (MSRecord *msr)
1226 {
1227   double span = 0.0; /* Time covered by the samples */
1228   double epoch;      /* Current epoch time */
1229   double latency = 0.0;
1230   time_t tv;
1231 
1232   if (msr == NULL)
1233     return 0.0;
1234 
1235   /* Calculate the time covered by the samples */
1236   if (msr->samprate > 0.0 && msr->samplecnt > 0)
1237     span = (1.0 / msr->samprate) * (msr->samplecnt - 1);
1238 
1239   /* Grab UTC time according to the system clock */
1240   epoch = (double)time (&tv);
1241 
1242   /* Now calculate the latency */
1243   latency = epoch - ((double)msr->starttime / HPTMODULUS) - span;
1244 
1245   return latency;
1246 } /* End of msr_host_latency() */
1247