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