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.
1084 lines
31 KiB
C
1084 lines
31 KiB
C
/***************************************************************************
|
|
* unpack.c:
|
|
*
|
|
* Generic routines to unpack Mini-SEED records.
|
|
*
|
|
* Appropriate values from the record header will be byte-swapped to
|
|
* the host order. The purpose of this code is to provide a portable
|
|
* way of accessing common SEED data record header information. All
|
|
* data structures in SEED 2.4 data records are supported. The data
|
|
* samples are optionally decompressed/unpacked.
|
|
*
|
|
* Written by Chad Trabant,
|
|
* ORFEUS/EC-Project MEREDIAN
|
|
* IRIS Data Management Center
|
|
*
|
|
* modified: 2014.197
|
|
***************************************************************************/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
|
|
#include "libmseed.h"
|
|
#include "unpackdata.h"
|
|
|
|
/* Function(s) internal to this file */
|
|
static int check_environment (int verbose);
|
|
|
|
/* Header and data byte order flags controlled by environment variables */
|
|
/* -2 = not checked, -1 = checked but not set, or 0 = LE and 1 = BE */
|
|
flag unpackheaderbyteorder = -2;
|
|
flag unpackdatabyteorder = -2;
|
|
|
|
/* Data encoding format/fallback controlled by environment variable */
|
|
/* -2 = not checked, -1 = checked but not set, or = encoding */
|
|
int unpackencodingformat = -2;
|
|
int unpackencodingfallback = -2;
|
|
|
|
/* A pointer to the srcname of the record being unpacked */
|
|
char *UNPACK_SRCNAME = NULL;
|
|
|
|
|
|
/***************************************************************************
|
|
* msr_unpack:
|
|
*
|
|
* Unpack a SEED data record header/blockettes and populate a MSRecord
|
|
* struct. All approriate fields are byteswapped, if needed, and
|
|
* pointers to structured data are setup in addition to setting the
|
|
* common header fields.
|
|
*
|
|
* If 'dataflag' is true the data samples are unpacked/decompressed
|
|
* and the MSRecord->datasamples pointer is set appropriately. The
|
|
* data samples will be either 32-bit integers, 32-bit floats or
|
|
* 64-bit floats (doubles) with the same byte order as the host
|
|
* machine. The MSRecord->numsamples will be set to the actual number
|
|
* of samples unpacked/decompressed and MSRecord->sampletype will
|
|
* indicated the sample type.
|
|
*
|
|
* All appropriate values will be byte-swapped to the host order,
|
|
* including the data samples.
|
|
*
|
|
* All header values, blockette values and data samples will be
|
|
* overwritten by subsequent calls to this function.
|
|
*
|
|
* If the msr struct is NULL it will be allocated.
|
|
*
|
|
* Returns MS_NOERROR and populates the MSRecord struct at *ppmsr on
|
|
* success, otherwise returns a libmseed error code (listed in
|
|
* libmseed.h).
|
|
***************************************************************************/
|
|
int
|
|
msr_unpack ( char *record, int reclen, MSRecord **ppmsr,
|
|
flag dataflag, flag verbose )
|
|
{
|
|
flag headerswapflag = 0;
|
|
flag dataswapflag = 0;
|
|
int retval;
|
|
|
|
MSRecord *msr = NULL;
|
|
char sequence_number[7];
|
|
char srcname[50];
|
|
|
|
/* For blockette parsing */
|
|
BlktLink *blkt_link = 0;
|
|
uint16_t blkt_type;
|
|
uint16_t next_blkt;
|
|
uint32_t blkt_offset;
|
|
uint32_t blkt_length;
|
|
int blkt_count = 0;
|
|
|
|
if ( ! ppmsr )
|
|
{
|
|
ms_log (2, "msr_unpack(): ppmsr argument cannot be NULL\n");
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
/* Verify that record includes a valid header */
|
|
if ( ! MS_ISVALIDHEADER(record) )
|
|
{
|
|
ms_recsrcname (record, srcname, 1);
|
|
ms_log (2, "msr_unpack(%s) Record header & quality indicator unrecognized: '%c'\n", srcname);
|
|
ms_log (2, "msr_unpack(%s) This is not a valid Mini-SEED record\n", srcname);
|
|
|
|
return MS_NOTSEED;
|
|
}
|
|
|
|
/* Verify that passed record length is within supported range */
|
|
if ( reclen < MINRECLEN || reclen > MAXRECLEN )
|
|
{
|
|
ms_recsrcname (record, srcname, 1);
|
|
ms_log (2, "msr_unpack(%s): Record length is out of range: %d\n", srcname, reclen);
|
|
return MS_OUTOFRANGE;
|
|
}
|
|
|
|
/* Initialize the MSRecord */
|
|
if ( ! (*ppmsr = msr_init (*ppmsr)) )
|
|
return MS_GENERROR;
|
|
|
|
/* Shortcut pointer, historical and help readability */
|
|
msr = *ppmsr;
|
|
|
|
/* Set raw record pointer and record length */
|
|
msr->record = record;
|
|
msr->reclen = reclen;
|
|
|
|
/* Check environment variables if necessary */
|
|
if ( unpackheaderbyteorder == -2 ||
|
|
unpackdatabyteorder == -2 ||
|
|
unpackencodingformat == -2 ||
|
|
unpackencodingfallback == -2 )
|
|
if ( check_environment(verbose) )
|
|
return MS_GENERROR;
|
|
|
|
/* Allocate and copy fixed section of data header */
|
|
msr->fsdh = realloc (msr->fsdh, sizeof (struct fsdh_s));
|
|
|
|
if ( msr->fsdh == NULL )
|
|
{
|
|
ms_log (2, "msr_unpack(): Cannot allocate memory\n");
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
memcpy (msr->fsdh, record, sizeof (struct fsdh_s));
|
|
|
|
/* Check to see if byte swapping is needed by testing the year and day */
|
|
if ( ! MS_ISVALIDYEARDAY (msr->fsdh->start_time.year, msr->fsdh->start_time.day) )
|
|
headerswapflag = dataswapflag = 1;
|
|
|
|
/* Check if byte order is forced */
|
|
if ( unpackheaderbyteorder >= 0 )
|
|
{
|
|
headerswapflag = ( ms_bigendianhost() != unpackheaderbyteorder ) ? 1 : 0;
|
|
}
|
|
|
|
if ( unpackdatabyteorder >= 0 )
|
|
{
|
|
dataswapflag = ( ms_bigendianhost() != unpackdatabyteorder ) ? 1 : 0;
|
|
}
|
|
|
|
/* Swap byte order? */
|
|
if ( headerswapflag )
|
|
{
|
|
MS_SWAPBTIME (&msr->fsdh->start_time);
|
|
ms_gswap2a (&msr->fsdh->numsamples);
|
|
ms_gswap2a (&msr->fsdh->samprate_fact);
|
|
ms_gswap2a (&msr->fsdh->samprate_mult);
|
|
ms_gswap4a (&msr->fsdh->time_correct);
|
|
ms_gswap2a (&msr->fsdh->data_offset);
|
|
ms_gswap2a (&msr->fsdh->blockette_offset);
|
|
}
|
|
|
|
/* Populate some of the common header fields */
|
|
strncpy (sequence_number, msr->fsdh->sequence_number, 6);
|
|
sequence_number[6] = '\0';
|
|
msr->sequence_number = (int32_t) strtol (sequence_number, NULL, 10);
|
|
msr->dataquality = msr->fsdh->dataquality;
|
|
ms_strncpcleantail (msr->network, msr->fsdh->network, 2);
|
|
ms_strncpcleantail (msr->station, msr->fsdh->station, 5);
|
|
ms_strncpcleantail (msr->location, msr->fsdh->location, 2);
|
|
ms_strncpcleantail (msr->channel, msr->fsdh->channel, 3);
|
|
msr->samplecnt = msr->fsdh->numsamples;
|
|
|
|
/* Generate source name for MSRecord */
|
|
if ( msr_srcname (msr, srcname, 1) == NULL )
|
|
{
|
|
ms_log (2, "msr_unpack(): Cannot generate srcname\n");
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
/* Set shared srcname pointer to source name */
|
|
UNPACK_SRCNAME = &srcname[0];
|
|
|
|
/* Report byte swapping status */
|
|
if ( verbose > 2 )
|
|
{
|
|
if ( headerswapflag )
|
|
ms_log (1, "%s: Byte swapping needed for unpacking of header\n",
|
|
UNPACK_SRCNAME);
|
|
else
|
|
ms_log (1, "%s: Byte swapping NOT needed for unpacking of header\n",
|
|
UNPACK_SRCNAME);
|
|
}
|
|
|
|
/* Traverse the blockettes */
|
|
blkt_offset = msr->fsdh->blockette_offset;
|
|
|
|
while ((blkt_offset != 0) &&
|
|
((int)blkt_offset < reclen) &&
|
|
(blkt_offset < MAXRECLEN))
|
|
{
|
|
/* Every blockette has a similar 4 byte header: type and next */
|
|
memcpy (&blkt_type, record + blkt_offset, 2);
|
|
blkt_offset += 2;
|
|
memcpy (&next_blkt, record + blkt_offset, 2);
|
|
blkt_offset += 2;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
ms_gswap2 (&blkt_type);
|
|
ms_gswap2 (&next_blkt);
|
|
}
|
|
|
|
/* Get blockette length */
|
|
blkt_length = ms_blktlen (blkt_type,
|
|
record + blkt_offset - 4,
|
|
headerswapflag);
|
|
|
|
if ( blkt_length == 0 )
|
|
{
|
|
ms_log (2, "msr_unpack(%s): Unknown blockette length for type %d\n",
|
|
UNPACK_SRCNAME, blkt_type);
|
|
break;
|
|
}
|
|
|
|
/* Make sure blockette is contained within the msrecord buffer */
|
|
if ( (int)(blkt_offset - 4 + blkt_length) > reclen )
|
|
{
|
|
ms_log (2, "msr_unpack(%s): Blockette %d extends beyond record size, truncated?\n",
|
|
UNPACK_SRCNAME, blkt_type);
|
|
break;
|
|
}
|
|
|
|
if ( blkt_type == 100 )
|
|
{ /* Found a Blockette 100 */
|
|
struct blkt_100_s *blkt_100;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_100_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_100 = (struct blkt_100_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
ms_gswap4 (&blkt_100->samprate);
|
|
}
|
|
|
|
msr->samprate = msr->Blkt100->samprate;
|
|
}
|
|
|
|
else if ( blkt_type == 200 )
|
|
{ /* Found a Blockette 200 */
|
|
struct blkt_200_s *blkt_200;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_200_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_200 = (struct blkt_200_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
ms_gswap4 (&blkt_200->amplitude);
|
|
ms_gswap4 (&blkt_200->period);
|
|
ms_gswap4 (&blkt_200->background_estimate);
|
|
MS_SWAPBTIME (&blkt_200->time);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 201 )
|
|
{ /* Found a Blockette 201 */
|
|
struct blkt_201_s *blkt_201;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_201_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_201 = (struct blkt_201_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
ms_gswap4 (&blkt_201->amplitude);
|
|
ms_gswap4 (&blkt_201->period);
|
|
ms_gswap4 (&blkt_201->background_estimate);
|
|
MS_SWAPBTIME (&blkt_201->time);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 300 )
|
|
{ /* Found a Blockette 300 */
|
|
struct blkt_300_s *blkt_300;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_300_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_300 = (struct blkt_300_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
MS_SWAPBTIME (&blkt_300->time);
|
|
ms_gswap4 (&blkt_300->step_duration);
|
|
ms_gswap4 (&blkt_300->interval_duration);
|
|
ms_gswap4 (&blkt_300->amplitude);
|
|
ms_gswap4 (&blkt_300->reference_amplitude);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 310 )
|
|
{ /* Found a Blockette 310 */
|
|
struct blkt_310_s *blkt_310;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_310_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_310 = (struct blkt_310_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
MS_SWAPBTIME (&blkt_310->time);
|
|
ms_gswap4 (&blkt_310->duration);
|
|
ms_gswap4 (&blkt_310->period);
|
|
ms_gswap4 (&blkt_310->amplitude);
|
|
ms_gswap4 (&blkt_310->reference_amplitude);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 320 )
|
|
{ /* Found a Blockette 320 */
|
|
struct blkt_320_s *blkt_320;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_320_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_320 = (struct blkt_320_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
MS_SWAPBTIME (&blkt_320->time);
|
|
ms_gswap4 (&blkt_320->duration);
|
|
ms_gswap4 (&blkt_320->ptp_amplitude);
|
|
ms_gswap4 (&blkt_320->reference_amplitude);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 390 )
|
|
{ /* Found a Blockette 390 */
|
|
struct blkt_390_s *blkt_390;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_390_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_390 = (struct blkt_390_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
MS_SWAPBTIME (&blkt_390->time);
|
|
ms_gswap4 (&blkt_390->duration);
|
|
ms_gswap4 (&blkt_390->amplitude);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 395 )
|
|
{ /* Found a Blockette 395 */
|
|
struct blkt_395_s *blkt_395;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_395_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_395 = (struct blkt_395_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
MS_SWAPBTIME (&blkt_395->time);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 400 )
|
|
{ /* Found a Blockette 400 */
|
|
struct blkt_400_s *blkt_400;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_400_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_400 = (struct blkt_400_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
ms_gswap4 (&blkt_400->azimuth);
|
|
ms_gswap4 (&blkt_400->slowness);
|
|
ms_gswap2 (&blkt_400->configuration);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 405 )
|
|
{ /* Found a Blockette 405 */
|
|
struct blkt_405_s *blkt_405;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_405_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_405 = (struct blkt_405_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
ms_gswap2 (&blkt_405->delay_values);
|
|
}
|
|
|
|
if ( verbose > 0 )
|
|
{
|
|
ms_log (1, "msr_unpack(%s): WARNING Blockette 405 cannot be fully supported\n",
|
|
UNPACK_SRCNAME);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 500 )
|
|
{ /* Found a Blockette 500 */
|
|
struct blkt_500_s *blkt_500;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_500_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_500 = (struct blkt_500_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
ms_gswap4 (&blkt_500->vco_correction);
|
|
MS_SWAPBTIME (&blkt_500->time);
|
|
ms_gswap4 (&blkt_500->exception_count);
|
|
}
|
|
}
|
|
|
|
else if ( blkt_type == 1000 )
|
|
{ /* Found a Blockette 1000 */
|
|
struct blkt_1000_s *blkt_1000;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_1000_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_1000 = (struct blkt_1000_s *) blkt_link->blktdata;
|
|
|
|
/* Calculate record length in bytes as 2^(blkt_1000->reclen) */
|
|
msr->reclen = (uint32_t) 1 << blkt_1000->reclen;
|
|
|
|
/* Compare against the specified length */
|
|
if ( msr->reclen != reclen && verbose )
|
|
{
|
|
ms_log (2, "msr_unpack(%s): Record length in Blockette 1000 (%d) != specified length (%d)\n",
|
|
UNPACK_SRCNAME, msr->reclen, reclen);
|
|
}
|
|
|
|
msr->encoding = blkt_1000->encoding;
|
|
msr->byteorder = blkt_1000->byteorder;
|
|
}
|
|
|
|
else if ( blkt_type == 1001 )
|
|
{ /* Found a Blockette 1001 */
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
sizeof (struct blkt_1001_s),
|
|
blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
}
|
|
|
|
else if ( blkt_type == 2000 )
|
|
{ /* Found a Blockette 2000 */
|
|
struct blkt_2000_s *blkt_2000;
|
|
uint16_t b2klen;
|
|
|
|
/* Read the blockette length from blockette */
|
|
memcpy (&b2klen, record + blkt_offset, 2);
|
|
if ( headerswapflag ) ms_gswap2 (&b2klen);
|
|
|
|
/* Minus four bytes for the blockette type and next fields */
|
|
b2klen -= 4;
|
|
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
b2klen, blkt_type, 0);
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
|
|
blkt_2000 = (struct blkt_2000_s *) blkt_link->blktdata;
|
|
|
|
if ( headerswapflag )
|
|
{
|
|
ms_gswap2 (&blkt_2000->length);
|
|
ms_gswap2 (&blkt_2000->data_offset);
|
|
ms_gswap4 (&blkt_2000->recnum);
|
|
}
|
|
}
|
|
|
|
else
|
|
{ /* Unknown blockette type */
|
|
if ( blkt_length >= 4 )
|
|
{
|
|
blkt_link = msr_addblockette (msr, record + blkt_offset,
|
|
blkt_length - 4,
|
|
blkt_type, 0);
|
|
|
|
if ( ! blkt_link )
|
|
break;
|
|
|
|
blkt_link->blktoffset = blkt_offset - 4;
|
|
blkt_link->next_blkt = next_blkt;
|
|
}
|
|
}
|
|
|
|
/* Check that the next blockette offset is beyond the current blockette */
|
|
if ( next_blkt && next_blkt < (blkt_offset + blkt_length - 4) )
|
|
{
|
|
ms_log (2, "msr_unpack(%s): Offset to next blockette (%d) is within current blockette ending at byte %d\n",
|
|
UNPACK_SRCNAME, next_blkt, (blkt_offset + blkt_length - 4));
|
|
|
|
blkt_offset = 0;
|
|
}
|
|
/* Check that the offset is within record length */
|
|
else if ( next_blkt && next_blkt > reclen )
|
|
{
|
|
ms_log (2, "msr_unpack(%s): Offset to next blockette (%d) from type %d is beyond record length\n",
|
|
UNPACK_SRCNAME, next_blkt, blkt_type);
|
|
|
|
blkt_offset = 0;
|
|
}
|
|
else
|
|
{
|
|
blkt_offset = next_blkt;
|
|
}
|
|
|
|
blkt_count++;
|
|
} /* End of while looping through blockettes */
|
|
|
|
/* Check for a Blockette 1000 */
|
|
if ( msr->Blkt1000 == 0 )
|
|
{
|
|
if ( verbose > 1 )
|
|
{
|
|
ms_log (1, "%s: Warning: No Blockette 1000 found\n", UNPACK_SRCNAME);
|
|
}
|
|
}
|
|
|
|
/* Check that the data offset is after the blockette chain */
|
|
if ( blkt_link && msr->fsdh->numsamples && msr->fsdh->data_offset < (blkt_link->blktoffset + blkt_link->blktdatalen + 4) )
|
|
{
|
|
ms_log (1, "%s: Warning: Data offset in fixed header (%d) is within the blockette chain ending at %d\n",
|
|
UNPACK_SRCNAME, msr->fsdh->data_offset, (blkt_link->blktoffset + blkt_link->blktdatalen + 4));
|
|
}
|
|
|
|
/* Check that the blockette count matches the number parsed */
|
|
if ( msr->fsdh->numblockettes != blkt_count )
|
|
{
|
|
ms_log (1, "%s: Warning: Number of blockettes in fixed header (%d) does not match the number parsed (%d)\n",
|
|
UNPACK_SRCNAME, msr->fsdh->numblockettes, blkt_count);
|
|
}
|
|
|
|
/* Populate remaining common header fields */
|
|
msr->starttime = msr_starttime (msr);
|
|
msr->samprate = msr_samprate (msr);
|
|
|
|
/* Set MSRecord->byteorder if data byte order is forced */
|
|
if ( unpackdatabyteorder >= 0 )
|
|
{
|
|
msr->byteorder = unpackdatabyteorder;
|
|
}
|
|
|
|
/* Check if encoding format is forced */
|
|
if ( unpackencodingformat >= 0 )
|
|
{
|
|
msr->encoding = unpackencodingformat;
|
|
}
|
|
|
|
/* Use encoding format fallback if defined and no encoding is set,
|
|
* also make sure the byteorder is set by default to big endian */
|
|
if ( unpackencodingfallback >= 0 && msr->encoding == -1 )
|
|
{
|
|
msr->encoding = unpackencodingfallback;
|
|
|
|
if ( msr->byteorder == -1 )
|
|
{
|
|
msr->byteorder = 1;
|
|
}
|
|
}
|
|
|
|
/* Unpack the data samples if requested */
|
|
if ( dataflag && msr->samplecnt > 0 )
|
|
{
|
|
flag dswapflag = headerswapflag;
|
|
flag bigendianhost = ms_bigendianhost();
|
|
|
|
/* Determine byte order of the data and set the dswapflag as
|
|
needed; if no Blkt1000 or UNPACK_DATA_BYTEORDER environment
|
|
variable setting assume the order is the same as the header */
|
|
if ( msr->Blkt1000 != 0 && unpackdatabyteorder < 0 )
|
|
{
|
|
dswapflag = 0;
|
|
|
|
/* If BE host and LE data need swapping */
|
|
if ( bigendianhost && msr->byteorder == 0 )
|
|
dswapflag = 1;
|
|
/* If LE host and BE data (or bad byte order value) need swapping */
|
|
else if ( !bigendianhost && msr->byteorder > 0 )
|
|
dswapflag = 1;
|
|
}
|
|
else if ( unpackdatabyteorder >= 0 )
|
|
{
|
|
dswapflag = dataswapflag;
|
|
}
|
|
|
|
if ( verbose > 2 && dswapflag )
|
|
ms_log (1, "%s: Byte swapping needed for unpacking of data samples\n",
|
|
UNPACK_SRCNAME);
|
|
else if ( verbose > 2 )
|
|
ms_log (1, "%s: Byte swapping NOT needed for unpacking of data samples \n",
|
|
UNPACK_SRCNAME);
|
|
|
|
retval = msr_unpack_data (msr, dswapflag, verbose);
|
|
|
|
if ( retval < 0 )
|
|
return retval;
|
|
else
|
|
msr->numsamples = retval;
|
|
}
|
|
else
|
|
{
|
|
if ( msr->datasamples )
|
|
free (msr->datasamples);
|
|
|
|
msr->datasamples = 0;
|
|
msr->numsamples = 0;
|
|
}
|
|
|
|
/* Unset shared pointer to source name */
|
|
UNPACK_SRCNAME = NULL;
|
|
|
|
return MS_NOERROR;
|
|
} /* End of msr_unpack() */
|
|
|
|
|
|
/************************************************************************
|
|
* msr_unpack_data:
|
|
*
|
|
* Unpack Mini-SEED data samples for a given MSRecord. The packed
|
|
* data is accessed in the record indicated by MSRecord->record and
|
|
* the unpacked samples are placed in MSRecord->datasamples. The
|
|
* resulting data samples are either 32-bit integers, 32-bit floats
|
|
* or 64-bit floats in host byte order.
|
|
*
|
|
* Return number of samples unpacked or negative libmseed error code.
|
|
************************************************************************/
|
|
int
|
|
msr_unpack_data ( MSRecord *msr, int swapflag, flag verbose )
|
|
{
|
|
int datasize; /* byte size of data samples in record */
|
|
int nsamples; /* number of samples unpacked */
|
|
int unpacksize; /* byte size of unpacked samples */
|
|
int samplesize = 0; /* size of the data samples in bytes */
|
|
const char *dbuf;
|
|
int32_t *diffbuff;
|
|
int32_t x0, xn;
|
|
|
|
/* Sanity record length */
|
|
if ( msr->reclen == -1 )
|
|
{
|
|
ms_log (2, "msr_unpack_data(%s): Record size unknown\n",
|
|
UNPACK_SRCNAME);
|
|
return MS_NOTSEED;
|
|
}
|
|
else if ( msr->reclen < MINRECLEN || msr->reclen > MAXRECLEN )
|
|
{
|
|
ms_log (2, "msr_unpack_data(%s): Unsupported record length: %d\n",
|
|
UNPACK_SRCNAME, msr->reclen);
|
|
return MS_OUTOFRANGE;
|
|
}
|
|
|
|
/* Sanity check data offset before creating a pointer based on the value */
|
|
if ( msr->fsdh->data_offset < 48 || msr->fsdh->data_offset >= msr->reclen )
|
|
{
|
|
ms_log (2, "msr_unpack_data(%s): data offset value is not valid: %d\n",
|
|
UNPACK_SRCNAME, msr->fsdh->data_offset);
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
datasize = msr->reclen - msr->fsdh->data_offset;
|
|
dbuf = msr->record + msr->fsdh->data_offset;
|
|
|
|
switch (msr->encoding)
|
|
{
|
|
case DE_ASCII:
|
|
samplesize = 1; break;
|
|
case DE_INT16:
|
|
case DE_INT32:
|
|
case DE_FLOAT32:
|
|
case DE_STEIM1:
|
|
case DE_STEIM2:
|
|
case DE_GEOSCOPE24:
|
|
case DE_GEOSCOPE163:
|
|
case DE_GEOSCOPE164:
|
|
case DE_CDSN:
|
|
case DE_SRO:
|
|
case DE_DWWSSN:
|
|
samplesize = 4; break;
|
|
case DE_FLOAT64:
|
|
samplesize = 8; break;
|
|
default:
|
|
samplesize = 0; break;
|
|
}
|
|
|
|
/* Calculate buffer size needed for unpacked samples */
|
|
unpacksize = (int) msr->samplecnt * samplesize;
|
|
|
|
/* (Re)Allocate space for the unpacked data */
|
|
if ( unpacksize > 0 )
|
|
{
|
|
msr->datasamples = realloc (msr->datasamples, unpacksize);
|
|
|
|
if ( msr->datasamples == NULL )
|
|
{
|
|
ms_log (2, "msr_unpack_data(%s): Cannot (re)allocate memory\n",
|
|
UNPACK_SRCNAME);
|
|
return MS_GENERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( msr->datasamples )
|
|
free (msr->datasamples);
|
|
msr->datasamples = 0;
|
|
msr->numsamples = 0;
|
|
}
|
|
|
|
if ( verbose > 2 )
|
|
ms_log (1, "%s: Unpacking %lld samples\n",
|
|
UNPACK_SRCNAME, (long long int)msr->samplecnt);
|
|
|
|
/* Decide if this is a encoding that we can decode */
|
|
switch (msr->encoding)
|
|
{
|
|
|
|
case DE_ASCII:
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Found ASCII data\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = (int)msr->samplecnt;
|
|
memcpy (msr->datasamples, dbuf, nsamples);
|
|
msr->sampletype = 'a';
|
|
break;
|
|
|
|
case DE_INT16:
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking INT-16 data samples\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_int_16 ((int16_t *)dbuf, (int)msr->samplecnt,
|
|
(int)msr->samplecnt, msr->datasamples,
|
|
swapflag);
|
|
msr->sampletype = 'i';
|
|
break;
|
|
|
|
case DE_INT32:
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking INT-32 data samples\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_int_32 ((int32_t *)dbuf, (int)msr->samplecnt,
|
|
(int)msr->samplecnt, msr->datasamples,
|
|
swapflag);
|
|
msr->sampletype = 'i';
|
|
break;
|
|
|
|
case DE_FLOAT32:
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking FLOAT-32 data samples\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_float_32 ((float *)dbuf, (int)msr->samplecnt,
|
|
(int)msr->samplecnt, msr->datasamples,
|
|
swapflag);
|
|
msr->sampletype = 'f';
|
|
break;
|
|
|
|
case DE_FLOAT64:
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking FLOAT-64 data samples\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_float_64 ((double *)dbuf, (int)msr->samplecnt,
|
|
(int)msr->samplecnt, msr->datasamples,
|
|
swapflag);
|
|
msr->sampletype = 'd';
|
|
break;
|
|
|
|
case DE_STEIM1:
|
|
diffbuff = (int32_t *) malloc(unpacksize);
|
|
if ( diffbuff == NULL )
|
|
{
|
|
ms_log (2, "msr_unpack_data(%s): Cannot allocate diff buffer\n",
|
|
UNPACK_SRCNAME);
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking Steim-1 data frames\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_steim1 ((FRAME *)dbuf, datasize, (int)msr->samplecnt,
|
|
(int)msr->samplecnt, msr->datasamples, diffbuff,
|
|
&x0, &xn, swapflag, verbose);
|
|
msr->sampletype = 'i';
|
|
free (diffbuff);
|
|
break;
|
|
|
|
case DE_STEIM2:
|
|
diffbuff = (int32_t *) malloc(unpacksize);
|
|
if ( diffbuff == NULL )
|
|
{
|
|
ms_log (2, "msr_unpack_data(%s): Cannot allocate diff buffer\n",
|
|
UNPACK_SRCNAME);
|
|
return MS_GENERROR;
|
|
}
|
|
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking Steim-2 data frames\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_steim2 ((FRAME *)dbuf, datasize, (int)msr->samplecnt,
|
|
(int)msr->samplecnt, msr->datasamples, diffbuff,
|
|
&x0, &xn, swapflag, verbose);
|
|
msr->sampletype = 'i';
|
|
free (diffbuff);
|
|
break;
|
|
|
|
case DE_GEOSCOPE24:
|
|
case DE_GEOSCOPE163:
|
|
case DE_GEOSCOPE164:
|
|
if ( verbose > 1 )
|
|
{
|
|
if ( msr->encoding == DE_GEOSCOPE24 )
|
|
ms_log (1, "%s: Unpacking GEOSCOPE 24bit integer data samples\n",
|
|
UNPACK_SRCNAME);
|
|
if ( msr->encoding == DE_GEOSCOPE163 )
|
|
ms_log (1, "%s: Unpacking GEOSCOPE 16bit gain ranged/3bit exponent data samples\n",
|
|
UNPACK_SRCNAME);
|
|
if ( msr->encoding == DE_GEOSCOPE164 )
|
|
ms_log (1, "%s: Unpacking GEOSCOPE 16bit gain ranged/4bit exponent data samples\n",
|
|
UNPACK_SRCNAME);
|
|
}
|
|
|
|
nsamples = msr_unpack_geoscope (dbuf, (int)msr->samplecnt, (int)msr->samplecnt,
|
|
msr->datasamples, msr->encoding, swapflag);
|
|
msr->sampletype = 'f';
|
|
break;
|
|
|
|
case DE_CDSN:
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking CDSN encoded data samples\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_cdsn ((int16_t *)dbuf, (int)msr->samplecnt, (int)msr->samplecnt,
|
|
msr->datasamples, swapflag);
|
|
msr->sampletype = 'i';
|
|
break;
|
|
|
|
case DE_SRO:
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking SRO encoded data samples\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_sro ((int16_t *)dbuf, (int)msr->samplecnt, (int)msr->samplecnt,
|
|
msr->datasamples, swapflag);
|
|
msr->sampletype = 'i';
|
|
break;
|
|
|
|
case DE_DWWSSN:
|
|
if ( verbose > 1 )
|
|
ms_log (1, "%s: Unpacking DWWSSN encoded data samples\n", UNPACK_SRCNAME);
|
|
|
|
nsamples = msr_unpack_dwwssn ((int16_t *)dbuf, (int)msr->samplecnt, (int)msr->samplecnt,
|
|
msr->datasamples, swapflag);
|
|
msr->sampletype = 'i';
|
|
break;
|
|
|
|
default:
|
|
ms_log (2, "%s: Unsupported encoding format %d (%s)\n",
|
|
UNPACK_SRCNAME, msr->encoding, (char *) ms_encodingstr(msr->encoding));
|
|
|
|
return MS_UNKNOWNFORMAT;
|
|
}
|
|
|
|
return nsamples;
|
|
} /* End of msr_unpack_data() */
|
|
|
|
|
|
/************************************************************************
|
|
* check_environment:
|
|
*
|
|
* Check environment variables and set global variables appropriately.
|
|
*
|
|
* Return 0 on success and -1 on error.
|
|
************************************************************************/
|
|
static int
|
|
check_environment (int verbose)
|
|
{
|
|
char *envvariable;
|
|
|
|
/* Read possible environmental variables that force byteorder */
|
|
if ( unpackheaderbyteorder == -2 )
|
|
{
|
|
if ( (envvariable = getenv("UNPACK_HEADER_BYTEORDER")) )
|
|
{
|
|
if ( *envvariable != '0' && *envvariable != '1' )
|
|
{
|
|
ms_log (2, "Environment variable UNPACK_HEADER_BYTEORDER must be set to '0' or '1'\n");
|
|
return -1;
|
|
}
|
|
else if ( *envvariable == '0' )
|
|
{
|
|
unpackheaderbyteorder = 0;
|
|
if ( verbose > 2 )
|
|
ms_log (1, "UNPACK_HEADER_BYTEORDER=0, unpacking little-endian header\n");
|
|
}
|
|
else
|
|
{
|
|
unpackheaderbyteorder = 1;
|
|
if ( verbose > 2 )
|
|
ms_log (1, "UNPACK_HEADER_BYTEORDER=1, unpacking big-endian header\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unpackheaderbyteorder = -1;
|
|
}
|
|
}
|
|
|
|
if ( unpackdatabyteorder == -2 )
|
|
{
|
|
if ( (envvariable = getenv("UNPACK_DATA_BYTEORDER")) )
|
|
{
|
|
if ( *envvariable != '0' && *envvariable != '1' )
|
|
{
|
|
ms_log (2, "Environment variable UNPACK_DATA_BYTEORDER must be set to '0' or '1'\n");
|
|
return -1;
|
|
}
|
|
else if ( *envvariable == '0' )
|
|
{
|
|
unpackdatabyteorder = 0;
|
|
if ( verbose > 2 )
|
|
ms_log (1, "UNPACK_DATA_BYTEORDER=0, unpacking little-endian data samples\n");
|
|
}
|
|
else
|
|
{
|
|
unpackdatabyteorder = 1;
|
|
if ( verbose > 2 )
|
|
ms_log (1, "UNPACK_DATA_BYTEORDER=1, unpacking big-endian data samples\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unpackdatabyteorder = -1;
|
|
}
|
|
}
|
|
|
|
/* Read possible environmental variable that forces encoding format */
|
|
if ( unpackencodingformat == -2 )
|
|
{
|
|
if ( (envvariable = getenv("UNPACK_DATA_FORMAT")) )
|
|
{
|
|
unpackencodingformat = (int) strtol (envvariable, NULL, 10);
|
|
|
|
if ( unpackencodingformat < 0 || unpackencodingformat > 33 )
|
|
{
|
|
ms_log (2, "Environment variable UNPACK_DATA_FORMAT set to invalid value: '%d'\n", unpackencodingformat);
|
|
return -1;
|
|
}
|
|
else if ( verbose > 2 )
|
|
ms_log (1, "UNPACK_DATA_FORMAT, unpacking data in encoding format %d\n", unpackencodingformat);
|
|
}
|
|
else
|
|
{
|
|
unpackencodingformat = -1;
|
|
}
|
|
}
|
|
|
|
/* Read possible environmental variable to be used as a fallback encoding format */
|
|
if ( unpackencodingfallback == -2 )
|
|
{
|
|
if ( (envvariable = getenv("UNPACK_DATA_FORMAT_FALLBACK")) )
|
|
{
|
|
unpackencodingfallback = (int) strtol (envvariable, NULL, 10);
|
|
|
|
if ( unpackencodingfallback < 0 || unpackencodingfallback > 33 )
|
|
{
|
|
ms_log (2, "Environment variable UNPACK_DATA_FORMAT_FALLBACK set to invalid value: '%d'\n",
|
|
unpackencodingfallback);
|
|
return -1;
|
|
}
|
|
else if ( verbose > 2 )
|
|
ms_log (1, "UNPACK_DATA_FORMAT_FALLBACK, fallback data unpacking encoding format %d\n",
|
|
unpackencodingfallback);
|
|
}
|
|
else
|
|
{
|
|
unpackencodingfallback = 10; /* Default fallback is Steim-1 encoding */
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} /* End of check_environment() */
|