DATRW++ library: seismic data I/O with multiple formats
mseedread_mseedrecord_read.cc
Go to the documentation of this file.
1 /*! \file mseedread_mseedrecord_read.cc
2  * \brief MiniSEEDRecord::read read complete MiniSEED record and decode sample data (implementation)
3  *
4  * ----------------------------------------------------------------------------
5  *
6  * \author Thomas Forbriger
7  * \date 22/07/2014
8  *
9  * MiniSEEDRecord::read read complete MiniSEED record and decode sample
10  * data (implementation)
11  *
12  * Copyright (c) 2014 by Thomas Forbriger (BFO Schiltach)
13  *
14  * ----
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28  * ----
29  *
30  * REVISIONS and CHANGES
31  * - 22/07/2014 V1.0 Thomas Forbriger (thof)
32  * - copied from mseed/mseedread.cc
33  * - issue warning if frame count is unreasonable
34  * - support new format modifier: estimateNframes
35  * - 27/06/2016 V1.1b thof
36  * - fix reading of incompletely filled records
37  * V1.2 thof
38  * - disable check for number of frames, if number of
39  * frames is guessed
40  * - 08/07/2016 V1.3 thof:
41  * - make correct use of new DATRW_nonfatal_assert
42  * - 11/07/2016 V1.4 thof:
43  * - improve error messages in consistency checks
44  * - 12/07/2016 V1.5 thof:
45  * - add consistency check for usec value
46  *
47  * ============================================================================
48  */
49 #define DATRW_MSEEDREAD_MSEEDRECORD_READ_CC_VERSION \
50  "DATRW_MSEEDREAD_MSEEDRECORD_READ_CC V1.5"
51 
52 #include <string>
53 #include <cstring>
54 #include <string.h>
55 #include <datrwxx/mseedread.h>
56 #include <datrwxx/mseed.h>
57 #include <datrwxx/bytesex.h>
58 #include <datrwxx/debug.h>
59 #include <datrwxx/mseed_keywords.h>
60 #include <aff/subarray.h>
61 
62 namespace datrw {
63 
64  namespace mseed {
65 
66  /*! \brief Big effort of decoding a MiniSEED record.
67  *
68  * Read all blocks belonging to one record in sequence. Extract all frames
69  * in all blocks in sequence (within the outer loop of reading blocks).
70  * Reading stops, if all blocks of the record are read or all frames (as
71  * indicated in the header of the record) are extracted are all expected
72  * samples are extracted. Extraction of samples stops when all samples (as
73  * indicated by the record header) are extracted.
74  *
75  * It may appear in normal operation that the capacity of a data record is
76  * not completely used to store samples.
77  */
78  void MiniSEEDRecord::read(std::istream& is)
79  {
80  // create block to read MiniSEED
81  MiniSEEDblock block=this->readheader(is);
82  // count blocks
83  int iblock=0;
84  // count frames
85  int iframe=0;
86 
87  if (Mvalid)
88  {
89  // data header is present
90  ++iblock;
91 
92  // For SEED data (not header fields) the byte order my differ from
93  // file to file. Use the byte order defined in the Data Only SEED
94  // Blockette.
95  bool doswap=needswap(Mblockette1000.bytesex);
96 
97  // extract essential header data
98  // -----------------------------
99  //
100  // number of samples to extract
102  // beginning of first frame
103  int pdata=Mrecordheader.dbeg;
104  // size of record in bytes
105  int reclen=Mblockette1000.reclenbytes();
106  // number of blocks to read for full record
107  int nblocks=reclen/block.bytesize();
108  // number of frames to be expected
109  int nframes=(reclen-pdata)/SEED::SteimFrame::blocksize;
110  // is number of frames read from file?
111  bool filetellsnframes=false;
113  {
114  filetellsnframes=true;
116  if (nframes < 1)
117  {
118  DATRW_warning("MiniSEEDRecord::read",
119  "unreasonable number of frames ("
120  << nframes <<
121  ") given in blockette1001\n"
122  << " consider to use format modifier \"estimateNframes\"");
123  }
124  }
125  // check Steim 1 encoding
128  "ERROR (reading MiniSEED record): "
129  "Can only decode Steim (1) or Steim (2) compression");
130  // check size of sample buffer
131  if (int(Mdata.size())<nsamples)
132  { Mdata=Tseries(nsamples); }
133  int isample=0;
134  // x0, xn, d1
135  int x0, xn, d1, sum;
136  // x0 and xn are not read yet
137  bool waitingforxn=true;
138 
139  // extract samples
140  // ---------------
141  // loop over blocks in record
142  while ((iblock <= nblocks) &&
143  (isample < nsamples) &&
144  ((iframe < nframes) || (!filetellsnframes)))
145  {
146  // extract
147  // (loop over frames in block)
148  while (pdata < int(block.bytesize()) &&
149  (isample < nsamples) &&
150  ((iframe < nframes) || (!filetellsnframes)))
151  {
152  SEED::SteimFrame* pframe;
153  switch (Mblockette1000.iformat())
154  {
155  case SEED::steim1:
156  pframe= new SEED::Steim1Frame(block.block(pdata), doswap);
157  break;
158  case SEED::steim2:
159  pframe= new SEED::Steim2Frame(block.block(pdata), doswap);
160  break;
161  default:
162  DATRW_abort("ERROR (reading MiniSEED record): "
163  "compression format not supported");
164  }
165  ++iframe;
166  SEED::SteimFrame& rframe(*pframe);
167 
168  if (waitingforxn) {
170  "ERROR (reading MiniSEED record): "
171  "missing X0");
172  x0=rframe.diff();
173  rframe.next();
175  "ERROR (reading MiniSEED record): "
176  "missing XN");
177  xn=rframe.diff();
178  rframe.next();
179  d1=rframe.diff();
180  rframe.next();
181  waitingforxn=false;
182  Mdata(isample)=x0;
183  ++isample;
184  sum=x0;
185  /*
186  * Calculate value of last sample of previous record in order to
187  * provide means for a continuous stream of sample values.
188  */
189  Mxm1=x0-d1;
190  }
191  while (rframe.valid() && (isample < nsamples))
192  {
193  if (rframe.ctrl() != SEED::SteimFrame::Fspecial)
194  {
195  sum += rframe.diff();
196  Mdata(isample)=sum;
197  ++isample;
198  }
199  rframe.next();
200  }
202  delete pframe;
203  } // while (pdata < int(block.bytesize()))
204 
205  // read next block
206  if (iblock < nblocks)
207  {
208  is >> block;
209  if (is.bad())
210  {
211  DATRW_warning("MiniSEEDRecord::read",
212  "input stream is bad!");
213  }
214  pdata=0;
215  ++iblock;
216  }
217  } // while ((iblock <= nblocks) &&
218  // (isample < nsamples) &&
219  // ((iframe < nframes) || (!filetellsnframes)))
220  // skip rest of blocks in record
221  while (iblock < nblocks)
222  {
223  // read next block
224  is >> block;
225  if (is.bad())
226  {
227  DATRW_warning("MiniSEEDRecord::read",
228  "input stream is bad!");
229  }
230  ++iblock;
231  }
232 
233  /* consistency checks
234  * ==================
235  * Check for data integrity, consistency, plausibility,...
236  */
237 
238  /* string constant containing hint to format modifiers
239  * (this is a very local constant, defined here becausing being used
240  * three times)
241  */
242  const std::string CHintToFormatModifiers(
243  "consider to use format modifiers \""
244  + std::string(key::nonfatal) + "\" or \""
245  + std::string(key::skipcheck) + "\"\n"
246  "if you like to ignore this inconsistency");
247 
248  /* Check for usec field being in specified range
249  * ---------------------------------------------
250  *
251  * On page 124 of "SEED Reference Manual, Standard for the Exchange of
252  * Earthquake Data, SEED Format Version 2.4, August, 2012" with
253  * respect to [1001] Data Extension Blockette (8 bytes):
254  *
255  * > field 4: BYTE: µsec has the data start time down to the
256  * > microsecond. The SEED format handles down to 100µsecs. This
257  * > field is an offset from that value. The recommended value is
258  * > from -50 to +49µsecs. At the users option, this value may be
259  * > from 0 to +99µsecs.
260  */
262  {
264  || (!Mchecks.usec.fatal),
265  (Mblockette1001.iusec()>=-50)
266  && (Mblockette1001.iusec()<=99),
267  "usec-value in MiniSEED record is out of specified range\n"
268  "consistency check \"" << key::nsamples << "\" complains:\n"
269  "usec-value in [1001] Data Extension Blockette: "
270  << DATRW_value(Mblockette1001.iusec()) << "\n"
271  "specified range SEED Reference Manual, "
272  "Version 2.4, August, 2012 (page 124):\n"
273  "The recommended value is from -50 to +49 usecs.\n"
274  "At the users option, this value may be from 0 to +99 usecs.\n"
275  << CHintToFormatModifiers);
276  }
277 
278  /* Check for consistent number of samples.
279  * ---------------------------------------
280  *
281  * Number of samples to be expected is provided in the header in
282  * the fixed section data header. This value will be compared against
283  * the number of samples actually provided in the logical record.
284  */
286  {
288  || (!Mchecks.nsamples.fatal),
289  (isample==nsamples),
290  "number of samples in MiniSEED record is inconsistent\n"
291  "consistency check \"" << key::nsamples << "\" complains:\n"
292  "number of samples announced in header: "
293  << DATRW_value(nsamples) << "\n"
294  "number of samples actually used: "
295  << DATRW_value(isample) << "\n"
296  << CHintToFormatModifiers);
297  }
298 
299  /* Check for consistent data values.
300  * ---------------------------------
301  *
302  * Page 142 of "SEED Reference Manual, Standard for the Exchange of
303  * Earthquake Data, SEED Format Version 2.4, August, 2012":
304  *
305  * > The reverse integrating constant also provides for a quick data
306  * > integrity check when compared with the last computed
307  * > sample. A discrepancy indicates that the contents of the data are
308  * > garbled.
309  *
310  * The sample value of the last sample in the record is compared against
311  * the value of the reverse integration constant as provided in the
312  * first data frame of the record.
313  */
314  if (Mchecks.data.docheck)
315  {
317  || (!Mchecks.data.fatal),
318  (Mdata(isample-1)==xn),
319  "data in MiniSEED record are inconsistent\n"
320  "consistency check \"" << key::data << "\" complains:\n"
321  "expected value of last sample "
322  "(i.e. reverse integration constant): "
323  << DATRW_value(xn) << "\n"
324  "value of last sample ("
325  << DATRW_value(isample) << ") actually read: "
326  << DATRW_value(Mdata(isample-1)) << "\n"
327  << CHintToFormatModifiers);
328  }
329 
330  /* Check for consistent number of frames.
331  * --------------------------------------
332  *
333  * Number of frames to be expected is provided in the header in
334  * Blockette 1001. This value will be compared against the number of
335  * frames actually used to store data.
336  *
337  * Skip this test if data file does not specify number of frames. If
338  * data records are not completely filled with samples, there is no
339  * reason, why we should have guessed the correct number of frames.
340  */
341  if (filetellsnframes && Mchecks.nframes.docheck)
342  {
344  || (!Mchecks.nframes.fatal),
345  (iframe==nframes),
346  "number of frames in MiniSEED record is inconsistent\n"
347  "consistency check \"" << key::nframes << "\" complains:\n"
348  "number of frames announced in header: "
349  << DATRW_value(nframes) << "\n"
350  "number of frames actually used: "
351  << DATRW_value(iframe) << "\n"
352  << CHintToFormatModifiers);
353  }
354 
355  // finished successfully
356  if (is.good()) { Mvalid=true; }
357  }
358  } // void MiniSEEDRecord::read(std::istream& is)
359 
360  } // namespace mseed
361 
362 } // namespace datrw
363 
364 /* ----- END OF mseedread_mseedrecord_read.cc ----- */
ConsistencyChecks Mchecks
flags controlling consistency checks
Definition: mseedread.h:235
bool fatal
Consequences of check.
Definition: mseed.h:77
#define DATRW_assert(C, M)
Check an assertion and report by throwing an exception.
Definition: error.h:92
ConsistencyCheckControl nsamples
Check for consistent number of samples.
Definition: mseed.h:106
MiniSEEDblock readheader(std::istream &is)
read header and return block
static const int blocksize
size of frame in bytes
Definition: seedstructs.h:465
ConsistencyCheckControl data
Check for consistent data values.
Definition: mseed.h:121
Steim (1) compression.
Definition: seedstructs.h:102
macro function for debugging output (prototypes)
SEED::DataExtensionBlockette Mblockette1001
Data Extension Blockette.
Definition: mseedread.h:241
unsigned short int dbeg
beginning of data
Definition: seedstructs.h:320
void read(std::istream &is)
read a full MiniSEED record including data samples
unsigned int bytesize() const
Definition: mseedread.h:90
const char *const nframes
keywords for consistency checks
unsigned short int nsamp
number of samples
Definition: seedstructs.h:311
bool Mhasblockette1001
Data Extension Blockette is present.
Definition: mseedread.h:231
int Mxm1
Expected value of last sample in previous block.
Definition: mseedread.h:243
bool MestimateNframes
estimate number of frames
Definition: mseedread.h:233
ConsistencyCheckControl usec
Check for usec field being in specified range.
Definition: mseed.h:134
const char *const data
keywords for consistency checks
bool needswap(const unsigned char &bytesex)
check bytesex
Definition: mseedread.cc:192
A copy of bytesex.h from libtfxx (prototypes)
aff::Series< Tvalue > Tseries
type of container for sample data
Definition: mseedread.h:159
#define DATRW_warning(N, M)
Report a warning.
Definition: error.h:148
Root namespace of library.
Definition: aalibdatrwxx.cc:16
bool docheck
Activation of check.
Definition: mseed.h:72
Steim (2) compression.
Definition: seedstructs.h:103
const char *const skipcheck
keywords for format modifiers
#define DATRW_abort(M)
Abort and give a message.
Definition: error.h:101
contains non-data information, like headers
Definition: seedstructs.h:469
bool inconsistencies_are_not_fatal
make inconsistencies non fatal
Definition: mseedread.h:134
SEED::FixedDataRecordHeader Mrecordheader
Fixed Data Record Header.
Definition: mseedread.h:237
#define DATRW_nonfatal_assert(F, C, M)
Macro to distinguish between fatal and non fatal assertions.
Definition: error.h:138
Tseries Mdata
Container for sample data.
Definition: mseedread.h:245
void next()
step to next difference value
Definition: seedstructs.cc:172
provide mini-SEED data (prototypes)
unsigned int nsamples() const
number of samples
Definition: mseedread.h:197
virtual int diff() const =0
return current difference value
bool valid() const
we are still inside the frame
Definition: seedstructs.h:506
const char *const nsamples
number of samples per minute block and channel
SEED::DataOnlySEEDBlockette Mblockette1000
Data Only SEED Blockette.
Definition: mseedread.h:239
#define DATRW_value(V)
report value
Definition: debug.h:65
Debug Mdebug
debug options
Definition: mseedread.h:225
bool Mvalid
contains valid data
Definition: mseedread.h:227
ConsistencyCheckControl nframes
Check for consistent number of frames.
Definition: mseed.h:98
Tvalue * block() const
Definition: mseedread.h:93
unsigned char bytesex
Word order.
Definition: seedstructs.h:356
ESteimControl ctrl(const int &i) const
return control code for word i
Definition: seedstructs.cc:95
const char *const nonfatal
keywords for format modifiers