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.

723 lines
21 KiB
C

/***********************************************************************
* Routines for packing Text, INT_16, INT_32, FLOAT_32, FLOAT_64,
* STEIM1 and STEIM2 data 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 <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include "libmseed.h"
#include "packdata.h"
/************************************************************************
* msr_encode_text:
*
* Encode text data and place in supplied buffer.
*
* Return number of samples in output buffer on success, -1 on failure.
************************************************************************/
int
msr_encode_text (char *input, int samplecount, char *output,
int outputlength)
{
int length;
if (samplecount <= 0)
return 0;
if (!input || !output || outputlength <= 0)
return -1;
/* Determine minimum of input or output */
length = (samplecount < outputlength) ? samplecount : outputlength;
memcpy (output, input, length);
return length;
} /* End of msr_encode_text() */
/************************************************************************
* msr_encode_int16:
*
* Encode 16-bit integer data from an array of 32-bit integers and
* place in supplied buffer. Swap if requested.
*
* Return number of samples in output buffer on success, -1 on failure.
************************************************************************/
int
msr_encode_int16 (int32_t *input, int samplecount, int16_t *output,
int outputlength, int swapflag)
{
int idx;
if (samplecount <= 0)
return 0;
if (!input || !output || outputlength <= 0)
return -1;
for (idx = 0; idx < samplecount && outputlength >= (int)sizeof (int16_t); idx++)
{
output[idx] = (int16_t)input[idx];
if (swapflag)
ms_gswap2 (&output[idx]);
outputlength -= sizeof (int16_t);
}
return idx;
} /* End of msr_encode_int16() */
/************************************************************************
* msr_encode_int32:
*
* Encode 32-bit integer data from an array of 32-bit integers and
* place in supplied buffer. Swap if requested.
*
* Return number of samples in output buffer on success, -1 on failure.
************************************************************************/
int
msr_encode_int32 (int32_t *input, int samplecount, int32_t *output,
int outputlength, int swapflag)
{
int idx;
if (samplecount <= 0)
return 0;
if (!input || !output || outputlength <= 0)
return -1;
for (idx = 0; idx < samplecount && outputlength >= (int)sizeof (int32_t); idx++)
{
output[idx] = input[idx];
if (swapflag)
ms_gswap4 (&output[idx]);
outputlength -= sizeof (int32_t);
}
return idx;
} /* End of msr_encode_int32() */
/************************************************************************
* msr_encode_float32:
*
* Encode 32-bit float data from an array of 32-bit floats and place
* in supplied buffer. Swap if requested.
*
* Return number of samples in output buffer on success, -1 on failure.
************************************************************************/
int
msr_encode_float32 (float *input, int samplecount, float *output,
int outputlength, int swapflag)
{
int idx;
if (samplecount <= 0)
return 0;
if (!input || !output || outputlength <= 0)
return -1;
for (idx = 0; idx < samplecount && outputlength >= (int)sizeof (float); idx++)
{
output[idx] = input[idx];
if (swapflag)
ms_gswap4 (&output[idx]);
outputlength -= sizeof (float);
}
return idx;
} /* End of msr_encode_float32() */
/************************************************************************
* msr_encode_float64:
*
* Encode 64-bit float data from an array of 64-bit doubles and place
* in supplied buffer. Swap if requested.
*
* Return number of samples in output buffer on success, -1 on failure.
************************************************************************/
int
msr_encode_float64 (double *input, int samplecount, double *output,
int outputlength, int swapflag)
{
int idx;
if (samplecount <= 0)
return 0;
if (!input || !output || outputlength <= 0)
return -1;
for (idx = 0; idx < samplecount && outputlength >= (int)sizeof (double); idx++)
{
output[idx] = input[idx];
if (swapflag)
ms_gswap8 (&output[idx]);
outputlength -= sizeof (double);
}
return idx;
} /* End of msr_encode_float64() */
/* Macro to determine number of bits needed to represent VALUE in
* the following bit widths: 4,5,6,8,10,15,16,30,32 and set RESULT. */
#define BITWIDTH(VALUE, RESULT) \
if (VALUE >= -8 && VALUE <= 7) \
RESULT = 4; \
else if (VALUE >= -16 && VALUE <= 15) \
RESULT = 5; \
else if (VALUE >= -32 && VALUE <= 31) \
RESULT = 6; \
else if (VALUE >= -128 && VALUE <= 127) \
RESULT = 8; \
else if (VALUE >= -512 && VALUE <= 511) \
RESULT = 10; \
else if (VALUE >= -16384 && VALUE <= 16383) \
RESULT = 15; \
else if (VALUE >= -32768 && VALUE <= 32767) \
RESULT = 16; \
else if (VALUE >= -536870912 && VALUE <= 536870911) \
RESULT = 30; \
else \
RESULT = 32;
/************************************************************************
* msr_encode_steim1:
*
* Encode Steim1 data frames from an array of 32-bit integers and
* place in supplied buffer. Swap if requested.
*
* diff0 is the first difference in the sequence and relates the first
* sample to the sample previous to it (not available to this
* function). It should be set to 0 if this value is not known.
*
* Return number of samples in output buffer on success, -1 on failure.
*
* \ref MessageOnError - this function logs a message on error
************************************************************************/
int
msr_encode_steim1 (int32_t *input, int samplecount, int32_t *output,
int outputlength, int32_t diff0, uint16_t *byteswritten,
int swapflag)
{
int32_t *frameptr; /* Frame pointer in output */
int32_t *Xnp = NULL; /* Reverse integration constant, aka last sample */
int32_t diffs[4];
int32_t bitwidth[4];
int diffcount = 0;
int inputidx = 0;
int outputsamples = 0;
int maxframes = outputlength / 64;
int packedsamples = 0;
int frameidx;
int startnibble;
int widx;
int idx;
union dword {
int8_t d8[4];
int16_t d16[2];
int32_t d32;
} * word;
if (samplecount <= 0)
return 0;
if (!input || !output || outputlength <= 0)
{
ms_log (2, "Required argument not defined: 'input', 'output' or 'outputlength' <= 0\n");
return -1;
}
#if ENCODE_DEBUG
ms_log (0, "Encoding Steim1 frames, samples: %d, max frames: %d, swapflag: %d\n",
samplecount, maxframes, swapflag);
#endif
/* Add first difference to buffers */
diffs[0] = diff0;
BITWIDTH (diffs[0], bitwidth[0]);
diffcount = 1;
for (frameidx = 0; frameidx < maxframes && outputsamples < samplecount; frameidx++)
{
frameptr = output + (16 * frameidx);
/* Set 64-byte frame to 0's */
memset (frameptr, 0, 64);
/* Save forward integration constant (X0), pointer to reverse integration constant (Xn)
* and set the starting nibble index depending on frame. */
if (frameidx == 0)
{
frameptr[1] = input[0];
if (swapflag)
ms_gswap4 (&frameptr[1]);
Xnp = &frameptr[2];
startnibble = 3; /* First frame: skip nibbles, X0, and Xn */
#if ENCODE_DEBUG
ms_log (0, "Frame %d: X0=%d\n", frameidx, input[0]);
#endif
}
else
{
startnibble = 1; /* Subsequent frames: skip nibbles */
#if ENCODE_DEBUG
ms_log (0, "Frame %d\n", frameidx);
#endif
}
for (widx = startnibble; widx < 16 && outputsamples < samplecount; widx++)
{
if (diffcount < 4)
{
/* Shift diffs and related bit widths to beginning of buffers */
for (idx = 0; idx < diffcount; idx++)
{
diffs[idx] = diffs[packedsamples + idx];
bitwidth[idx] = bitwidth[packedsamples + idx];
}
/* Add new diffs and determine bit width needed to represent */
for (idx = diffcount; idx < 4 && inputidx < (samplecount - 1); idx++, inputidx++)
{
diffs[idx] = *(input + inputidx + 1) - *(input + inputidx);
BITWIDTH (diffs[idx], bitwidth[idx]);
diffcount++;
}
}
/* Determine optimal packing by checking, in-order:
* 4 x 8-bit differences
* 2 x 16-bit differences
* 1 x 32-bit difference */
word = (union dword *)&frameptr[widx];
packedsamples = 0;
/* 4 x 8-bit differences */
if (diffcount == 4 &&
bitwidth[0] <= 8 && bitwidth[1] <= 8 &&
bitwidth[2] <= 8 && bitwidth[3] <= 8)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 01=4x8b %d %d %d %d\n",
widx, diffs[0], diffs[1], diffs[2], diffs[3]);
#endif
word->d8[0] = diffs[0];
word->d8[1] = diffs[1];
word->d8[2] = diffs[2];
word->d8[3] = diffs[3];
/* 2-bit nibble is 0b01 (0x1) */
frameptr[0] |= 0x1ul << (30 - 2 * widx);
packedsamples = 4;
}
/* 2 x 16-bit differences */
else if (diffcount >= 2 &&
bitwidth[0] <= 16 && bitwidth[1] <= 16)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 2=2x16b %d %d\n", widx, diffs[0], diffs[1]);
#endif
word->d16[0] = diffs[0];
word->d16[1] = diffs[1];
if (swapflag)
{
ms_gswap2 (&word->d16[0]);
ms_gswap2 (&word->d16[1]);
}
/* 2-bit nibble is 0b10 (0x2) */
frameptr[0] |= 0x2ul << (30 - 2 * widx);
packedsamples = 2;
}
/* 1 x 32-bit difference */
else
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 3=1x32b %d\n", widx, diffs[0]);
#endif
frameptr[widx] = diffs[0];
if (swapflag)
ms_gswap4 (&frameptr[widx]);
/* 2-bit nibble is 0b11 (0x3) */
frameptr[0] |= 0x3ul << (30 - 2 * widx);
packedsamples = 1;
}
diffcount -= packedsamples;
outputsamples += packedsamples;
} /* Done with words in frame */
/* Swap word with nibbles */
if (swapflag)
ms_gswap4 (&frameptr[0]);
} /* Done with frames */
/* Set Xn (reverse integration constant) in first frame to last sample */
if (Xnp)
*Xnp = *(input + outputsamples - 1);
if (swapflag)
ms_gswap4 (Xnp);
if (byteswritten)
*byteswritten = frameidx * 64;
return outputsamples;
} /* End of msr_encode_steim1() */
/************************************************************************
* msr_encode_steim2:
*
* Encode Steim2 data frames from an array of 32-bit integers and
* place in supplied buffer. Swap if requested.
*
* diff0 is the first difference in the sequence and relates the first
* sample to the sample previous to it (not available to this
* function). It should be set to 0 if this value is not known.
*
* Return number of samples in output buffer on success, -1 on failure.
*
* \ref MessageOnError - this function logs a message on error
************************************************************************/
int
msr_encode_steim2 (int32_t *input, int samplecount, int32_t *output,
int outputlength, int32_t diff0, uint16_t *byteswritten,
const char *sid, int swapflag)
{
uint32_t *frameptr; /* Frame pointer in output */
int32_t *Xnp = NULL; /* Reverse integration constant, aka last sample */
int32_t diffs[7];
int32_t bitwidth[7];
int diffcount = 0;
int inputidx = 0;
int outputsamples = 0;
int maxframes = outputlength / 64;
int packedsamples = 0;
int frameidx;
int startnibble;
int widx;
int idx;
union dword {
int8_t d8[4];
int16_t d16[2];
int32_t d32;
} * word;
if (samplecount <= 0)
return 0;
if (!input || !output || outputlength <= 0)
{
ms_log (2, "Required argument not defined: 'input', 'output' or 'outputlength' <= 0\n");
return -1;
}
#if ENCODE_DEBUG
ms_log (0, "Encoding Steim2 frames, samples: %d, max frames: %d, swapflag: %d\n",
samplecount, maxframes, swapflag);
#endif
/* Add first difference to buffers */
diffs[0] = diff0;
BITWIDTH (diffs[0], bitwidth[0]);
diffcount = 1;
for (frameidx = 0; frameidx < maxframes && outputsamples < samplecount; frameidx++)
{
frameptr = (uint32_t *)output + (16 * frameidx);
/* Set 64-byte frame to 0's */
memset (frameptr, 0, 64);
/* Save forward integration constant (X0), pointer to reverse integration constant (Xn)
* and set the starting nibble index depending on frame. */
if (frameidx == 0)
{
frameptr[1] = input[0];
#if ENCODE_DEBUG
ms_log (0, "Frame %d: X0=%d\n", frameidx, input[0]);
#endif
if (swapflag)
ms_gswap4 (&frameptr[1]);
Xnp = (int32_t *)&frameptr[2];
startnibble = 3; /* First frame: skip nibbles, X0, and Xn */
}
else
{
startnibble = 1; /* Subsequent frames: skip nibbles */
#if ENCODE_DEBUG
ms_log (0, "Frame %d\n", frameidx);
#endif
}
for (widx = startnibble; widx < 16 && outputsamples < samplecount; widx++)
{
if (diffcount < 7)
{
/* Shift diffs and related bit widths to beginning of buffers */
for (idx = 0; idx < diffcount; idx++)
{
diffs[idx] = diffs[packedsamples + idx];
bitwidth[idx] = bitwidth[packedsamples + idx];
}
/* Add new diffs and determine bit width needed to represent */
for (idx = diffcount; idx < 7 && inputidx < (samplecount - 1); idx++, inputidx++)
{
diffs[idx] = *(input + inputidx + 1) - *(input + inputidx);
BITWIDTH (diffs[idx], bitwidth[idx]);
diffcount++;
}
}
/* Determine optimal packing by checking, in-order:
* 7 x 4-bit differences
* 6 x 5-bit differences
* 5 x 6-bit differences
* 4 x 8-bit differences
* 3 x 10-bit differences
* 2 x 15-bit differences
* 1 x 30-bit difference */
packedsamples = 0;
/* 7 x 4-bit differences */
if (diffcount == 7 && bitwidth[0] <= 4 &&
bitwidth[1] <= 4 && bitwidth[2] <= 4 && bitwidth[3] <= 4 &&
bitwidth[4] <= 4 && bitwidth[5] <= 4 && bitwidth[6] <= 4)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 11,10=7x4b %d %d %d %d %d %d %d\n",
widx, diffs[0], diffs[1], diffs[2], diffs[3], diffs[4], diffs[5], diffs[6]);
#endif
/* Mask the values, shift to proper location and set in word */
frameptr[widx] = ((uint32_t)diffs[6] & 0xFul);
frameptr[widx] |= ((uint32_t)diffs[5] & 0xFul) << 4;
frameptr[widx] |= ((uint32_t)diffs[4] & 0xFul) << 8;
frameptr[widx] |= ((uint32_t)diffs[3] & 0xFul) << 12;
frameptr[widx] |= ((uint32_t)diffs[2] & 0xFul) << 16;
frameptr[widx] |= ((uint32_t)diffs[1] & 0xFul) << 20;
frameptr[widx] |= ((uint32_t)diffs[0] & 0xFul) << 24;
/* 2-bit decode nibble is 0b10 (0x2) */
frameptr[widx] |= 0x2ul << 30;
/* 2-bit nibble is 0b11 (0x3) */
frameptr[0] |= 0x3ul << (30 - 2 * widx);
packedsamples = 7;
}
/* 6 x 5-bit differences */
else if (diffcount >= 6 &&
bitwidth[0] <= 5 && bitwidth[1] <= 5 && bitwidth[2] <= 5 &&
bitwidth[3] <= 5 && bitwidth[4] <= 5 && bitwidth[5] <= 5)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 11,01=6x5b %d %d %d %d %d %d\n",
widx, diffs[0], diffs[1], diffs[2], diffs[3], diffs[4], diffs[5]);
#endif
/* Mask the values, shift to proper location and set in word */
frameptr[widx] = ((uint32_t)diffs[5] & 0x1Ful);
frameptr[widx] |= ((uint32_t)diffs[4] & 0x1Ful) << 5;
frameptr[widx] |= ((uint32_t)diffs[3] & 0x1Ful) << 10;
frameptr[widx] |= ((uint32_t)diffs[2] & 0x1Ful) << 15;
frameptr[widx] |= ((uint32_t)diffs[1] & 0x1Ful) << 20;
frameptr[widx] |= ((uint32_t)diffs[0] & 0x1Ful) << 25;
/* 2-bit decode nibble is 0b01 (0x1) */
frameptr[widx] |= 0x1ul << 30;
/* 2-bit nibble is 0b11 (0x3) */
frameptr[0] |= 0x3ul << (30 - 2 * widx);
packedsamples = 6;
}
/* 5 x 6-bit differences */
else if (diffcount >= 5 &&
bitwidth[0] <= 6 && bitwidth[1] <= 6 && bitwidth[2] <= 6 &&
bitwidth[3] <= 6 && bitwidth[4] <= 6)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 11,00=5x6b %d %d %d %d %d\n",
widx, diffs[0], diffs[1], diffs[2], diffs[3], diffs[4]);
#endif
/* Mask the values, shift to proper location and set in word */
frameptr[widx] = ((uint32_t)diffs[4] & 0x3Ful);
frameptr[widx] |= ((uint32_t)diffs[3] & 0x3Ful) << 6;
frameptr[widx] |= ((uint32_t)diffs[2] & 0x3Ful) << 12;
frameptr[widx] |= ((uint32_t)diffs[1] & 0x3Ful) << 18;
frameptr[widx] |= ((uint32_t)diffs[0] & 0x3Ful) << 24;
/* 2-bit decode nibble is 0b00, nothing to set */
/* 2-bit nibble is 0b11 (0x3) */
frameptr[0] |= 0x3ul << (30 - 2 * widx);
packedsamples = 5;
}
/* 4 x 8-bit differences */
else if (diffcount >= 4 &&
bitwidth[0] <= 8 && bitwidth[1] <= 8 &&
bitwidth[2] <= 8 && bitwidth[3] <= 8)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 01=4x8b %d %d %d %d\n",
widx, diffs[0], diffs[1], diffs[2], diffs[3]);
#endif
word = (union dword *)&frameptr[widx];
word->d8[0] = diffs[0];
word->d8[1] = diffs[1];
word->d8[2] = diffs[2];
word->d8[3] = diffs[3];
/* 2-bit nibble is 0b01, only need to set 2nd bit */
frameptr[0] |= 0x1ul << (30 - 2 * widx);
packedsamples = 4;
}
/* 3 x 10-bit differences */
else if (diffcount >= 3 &&
bitwidth[0] <= 10 && bitwidth[1] <= 10 && bitwidth[2] <= 10)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 10,11=3x10b %d %d %d\n",
widx, diffs[0], diffs[1], diffs[2]);
#endif
/* Mask the values, shift to proper location and set in word */
frameptr[widx] = ((uint32_t)diffs[2] & 0x3FFul);
frameptr[widx] |= ((uint32_t)diffs[1] & 0x3FFul) << 10;
frameptr[widx] |= ((uint32_t)diffs[0] & 0x3FFul) << 20;
/* 2-bit decode nibble is 0b11 (0x3) */
frameptr[widx] |= 0x3ul << 30;
/* 2-bit nibble is 0b10 (0x2) */
frameptr[0] |= 0x2ul << (30 - 2 * widx);
packedsamples = 3;
}
/* 2 x 15-bit differences */
else if (diffcount >= 2 &&
bitwidth[0] <= 15 && bitwidth[1] <= 15)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 10,10=2x15b %d %d\n",
widx, diffs[0], diffs[1]);
#endif
/* Mask the values, shift to proper location and set in word */
frameptr[widx] = ((uint32_t)diffs[1] & 0x7FFFul);
frameptr[widx] |= ((uint32_t)diffs[0] & 0x7FFFul) << 15;
/* 2-bit decode nibble is 0b10 (0x2) */
frameptr[widx] |= 0x2ul << 30;
/* 2-bit nibble is 0b10 (0x2) */
frameptr[0] |= 0x2ul << (30 - 2 * widx);
packedsamples = 2;
}
/* 1 x 30-bit difference */
else if (diffcount >= 1 &&
bitwidth[0] <= 30)
{
#if ENCODE_DEBUG
ms_log (0, " W%02d: 10,01=1x30b %d\n",
widx, diffs[0]);
#endif
/* Mask the value and set in word */
frameptr[widx] = ((uint32_t)diffs[0] & 0x3FFFFFFFul);
/* 2-bit decode nibble is 0b01 (0x1) */
frameptr[widx] |= 0x1ul << 30;
/* 2-bit nibble is 0b10 (0x2) */
frameptr[0] |= 0x2ul << (30 - 2 * widx);
packedsamples = 1;
}
else
{
ms_log (2, "%s: Unable to represent difference in <= 30 bits\n", sid);
return -1;
}
/* Swap encoded word except for 4x8-bit samples */
if (swapflag && packedsamples != 4)
ms_gswap4 (&frameptr[widx]);
diffcount -= packedsamples;
outputsamples += packedsamples;
} /* Done with words in frame */
/* Swap word with nibbles */
if (swapflag)
ms_gswap4 (&frameptr[0]);
} /* Done with frames */
/* Set Xn (reverse integration constant) in first frame to last sample */
if (Xnp)
*Xnp = *(input + outputsamples - 1);
if (swapflag)
ms_gswap4 (Xnp);
if (byteswritten)
*byteswritten = frameidx * 64;
return outputsamples;
} /* End of msr_encode_steim2() */