def interpolateToSecond(timeData: TimeData, inplace: bool = True) -> TimeData: """Interpolate data to be on the second Some formats of time data (e.g. SPAM) do not start on the second with their sampling. This method interpolates so that sampling starts on the second and improves interoperability with other recording formats. Parameters ---------- timeData : TimeData Time data to interpolate onto the second inplace : bool, optional Whether to do the interpolation inplace or not. Default is True. Returns ------- TimeData Time data interpolated to start on the second """ startTimeInterp, numSamplesInterp, dataInterp = interpolateToSecondData( timeData.data, timeData.sampleFreq, timeData.startTime ) if not inplace: timeData = timeData.copy() timeData.numSamples = numSamplesInterp timeData.startTime = startTimeInterp # calculate end timeEnd timeData.stopTime = timeData.startTime + timedelta( seconds=(1.0 / timeData.sampleFreq) * (timeData.numSamples - 1) ) timeData.data = dataInterp timeData.addComment( "Time data interpolated to nearest second. New start time {}, new end time {}, new number of samples {} ".format( timeData.startTime, timeData.stopTime, timeData.numSamples ) ) return timeData
def resample(timeData: TimeData, resampFreq: float, inplace: bool = True) -> TimeData: """Resample time data Parameters ---------- timeData : TimeData timeData to filter resampFreq : float The frequency to resample to inplace : bool, optional Whether to manipulate the data inplace Returns ------- TimeData Filtered time data """ origFreq = timeData.sampleFreq if not inplace: timeData = timeData.copy() timeData.data = resampleData(timeData.data, timeData.sampleFreq, resampFreq) # update the time info timeData.sampleFreq = resampFreq timeData.numSamples = timeData.data[timeData.chans[0]].size timeData.stopTime = timeData.startTime + timedelta( seconds=(1.0 / timeData.sampleFreq) * (timeData.numSamples - 1) ) timeData.addComment( "Time data resampled from {:.6f} Hz to {:.6f} Hz".format(origFreq, resampFreq) ) return timeData
def write(self, headers: Dict, chanHeaders: List, chanMap: Dict, timeData: TimeData, **kwargs): """Write out the header file Parameters ---------- headers : Dict Dictionary of headers chanHeaders : List List of channel headers chanMap : Dict Maps channel to index for chanHeaders timeData : TimeData Time series data as TimeData object """ # set global headers for keyword arguments headers = self.setGlobalHeadersFromKeywords(headers, kwargs) # set channel headers for keyword arguments chanHeaders = self.setChanHeadersFromKeywords(chanHeaders, kwargs) # now overwrite the options by checking the TimeData object # number of samples and sample frequency # Current method favours the time data object chans = sorted(list(timeData.chans)) dataSizes = [] for c in chans: dataSizes.append(timeData.data[c].size) if min(dataSizes) != max(dataSizes): self.printWarning( "Channels do not have the same number of samples: {} - {}". format(", ".join(chans), ", ".join(dataSizes))) self.printWarning( "Only the smallest number of samples will be written out") numSamples = min(dataSizes) if headers["num_samples"] != numSamples: self.printWarning( "Number of samples {} in headers does not match number of samples in TimeData object {}. TimeData info will be used." .format(headers["num_samples"], numSamples)) headers["num_samples"] = numSamples timeData.numSamples = numSamples # sample freq if headers["sample_freq"] != timeData.sampleFreq: self.printWarning( "Sample frequency of {} Hz in headers does not match {} Hz in TimeData object" .format(headers["sample_freq"], timeData.sampleFreq)) self.printWarning( "Sample frequency in TimeData object will be used") headers["sample_freq"] = timeData.sampleFreq # deal with start and end time and create datetime objects # the start time does not change on resampling, only the end time datetimeStart = datetime.strptime( "{} {}".format(headers["start_date"], headers["start_time"]), "%Y-%m-%d %H:%M:%S.%f", ) datetimeStop = datetime.strptime( "{} {}".format(headers["stop_date"], headers["stop_time"]), "%Y-%m-%d %H:%M:%S.%f", ) # now let's compare to the time data if datetimeStart != timeData.startTime: self.printWarning( "Start in headers {} does not match that in TimeData object {}. TimeData start time will be used" .format(datetimeStart, timeData.startTime)) datetimeStart = timeData.startTime if datetimeStop != timeData.stopTime: self.printWarning( "Stop in headers {} does not match that in TimeData object {}. TimeData stop time will be used" .format(datetimeStop, timeData.stopTime)) datetimeStop = timeData.stopTime # now recalculate datetime using the number of samples and compare again datetimeRecalc = self.calcStopDateTime(timeData.sampleFreq, numSamples, datetimeStart) if datetimeRecalc != datetimeStop: self.printWarning( "Note, discrepancy between stop time in given headers and those calculated from data" ) self.printWarning( "Causes of this might be resampling or interpolation processes and the limiting of data" ) self.printWarning( "If no resampling, interpolation or limiting of data has been performed, please check all times" ) self.printWarning( "Stop time {} calculated from data will be used instead of that in data {}" .format(datetimeRecalc, datetimeStop)) datetimeStop = datetimeRecalc headers["start_date"] = datetimeStart.strftime("%Y-%m-%d") headers["start_time"] = datetimeStart.strftime("%H:%M:%S.%f") headers["stop_date"] = datetimeStop.strftime("%Y-%m-%d") headers["stop_time"] = datetimeStop.strftime("%H:%M:%S.%f") # now update all the chan headers and limit data to numSamples for c in chans: timeData.data[c] = timeData.data[c][:numSamples] cIndex = chanMap[c] chanHeaders[cIndex]["num_samples"] = headers["num_samples"] chanHeaders[cIndex]["sample_freq"] = headers["sample_freq"] chanHeaders[cIndex]["start_date"] = headers["start_date"] chanHeaders[cIndex]["start_time"] = headers["start_time"] chanHeaders[cIndex]["stop_date"] = headers["stop_date"] chanHeaders[cIndex]["stop_time"] = headers["stop_time"] # finally, check the number of measurement channels headers["meas_channels"] = len(chans) # now write out the headers and save to class variables self.writeHeaders(headers, chans, chanMap, chanHeaders) self.headers = headers self.chans = chans self.chanMap = chanMap self.chanHeaders = chanHeaders # write out comment file self.writeComments(timeData.comments) # write out the data files self.writeDataFiles(chans, timeData)