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