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