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.

396 lines
12 KiB
C

/***************************************************************************
* Generic routines to operate on miniSEED records.
*
* This file is part of the miniSEED Library.
*
* Copyright (c) 2023 Chad Trabant, EarthScope Data Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "libmseed.h"
/**********************************************************************/ /**
* @brief Initialize and return an ::MS3Record
*
* Memory is allocated if for a new ::MS3Record if \a msr is NULL.
*
* If memory for the \c datasamples field has been allocated the pointer
* will be retained for reuse. If memory for extra headers has been
* allocated it will be released.
*
* @param[in] msr A ::MS3Record to re-initialize
*
* @returns a pointer to a ::MS3Record struct on success or NULL on error.
*
* \ref MessageOnError - this function logs a message on error
***************************************************************************/
MS3Record *
msr3_init (MS3Record *msr)
{
void *datasamples = NULL;
size_t datasize = 0;
if (!msr)
{
msr = (MS3Record *)libmseed_memory.malloc (sizeof (MS3Record));
}
else
{
datasamples = msr->datasamples;
datasize = msr->datasize;
if (msr->extra)
libmseed_memory.free (msr->extra);
}
if (msr == NULL)
{
ms_log (2, "Cannot allocate memory\n");
return NULL;
}
memset (msr, 0, sizeof (MS3Record));
msr->datasamples = datasamples;
msr->datasize = datasize;
msr->reclen = -1;
msr->samplecnt = -1;
msr->encoding = -1;
return msr;
} /* End of msr3_init() */
/**********************************************************************/ /**
* @brief Free all memory associated with a ::MS3Record
*
* Free all memory associated with a ::MS3Record, including extra
* header and data samples if present.
*
* @param[in] ppmsr Pointer to point of the ::MS3Record to free
***************************************************************************/
void
msr3_free (MS3Record **ppmsr)
{
if (ppmsr != NULL && *ppmsr != 0)
{
if ((*ppmsr)->extra)
libmseed_memory.free ((*ppmsr)->extra);
if ((*ppmsr)->datasamples)
libmseed_memory.free ((*ppmsr)->datasamples);
libmseed_memory.free (*ppmsr);
*ppmsr = NULL;
}
} /* End of msr3_free() */
/**********************************************************************/ /**
* @brief Duplicate a ::MS3Record
*
* Extra headers are duplicated as well.
*
* If the \a datadup flag is true (non-zero) and the source
* ::MS3Record has associated data samples copy them as well.
*
* @param[in] msr ::MS3Record to duplicate
* @param[in] datadup Flag to control duplication of data samples
*
* @returns Pointer to a new ::MS3Record on success and NULL on error
*
* \ref MessageOnError - this function logs a message on error
***************************************************************************/
MS3Record *
msr3_duplicate (const MS3Record *msr, int8_t datadup)
{
MS3Record *dupmsr = 0;
if (!msr)
{
ms_log (2, "Required argument not defined: 'msr'\n");
return NULL;
}
/* Allocate target MS3Record structure */
if ((dupmsr = msr3_init (NULL)) == NULL)
return NULL;
/* Copy MS3Record structure */
memcpy (dupmsr, msr, sizeof (MS3Record));
/* Disconnect pointers from the source structure and reference values */
dupmsr->extra = NULL;
dupmsr->extralength = 0;
dupmsr->datasamples = NULL;
dupmsr->datasize = 0;
dupmsr->numsamples = 0;
/* Copy extra headers */
if (msr->extralength > 0 && msr->extra)
{
/* Allocate memory for new FSDH structure */
if ((dupmsr->extra = (char *)libmseed_memory.malloc (msr->extralength)) == NULL)
{
ms_log (2, "Error allocating memory\n");
msr3_free (&dupmsr);
return NULL;
}
memcpy (dupmsr->extra, msr->extra, msr->extralength);
if (dupmsr->extra)
dupmsr->extralength = msr->extralength;
}
/* Copy data samples if requested and available */
if (datadup && msr->numsamples > 0 && msr->datasize > 0 && msr->datasamples)
{
/* Allocate memory for new data array */
if ((dupmsr->datasamples = libmseed_memory.malloc ((size_t) (msr->datasize))) == NULL)
{
ms_log (2, "Error allocating memory\n");
msr3_free (&dupmsr);
return NULL;
}
memcpy (dupmsr->datasamples, msr->datasamples, msr->datasize);
if (dupmsr->datasamples)
{
dupmsr->datasize = msr->datasize;
dupmsr->numsamples = msr->numsamples;
}
}
return dupmsr;
} /* End of msr3_duplicate() */
/**********************************************************************/ /**
* @brief Calculate time of the last sample in a record
*
* If leap seconds have been loaded into the internal library list:
* when a record completely contains a leap second, starts before and
* ends after, the calculated end time will be adjusted (reduced) by
* one second.
* @note On the epoch time scale the value of a leap second is the
* same as the second following the leap second, without external
* information the values are ambiguous.
* \sa ms_readleapsecondfile()
*
* @param[in] msr ::MS3Record to calculate end time of
*
* @returns Time of the last sample on success and NSTERROR on error.
***************************************************************************/
nstime_t
msr3_endtime (const MS3Record *msr)
{
int64_t sampleoffset = 0;
if (!msr)
return NSTERROR;
if (msr->samplecnt > 0)
sampleoffset = msr->samplecnt - 1;
return ms_sampletime (msr->starttime, sampleoffset, msr->samprate);
} /* End of msr3_endtime() */
/**********************************************************************/ /**
* @brief Print header values of an MS3Record
*
* @param[in] msr ::MS3Record to print
* @param[in] details Flags to control the level of details:
* @parblock
* - \c 0 - print a single summary line
* - \c 1 - print most details of header
* - \c >1 - print all details of header and extra headers if present
* @endparblock
***************************************************************************/
void
msr3_print (const MS3Record *msr, int8_t details)
{
char time[40];
char b;
if (!msr)
return;
/* Generate a start time string */
ms_nstime2timestr (msr->starttime, time, ISOMONTHDAY_DOY_Z, NANO_MICRO);
/* Report information in the fixed header */
if (details > 0)
{
ms_log (0, "%s, version %d, %d bytes (format: %d)\n",
msr->sid, msr->pubversion, msr->reclen, msr->formatversion);
ms_log (0, " start time: %s\n", time);
ms_log (0, " number of samples: %" PRId64 "\n", msr->samplecnt);
ms_log (0, " sample rate (Hz): %.10g\n", msr3_sampratehz(msr));
if (details > 1)
{
b = msr->flags;
ms_log (0, " flags: [%d%d%d%d%d%d%d%d] 8 bits\n",
bit (b, 0x80), bit (b, 0x40), bit (b, 0x20), bit (b, 0x10),
bit (b, 0x08), bit (b, 0x04), bit (b, 0x02), bit (b, 0x01));
if (b & 0x01)
ms_log (0, " [Bit 0] Calibration signals present\n");
if (b & 0x02)
ms_log (0, " [Bit 1] Time tag is questionable\n");
if (b & 0x04)
ms_log (0, " [Bit 2] Clock locked\n");
if (b & 0x08)
ms_log (0, " [Bit 3] Undefined bit set\n");
if (b & 0x10)
ms_log (0, " [Bit 4] Undefined bit set\n");
if (b & 0x20)
ms_log (0, " [Bit 5] Undefined bit set\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");
}
ms_log (0, " CRC: 0x%0X\n", msr->crc);
ms_log (0, " extra header length: %d bytes\n", msr->extralength);
ms_log (0, " data payload length: %d bytes\n", msr->datalength);
ms_log (0, " payload encoding: %s (val: %d)\n",
(char *)ms_encodingstr (msr->encoding), msr->encoding);
if (details > 1 && msr->extralength > 0 && msr->extra)
{
ms_log (0, " extra headers:\n");
mseh_print (msr, 16);
}
}
else
{
ms_log (0, "%s, %d, %d, %" PRId64 " samples, %-.10g Hz, %s\n",
msr->sid, msr->pubversion, msr->reclen,
msr->samplecnt, msr3_sampratehz(msr), time);
}
} /* End of msr3_print() */
/**********************************************************************/ /**
* @brief Resize data sample buffer of ::MS3Record to what is needed
*
* This routine should only be used if pre-allocation of memory, via
* ::libmseed_prealloc_block_size, was enabled to allocate the buffer.
*
* @param[in] msr ::MS3Record to resize buffer
*
* @returns Return 0 on success, otherwise returns a libmseed error code.
*
* \ref MessageOnError - this function logs a message on error
***************************************************************************/
int
msr3_resize_buffer (MS3Record *msr)
{
uint8_t samplesize = 0;
size_t datasize;
if (!msr)
{
ms_log (2, "Required argument not defined: 'msr'\n");
return MS_GENERROR;
}
samplesize = ms_samplesize(msr->sampletype);
if (samplesize && msr->datasamples && msr->numsamples > 0)
{
datasize = (size_t) msr->numsamples * samplesize;
if (msr->datasize > datasize)
{
msr->datasamples = libmseed_memory.realloc (msr->datasamples, datasize);
if (msr->datasamples == NULL)
{
ms_log (2, "%s: Cannot (re)allocate memory\n", msr->sid);
return MS_GENERROR;
}
msr->datasize = datasize;
}
}
return 0;
} /* End of msr3_resize_buffer() */
/**********************************************************************/ /**
* @brief Calculate sample rate in samples/second (Hertz) for a given ::MS3Record
*
* @param[in] msr ::MS3Record to calculate sample rate for
*
* @returns Return sample rate in Hertz (samples per second)
***************************************************************************/
inline double
msr3_sampratehz (const MS3Record *msr)
{
if (!msr)
return 0.0;
if (msr->samprate < 0.0)
return (-1.0 / msr->samprate);
else
return msr->samprate;
} /* End of msr3_sampratehz() */
/**********************************************************************/ /**
* @brief Calculate data latency based on the host time
*
* Calculation is based on the time of the last sample in the record; in
* other words, the difference between the host time and the time of
* the last sample in the record.
*
* Double precision is returned, but the true precision is dependent
* on the accuracy of the host system clock among other things.
*
* @param[in] msr ::MS3Record to calculate lactency for
*
* @returns seconds of latency or 0.0 on error (indistinguishable from
* 0.0 latency).
***************************************************************************/
double
msr3_host_latency (const MS3Record *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 / NSTMODULUS) - span;
return latency;
} /* End of msr3_host_latency() */