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.

1180 lines
42 KiB
C

/***************************************************************************
* msrutils.c:
*
* Generic routines to operate on Mini-SEED records.
*
* Written by Chad Trabant
* ORFEUS/EC-Project MEREDIAN
* IRIS Data Management Center
*
* modified: 2012.088
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "libmseed.h"
/***************************************************************************
* msr_init:
*
* Initialize and return an MSRecord struct, allocating memory if
* needed. If memory for the fsdh and datasamples fields has been
* allocated the pointers will be retained for reuse. If a blockette
* chain is present all associated memory will be released.
*
* Returns a pointer to a MSRecord struct on success or NULL on error.
***************************************************************************/
MSRecord *
msr_init ( MSRecord *msr )
{
void *fsdh = 0;
void *datasamples = 0;
if ( ! msr )
{
msr = (MSRecord *) malloc (sizeof(MSRecord));
}
else
{
fsdh = msr->fsdh;
datasamples = msr->datasamples;
if ( msr->blkts )
msr_free_blktchain (msr);
if ( msr->ststate )
free (msr->ststate);
}
if ( msr == NULL )
{
ms_log (2, "msr_init(): Cannot allocate memory\n");
return NULL;
}
memset (msr, 0, sizeof (MSRecord));
msr->fsdh = fsdh;
msr->datasamples = datasamples;
msr->reclen = -1;
msr->samplecnt = -1;
msr->byteorder = -1;
msr->encoding = -1;
return msr;
} /* End of msr_init() */
/***************************************************************************
* msr_free:
*
* Free all memory associated with a MSRecord struct.
***************************************************************************/
void
msr_free ( MSRecord **ppmsr )
{
if ( ppmsr != NULL && *ppmsr != 0 )
{
/* Free fixed section header if populated */
if ( (*ppmsr)->fsdh )
free ((*ppmsr)->fsdh);
/* Free blockette chain if populated */
if ( (*ppmsr)->blkts )
msr_free_blktchain (*ppmsr);
/* Free datasamples if present */
if ( (*ppmsr)->datasamples )
free ((*ppmsr)->datasamples);
/* Free stream processing state if present */
if ( (*ppmsr)->ststate )
free ((*ppmsr)->ststate);
free (*ppmsr);
*ppmsr = NULL;
}
} /* End of msr_free() */
/***************************************************************************
* msr_free_blktchain:
*
* Free all memory associated with a blockette chain in a MSRecord
* struct and set MSRecord->blkts to NULL. Also reset the shortcut
* blockette pointers.
***************************************************************************/
void
msr_free_blktchain ( MSRecord *msr )
{
if ( msr )
{
if ( msr->blkts )
{
BlktLink *bc = msr->blkts;
BlktLink *nb = NULL;
while ( bc )
{
nb = bc->next;
if ( bc->blktdata )
free (bc->blktdata);
free (bc);
bc = nb;
}
msr->blkts = 0;
}
msr->Blkt100 = 0;
msr->Blkt1000 = 0;
msr->Blkt1001 = 0;
}
} /* End of msr_free_blktchain() */
/***************************************************************************
* msr_addblockette:
*
* Add a blockette to the blockette chain of an MSRecord. 'blktdata'
* should be the body of the blockette type 'blkttype' of 'length'
* bytes without the blockette header (type and next offsets). The
* 'chainpos' value controls which end of the chain the blockette is
* added to. If 'chainpos' is 0 the blockette will be added to the
* end of the chain (last blockette), other wise it will be added to
* the beginning of the chain (first blockette).
*
* Returns a pointer to the BlktLink added to the chain on success and
* NULL on error.
***************************************************************************/
BlktLink *
msr_addblockette (MSRecord *msr, char *blktdata, int length, int blkttype,
int chainpos)
{
BlktLink *blkt;
if ( ! msr )
return NULL;
blkt = msr->blkts;
if ( blkt )
{
if ( chainpos != 0 )
{
blkt = (BlktLink *) malloc (sizeof(BlktLink));
blkt->next = msr->blkts;
msr->blkts = blkt;
}
else
{
/* Find the last blockette */
while ( blkt && blkt->next )
{
blkt = blkt->next;
}
blkt->next = (BlktLink *) malloc (sizeof(BlktLink));
blkt = blkt->next;
blkt->next = 0;
}
if ( blkt == NULL )
{
ms_log (2, "msr_addblockette(): Cannot allocate memory\n");
return NULL;
}
}
else
{
msr->blkts = (BlktLink *) malloc (sizeof(BlktLink));
if ( msr->blkts == NULL )
{
ms_log (2, "msr_addblockette(): Cannot allocate memory\n");
return NULL;
}
blkt = msr->blkts;
blkt->next = 0;
}
blkt->blktoffset = 0;
blkt->blkt_type = blkttype;
blkt->next_blkt = 0;
blkt->blktdata = (char *) malloc (length);
if ( blkt->blktdata == NULL )
{
ms_log (2, "msr_addblockette(): Cannot allocate memory\n");
return NULL;
}
memcpy (blkt->blktdata, blktdata, length);
blkt->blktdatalen = length;
/* Setup the shortcut pointer for common blockettes */
switch ( blkttype )
{
case 100:
msr->Blkt100 = blkt->blktdata;
break;
case 1000:
msr->Blkt1000 = blkt->blktdata;
break;
case 1001:
msr->Blkt1001 = blkt->blktdata;
break;
}
return blkt;
} /* End of msr_addblockette() */
/***************************************************************************
* msr_normalize_header:
*
* Normalize header values between the MSRecord struct and the
* associated fixed-section of the header and blockettes. Essentially
* this updates the SEED structured data in the MSRecord.fsdh struct
* and MSRecord.blkts chain with values stored at the MSRecord level.
*
* Returns the header length in bytes on success or -1 on error.
***************************************************************************/
int
msr_normalize_header ( MSRecord *msr, flag verbose )
{
struct blkt_link_s *cur_blkt;
char seqnum[7];
int offset = 0;
int blktcnt = 0;
int reclenexp = 0;
int reclenfind;
if ( ! msr )
return -1;
/* Update values in fixed section of data header */
if ( msr->fsdh )
{
if ( verbose > 2 )
ms_log (1, "Normalizing fixed section of data header\n");
/* Roll-over sequence number if necessary */
if ( msr->sequence_number > 999999 )
msr->sequence_number = 1;
/* Update values in the MSRecord.fsdh struct */
snprintf (seqnum, 7, "%06d", msr->sequence_number);
memcpy (msr->fsdh->sequence_number, seqnum, 6);
msr->fsdh->dataquality = msr->dataquality;
msr->fsdh->reserved = ' ';
ms_strncpopen (msr->fsdh->network, msr->network, 2);
ms_strncpopen (msr->fsdh->station, msr->station, 5);
ms_strncpopen (msr->fsdh->location, msr->location, 2);
ms_strncpopen (msr->fsdh->channel, msr->channel, 3);
ms_hptime2btime (msr->starttime, &(msr->fsdh->start_time));
/* When the sampling rate is <= 32767 Hertz determine the factor
* and multipler through rational approximation. For higher rates
* set the factor and multiplier to 0. */
if ( msr->samprate <= 32767.0 )
{
ms_genfactmult (msr->samprate, &(msr->fsdh->samprate_fact), &(msr->fsdh->samprate_mult));
}
else
{
if ( verbose > 1 )
ms_log (1, "Sampling rate too high to approximate factor & multiplier: %g\n",
msr->samprate);
msr->fsdh->samprate_fact = 0;
msr->fsdh->samprate_mult = 0;
}
offset += 48;
if ( msr->blkts )
msr->fsdh->blockette_offset = offset;
else
msr->fsdh->blockette_offset = 0;
}
/* Traverse blockette chain and performs necessary updates*/
cur_blkt = msr->blkts;
if ( cur_blkt && verbose > 2 )
ms_log (1, "Normalizing blockette chain\n");
while ( cur_blkt )
{
offset += 4;
if ( cur_blkt->blkt_type == 100 && msr->Blkt100 )
{
msr->Blkt100->samprate = (float)msr->samprate;
offset += sizeof (struct blkt_100_s);
}
else if ( cur_blkt->blkt_type == 1000 && msr->Blkt1000 )
{
msr->Blkt1000->byteorder = msr->byteorder;
msr->Blkt1000->encoding = msr->encoding;
/* Calculate the record length as an exponent of 2 */
for (reclenfind=1, reclenexp=1; reclenfind <= MAXRECLEN; reclenexp++)
{
reclenfind *= 2;
if ( reclenfind == msr->reclen ) break;
}
if ( reclenfind != msr->reclen )
{
ms_log (2, "msr_normalize_header(): Record length %d is not a power of 2\n",
msr->reclen);
return -1;
}
msr->Blkt1000->reclen = reclenexp;
offset += sizeof (struct blkt_1000_s);
}
else if ( cur_blkt->blkt_type == 1001 )
{
hptime_t sec, usec;
/* Insert microseconds offset */
sec = msr->starttime / (HPTMODULUS / 10000);
usec = msr->starttime - (sec * (HPTMODULUS / 10000));
usec /= (HPTMODULUS / 1000000);
msr->Blkt1001->usec = (int8_t) usec;
offset += sizeof (struct blkt_1001_s);
}
blktcnt++;
cur_blkt = cur_blkt->next;
}
if ( msr->fsdh )
msr->fsdh->numblockettes = blktcnt;
return offset;
} /* End of msr_normalize_header() */
/***************************************************************************
* msr_duplicate:
*
* Duplicate an MSRecord struct
* including the fixed-section data
* header and blockette chain. If
* the datadup flag is true and the
* source MSRecord has associated
* data samples copy them as well.
*
* Returns a pointer to a new MSRecord on success and NULL on error.
***************************************************************************/
MSRecord *
msr_duplicate (MSRecord *msr, flag datadup)
{
MSRecord *dupmsr = 0;
int samplesize = 0;
if ( ! msr )
return NULL;
/* Allocate target MSRecord structure */
if ( (dupmsr = msr_init (NULL)) == NULL )
return NULL;
/* Copy MSRecord structure */
memcpy (dupmsr, msr, sizeof(MSRecord));
/* Copy fixed-section data header structure */
if ( msr->fsdh )
{
/* Allocate memory for new FSDH structure */
if ( (dupmsr->fsdh = (struct fsdh_s *) malloc (sizeof(struct fsdh_s))) == NULL )
{
ms_log (2, "msr_duplicate(): Error allocating memory\n");
free (dupmsr);
return NULL;
}
/* Copy the contents */
memcpy (dupmsr->fsdh, msr->fsdh, sizeof(struct fsdh_s));
}
/* Copy the blockette chain */
if ( msr->blkts )
{
BlktLink *blkt = msr->blkts;
BlktLink *next = NULL;
dupmsr->blkts = 0;
while ( blkt )
{
next = blkt->next;
/* Add blockette to chain of new MSRecord */
if ( msr_addblockette (dupmsr, blkt->blktdata, blkt->blktdatalen,
blkt->blkt_type, 0) == NULL )
{
ms_log (2, "msr_duplicate(): Error adding blockettes\n");
msr_free (&dupmsr);
return NULL;
}
blkt = next;
}
}
/* Copy data samples if requested and available */
if ( datadup && msr->datasamples )
{
/* Determine size of samples in bytes */
samplesize = ms_samplesize (msr->sampletype);
if ( samplesize == 0 )
{
ms_log (2, "msr_duplicate(): unrecognized sample type: '%c'\n",
msr->sampletype);
free (dupmsr);
return NULL;
}
/* Allocate memory for new data array */
if ( (dupmsr->datasamples = (void *) malloc ((size_t)(msr->numsamples * samplesize))) == NULL )
{
ms_log (2, "msr_duplicate(): Error allocating memory\n");
free (dupmsr);
return NULL;
}
/* Copy the data array */
memcpy (dupmsr->datasamples, msr->datasamples, ((size_t)(msr->numsamples * samplesize)));
}
/* Otherwise make sure the sample array and count are zero */
else
{
dupmsr->datasamples = 0;
dupmsr->numsamples = 0;
}
return dupmsr;
} /* End of msr_duplicate() */
/***************************************************************************
* msr_samprate:
*
* Calculate and return a double precision sample rate for the
* specified MSRecord. If a Blockette 100 was included and parsed,
* the "Actual sample rate" (field 3) will be returned, otherwise a
* nominal sample rate will be calculated from the sample rate factor
* and multiplier in the fixed section data header.
*
* Returns the positive sample rate on success and -1.0 on error.
***************************************************************************/
double
msr_samprate (MSRecord *msr)
{
if ( ! msr )
return -1.0;
if ( msr->Blkt100 )
return (double) msr->Blkt100->samprate;
else
return msr_nomsamprate (msr);
} /* End of msr_samprate() */
/***************************************************************************
* msr_nomsamprate:
*
* Calculate a double precision nominal sample rate from the sample
* rate factor and multiplier in the FSDH struct of the specified
* MSRecord.
*
* Returns the positive sample rate on success and -1.0 on error.
***************************************************************************/
double
msr_nomsamprate (MSRecord *msr)
{
if ( ! msr )
return -1.0;
return ms_nomsamprate (msr->fsdh->samprate_fact, msr->fsdh->samprate_mult);
} /* End of msr_nomsamprate() */
/***************************************************************************
* msr_starttime:
*
* Convert a btime struct of a FSDH struct of a MSRecord (the record
* start time) into a high precision epoch time and apply time
* corrections if any are specified in the header and bit 1 of the
* activity flags indicates that it has not already been applied. If
* a Blockette 1001 is included and has been parsed the microseconds
* of field 4 are also applied.
*
* Returns a high precision epoch time on success and HPTERROR on
* error.
***************************************************************************/
hptime_t
msr_starttime (MSRecord *msr)
{
hptime_t starttime = msr_starttime_uc (msr);
if ( ! msr || starttime == HPTERROR )
return HPTERROR;
/* Check if a correction is included and if it has been applied,
bit 1 of activity flags indicates if it has been appiled */
if ( msr->fsdh->time_correct != 0 &&
! (msr->fsdh->act_flags & 0x02) )
{
starttime += (hptime_t) msr->fsdh->time_correct * (HPTMODULUS / 10000);
}
/* Apply microsecond precision in a parsed Blockette 1001 */
if ( msr->Blkt1001 )
{
starttime += (hptime_t) msr->Blkt1001->usec * (HPTMODULUS / 1000000);
}
return starttime;
} /* End of msr_starttime() */
/***************************************************************************
* msr_starttime_uc:
*
* Convert a btime struct of a FSDH struct of a MSRecord (the record
* start time) into a high precision epoch time. This time has no
* correction(s) applied to it.
*
* Returns a high precision epoch time on success and HPTERROR on
* error.
***************************************************************************/
hptime_t
msr_starttime_uc (MSRecord *msr)
{
if ( ! msr )
return HPTERROR;
if ( ! msr->fsdh )
return HPTERROR;
return ms_btime2hptime (&msr->fsdh->start_time);
} /* End of msr_starttime_uc() */
/***************************************************************************
* msr_endtime:
*
* Calculate the time of the last sample in the record; this is the
* actual last sample time and *not* the time "covered" by the last
* sample.
*
* Returns the time of the last sample as a high precision epoch time
* on success and HPTERROR on error.
***************************************************************************/
hptime_t
msr_endtime (MSRecord *msr)
{
hptime_t span = 0;
if ( ! msr )
return HPTERROR;
if ( msr->samprate > 0.0 && msr->samplecnt > 0 )
span = (hptime_t)(((double) (msr->samplecnt - 1) / msr->samprate * HPTMODULUS) + 0.5);
/* If a positive leap second occurred during this record as denoted by
* bit 4 of the activity flags being set, reduce the end time to match
* the now shifted UTC time. */
if ( msr->fsdh )
if ( msr->fsdh->act_flags & 0x10 )
span -= HPTMODULUS;
return (msr->starttime + span);
} /* End of msr_endtime() */
/***************************************************************************
* msr_srcname:
*
* Generate a source name string for a specified MSRecord in the
* format: 'NET_STA_LOC_CHAN' or, if the quality flag is true:
* 'NET_STA_LOC_CHAN_QUAL'. The passed srcname must have enough room
* for the resulting string.
*
* Returns a pointer to the resulting string or NULL on error.
***************************************************************************/
char *
msr_srcname (MSRecord *msr, char *srcname, flag quality)
{
char *src = srcname;
char *cp = srcname;
if ( ! msr || ! srcname )
return NULL;
/* Build the source name string */
cp = msr->network;
while ( *cp ) { *src++ = *cp++; }
*src++ = '_';
cp = msr->station;
while ( *cp ) { *src++ = *cp++; }
*src++ = '_';
cp = msr->location;
while ( *cp ) { *src++ = *cp++; }
*src++ = '_';
cp = msr->channel;
while ( *cp ) { *src++ = *cp++; }
if ( quality )
{
*src++ = '_';
*src++ = msr->dataquality;
}
*src = '\0';
return srcname;
} /* End of msr_srcname() */
/***************************************************************************
* msr_print:
*
* Prints header values in an MSRecord struct, if 'details' is greater
* than 0 then detailed information about each blockette is printed.
* If 'details' is greater than 1 very detailed information is
* printed. If no FSDH (msr->fsdh) is present only a single line with
* basic information is printed.
***************************************************************************/
void
msr_print (MSRecord *msr, flag details)
{
double nomsamprate;
char srcname[50];
char time[25];
char b;
int idx;
if ( ! msr )
return;
/* Generate a source name string */
srcname[0] = '\0';
msr_srcname (msr, srcname, 0);
/* Generate a start time string */
ms_hptime2seedtimestr (msr->starttime, time, 1);
/* Report information in the fixed header */
if ( details > 0 && msr->fsdh )
{
nomsamprate = msr_nomsamprate (msr);
ms_log (0, "%s, %06d, %c\n", srcname, msr->sequence_number, msr->dataquality);
ms_log (0, " start time: %s\n", time);
ms_log (0, " number of samples: %d\n", msr->fsdh->numsamples);
ms_log (0, " sample rate factor: %d (%.10g samples per second)\n",
msr->fsdh->samprate_fact, nomsamprate);
ms_log (0, " sample rate multiplier: %d\n", msr->fsdh->samprate_mult);
if ( details > 1 )
{
/* Activity flags */
b = msr->fsdh->act_flags;
ms_log (0, " activity flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x01 ) ms_log (0, " [Bit 0] Calibration signals present\n");
if ( b & 0x02 ) ms_log (0, " [Bit 1] Time correction applied\n");
if ( b & 0x04 ) ms_log (0, " [Bit 2] Beginning of an event, station trigger\n");
if ( b & 0x08 ) ms_log (0, " [Bit 3] End of an event, station detrigger\n");
if ( b & 0x10 ) ms_log (0, " [Bit 4] A positive leap second happened in this record\n");
if ( b & 0x20 ) ms_log (0, " [Bit 5] A negative leap second happened in this record\n");
if ( b & 0x40 ) ms_log (0, " [Bit 6] Event in progress\n");
if ( b & 0x80 ) ms_log (0, " [Bit 7] Undefined bit set\n");
/* I/O and clock flags */
b = msr->fsdh->io_flags;
ms_log (0, " I/O and clock flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x01 ) ms_log (0, " [Bit 0] Station volume parity error possibly present\n");
if ( b & 0x02 ) ms_log (0, " [Bit 1] Long record read (possibly no problem)\n");
if ( b & 0x04 ) ms_log (0, " [Bit 2] Short record read (record padded)\n");
if ( b & 0x08 ) ms_log (0, " [Bit 3] Start of time series\n");
if ( b & 0x10 ) ms_log (0, " [Bit 4] End of time series\n");
if ( b & 0x20 ) ms_log (0, " [Bit 5] Clock locked\n");
if ( b & 0x40 ) ms_log (0, " [Bit 6] Undefined bit set\n");
if ( b & 0x80 ) ms_log (0, " [Bit 7] Undefined bit set\n");
/* Data quality flags */
b = msr->fsdh->dq_flags;
ms_log (0, " data quality flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x01 ) ms_log (0, " [Bit 0] Amplifier saturation detected\n");
if ( b & 0x02 ) ms_log (0, " [Bit 1] Digitizer clipping detected\n");
if ( b & 0x04 ) ms_log (0, " [Bit 2] Spikes detected\n");
if ( b & 0x08 ) ms_log (0, " [Bit 3] Glitches detected\n");
if ( b & 0x10 ) ms_log (0, " [Bit 4] Missing/padded data present\n");
if ( b & 0x20 ) ms_log (0, " [Bit 5] Telemetry synchronization error\n");
if ( b & 0x40 ) ms_log (0, " [Bit 6] A digital filter may be charging\n");
if ( b & 0x80 ) ms_log (0, " [Bit 7] Time tag is questionable\n");
}
ms_log (0, " number of blockettes: %d\n", msr->fsdh->numblockettes);
ms_log (0, " time correction: %ld\n", (long int) msr->fsdh->time_correct);
ms_log (0, " data offset: %d\n", msr->fsdh->data_offset);
ms_log (0, " first blockette offset: %d\n", msr->fsdh->blockette_offset);
}
else
{
ms_log (0, "%s, %06d, %c, %d, %lld samples, %-.10g Hz, %s\n",
srcname, msr->sequence_number, msr->dataquality,
msr->reclen, (long long int) msr->samplecnt, msr->samprate, time);
}
/* Report information in the blockette chain */
if ( details > 0 && msr->blkts )
{
BlktLink *cur_blkt = msr->blkts;
while ( cur_blkt )
{
if ( cur_blkt->blkt_type == 100 )
{
struct blkt_100_s *blkt_100 = (struct blkt_100_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " actual sample rate: %.10g\n", blkt_100->samprate);
if ( details > 1 )
{
b = blkt_100->flags;
ms_log (0, " undefined flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
ms_log (0, " reserved bytes (3): %u,%u,%u\n",
blkt_100->reserved[0], blkt_100->reserved[1], blkt_100->reserved[2]);
}
}
else if ( cur_blkt->blkt_type == 200 )
{
struct blkt_200_s *blkt_200 = (struct blkt_200_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " signal amplitude: %g\n", blkt_200->amplitude);
ms_log (0, " signal period: %g\n", blkt_200->period);
ms_log (0, " background estimate: %g\n", blkt_200->background_estimate);
if ( details > 1 )
{
b = blkt_200->flags;
ms_log (0, " event detection flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x01 ) ms_log (0, " [Bit 0] 1: Dilatation wave\n");
else ms_log (0, " [Bit 0] 0: Compression wave\n");
if ( b & 0x02 ) ms_log (0, " [Bit 1] 1: Units after deconvolution\n");
else ms_log (0, " [Bit 1] 0: Units are digital counts\n");
if ( b & 0x04 ) ms_log (0, " [Bit 2] Bit 0 is undetermined\n");
ms_log (0, " reserved byte: %u\n", blkt_200->reserved);
}
ms_btime2seedtimestr (&blkt_200->time, time);
ms_log (0, " signal onset time: %s\n", time);
ms_log (0, " detector name: %.24s\n", blkt_200->detector);
}
else if ( cur_blkt->blkt_type == 201 )
{
struct blkt_201_s *blkt_201 = (struct blkt_201_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " signal amplitude: %g\n", blkt_201->amplitude);
ms_log (0, " signal period: %g\n", blkt_201->period);
ms_log (0, " background estimate: %g\n", blkt_201->background_estimate);
b = blkt_201->flags;
ms_log (0, " event detection flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x01 ) ms_log (0, " [Bit 0] 1: Dilation wave\n");
else ms_log (0, " [Bit 0] 0: Compression wave\n");
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_201->reserved);
ms_btime2seedtimestr (&blkt_201->time, time);
ms_log (0, " signal onset time: %s\n", time);
ms_log (0, " SNR values: ");
for (idx=0; idx < 6; idx++) ms_log (0, "%u ", blkt_201->snr_values[idx]);
ms_log (0, "\n");
ms_log (0, " loopback value: %u\n", blkt_201->loopback);
ms_log (0, " pick algorithm: %u\n", blkt_201->pick_algorithm);
ms_log (0, " detector name: %.24s\n", blkt_201->detector);
}
else if ( cur_blkt->blkt_type == 300 )
{
struct blkt_300_s *blkt_300 = (struct blkt_300_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_btime2seedtimestr (&blkt_300->time, time);
ms_log (0, " calibration start time: %s\n", time);
ms_log (0, " number of calibrations: %u\n", blkt_300->numcalibrations);
b = blkt_300->flags;
ms_log (0, " calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x01 ) ms_log (0, " [Bit 0] First pulse is positive\n");
if ( b & 0x02 ) ms_log (0, " [Bit 1] Calibration's alternate sign\n");
if ( b & 0x04 ) ms_log (0, " [Bit 2] Calibration was automatic\n");
if ( b & 0x08 ) ms_log (0, " [Bit 3] Calibration continued from previous record(s)\n");
ms_log (0, " step duration: %u\n", blkt_300->step_duration);
ms_log (0, " interval duration: %u\n", blkt_300->interval_duration);
ms_log (0, " signal amplitude: %g\n", blkt_300->amplitude);
ms_log (0, " input signal channel: %.3s", blkt_300->input_channel);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_300->reserved);
ms_log (0, " reference amplitude: %u\n", blkt_300->reference_amplitude);
ms_log (0, " coupling: %.12s\n", blkt_300->coupling);
ms_log (0, " rolloff: %.12s\n", blkt_300->rolloff);
}
else if ( cur_blkt->blkt_type == 310 )
{
struct blkt_310_s *blkt_310 = (struct blkt_310_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_btime2seedtimestr (&blkt_310->time, time);
ms_log (0, " calibration start time: %s\n", time);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_310->reserved1);
b = blkt_310->flags;
ms_log (0, " calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x04 ) ms_log (0, " [Bit 2] Calibration was automatic\n");
if ( b & 0x08 ) ms_log (0, " [Bit 3] Calibration continued from previous record(s)\n");
if ( b & 0x10 ) ms_log (0, " [Bit 4] Peak-to-peak amplitude\n");
if ( b & 0x20 ) ms_log (0, " [Bit 5] Zero-to-peak amplitude\n");
if ( b & 0x40 ) ms_log (0, " [Bit 6] RMS amplitude\n");
ms_log (0, " calibration duration: %u\n", blkt_310->duration);
ms_log (0, " signal period: %g\n", blkt_310->period);
ms_log (0, " signal amplitude: %g\n", blkt_310->amplitude);
ms_log (0, " input signal channel: %.3s", blkt_310->input_channel);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_310->reserved2);
ms_log (0, " reference amplitude: %u\n", blkt_310->reference_amplitude);
ms_log (0, " coupling: %.12s\n", blkt_310->coupling);
ms_log (0, " rolloff: %.12s\n", blkt_310->rolloff);
}
else if ( cur_blkt->blkt_type == 320 )
{
struct blkt_320_s *blkt_320 = (struct blkt_320_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_btime2seedtimestr (&blkt_320->time, time);
ms_log (0, " calibration start time: %s\n", time);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_320->reserved1);
b = blkt_320->flags;
ms_log (0, " calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x04 ) ms_log (0, " [Bit 2] Calibration was automatic\n");
if ( b & 0x08 ) ms_log (0, " [Bit 3] Calibration continued from previous record(s)\n");
if ( b & 0x10 ) ms_log (0, " [Bit 4] Random amplitudes\n");
ms_log (0, " calibration duration: %u\n", blkt_320->duration);
ms_log (0, " peak-to-peak amplitude: %g\n", blkt_320->ptp_amplitude);
ms_log (0, " input signal channel: %.3s", blkt_320->input_channel);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_320->reserved2);
ms_log (0, " reference amplitude: %u\n", blkt_320->reference_amplitude);
ms_log (0, " coupling: %.12s\n", blkt_320->coupling);
ms_log (0, " rolloff: %.12s\n", blkt_320->rolloff);
ms_log (0, " noise type: %.8s\n", blkt_320->noise_type);
}
else if ( cur_blkt->blkt_type == 390 )
{
struct blkt_390_s *blkt_390 = (struct blkt_390_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_btime2seedtimestr (&blkt_390->time, time);
ms_log (0, " calibration start time: %s\n", time);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_390->reserved1);
b = blkt_390->flags;
ms_log (0, " calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( b & 0x04 ) ms_log (0, " [Bit 2] Calibration was automatic\n");
if ( b & 0x08 ) ms_log (0, " [Bit 3] Calibration continued from previous record(s)\n");
ms_log (0, " calibration duration: %u\n", blkt_390->duration);
ms_log (0, " signal amplitude: %g\n", blkt_390->amplitude);
ms_log (0, " input signal channel: %.3s", blkt_390->input_channel);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_390->reserved2);
}
else if ( cur_blkt->blkt_type == 395 )
{
struct blkt_395_s *blkt_395 = (struct blkt_395_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_btime2seedtimestr (&blkt_395->time, time);
ms_log (0, " calibration end time: %s\n", time);
if ( details > 1 )
ms_log (0, " reserved bytes (2): %u,%u\n",
blkt_395->reserved[0], blkt_395->reserved[1]);
}
else if ( cur_blkt->blkt_type == 400 )
{
struct blkt_400_s *blkt_400 = (struct blkt_400_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " beam azimuth (degrees): %g\n", blkt_400->azimuth);
ms_log (0, " beam slowness (sec/degree): %g\n", blkt_400->slowness);
ms_log (0, " configuration: %u\n", blkt_400->configuration);
if ( details > 1 )
ms_log (0, " reserved bytes (2): %u,%u\n",
blkt_400->reserved[0], blkt_400->reserved[1]);
}
else if ( cur_blkt->blkt_type == 405 )
{
struct blkt_405_s *blkt_405 = (struct blkt_405_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s, incomplete)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " first delay value: %u\n", blkt_405->delay_values[0]);
}
else if ( cur_blkt->blkt_type == 500 )
{
struct blkt_500_s *blkt_500 = (struct blkt_500_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " VCO correction: %g%%\n", blkt_500->vco_correction);
ms_btime2seedtimestr (&blkt_500->time, time);
ms_log (0, " time of exception: %s\n", time);
ms_log (0, " usec: %d\n", blkt_500->usec);
ms_log (0, " reception quality: %u%%\n", blkt_500->reception_qual);
ms_log (0, " exception count: %u\n", blkt_500->exception_count);
ms_log (0, " exception type: %.16s\n", blkt_500->exception_type);
ms_log (0, " clock model: %.32s\n", blkt_500->clock_model);
ms_log (0, " clock status: %.128s\n", blkt_500->clock_status);
}
else if ( cur_blkt->blkt_type == 1000 )
{
struct blkt_1000_s *blkt_1000 = (struct blkt_1000_s *) cur_blkt->blktdata;
int recsize;
char order[40];
/* Calculate record size in bytes as 2^(blkt_1000->rec_len) */
recsize = (unsigned int) 1 << blkt_1000->reclen;
/* Big or little endian? */
if (blkt_1000->byteorder == 0)
strncpy (order, "Little endian", sizeof(order)-1);
else if (blkt_1000->byteorder == 1)
strncpy (order, "Big endian", sizeof(order)-1);
else
strncpy (order, "Unknown value", sizeof(order)-1);
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " encoding: %s (val:%u)\n",
(char *) ms_encodingstr (blkt_1000->encoding), blkt_1000->encoding);
ms_log (0, " byte order: %s (val:%u)\n",
order, blkt_1000->byteorder);
ms_log (0, " record length: %d (val:%u)\n",
recsize, blkt_1000->reclen);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_1000->reserved);
}
else if ( cur_blkt->blkt_type == 1001 )
{
struct blkt_1001_s *blkt_1001 = (struct blkt_1001_s *) cur_blkt->blktdata;
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " timing quality: %u%%\n", blkt_1001->timing_qual);
ms_log (0, " micro second: %d\n", blkt_1001->usec);
if ( details > 1 )
ms_log (0, " reserved byte: %u\n", blkt_1001->reserved);
ms_log (0, " frame count: %u\n", blkt_1001->framecnt);
}
else if ( cur_blkt->blkt_type == 2000 )
{
struct blkt_2000_s *blkt_2000 = (struct blkt_2000_s *) cur_blkt->blktdata;
char order[40];
/* Big or little endian? */
if (blkt_2000->byteorder == 0)
strncpy (order, "Little endian", sizeof(order)-1);
else if (blkt_2000->byteorder == 1)
strncpy (order, "Big endian", sizeof(order)-1);
else
strncpy (order, "Unknown value", sizeof(order)-1);
ms_log (0, " BLOCKETTE %u: (%s)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
ms_log (0, " blockette length: %u\n", blkt_2000->length);
ms_log (0, " data offset: %u\n", blkt_2000->data_offset);
ms_log (0, " record number: %u\n", blkt_2000->recnum);
ms_log (0, " byte order: %s (val:%u)\n",
order, blkt_2000->byteorder);
b = blkt_2000->flags;
ms_log (0, " data flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
bit(b,0x01), bit(b,0x02), bit(b,0x04), bit(b,0x08),
bit(b,0x10), bit(b,0x20), bit(b,0x40), bit(b,0x80));
if ( details > 1 )
{
if ( b & 0x01 ) ms_log (0, " [Bit 0] 1: Stream oriented\n");
else ms_log (0, " [Bit 0] 0: Record oriented\n");
if ( b & 0x02 ) ms_log (0, " [Bit 1] 1: Blockette 2000s may NOT be packaged\n");
else ms_log (0, " [Bit 1] 0: Blockette 2000s may be packaged\n");
if ( ! (b & 0x04) && ! (b & 0x08) )
ms_log (0, " [Bits 2-3] 00: Complete blockette\n");
else if ( ! (b & 0x04) && (b & 0x08) )
ms_log (0, " [Bits 2-3] 01: First blockette in span\n");
else if ( (b & 0x04) && (b & 0x08) )
ms_log (0, " [Bits 2-3] 11: Continuation blockette in span\n");
else if ( (b & 0x04) && ! (b & 0x08) )
ms_log (0, " [Bits 2-3] 10: Final blockette in span\n");
if ( ! (b & 0x10) && ! (b & 0x20) )
ms_log (0, " [Bits 4-5] 00: Not file oriented\n");
else if ( ! (b & 0x10) && (b & 0x20) )
ms_log (0, " [Bits 4-5] 01: First blockette of file\n");
else if ( (b & 0x10) && ! (b & 0x20) )
ms_log (0, " [Bits 4-5] 10: Continuation of file\n");
else if ( (b & 0x10) && (b & 0x20) )
ms_log (0, " [Bits 4-5] 11: Last blockette of file\n");
}
ms_log (0, " number of headers: %u\n", blkt_2000->numheaders);
/* Crude display of the opaque data headers */
if ( details > 1 )
ms_log (0, " headers: %.*s\n",
(blkt_2000->data_offset - 15), blkt_2000->payload);
}
else
{
ms_log (0, " BLOCKETTE %u: (%s, not parsed)\n", cur_blkt->blkt_type,
ms_blktdesc(cur_blkt->blkt_type));
ms_log (0, " next blockette: %u\n", cur_blkt->next_blkt);
}
cur_blkt = cur_blkt->next;
}
}
} /* End of msr_print() */
/***************************************************************************
* msr_host_latency:
*
* Calculate the latency based on the host time in UTC accounting for
* the time covered using the number of samples and sample rate; in
* other words, the difference between the host time and the time of
* the last sample in the specified Mini-SEED record.
*
* Double precision is returned, but the true precision is dependent
* on the accuracy of the host system clock among other things.
*
* Returns seconds of latency or 0.0 on error (indistinguishable from
* 0.0 latency).
***************************************************************************/
double
msr_host_latency (MSRecord *msr)
{
double span = 0.0; /* Time covered by the samples */
double epoch; /* Current epoch time */
double latency = 0.0;
time_t tv;
if ( msr == NULL )
return 0.0;
/* Calculate the time covered by the samples */
if ( msr->samprate > 0.0 && msr->samplecnt > 0 )
span = (1.0 / msr->samprate) * (msr->samplecnt - 1);
/* Grab UTC time according to the system clock */
epoch = (double) time(&tv);
/* Now calculate the latency */
latency = epoch - ((double) msr->starttime / HPTMODULUS) - span;
return latency;
} /* End of msr_host_latency() */