def combine(self, *args, **kwargs): """ Yield a framestream of dictionaries, one for each timeframe in the primary source. Interpolate the channels provided by secondary sources. Use dplkit.frame.struct.as_struct() adapter if struct-like frames are preferred. """ # for each frame in the primary # see if we have an applicable frame from each of the secondaries # call secondary.next() if we need another frame in order to interpolate # catch StopIteration exception? yes, just return when we encounter StopIteration # time interpolate between neighboring secondary frames, for channels of interest # use simple linear interpolation using frame center times? # maintain dimensionality in all the arrays in the desired channels # if channels is None, use all channels from the secondary # create a new output frame which merges the primary data with the interpolated secondary data # yield that frame # for each frame in primary, obtain time window # for each channel,source in schedule # check source # self._integrity_check() for timeframe in self._iters[self._primary]: LOG.debug('timeframe: %s' % timeframe) timeframe = as_struct(timeframe) self._eat_next_frame(self._primary, timeframe) start, when, width = center_time(timeframe) # FUTURE: should this be part of schedule? zult = self._frame_at_time(when) zult['start'] = start zult['width'] = width zult['_center_time'] = when #FUTURE: is this advisable? useful? yield zult
def _eat_next_frame(self, source, data = None): "append new data from a source to the interpolator objects, returning the new time-span we can interpolate within" # self._integrity_check() LOG.debug('eating frame from %s' % source) if data is None: it = self._iters[source] try: data = it.next() except StopIteration: # FIXME: if this throws a StopIteration, we have a secondary source that ran out of track # we should let the interpolator for the source's channels generate nans for everything from here on out LOG.warning('source %s is out of data, future version should handle this by providing NaNs for this source' % source) raise # LOG.debug(repr(data)) data = as_struct(data) start, t, width = center_time(data) LOG.debug('using frame center time of %s' % (t)) # collect inner time-span of all the TSIPs to return as guidance, and push the channel data into the interpolators s,e = None, None for channel,sched_source in self._schedule: tsip = self._tsips[channel] # FUTURE: merge this into schedule if source is sched_source: # assert(channel in source.provides) should already be guaranteed in schedule creation LOG.debug('appending channel data to interpolator for %s' % (channel)) tsip.append((t, getattr(data, channel))) # track the available time period for the current tsips ts,te = tsip.span if (ts is not None) and ((s is None) or (s < ts)): s = ts if (te is not None) and ((e is None) or (e > te)): e = te self._consumed[source] += 1 return s,e
def _eat_next_frame(self, source, data=None): "append new data from a source to the interpolator objects, returning the new time-span we can interpolate within" # self._integrity_check() LOG.debug('eating frame from %s' % source) if data is None: it = self._iters[source] try: data = it.next() except StopIteration: # FIXME: if this throws a StopIteration, we have a secondary source that ran out of track # we should let the interpolator for the source's channels generate nans for everything from here on out LOG.warning( 'source %s is out of data, future version should handle this by providing NaNs for this source' % source) raise # LOG.debug(repr(data)) data = as_struct(data) start, t, width = center_time(data) LOG.debug('using frame center time of %s' % (t)) # collect inner time-span of all the TSIPs to return as guidance, and push the channel data into the interpolators s, e = None, None for channel, sched_source in self._schedule: tsip = self._tsips[channel] # FUTURE: merge this into schedule if source is sched_source: # assert(channel in source.provides) should already be guaranteed in schedule creation LOG.debug('appending channel data to interpolator for %s' % (channel)) tsip.append((t, getattr(data, channel))) # track the available time period for the current tsips ts, te = tsip.span if (ts is not None) and ((s is None) or (s < ts)): s = ts if (te is not None) and ((e is None) or (e > te)): e = te self._consumed[source] += 1 return s, e