You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1278 lines
35 KiB
C
1278 lines
35 KiB
C
/***************************************************************************
|
|
* tracelist.c:
|
|
*
|
|
* Routines to handle TraceList and related structures.
|
|
*
|
|
* Written by Chad Trabant, IRIS Data Management Center
|
|
*
|
|
* modified: 2012.273
|
|
***************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "libmseed.h"
|
|
|
|
|
|
MSTraceSeg *mstl_msr2seg (MSRecord *msr, hptime_t endtime);
|
|
MSTraceSeg *mstl_addmsrtoseg (MSTraceSeg *seg, MSRecord *msr, hptime_t endtime, flag whence);
|
|
MSTraceSeg *mstl_addsegtoseg (MSTraceSeg *seg1, MSTraceSeg *seg2);
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_init:
|
|
*
|
|
* Initialize and return a MSTraceList struct, allocating memory if
|
|
* needed. If the supplied MSTraceList is not NULL any associated
|
|
* memory it will be freed including data at prvtptr pointers.
|
|
*
|
|
* Returns a pointer to a MSTraceList struct on success or NULL on error.
|
|
***************************************************************************/
|
|
MSTraceList *
|
|
mstl_init ( MSTraceList *mstl )
|
|
{
|
|
if ( mstl )
|
|
{
|
|
mstl_free (&mstl, 1);
|
|
}
|
|
|
|
mstl = (MSTraceList *) malloc (sizeof(MSTraceList));
|
|
|
|
if ( mstl == NULL )
|
|
{
|
|
ms_log (2, "mstl_init(): Cannot allocate memory\n");
|
|
return NULL;
|
|
}
|
|
|
|
memset (mstl, 0, sizeof (MSTraceList));
|
|
|
|
return mstl;
|
|
} /* End of mstl_init() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_free:
|
|
*
|
|
* Free all memory associated with a MSTraceList struct and set the
|
|
* pointer to 0.
|
|
*
|
|
* If the freeprvtptr flag is true any private pointer data will also
|
|
* be freed when present.
|
|
***************************************************************************/
|
|
void
|
|
mstl_free ( MSTraceList **ppmstl, flag freeprvtptr )
|
|
{
|
|
MSTraceID *id = 0;
|
|
MSTraceID *nextid = 0;
|
|
MSTraceSeg *seg = 0;
|
|
MSTraceSeg *nextseg = 0;
|
|
|
|
if ( ! ppmstl )
|
|
return;
|
|
|
|
if ( *ppmstl )
|
|
{
|
|
/* Free any associated traces */
|
|
id = (*ppmstl)->traces;
|
|
while ( id )
|
|
{
|
|
nextid = id->next;
|
|
|
|
/* Free any associated trace segments */
|
|
seg = id->first;
|
|
while ( seg )
|
|
{
|
|
nextseg = seg->next;
|
|
|
|
/* Free private pointer data if present and requested*/
|
|
if ( freeprvtptr && seg->prvtptr )
|
|
free (seg->prvtptr);
|
|
|
|
/* Free data array if allocated */
|
|
if ( seg->datasamples )
|
|
free (seg->datasamples);
|
|
|
|
free (seg);
|
|
seg = nextseg;
|
|
}
|
|
|
|
/* Free private pointer data if present and requested*/
|
|
if ( freeprvtptr && id->prvtptr )
|
|
free (id->prvtptr);
|
|
|
|
free (id);
|
|
id = nextid;
|
|
}
|
|
|
|
free (*ppmstl);
|
|
|
|
*ppmstl = NULL;
|
|
}
|
|
|
|
return;
|
|
} /* End of mstl_free() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_addmsr:
|
|
*
|
|
* Add data coverage from an MSRecord to a MSTraceList by searching the
|
|
* list for the appropriate MSTraceID and MSTraceSeg and either adding
|
|
* data to it or creating a new MStraceID and/or MSTraceSeg if needed.
|
|
*
|
|
* If the dataquality flag is true the data quality bytes must also
|
|
* match otherwise they are ignored.
|
|
*
|
|
* If the autoheal flag is true extra processing is invoked to conjoin
|
|
* trace segments that fit together after the MSRecord coverage is
|
|
* added. For segments that are removed, any memory at the prvtptr
|
|
* will be freed.
|
|
*
|
|
* An MSTraceList is always maintained with the MSTraceIDs in
|
|
* descending alphanumeric order. MSTraceIDs are always maintained
|
|
* with MSTraceSegs in data time time order.
|
|
*
|
|
* Return a pointer to the MSTraceSeg updated or 0 on error.
|
|
***************************************************************************/
|
|
MSTraceSeg *
|
|
mstl_addmsr ( MSTraceList *mstl, MSRecord *msr, flag dataquality,
|
|
flag autoheal, double timetol, double sampratetol )
|
|
{
|
|
MSTraceID *id = 0;
|
|
MSTraceID *searchid = 0;
|
|
MSTraceID *ltid = 0;
|
|
|
|
MSTraceSeg *seg = 0;
|
|
MSTraceSeg *searchseg = 0;
|
|
MSTraceSeg *segbefore = 0;
|
|
MSTraceSeg *segafter = 0;
|
|
MSTraceSeg *followseg = 0;
|
|
|
|
hptime_t endtime;
|
|
hptime_t pregap;
|
|
hptime_t postgap;
|
|
hptime_t lastgap;
|
|
hptime_t firstgap;
|
|
hptime_t hpdelta;
|
|
hptime_t hptimetol = 0;
|
|
hptime_t nhptimetol = 0;
|
|
|
|
char srcname[45];
|
|
char *s1, *s2;
|
|
flag whence;
|
|
flag lastratecheck;
|
|
flag firstratecheck;
|
|
int mag;
|
|
int cmp;
|
|
int ltmag;
|
|
int ltcmp;
|
|
|
|
if ( ! mstl || ! msr )
|
|
return 0;
|
|
|
|
/* Calculate end time for MSRecord */
|
|
if ( (endtime = msr_endtime (msr)) == HPTERROR )
|
|
{
|
|
ms_log (2, "mstl_addmsr(): Error calculating record end time\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Generate source name string */
|
|
if ( ! msr_srcname (msr, srcname, dataquality) )
|
|
{
|
|
ms_log (2, "mstl_addmsr(): Error generating srcname for MSRecord\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Search for matching trace ID starting with last accessed ID and
|
|
then looping through the trace ID list. */
|
|
if ( mstl->last )
|
|
{
|
|
s1 = mstl->last->srcname;
|
|
s2 = srcname;
|
|
while ( *s1 == *s2++ )
|
|
{
|
|
if ( *s1++ == '\0' )
|
|
break;
|
|
}
|
|
cmp = (*s1 - *--s2);
|
|
|
|
if ( ! cmp )
|
|
{
|
|
id = mstl->last;
|
|
}
|
|
else
|
|
{
|
|
/* Loop through trace ID list searching for a match, simultaneously
|
|
* track the source name which is closest but less than the MSRecord
|
|
* to allow for later insertion with sort order. */
|
|
searchid = mstl->traces;
|
|
ltcmp = 0;
|
|
ltmag = 0;
|
|
while ( searchid )
|
|
{
|
|
/* Compare source names */
|
|
s1 = searchid->srcname;
|
|
s2 = srcname;
|
|
mag = 0;
|
|
while ( *s1 == *s2++ )
|
|
{
|
|
mag++;
|
|
if ( *s1++ == '\0' )
|
|
break;
|
|
}
|
|
cmp = (*s1 - *--s2);
|
|
|
|
/* If source names did not match track closest "less than" value
|
|
* and continue searching. */
|
|
if ( cmp != 0 )
|
|
{
|
|
if ( cmp < 0 )
|
|
{
|
|
if ( (ltcmp == 0 || cmp >= ltcmp) && mag >= ltmag )
|
|
{
|
|
ltcmp = cmp;
|
|
ltmag = mag;
|
|
ltid = searchid;
|
|
}
|
|
else if ( mag > ltmag )
|
|
{
|
|
ltcmp = cmp;
|
|
ltmag = mag;
|
|
ltid = searchid;
|
|
}
|
|
}
|
|
|
|
searchid = searchid->next;
|
|
continue;
|
|
}
|
|
|
|
/* If we made it this far we found a match */
|
|
id = searchid;
|
|
break;
|
|
}
|
|
}
|
|
} /* Done searching for match in trace ID list */
|
|
|
|
/* If no matching ID was found create new MSTraceID and MSTraceSeg entries */
|
|
if ( ! id )
|
|
{
|
|
if ( ! (id = (MSTraceID *) calloc (1, sizeof(MSTraceID))) )
|
|
{
|
|
ms_log (2, "mstl_addmsr(): Error allocating memory\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Populate MSTraceID */
|
|
strcpy (id->network, msr->network);
|
|
strcpy (id->station, msr->station);
|
|
strcpy (id->location, msr->location);
|
|
strcpy (id->channel, msr->channel);
|
|
id->dataquality = msr->dataquality;
|
|
strcpy (id->srcname, srcname);
|
|
|
|
id->earliest = msr->starttime;
|
|
id->latest = endtime;
|
|
id->numsegments = 1;
|
|
|
|
if ( ! (seg = mstl_msr2seg (msr, endtime)) )
|
|
{
|
|
return 0;
|
|
}
|
|
id->first = id->last = seg;
|
|
|
|
/* Add new MSTraceID to MSTraceList */
|
|
if ( ! mstl->traces || ! ltid )
|
|
{
|
|
id->next = mstl->traces;
|
|
mstl->traces = id;
|
|
}
|
|
else
|
|
{
|
|
id->next = ltid->next;
|
|
ltid->next = id;
|
|
}
|
|
|
|
mstl->numtraces++;
|
|
}
|
|
/* Add data coverage to the matching MSTraceID */
|
|
else
|
|
{
|
|
/* Calculate high-precision sample period */
|
|
hpdelta = (hptime_t) (( msr->samprate ) ? (HPTMODULUS / msr->samprate) : 0.0);
|
|
|
|
/* Calculate high-precision time tolerance */
|
|
if ( timetol == -1.0 )
|
|
hptimetol = (hptime_t) (0.5 * hpdelta); /* Default time tolerance is 1/2 sample period */
|
|
else if ( timetol >= 0.0 )
|
|
hptimetol = (hptime_t) (timetol * HPTMODULUS);
|
|
|
|
nhptimetol = ( hptimetol ) ? -hptimetol : 0;
|
|
|
|
/* last/firstgap are negative when the record overlaps the trace
|
|
* segment and positive when there is a time gap. */
|
|
|
|
/* Gap relative to the last segment */
|
|
lastgap = msr->starttime - id->last->endtime - hpdelta;
|
|
|
|
/* Gap relative to the first segment */
|
|
firstgap = id->first->starttime - endtime - hpdelta;
|
|
|
|
/* Sample rate tolerance checks for first and last segments */
|
|
if ( sampratetol == -1.0 )
|
|
{
|
|
lastratecheck = MS_ISRATETOLERABLE (msr->samprate, id->last->samprate);
|
|
firstratecheck = MS_ISRATETOLERABLE (msr->samprate, id->first->samprate);
|
|
}
|
|
else
|
|
{
|
|
lastratecheck = (ms_dabs (msr->samprate - id->last->samprate) > sampratetol) ? 0 : 1;
|
|
firstratecheck = (ms_dabs (msr->samprate - id->first->samprate) > sampratetol) ? 0 : 1;
|
|
}
|
|
|
|
/* Search first for the simple scenarios in order of likelihood:
|
|
* - Record fits at end of last segment
|
|
* - Record fits after all coverage
|
|
* - Record fits before all coverage
|
|
* - Record fits at beginning of first segment
|
|
*
|
|
* If none of those scenarios are true search the complete segment list.
|
|
*/
|
|
|
|
/* Record coverage fits at end of last segment */
|
|
if ( lastgap <= hptimetol && lastgap >= nhptimetol && lastratecheck )
|
|
{
|
|
if ( ! mstl_addmsrtoseg (id->last, msr, endtime, 1) )
|
|
return 0;
|
|
|
|
seg = id->last;
|
|
|
|
if ( endtime > id->latest )
|
|
id->latest = endtime;
|
|
}
|
|
/* Record coverage is after all other coverage */
|
|
else if ( (msr->starttime - hpdelta - hptimetol) > id->latest )
|
|
{
|
|
if ( ! (seg = mstl_msr2seg (msr, endtime)) )
|
|
return 0;
|
|
|
|
/* Add to end of list */
|
|
id->last->next = seg;
|
|
seg->prev = id->last;
|
|
id->last = seg;
|
|
id->numsegments++;
|
|
|
|
if ( endtime > id->latest )
|
|
id->latest = endtime;
|
|
}
|
|
/* Record coverage is before all other coverage */
|
|
else if ( (endtime + hpdelta + hptimetol) < id->earliest )
|
|
{
|
|
if ( ! (seg = mstl_msr2seg (msr, endtime)) )
|
|
return 0;
|
|
|
|
/* Add to beginning of list */
|
|
id->first->prev = seg;
|
|
seg->next = id->first;
|
|
id->first = seg;
|
|
id->numsegments++;
|
|
|
|
if ( msr->starttime < id->earliest )
|
|
id->earliest = msr->starttime;
|
|
}
|
|
/* Record coverage fits at beginning of first segment */
|
|
else if ( firstgap <= hptimetol && firstgap >= nhptimetol && firstratecheck )
|
|
{
|
|
if ( ! mstl_addmsrtoseg (id->first, msr, endtime, 2) )
|
|
return 0;
|
|
|
|
seg = id->first;
|
|
|
|
if ( msr->starttime < id->earliest )
|
|
id->earliest = msr->starttime;
|
|
}
|
|
/* Search complete segment list for matches */
|
|
else
|
|
{
|
|
searchseg = id->first;
|
|
segbefore = 0; /* Find segment that record fits before */
|
|
segafter = 0; /* Find segment that record fits after */
|
|
followseg = 0; /* Track segment that record follows in time order */
|
|
while ( searchseg )
|
|
{
|
|
if ( msr->starttime > searchseg->starttime )
|
|
followseg = searchseg;
|
|
|
|
whence = 0;
|
|
|
|
postgap = msr->starttime - searchseg->endtime - hpdelta;
|
|
if ( ! segbefore && postgap <= hptimetol && postgap >= nhptimetol )
|
|
whence = 1;
|
|
|
|
pregap = searchseg->starttime - endtime - hpdelta;
|
|
if ( ! segafter && pregap <= hptimetol && pregap >= nhptimetol )
|
|
whence = 2;
|
|
|
|
if ( ! whence )
|
|
{
|
|
searchseg = searchseg->next;
|
|
continue;
|
|
}
|
|
|
|
if ( sampratetol == -1.0 )
|
|
{
|
|
if ( ! MS_ISRATETOLERABLE (msr->samprate, searchseg->samprate) )
|
|
{
|
|
searchseg = searchseg->next;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ms_dabs (msr->samprate - searchseg->samprate) > sampratetol )
|
|
{
|
|
searchseg = searchseg->next;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( whence == 1 )
|
|
segbefore = searchseg;
|
|
else
|
|
segafter = searchseg;
|
|
|
|
/* Done searching if not autohealing */
|
|
if ( ! autoheal )
|
|
break;
|
|
|
|
/* Done searching if both before and after segments are found */
|
|
if ( segbefore && segafter )
|
|
break;
|
|
|
|
searchseg = searchseg->next;
|
|
} /* Done looping through segments */
|
|
|
|
/* Add MSRecord coverage to end of segment before */
|
|
if ( segbefore )
|
|
{
|
|
if ( ! mstl_addmsrtoseg (segbefore, msr, endtime, 1) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Merge two segments that now fit if autohealing */
|
|
if ( autoheal && segafter && segbefore != segafter )
|
|
{
|
|
/* Add segafter coverage to segbefore */
|
|
if ( ! mstl_addsegtoseg (segbefore, segafter) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Shift last segment pointer if it's going to be removed */
|
|
if ( segafter == id->last )
|
|
id->last = id->last->prev;
|
|
|
|
/* Remove segafter from list */
|
|
if ( segafter->prev )
|
|
segafter->prev->next = segafter->next;
|
|
if ( segafter->next )
|
|
segafter->next->prev = segafter->prev;
|
|
|
|
/* Free data samples, private data and segment structure */
|
|
if (segafter->datasamples)
|
|
free (segafter->datasamples);
|
|
|
|
if (segafter->prvtptr)
|
|
free (segafter->prvtptr);
|
|
|
|
free (segafter);
|
|
}
|
|
|
|
seg = segbefore;
|
|
}
|
|
/* Add MSRecord coverage to beginning of segment after */
|
|
else if ( segafter )
|
|
{
|
|
if ( ! mstl_addmsrtoseg (segafter, msr, endtime, 2) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
seg = segafter;
|
|
}
|
|
/* Add MSRecord coverage to new segment */
|
|
else
|
|
{
|
|
/* Create new segment */
|
|
if ( ! (seg = mstl_msr2seg (msr, endtime)) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Add new segment as first in list */
|
|
if ( ! followseg )
|
|
{
|
|
seg->next = id->first;
|
|
if ( id->first )
|
|
id->first->prev = seg;
|
|
|
|
id->first = seg;
|
|
}
|
|
/* Add new segment after the followseg segment */
|
|
else
|
|
{
|
|
seg->next = followseg->next;
|
|
seg->prev = followseg;
|
|
if ( followseg->next )
|
|
followseg->next->prev = seg;
|
|
followseg->next = seg;
|
|
|
|
if ( followseg == id->last )
|
|
id->last = seg;
|
|
}
|
|
|
|
id->numsegments++;
|
|
}
|
|
|
|
/* Track earliest and latest times */
|
|
if ( msr->starttime < id->earliest )
|
|
id->earliest = msr->starttime;
|
|
|
|
if ( endtime > id->latest )
|
|
id->latest = endtime;
|
|
} /* End of searching segment list */
|
|
} /* End of adding coverage to matching ID */
|
|
|
|
/* Sort modified segment into place, logic above should limit these to few shifts if any */
|
|
while ( seg->next && ( seg->starttime > seg->next->starttime ||
|
|
(seg->starttime == seg->next->starttime && seg->endtime < seg->next->endtime) ) )
|
|
{
|
|
/* Move segment down list, swap seg and seg->next */
|
|
segafter = seg->next;
|
|
|
|
if ( seg->prev )
|
|
seg->prev->next = segafter;
|
|
|
|
if ( segafter->next )
|
|
segafter->next->prev = seg;
|
|
|
|
segafter->prev = seg->prev;
|
|
seg->prev = segafter;
|
|
seg->next = segafter->next;
|
|
segafter->next = seg;
|
|
|
|
/* Reset first and last segment pointers if replaced */
|
|
if ( id->first == seg )
|
|
id->first = segafter;
|
|
|
|
if ( id->last == segafter )
|
|
id->last = seg;
|
|
}
|
|
while ( seg->prev && ( seg->starttime < seg->prev->starttime ||
|
|
(seg->starttime == seg->prev->starttime && seg->endtime > seg->prev->endtime) ) )
|
|
{
|
|
/* Move segment up list, swap seg and seg->prev */
|
|
segbefore = seg->prev;
|
|
|
|
if ( seg->next )
|
|
seg->next->prev = segbefore;
|
|
|
|
if ( segbefore->prev )
|
|
segbefore->prev->next = seg;
|
|
|
|
segbefore->next = seg->next;
|
|
seg->next = segbefore;
|
|
seg->prev = segbefore->prev;
|
|
segbefore->prev = seg;
|
|
|
|
/* Reset first and last segment pointers if replaced */
|
|
if ( id->first == segbefore )
|
|
id->first = seg;
|
|
|
|
if ( id->last == seg )
|
|
id->last = segbefore;
|
|
}
|
|
|
|
/* Set MSTraceID as last accessed */
|
|
mstl->last = id;
|
|
|
|
return seg;
|
|
} /* End of mstl_addmsr() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_msr2seg:
|
|
*
|
|
* Create an MSTraceSeg structure from an MSRecord structure.
|
|
*
|
|
* Return a pointer to a MSTraceSeg otherwise 0 on error.
|
|
***************************************************************************/
|
|
MSTraceSeg *
|
|
mstl_msr2seg (MSRecord *msr, hptime_t endtime)
|
|
{
|
|
MSTraceSeg *seg = 0;
|
|
int samplesize;
|
|
|
|
if ( ! (seg = (MSTraceSeg *) calloc (1, sizeof(MSTraceSeg))) )
|
|
{
|
|
ms_log (2, "mstl_addmsr(): Error allocating memory\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Populate MSTraceSeg */
|
|
seg->starttime = msr->starttime;
|
|
seg->endtime = endtime;
|
|
seg->samprate = msr->samprate;
|
|
seg->samplecnt = msr->samplecnt;
|
|
seg->sampletype = msr->sampletype;
|
|
seg->numsamples = msr->numsamples;
|
|
|
|
/* Allocate space for and copy datasamples */
|
|
if ( msr->datasamples && msr->numsamples )
|
|
{
|
|
samplesize = ms_samplesize (msr->sampletype);
|
|
|
|
if ( ! (seg->datasamples = malloc ((size_t) (samplesize * msr->numsamples))) )
|
|
{
|
|
ms_log (2, "mstl_msr2seg(): Error allocating memory\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Copy data samples from MSRecord to MSTraceSeg */
|
|
memcpy (seg->datasamples, msr->datasamples, (size_t) (samplesize * msr->numsamples));
|
|
}
|
|
|
|
return seg;
|
|
} /* End of mstl_msr2seg() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_addmsrtoseg:
|
|
*
|
|
* Add data coverage from a MSRecord structure to a MSTraceSeg structure.
|
|
*
|
|
* Data coverage is added to the beginning or end of MSTraceSeg
|
|
* according to the whence flag:
|
|
* 1 : add coverage to the end
|
|
* 2 : add coverage to the beginninig
|
|
*
|
|
* Return a pointer to a MSTraceSeg otherwise 0 on error.
|
|
***************************************************************************/
|
|
MSTraceSeg *
|
|
mstl_addmsrtoseg (MSTraceSeg *seg, MSRecord *msr, hptime_t endtime, flag whence)
|
|
{
|
|
int samplesize = 0;
|
|
void *newdatasamples;
|
|
|
|
if ( ! seg || ! msr )
|
|
return 0;
|
|
|
|
/* Allocate more memory for data samples if included */
|
|
if ( msr->datasamples && msr->numsamples > 0 )
|
|
{
|
|
if ( msr->sampletype != seg->sampletype )
|
|
{
|
|
ms_log (2, "mstl_addmsrtoseg(): MSRecord sample type (%c) does not match segment sample type (%c)\n",
|
|
msr->sampletype, seg->sampletype);
|
|
return 0;
|
|
}
|
|
|
|
if ( ! (samplesize = ms_samplesize (msr->sampletype)) )
|
|
{
|
|
ms_log (2, "mstl_addmsrtoseg(): Unknown sample size for sample type: %c\n", msr->sampletype);
|
|
return 0;
|
|
}
|
|
|
|
if ( ! (newdatasamples = realloc (seg->datasamples, (size_t)((seg->numsamples + msr->numsamples) * samplesize))) )
|
|
{
|
|
ms_log (2, "mstl_addmsrtoseg(): Error allocating memory\n");
|
|
return 0;
|
|
}
|
|
|
|
seg->datasamples = newdatasamples;
|
|
}
|
|
|
|
/* Add coverage to end of segment */
|
|
if ( whence == 1 )
|
|
{
|
|
seg->endtime = endtime;
|
|
seg->samplecnt += msr->samplecnt;
|
|
|
|
if ( msr->datasamples && msr->numsamples > 0 )
|
|
{
|
|
memcpy ((char *)seg->datasamples + (seg->numsamples * samplesize),
|
|
msr->datasamples,
|
|
(size_t) (msr->numsamples * samplesize));
|
|
|
|
seg->numsamples += msr->numsamples;
|
|
}
|
|
}
|
|
/* Add coverage to beginning of segment */
|
|
else if ( whence == 2 )
|
|
{
|
|
seg->starttime = msr->starttime;
|
|
seg->samplecnt += msr->samplecnt;
|
|
|
|
if ( msr->datasamples && msr->numsamples > 0 )
|
|
{
|
|
memmove ((char *)seg->datasamples + (msr->numsamples * samplesize),
|
|
seg->datasamples,
|
|
(size_t) (seg->numsamples * samplesize));
|
|
|
|
memcpy (seg->datasamples,
|
|
msr->datasamples,
|
|
(size_t) (msr->numsamples * samplesize));
|
|
|
|
seg->numsamples += msr->numsamples;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ms_log (2, "mstl_addmsrtoseg(): unrecognized whence value: %d\n", whence);
|
|
return 0;
|
|
}
|
|
|
|
return seg;
|
|
} /* End of mstl_addmsrtoseg() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_addsegtoseg:
|
|
*
|
|
* Add data coverage from seg2 to seg1.
|
|
*
|
|
* Return a pointer to a seg1 otherwise 0 on error.
|
|
***************************************************************************/
|
|
MSTraceSeg *
|
|
mstl_addsegtoseg (MSTraceSeg *seg1, MSTraceSeg *seg2)
|
|
{
|
|
int samplesize = 0;
|
|
void *newdatasamples;
|
|
|
|
if ( ! seg1 || ! seg2 )
|
|
return 0;
|
|
|
|
/* Allocate more memory for data samples if included */
|
|
if ( seg2->datasamples && seg2->numsamples > 0 )
|
|
{
|
|
if ( seg2->sampletype != seg1->sampletype )
|
|
{
|
|
ms_log (2, "mstl_addsegtoseg(): MSTraceSeg sample types do not match (%c and %c)\n",
|
|
seg1->sampletype, seg2->sampletype);
|
|
return 0;
|
|
}
|
|
|
|
if ( ! (samplesize = ms_samplesize (seg1->sampletype)) )
|
|
{
|
|
ms_log (2, "mstl_addsegtoseg(): Unknown sample size for sample type: %c\n", seg1->sampletype);
|
|
return 0;
|
|
}
|
|
|
|
if ( ! (newdatasamples = realloc (seg1->datasamples, (size_t) ((seg1->numsamples + seg2->numsamples) * samplesize))) )
|
|
{
|
|
ms_log (2, "mstl_addsegtoseg(): Error allocating memory\n");
|
|
return 0;
|
|
}
|
|
|
|
seg1->datasamples = newdatasamples;
|
|
}
|
|
|
|
/* Add seg2 coverage to end of seg1 */
|
|
seg1->endtime = seg2->endtime;
|
|
seg1->samplecnt += seg2->samplecnt;
|
|
|
|
if ( seg2->datasamples && seg2->numsamples > 0 )
|
|
{
|
|
memcpy ((char *)seg1->datasamples + (seg1->numsamples * samplesize),
|
|
seg2->datasamples,
|
|
(size_t) (seg2->numsamples * samplesize));
|
|
|
|
seg1->numsamples += seg2->numsamples;
|
|
}
|
|
|
|
return seg1;
|
|
} /* End of mstl_addsegtoseg() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_convertsamples:
|
|
*
|
|
* Convert the data samples associated with an MSTraceSeg to another
|
|
* data type. ASCII data samples cannot be converted, if supplied or
|
|
* requested an error will be returned.
|
|
*
|
|
* When converting float & double sample types to integer type a
|
|
* simple rounding is applied by adding 0.5 to the sample value before
|
|
* converting (truncating) to integer.
|
|
*
|
|
* If the truncate flag is true data samples will be truncated to
|
|
* integers even if loss of sample precision is detected. If the
|
|
* truncate flag is false (0) and loss of precision is detected an
|
|
* error is returned.
|
|
*
|
|
* Returns 0 on success, and -1 on failure.
|
|
***************************************************************************/
|
|
int
|
|
mstl_convertsamples ( MSTraceSeg *seg, char type, flag truncate )
|
|
{
|
|
int32_t *idata;
|
|
float *fdata;
|
|
double *ddata;
|
|
int64_t idx;
|
|
|
|
if ( ! seg )
|
|
return -1;
|
|
|
|
/* No conversion necessary, report success */
|
|
if ( seg->sampletype == type )
|
|
return 0;
|
|
|
|
if ( seg->sampletype == 'a' || type == 'a' )
|
|
{
|
|
ms_log (2, "mstl_convertsamples: cannot convert ASCII samples to/from numeric type\n");
|
|
return -1;
|
|
}
|
|
|
|
idata = (int32_t *) seg->datasamples;
|
|
fdata = (float *) seg->datasamples;
|
|
ddata = (double *) seg->datasamples;
|
|
|
|
/* Convert to 32-bit integers */
|
|
if ( type == 'i' )
|
|
{
|
|
if ( seg->sampletype == 'f' ) /* Convert floats to integers with simple rounding */
|
|
{
|
|
for (idx = 0; idx < seg->numsamples; idx++)
|
|
{
|
|
/* Check for loss of sub-integer */
|
|
if ( ! truncate && (fdata[idx] - (int32_t)fdata[idx]) > 0.000001 )
|
|
{
|
|
ms_log (1, "mstl_convertsamples: Warning, loss of precision when converting floats to integers, loss: %g\n",
|
|
(fdata[idx] - (int32_t)fdata[idx]));
|
|
return -1;
|
|
}
|
|
|
|
idata[idx] = (int32_t) (fdata[idx] + 0.5);
|
|
}
|
|
}
|
|
else if ( seg->sampletype == 'd' ) /* Convert doubles to integers with simple rounding */
|
|
{
|
|
for (idx = 0; idx < seg->numsamples; idx++)
|
|
{
|
|
/* Check for loss of sub-integer */
|
|
if ( ! truncate && (ddata[idx] - (int32_t)ddata[idx]) > 0.000001 )
|
|
{
|
|
ms_log (1, "mstl_convertsamples: Warning, loss of precision when converting doubles to integers, loss: %g\n",
|
|
(ddata[idx] - (int32_t)ddata[idx]));
|
|
return -1;
|
|
}
|
|
|
|
idata[idx] = (int32_t) (ddata[idx] + 0.5);
|
|
}
|
|
|
|
/* Reallocate buffer for reduced size needed */
|
|
if ( ! (seg->datasamples = realloc (seg->datasamples, (seg->numsamples * sizeof(int32_t)))) )
|
|
{
|
|
ms_log (2, "mstl_convertsamples: cannot re-allocate buffer for sample conversion\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
seg->sampletype = 'i';
|
|
} /* Done converting to 32-bit integers */
|
|
|
|
/* Convert to 32-bit floats */
|
|
else if ( type == 'f' )
|
|
{
|
|
if ( seg->sampletype == 'i' ) /* Convert integers to floats */
|
|
{
|
|
for (idx = 0; idx < seg->numsamples; idx++)
|
|
fdata[idx] = (float) idata[idx];
|
|
}
|
|
else if ( seg->sampletype == 'd' ) /* Convert doubles to floats */
|
|
{
|
|
for (idx = 0; idx < seg->numsamples; idx++)
|
|
fdata[idx] = (float) ddata[idx];
|
|
|
|
/* Reallocate buffer for reduced size needed */
|
|
if ( ! (seg->datasamples = realloc (seg->datasamples, (seg->numsamples * sizeof(float)))) )
|
|
{
|
|
ms_log (2, "mstl_convertsamples: cannot re-allocate buffer after sample conversion\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
seg->sampletype = 'f';
|
|
} /* Done converting to 32-bit floats */
|
|
|
|
/* Convert to 64-bit doubles */
|
|
else if ( type == 'd' )
|
|
{
|
|
if ( ! (ddata = (double *) malloc (seg->numsamples * sizeof(double))) )
|
|
{
|
|
ms_log (2, "mstl_convertsamples: cannot allocate buffer for sample conversion to doubles\n");
|
|
return -1;
|
|
}
|
|
|
|
if ( seg->sampletype == 'i' ) /* Convert integers to doubles */
|
|
{
|
|
for (idx = 0; idx < seg->numsamples; idx++)
|
|
ddata[idx] = (double) idata[idx];
|
|
|
|
free (idata);
|
|
}
|
|
else if ( seg->sampletype == 'f' ) /* Convert floats to doubles */
|
|
{
|
|
for (idx = 0; idx < seg->numsamples; idx++)
|
|
ddata[idx] = (double) fdata[idx];
|
|
|
|
free (fdata);
|
|
}
|
|
|
|
seg->datasamples = ddata;
|
|
seg->sampletype = 'd';
|
|
} /* Done converting to 64-bit doubles */
|
|
|
|
return 0;
|
|
} /* End of mstl_convertsamples() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_printtracelist:
|
|
*
|
|
* Print trace list summary information for the specified MSTraceList.
|
|
*
|
|
* By default only print the srcname, starttime and endtime for each
|
|
* trace. If details is greater than 0 include the sample rate,
|
|
* number of samples and a total trace count. If gaps is greater than
|
|
* 0 and the previous trace matches (srcname & samprate) include the
|
|
* gap between the endtime of the last trace and the starttime of the
|
|
* current trace.
|
|
*
|
|
* The timeformat flag can either be:
|
|
* 0 : SEED time format (year, day-of-year, hour, min, sec)
|
|
* 1 : ISO time format (year, month, day, hour, min, sec)
|
|
* 2 : Epoch time, seconds since the epoch
|
|
***************************************************************************/
|
|
void
|
|
mstl_printtracelist ( MSTraceList *mstl, flag timeformat,
|
|
flag details, flag gaps )
|
|
{
|
|
MSTraceID *id = 0;
|
|
MSTraceSeg *seg = 0;
|
|
char stime[30];
|
|
char etime[30];
|
|
char gapstr[20];
|
|
flag nogap;
|
|
double gap;
|
|
double delta;
|
|
int tracecnt = 0;
|
|
int segcnt = 0;
|
|
|
|
if ( ! mstl )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Print out the appropriate header */
|
|
if ( details > 0 && gaps > 0 )
|
|
ms_log (0, " Source Start sample End sample Gap Hz Samples\n");
|
|
else if ( details <= 0 && gaps > 0 )
|
|
ms_log (0, " Source Start sample End sample Gap\n");
|
|
else if ( details > 0 && gaps <= 0 )
|
|
ms_log (0, " Source Start sample End sample Hz Samples\n");
|
|
else
|
|
ms_log (0, " Source Start sample End sample\n");
|
|
|
|
/* Loop through trace list */
|
|
id = mstl->traces;
|
|
while ( id )
|
|
{
|
|
/* Loop through segment list */
|
|
seg = id->first;
|
|
while ( seg )
|
|
{
|
|
/* Create formatted time strings */
|
|
if ( timeformat == 2 )
|
|
{
|
|
snprintf (stime, sizeof(stime), "%.6f", (double) MS_HPTIME2EPOCH(seg->starttime) );
|
|
snprintf (etime, sizeof(etime), "%.6f", (double) MS_HPTIME2EPOCH(seg->endtime) );
|
|
}
|
|
else if ( timeformat == 1 )
|
|
{
|
|
if ( ms_hptime2isotimestr (seg->starttime, stime, 1) == NULL )
|
|
ms_log (2, "Cannot convert trace start time for %s\n", id->srcname);
|
|
|
|
if ( ms_hptime2isotimestr (seg->endtime, etime, 1) == NULL )
|
|
ms_log (2, "Cannot convert trace end time for %s\n", id->srcname);
|
|
}
|
|
else
|
|
{
|
|
if ( ms_hptime2seedtimestr (seg->starttime, stime, 1) == NULL )
|
|
ms_log (2, "Cannot convert trace start time for %s\n", id->srcname);
|
|
|
|
if ( ms_hptime2seedtimestr (seg->endtime, etime, 1) == NULL )
|
|
ms_log (2, "Cannot convert trace end time for %s\n", id->srcname);
|
|
}
|
|
|
|
/* Print segment info at varying levels */
|
|
if ( gaps > 0 )
|
|
{
|
|
gap = 0.0;
|
|
nogap = 0;
|
|
|
|
if ( seg->prev )
|
|
gap = (double) (seg->starttime - seg->prev->endtime) / HPTMODULUS;
|
|
else
|
|
nogap = 1;
|
|
|
|
/* Check that any overlap is not larger than the trace coverage */
|
|
if ( gap < 0.0 )
|
|
{
|
|
delta = ( seg->samprate ) ? (1.0 / seg->samprate) : 0.0;
|
|
|
|
if ( (gap * -1.0) > (((double)(seg->endtime - seg->starttime)/HPTMODULUS) + delta) )
|
|
gap = -(((double)(seg->endtime - seg->starttime)/HPTMODULUS) + delta);
|
|
}
|
|
|
|
/* Fix up gap display */
|
|
if ( nogap )
|
|
snprintf (gapstr, sizeof(gapstr), " == ");
|
|
else if ( gap >= 86400.0 || gap <= -86400.0 )
|
|
snprintf (gapstr, sizeof(gapstr), "%-3.1fd", (gap / 86400));
|
|
else if ( gap >= 3600.0 || gap <= -3600.0 )
|
|
snprintf (gapstr, sizeof(gapstr), "%-3.1fh", (gap / 3600));
|
|
else if ( gap == 0.0 )
|
|
snprintf (gapstr, sizeof(gapstr), "-0 ");
|
|
else
|
|
snprintf (gapstr, sizeof(gapstr), "%-4.4g", gap);
|
|
|
|
if ( details <= 0 )
|
|
ms_log (0, "%-17s %-24s %-24s %-4s\n",
|
|
id->srcname, stime, etime, gapstr);
|
|
else
|
|
ms_log (0, "%-17s %-24s %-24s %-s %-3.3g %-lld\n",
|
|
id->srcname, stime, etime, gapstr, seg->samprate, (long long int)seg->samplecnt);
|
|
}
|
|
else if ( details > 0 && gaps <= 0 )
|
|
ms_log (0, "%-17s %-24s %-24s %-3.3g %-lld\n",
|
|
id->srcname, stime, etime, seg->samprate, (long long int)seg->samplecnt);
|
|
else
|
|
ms_log (0, "%-17s %-24s %-24s\n", id->srcname, stime, etime);
|
|
|
|
segcnt++;
|
|
seg = seg->next;
|
|
}
|
|
|
|
tracecnt++;
|
|
id = id->next;
|
|
}
|
|
|
|
if ( tracecnt != mstl->numtraces )
|
|
ms_log (2, "mstl_printtracelist(): number of traces in trace list is inconsistent\n");
|
|
|
|
if ( details > 0 )
|
|
ms_log (0, "Total: %d trace(s) with %d segment(s)\n", tracecnt, segcnt);
|
|
|
|
return;
|
|
} /* End of mstl_printtracelist() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_printsynclist:
|
|
*
|
|
* Print SYNC trace list summary information for the specified MSTraceList.
|
|
*
|
|
* The SYNC header line will be created using the supplied dccid, if
|
|
* the pointer is NULL the string "DCC" will be used instead.
|
|
*
|
|
* If the subsecond flag is true the segment start and end times will
|
|
* include subsecond precision, otherwise they will be truncated to
|
|
* integer seconds.
|
|
*
|
|
***************************************************************************/
|
|
void
|
|
mstl_printsynclist ( MSTraceList *mstl, char *dccid, flag subsecond )
|
|
{
|
|
MSTraceID *id = 0;
|
|
MSTraceSeg *seg = 0;
|
|
char starttime[30];
|
|
char endtime[30];
|
|
char yearday[10];
|
|
time_t now;
|
|
struct tm *nt;
|
|
|
|
if ( ! mstl )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Generate current time stamp */
|
|
now = time (NULL);
|
|
nt = localtime ( &now ); nt->tm_year += 1900; nt->tm_yday += 1;
|
|
snprintf ( yearday, sizeof(yearday), "%04d,%03d", nt->tm_year, nt->tm_yday);
|
|
|
|
/* Print SYNC header line */
|
|
ms_log (0, "%s|%s\n", (dccid)?dccid:"DCC", yearday);
|
|
|
|
/* Loop through trace list */
|
|
id = mstl->traces;
|
|
while ( id )
|
|
{
|
|
/* Loop through segment list */
|
|
seg = id->first;
|
|
while ( seg )
|
|
{
|
|
ms_hptime2seedtimestr (seg->starttime, starttime, subsecond);
|
|
ms_hptime2seedtimestr (seg->endtime, endtime, subsecond);
|
|
|
|
/* Print SYNC line */
|
|
ms_log (0, "%s|%s|%s|%s|%s|%s||%.10g|%lld|||||||%s\n",
|
|
id->network, id->station, id->location, id->channel,
|
|
starttime, endtime, seg->samprate, (long long int)seg->samplecnt,
|
|
yearday);
|
|
|
|
seg = seg->next;
|
|
}
|
|
|
|
id = id->next;
|
|
}
|
|
|
|
return;
|
|
} /* End of mstl_printsynclist() */
|
|
|
|
|
|
/***************************************************************************
|
|
* mstl_printgaplist:
|
|
*
|
|
* Print gap/overlap list summary information for the specified
|
|
* MSTraceList. Overlaps are printed as negative gaps.
|
|
*
|
|
* If mingap and maxgap are not NULL their values will be enforced and
|
|
* only gaps/overlaps matching their implied criteria will be printed.
|
|
*
|
|
* The timeformat flag can either be:
|
|
* 0 : SEED time format (year, day-of-year, hour, min, sec)
|
|
* 1 : ISO time format (year, month, day, hour, min, sec)
|
|
* 2 : Epoch time, seconds since the epoch
|
|
***************************************************************************/
|
|
void
|
|
mstl_printgaplist (MSTraceList *mstl, flag timeformat,
|
|
double *mingap, double *maxgap)
|
|
{
|
|
MSTraceID *id = 0;
|
|
MSTraceSeg *seg = 0;
|
|
|
|
char time1[30], time2[30];
|
|
char gapstr[30];
|
|
double gap;
|
|
double delta;
|
|
double nsamples;
|
|
flag printflag;
|
|
int gapcnt = 0;
|
|
|
|
if ( ! mstl )
|
|
return;
|
|
|
|
if ( ! mstl->traces )
|
|
return;
|
|
|
|
ms_log (0, " Source Last Sample Next Sample Gap Samples\n");
|
|
|
|
id = mstl->traces;
|
|
while ( id )
|
|
{
|
|
seg = id->first;
|
|
while ( seg->next )
|
|
{
|
|
/* Skip segments with 0 sample rate, usually from SOH records */
|
|
if ( seg->samprate == 0.0 )
|
|
{
|
|
seg = seg->next;
|
|
continue;
|
|
}
|
|
|
|
gap = (double) (seg->next->starttime - seg->endtime) / HPTMODULUS;
|
|
|
|
/* Check that any overlap is not larger than the trace coverage */
|
|
if ( gap < 0.0 )
|
|
{
|
|
delta = ( seg->next->samprate ) ? (1.0 / seg->next->samprate) : 0.0;
|
|
|
|
if ( (gap * -1.0) > (((double)(seg->next->endtime - seg->next->starttime)/HPTMODULUS) + delta) )
|
|
gap = -(((double)(seg->next->endtime - seg->next->starttime)/HPTMODULUS) + delta);
|
|
}
|
|
|
|
printflag = 1;
|
|
|
|
/* Check gap/overlap criteria */
|
|
if ( mingap )
|
|
if ( gap < *mingap )
|
|
printflag = 0;
|
|
|
|
if ( maxgap )
|
|
if ( gap > *maxgap )
|
|
printflag = 0;
|
|
|
|
if ( printflag )
|
|
{
|
|
nsamples = ms_dabs(gap) * seg->samprate;
|
|
|
|
if ( gap > 0.0 )
|
|
nsamples -= 1.0;
|
|
else
|
|
nsamples += 1.0;
|
|
|
|
/* Fix up gap display */
|
|
if ( gap >= 86400.0 || gap <= -86400.0 )
|
|
snprintf (gapstr, sizeof(gapstr), "%-3.1fd", (gap / 86400));
|
|
else if ( gap >= 3600.0 || gap <= -3600.0 )
|
|
snprintf (gapstr, sizeof(gapstr), "%-3.1fh", (gap / 3600));
|
|
else if ( gap == 0.0 )
|
|
snprintf (gapstr, sizeof(gapstr), "-0 ");
|
|
else
|
|
snprintf (gapstr, sizeof(gapstr), "%-4.4g", gap);
|
|
|
|
/* Create formatted time strings */
|
|
if ( timeformat == 2 )
|
|
{
|
|
snprintf (time1, sizeof(time1), "%.6f", (double) MS_HPTIME2EPOCH(seg->endtime) );
|
|
snprintf (time2, sizeof(time2), "%.6f", (double) MS_HPTIME2EPOCH(seg->next->starttime) );
|
|
}
|
|
else if ( timeformat == 1 )
|
|
{
|
|
if ( ms_hptime2isotimestr (seg->endtime, time1, 1) == NULL )
|
|
ms_log (2, "Cannot convert trace end time for %s\n", id->srcname);
|
|
|
|
if ( ms_hptime2isotimestr (seg->next->starttime, time2, 1) == NULL )
|
|
ms_log (2, "Cannot convert next trace start time for %s\n", id->srcname);
|
|
}
|
|
else
|
|
{
|
|
if ( ms_hptime2seedtimestr (seg->endtime, time1, 1) == NULL )
|
|
ms_log (2, "Cannot convert trace end time for %s\n", id->srcname);
|
|
|
|
if ( ms_hptime2seedtimestr (seg->next->starttime, time2, 1) == NULL )
|
|
ms_log (2, "Cannot convert next trace start time for %s\n", id->srcname);
|
|
}
|
|
|
|
ms_log (0, "%-17s %-24s %-24s %-4s %-.8g\n",
|
|
id->srcname, time1, time2, gapstr, nsamples);
|
|
|
|
gapcnt++;
|
|
}
|
|
|
|
seg = seg->next;
|
|
}
|
|
|
|
id = id->next;
|
|
}
|
|
|
|
ms_log (0, "Total: %d gap(s)\n", gapcnt);
|
|
|
|
return;
|
|
} /* End of mstl_printgaplist() */
|