/********************************************************************* * ms_readtracelist_selection: * * This routine will open and read all Mini-SEED records in specified * file and populate a trace list. This routine is thread safe. * * If reclen is <= 0 the length of every record is automatically * detected. * * If a Selections list is supplied it will be used to limit which * records are added to the trace list. * * Returns MS_NOERROR and populates an MSTraceList struct at *ppmstl * on successful read, otherwise returns a libmseed error code (listed * in libmseed.h). *********************************************************************/ int ms_readtracelist_selection (MSTraceList **ppmstl, const char *msfile, int reclen, double timetol, double sampratetol, Selections *selections, flag dataquality, flag skipnotdata, flag dataflag, flag verbose) { MSRecord *msr = 0; MSFileParam *msfp = 0; int retcode; if (!ppmstl) return MS_GENERROR; /* Initialize MSTraceList if needed */ if (!*ppmstl) { *ppmstl = mstl_init (*ppmstl); if (!*ppmstl) return MS_GENERROR; } /* Loop over the input file */ while ((retcode = ms_readmsr_main (&msfp, &msr, msfile, reclen, NULL, NULL, skipnotdata, dataflag, NULL, verbose)) == MS_NOERROR) { /* Test against selections if supplied */ if (selections) { char srcname[50]; hptime_t endtime; msr_srcname (msr, srcname, 1); endtime = msr_endtime (msr); if (ms_matchselect (selections, srcname, msr->starttime, endtime, NULL) == NULL) { continue; } } /* Add to trace list */ mstl_addmsr (*ppmstl, msr, dataquality, 1, timetol, sampratetol); } /* Reset return code to MS_NOERROR on successful read by ms_readmsr() */ if (retcode == MS_ENDOFFILE) retcode = MS_NOERROR; ms_readmsr_main (&msfp, &msr, NULL, 0, NULL, NULL, 0, 0, NULL, 0); return retcode; } /* End of ms_readtracelist_selection() */
// Function that reads from a MiniSEED binary file from a char buffer and // returns a LinkedIDList. LinkedIDList * readMSEEDBuffer (char *mseed, int buflen, Selections *selections, flag unpack_data, int reclen, flag verbose, flag details, int header_byteorder, long (*allocData) (int, char), void (*diag_print) (char*), void (*log_print) (char*)) { int retcode = 0; int retval = 0; flag swapflag = 0; // current offset of mseed char pointer int offset = 0; // Unpack without reading the data first flag dataflag = 0; // the timing_qual of BLK 1001 uint8_t timing_qual = 0xFF; // the calibration type, availability of BLK 300, 310, 320, 390, 395 int8_t calibration_type = -1; // Init all the pointers to NULL. Most compilers should do this anyway. LinkedIDList * idListHead = NULL; LinkedIDList * idListCurrent = NULL; LinkedIDList * idListLast = NULL; MSRecord *msr = NULL; ContinuousSegment * segmentCurrent = NULL; hptime_t lastgap = 0; hptime_t hptimetol = 0; hptime_t nhptimetol = 0; long data_offset; LinkedRecordList *recordHead = NULL; LinkedRecordList *recordPrevious = NULL; LinkedRecordList *recordCurrent = NULL; int datasize; int record_count = 0; // A negative verbosity suppressed as much as possible. if (verbose < 0) { ms_loginit(&empty_print, NULL, &empty_print, NULL); } else { ms_loginit(log_print, "INFO: ", diag_print, "ERROR: "); } if (header_byteorder >= 0) { // Enforce little endian. if (header_byteorder == 0) { MS_UNPACKHEADERBYTEORDER(0); } // Enforce big endian. else { MS_UNPACKHEADERBYTEORDER(1); } } else { MS_UNPACKHEADERBYTEORDER(-1); } // // Read all records and save them in a linked list. // while (offset < buflen) { msr = msr_init(NULL); if ( msr == NULL ) { ms_log (2, "readMSEEDBuffer(): Error initializing msr\n"); return NULL; } if (verbose > 1) { ms_log(0, "readMSEEDBuffer(): calling msr_parse with " "mseed+offset=%d+%d, buflen=%d, reclen=%d, dataflag=%d, verbose=%d\n", mseed, offset, buflen, reclen, dataflag, verbose); } // If the record length is given, make sure at least that amount of data is available. if (reclen != -1) { if (offset + reclen > buflen) { ms_log(1, "readMSEEDBuffer(): Last reclen exceeds buflen, skipping.\n"); msr_free(&msr); break; } } // Otherwise assume the smallest possible record length and assure that enough // data is present. else { if (offset + 256 > buflen) { ms_log(1, "readMSEEDBuffer(): Last record only has %i byte(s) which " "is not enough to constitute a full SEED record. Corrupt data? " "Record will be skipped.\n", buflen - offset); msr_free(&msr); break; } } // Pass (buflen - offset) because msr_parse() expects only a single record. This // way libmseed can take care to not overstep bounds. retcode = msr_parse ( (mseed+offset), buflen - offset, &msr, reclen, dataflag, verbose); if (retcode != MS_NOERROR) { switch ( retcode ) { case MS_ENDOFFILE: ms_log(1, "readMSEEDBuffer(): Unexpected end of file when " "parsing record starting at offset %d. The rest " "of the file will not be read.\n", offset); break; case MS_GENERROR: ms_log(1, "readMSEEDBuffer(): Generic error when parsing " "record starting at offset %d. The rest of the " "file will not be read.\n", offset); break; case MS_NOTSEED: ms_log(1, "readMSEEDBuffer(): Record starting at offset " "%d is not valid SEED. The rest of the file " "will not be read.\n", offset); break; case MS_WRONGLENGTH: ms_log(1, "readMSEEDBuffer(): Length of data read was not " "correct when parsing record starting at " "offset %d. The rest of the file will not be " "read.\n", offset); break; case MS_OUTOFRANGE: ms_log(1, "readMSEEDBuffer(): SEED record length out of " "range for record starting at offset %d. The " "rest of the file will not be read.\n", offset); break; case MS_UNKNOWNFORMAT: ms_log(1, "readMSEEDBuffer(): Unknown data encoding " "format for record starting at offset %d. The " "rest of the file will not be read.\n", offset); break; case MS_STBADCOMPFLAG: ms_log(1, "readMSEEDBuffer(): Invalid STEIM compression " "flag(s) in record starting at offset %d. The " "rest of the file will not be read.\n", offset); break; default: ms_log(1, "readMSEEDBuffer(): Unknown error '%d' in " "record starting at offset %d. The rest of the " "file will not be read.\n", retcode, offset); break; } msr_free(&msr); break; } if (offset + msr->reclen > buflen) { ms_log(1, "readMSEEDBuffer(): Last msr->reclen exceeds buflen, skipping.\n"); msr_free(&msr); break; } // Test against selections if supplied if ( selections ) { char srcname[50]; hptime_t endtime; msr_srcname (msr, srcname, 1); endtime = msr_endtime (msr); if ( ms_matchselect (selections, srcname, msr->starttime, endtime, NULL) == NULL ) { // Add the record length for the next iteration offset += msr->reclen; // Free record. msr_free(&msr); continue; } } record_count += 1; recordCurrent = lrl_init (); // Append to linked record list if one exists. if ( recordHead != NULL ) { recordPrevious->next = recordCurrent; recordCurrent->previous = recordPrevious; recordCurrent->next = NULL; recordPrevious = recordCurrent; } // Otherwise create a new one. else { recordHead = recordCurrent; recordCurrent->previous = NULL; recordPrevious = recordCurrent; } recordCurrent->record = msr; // Determine the byte order swapflag only for the very first record. // The byte order should not change within the file. // XXX: Maybe check for every record? if (swapflag <= 0) { // Returns 0 if the host is little endian, otherwise 1. flag bigendianhost = ms_bigendianhost(); // Set the swapbyteflag if it is needed. if ( msr->Blkt1000 != 0) { /* If BE host and LE data need swapping */ if ( bigendianhost && msr->byteorder == 0 ) { swapflag = 1; } /* If LE host and BE data (or bad byte order value) need swapping */ if ( !bigendianhost && msr->byteorder > 0 ) { swapflag = 1; } } } // Actually unpack the data if the flag is not set. if (unpack_data != 0) { retval = msr_unpack_data (msr, swapflag, verbose); } if ( retval > 0 ) { msr->numsamples = retval; } // Add the record length for the next iteration offset += msr->reclen; } // Return empty id list if no records could be found. if (record_count == 0) { idListHead = lil_init(); return idListHead; } // All records that match the selection are now stored in a LinkedRecordList // that starts at recordHead. The next step is to sort them by matching ids // and then by time. recordCurrent = recordHead; while (recordCurrent != NULL) { // Check if the ID of the record is already available and if not create a // new one. // Start with the last id as it is most likely to be the correct one. idListCurrent = idListLast; while (idListCurrent != NULL) { if (strcmp(idListCurrent->network, recordCurrent->record->network) == 0 && strcmp(idListCurrent->station, recordCurrent->record->station) == 0 && strcmp(idListCurrent->location, recordCurrent->record->location) == 0 && strcmp(idListCurrent->channel, recordCurrent->record->channel) == 0 && idListCurrent->dataquality == recordCurrent->record->dataquality) { break; } else { idListCurrent = idListCurrent->previous; } } // Create a new id list if one is needed. if (idListCurrent == NULL) { idListCurrent = lil_init(); idListCurrent->previous = idListLast; if (idListLast != NULL) { idListLast->next = idListCurrent; } idListLast = idListCurrent; if (idListHead == NULL) { idListHead = idListCurrent; } // Set the IdList attributes. strcpy(idListCurrent->network, recordCurrent->record->network); strcpy(idListCurrent->station, recordCurrent->record->station); strcpy(idListCurrent->location, recordCurrent->record->location); strcpy(idListCurrent->channel, recordCurrent->record->channel); idListCurrent->dataquality = recordCurrent->record->dataquality; } // Now check if the current record fits exactly to the end of the last // segment of the current id. If not create a new segment. Therefore // if records with the same id are in wrong order a new segment will be // created. This is on purpose. segmentCurrent = idListCurrent->lastSegment; if (segmentCurrent != NULL) { hptimetol = (hptime_t) (0.5 * segmentCurrent->hpdelta); nhptimetol = ( hptimetol ) ? -hptimetol : 0; lastgap = recordCurrent->record->starttime - segmentCurrent->endtime - segmentCurrent->hpdelta; } if (details == 1) { /* extract information on calibration BLKs */ calibration_type = -1; if (recordCurrent->record->blkts) { BlktLink *cur_blkt = recordCurrent->record->blkts; while (cur_blkt) { switch (cur_blkt->blkt_type) { case 300: calibration_type = 1; break; case 310: calibration_type = 2; break; case 320: calibration_type = 3; break; case 390: calibration_type = 4; break; case 395: calibration_type = -2; break; default: break; } cur_blkt = cur_blkt->next; } } /* extract information based on timing quality */ timing_qual = 0xFF; if (recordCurrent->record->Blkt1001 != 0) { timing_qual = recordCurrent->record->Blkt1001->timing_qual; } } if ( segmentCurrent != NULL && segmentCurrent->sampletype == recordCurrent->record->sampletype && // Test the default sample rate tolerance: abs(1-sr1/sr2) < 0.0001 MS_ISRATETOLERABLE (segmentCurrent->samprate, recordCurrent->record->samprate) && // Check if the times are within the time tolerance lastgap <= hptimetol && lastgap >= nhptimetol && segmentCurrent->timing_qual == timing_qual && segmentCurrent->calibration_type == calibration_type) { recordCurrent->previous = segmentCurrent->lastRecord; segmentCurrent->lastRecord = segmentCurrent->lastRecord->next = recordCurrent; segmentCurrent->samplecnt += recordCurrent->record->samplecnt; segmentCurrent->endtime = msr_endtime(recordCurrent->record); } // Otherwise create a new segment and add the current record. else { segmentCurrent = seg_init(); segmentCurrent->previous = idListCurrent->lastSegment; if (idListCurrent->lastSegment != NULL) { idListCurrent->lastSegment->next = segmentCurrent; } else { idListCurrent->firstSegment = segmentCurrent; } idListCurrent->lastSegment = segmentCurrent; segmentCurrent->starttime = recordCurrent->record->starttime; segmentCurrent->endtime = msr_endtime(recordCurrent->record); segmentCurrent->samprate = recordCurrent->record->samprate; segmentCurrent->sampletype = recordCurrent->record->sampletype; segmentCurrent->samplecnt = recordCurrent->record->samplecnt; // Calculate high-precision sample period segmentCurrent->hpdelta = (hptime_t) (( recordCurrent->record->samprate ) ? (HPTMODULUS / recordCurrent->record->samprate) : 0.0); segmentCurrent->timing_qual = timing_qual; segmentCurrent->calibration_type = calibration_type; segmentCurrent->firstRecord = segmentCurrent->lastRecord = recordCurrent; recordCurrent->previous = NULL; } recordPrevious = recordCurrent->next; recordCurrent->next = NULL; recordCurrent = recordPrevious; } // Now loop over all segments, combine the records and free the msr // structures. idListCurrent = idListHead; while (idListCurrent != NULL) { segmentCurrent = idListCurrent->firstSegment; while (segmentCurrent != NULL) { if (segmentCurrent->datasamples) { free(segmentCurrent->datasamples); } // Allocate data via a callback function. if (unpack_data != 0) { segmentCurrent->datasamples = (void *) allocData(segmentCurrent->samplecnt, segmentCurrent->sampletype); } // Loop over all records, write the data to the buffer and free the msr structures. recordCurrent = segmentCurrent->firstRecord; data_offset = (long)(segmentCurrent->datasamples); while (recordCurrent != NULL) { datasize = recordCurrent->record->samplecnt * ms_samplesize(recordCurrent->record->sampletype); memcpy((void *)data_offset, recordCurrent->record->datasamples, datasize); // Free the record. msr_free(&(recordCurrent->record)); // Increase the data_offset and the record. data_offset += (long)datasize; recordCurrent = recordCurrent->next; } segmentCurrent = segmentCurrent->next; } idListCurrent = idListCurrent->next; } return idListHead; }
// Function that reads from a MiniSEED binary file from a char buffer and // returns a LinkedIDList. LinkedIDList * readMSEEDBuffer (char *mseed, int buflen, Selections *selections, flag unpack_data, int reclen, flag verbose, flag details, int header_byteorder, long long (*allocData) (int, char), void (*diag_print) (char*), void (*log_print) (char*)) { int retcode = 0; int retval = 0; flag swapflag = 0; flag bigendianhost = ms_bigendianhost(); // current offset of mseed char pointer int offset = 0; // Unpack without reading the data first flag dataflag = 0; // the timing_qual of BLK 1001 uint8_t timing_qual = 0xFF; // the calibration type, availability of BLK 300, 310, 320, 390, 395 int8_t calibration_type = -1; // Init all the pointers to NULL. Most compilers should do this anyway. LinkedIDList * idListHead = NULL; LinkedIDList * idListCurrent = NULL; LinkedIDList * idListLast = NULL; MSRecord *msr = NULL; ContinuousSegment * segmentCurrent = NULL; hptime_t lastgap = 0; hptime_t hptimetol = 0; hptime_t nhptimetol = 0; long long data_offset; LinkedRecordList *recordHead = NULL; LinkedRecordList *recordPrevious = NULL; LinkedRecordList *recordCurrent = NULL; int datasize; int record_count = 0; // A negative verbosity suppresses as much as possible. if (verbose < 0) { ms_loginit(&empty_print, NULL, &empty_print, NULL); } else { ms_loginit(log_print, "INFO: ", diag_print, "ERROR: "); } if (header_byteorder >= 0) { // Enforce little endian. if (header_byteorder == 0) { MS_UNPACKHEADERBYTEORDER(0); } // Enforce big endian. else { MS_UNPACKHEADERBYTEORDER(1); } } else { MS_UNPACKHEADERBYTEORDER(-1); } // Read all records and save them in a linked list. while (offset < buflen) { msr = msr_init(NULL); if ( msr == NULL ) { ms_log (2, "readMSEEDBuffer(): Error initializing msr\n"); return NULL; } if (verbose > 1) { ms_log(0, "readMSEEDBuffer(): calling msr_parse with " "mseed+offset=%d+%d, buflen=%d, reclen=%d, dataflag=%d, verbose=%d\n", mseed, offset, buflen, reclen, dataflag, verbose); } // If the record length is given, make sure at least that amount of data is available. if (reclen != -1) { if (offset + reclen > buflen) { ms_log(1, "readMSEEDBuffer(): Last reclen exceeds buflen, skipping.\n"); msr_free(&msr); break; } } // Otherwise assume the smallest possible record length and assure that enough // data is present. else { if (offset + MINRECLEN > buflen) { ms_log(1, "readMSEEDBuffer(): Last record only has %i byte(s) which " "is not enough to constitute a full SEED record. Corrupt data? " "Record will be skipped.\n", buflen - offset); msr_free(&msr); break; } } // Skip empty or noise records. if (OBSPY_ISVALIDBLANK(mseed + offset)) { offset += MINRECLEN; continue; } // Pass (buflen - offset) because msr_parse() expects only a single record. This // way libmseed can take care to not overstep bounds. // Return values: // 0 : Success, populates the supplied MSRecord. // >0 : Data record detected but not enough data is present, the // return value is a hint of how many more bytes are needed. // <0 : libmseed error code (listed in libmseed.h) is returned. retcode = msr_parse ((mseed+offset), buflen - offset, &msr, reclen, dataflag, verbose); // Handle error. if (retcode < 0) { log_error(retcode, offset); msr_free(&msr); break; } // msr_parse() returns > 0 if a data record has been detected but the buffer either has not enough // data (this cannot happen with ObsPy's logic) or the last record has no Blockette 1000 and it cannot // determine the record length because there is no next record (this can happen in ObsPy) - handle that // case by just calling msr_parse() with an explicit record length set. else if ( retcode > 0 && retcode < (buflen - offset)) { // Check if the remaining bytes can exactly make up a record length. int r_bytes = buflen - offset; float exp = log10((float)r_bytes) / log10(2.0); if ((fmodf(exp, 1.0) < 0.0000001) && ((int)roundf_(exp) >= 7) && ((int)roundf_(exp) <= 256)) { retcode = msr_parse((mseed + offset), buflen - offset, &msr, r_bytes, dataflag, verbose); if ( retcode != 0 ) { log_error(retcode, offset); msr_free(&msr); break; } } else { msr_free(&msr); break; } } if (offset + msr->reclen > buflen) { ms_log(1, "readMSEEDBuffer(): Last msr->reclen exceeds buflen, skipping.\n"); msr_free(&msr); break; } // Test against selections if supplied if ( selections ) { char srcname[50]; hptime_t endtime; msr_srcname (msr, srcname, 1); endtime = msr_endtime (msr); if ( ms_matchselect (selections, srcname, msr->starttime, endtime, NULL) == NULL ) { // Add the record length for the next iteration offset += msr->reclen; // Free record. msr_free(&msr); continue; } } record_count += 1; recordCurrent = lrl_init (); // Append to linked record list if one exists. if ( recordHead != NULL ) { recordPrevious->next = recordCurrent; recordCurrent->previous = recordPrevious; recordCurrent->next = NULL; recordPrevious = recordCurrent; } // Otherwise create a new one. else { recordHead = recordCurrent; recordCurrent->previous = NULL; recordPrevious = recordCurrent; } recordCurrent->record = msr; // Figure out if the byte-order of the data has to be swapped. swapflag = 0; // If blockette 1000 is present, use it. if ( msr->Blkt1000 != 0) { /* If BE host and LE data need swapping */ if ( bigendianhost && msr->byteorder == 0 ) { swapflag = 1; } /* If LE host and BE data (or bad byte order value) need swapping */ if ( !bigendianhost && msr->byteorder > 0 ) { swapflag = 1; } } // Otherwise assume the data has the same byte order as the header. // This needs to be done on the raw header bytes as libmseed only returns // header fields in the native byte order. else { unsigned char* _t = (unsigned char*)mseed + offset + 20; unsigned int year = _t[0] | _t[1] << 8; unsigned int day = _t[2] | _t[3] << 8; // Swap data if header needs to be swapped. if (!MS_ISVALIDYEARDAY(year, day)) { swapflag = 1; } } // Actually unpack the data if the flag is not set and if the data // offset is valid. if ((unpack_data != 0) && (msr->fsdh->data_offset >= 48) && (msr->fsdh->data_offset < msr->reclen) && (msr->samplecnt > 0)) { retval = msr_unpack_data (msr, swapflag, verbose); } if ( retval > 0 ) { msr->numsamples = retval; } if ( msr->fsdh->start_time.fract > 9999 ) { ms_log(1, "readMSEEDBuffer(): Record with offset=%d has a " "fractional second (.0001 seconds) of %d. This is not " "strictly valid but will be interpreted as one or more " "additional seconds.", offset, msr->fsdh->start_time.fract); } // Add the record length for the next iteration offset += msr->reclen; } // Return empty id list if no records could be found. if (record_count == 0) { idListHead = lil_init(); return idListHead; } // All records that match the selection are now stored in a LinkedRecordList // that starts at recordHead. The next step is to sort them by matching ids // and then by time. recordCurrent = recordHead; while (recordCurrent != NULL) { // Check if the ID of the record is already available and if not create a // new one. // Start with the last id as it is most likely to be the correct one. idListCurrent = idListLast; while (idListCurrent != NULL) { if (strcmp(idListCurrent->network, recordCurrent->record->network) == 0 && strcmp(idListCurrent->station, recordCurrent->record->station) == 0 && strcmp(idListCurrent->location, recordCurrent->record->location) == 0 && strcmp(idListCurrent->channel, recordCurrent->record->channel) == 0 && idListCurrent->dataquality == recordCurrent->record->dataquality) { break; } else { idListCurrent = idListCurrent->previous; } } // Create a new id list if one is needed. if (idListCurrent == NULL) { idListCurrent = lil_init(); idListCurrent->previous = idListLast; if (idListLast != NULL) { idListLast->next = idListCurrent; } idListLast = idListCurrent; if (idListHead == NULL) { idListHead = idListCurrent; } // Set the IdList attributes. strcpy(idListCurrent->network, recordCurrent->record->network); strcpy(idListCurrent->station, recordCurrent->record->station); strcpy(idListCurrent->location, recordCurrent->record->location); strcpy(idListCurrent->channel, recordCurrent->record->channel); idListCurrent->dataquality = recordCurrent->record->dataquality; } // Now check if the current record fits exactly to the end of the last // segment of the current id. If not create a new segment. Therefore // if records with the same id are in wrong order a new segment will be // created. This is on purpose. segmentCurrent = idListCurrent->lastSegment; if (segmentCurrent != NULL) { hptimetol = (hptime_t) (0.5 * segmentCurrent->hpdelta); nhptimetol = ( hptimetol ) ? -hptimetol : 0; lastgap = recordCurrent->record->starttime - segmentCurrent->endtime - segmentCurrent->hpdelta; } if (details == 1) { /* extract information on calibration BLKs */ calibration_type = -1; if (recordCurrent->record->blkts) { BlktLink *cur_blkt = recordCurrent->record->blkts; while (cur_blkt) { switch (cur_blkt->blkt_type) { case 300: calibration_type = 1; break; case 310: calibration_type = 2; break; case 320: calibration_type = 3; break; case 390: calibration_type = 4; break; case 395: calibration_type = -2; break; default: break; } cur_blkt = cur_blkt->next; } } /* extract information based on timing quality */ timing_qual = 0xFF; if (recordCurrent->record->Blkt1001 != 0) { timing_qual = recordCurrent->record->Blkt1001->timing_qual; } } if ( segmentCurrent != NULL && // This is important for zero data record coupled with not unpacking // the data. It needs to be split in two places: Before the zero data // record and after it. recordCurrent->record->samplecnt > 0 && segmentCurrent->samplecnt > 0 && segmentCurrent->sampletype == recordCurrent->record->sampletype && // Test the default sample rate tolerance: abs(1-sr1/sr2) < 0.0001 MS_ISRATETOLERABLE (segmentCurrent->samprate, recordCurrent->record->samprate) && // Check if the times are within the time tolerance lastgap <= hptimetol && lastgap >= nhptimetol && segmentCurrent->timing_qual == timing_qual && segmentCurrent->calibration_type == calibration_type) { recordCurrent->previous = segmentCurrent->lastRecord; segmentCurrent->lastRecord = segmentCurrent->lastRecord->next = recordCurrent; segmentCurrent->samplecnt += recordCurrent->record->samplecnt; segmentCurrent->endtime = msr_endtime(recordCurrent->record); } // Otherwise create a new segment and add the current record. else { segmentCurrent = seg_init(); segmentCurrent->previous = idListCurrent->lastSegment; if (idListCurrent->lastSegment != NULL) { idListCurrent->lastSegment->next = segmentCurrent; } else { idListCurrent->firstSegment = segmentCurrent; } idListCurrent->lastSegment = segmentCurrent; segmentCurrent->starttime = recordCurrent->record->starttime; segmentCurrent->endtime = msr_endtime(recordCurrent->record); segmentCurrent->samprate = recordCurrent->record->samprate; segmentCurrent->sampletype = recordCurrent->record->sampletype; segmentCurrent->samplecnt = recordCurrent->record->samplecnt; // Calculate high-precision sample period segmentCurrent->hpdelta = (hptime_t) (( recordCurrent->record->samprate ) ? (HPTMODULUS / recordCurrent->record->samprate) : 0.0); segmentCurrent->timing_qual = timing_qual; segmentCurrent->calibration_type = calibration_type; segmentCurrent->firstRecord = segmentCurrent->lastRecord = recordCurrent; recordCurrent->previous = NULL; } recordPrevious = recordCurrent->next; recordCurrent->next = NULL; recordCurrent = recordPrevious; } // Now loop over all segments, combine the records and free the msr // structures. idListCurrent = idListHead; while (idListCurrent != NULL) { segmentCurrent = idListCurrent->firstSegment; while (segmentCurrent != NULL) { if (segmentCurrent->datasamples) { free(segmentCurrent->datasamples); } // Allocate data via a callback function. if (unpack_data != 0) { segmentCurrent->datasamples = (void *) allocData(segmentCurrent->samplecnt, segmentCurrent->sampletype); } // Loop over all records, write the data to the buffer and free the msr structures. recordCurrent = segmentCurrent->firstRecord; data_offset = (long long)(segmentCurrent->datasamples); while (recordCurrent != NULL) { datasize = recordCurrent->record->samplecnt * ms_samplesize(recordCurrent->record->sampletype); memcpy((void *)data_offset, recordCurrent->record->datasamples, datasize); // Free the record. msr_free(&(recordCurrent->record)); // Increase the data_offset and the record. data_offset += (long long)datasize; recordCurrent = recordCurrent->next; } segmentCurrent = segmentCurrent->next; } idListCurrent = idListCurrent->next; } return idListHead; }
/********************************************************************** * ms_readmsr_main: * * This routine will open and read, with subsequent calls, all * Mini-SEED records in specified file. * * All static file reading parameters are stored in a MSFileParam * struct and returned (via a pointer to a pointer) for the calling * routine to use in subsequent calls. A MSFileParam struct will be * allocated if necessary. This routine is thread safe and can be * used to read multiple files in parallel as long as the file reading * parameters are managed appropriately. * * If reclen is 0 or negative the length of every record is * automatically detected. For auto detection of record length the * record must include a 1000 blockette or be followed by a valid * record header or end of file. * * If *fpos is not NULL it will be updated to reflect the file * position (offset from the beginning in bytes) from where the * returned record was read. As a special case, if *fpos is not NULL * and the value it points to is less than 0 this will be interpreted * as a (positive) starting offset from which to begin reading data; * this feature does not work with packed files. * * If *last is not NULL it will be set to 1 when the last record in * the file is being returned, otherwise it will be 0. * * If the skipnotdata flag is true any data chunks read that do not * have valid data record indicators (D, R, Q, M, etc.) will be skipped. * * dataflag will be passed directly to msr_unpack(). * * If a Selections list is supplied it will be used to determine when * a section of data in a packed file may be skipped, packed files are * internal to the IRIS DMC. * * After reading all the records in a file the controlling program * should call it one last time with msfile set to NULL. This will * close the file and free allocated memory. * * Returns MS_NOERROR and populates an MSRecord struct at *ppmsr on * successful read, returns MS_ENDOFFILE on EOF, otherwise returns a * libmseed error code (listed in libmseed.h) and *ppmsr is set to * NULL. *********************************************************************/ int ms_readmsr_main (MSFileParam **ppmsfp, MSRecord **ppmsr, const char *msfile, int reclen, off_t *fpos, int *last, flag skipnotdata, flag dataflag, Selections *selections, flag verbose) { MSFileParam *msfp; off_t packdatasize = 0; int packskipsize; int parseval = 0; int readsize = 0; int readcount = 0; int retcode = MS_NOERROR; if (!ppmsr) return MS_GENERROR; if (!ppmsfp) return MS_GENERROR; msfp = *ppmsfp; /* Initialize the file read parameters if needed */ if (!msfp) { msfp = (MSFileParam *)malloc (sizeof (MSFileParam)); if (msfp == NULL) { ms_log (2, "ms_readmsr_main(): Cannot allocate memory for MSFP\n"); return MS_GENERROR; } /* Redirect the supplied pointer to the allocated params */ *ppmsfp = msfp; msfp->fp = NULL; msfp->filename[0] = '\0'; msfp->rawrec = NULL; msfp->readlen = 0; msfp->readoffset = 0; msfp->packtype = 0; msfp->packhdroffset = 0; msfp->filepos = 0; msfp->filesize = 0; msfp->recordcount = 0; } /* When cleanup is requested */ if (msfile == NULL) { msr_free (ppmsr); if (msfp->fp != NULL) fclose (msfp->fp); if (msfp->rawrec != NULL) free (msfp->rawrec); /* If the file parameters are the global parameters reset them */ if (*ppmsfp == &gMSFileParam) { gMSFileParam.fp = NULL; gMSFileParam.filename[0] = '\0'; gMSFileParam.rawrec = NULL; gMSFileParam.readlen = 0; gMSFileParam.readoffset = 0; gMSFileParam.packtype = 0; gMSFileParam.packhdroffset = 0; gMSFileParam.filepos = 0; gMSFileParam.filesize = 0; gMSFileParam.recordcount = 0; } /* Otherwise free the MSFileParam */ else { free (*ppmsfp); *ppmsfp = NULL; } return MS_NOERROR; } /* Allocate reading buffer */ if (msfp->rawrec == NULL) { if (!(msfp->rawrec = (char *)malloc (MAXRECLEN))) { ms_log (2, "ms_readmsr_main(): Cannot allocate memory for read buffer\n"); return MS_GENERROR; } } /* Sanity check: track if we are reading the same file */ if (msfp->fp && strncmp (msfile, msfp->filename, sizeof (msfp->filename))) { ms_log (2, "ms_readmsr_main() called with a different file name without being reset\n"); /* Close previous file and reset needed variables */ if (msfp->fp != NULL) fclose (msfp->fp); msfp->fp = NULL; msfp->readlen = 0; msfp->readoffset = 0; msfp->packtype = 0; msfp->packhdroffset = 0; msfp->filepos = 0; msfp->filesize = 0; msfp->recordcount = 0; } /* Open the file if needed, redirect to stdin if file is "-" */ if (msfp->fp == NULL) { /* Store the filename for tracking */ strncpy (msfp->filename, msfile, sizeof (msfp->filename) - 1); msfp->filename[sizeof (msfp->filename) - 1] = '\0'; if (strcmp (msfile, "-") == 0) { msfp->fp = stdin; } else { if ((msfp->fp = fopen (msfile, "rb")) == NULL) { ms_log (2, "Cannot open file: %s (%s)\n", msfile, strerror (errno)); msr_free (ppmsr); return MS_GENERROR; } else { /* Determine file size */ struct stat sbuf; if (fstat (fileno (msfp->fp), &sbuf)) { ms_log (2, "Cannot open file: %s (%s)\n", msfile, strerror (errno)); msr_free (ppmsr); return MS_GENERROR; } msfp->filesize = sbuf.st_size; } } } /* Seek to a specified offset if requested */ if (fpos != NULL && *fpos < 0) { /* Only try to seek in real files, not stdin */ if (msfp->fp != stdin) { if (lmp_fseeko (msfp->fp, *fpos * -1, SEEK_SET)) { ms_log (2, "Cannot seek in file: %s (%s)\n", msfile, strerror (errno)); return MS_GENERROR; } msfp->filepos = *fpos * -1; msfp->readlen = 0; msfp->readoffset = 0; } } /* Zero the last record indicator */ if (last) *last = 0; /* Read data and search for records */ for (;;) { /* Read more data into buffer if not at EOF and buffer has less than MINRECLEN * or more data is needed for the current record detected in buffer. */ if (!feof (msfp->fp) && (MSFPBUFLEN (msfp) < MINRECLEN || parseval > 0)) { /* Reset offsets if no unprocessed data in buffer */ if (MSFPBUFLEN (msfp) <= 0) { msfp->readlen = 0; msfp->readoffset = 0; } /* Otherwise shift existing data to beginning of buffer */ else if (msfp->readoffset > 0) { ms_shift_msfp (msfp, msfp->readoffset); } /* Determine read size */ readsize = (MAXRECLEN - msfp->readlen); /* Read data into record buffer */ readcount = ms_fread (msfp->rawrec + msfp->readlen, 1, readsize, msfp->fp); if (readcount != readsize) { if (!feof (msfp->fp)) { ms_log (2, "Short read of %d bytes starting from %" PRId64 "\n", readsize, msfp->filepos); retcode = MS_GENERROR; break; } } /* Update read buffer length */ msfp->readlen += readcount; /* File position corresponding to start of buffer; not strictly necessary */ if (msfp->fp != stdin) msfp->filepos = lmp_ftello (msfp->fp) - msfp->readlen; } /* Test for packed file signature at the beginning of the file */ if (msfp->filepos == 0 && *(MSFPREADPTR (msfp)) == 'P' && MSFPBUFLEN (msfp) >= 48) { msfp->packtype = 0; /* Determine pack type, the negative pack type indicates initial header */ if (!memcmp ("PED", MSFPREADPTR (msfp), 3)) msfp->packtype = -1; else if (!memcmp ("PSD", MSFPREADPTR (msfp), 3)) msfp->packtype = -2; else if (!memcmp ("PLC", MSFPREADPTR (msfp), 3)) msfp->packtype = -6; else if (!memcmp ("PQI", MSFPREADPTR (msfp), 3)) msfp->packtype = -7; else if (!memcmp ("PLS", MSFPREADPTR (msfp), 3)) msfp->packtype = -8; if (verbose > 0) ms_log (1, "Detected packed file (%3.3s: type %d)\n", MSFPREADPTR (msfp), -msfp->packtype); } /* Read pack headers, initial and subsequent headers including (ignored) chksum values */ if (msfp->packtype && (msfp->packtype < 0 || msfp->filepos == msfp->packhdroffset) && MSFPBUFLEN (msfp) >= 48) { char hdrstr[30]; int64_t datasize; /* Determine bytes to skip before header: either initial ID block or type-specific chksum block */ packskipsize = (msfp->packtype < 0) ? 10 : packtypes[msfp->packtype][2]; if (msfp->packtype < 0) msfp->packtype = -msfp->packtype; /* Read pack length from pack header accounting for bytes that should be skipped */ memset (hdrstr, 0, sizeof (hdrstr)); memcpy (hdrstr, MSFPREADPTR (msfp) + (packtypes[msfp->packtype][0] + packskipsize - packtypes[msfp->packtype][1]), packtypes[msfp->packtype][1]); sscanf (hdrstr, " %" SCNd64, &datasize); packdatasize = (off_t)datasize; /* Next pack header = File position + skipsize + header size + data size * This offset is actually to the data block chksum which is skipped by the logic above, * the next pack header should directly follow the chksum. */ msfp->packhdroffset = msfp->filepos + packskipsize + packtypes[msfp->packtype][0] + packdatasize; if (verbose > 1) ms_log (1, "Read packed file header at offset %" PRId64 " (%d bytes follow), chksum offset: %" PRId64 "\n", (msfp->filepos + packskipsize), packdatasize, msfp->packhdroffset); /* Shift buffer to new reading offset (aligns records in buffer) */ ms_shift_msfp (msfp, msfp->readoffset + (packskipsize + packtypes[msfp->packtype][0])); } /* End of packed header processing */ /* Check for match if selections are supplied and pack header was read, */ /* only when enough data is in buffer and not reading from stdin pipe */ if (selections && msfp->packtype && packdatasize && MSFPBUFLEN (msfp) >= 48 && msfp->fp != stdin) { char srcname[100]; ms_recsrcname (MSFPREADPTR (msfp), srcname, 1); if (!ms_matchselect (selections, srcname, HPTERROR, HPTERROR, NULL)) { /* Update read position if next section is in buffer */ if (MSFPBUFLEN (msfp) >= (msfp->packhdroffset - msfp->filepos)) { if (verbose > 1) { ms_log (1, "Skipping (jump) packed section for %s (%d bytes) starting at offset %" PRId64 "\n", srcname, (msfp->packhdroffset - msfp->filepos), msfp->filepos); } msfp->readoffset += (msfp->packhdroffset - msfp->filepos); msfp->filepos = msfp->packhdroffset; packdatasize = 0; } /* Otherwise seek to next pack header and reset reading position */ else { if (verbose > 1) { ms_log (1, "Skipping (seek) packed section for %s (%d bytes) starting at offset %" PRId64 "\n", srcname, (msfp->packhdroffset - msfp->filepos), msfp->filepos); } if (lmp_fseeko (msfp->fp, msfp->packhdroffset, SEEK_SET)) { ms_log (2, "Cannot seek in file: %s (%s)\n", msfile, strerror (errno)); return MS_GENERROR; break; } msfp->filepos = msfp->packhdroffset; msfp->readlen = 0; msfp->readoffset = 0; packdatasize = 0; } /* Return to top of loop for proper pack header handling */ continue; } } /* End of selection processing */ /* Attempt to parse record from buffer */ if (MSFPBUFLEN (msfp) >= MINRECLEN) { int parselen = MSFPBUFLEN (msfp); /* Limit the parse length to offset of pack header if present in the buffer */ if (msfp->packhdroffset && msfp->packhdroffset < (msfp->filepos + MSFPBUFLEN (msfp))) parselen = msfp->packhdroffset - msfp->filepos; parseval = msr_parse (MSFPREADPTR (msfp), parselen, ppmsr, reclen, dataflag, verbose); /* Record detected and parsed */ if (parseval == 0) { if (verbose > 1) ms_log (1, "Read record length of %d bytes\n", (*ppmsr)->reclen); /* Test if this is the last record if file size is known (not pipe) */ if (last && msfp->filesize) if ((msfp->filesize - (msfp->filepos + (*ppmsr)->reclen)) < MINRECLEN) *last = 1; /* Return file position for this record */ if (fpos) *fpos = msfp->filepos; /* Update reading offset, file position and record count */ msfp->readoffset += (*ppmsr)->reclen; msfp->filepos += (*ppmsr)->reclen; msfp->recordcount++; retcode = MS_NOERROR; break; } else if (parseval < 0) { /* Skip non-data if requested */ if (skipnotdata) { if (verbose > 1) { if (MS_ISVALIDBLANK ((char *)MSFPREADPTR (msfp))) ms_log (1, "Skipped %d bytes of blank/noise record at byte offset %" PRId64 "\n", MINRECLEN, msfp->filepos); else ms_log (1, "Skipped %d bytes of non-data record at byte offset %" PRId64 "\n", MINRECLEN, msfp->filepos); } /* Skip MINRECLEN bytes, update reading offset and file position */ msfp->readoffset += MINRECLEN; msfp->filepos += MINRECLEN; } /* Parsing errors */ else { ms_log (2, "Cannot detect record at byte offset %" PRId64 ": %s\n", msfp->filepos, msfile); /* Print common errors and raw details if verbose */ ms_parse_raw (MSFPREADPTR (msfp), MSFPBUFLEN (msfp), verbose, -1); retcode = parseval; break; } } else /* parseval > 0 (found record but need more data) */ { /* Determine implied record length if needed */ int32_t impreclen = reclen; /* Check for parse hints that are larger than MAXRECLEN */ if ((MSFPBUFLEN (msfp) + parseval) > MAXRECLEN) { if (skipnotdata) { /* Skip MINRECLEN bytes, update reading offset and file position */ msfp->readoffset += MINRECLEN; msfp->filepos += MINRECLEN; } else { retcode = MS_OUTOFRANGE; break; } } /* Pack header check, if pack header offset is within buffer */ else if (impreclen <= 0 && msfp->packhdroffset && msfp->packhdroffset < (msfp->filepos + MSFPBUFLEN (msfp))) { impreclen = msfp->packhdroffset - msfp->filepos; /* Check that record length is within range and a power of 2. * Power of two if (X & (X - 1)) == 0 */ if (impreclen >= MINRECLEN && impreclen <= MAXRECLEN && (impreclen & (impreclen - 1)) == 0) { /* Set the record length implied by the next pack header */ reclen = impreclen; } else { ms_log (1, "Implied record length (%d) is invalid\n", impreclen); retcode = MS_NOTSEED; break; } } /* End of file check */ else if (impreclen <= 0 && feof (msfp->fp)) { impreclen = msfp->filesize - msfp->filepos; /* Check that record length is within range and a power of 2. * Power of two if (X & (X - 1)) == 0 */ if (impreclen >= MINRECLEN && impreclen <= MAXRECLEN && (impreclen & (impreclen - 1)) == 0) { /* Set the record length implied by the end of the file */ reclen = impreclen; } /* Otherwise a trucated record */ else { if (verbose) { if (msfp->filesize) ms_log (1, "Truncated record at byte offset %" PRId64 ", filesize %d: %s\n", msfp->filepos, msfp->filesize, msfile); else ms_log (1, "Truncated record at byte offset %" PRId64 "\n", msfp->filepos); } retcode = MS_ENDOFFILE; break; } } } } /* End of record detection */ /* Finished when within MINRECLEN from EOF and buffer less than MINRECLEN */ if ((msfp->filesize - msfp->filepos) < MINRECLEN && MSFPBUFLEN (msfp) < MINRECLEN) { if (msfp->recordcount == 0 && msfp->packtype == 0) { if (verbose > 0) ms_log (2, "%s: No data records read, not SEED?\n", msfile); retcode = MS_NOTSEED; } else { retcode = MS_ENDOFFILE; } break; } } /* End of reading, record detection and parsing loop */ /* Cleanup target MSRecord if returning an error */ if (retcode != MS_NOERROR) { msr_free (ppmsr); } return retcode; } /* End of ms_readmsr_main() */
// Function that reads from a MiniSEED binary file from a char buffer and // returns a LinkedIDList. LinkedIDList * readMSEEDBuffer (char *mseed, int buflen, Selections *selections, flag unpack_data, int reclen, flag verbose, flag details, long (*allocData) (int, char)) { int retcode = 0; int retval = 0; flag swapflag = 0; // current offset of mseed char pointer int offset = 0; // Unpack without reading the data first flag dataflag = 0; // the timing_qual of BLK 1001 uint8_t timing_qual = 0xFF; // the calibration type, availability of BLK 300, 310, 320, 390, 395 int8_t calibration_type = -1; // Init all the pointers to NULL. Most compilers should do this anyway. LinkedIDList * idListHead = NULL; LinkedIDList * idListCurrent = NULL; LinkedIDList * idListLast = NULL; MSRecord *msr = NULL; ContinuousSegment * segmentCurrent = NULL; hptime_t lastgap; hptime_t hptimetol; hptime_t nhptimetol; long data_offset; LinkedRecordList *recordHead = NULL; LinkedRecordList *recordPrevious = NULL; LinkedRecordList *recordCurrent = NULL; int datasize; // // Read all records and save them in a linked list. // int record_count = 0; while (offset < buflen) { msr = msr_init(NULL); retcode = msr_parse ( (mseed+offset), buflen, &msr, reclen, dataflag, verbose); if ( ! (retcode == MS_NOERROR)) { msr_free(&msr); break; } // Test against selections if supplied if ( selections ) { char srcname[50]; hptime_t endtime; msr_srcname (msr, srcname, 1); endtime = msr_endtime (msr); if ( ms_matchselect (selections, srcname, msr->starttime, endtime, NULL) == NULL ) { // Add the record length for the next iteration offset += msr->reclen; // Free record. msr_free(&msr); continue; } } record_count += 1; recordCurrent = lrl_init (); // Append to linked record list if one exists. if ( recordHead != NULL ) { recordPrevious->next = recordCurrent; recordCurrent->previous = recordPrevious; recordCurrent->next = NULL; recordPrevious = recordCurrent; } // Otherwise create a new one. else { recordHead = recordCurrent; recordCurrent->previous = NULL; recordPrevious = recordCurrent; } recordCurrent->record = msr; // Determine the byteorder swapflag only for the very first record. The byteorder // should not change within the file. // XXX: Maybe check for every record? if (swapflag <= 0) { // Returns 0 if the host is little endian, otherwise 1. flag bigendianhost = ms_bigendianhost(); // Set the swapbyteflag if it is needed. if ( msr->Blkt1000 != 0) { /* If BE host and LE data need swapping */ if ( bigendianhost && msr->byteorder == 0 ) { swapflag = 1; } /* If LE host and BE data (or bad byte order value) need swapping */ if ( !bigendianhost && msr->byteorder > 0 ) { swapflag = 1; } } } // Actually unpack the data if the flag is not set. if (unpack_data != 0) { retval = msr_unpack_data (msr, swapflag, verbose); } if ( retval > 0 ) { msr->numsamples = retval; } // Add the record length for the next iteration offset += msr->reclen; } // Return empty id list if no records could be found. if (record_count == 0) { idListHead = lil_init(); return idListHead; } // All records that match the selection are now stored in a LinkedRecordList // that starts at recordHead. The next step is to sort them by matching ids // and then by time. recordCurrent = recordHead; while (recordCurrent != NULL) { // Check if the ID of the record is already available and if not create a // new one. // Start with the last id as it is most likely to be the correct one. idListCurrent = idListLast; while (idListCurrent != NULL) { if (strcmp(idListCurrent->network, recordCurrent->record->network) == 0 && strcmp(idListCurrent->station, recordCurrent->record->station) == 0 && strcmp(idListCurrent->location, recordCurrent->record->location) == 0 && strcmp(idListCurrent->channel, recordCurrent->record->channel) == 0 && idListCurrent->dataquality == recordCurrent->record->dataquality) { break; } else { idListCurrent = idListCurrent->previous; } } // Create a new id list if one is needed. if (idListCurrent == NULL) { idListCurrent = lil_init(); idListCurrent->previous = idListLast; if (idListLast != NULL) { idListLast->next = idListCurrent; } idListLast = idListCurrent; if (idListHead == NULL) { idListHead = idListCurrent; } // Set the IdList attributes. strcpy(idListCurrent->network, recordCurrent->record->network); strcpy(idListCurrent->station, recordCurrent->record->station); strcpy(idListCurrent->location, recordCurrent->record->location); strcpy(idListCurrent->channel, recordCurrent->record->channel); idListCurrent->dataquality = recordCurrent->record->dataquality; } // Now check if the current record fits exactly to the end of the last // segment of the current id. If not create a new segment. Therefore // if records with the same id are in wrong order a new segment will be // created. This is on purpose. segmentCurrent = idListCurrent->lastSegment; if (segmentCurrent != NULL) { hptimetol = (hptime_t) (0.5 * segmentCurrent->hpdelta); nhptimetol = ( hptimetol ) ? -hptimetol : 0; lastgap = recordCurrent->record->starttime - segmentCurrent->endtime - segmentCurrent->hpdelta; } if (details == 1) { /* extract information on calibration BLKs */ calibration_type = -1; if (recordCurrent->record->blkts) { BlktLink *cur_blkt = recordCurrent->record->blkts; while (cur_blkt) { switch (cur_blkt->blkt_type) { case 300: calibration_type = 1; break; case 310: calibration_type = 2; break; case 320: calibration_type = 3; break; case 390: calibration_type = 4; break; case 395: calibration_type = -2; break; default: break; } cur_blkt = cur_blkt->next; } } /* extract information based on timing quality */ timing_qual = 0xFF; if (recordCurrent->record->Blkt1001 != 0) { timing_qual = recordCurrent->record->Blkt1001->timing_qual; } } if ( segmentCurrent != NULL && segmentCurrent->sampletype == recordCurrent->record->sampletype && // Test the default sample rate tolerance: abs(1-sr1/sr2) < 0.0001 MS_ISRATETOLERABLE (segmentCurrent->samprate, recordCurrent->record->samprate) && // Check if the times are within the time tolerance lastgap <= hptimetol && lastgap >= nhptimetol && segmentCurrent->timing_qual == timing_qual && segmentCurrent->calibration_type == calibration_type) { recordCurrent->previous = segmentCurrent->lastRecord; segmentCurrent->lastRecord = segmentCurrent->lastRecord->next = recordCurrent; segmentCurrent->samplecnt += recordCurrent->record->samplecnt; segmentCurrent->endtime = msr_endtime(recordCurrent->record); } // Otherwise create a new segment and add the current record. else { segmentCurrent = seg_init(); segmentCurrent->previous = idListCurrent->lastSegment; if (idListCurrent->lastSegment != NULL) { idListCurrent->lastSegment->next = segmentCurrent; } else { idListCurrent->firstSegment = segmentCurrent; } idListCurrent->lastSegment = segmentCurrent; segmentCurrent->starttime = recordCurrent->record->starttime; segmentCurrent->endtime = msr_endtime(recordCurrent->record); segmentCurrent->samprate = recordCurrent->record->samprate; segmentCurrent->sampletype = recordCurrent->record->sampletype; segmentCurrent->samplecnt = recordCurrent->record->samplecnt; // Calculate high-precision sample period segmentCurrent->hpdelta = (hptime_t) (( recordCurrent->record->samprate ) ? (HPTMODULUS / recordCurrent->record->samprate) : 0.0); segmentCurrent->timing_qual = timing_qual; segmentCurrent->calibration_type = calibration_type; segmentCurrent->firstRecord = segmentCurrent->lastRecord = recordCurrent; recordCurrent->previous = NULL; } recordPrevious = recordCurrent->next; recordCurrent->next = NULL; recordCurrent = recordPrevious; } // Now loop over all segments, combine the records and free the msr // structures. idListCurrent = idListHead; while (idListCurrent != NULL) { segmentCurrent = idListCurrent->firstSegment; while (segmentCurrent != NULL) { if (segmentCurrent->datasamples) { free(segmentCurrent->datasamples); } // Allocate data via a callback function. if (unpack_data != 0) { segmentCurrent->datasamples = (void *) allocData(segmentCurrent->samplecnt, segmentCurrent->sampletype); } // Loop over all records, write the data to the buffer and free the msr structures. recordCurrent = segmentCurrent->firstRecord; data_offset = (long)(segmentCurrent->datasamples); while (recordCurrent != NULL) { datasize = recordCurrent->record->samplecnt * ms_samplesize(recordCurrent->record->sampletype); memcpy((void *)data_offset, recordCurrent->record->datasamples, datasize); // Free the record. msr_free(&(recordCurrent->record)); // Increase the data_offset and the record. data_offset += (long)datasize; recordCurrent = recordCurrent->next; } segmentCurrent = segmentCurrent->next; } idListCurrent = idListCurrent->next; } return idListHead; }