1 /***************************************************************************
2 * msrepack.c
3 *
4 * A simple example of using the Mini-SEED record library to pack data.
5 *
6 * Opens a user specified file, parses the Mini-SEED records and
7 * opionally re-packs the data records and saves them to a specified
8 * output file.
9 *
10 * Written by Chad Trabant, IRIS Data Management Center
11 *
12 * modified 2012.105
13 ***************************************************************************/
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <time.h>
19 #include <errno.h>
20
21 #ifndef WIN32
22 #include <signal.h>
23 static void term_handler (int sig);
24 #endif
25
26 #include <libmseed.h>
27
28 #define VERSION "[libmseed " LIBMSEED_VERSION " example]"
29 #define PACKAGE "msrepack"
30
31 static flag verbose = 0;
32 static flag ppackets = 0;
33 static flag tracepack = 1;
34 static int reclen = 0;
35 static int packreclen = -1;
36 static char *encodingstr = 0;
37 static char *netcode = 0;
38 static int packencoding = -1;
39 static int byteorder = -1;
40 static char *inputfile = 0;
41 static FILE *outfile = 0;
42
43 static int convertsamples (MSRecord *msr, int packencoding);
44 static int parameter_proc (int argcount, char **argvec);
45 static void record_handler (char *record, int reclen, void *ptr);
46 static void usage (void);
47 static void term_handler (int sig);
48
49 int
main(int argc,char ** argv)50 main (int argc, char **argv)
51 {
52 MSRecord *msr = 0;
53 MSTraceGroup *mstg = 0;
54 MSTrace *mst;
55 int retcode;
56
57 int64_t packedsamples;
58 int64_t packedrecords;
59 int lastrecord;
60 int iseqnum = 1;
61
62 #ifndef WIN32
63 /* Signal handling, use POSIX calls with standardized semantics */
64 struct sigaction sa;
65
66 sa.sa_flags = SA_RESTART;
67 sigemptyset (&sa.sa_mask);
68
69 sa.sa_handler = term_handler;
70 sigaction (SIGINT, &sa, NULL);
71 sigaction (SIGQUIT, &sa, NULL);
72 sigaction (SIGTERM, &sa, NULL);
73
74 sa.sa_handler = SIG_IGN;
75 sigaction (SIGHUP, &sa, NULL);
76 sigaction (SIGPIPE, &sa, NULL);
77 #endif
78
79 /* Process given parameters (command line and parameter file) */
80 if (parameter_proc (argc, argv) < 0)
81 return -1;
82
83 /* Setup input encoding format if specified */
84 if ( encodingstr )
85 {
86 int inputencoding = strtoul (encodingstr, NULL, 10);
87
88 if ( inputencoding == 0 && errno == EINVAL )
89 {
90 ms_log (2, "Error parsing input encoding format: %s\n", encodingstr);
91 return -1;
92 }
93
94 MS_UNPACKENCODINGFORMAT (inputencoding);
95 }
96
97 /* Init MSTraceGroup */
98 mstg = mst_initgroup (mstg);
99
100 /* Loop over the input file */
101 while ( (retcode = ms_readmsr (&msr, inputfile, reclen, NULL, &lastrecord,
102 1, 1, verbose)) == MS_NOERROR )
103 {
104 msr_print (msr, ppackets);
105
106 /* Convert sample type as needed for packencoding */
107 if ( packencoding >= 0 && packencoding != msr->encoding )
108 {
109 if ( convertsamples (msr, packencoding) )
110 {
111 ms_log (2, "Error converting samples for encoding %d\n", packencoding);
112 break;
113 }
114 }
115
116 if ( packreclen >= 0 )
117 msr->reclen = packreclen;
118 else
119 packreclen = msr->reclen;
120
121 if ( packencoding >= 0 )
122 msr->encoding = packencoding;
123 else
124 packencoding = msr->encoding;
125
126 if ( byteorder >= 0 )
127 msr->byteorder = byteorder;
128 else
129 byteorder = msr->byteorder;
130
131 /* After unpacking the record, the start time in msr->starttime
132 is a potentially corrected start time, if correction has been
133 applied make sure the correction bit flag is set as it will
134 be used as a packing template. */
135 if ( msr->fsdh->time_correct && ! (msr->fsdh->act_flags & 0x02) )
136 {
137 ms_log (1, "Setting time correction applied flag for %s_%s_%s_%s\n",
138 msr->network, msr->station, msr->location, msr->channel);
139 msr->fsdh->act_flags |= 0x02;
140 }
141
142 /* Replace network code */
143 if ( netcode )
144 strncpy (msr->network, netcode, sizeof(msr->network));
145
146 /* If no samples in the record just pack the header */
147 if ( outfile && msr->numsamples == 0 )
148 {
149 msr_pack_header (msr, 1, verbose);
150 record_handler (msr->record, msr->reclen, NULL);
151 }
152
153 /* Pack each record individually */
154 else if ( outfile && ! tracepack )
155 {
156 msr->sequence_number = iseqnum;
157
158 packedrecords = msr_pack (msr, &record_handler, NULL, &packedsamples, 1, verbose);
159
160 if ( packedrecords == -1 )
161 ms_log (2, "Cannot pack records\n");
162 else
163 ms_log (1, "Packed %d records\n", packedrecords);
164
165 iseqnum = msr->sequence_number;
166 }
167
168 /* Pack records from a MSTraceGroup */
169 else if ( outfile && tracepack )
170 {
171 mst = mst_addmsrtogroup (mstg, msr, 0, -1.0, -1.0);
172
173 if ( ! mst )
174 {
175 ms_log (2, "Error adding MSRecord to MStrace!\n");
176 break;
177 }
178
179 /* Reset sequence number and free previous template */
180 if ( mst->prvtptr )
181 {
182 MSRecord *tmsr = (MSRecord *) mst->prvtptr;
183
184 /* Retain sequence number from previous template */
185 msr->sequence_number = tmsr->sequence_number;
186
187 msr_free (&tmsr);
188 }
189 else
190 {
191 msr->sequence_number = 1;
192 }
193
194 /* Copy MSRecord and store as template */
195 mst->prvtptr = msr_duplicate (msr, 0);
196
197 if ( ! mst->prvtptr )
198 {
199 ms_log (2, "Error duplicating MSRecord for template!\n");
200 break;
201 }
202
203 /* Pack traces based on selected method */
204 packedrecords = 0;
205 if ( tracepack == 1 )
206 {
207 mst = mstg->traces;
208 while ( mst )
209 {
210 packedrecords += mst_pack (mst, &record_handler, NULL, packreclen,
211 packencoding, byteorder, &packedsamples,
212 0, verbose, (MSRecord *)mst->prvtptr);
213 mst = mst->next;
214 }
215
216 ms_log (1, "Packed %d records\n", packedrecords);
217 }
218 if ( tracepack == 2 && lastrecord )
219 {
220 mst = mstg->traces;
221 while ( mst )
222 {
223 packedrecords += mst_pack (mst, &record_handler, NULL, packreclen,
224 packencoding, byteorder, &packedsamples,
225 1, verbose, (MSRecord *)mst->prvtptr);
226 mst = mst->next;
227 }
228
229 ms_log (1, "Packed %d records\n", packedrecords);
230 }
231 }
232 }
233
234 /* Make sure buffer of input data is flushed */
235 packedrecords = 0;
236 if ( tracepack )
237 {
238 mst = mstg->traces;
239 while ( mst )
240 {
241 packedrecords += mst_pack (mst, &record_handler, NULL, packreclen,
242 packencoding, byteorder, &packedsamples,
243 1, verbose, (MSRecord *)mst->prvtptr);
244 mst = mst->next;
245 }
246
247 if ( packedrecords )
248 ms_log (1, "Packed %d records\n", packedrecords);
249 }
250
251 if ( retcode != MS_ENDOFFILE )
252 ms_log (2, "Error reading %s: %s\n", inputfile, ms_errorstr(retcode));
253
254 /* Make sure everything is cleaned up */
255 ms_readmsr (&msr, NULL, 0, NULL, NULL, 0, 0, 0);
256 mst_freegroup (&mstg);
257
258 if ( outfile )
259 fclose (outfile);
260
261 return 0;
262 } /* End of main() */
263
264
265 /***************************************************************************
266 * convertsamples:
267 *
268 * Convert samples to type needed for the specified pack encoding.
269 *
270 * Returns 0 on success, and -1 on failure
271 ***************************************************************************/
272 static int
convertsamples(MSRecord * msr,int packencoding)273 convertsamples (MSRecord *msr, int packencoding)
274 {
275 char encodingtype;
276 int32_t *idata;
277 float *fdata;
278 double *ddata;
279 int idx;
280
281 if ( ! msr )
282 {
283 ms_log (2, "convertsamples: Error, no MSRecord specified!\n");
284 return -1;
285 }
286
287 /* Determine sample type needed for pack encoding */
288 switch (packencoding)
289 {
290 case DE_ASCII:
291 encodingtype = 'a';
292 break;
293 case DE_INT16:
294 case DE_INT32:
295 case DE_STEIM1:
296 case DE_STEIM2:
297 encodingtype = 'i';
298 break;
299 case DE_FLOAT32:
300 encodingtype = 'f';
301 break;
302 case DE_FLOAT64:
303 encodingtype = 'd';
304 break;
305 default:
306 encodingtype = msr->encoding;
307 break;
308 }
309
310 idata = (int32_t *) msr->datasamples;
311 fdata = (float *) msr->datasamples;
312 ddata = (double *) msr->datasamples;
313
314 /* Convert sample type if needed */
315 if ( msr->sampletype != encodingtype )
316 {
317 if ( msr->sampletype == 'a' || encodingtype == 'a' )
318 {
319 ms_log (2, "Error, cannot convert ASCII samples to/from numeric type\n");
320 return -1;
321 }
322
323 /* Convert to integers */
324 else if ( encodingtype == 'i' )
325 {
326 if ( msr->sampletype == 'f' ) /* Convert floats to integers with simple rounding */
327 {
328 for (idx = 0; idx < msr->numsamples; idx++)
329 {
330 /* Check for loss of sub-integer */
331 if ( (fdata[idx] - (int32_t)fdata[idx]) > 0.000001 )
332 {
333 ms_log (2, "Warning, Loss of precision when converting floats to integers, loss: %g\n",
334 (fdata[idx] - (int32_t)fdata[idx]));
335 return -1;
336 }
337
338 idata[idx] = (int32_t) (fdata[idx] + 0.5);
339 }
340 }
341 else if ( msr->sampletype == 'd' ) /* Convert doubles to integers with simple rounding */
342 {
343 for (idx = 0; idx < msr->numsamples; idx++)
344 {
345 /* Check for loss of sub-integer */
346 if ( (ddata[idx] - (int32_t)ddata[idx]) > 0.000001 )
347 {
348 ms_log (2, "Warning, Loss of precision when converting doubles to integers, loss: %g\n",
349 (ddata[idx] - (int32_t)ddata[idx]));
350 return -1;
351 }
352
353 idata[idx] = (int32_t) (ddata[idx] + 0.5);
354 }
355
356 /* Reallocate buffer for reduced size needed */
357 if ( ! (msr->datasamples = realloc (msr->datasamples,(size_t)(msr->numsamples * sizeof(int32_t)))) )
358 {
359 ms_log (2, "Error, cannot re-allocate buffer for sample conversion\n");
360 return -1;
361 }
362 }
363
364 msr->sampletype = 'i';
365 }
366
367 /* Convert to floats */
368 else if ( encodingtype == 'f' )
369 {
370 if ( msr->sampletype == 'i' ) /* Convert integers to floats */
371 {
372 for (idx = 0; idx < msr->numsamples; idx++)
373 fdata[idx] = (float) idata[idx];
374 }
375 else if ( msr->sampletype == 'd' ) /* Convert doubles to floats */
376 {
377 for (idx = 0; idx < msr->numsamples; idx++)
378 fdata[idx] = (float) ddata[idx];
379
380 /* Reallocate buffer for reduced size needed */
381 if ( ! (msr->datasamples = realloc (msr->datasamples, (size_t)(msr->numsamples * sizeof(float)))) )
382 {
383 ms_log (2, "Error, cannot re-allocate buffer for sample conversion\n");
384 return -1;
385 }
386 }
387
388 msr->sampletype = 'f';
389 }
390
391 /* Convert to doubles */
392 else if ( encodingtype == 'd' )
393 {
394 if ( ! (ddata = (double *) malloc ((size_t)(msr->numsamples * sizeof(double)))) )
395 {
396 ms_log (2, "Error, cannot allocate buffer for sample conversion to doubles\n");
397 return -1;
398 }
399
400 if ( msr->sampletype == 'i' ) /* Convert integers to doubles */
401 {
402 for (idx = 0; idx < msr->numsamples; idx++)
403 ddata[idx] = (double) idata[idx];
404
405 free (idata);
406 }
407 else if ( msr->sampletype == 'f' ) /* Convert floats to doubles */
408 {
409 for (idx = 0; idx < msr->numsamples; idx++)
410 ddata[idx] = (double) fdata[idx];
411
412 free (fdata);
413 }
414
415 msr->datasamples = ddata;
416 msr->sampletype = 'd';
417 }
418 }
419
420 return 0;
421 } /* End of convertsamples() */
422
423
424 /***************************************************************************
425 * parameter_proc:
426 *
427 * Process the command line parameters.
428 *
429 * Returns 0 on success, and -1 on failure
430 ***************************************************************************/
431 static int
parameter_proc(int argcount,char ** argvec)432 parameter_proc (int argcount, char **argvec)
433 {
434 int optind;
435 char *outputfile = 0;
436
437 /* Process all command line arguments */
438 for (optind = 1; optind < argcount; optind++)
439 {
440 if (strcmp (argvec[optind], "-V") == 0)
441 {
442 ms_log (1, "%s version: %s\n", PACKAGE, VERSION);
443 exit (0);
444 }
445 else if (strcmp (argvec[optind], "-h") == 0)
446 {
447 usage();
448 exit (0);
449 }
450 else if (strncmp (argvec[optind], "-v", 2) == 0)
451 {
452 verbose += strspn (&argvec[optind][1], "v");
453 }
454 else if (strncmp (argvec[optind], "-p", 2) == 0)
455 {
456 ppackets += strspn (&argvec[optind][1], "p");
457 }
458 else if (strcmp (argvec[optind], "-a") == 0)
459 {
460 reclen = -1;
461 }
462 else if (strcmp (argvec[optind], "-i") == 0)
463 {
464 tracepack = 0;
465 }
466 else if (strcmp (argvec[optind], "-t") == 0)
467 {
468 tracepack = 2;
469 }
470 else if (strcmp (argvec[optind], "-r") == 0)
471 {
472 reclen = strtol (argvec[++optind], NULL, 10);
473 }
474 else if (strcmp (argvec[optind], "-e") == 0)
475 {
476 encodingstr = argvec[++optind];
477 }
478 else if (strcmp (argvec[optind], "-R") == 0)
479 {
480 packreclen = strtol (argvec[++optind], NULL, 10);
481 }
482 else if (strcmp (argvec[optind], "-E") == 0)
483 {
484 packencoding = strtol (argvec[++optind], NULL, 10);
485 }
486 else if (strcmp (argvec[optind], "-b") == 0)
487 {
488 byteorder = strtol (argvec[++optind], NULL, 10);
489 }
490 else if (strcmp (argvec[optind], "-N") == 0)
491 {
492 netcode = argvec[++optind];
493 }
494 else if (strcmp (argvec[optind], "-o") == 0)
495 {
496 outputfile = argvec[++optind];
497 }
498 else if (strncmp (argvec[optind], "-", 1) == 0 &&
499 strlen (argvec[optind]) > 1 )
500 {
501 ms_log (2, "Unknown option: %s\n", argvec[optind]);
502 exit (1);
503 }
504 else if ( ! inputfile )
505 {
506 inputfile = argvec[optind];
507 }
508 else
509 {
510 ms_log (2, "Unknown option: %s\n", argvec[optind]);
511 exit (1);
512 }
513 }
514
515 /* Make sure an inputfile was specified */
516 if ( ! inputfile )
517 {
518 ms_log (2, "No input file was specified\n\n");
519 ms_log (1, "%s version %s\n\n", PACKAGE, VERSION);
520 ms_log (1, "Try %s -h for usage\n", PACKAGE);
521 exit (1);
522 }
523
524 /* Make sure an outputfile was specified */
525 if ( ! outputfile )
526 {
527 ms_log (2, "No output file was specified\n\n");
528 ms_log (1, "Try %s -h for usage\n", PACKAGE);
529 exit (1);
530 }
531 else if ( (outfile = fopen(outputfile, "wb")) == NULL )
532 {
533 ms_log (2, "Error opening output file: %s\n", outputfile);
534 exit (1);
535 }
536
537 /* Make sure network code is valid */
538 if ( netcode )
539 {
540 if ( strlen(netcode) > 2 || strlen(netcode) < 1 )
541 {
542 ms_log (2, "Error, invalid output network code: '%s'\n", netcode);
543 exit (1);
544 }
545 }
546
547 /* Report the program version */
548 if ( verbose )
549 ms_log (1, "%s version: %s\n", PACKAGE, VERSION);
550
551 return 0;
552 } /* End of parameter_proc() */
553
554
555 /***************************************************************************
556 * record_handler:
557 * Saves passed records to the output file.
558 ***************************************************************************/
559 static void
record_handler(char * record,int reclen,void * ptr)560 record_handler (char *record, int reclen, void *ptr)
561 {
562 if ( fwrite(record, reclen, 1, outfile) != 1 )
563 {
564 ms_log (2, "Cannot write to output file\n");
565 }
566 } /* End of record_handler() */
567
568
569 /***************************************************************************
570 * usage:
571 * Print the usage message and exit.
572 ***************************************************************************/
573 static void
usage(void)574 usage (void)
575 {
576 fprintf (stderr, "%s version: %s\n\n", PACKAGE, VERSION);
577 fprintf (stderr, "Usage: %s [options] -o outfile infile\n\n", PACKAGE);
578 fprintf (stderr,
579 " ## Options ##\n"
580 " -V Report program version\n"
581 " -h Show this usage message\n"
582 " -v Be more verbose, multiple flags can be used\n"
583 " -p Print details of input headers, multiple flags can be used\n"
584 " -a Autodetect every input record length, needed with mixed lengths\n"
585 " -r bytes Specify record length in bytes, required if no Blockette 1000\n"
586 " -e encoding Specify encoding format for input data samples\n"
587 " -i Pack data individually for each input record\n"
588 " -t Pack data from traces after reading all data\n"
589 " -R bytes Specify record length in bytes for packing\n"
590 " -E encoding Specify encoding format for packing\n"
591 " -b byteorder Specify byte order for packing, MSBF: 1, LSBF: 0\n"
592 " -N netcode Specify network code for output data\n"
593 "\n"
594 " -o outfile Specify the output file, required\n"
595 "\n"
596 " infile Input Mini-SEED file\n"
597 "\n"
598 "The default packing method is to use parameters from the input records\n"
599 "(reclen, encoding, byteorder, etc.) and pack records as soon as enough\n"
600 "samples are available. This method is a good balance between preservation\n"
601 "of blockettes, header values from input records and pack efficiency\n"
602 "compared to the other methods of packing, namely options -i and -t.\n"
603 "In most Mini-SEED repacking schemes some level of header information loss\n"
604 "or time shifting should be expected, especially in the case where the record\n"
605 "length is changed.\n"
606 "\n"
607 "Unless each input record is being packed individually, option -i, it is\n"
608 "not recommended to pack files containing records for different data streams.\n");
609 } /* End of usage() */
610
611
612 #ifndef WIN32
613 /***************************************************************************
614 * term_handler:
615 * Signal handler routine.
616 ***************************************************************************/
617 static void
term_handler(int sig)618 term_handler (int sig)
619 {
620 exit (0);
621 }
622 #endif
623