Source code for crappy.blocks.multiplex

# coding: utf-8

import numpy as np
from itertools import chain
# chain(*l) is MUCH faster than sum(l, []) to concatenate lists

from .block import Block


def interp(xp, yp, x):
  try:
    return np.interp(x, xp, yp)
  except (TypeError, ValueError):
    # Make a nearest interpolation for non numerical values
    a = range(len(yp))
    return yp[int(np.interp(x, xp, a) + .5)]


[docs]class Multiplex(Block): """This block interpolates data. It is used to read data and return the reading from multiple sensors at the same instants and a constant frequency. This block uses linear interpolation whenever possible, else the nearest neighbor. Warning: This block needs a delay to make sure all the required data for interpolation has already been received, so do not use this block as the input of a decision block! """
[docs] def __init__(self, key='t(s)', freq=200): """Sets the args and initializes the parent class. Args: key (:obj:`str`, optional): The label carrying the time information. freq (:obj:`float`, optional): The block will loop at this frequency. """ Block.__init__(self) self.k = key self.freq = freq self.hist = dict() # Dict, keys = labels, values = list of the values self.t_hist = [] # ith element is a list containing a list of the timestamps from # the ith link self.label_list = [] # ith element is a list containing a list of the labels to get from # the ith link self.t = 0 self.dt = 1 / self.freq
[docs] def begin(self): """We need to receive the first bit of data from each input to know the labels and make lists of what we will read in the main loop.""" for i, link in enumerate(self.inputs): r = link.recv_chunk() if self.k not in r: # This input don't have the timebase label! self.t_hist.append([]) self.label_list.append([]) continue self.t_hist.append(r[self.k]) # Append the time to this link's history labels = [] # To recap all the labels we will use at each loop for k in r: if k == self.k: # Ignore the timebase label (treated separately) continue if k in chain(*self.label_list): print("WARNING, label %s comes from multiple inputs, " "ignoring one" % k) continue labels.append(k) self.hist[k] = r[k] # Put the data in the hist dict self.label_list.append(labels) # Add the label list of this link
def get_data(self): for i, l in enumerate(self.label_list): r = self.inputs[i].recv_chunk() # Get the data if not l: continue self.t_hist[i].extend(r[self.k]) # Add the time to this link's history for k in l: self.hist[k].extend(r[k]) # Add each data to their history def send_data(self): while all([i and i[-1] > self.t for i in self.t_hist]) \ and self.t_hist: # Send all we can r = {self.k: self.t} # First data to return: our new timebase for i, l in enumerate(self.label_list): if not l: continue for k in l: r[k] = interp(self.t_hist[i], self.hist[k], self.t) # Interpolate # To know until when we can delete last = int(interp(self.t_hist[i], range(len(self.t_hist[i])), self.t + self.dt)) # Delete the data we don't need anymore self.t_hist[i] = self.t_hist[i][last:] for k in l: self.hist[k] = self.hist[k][last:] self.send(r) # Send it! self.t += self.dt # And increment our timebase def loop(self): self.get_data() self.send_data()
[docs] def finish(self): self.send_data() while any([len(k) > 1 for k in self.t_hist]): r = {self.k: self.t} # First data to return: our new timebase for i, l in enumerate(self.label_list): if not l: continue for k in l: r[k] = interp(self.t_hist[i], self.hist[k], self.t) # Interpolate # To know until when we can delete last = int(interp(self.t_hist[i], range(len(self.t_hist[i])), self.t + self.dt)) # Delete the data we don't need anymore self.t_hist[i] = self.t_hist[i][last:] for k in l: self.hist[k] = self.hist[k][last:] self.send(r) # Send it! self.t += self.dt # And increment our timebase