1 /***************************************************************************
2 * traceutils.c:
3 *
4 * Generic routines to handle Traces.
5 *
6 * Written by Chad Trabant, IRIS Data Management Center
7 *
8 * modified: 2015.108
9 ***************************************************************************/
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <time.h>
15
16 #include "libmseed.h"
17
18 static int mst_groupsort_cmp (MSTrace *mst1, MSTrace *mst2, flag quality);
19
20 /***************************************************************************
21 * mst_init:
22 *
23 * Initialize and return a MSTrace struct, allocating memory if needed.
24 * If the specified MSTrace includes data samples they will be freed.
25 *
26 * Returns a pointer to a MSTrace struct on success or NULL on error.
27 ***************************************************************************/
28 MSTrace *
mst_init(MSTrace * mst)29 mst_init (MSTrace *mst)
30 {
31 /* Free datasamples, prvtptr and stream state if present */
32 if (mst)
33 {
34 if (mst->datasamples)
35 free (mst->datasamples);
36
37 if (mst->prvtptr)
38 free (mst->prvtptr);
39
40 if (mst->ststate)
41 free (mst->ststate);
42 }
43 else
44 {
45 mst = (MSTrace *)malloc (sizeof (MSTrace));
46 }
47
48 if (mst == NULL)
49 {
50 ms_log (2, "mst_init(): Cannot allocate memory\n");
51 return NULL;
52 }
53
54 memset (mst, 0, sizeof (MSTrace));
55
56 return mst;
57 } /* End of mst_init() */
58
59 /***************************************************************************
60 * mst_free:
61 *
62 * Free all memory associated with a MSTrace struct and set the pointer
63 * to 0.
64 ***************************************************************************/
65 void
mst_free(MSTrace ** ppmst)66 mst_free (MSTrace **ppmst)
67 {
68 if (ppmst && *ppmst)
69 {
70 /* Free datasamples if present */
71 if ((*ppmst)->datasamples)
72 free ((*ppmst)->datasamples);
73
74 /* Free private memory if present */
75 if ((*ppmst)->prvtptr)
76 free ((*ppmst)->prvtptr);
77
78 /* Free stream processing state if present */
79 if ((*ppmst)->ststate)
80 free ((*ppmst)->ststate);
81
82 free (*ppmst);
83
84 *ppmst = 0;
85 }
86 } /* End of mst_free() */
87
88 /***************************************************************************
89 * mst_initgroup:
90 *
91 * Initialize and return a MSTraceGroup struct, allocating memory if
92 * needed. If the supplied MSTraceGroup is not NULL any associated
93 * memory it will be freed.
94 *
95 * Returns a pointer to a MSTraceGroup struct on success or NULL on error.
96 ***************************************************************************/
97 MSTraceGroup *
mst_initgroup(MSTraceGroup * mstg)98 mst_initgroup (MSTraceGroup *mstg)
99 {
100 MSTrace *mst = 0;
101 MSTrace *next = 0;
102
103 if (mstg)
104 {
105 mst = mstg->traces;
106
107 while (mst)
108 {
109 next = mst->next;
110 mst_free (&mst);
111 mst = next;
112 }
113 }
114 else
115 {
116 mstg = (MSTraceGroup *)malloc (sizeof (MSTraceGroup));
117 }
118
119 if (mstg == NULL)
120 {
121 ms_log (2, "mst_initgroup(): Cannot allocate memory\n");
122 return NULL;
123 }
124
125 memset (mstg, 0, sizeof (MSTraceGroup));
126
127 return mstg;
128 } /* End of mst_initgroup() */
129
130 /***************************************************************************
131 * mst_freegroup:
132 *
133 * Free all memory associated with a MSTraceGroup struct and set the
134 * pointer to 0.
135 ***************************************************************************/
136 void
mst_freegroup(MSTraceGroup ** ppmstg)137 mst_freegroup (MSTraceGroup **ppmstg)
138 {
139 MSTrace *mst = 0;
140 MSTrace *next = 0;
141
142 if (*ppmstg)
143 {
144 mst = (*ppmstg)->traces;
145
146 while (mst)
147 {
148 next = mst->next;
149 mst_free (&mst);
150 mst = next;
151 }
152
153 free (*ppmstg);
154
155 *ppmstg = 0;
156 }
157 } /* End of mst_freegroup() */
158
159 /***************************************************************************
160 * mst_findmatch:
161 *
162 * Traverse the MSTrace chain starting at 'startmst' until a MSTrace
163 * is found that matches the given name identifiers. If the dataquality
164 * byte is not 0 it must also match.
165 *
166 * Return a pointer a matching MSTrace otherwise 0 if no match found.
167 ***************************************************************************/
168 MSTrace *
mst_findmatch(MSTrace * startmst,char dataquality,char * network,char * station,char * location,char * channel)169 mst_findmatch (MSTrace *startmst, char dataquality,
170 char *network, char *station, char *location, char *channel)
171 {
172 int idx;
173
174 if (!startmst)
175 return 0;
176
177 while (startmst)
178 {
179 if (dataquality && dataquality != startmst->dataquality)
180 {
181 startmst = startmst->next;
182 continue;
183 }
184
185 /* Compare network */
186 idx = 0;
187 while (network[idx] == startmst->network[idx])
188 {
189 if (network[idx] == '\0')
190 break;
191 idx++;
192 }
193 if (network[idx] != '\0' || startmst->network[idx] != '\0')
194 {
195 startmst = startmst->next;
196 continue;
197 }
198 /* Compare station */
199 idx = 0;
200 while (station[idx] == startmst->station[idx])
201 {
202 if (station[idx] == '\0')
203 break;
204 idx++;
205 }
206 if (station[idx] != '\0' || startmst->station[idx] != '\0')
207 {
208 startmst = startmst->next;
209 continue;
210 }
211 /* Compare location */
212 idx = 0;
213 while (location[idx] == startmst->location[idx])
214 {
215 if (location[idx] == '\0')
216 break;
217 idx++;
218 }
219 if (location[idx] != '\0' || startmst->location[idx] != '\0')
220 {
221 startmst = startmst->next;
222 continue;
223 }
224 /* Compare channel */
225 idx = 0;
226 while (channel[idx] == startmst->channel[idx])
227 {
228 if (channel[idx] == '\0')
229 break;
230 idx++;
231 }
232 if (channel[idx] != '\0' || startmst->channel[idx] != '\0')
233 {
234 startmst = startmst->next;
235 continue;
236 }
237
238 /* A match was found if we made it this far */
239 break;
240 }
241
242 return startmst;
243 } /* End of mst_findmatch() */
244
245 /***************************************************************************
246 * mst_findadjacent:
247 *
248 * Find a MSTrace in a MSTraceGroup matching the given name
249 * identifiers, samplerate and is adjacent with a time span. If the
250 * dataquality byte is not 0 it must also match.
251 *
252 * The time tolerance and sample rate tolerance are used to determine
253 * if traces abut. If timetol is -1.0 the default tolerance of 1/2
254 * the sample period will be used. If samprratetol is -1.0 the
255 * default tolerance check of abs(1-sr1/sr2) < 0.0001 is used (defined
256 * in libmseed.h). If timetol or sampratetol is -2.0 the respective
257 * tolerance check will not be performed.
258 *
259 * The 'whence' flag will be set, when a matching MSTrace is found, to
260 * indicate where the indicated time span is adjacent to the MSTrace
261 * using the following values:
262 * 1: time span fits at the end of the MSTrace
263 * 2: time span fits at the beginning of the MSTrace
264 *
265 * Return a pointer a matching MSTrace and set the 'whence' flag
266 * otherwise 0 if no match found.
267 ***************************************************************************/
268 MSTrace *
mst_findadjacent(MSTraceGroup * mstg,flag * whence,char dataquality,char * network,char * station,char * location,char * channel,double samprate,double sampratetol,hptime_t starttime,hptime_t endtime,double timetol)269 mst_findadjacent (MSTraceGroup *mstg, flag *whence, char dataquality,
270 char *network, char *station, char *location, char *channel,
271 double samprate, double sampratetol,
272 hptime_t starttime, hptime_t endtime, double timetol)
273 {
274 MSTrace *mst = 0;
275 hptime_t pregap;
276 hptime_t postgap;
277 hptime_t hpdelta;
278 hptime_t hptimetol = 0;
279 hptime_t nhptimetol = 0;
280 int idx;
281
282 if (!mstg)
283 return 0;
284
285 *whence = 0;
286
287 /* Calculate high-precision sample period */
288 hpdelta = (hptime_t) ((samprate) ? (HPTMODULUS / samprate) : 0.0);
289
290 /* Calculate high-precision time tolerance */
291 if (timetol == -1.0)
292 hptimetol = (hptime_t) (0.5 * hpdelta); /* Default time tolerance is 1/2 sample period */
293 else if (timetol >= 0.0)
294 hptimetol = (hptime_t) (timetol * HPTMODULUS);
295
296 nhptimetol = (hptimetol) ? -hptimetol : 0;
297
298 mst = mstg->traces;
299
300 while (mst)
301 {
302 /* post/pregap are negative when the record overlaps the trace
303 * segment and positive when there is a time gap. */
304 postgap = starttime - mst->endtime - hpdelta;
305
306 pregap = mst->starttime - endtime - hpdelta;
307
308 /* If not checking the time tolerance decide if beginning or end is a better fit */
309 if (timetol == -2.0)
310 {
311 if (ms_dabs ((double)postgap) < ms_dabs ((double)pregap))
312 *whence = 1;
313 else
314 *whence = 2;
315 }
316 else
317 {
318 if (postgap <= hptimetol && postgap >= nhptimetol)
319 {
320 /* Span fits right at the end of the trace */
321 *whence = 1;
322 }
323 else if (pregap <= hptimetol && pregap >= nhptimetol)
324 {
325 /* Span fits right at the beginning of the trace */
326 *whence = 2;
327 }
328 else
329 {
330 /* Span does not fit with this Trace */
331 mst = mst->next;
332 continue;
333 }
334 }
335
336 /* Perform samprate tolerance check if requested */
337 if (sampratetol != -2.0)
338 {
339 /* Perform default samprate tolerance check if requested */
340 if (sampratetol == -1.0)
341 {
342 if (!MS_ISRATETOLERABLE (samprate, mst->samprate))
343 {
344 mst = mst->next;
345 continue;
346 }
347 }
348 /* Otherwise check against the specified sample rate tolerance */
349 else if (ms_dabs (samprate - mst->samprate) > sampratetol)
350 {
351 mst = mst->next;
352 continue;
353 }
354 }
355
356 /* Compare data qualities */
357 if (dataquality && dataquality != mst->dataquality)
358 {
359 mst = mst->next;
360 continue;
361 }
362
363 /* Compare network */
364 idx = 0;
365 while (network[idx] == mst->network[idx])
366 {
367 if (network[idx] == '\0')
368 break;
369 idx++;
370 }
371 if (network[idx] != '\0' || mst->network[idx] != '\0')
372 {
373 mst = mst->next;
374 continue;
375 }
376 /* Compare station */
377 idx = 0;
378 while (station[idx] == mst->station[idx])
379 {
380 if (station[idx] == '\0')
381 break;
382 idx++;
383 }
384 if (station[idx] != '\0' || mst->station[idx] != '\0')
385 {
386 mst = mst->next;
387 continue;
388 }
389 /* Compare location */
390 idx = 0;
391 while (location[idx] == mst->location[idx])
392 {
393 if (location[idx] == '\0')
394 break;
395 idx++;
396 }
397 if (location[idx] != '\0' || mst->location[idx] != '\0')
398 {
399 mst = mst->next;
400 continue;
401 }
402 /* Compare channel */
403 idx = 0;
404 while (channel[idx] == mst->channel[idx])
405 {
406 if (channel[idx] == '\0')
407 break;
408 idx++;
409 }
410 if (channel[idx] != '\0' || mst->channel[idx] != '\0')
411 {
412 mst = mst->next;
413 continue;
414 }
415
416 /* A match was found if we made it this far */
417 break;
418 }
419
420 return mst;
421 } /* End of mst_findadjacent() */
422
423 /***************************************************************************
424 * mst_addmsr:
425 *
426 * Add MSRecord time coverage to a MSTrace. The start or end time will
427 * be updated and samples will be copied if they exist. No checking
428 * is done to verify that the record matches the trace in any way.
429 *
430 * If whence is 1 the coverage will be added at the end of the trace,
431 * whereas if whence is 2 the coverage will be added at the beginning
432 * of the trace.
433 *
434 * Return 0 on success and -1 on error.
435 ***************************************************************************/
436 int
mst_addmsr(MSTrace * mst,MSRecord * msr,flag whence)437 mst_addmsr (MSTrace *mst, MSRecord *msr, flag whence)
438 {
439 int samplesize = 0;
440
441 if (!mst || !msr)
442 return -1;
443
444 /* Reallocate data sample buffer if samples are present */
445 if (msr->datasamples && msr->numsamples >= 0)
446 {
447 /* Check that the entire record was decompressed */
448 if (msr->samplecnt != msr->numsamples)
449 {
450 ms_log (2, "mst_addmsr(): Sample counts do not match, record not fully decompressed?\n");
451 ms_log (2, " The sample buffer will likely contain a discontinuity.\n");
452 }
453
454 if ((samplesize = ms_samplesize (msr->sampletype)) == 0)
455 {
456 ms_log (2, "mst_addmsr(): Unrecognized sample type: '%c'\n",
457 msr->sampletype);
458 return -1;
459 }
460
461 if (msr->sampletype != mst->sampletype)
462 {
463 ms_log (2, "mst_addmsr(): Mismatched sample type, '%c' and '%c'\n",
464 msr->sampletype, mst->sampletype);
465 return -1;
466 }
467
468 mst->datasamples = realloc (mst->datasamples,
469 (size_t) (mst->numsamples * samplesize + msr->numsamples * samplesize));
470
471 if (mst->datasamples == NULL)
472 {
473 ms_log (2, "mst_addmsr(): Cannot allocate memory\n");
474 return -1;
475 }
476 }
477
478 /* Add samples at end of trace */
479 if (whence == 1)
480 {
481 if (msr->datasamples && msr->numsamples >= 0)
482 {
483 memcpy ((char *)mst->datasamples + (mst->numsamples * samplesize),
484 msr->datasamples,
485 (size_t) (msr->numsamples * samplesize));
486
487 mst->numsamples += msr->numsamples;
488 }
489
490 mst->endtime = msr_endtime (msr);
491
492 if (mst->endtime == HPTERROR)
493 {
494 ms_log (2, "mst_addmsr(): Error calculating record end time\n");
495 return -1;
496 }
497 }
498
499 /* Add samples at the beginning of trace */
500 else if (whence == 2)
501 {
502 if (msr->datasamples && msr->numsamples >= 0)
503 {
504 /* Move any samples to end of buffer */
505 if (mst->numsamples > 0)
506 {
507 memmove ((char *)mst->datasamples + (msr->numsamples * samplesize),
508 mst->datasamples,
509 (size_t) (mst->numsamples * samplesize));
510 }
511
512 memcpy (mst->datasamples,
513 msr->datasamples,
514 (size_t) (msr->numsamples * samplesize));
515
516 mst->numsamples += msr->numsamples;
517 }
518
519 mst->starttime = msr->starttime;
520 }
521
522 /* If two different data qualities reset the MSTrace.dataquality to 0 */
523 if (mst->dataquality && msr->dataquality && mst->dataquality != msr->dataquality)
524 mst->dataquality = 0;
525
526 /* Update MSTrace sample count */
527 mst->samplecnt += msr->samplecnt;
528
529 return 0;
530 } /* End of mst_addmsr() */
531
532 /***************************************************************************
533 * mst_addspan:
534 *
535 * Add a time span to a MSTrace. The start or end time will be updated
536 * and samples will be copied if they are provided. No checking is done to
537 * verify that the record matches the trace in any way.
538 *
539 * If whence is 1 the coverage will be added at the end of the trace,
540 * whereas if whence is 2 the coverage will be added at the beginning
541 * of the trace.
542 *
543 * Return 0 on success and -1 on error.
544 ***************************************************************************/
545 int
mst_addspan(MSTrace * mst,hptime_t starttime,hptime_t endtime,void * datasamples,int64_t numsamples,char sampletype,flag whence)546 mst_addspan (MSTrace *mst, hptime_t starttime, hptime_t endtime,
547 void *datasamples, int64_t numsamples, char sampletype,
548 flag whence)
549 {
550 int samplesize = 0;
551
552 if (!mst)
553 return -1;
554
555 if (datasamples && numsamples > 0)
556 {
557 if ((samplesize = ms_samplesize (sampletype)) == 0)
558 {
559 ms_log (2, "mst_addspan(): Unrecognized sample type: '%c'\n",
560 sampletype);
561 return -1;
562 }
563
564 if (sampletype != mst->sampletype)
565 {
566 ms_log (2, "mst_addspan(): Mismatched sample type, '%c' and '%c'\n",
567 sampletype, mst->sampletype);
568 return -1;
569 }
570
571 mst->datasamples = realloc (mst->datasamples,
572 (size_t) (mst->numsamples * samplesize + numsamples * samplesize));
573
574 if (mst->datasamples == NULL)
575 {
576 ms_log (2, "mst_addspan(): Cannot allocate memory\n");
577 return -1;
578 }
579 }
580
581 /* Add samples at end of trace */
582 if (whence == 1)
583 {
584 if (datasamples && numsamples > 0)
585 {
586 memcpy ((char *)mst->datasamples + (mst->numsamples * samplesize),
587 datasamples,
588 (size_t) (numsamples * samplesize));
589
590 mst->numsamples += numsamples;
591 }
592
593 mst->endtime = endtime;
594 }
595
596 /* Add samples at the beginning of trace */
597 else if (whence == 2)
598 {
599 if (datasamples && numsamples > 0)
600 {
601 /* Move any samples to end of buffer */
602 if (mst->numsamples > 0)
603 {
604 memmove ((char *)mst->datasamples + (numsamples * samplesize),
605 mst->datasamples,
606 (size_t) (mst->numsamples * samplesize));
607 }
608
609 memcpy (mst->datasamples,
610 datasamples,
611 (size_t) (numsamples * samplesize));
612
613 mst->numsamples += numsamples;
614 }
615
616 mst->starttime = starttime;
617 }
618
619 /* Update MSTrace sample count */
620 if (numsamples > 0)
621 mst->samplecnt += numsamples;
622
623 return 0;
624 } /* End of mst_addspan() */
625
626 /***************************************************************************
627 * mst_addmsrtogroup:
628 *
629 * Add data samples from a MSRecord to a MSTrace in a MSTraceGroup by
630 * searching the group for the approriate MSTrace and either adding data
631 * to it or creating a new MSTrace if no match found.
632 *
633 * Matching traces are found using the mst_findadjacent() routine. If
634 * the dataquality flag is true the data quality bytes must also match
635 * otherwise they are ignored.
636 *
637 * Return a pointer to the MSTrace updated or 0 on error.
638 ***************************************************************************/
639 MSTrace *
mst_addmsrtogroup(MSTraceGroup * mstg,MSRecord * msr,flag dataquality,double timetol,double sampratetol)640 mst_addmsrtogroup (MSTraceGroup *mstg, MSRecord *msr, flag dataquality,
641 double timetol, double sampratetol)
642 {
643 MSTrace *mst = 0;
644 hptime_t endtime;
645 flag whence;
646 char dq;
647
648 if (!mstg || !msr)
649 return 0;
650
651 dq = (dataquality) ? msr->dataquality : 0;
652
653 endtime = msr_endtime (msr);
654
655 if (endtime == HPTERROR)
656 {
657 ms_log (2, "mst_addmsrtogroup(): Error calculating record end time\n");
658 return 0;
659 }
660
661 /* Find matching, time adjacent MSTrace */
662 mst = mst_findadjacent (mstg, &whence, dq,
663 msr->network, msr->station, msr->location, msr->channel,
664 msr->samprate, sampratetol,
665 msr->starttime, endtime, timetol);
666
667 /* If a match was found update it otherwise create a new MSTrace and
668 add to end of MSTrace chain */
669 if (mst)
670 {
671 /* Records with no time coverage do not contribute to a trace */
672 if (msr->samplecnt <= 0 || msr->samprate <= 0.0)
673 return mst;
674
675 if (mst_addmsr (mst, msr, whence))
676 {
677 return 0;
678 }
679 }
680 else
681 {
682 mst = mst_init (NULL);
683
684 mst->dataquality = dq;
685
686 strncpy (mst->network, msr->network, sizeof (mst->network));
687 strncpy (mst->station, msr->station, sizeof (mst->station));
688 strncpy (mst->location, msr->location, sizeof (mst->location));
689 strncpy (mst->channel, msr->channel, sizeof (mst->channel));
690
691 mst->starttime = msr->starttime;
692 mst->samprate = msr->samprate;
693 mst->sampletype = msr->sampletype;
694
695 if (mst_addmsr (mst, msr, 1))
696 {
697 mst_free (&mst);
698 return 0;
699 }
700
701 /* Link new MSTrace into the end of the chain */
702 if (!mstg->traces)
703 {
704 mstg->traces = mst;
705 }
706 else
707 {
708 MSTrace *lasttrace = mstg->traces;
709
710 while (lasttrace->next)
711 lasttrace = lasttrace->next;
712
713 lasttrace->next = mst;
714 }
715
716 mstg->numtraces++;
717 }
718
719 return mst;
720 } /* End of mst_addmsrtogroup() */
721
722 /***************************************************************************
723 * mst_addtracetogroup:
724 *
725 * Add a MSTrace to a MSTraceGroup at the end of the MSTrace chain.
726 *
727 * Return a pointer to the MSTrace added or 0 on error.
728 ***************************************************************************/
729 MSTrace *
mst_addtracetogroup(MSTraceGroup * mstg,MSTrace * mst)730 mst_addtracetogroup (MSTraceGroup *mstg, MSTrace *mst)
731 {
732 MSTrace *lasttrace;
733
734 if (!mstg || !mst)
735 return 0;
736
737 if (!mstg->traces)
738 {
739 mstg->traces = mst;
740 }
741 else
742 {
743 lasttrace = mstg->traces;
744
745 while (lasttrace->next)
746 lasttrace = lasttrace->next;
747
748 lasttrace->next = mst;
749 }
750
751 mst->next = 0;
752
753 mstg->numtraces++;
754
755 return mst;
756 } /* End of mst_addtracetogroup() */
757
758 /***************************************************************************
759 * mst_groupheal:
760 *
761 * Check if traces in MSTraceGroup can be healed, if contiguous segments
762 * belong together they will be merged. This routine is only useful
763 * if the trace group was assembled from segments out of time order
764 * (e.g. a file of Mini-SEED records not in time order) but forming
765 * contiguous time coverage. The MSTraceGroup will be sorted using
766 * mst_groupsort() before healing.
767 *
768 * The time tolerance and sample rate tolerance are used to determine
769 * if the traces are indeed the same. If timetol is -1.0 the default
770 * tolerance of 1/2 the sample period will be used. If samprratetol
771 * is -1.0 the default tolerance check of abs(1-sr1/sr2) < 0.0001 is
772 * used (defined in libmseed.h).
773 *
774 * Return number of trace mergings on success otherwise -1 on error.
775 ***************************************************************************/
776 int
mst_groupheal(MSTraceGroup * mstg,double timetol,double sampratetol)777 mst_groupheal (MSTraceGroup *mstg, double timetol, double sampratetol)
778 {
779 int mergings = 0;
780 MSTrace *curtrace = 0;
781 MSTrace *nexttrace = 0;
782 MSTrace *searchtrace = 0;
783 MSTrace *prevtrace = 0;
784 int8_t merged = 0;
785 double postgap, pregap, delta;
786
787 if (!mstg)
788 return -1;
789
790 /* Sort MSTraceGroup before any healing */
791 if (mst_groupsort (mstg, 1))
792 return -1;
793
794 curtrace = mstg->traces;
795
796 while (curtrace)
797 {
798 nexttrace = mstg->traces;
799 prevtrace = mstg->traces;
800
801 while (nexttrace)
802 {
803 searchtrace = nexttrace;
804 nexttrace = searchtrace->next;
805
806 /* Do not process the same MSTrace we are trying to match */
807 if (searchtrace == curtrace)
808 {
809 prevtrace = searchtrace;
810 continue;
811 }
812
813 /* Check if this trace matches the curtrace */
814 if (strcmp (searchtrace->network, curtrace->network) ||
815 strcmp (searchtrace->station, curtrace->station) ||
816 strcmp (searchtrace->location, curtrace->location) ||
817 strcmp (searchtrace->channel, curtrace->channel))
818 {
819 prevtrace = searchtrace;
820 continue;
821 }
822
823 /* Perform default samprate tolerance check if requested */
824 if (sampratetol == -1.0)
825 {
826 if (!MS_ISRATETOLERABLE (searchtrace->samprate, curtrace->samprate))
827 {
828 prevtrace = searchtrace;
829 continue;
830 }
831 }
832 /* Otherwise check against the specified sample rates tolerance */
833 else if (ms_dabs (searchtrace->samprate - curtrace->samprate) > sampratetol)
834 {
835 prevtrace = searchtrace;
836 continue;
837 }
838
839 merged = 0;
840
841 /* post/pregap are negative when searchtrace overlaps curtrace
842 segment and positive when there is a time gap. */
843 delta = (curtrace->samprate) ? (1.0 / curtrace->samprate) : 0.0;
844
845 postgap = ((double)(searchtrace->starttime - curtrace->endtime) / HPTMODULUS) - delta;
846
847 pregap = ((double)(curtrace->starttime - searchtrace->endtime) / HPTMODULUS) - delta;
848
849 /* Calculate default time tolerance (1/2 sample period) if needed */
850 if (timetol == -1.0)
851 timetol = 0.5 * delta;
852
853 /* Fits right at the end of curtrace */
854 if (ms_dabs (postgap) <= timetol)
855 {
856 /* Merge searchtrace with curtrace */
857 mst_addspan (curtrace, searchtrace->starttime, searchtrace->endtime,
858 searchtrace->datasamples, searchtrace->numsamples,
859 searchtrace->sampletype, 1);
860
861 /* If no data is present, make sure sample count is updated */
862 if (searchtrace->numsamples <= 0)
863 curtrace->samplecnt += searchtrace->samplecnt;
864
865 /* If qualities do not match reset the indicator */
866 if (curtrace->dataquality != searchtrace->dataquality)
867 curtrace->dataquality = 0;
868
869 merged = 1;
870 }
871
872 /* Fits right at the beginning of curtrace */
873 else if (ms_dabs (pregap) <= timetol)
874 {
875 /* Merge searchtrace with curtrace */
876 mst_addspan (curtrace, searchtrace->starttime, searchtrace->endtime,
877 searchtrace->datasamples, searchtrace->numsamples,
878 searchtrace->sampletype, 2);
879
880 /* If no data is present, make sure sample count is updated */
881 if (searchtrace->numsamples <= 0)
882 curtrace->samplecnt += searchtrace->samplecnt;
883
884 /* If qualities do not match reset the indicator */
885 if (curtrace->dataquality != searchtrace->dataquality)
886 curtrace->dataquality = 0;
887
888 merged = 1;
889 }
890
891 /* If searchtrace was merged with curtrace remove it from the chain */
892 if (merged)
893 {
894 /* Re-link trace chain and free searchtrace */
895 if (searchtrace == mstg->traces)
896 mstg->traces = nexttrace;
897 else
898 prevtrace->next = nexttrace;
899
900 mst_free (&searchtrace);
901
902 mstg->numtraces--;
903 mergings++;
904 }
905 else
906 {
907 prevtrace = searchtrace;
908 }
909 }
910
911 curtrace = curtrace->next;
912 }
913
914 return mergings;
915 } /* End of mst_groupheal() */
916
917 /***************************************************************************
918 * mst_groupsort:
919 *
920 * Sort a MSTraceGroup using a mergesort algorithm. MSTrace entries
921 * are compared using the mst_groupsort_cmp() function.
922 *
923 * The mergesort implementation was inspired by the listsort function
924 * published and copyright 2001 by Simon Tatham.
925 *
926 * Return 0 on success and -1 on error.
927 ***************************************************************************/
928 int
mst_groupsort(MSTraceGroup * mstg,flag quality)929 mst_groupsort (MSTraceGroup *mstg, flag quality)
930 {
931 MSTrace *p, *q, *e, *top, *tail;
932 int nmerges;
933 int insize, psize, qsize, i;
934
935 if (!mstg)
936 return -1;
937
938 if (!mstg->traces)
939 return 0;
940
941 top = mstg->traces;
942 insize = 1;
943
944 for (;;)
945 {
946 p = top;
947 top = NULL;
948 tail = NULL;
949
950 nmerges = 0; /* count number of merges we do in this pass */
951
952 while (p)
953 {
954 nmerges++; /* there exists a merge to be done */
955
956 /* step `insize' places along from p */
957 q = p;
958 psize = 0;
959 for (i = 0; i < insize; i++)
960 {
961 psize++;
962 q = q->next;
963 if (!q)
964 break;
965 }
966
967 /* if q hasn't fallen off end, we have two lists to merge */
968 qsize = insize;
969
970 /* now we have two lists; merge them */
971 while (psize > 0 || (qsize > 0 && q))
972 {
973 /* decide whether next element of merge comes from p or q */
974 if (psize == 0)
975 { /* p is empty; e must come from q. */
976 e = q;
977 q = q->next;
978 qsize--;
979 }
980 else if (qsize == 0 || !q)
981 { /* q is empty; e must come from p. */
982 e = p;
983 p = p->next;
984 psize--;
985 }
986 else if (mst_groupsort_cmp (p, q, quality) <= 0)
987 { /* First element of p is lower (or same), e must come from p. */
988 e = p;
989 p = p->next;
990 psize--;
991 }
992 else
993 { /* First element of q is lower; e must come from q. */
994 e = q;
995 q = q->next;
996 qsize--;
997 }
998
999 /* add the next element to the merged list */
1000 if (tail)
1001 tail->next = e;
1002 else
1003 top = e;
1004
1005 tail = e;
1006 }
1007
1008 /* now p has stepped `insize' places along, and q has too */
1009 p = q;
1010 }
1011
1012 tail->next = NULL;
1013
1014 /* If we have done only one merge, we're finished. */
1015 if (nmerges <= 1) /* allow for nmerges==0, the empty list case */
1016 {
1017 mstg->traces = top;
1018
1019 return 0;
1020 }
1021
1022 /* Otherwise repeat, merging lists twice the size */
1023 insize *= 2;
1024 }
1025 } /* End of mst_groupsort() */
1026
1027 /***************************************************************************
1028 * mst_groupsort_cmp:
1029 *
1030 * Compare two MSTrace entities for the purposes of sorting a
1031 * MSTraceGroup. Criteria for MSTrace comparison are (in order of
1032 * testing): source name, start time, descending endtime (longest
1033 * trace first) and sample rate.
1034 *
1035 * Return 1 if mst1 is "greater" than mst2, otherwise return 0.
1036 ***************************************************************************/
1037 static int
mst_groupsort_cmp(MSTrace * mst1,MSTrace * mst2,flag quality)1038 mst_groupsort_cmp (MSTrace *mst1, MSTrace *mst2, flag quality)
1039 {
1040 char src1[50], src2[50];
1041 int strcmpval;
1042
1043 if (!mst1 || !mst2)
1044 return -1;
1045
1046 mst_srcname (mst1, src1, quality);
1047 mst_srcname (mst2, src2, quality);
1048
1049 strcmpval = strcmp (src1, src2);
1050
1051 /* If the source names do not match make sure the "greater" string is 2nd,
1052 * otherwise, if source names do match, make sure the later start time is 2nd
1053 * otherwise, if start times match, make sure the earlier end time is 2nd
1054 * otherwise, if end times match, make sure the highest sample rate is 2nd
1055 */
1056 if (strcmpval > 0)
1057 {
1058 return 1;
1059 }
1060 else if (strcmpval == 0)
1061 {
1062 if (mst1->starttime > mst2->starttime)
1063 {
1064 return 1;
1065 }
1066 else if (mst1->starttime == mst2->starttime)
1067 {
1068 if (mst1->endtime < mst2->endtime)
1069 {
1070 return 1;
1071 }
1072 else if (mst1->endtime == mst2->endtime)
1073 {
1074 if (!MS_ISRATETOLERABLE (mst1->samprate, mst2->samprate) &&
1075 mst1->samprate > mst2->samprate)
1076 {
1077 return 1;
1078 }
1079 }
1080 }
1081 }
1082
1083 return 0;
1084 } /* End of mst_groupsort_cmp() */
1085
1086 /***************************************************************************
1087 * mst_convertsamples:
1088 *
1089 * Convert the data samples associated with an MSTrace to another data
1090 * type. ASCII data samples cannot be converted, if supplied or
1091 * requested an error will be returned.
1092 *
1093 * When converting float & double sample types to integer type a
1094 * simple rounding is applied by adding 0.5 to the sample value before
1095 * converting (truncating) to integer.
1096 *
1097 * If the truncate flag is true data samples will be truncated to
1098 * integers even if loss of sample precision is detected. If the
1099 * truncate flag is false (0) and loss of precision is detected an
1100 * error is returned.
1101 *
1102 * Returns 0 on success, and -1 on failure.
1103 ***************************************************************************/
1104 int
mst_convertsamples(MSTrace * mst,char type,flag truncate)1105 mst_convertsamples (MSTrace *mst, char type, flag truncate)
1106 {
1107 int32_t *idata;
1108 float *fdata;
1109 double *ddata;
1110 int64_t idx;
1111
1112 if (!mst)
1113 return -1;
1114
1115 /* No conversion necessary, report success */
1116 if (mst->sampletype == type)
1117 return 0;
1118
1119 if (mst->sampletype == 'a' || type == 'a')
1120 {
1121 ms_log (2, "mst_convertsamples: cannot convert ASCII samples to/from numeric type\n");
1122 return -1;
1123 }
1124
1125 idata = (int32_t *)mst->datasamples;
1126 fdata = (float *)mst->datasamples;
1127 ddata = (double *)mst->datasamples;
1128
1129 /* Convert to 32-bit integers */
1130 if (type == 'i')
1131 {
1132 if (mst->sampletype == 'f') /* Convert floats to integers with simple rounding */
1133 {
1134 for (idx = 0; idx < mst->numsamples; idx++)
1135 {
1136 /* Check for loss of sub-integer */
1137 if (!truncate && (fdata[idx] - (int32_t)fdata[idx]) > 0.000001)
1138 {
1139 ms_log (1, "mst_convertsamples: Warning, loss of precision when converting floats to integers, loss: %g\n",
1140 (fdata[idx] - (int32_t)fdata[idx]));
1141 return -1;
1142 }
1143
1144 idata[idx] = (int32_t) (fdata[idx] + 0.5);
1145 }
1146 }
1147 else if (mst->sampletype == 'd') /* Convert doubles to integers with simple rounding */
1148 {
1149 for (idx = 0; idx < mst->numsamples; idx++)
1150 {
1151 /* Check for loss of sub-integer */
1152 if (!truncate && (ddata[idx] - (int32_t)ddata[idx]) > 0.000001)
1153 {
1154 ms_log (1, "mst_convertsamples: Warning, loss of precision when converting doubles to integers, loss: %g\n",
1155 (ddata[idx] - (int32_t)ddata[idx]));
1156 return -1;
1157 }
1158
1159 idata[idx] = (int32_t) (ddata[idx] + 0.5);
1160 }
1161
1162 /* Reallocate buffer for reduced size needed */
1163 if (!(mst->datasamples = realloc (mst->datasamples, (size_t) (mst->numsamples * sizeof (int32_t)))))
1164 {
1165 ms_log (2, "mst_convertsamples: cannot re-allocate buffer for sample conversion\n");
1166 return -1;
1167 }
1168 }
1169
1170 mst->sampletype = 'i';
1171 } /* Done converting to 32-bit integers */
1172
1173 /* Convert to 32-bit floats */
1174 else if (type == 'f')
1175 {
1176 if (mst->sampletype == 'i') /* Convert integers to floats */
1177 {
1178 for (idx = 0; idx < mst->numsamples; idx++)
1179 fdata[idx] = (float)idata[idx];
1180 }
1181 else if (mst->sampletype == 'd') /* Convert doubles to floats */
1182 {
1183 for (idx = 0; idx < mst->numsamples; idx++)
1184 fdata[idx] = (float)ddata[idx];
1185
1186 /* Reallocate buffer for reduced size needed */
1187 if (!(mst->datasamples = realloc (mst->datasamples, (size_t) (mst->numsamples * sizeof (float)))))
1188 {
1189 ms_log (2, "mst_convertsamples: cannot re-allocate buffer after sample conversion\n");
1190 return -1;
1191 }
1192 }
1193
1194 mst->sampletype = 'f';
1195 } /* Done converting to 32-bit floats */
1196
1197 /* Convert to 64-bit doubles */
1198 else if (type == 'd')
1199 {
1200 if (!(ddata = (double *)malloc ((size_t) (mst->numsamples * sizeof (double)))))
1201 {
1202 ms_log (2, "mst_convertsamples: cannot allocate buffer for sample conversion to doubles\n");
1203 return -1;
1204 }
1205
1206 if (mst->sampletype == 'i') /* Convert integers to doubles */
1207 {
1208 for (idx = 0; idx < mst->numsamples; idx++)
1209 ddata[idx] = (double)idata[idx];
1210
1211 free (idata);
1212 }
1213 else if (mst->sampletype == 'f') /* Convert floats to doubles */
1214 {
1215 for (idx = 0; idx < mst->numsamples; idx++)
1216 ddata[idx] = (double)fdata[idx];
1217
1218 free (fdata);
1219 }
1220
1221 mst->datasamples = ddata;
1222 mst->sampletype = 'd';
1223 } /* Done converting to 64-bit doubles */
1224
1225 return 0;
1226 } /* End of mst_convertsamples() */
1227
1228 /***************************************************************************
1229 * mst_srcname:
1230 *
1231 * Generate a source name string for a specified MSTrace in the
1232 * format: 'NET_STA_LOC_CHAN[_QUAL]'. The quality is added to the
1233 * srcname if the quality flag argument is 1 and mst->dataquality is
1234 * not zero. The passed srcname must have enough room for the
1235 * resulting string.
1236 *
1237 * Returns a pointer to the resulting string or NULL on error.
1238 ***************************************************************************/
1239 char *
mst_srcname(MSTrace * mst,char * srcname,flag quality)1240 mst_srcname (MSTrace *mst, char *srcname, flag quality)
1241 {
1242 char *src = srcname;
1243 char *cp = srcname;
1244
1245 if (!mst || !srcname)
1246 return NULL;
1247
1248 /* Build the source name string */
1249 cp = mst->network;
1250 while (*cp)
1251 {
1252 *src++ = *cp++;
1253 }
1254 *src++ = '_';
1255 cp = mst->station;
1256 while (*cp)
1257 {
1258 *src++ = *cp++;
1259 }
1260 *src++ = '_';
1261 cp = mst->location;
1262 while (*cp)
1263 {
1264 *src++ = *cp++;
1265 }
1266 *src++ = '_';
1267 cp = mst->channel;
1268 while (*cp)
1269 {
1270 *src++ = *cp++;
1271 }
1272
1273 if (quality && mst->dataquality)
1274 {
1275 *src++ = '_';
1276 *src++ = mst->dataquality;
1277 }
1278
1279 *src = '\0';
1280
1281 return srcname;
1282 } /* End of mst_srcname() */
1283
1284 /***************************************************************************
1285 * mst_printtracelist:
1286 *
1287 * Print trace list summary information for the specified MSTraceGroup.
1288 *
1289 * By default only print the srcname, starttime and endtime for each
1290 * trace. If details is greater than 0 include the sample rate,
1291 * number of samples and a total trace count. If gaps is greater than
1292 * 0 and the previous trace matches (srcname & samprate) include the
1293 * gap between the endtime of the last trace and the starttime of the
1294 * current trace.
1295 *
1296 * The timeformat flag can either be:
1297 * 0 : SEED time format (year, day-of-year, hour, min, sec)
1298 * 1 : ISO time format (year, month, day, hour, min, sec)
1299 * 2 : Epoch time, seconds since the epoch
1300 ***************************************************************************/
1301 void
mst_printtracelist(MSTraceGroup * mstg,flag timeformat,flag details,flag gaps)1302 mst_printtracelist (MSTraceGroup *mstg, flag timeformat,
1303 flag details, flag gaps)
1304 {
1305 MSTrace *mst = 0;
1306 char srcname[50];
1307 char prevsrcname[50];
1308 char stime[30];
1309 char etime[30];
1310 char gapstr[20];
1311 flag nogap;
1312 double gap;
1313 double delta;
1314 double prevsamprate;
1315 hptime_t prevendtime;
1316 int tracecnt = 0;
1317
1318 if (!mstg)
1319 return;
1320
1321 mst = mstg->traces;
1322
1323 /* Print out the appropriate header */
1324 if (details > 0 && gaps > 0)
1325 ms_log (0, " Source Start sample End sample Gap Hz Samples\n");
1326 else if (details <= 0 && gaps > 0)
1327 ms_log (0, " Source Start sample End sample Gap\n");
1328 else if (details > 0 && gaps <= 0)
1329 ms_log (0, " Source Start sample End sample Hz Samples\n");
1330 else
1331 ms_log (0, " Source Start sample End sample\n");
1332
1333 prevsrcname[0] = '\0';
1334 prevsamprate = -1.0;
1335 prevendtime = 0;
1336
1337 while (mst)
1338 {
1339 mst_srcname (mst, srcname, 1);
1340
1341 /* Create formatted time strings */
1342 if (timeformat == 2)
1343 {
1344 snprintf (stime, sizeof (stime), "%.6f", (double)MS_HPTIME2EPOCH (mst->starttime));
1345 snprintf (etime, sizeof (etime), "%.6f", (double)MS_HPTIME2EPOCH (mst->endtime));
1346 }
1347 else if (timeformat == 1)
1348 {
1349 if (ms_hptime2isotimestr (mst->starttime, stime, 1) == NULL)
1350 ms_log (2, "Cannot convert trace start time for %s\n", srcname);
1351
1352 if (ms_hptime2isotimestr (mst->endtime, etime, 1) == NULL)
1353 ms_log (2, "Cannot convert trace end time for %s\n", srcname);
1354 }
1355 else
1356 {
1357 if (ms_hptime2seedtimestr (mst->starttime, stime, 1) == NULL)
1358 ms_log (2, "Cannot convert trace start time for %s\n", srcname);
1359
1360 if (ms_hptime2seedtimestr (mst->endtime, etime, 1) == NULL)
1361 ms_log (2, "Cannot convert trace end time for %s\n", srcname);
1362 }
1363
1364 /* Print trace info at varying levels */
1365 if (gaps > 0)
1366 {
1367 gap = 0.0;
1368 nogap = 0;
1369
1370 if (!strcmp (prevsrcname, srcname) && prevsamprate != -1.0 &&
1371 MS_ISRATETOLERABLE (prevsamprate, mst->samprate))
1372 gap = (double)(mst->starttime - prevendtime) / HPTMODULUS;
1373 else
1374 nogap = 1;
1375
1376 /* Check that any overlap is not larger than the trace coverage */
1377 if (gap < 0.0)
1378 {
1379 delta = (mst->samprate) ? (1.0 / mst->samprate) : 0.0;
1380
1381 if ((gap * -1.0) > (((double)(mst->endtime - mst->starttime) / HPTMODULUS) + delta))
1382 gap = -(((double)(mst->endtime - mst->starttime) / HPTMODULUS) + delta);
1383 }
1384
1385 /* Fix up gap display */
1386 if (nogap)
1387 snprintf (gapstr, sizeof (gapstr), " == ");
1388 else if (gap >= 86400.0 || gap <= -86400.0)
1389 snprintf (gapstr, sizeof (gapstr), "%-3.1fd", (gap / 86400));
1390 else if (gap >= 3600.0 || gap <= -3600.0)
1391 snprintf (gapstr, sizeof (gapstr), "%-3.1fh", (gap / 3600));
1392 else if (gap == 0.0)
1393 snprintf (gapstr, sizeof (gapstr), "-0 ");
1394 else
1395 snprintf (gapstr, sizeof (gapstr), "%-4.4g", gap);
1396
1397 if (details <= 0)
1398 ms_log (0, "%-17s %-24s %-24s %-4s\n",
1399 srcname, stime, etime, gapstr);
1400 else
1401 ms_log (0, "%-17s %-24s %-24s %-s %-3.3g %-" PRId64 "\n",
1402 srcname, stime, etime, gapstr, mst->samprate, mst->samplecnt);
1403 }
1404 else if (details > 0 && gaps <= 0)
1405 ms_log (0, "%-17s %-24s %-24s %-3.3g %-" PRId64 "\n",
1406 srcname, stime, etime, mst->samprate, mst->samplecnt);
1407 else
1408 ms_log (0, "%-17s %-24s %-24s\n", srcname, stime, etime);
1409
1410 if (gaps > 0)
1411 {
1412 strcpy (prevsrcname, srcname);
1413 prevsamprate = mst->samprate;
1414 prevendtime = mst->endtime;
1415 }
1416
1417 tracecnt++;
1418 mst = mst->next;
1419 }
1420
1421 if (tracecnt != mstg->numtraces)
1422 ms_log (2, "mst_printtracelist(): number of traces in trace group is inconsistent\n");
1423
1424 if (details > 0)
1425 ms_log (0, "Total: %d trace segment(s)\n", tracecnt);
1426
1427 } /* End of mst_printtracelist() */
1428
1429 /***************************************************************************
1430 * mst_printsynclist:
1431 *
1432 * Print SYNC trace list summary information for the specified MSTraceGroup.
1433 *
1434 * The SYNC header line will be created using the supplied dccid, if
1435 * the pointer is NULL the string "DCC" will be used instead.
1436 *
1437 * If the subsecond flag is true the segment start and end times will
1438 * include subsecond precision, otherwise they will be truncated to
1439 * integer seconds.
1440 *
1441 ***************************************************************************/
1442 void
mst_printsynclist(MSTraceGroup * mstg,char * dccid,flag subsecond)1443 mst_printsynclist (MSTraceGroup *mstg, char *dccid, flag subsecond)
1444 {
1445 MSTrace *mst = 0;
1446 char stime[30];
1447 char etime[30];
1448 char yearday[10];
1449 time_t now;
1450 struct tm *nt;
1451
1452 if (!mstg)
1453 {
1454 return;
1455 }
1456
1457 /* Generate current time stamp */
1458 now = time (NULL);
1459 nt = localtime (&now);
1460 nt->tm_year += 1900;
1461 nt->tm_yday += 1;
1462 snprintf (yearday, sizeof (yearday), "%04d,%03d", nt->tm_year, nt->tm_yday);
1463
1464 /* Print SYNC header line */
1465 ms_log (0, "%s|%s\n", (dccid) ? dccid : "DCC", yearday);
1466
1467 /* Loope through trace list */
1468 mst = mstg->traces;
1469 while (mst)
1470 {
1471 ms_hptime2seedtimestr (mst->starttime, stime, subsecond);
1472 ms_hptime2seedtimestr (mst->endtime, etime, subsecond);
1473
1474 /* Print SYNC line */
1475 ms_log (0, "%s|%s|%s|%s|%s|%s||%.10g|%" PRId64 "|||||||%s\n",
1476 mst->network, mst->station, mst->location, mst->channel,
1477 stime, etime, mst->samprate, mst->samplecnt,
1478 yearday);
1479
1480 mst = mst->next;
1481 }
1482 } /* End of mst_printsynclist() */
1483
1484 /***************************************************************************
1485 * mst_printgaplist:
1486 *
1487 * Print gap/overlap list summary information for the specified
1488 * MSTraceGroup. Overlaps are printed as negative gaps. The trace
1489 * summary information in the MSTraceGroup is logically inverted so gaps
1490 * for like channels are identified.
1491 *
1492 * If mingap and maxgap are not NULL their values will be enforced and
1493 * only gaps/overlaps matching their implied criteria will be printed.
1494 *
1495 * The timeformat flag can either be:
1496 * 0 : SEED time format (year, day-of-year, hour, min, sec)
1497 * 1 : ISO time format (year, month, day, hour, min, sec)
1498 * 2 : Epoch time, seconds since the epoch
1499 ***************************************************************************/
1500 void
mst_printgaplist(MSTraceGroup * mstg,flag timeformat,double * mingap,double * maxgap)1501 mst_printgaplist (MSTraceGroup *mstg, flag timeformat,
1502 double *mingap, double *maxgap)
1503 {
1504 MSTrace *mst;
1505 char src1[50], src2[50];
1506 char time1[30], time2[30];
1507 char gapstr[30];
1508 double gap;
1509 double delta;
1510 double nsamples;
1511 flag printflag;
1512 int gapcnt = 0;
1513
1514 if (!mstg)
1515 return;
1516
1517 if (!mstg->traces)
1518 return;
1519
1520 mst = mstg->traces;
1521
1522 ms_log (0, " Source Last Sample Next Sample Gap Samples\n");
1523
1524 while (mst->next)
1525 {
1526 mst_srcname (mst, src1, 1);
1527 mst_srcname (mst->next, src2, 1);
1528
1529 if (!strcmp (src1, src2))
1530 {
1531 /* Skip MSTraces with 0 sample rate, usually from SOH records */
1532 if (mst->samprate == 0.0)
1533 {
1534 mst = mst->next;
1535 continue;
1536 }
1537
1538 /* Check that sample rates match using default tolerance */
1539 if (!MS_ISRATETOLERABLE (mst->samprate, mst->next->samprate))
1540 {
1541 ms_log (2, "%s Sample rate changed! %.10g -> %.10g\n",
1542 src1, mst->samprate, mst->next->samprate);
1543 }
1544
1545 gap = (double)(mst->next->starttime - mst->endtime) / HPTMODULUS;
1546
1547 /* Check that any overlap is not larger than the trace coverage */
1548 if (gap < 0.0)
1549 {
1550 delta = (mst->next->samprate) ? (1.0 / mst->next->samprate) : 0.0;
1551
1552 if ((gap * -1.0) > (((double)(mst->next->endtime - mst->next->starttime) / HPTMODULUS) + delta))
1553 gap = -(((double)(mst->next->endtime - mst->next->starttime) / HPTMODULUS) + delta);
1554 }
1555
1556 printflag = 1;
1557
1558 /* Check gap/overlap criteria */
1559 if (mingap)
1560 if (gap < *mingap)
1561 printflag = 0;
1562
1563 if (maxgap)
1564 if (gap > *maxgap)
1565 printflag = 0;
1566
1567 if (printflag)
1568 {
1569 nsamples = ms_dabs (gap) * mst->samprate;
1570
1571 if (gap > 0.0)
1572 nsamples -= 1.0;
1573 else
1574 nsamples += 1.0;
1575
1576 /* Fix up gap display */
1577 if (gap >= 86400.0 || gap <= -86400.0)
1578 snprintf (gapstr, sizeof (gapstr), "%-3.1fd", (gap / 86400));
1579 else if (gap >= 3600.0 || gap <= -3600.0)
1580 snprintf (gapstr, sizeof (gapstr), "%-3.1fh", (gap / 3600));
1581 else if (gap == 0.0)
1582 snprintf (gapstr, sizeof (gapstr), "-0 ");
1583 else
1584 snprintf (gapstr, sizeof (gapstr), "%-4.4g", gap);
1585
1586 /* Create formatted time strings */
1587 if (timeformat == 2)
1588 {
1589 snprintf (time1, sizeof (time1), "%.6f", (double)MS_HPTIME2EPOCH (mst->endtime));
1590 snprintf (time2, sizeof (time2), "%.6f", (double)MS_HPTIME2EPOCH (mst->next->starttime));
1591 }
1592 else if (timeformat == 1)
1593 {
1594 if (ms_hptime2isotimestr (mst->endtime, time1, 1) == NULL)
1595 ms_log (2, "Cannot convert trace end time for %s\n", src1);
1596
1597 if (ms_hptime2isotimestr (mst->next->starttime, time2, 1) == NULL)
1598 ms_log (2, "Cannot convert next trace start time for %s\n", src1);
1599 }
1600 else
1601 {
1602 if (ms_hptime2seedtimestr (mst->endtime, time1, 1) == NULL)
1603 ms_log (2, "Cannot convert trace end time for %s\n", src1);
1604
1605 if (ms_hptime2seedtimestr (mst->next->starttime, time2, 1) == NULL)
1606 ms_log (2, "Cannot convert next trace start time for %s\n", src1);
1607 }
1608
1609 ms_log (0, "%-17s %-24s %-24s %-4s %-.8g\n",
1610 src1, time1, time2, gapstr, nsamples);
1611
1612 gapcnt++;
1613 }
1614 }
1615
1616 mst = mst->next;
1617 }
1618
1619 ms_log (0, "Total: %d gap(s)\n", gapcnt);
1620
1621 } /* End of mst_printgaplist() */
1622
1623 /***************************************************************************
1624 * mst_pack:
1625 *
1626 * Pack MSTrace data into Mini-SEED records using the specified record
1627 * length, encoding format and byte order. The datasamples array and
1628 * numsamples field will be adjusted (reduced) based on how many
1629 * samples were packed.
1630 *
1631 * As each record is filled and finished they are passed to
1632 * record_handler which expects 1) a char * to the record, 2) the
1633 * length of the record and 3) a pointer supplied by the original
1634 * caller containing optional private data (handlerdata). It is the
1635 * responsibility of record_handler to process the record, the memory
1636 * will be re-used or freed when record_handler returns.
1637 *
1638 * If the flush flag is > 0 all of the data will be packed into data
1639 * records even though the last one will probably not be filled.
1640 *
1641 * If the mstemplate argument is not NULL it will be used as the
1642 * template for the packed Mini-SEED records. Otherwise a new
1643 * MSRecord will be initialized and populated from values in the
1644 * MSTrace. The reclen, encoding and byteorder arguments take
1645 * precedence over those in the template. The start time, sample
1646 * rate, datasamples, numsamples and sampletype values from the
1647 * template will be preserved.
1648 *
1649 * Returns the number of records created on success and -1 on error.
1650 ***************************************************************************/
1651 int
mst_pack(MSTrace * mst,void (* record_handler)(char *,int,void *),void * handlerdata,int reclen,flag encoding,flag byteorder,int64_t * packedsamples,flag flush,flag verbose,MSRecord * mstemplate)1652 mst_pack (MSTrace *mst, void (*record_handler) (char *, int, void *),
1653 void *handlerdata, int reclen, flag encoding, flag byteorder,
1654 int64_t *packedsamples, flag flush, flag verbose,
1655 MSRecord *mstemplate)
1656 {
1657 MSRecord *msr;
1658 char srcname[50];
1659 int trpackedrecords = 0;
1660 int64_t trpackedsamples = 0;
1661 int samplesize;
1662 int64_t bufsize;
1663
1664 hptime_t preservestarttime = 0;
1665 double preservesamprate = 0.0;
1666 void *preservedatasamples = 0;
1667 int64_t preservenumsamples = 0;
1668 char preservesampletype = 0;
1669 StreamState *preserveststate = 0;
1670
1671 if (packedsamples)
1672 *packedsamples = 0;
1673
1674 /* Allocate stream processing state space if needed */
1675 if (!mst->ststate)
1676 {
1677 mst->ststate = (StreamState *)malloc (sizeof (StreamState));
1678 if (!mst->ststate)
1679 {
1680 ms_log (2, "mst_pack(): Could not allocate memory for StreamState\n");
1681 return -1;
1682 }
1683 memset (mst->ststate, 0, sizeof (StreamState));
1684 }
1685
1686 if (mstemplate)
1687 {
1688 msr = mstemplate;
1689
1690 preservestarttime = msr->starttime;
1691 preservesamprate = msr->samprate;
1692 preservedatasamples = msr->datasamples;
1693 preservenumsamples = msr->numsamples;
1694 preservesampletype = msr->sampletype;
1695 preserveststate = msr->ststate;
1696 }
1697 else
1698 {
1699 msr = msr_init (NULL);
1700
1701 if (msr == NULL)
1702 {
1703 ms_log (2, "mst_pack(): Error initializing msr\n");
1704 return -1;
1705 }
1706
1707 msr->dataquality = 'D';
1708 strcpy (msr->network, mst->network);
1709 strcpy (msr->station, mst->station);
1710 strcpy (msr->location, mst->location);
1711 strcpy (msr->channel, mst->channel);
1712 }
1713
1714 /* Setup MSRecord template for packing */
1715 msr->reclen = reclen;
1716 msr->encoding = encoding;
1717 msr->byteorder = byteorder;
1718
1719 msr->starttime = mst->starttime;
1720 msr->samprate = mst->samprate;
1721 msr->datasamples = mst->datasamples;
1722 msr->numsamples = mst->numsamples;
1723 msr->sampletype = mst->sampletype;
1724 msr->ststate = mst->ststate;
1725
1726 /* Sample count sanity check */
1727 if (mst->samplecnt != mst->numsamples)
1728 {
1729 ms_log (2, "mst_pack(): Sample counts do not match, abort\n");
1730 return -1;
1731 }
1732
1733 /* Pack data */
1734 trpackedrecords = msr_pack (msr, record_handler, handlerdata, &trpackedsamples, flush, verbose);
1735
1736 if (verbose > 1)
1737 {
1738 ms_log (1, "Packed %d records for %s trace\n", trpackedrecords, mst_srcname (mst, srcname, 1));
1739 }
1740
1741 /* Adjust MSTrace start time, data array and sample count */
1742 if (trpackedsamples > 0)
1743 {
1744 /* The new start time was calculated my msr_pack */
1745 mst->starttime = msr->starttime;
1746
1747 samplesize = ms_samplesize (mst->sampletype);
1748 bufsize = (mst->numsamples - trpackedsamples) * samplesize;
1749
1750 if (bufsize)
1751 {
1752 memmove (mst->datasamples,
1753 (char *)mst->datasamples + (trpackedsamples * samplesize),
1754 (size_t)bufsize);
1755
1756 mst->datasamples = realloc (mst->datasamples, (size_t)bufsize);
1757
1758 if (mst->datasamples == NULL)
1759 {
1760 ms_log (2, "mst_pack(): Cannot (re)allocate datasamples buffer\n");
1761 return -1;
1762 }
1763 }
1764 else
1765 {
1766 if (mst->datasamples)
1767 free (mst->datasamples);
1768 mst->datasamples = 0;
1769 }
1770
1771 mst->samplecnt -= trpackedsamples;
1772 mst->numsamples -= trpackedsamples;
1773 }
1774
1775 /* Reinstate preserved values if a template was used */
1776 if (mstemplate)
1777 {
1778 msr->starttime = preservestarttime;
1779 msr->samprate = preservesamprate;
1780 msr->datasamples = preservedatasamples;
1781 msr->numsamples = preservenumsamples;
1782 msr->sampletype = preservesampletype;
1783 msr->ststate = preserveststate;
1784 }
1785 else
1786 {
1787 msr->datasamples = 0;
1788 msr->ststate = 0;
1789 msr_free (&msr);
1790 }
1791
1792 if (packedsamples)
1793 *packedsamples = trpackedsamples;
1794
1795 return trpackedrecords;
1796 } /* End of mst_pack() */
1797
1798 /***************************************************************************
1799 * mst_packgroup:
1800 *
1801 * Pack MSTraceGroup data into Mini-SEED records by calling mst_pack()
1802 * for each MSTrace in the group.
1803 *
1804 * Returns the number of records created on success and -1 on error.
1805 ***************************************************************************/
1806 int
mst_packgroup(MSTraceGroup * mstg,void (* record_handler)(char *,int,void *),void * handlerdata,int reclen,flag encoding,flag byteorder,int64_t * packedsamples,flag flush,flag verbose,MSRecord * mstemplate)1807 mst_packgroup (MSTraceGroup *mstg, void (*record_handler) (char *, int, void *),
1808 void *handlerdata, int reclen, flag encoding, flag byteorder,
1809 int64_t *packedsamples, flag flush, flag verbose,
1810 MSRecord *mstemplate)
1811 {
1812 MSTrace *mst;
1813 int trpackedrecords = 0;
1814 int64_t trpackedsamples = 0;
1815 char srcname[50];
1816
1817 if (!mstg)
1818 {
1819 return -1;
1820 }
1821
1822 if (packedsamples)
1823 *packedsamples = 0;
1824
1825 mst = mstg->traces;
1826
1827 while (mst)
1828 {
1829 if (mst->numsamples <= 0)
1830 {
1831 if (verbose > 1)
1832 {
1833 mst_srcname (mst, srcname, 1);
1834 ms_log (1, "No data samples for %s, skipping\n", srcname);
1835 }
1836 }
1837 else
1838 {
1839 trpackedrecords += mst_pack (mst, record_handler, handlerdata, reclen,
1840 encoding, byteorder, &trpackedsamples, flush,
1841 verbose, mstemplate);
1842
1843 if (trpackedrecords == -1)
1844 break;
1845
1846 if (packedsamples)
1847 *packedsamples += trpackedsamples;
1848 }
1849
1850 mst = mst->next;
1851 }
1852
1853 return trpackedrecords;
1854 } /* End of mst_packgroup() */
1855