Ejemplo n.º 1
0
class Platform(traits.HasTraits):
    ID = traits.Int
    berths = traits.List(traits.Instance(Berth))
    track_segment = traits.Instance(TrackSegment)

    def __init__(self, ID, track_segment):
        traits.HasTraits.__init__(self)
        self.ID = ID
        self.berths = []
        self.track_segment = track_segment
Ejemplo n.º 2
0
class Platform(traits.HasTraits):
    berths = traits.List(traits.Instance(Berth))
    track_segment = traits.Instance('pyprt.sim.layout.TrackSegment')
    berth_length = traits.CFloat
    unloading = traits.CBool
    loading = traits.CBool

    def __init__(self, berths, track_segment, berth_length, unloading, loading):
        traits.HasTraits.__init__(self)
        self.berths = berths
        self.track_segment = track_segment
        self.berth_length = berth_length
        self.unloading = unloading
        self.loading = loading

    def advance(self, prev_platform):
        """Vehicles 'bubble' forward one slot. There is no 'accordian' effect
        -- all vehicles at the platform move synchronously. Does not advance
        sim time. Moves a vehicle from the front berth of prev_platform into the
        rear berth of self if there is room.
        prev_platform may be None.
        """
        if len(self.berths) >= 2:
            for i in xrange(len(self.berths) - 1):
                # Lead berth is empty, the following berth has a non-busy vehicle
                if self.berths[i].is_empty() and \
                      not self.berths[i+1].is_empty() and \
                      not self.berths[i+1].is_busy():
                    # swap the berths' vehicle
                    self.berths[i+1].vehicle.bump_forward(self.berth_length)
                    self.berths[i].vehicle, self.berths[i+1].vehicle = \
                            self.berths[i+1].vehicle, self.berths[i].vehicle

        if self.berths[-1].is_empty() and prev_platform != None:
            prev_front_berth = prev_platform.berths[0]
            if not prev_front_berth.is_empty() and not prev_front_berth.is_busy():
                prev_front_berth.vehicle.bump_forward(self.berth_length)
                self.berths[-1].vehicle, prev_front_berth.vehicle = \
                       prev_front_berth.vehicle, self.berths[-1].vehicle

    def is_empty(self):
        """True if no berths are occupied."""
        empty = True
        for berth in self.berths:
            if not berth.is_empty():
                empty = False
                break
        return empty
Ejemplo n.º 3
0
class Fit(traits.HasTraits):

    name = traits.Str(desc="name of fit")
    function = traits.Str(desc="function we are fitting with all parameters")
    variablesList = traits.List(FitVariable)
    calculatedParametersList = traits.List(CalculatedParameter)
    xs = None  # will be a scipy array
    ys = None  # will be a scipy array
    zs = None  # will be a scipy array
    performFitButton = traits.Button("Perform Fit")
    getInitialParametersButton = traits.Button("Guess Initial Values")
    drawRequestButton = traits.Button("Draw Fit")
    autoFitBool = traits.Bool(
        False,
        desc=
        "Automatically perform this Fit with current settings whenever a new image is loaded"
    )
    autoGuessBool = traits.Bool(
        False,
        desc=
        "Whenever a fit is completed replace the guess values with the calculated values (useful for increasing speed of the next fit)"
    )
    autoDrawBool = traits.Bool(
        False,
        desc=
        "Once a fit is complete update the drawing of the fit or draw the fit for the first time"
    )
    logBool = traits.Bool(
        False, desc="Log the calculated and fitted values with a timestamp")
    logFile = traits.File(desc="file path of logFile")

    imageInspectorReference = None  #will be a reference to the image inspector
    fitting = traits.Bool(False)  #true when performing fit
    fitted = traits.Bool(
        False)  #true when current data displayed has been fitted
    fitSubSpace = traits.Bool(
        False)  #true when current data displayed has been fitted
    startX = traits.Int
    startY = traits.Int
    endX = traits.Int
    endY = traits.Int
    fittingStatus = traits.Str()
    fitThread = None
    physics = traits.Instance(physicsProperties.PhysicsProperties)
    #status strings
    notFittedForCurrentStatus = "Not Fitted for Current Image"
    fittedForCurrentImageStatus = "Fit Complete for Current Image"
    currentlyFittingStatus = "Currently Fitting..."
    failedFitStatus = "Failed to finish fit. See logger"

    fitSubSpaceGroup = traitsui.VGroup(
        traitsui.Item("fitSubSpace", label="Fit Sub Space"),
        traitsui.VGroup(traitsui.HGroup(traitsui.Item("startX"),
                                        traitsui.Item("startY")),
                        traitsui.HGroup(traitsui.Item("endX"),
                                        traitsui.Item("endY")),
                        visible_when="fitSubSpace"),
        label="Fit Sub Space",
        show_border=True)

    generalGroup = traitsui.VGroup(traitsui.Item("name",
                                                 label="Fit Name",
                                                 style="readonly",
                                                 resizable=True),
                                   traitsui.Item("function",
                                                 label="Fit Function",
                                                 style="readonly",
                                                 resizable=True),
                                   fitSubSpaceGroup,
                                   label="Fit",
                                   show_border=True)

    variablesGroup = traitsui.VGroup(traitsui.Item(
        "variablesList",
        editor=traitsui.ListEditor(style="custom"),
        show_label=False,
        resizable=True),
                                     show_border=True,
                                     label="parameters")

    derivedGroup = traitsui.VGroup(traitsui.Item(
        "calculatedParametersList",
        editor=traitsui.ListEditor(style="custom"),
        show_label=False,
        resizable=True),
                                   show_border=True,
                                   label="derived values")

    buttons = traitsui.VGroup(
        traitsui.HGroup(traitsui.Item("autoFitBool"),
                        traitsui.Item("performFitButton")),
        traitsui.HGroup(traitsui.Item("autoGuessBool"),
                        traitsui.Item("getInitialParametersButton")),
        traitsui.HGroup(traitsui.Item("autoDrawBool"),
                        traitsui.Item("drawRequestButton")))

    logGroup = traitsui.HGroup(traitsui.Item("logBool"),
                               traitsui.Item("logFile",
                                             visible_when="logBool"),
                               label="Logging",
                               show_border=True)

    actionsGroup = traitsui.VGroup(traitsui.Item("fittingStatus",
                                                 style="readonly"),
                                   logGroup,
                                   buttons,
                                   label="Fit Actions",
                                   show_border=True)
    traits_view = traitsui.View(
        traitsui.VGroup(generalGroup, variablesGroup, derivedGroup,
                        actionsGroup))

    def __init__(self, **traitsDict):
        super(Fit, self).__init__(**traitsDict)
        self.startX = 0
        self.startY = 0

    def _set_xs(self, xs):
        self.xs = xs

    def _set_ys(self, ys):
        self.ys = ys

    def _set_zs(self, zs):
        self.zs = zs

    def _fittingStatus_default(self):
        return self.notFittedForCurrentStatus

    def _getInitialValues(self):
        """returns ordered list of initial values from variables List """
        return [_.initialValue for _ in self.variablesList]

    def _getCalculatedValues(self):
        """returns ordered list of initial values from variables List """
        return [_.calculatedValue for _ in self.variablesList]

    def _log_fit(self):
        if self.logFile == "":
            logger.warning("no log file defined. Will not log")
            return
        if not os.path.exists(self.logFile):
            variables = [_.name for _ in self.variablesList]
            calculated = [_.name for _ in self.calculatedParametersList]
            times = ["datetime", "epoch seconds"]
            info = ["img file name"]
            columnNames = times + info + variables + calculated
            with open(self.logFile, 'a+') as logFile:
                writer = csv.writer(logFile)
                writer.writerow(columnNames)
        #column names already exist so...
        variables = [_.calculatedValue for _ in self.variablesList]
        calculated = [_.value for _ in self.calculatedParametersList]
        now = time.time()  #epoch seconds
        timeTuple = time.localtime(now)
        date = time.strftime("%Y-%m-%dT%H:%M:%S", timeTuple)
        times = [date, now]
        info = [self.imageInspectorReference.selectedFile]
        data = times + info + variables + calculated
        with open(self.logFile, 'a+') as logFile:
            writer = csv.writer(logFile)
            writer.writerow(data)

    def _intelligentInitialValues(self):
        """If possible we can auto set the initial parameters to intelligent guesses user can always overwrite them """
        self._setInitialValues(self._getIntelligentInitialValues())

    def _get_subSpaceArrays(self):
        """returns the arrays of the selected sub space. If subspace is not
        activated then returns the full arrays"""
        if self.fitSubSpace:
            xs = self.xs[self.startX:self.endX]
            ys = self.ys[self.startY:self.endY]
            logger.debug("xs array sliced length %s " % (xs.shape))
            logger.debug("ys array sliced length %s  " % (ys.shape))
            zs = self.zs[self.startY:self.endY, self.startX:self.endX]
            print zs
            print zs.shape
            logger.debug("zs sub space array %s,%s " % (zs.shape))

            return xs, ys, zs
        else:
            return self.xs, self.ys, self.zs

    def _getIntelligentInitialValues(self):
        """If possible we can auto set the initial parameters to intelligent guesses user can always overwrite them """
        logger.debug("Dummy function should not be called directly")
        return

    def fitFunc(self, data, *p):
        """Function that we are trying to fit to. """
        logger.error("Dummy function should not be called directly")
        return

    def _setCalculatedValues(self, calculated):
        """updates calculated values with calculated argument """
        c = 0
        for variable in self.variablesList:
            variable.calculatedValue = calculated[c]
            c += 1

    def _setCalculatedValuesErrors(self, covarianceMatrix):
        """given the covariance matrix returned by scipy optimize fit
        convert this into stdeviation errors for parameters list and updated
        the stdevError attribute of variables"""
        logger.debug("covariance matrix -> %s " % covarianceMatrix)
        parameterErrors = scipy.sqrt(scipy.diag(covarianceMatrix))
        logger.debug("parameterErrors  -> %s " % parameterErrors)
        c = 0
        for variable in self.variablesList:
            variable.stdevError = parameterErrors[c]
            c += 1

    def _setInitialValues(self, guesses):
        """updates calculated values with calculated argument """
        c = 0
        for variable in self.variablesList:
            variable.initialValue = guesses[c]
            c += 1

    def deriveCalculatedParameters(self):
        """Wrapper for subclass definition of deriving calculated parameters
        can put more general calls in here"""
        if self.fitted:
            self._deriveCalculatedParameters()

    def _deriveCalculatedParameters(self):
        """Should be implemented by subclass. should update all variables in calculate parameters list"""
        logger.error("Should only be called by subclass")
        return

    def _fit_routine(self):
        """This function performs the fit in an appropriate thread and 
        updates necessary values when the fit has been performed"""
        self.fitting = True
        if self.fitThread and self.fitThread.isAlive():
            logger.warning(
                "Fitting is already running cannot kick off a new fit until it has finished!"
            )
            return
        else:
            self.fitThread = FitThread()
            self.fitThread.fitReference = self
            self.fitThread.start()
            self.fittingStatus = self.currentlyFittingStatus

    def _perform_fit(self):
        """Perform the fit using scipy optimise curve fit.
        We must supply x and y as one argument and zs as anothger. in the form
        xs: 0 1 2 0 1 2 0 
        ys: 0 0 0 1 1 1 2
        zs: 1 5 6 1 9 8 2

        Hence the use of repeat and tile in  positions and unravel for zs
        
        initially xs,ys is a linspace array and zs is a 2d image array
        """
        if self.xs is None or self.ys is None or self.zs is None:
            logger.warning(
                "attempted to fit data but had no data inside the Fit object. set xs,ys,zs first"
            )
            return ([], [])
        p0 = self._getInitialValues()
        if self.fitSubSpace:  #fit only the sub space
            #create xs, ys and zs which are appropriate slices of the arrays
            xs, ys, zs = self._get_subSpaceArrays()
            positions = [scipy.tile(xs, len(ys)),
                         scipy.repeat(ys, len(xs))
                         ]  #for creating data necessary for gauss2D function
            params2D, cov2D = scipy.optimize.curve_fit(self.fitFunc,
                                                       positions,
                                                       scipy.ravel(zs),
                                                       p0=p0)
            chi2 = scipy.sum(
                (scipy.ravel(zs) - self.fitFunc(positions, *params2D))**2 /
                self.fitFunc(positions, *params2D))
            logger.debug("TEMPORARY ::: CHI^2 = %s " % chi2)
        else:  #fit the whole array of data (slower)
            positions = [
                scipy.tile(self.xs, len(self.ys)),
                scipy.repeat(self.ys, len(self.xs))
            ]  #for creating data necessary for gauss2D function
            #note that it is necessary to ravel zs as curve_fit expects a flattened array
            params2D, cov2D = scipy.optimize.curve_fit(self.fitFunc,
                                                       positions,
                                                       scipy.ravel(self.zs),
                                                       p0=p0)

        return params2D, cov2D

    def _performFitButton_fired(self):
        self._fit_routine()

    def _getInitialParametersButton_fired(self):
        self._intelligentInitialValues()

    def _drawRequestButton_fired(self):
        """tells the imageInspector to try and draw this fit as an overlay contour plot"""
        self.imageInspectorReference.addFitPlot(self)

    def _getFitFuncData(self):
        """if data has been fitted, this returns the zs data for the ideal
        fitted function using the calculated paramters"""
        positions = [
            scipy.tile(self.xs, len(self.ys)),
            scipy.repeat(self.ys, len(self.xs))
        ]  #for creating data necessary for gauss2D function
        zsravelled = self.fitFunc(positions, *self._getCalculatedValues())
        return zsravelled.reshape(self.zs.shape)
Ejemplo n.º 4
0
class Signal(t.HasTraits, MVA):
    data = t.Any()
    axes_manager = t.Instance(AxesManager)
    original_parameters = t.Instance(Parameters)
    mapped_parameters = t.Instance(Parameters)
    physical_property = t.Str()

    def __init__(self, file_data_dict=None, *args, **kw):
        """All data interaction is made through this class or its subclasses


        Parameters:
        -----------
        dictionary : dictionary
           see load_dictionary for the format
        """
        super(Signal, self).__init__()
        self.mapped_parameters = Parameters()
        self.original_parameters = Parameters()
        if type(file_data_dict).__name__ == "dict":
            self.load_dictionary(file_data_dict)
        self._plot = None
        self.mva_results = MVA_Results()
        self._shape_before_unfolding = None
        self._axes_manager_before_unfolding = None

    def load_dictionary(self, file_data_dict):
        """Parameters:
        -----------
        file_data_dict : dictionary
            A dictionary containing at least a 'data' keyword with an array of
            arbitrary dimensions. Additionally the dictionary can contain the
            following keys:
                axes: a dictionary that defines the axes (see the
                    AxesManager class)
                attributes: a dictionary which keywords are stored as
                    attributes of the signal class
                mapped_parameters: a dictionary containing a set of parameters
                    that will be stored as attributes of a Parameters class.
                    For some subclasses some particular parameters might be
                    mandatory.
                original_parameters: a dictionary that will be accesible in the
                    original_parameters attribute of the signal class and that
                    typically contains all the parameters that has been
                    imported from the original data file.

        """
        self.data = file_data_dict['data']
        if 'axes' not in file_data_dict:
            file_data_dict['axes'] = self._get_undefined_axes_list()
        self.axes_manager = AxesManager(file_data_dict['axes'])
        if not 'mapped_parameters' in file_data_dict:
            file_data_dict['mapped_parameters'] = {}
        if not 'original_parameters' in file_data_dict:
            file_data_dict['original_parameters'] = {}
        if 'attributes' in file_data_dict:
            for key, value in file_data_dict['attributes'].iteritems():
                self.__setattr__(key, value)
        self.original_parameters.load_dictionary(
            file_data_dict['original_parameters'])
        self.mapped_parameters.load_dictionary(
            file_data_dict['mapped_parameters'])

    def _get_signal_dict(self):
        dic = {}
        dic['data'] = self.data.copy()
        dic['axes'] = self.axes_manager._get_axes_dicts()
        dic['mapped_parameters'] = \
        self.mapped_parameters._get_parameters_dictionary()
        dic['original_parameters'] = \
        self.original_parameters._get_parameters_dictionary()
        return dic

    def _get_undefined_axes_list(self):
        axes = []
        for i in xrange(len(self.data.shape)):
            axes.append({
                'name': 'undefined',
                'scale': 1.,
                'offset': 0.,
                'size': int(self.data.shape[i]),
                'units': 'undefined',
                'index_in_array': i,
            })
        return axes

    def __call__(self, axes_manager=None):
        if axes_manager is None:
            axes_manager = self.axes_manager
        return self.data.__getitem__(axes_manager._getitem_tuple)

    def _get_hse_1D_explorer(self, *args, **kwargs):
        islice = self.axes_manager._slicing_axes[0].index_in_array
        inslice = self.axes_manager._non_slicing_axes[0].index_in_array
        if islice > inslice:
            return self.data.squeeze()
        else:
            return self.data.squeeze().T

    def _get_hse_2D_explorer(self, *args, **kwargs):
        islice = self.axes_manager._slicing_axes[0].index_in_array
        data = self.data.sum(islice)
        return data

    def _get_hie_explorer(self, *args, **kwargs):
        isslice = [
            self.axes_manager._slicing_axes[0].index_in_array,
            self.axes_manager._slicing_axes[1].index_in_array
        ]
        isslice.sort()
        data = self.data.sum(isslice[1]).sum(isslice[0])
        return data

    def _get_explorer(self, *args, **kwargs):
        nav_dim = self.axes_manager.navigation_dimension
        if self.axes_manager.signal_dimension == 1:
            if nav_dim == 1:
                return self._get_hse_1D_explorer(*args, **kwargs)
            elif nav_dim == 2:
                return self._get_hse_2D_explorer(*args, **kwargs)
            else:
                return None
        if self.axes_manager.signal_dimension == 2:
            if nav_dim == 1 or nav_dim == 2:
                return self._get_hie_explorer(*args, **kwargs)
            else:
                return None
        else:
            return None

    def plot(self, axes_manager=None):
        if self._plot is not None:
            try:
                self._plot.close()
            except:
                # If it was already closed it will raise an exception,
                # but we want to carry on...
                pass

        if axes_manager is None:
            axes_manager = self.axes_manager

        if axes_manager.signal_dimension == 1:
            # Hyperspectrum

            self._plot = mpl_hse.MPL_HyperSpectrum_Explorer()
            self._plot.spectrum_data_function = self.__call__
            self._plot.spectrum_title = self.mapped_parameters.name
            self._plot.xlabel = '%s (%s)' % (
                self.axes_manager._slicing_axes[0].name,
                self.axes_manager._slicing_axes[0].units)
            self._plot.ylabel = 'Intensity'
            self._plot.axes_manager = axes_manager
            self._plot.axis = self.axes_manager._slicing_axes[0].axis

            # Image properties
            if self.axes_manager._non_slicing_axes:
                self._plot.image_data_function = self._get_explorer
                self._plot.image_title = ''
                self._plot.pixel_size = \
                self.axes_manager._non_slicing_axes[0].scale
                self._plot.pixel_units = \
                self.axes_manager._non_slicing_axes[0].units
            self._plot.plot()

        elif axes_manager.signal_dimension == 2:

            # Mike's playground with new plotting toolkits - needs to be a
            # branch.
            """
            if len(self.data.shape)==2:
                from drawing.guiqwt_hie import image_plot_2D
                image_plot_2D(self)

            import drawing.chaco_hie
            self._plot = drawing.chaco_hie.Chaco_HyperImage_Explorer(self)
            self._plot.configure_traits()
            """
            self._plot = mpl_hie.MPL_HyperImage_Explorer()
            self._plot.image_data_function = self.__call__
            self._plot.navigator_data_function = self._get_explorer
            self._plot.axes_manager = axes_manager
            self._plot.plot()

        else:
            messages.warning_exit('Plotting is not supported for this view')

    traits_view = tui.View(
        tui.Item('name'),
        tui.Item('physical_property'),
        tui.Item('units'),
        tui.Item('offset'),
        tui.Item('scale'),
    )

    def plot_residual(self, axes_manager=None):
        """Plot the residual between original data and reconstructed data

        Requires you to have already run PCA or ICA, and to reconstruct data
        using either the pca_build_SI or ica_build_SI methods.
        """

        if hasattr(self, 'residual'):
            self.residual.plot(axes_manager)
        else:
            print "Object does not have any residual information.  Is it a \
reconstruction created using either pca_build_SI or ica_build_SI methods?"

    def save(self, filename, only_view=False, **kwds):
        """Saves the signal in the specified format.

        The function gets the format from the extension. You can use:
            - hdf5 for HDF5
            - nc for NetCDF
            - msa for EMSA/MSA single spectrum saving.
            - bin to produce a raw binary file
            - Many image formats such as png, tiff, jpeg...

        Please note that not all the formats supports saving datasets of
        arbitrary dimensions, e.g. msa only suports 1D data.

        Parameters
        ----------
        filename : str
        msa_format : {'Y', 'XY'}
            'Y' will produce a file without the energy axis. 'XY' will also
            save another column with the energy axis. For compatibility with
            Gatan Digital Micrograph 'Y' is the default.
        only_view : bool
            If True, only the current view will be saved. Otherwise the full
            dataset is saved. Please note that not all the formats support this
            option at the moment.
        """
        io.save(filename, self, **kwds)

    def _replot(self):
        if self._plot is not None:
            if self._plot.is_active() is True:
                self.plot()

    def get_dimensions_from_data(self):
        """Get the dimension parameters from the data_cube. Useful when the
        data_cube was externally modified, or when the SI was not loaded from
        a file
        """
        dc = self.data
        for axis in self.axes_manager.axes:
            axis.size = int(dc.shape[axis.index_in_array])
            print("%s size: %i" % (axis.name, dc.shape[axis.index_in_array]))
        self._replot()

    def crop_in_pixels(self, axis, i1=None, i2=None):
        """Crops the data in a given axis. The range is given in pixels
        axis : int
        i1 : int
            Start index
        i2 : int
            End index

        See also:
        ---------
        crop_in_units
        """
        axis = self._get_positive_axis_index_index(axis)
        if i1 is not None:
            new_offset = self.axes_manager.axes[axis].axis[i1]
        # We take a copy to guarantee the continuity of the data
        self.data = self.data[(slice(None), ) * axis +
                              (slice(i1, i2), Ellipsis)].copy()

        if i1 is not None:
            self.axes_manager.axes[axis].offset = new_offset
        self.get_dimensions_from_data()

    def crop_in_units(self, axis, x1=None, x2=None):
        """Crops the data in a given axis. The range is given in the units of
        the axis

        axis : int
        i1 : int
            Start index
        i2 : int
            End index

        See also:
        ---------
        crop_in_pixels

        """
        i1 = self.axes_manager.axes[axis].value2index(x1)
        i2 = self.axes_manager.axes[axis].value2index(x2)
        self.crop_in_pixels(axis, i1, i2)

    def roll_xy(self, n_x, n_y=1):
        """Roll over the x axis n_x positions and n_y positions the former rows

        This method has the purpose of "fixing" a bug in the acquisition of the
        Orsay's microscopes and probably it does not have general interest

        Parameters
        ----------
        n_x : int
        n_y : int

        Note: Useful to correct the SI column storing bug in Marcel's
        acquisition routines.
        """
        self.data = np.roll(self.data, n_x, 0)
        self.data[:n_x, ...] = np.roll(self.data[:n_x, ...], n_y, 1)
        self._replot()

    # TODO: After using this function the plotting does not work
    def swap_axis(self, axis1, axis2):
        """Swaps the axes

        Parameters
        ----------
        axis1 : positive int
        axis2 : positive int
        """
        self.data = self.data.swapaxes(axis1, axis2)
        c1 = self.axes_manager.axes[axis1]
        c2 = self.axes_manager.axes[axis2]
        c1.index_in_array, c2.index_in_array =  \
            c2.index_in_array, c1.index_in_array
        self.axes_manager.axes[axis1] = c2
        self.axes_manager.axes[axis2] = c1
        self.axes_manager.set_signal_dimension()
        self._replot()

    def rebin(self, new_shape):
        """
        Rebins the data to the new shape

        Parameters
        ----------
        new_shape: tuple of ints
            The new shape must be a divisor of the original shape
        """
        factors = np.array(self.data.shape) / np.array(new_shape)
        self.data = utils.rebin(self.data, new_shape)
        for axis in self.axes_manager.axes:
            axis.scale *= factors[axis.index_in_array]
        self.get_dimensions_from_data()

    def split_in(self, axis, number_of_parts=None, steps=None):
        """Splits the data

        The split can be defined either by the `number_of_parts` or by the
        `steps` size.

        Parameters
        ----------
        number_of_parts : int or None
            Number of parts in which the SI will be splitted
        steps : int or None
            Size of the splitted parts
        axis : int
            The splitting axis

        Return
        ------
        tuple with the splitted signals
        """
        axis = self._get_positive_axis_index_index(axis)
        if number_of_parts is None and steps is None:
            if not self._splitting_steps:
                messages.warning_exit(
                    "Please provide either number_of_parts or a steps list")
            else:
                steps = self._splitting_steps
                print "Splitting in ", steps
        elif number_of_parts is not None and steps is not None:
            print "Using the given steps list. number_of_parts dimissed"
        splitted = []
        shape = self.data.shape

        if steps is None:
            rounded = (shape[axis] - (shape[axis] % number_of_parts))
            step = rounded / number_of_parts
            cut_node = range(0, rounded + step, step)
        else:
            cut_node = np.array([0] + steps).cumsum()
        for i in xrange(len(cut_node) - 1):
            data = self.data[(slice(None), ) * axis +
                             (slice(cut_node[i], cut_node[i + 1]), Ellipsis)]
            s = Signal({'data': data})
            # TODO: When copying plotting does not work
            #            s.axes = copy.deepcopy(self.axes_manager)
            s.get_dimensions_from_data()
            splitted.append(s)
        return splitted

    def unfold_if_multidim(self):
        """Unfold the datacube if it is >2D

        Returns
        -------

        Boolean. True if the data was unfolded by the function.
        """
        if len(self.axes_manager.axes) > 2:
            print "Automatically unfolding the data"
            self.unfold()
            return True
        else:
            return False

    def _unfold(self, steady_axes, unfolded_axis):
        """Modify the shape of the data by specifying the axes the axes which
        dimension do not change and the axis over which the remaining axes will
        be unfolded

        Parameters
        ----------
        steady_axes : list
            The indexes of the axes which dimensions do not change
        unfolded_axis : int
            The index of the axis over which all the rest of the axes (except
            the steady axes) will be unfolded

        See also
        --------
        fold
        """

        # It doesn't make sense unfolding when dim < 3
        if len(self.data.squeeze().shape) < 3:
            return False

        # We need to store the original shape and coordinates to be used by
        # the fold function only if it has not been already stored by a
        # previous unfold
        if self._shape_before_unfolding is None:
            self._shape_before_unfolding = self.data.shape
            self._axes_manager_before_unfolding = self.axes_manager

        new_shape = [1] * len(self.data.shape)
        for index in steady_axes:
            new_shape[index] = self.data.shape[index]
        new_shape[unfolded_axis] = -1
        self.data = self.data.reshape(new_shape)
        self.axes_manager = self.axes_manager.deepcopy()
        i = 0
        uname = ''
        uunits = ''
        to_remove = []
        for axis, dim in zip(self.axes_manager.axes, new_shape):
            if dim == 1:
                uname += ',' + axis.name
                uunits = ',' + axis.units
                to_remove.append(axis)
            else:
                axis.index_in_array = i
                i += 1
        self.axes_manager.axes[unfolded_axis].name += uname
        self.axes_manager.axes[unfolded_axis].units += uunits
        self.axes_manager.axes[unfolded_axis].size = \
                                                self.data.shape[unfolded_axis]
        for axis in to_remove:
            self.axes_manager.axes.remove(axis)

        self.data = self.data.squeeze()
        self._replot()

    def unfold(self):
        """Modifies the shape of the data by unfolding the signal and
        navigation dimensions separaterly

        """
        self.unfold_navigation_space()
        self.unfold_signal_space()

    def unfold_navigation_space(self):
        """Modify the shape of the data to obtain a navigation space of
        dimension 1
        """

        if self.axes_manager.navigation_dimension < 2:
            messages.information('Nothing done, the navigation dimension was '
                                 'already 1')
            return False
        steady_axes = [
            axis.index_in_array for axis in self.axes_manager._slicing_axes
        ]
        unfolded_axis = self.axes_manager._non_slicing_axes[-1].index_in_array
        self._unfold(steady_axes, unfolded_axis)

    def unfold_signal_space(self):
        """Modify the shape of the data to obtain a signal space of
        dimension 1
        """
        if self.axes_manager.signal_dimension < 2:
            messages.information('Nothing done, the signal dimension was '
                                 'already 1')
            return False
        steady_axes = [
            axis.index_in_array for axis in self.axes_manager._non_slicing_axes
        ]
        unfolded_axis = self.axes_manager._slicing_axes[-1].index_in_array
        self._unfold(steady_axes, unfolded_axis)

    def fold(self):
        """If the signal was previously unfolded, folds it back"""
        if self._shape_before_unfolding is not None:
            self.data = self.data.reshape(self._shape_before_unfolding)
            self.axes_manager = self._axes_manager_before_unfolding
            self._shape_before_unfolding = None
            self._axes_manager_before_unfolding = None
            self._replot()

    def _get_positive_axis_index_index(self, axis):
        if axis < 0:
            axis = len(self.data.shape) + axis
        return axis

    def iterate_axis(self, axis=-1):
        # We make a copy to guarantee that the data in contiguous, otherwise
        # it will not return a view of the data
        self.data = self.data.copy()
        axis = self._get_positive_axis_index_index(axis)
        unfolded_axis = axis - 1
        new_shape = [1] * len(self.data.shape)
        new_shape[axis] = self.data.shape[axis]
        new_shape[unfolded_axis] = -1
        # Warning! if the data is not contigous it will make a copy!!
        data = self.data.reshape(new_shape)
        for i in xrange(data.shape[unfolded_axis]):
            getitem = [0] * len(data.shape)
            getitem[axis] = slice(None)
            getitem[unfolded_axis] = i
            yield (data[getitem])

    def sum(self, axis, return_signal=False):
        """Sum the data over the specify axis

        Parameters
        ----------
        axis : int
            The axis over which the operation will be performed
        return_signal : bool
            If False the operation will be performed on the current object. If
            True, the current object will not be modified and the operation
             will be performed in a new signal object that will be returned.

        Returns
        -------
        Depending on the value of the return_signal keyword, nothing or a
        signal instance

        See also
        --------
        sum_in_mask, mean

        Usage
        -----
        >>> import numpy as np
        >>> s = Signal({'data' : np.random.random((64,64,1024))})
        >>> s.data.shape
        (64,64,1024)
        >>> s.sum(-1)
        >>> s.data.shape
        (64,64)
        # If we just want to plot the result of the operation
        s.sum(-1, True).plot()
        """
        if return_signal is True:
            s = self.deepcopy()
        else:
            s = self
        s.data = s.data.sum(axis)
        s.axes_manager.axes.remove(s.axes_manager.axes[axis])
        for _axis in s.axes_manager.axes:
            if _axis.index_in_array > axis:
                _axis.index_in_array -= 1
        s.axes_manager.set_signal_dimension()
        if return_signal is True:
            return s

    def mean(self, axis, return_signal=False):
        """Average the data over the specify axis

        Parameters
        ----------
        axis : int
            The axis over which the operation will be performed
        return_signal : bool
            If False the operation will be performed on the current object. If
            True, the current object will not be modified and the operation
            will be performed in a new signal object that will be returned.

        Returns
        -------
        Depending on the value of the return_signal keyword, nothing or a
        signal instance

        See also
        --------
        sum_in_mask, mean

        Usage
        -----
        >>> import numpy as np
        >>> s = Signal({'data' : np.random.random((64,64,1024))})
        >>> s.data.shape
        (64,64,1024)
        >>> s.mean(-1)
        >>> s.data.shape
        (64,64)
        # If we just want to plot the result of the operation
        s.mean(-1, True).plot()
        """
        if return_signal is True:
            s = self.deepcopy()
        else:
            s = self
        s.data = s.data.mean(axis)
        s.axes_manager.axes.remove(s.axes_manager.axes[axis])
        for _axis in s.axes_manager.axes:
            if _axis.index_in_array > axis:
                _axis.index_in_array -= 1
        s.axes_manager.set_signal_dimension()
        if return_signal is True:
            return s

    def copy(self):
        return (copy.copy(self))

    def deepcopy(self):
        return (copy.deepcopy(self))

#    def sum_in_mask(self, mask):
#        """Returns the result of summing all the spectra in the mask.
#
#        Parameters
#        ----------
#        mask : boolean numpy array
#
#        Returns
#        -------
#        Spectrum
#        """
#        dc = self.data_cube.copy()
#        mask3D = mask.reshape([1,] + list(mask.shape)) * np.ones(dc.shape)
#        dc = (mask3D*dc).sum(1).sum(1) / mask.sum()
#        s = Spectrum()
#        s.data_cube = dc.reshape((-1,1,1))
#        s.get_dimensions_from_cube()
#        utils.copy_energy_calibration(self,s)
#        return s
#
#    def mean(self, axis):
#        """Average the SI over the given axis
#
#        Parameters
#        ----------
#        axis : int
#        """
#        dc = self.data_cube
#        dc = dc.mean(axis)
#        dc = dc.reshape(list(dc.shape) + [1,])
#        self.data_cube = dc
#        self.get_dimensions_from_cube()
#
#    def roll(self, axis = 2, shift = 1):
#        """Roll the SI. see numpy.roll
#
#        Parameters
#        ----------
#        axis : int
#        shift : int
#        """
#        self.data_cube = np.roll(self.data_cube, shift, axis)
#        self._replot()
#

#
#    def get_calibration_from(self, s):
#        """Copy the calibration from another Spectrum instance
#        Parameters
#        ----------
#        s : spectrum instance
#        """
#        utils.copy_energy_calibration(s, self)
#
#    def estimate_variance(self, dc = None, gaussian_noise_var = None):
#        """Variance estimation supposing Poissonian noise
#
#        Parameters
#        ----------
#        dc : None or numpy array
#            If None the SI is used to estimate its variance. Otherwise, the
#            provided array will be used.
#        Note
#        ----
#        The gain_factor and gain_offset from the aquisition parameters are used
#        """
#        print "Variace estimation using the following values:"
#        print "Gain factor = ", self.acquisition_parameters.gain_factor
#        print "Gain offset = ", self.acquisition_parameters.gain_offset
#        if dc is None:
#            dc = self.data_cube
#        gain_factor = self.acquisition_parameters.gain_factor
#        gain_offset = self.acquisition_parameters.gain_offset
#        self.variance = dc*gain_factor + gain_offset
#        if self.variance.min() < 0:
#            if gain_offset == 0 and gaussian_noise_var is None:
#                print "The variance estimation results in negative values"
#                print "Maybe the gain_offset is wrong?"
#                self.variance = None
#                return
#            elif gaussian_noise_var is None:
#                print "Clipping the variance to the gain_offset value"
#                self.variance = np.clip(self.variance, np.abs(gain_offset),
#                np.Inf)
#            else:
#                print "Clipping the variance to the gaussian_noise_var"
#                self.variance = np.clip(self.variance, gaussian_noise_var,
#                np.Inf)
#
#    def calibrate(self, lcE = 642.6, rcE = 849.7, lc = 161.9, rc = 1137.6,
#    modify_calibration = True):
#        dispersion = (rcE - lcE) / (rc - lc)
#        origin = lcE - dispersion * lc
#        print "Energy step = ", dispersion
#        print "Energy origin = ", origin
#        if modify_calibration is True:
#            self.set_new_calibration(origin, dispersion)
#        return origin, dispersion
#

    def _correct_navigation_mask_when_unfolded(
        self,
        navigation_mask=None,
    ):
        #if 'unfolded' in self.history:
        if navigation_mask is not None:
            navigation_mask = navigation_mask.reshape((-1, ))
        return navigation_mask
Ejemplo n.º 5
0
def create_mapped_feature(name, map, **kw):
    return traits.Instance(MappedFeature(name = name, map = map),(), **kw) 
Ejemplo n.º 6
0
def create_int_range_feature(name, **kw):
    return traits.Instance(IntRangeFeature(name = name),(), **kw)    
Ejemplo n.º 7
0
class DeviceAnalogInState(traits.HasTraits):
    """encapsulate all (relevant) analog input state on the device

    Making these variables a member of their own HasTraits class means
    that updates to the device can be treated in an atomic way.
    """
    # Analog input state
    AIN0_enabled = traits.Bool(False)
    AIN0_name = traits.String("AIN0")
    AIN1_enabled = traits.Bool(False)
    AIN1_name = traits.String("AIN1")
    AIN2_enabled = traits.Bool(True)
    AIN2_name = traits.String("AIN2")
    AIN3_enabled = traits.Bool(False)
    AIN3_name = traits.String("AIN3")
    trigger_device = traits.Instance('DeviceModel',transient=True)

    adc_prescaler = traits.Trait(128.0,{
        128.0:0x07,64.0: 0x06,
        # According to Atmel's at90usb1287 manual, faster than this is
        # too fast to get good measurements with 8MHz crystal.
        ## '32': 0x05,'16': 0x04,'8': 0x03,
        ## '4': 0x02,'2': 0x00, # also 0x01
        })
    downsample_bits = traits.Range(low=0,high=2**5-1,value=0)
    AIN_running = traits.Bool(False)
    sample_rate_total = traits.Property(label='Sample rate (Hz), all channels',
                                        depends_on=['adc_prescaler',
                                                    'trigger_device',
                                                    'downsample_bits'])
    sample_rate_chan = traits.Property(label='each channel',
                                       depends_on=['sample_rate_total',
                                                   'AIN0_enabled','AIN1_enabled',
                                                   'AIN2_enabled','AIN3_enabled',])

    # but useful when plotting/saving data
    Vcc = traits.Float(3.3)

    traits_view = View(Group(Group(Item('AIN_running'),
                                   Item(
        'Vcc',
        tooltip=('This does not set Vcc on the AT90USBKEY. Use to record the '
                 'value of Vcc. (default = 3.3V)')),
                                   orientation='horizontal'),
                                   Group(Item('AIN0_enabled',padding=0),
                                         Item('AIN0_name',padding=0),
                                         Item('AIN1_enabled',padding=0),
                                         Item('AIN1_name',padding=0),
                                         padding=0,
                                         orientation='horizontal'),
                                   Group(Item('AIN2_enabled',padding=0),
                                         Item('AIN2_name',padding=0),
                                         Item('AIN3_enabled',padding=0),
                                         Item('AIN3_name',padding=0),
                                         padding=0,
                                         orientation='horizontal'),
                             Group(Item('adc_prescaler'),
                                   Item('downsample_bits'),
                                   orientation='horizontal'),
                             Group(Item('sample_rate_total',
                                        #show_label=False,
                                        style='readonly',
                                        ),
                                   Item('sample_rate_chan',
                                        #show_label=False,
                                        style='readonly',
                                        ),
                                   orientation='horizontal'),
                             ))

    @traits.cached_property
    def _get_sample_rate_total(self):
        if self.trigger_device is not None:
            input_frequency = self.trigger_device.FOSC/self.adc_prescaler
        else:
            input_frequency = 100*1e3 # fake value
        # from USBKEY datasheet:
        if input_frequency < 50*1e3:
            warnings.warn('ADC sample frequency is too slow to get good sampling')
        if input_frequency > 200*1e3:
            warnings.warn('ADC sample frequency is too fast to get good sampling')
        #print 'input_frequency %.1f (kHz)'%(input_frequency/1000.0,)
        clock_cycles_per_sample = 13.0
        clock_adc = input_frequency/clock_cycles_per_sample
        downsample_factor = self.downsample_bits+1
        downsampled_clock_adc = clock_adc/downsample_factor
        return downsampled_clock_adc

    @traits.cached_property
    def _get_sample_rate_chan(self):
        n_chan = sum(map(int,[self.AIN0_enabled,self.AIN1_enabled,
                              self.AIN2_enabled,self.AIN3_enabled]))
        if n_chan == 0:
            return 0.0
        rate = self.sample_rate_total/float(n_chan)
        return rate
Ejemplo n.º 8
0
class SplineExplorer(traits.HasTraits):
    """A simple UI to adjust the parameters and view the resulting splines."""

    v_min = traits.Float(0)
    v_max = traits.Float(15)
    a_min = traits.Float(-5)
    a_max = traits.Float(5)
    j_min = traits.Float(-2.5)
    j_max = traits.Float(2.5)
    mass = traits.Float(400)

    q_i = traits.Float
    v_i = traits.Float
    a_i = traits.Float
    t_i = traits.Float

    q_f = traits.Float(100)
    v_f = traits.Float(0)
    a_f = traits.Float(0)
    t_f = traits.Float(18)

    plot_names = traits.List(
        ["Position", "Jerk", "Velocity", "Power", "Acceleration"])
    active_plots = traits.List

    target_type = traits.Enum(('Position', 'Velocity', 'Acceleration', 'Time'))

    plot_container = traits.Instance(Container)
    recalculate = menu.Action(name="Recalculate", action="recalc")
    dump = menu.Action(name="Print", action="dump")
    save = menu.Action(name="Save", action="save")
    trait_view = ui.View(ui.HGroup(
        ui.VGroup(
            ui.Item(name='target_type', label='Target'),
            ui.VGroup(ui.Item(name='active_plots',
                              show_label=False,
                              editor=ui.CheckListEditor(cols=3,
                                                        name='plot_names'),
                              style='custom'),
                      label='Show Plots',
                      show_border=True),
            ui.VGroup(ui.Item(name='q_i', label='Position'),
                      ui.Item(name='v_i', label='Velocity'),
                      ui.Item(name='a_i', label='Acceleration'),
                      ui.Item(name='t_i', label='Time'),
                      label='Initial Conditions',
                      show_border=True),
            ui.VGroup(ui.Item(
                name='q_f',
                label='Position',
                enabled_when="target_type not in ('Velocity', 'Acceleration')"
            ),
                      ui.Item(name='v_f',
                              label='Velocity',
                              enabled_when="target_type != 'Acceleration'"),
                      ui.Item(name='a_f', label='Acceleration'),
                      ui.Item(name='t_f',
                              label='Time',
                              enabled_when="target_type == 'Time'"),
                      label='Final Conditions:',
                      show_border=True),
            ui.VGroup(ui.Item(name='v_min', label='Min Velocity'),
                      ui.Item(name='v_max', label='Max Velocity'),
                      ui.Item(name='a_min', label='Min Acceleration'),
                      ui.Item(name='a_max', label='Max Acceleration'),
                      ui.Item(name='j_min', label='Min Jerk'),
                      ui.Item(name='j_max', label='Max Jerk'),
                      ui.Item(name='mass', label='Vehicle Mass'),
                      label='Constraints',
                      show_border=True)),
        ui.Item('plot_container', editor=ComponentEditor(), show_label=False)),
                         title='Cubic Spline Explorer',
                         handler=SEButtonHandler(),
                         buttons=[recalculate, dump, save],
                         resizable=True,
                         width=1000)

    def __init__(self):
        super(SplineExplorer, self).__init__()
        self.active_plots = self.plot_names[:]
        self.active_plots.remove("Power")
        self.calc()

    def calc(self):
        try:
            self.solver = TrajectorySolver(self.v_max, self.a_max, self.j_max,
                                           self.v_min, self.a_min, self.j_min)
            self.initial = Knot(self.q_i, self.v_i, self.a_i, self.t_i)
            self.final = Knot(self.q_f, self.v_f, self.a_f, self.t_f)

            if self.target_type == 'Position':
                self.spline = self.solver.target_position(
                    self.initial, self.final)
            elif self.target_type == 'Velocity':
                self.spline = self.solver.target_velocity(
                    self.initial, self.final)
            elif self.target_type == 'Acceleration':
                self.spline = self.solver.target_acceleration(
                    self.initial, self.final)
            elif self.target_type == 'Time':
                self.spline = self.solver.target_time(self.initial, self.final)

            pos = vel = accel = jerk = power = False
            if "Position" in self.active_plots: pos = True
            if "Velocity" in self.active_plots: vel = True
            if "Acceleration" in self.active_plots: accel = True
            if "Jerk" in self.active_plots: jerk = True
            if "Power" in self.active_plots: power = True

            self.plotter = CSplinePlotter(self.spline,
                                          self.v_max,
                                          self.a_max,
                                          self.j_max,
                                          self.v_min,
                                          self.a_min,
                                          self.j_min,
                                          mass=self.mass,
                                          plot_pos=pos,
                                          plot_vel=vel,
                                          plot_accel=accel,
                                          plot_jerk=jerk,
                                          plot_power=power)
            self.plot_container = self.plotter.container
        except:
            self.initial = None
            self.final = None
            self.spline = None
            self.plot_container = Container()

    def display(self):
        self.configure_traits()

    def get_save_filename(self):
        """Get a filename from the user via a FileDialog. Returns the filename."""
        dialog = FileDialog(action="save as",
                            default_filename="spline_00",
                            wildcard="*.png")
        dialog.open()
        if dialog.return_code == OK:
            return dialog.path

    def save(self, path):
        """Save an image of the plot. Does not catch any exceptions."""
        # Create a graphics context of the right size
        win_size = self.plot_container.outer_bounds
        plot_gc = chaco.PlotGraphicsContext(win_size)
        #plot_gc.set_fill_color("transparent")
        # Place the plot component into it
        plot_gc.render_component(self.plot_container)

        # Save out to the user supplied filename
        plot_gc.save(path)

    def _active_plots_changed(self):
        self.calc()

    def _target_type_changed(self):
        self.calc()
Ejemplo n.º 9
0
class Berth(Sim.Process, traits.HasTraits):
    label = traits.Str
    platform_index = traits.Int
    station_id = traits.Int
    vehicle = traits.Instance('pyprt.sim.vehicle.BaseVehicle', None)

    _busy = traits.Bool
    _unload = traits.Bool
    _load = traits.Bool
    _msg_id = traits.Int
    _pax = traits.List(traits.Instance('pyprt.sim.events.Passenger'))

    traits_view =  ui.View(ui.HGroup(ui.Item(name='vehicle',
                                             editor = ui.TextEditor()),
                                     ui.Item('busy')))

    def __init__(self, label, station_id, vehicle, **tr):
        Sim.Process.__init__(self, name=label)
        traits.HasTraits.__init__(self, **tr)
        self.label = label
        self.station_id = station_id
        self.vehicle = vehicle

        # Control flags/settings for the run loop
        self._busy = False
        self._unload = False
        self._load   = False
        self._msg_id = api.NONE_ID
        self._pax = []

    def __str__(self):
        return str( (self.label, str(self.vehicle), str(self._busy)) )

    def is_empty(self):
        """Returns True if the berth is not occupied by a vehicle."""
        return False if self.vehicle else True

    def unload(self, msg_id, passengers):
        self._unload = True
        self._msg_id = msg_id
        self._pax = passengers
        if self.passive:
            Sim.reactivate(self, prior=True)

    def load(self, msg_id, passengers):
        self._load = True
        self._msg_id = msg_id
        self._pax = passengers
        if self.passive:
            Sim.reactivate(self, prior=True)

    def is_busy(self):
        return self._busy

    def run(self):
        station = common.stations[self.station_id]
        while True:
            # Unloading
            if self._unload:
                for pax in reversed(self.vehicle.passengers):
                    self._unload = False
                    self._busy = True
                    yield Sim.hold, self, pax.unload_delay
                    del self.vehicle.passengers[-1] # pax that left vehicle
                    pax.loc = self.station
                    pax.trip_end = Sim.now()
                    if self.station_id == pax.dest_station.ID:
                        pax.trip_success = True
                        common.delivered_pax.add(pax)
                        logging.info("T=%4.3f %s delivered to %s by %s. Unloaded in berth %s",
                                      Sim.now(), pax, self.station_id, self.vehicle, self.label)

                self._busy = False

                if station.passive():
                    Sim.reactivate(station, prior = True)

            # Loading
            elif self._load:
                for pax in self._pax:
                    self._load = False
                    self._busy = True
                    s_notify = api.SimNotifyPassengerLoadStart()
                    s_notify.vID = self.vehicle.ID
                    s_notify.sID = self.station_id
                    s_notify.pID = pax.ID
                    common.interface.send(api.SIM_NOTIFY_PASSENGER_LOAD_START,
                                          s_notify)
                    yield Sim.hold, self, pax.load_delay
                    self.vehicle.passengers.append(pax)
                    pax.trip_boarded = Sim.now()
                    pax.loc = self.vehicle
                    logging.info("T=%4.3f %s loaded into %s at station %s",
                                 Sim.now(), pax, self.vehicle, self.station_id)
                    e_notify = api.SimNotifyPassengerLoadEnd()
                    e_notify.vID = self.vehicle.ID
                    e_notify.sID = self.station_id
                    e_notify.pID = pax.ID
                    common.interface.send(api.SIM_NOTIFY_PASSENGER_LOAD_END,
                                          e_notify)
                    # If using the LOBBY policy, notify that passenger load command
                    # has completed.
                    if self._load_msgID:
                        cmd_notify = api.SimCompletePassengerLoadVehicle()
                        cmd_notify.msgID = self._msgID
                        cmd_notify.vID = self.vehicle.ID
                        cmd_notify.sID = self.station_id
                        cmd_notify.pID = pax.ID
                        common.interface.send(api.SIM_COMPLETE_PASSENGER_LOAD_VEHICLE,
                                          cmd_notify)
                        self._load_msgID = None

                self._busy = False
                if station.passive():
                    Sim.reactivate(station, prior = True)

            else:
                assert not self._busy
                yield Sim.passivate, self
Ejemplo n.º 10
0
class Station(traits.HasTraits):
    platforms = traits.List(traits.Instance(Platform))
    track_segments = traits.Set(traits.Instance(TrackSegment))

    # Passengers waiting at the station.
    _passengers = traits.List(traits.Instance(Passenger))

    traits_view = ui.View(
        ui.VGroup(
            ui.Group(
                ui.Label('Waiting Passengers'),
                ##                                ui.Item(name='_passengers',
                ##                                        show_label = False,
                ##                                        editor=Passenger.table_editor
                ##                                        ),
                show_border=True)),
        title='Station',  # was: self.label
        #                       scrollable = True,
        resizable=True,
        height=700,
        width=470,
        handler=NoWritebackOnCloseHandler())

    table_editor = ui.TableEditor(
        columns=[
            ui_tc.ObjectColumn(name='ID', label='ID', tooltip='Station ID'),
            ui_tc.ObjectColumn(name='label',
                               label='Label',
                               tooltip='Non-unique identifier'),
            ui_tc.ExpressionColumn(
                label='Current Pax',
                format='%d',
                expression='len(object._passengers)',
                tooltip='Number of passengers currently at station.')
            # TODO: The rest...
        ],
        deletable=False,
        editable=False,
        sortable=True,
        sort_model=False,
        auto_size=True,
        orientation='vertical',
        show_toolbar=True,
        reorderable=False,
        rows=15,
        row_factory=traits.This)

    def __init__(self, ID, label, track_segments, storage_entrance_delay,
                 storage_exit_delay, storage_dict):

        traits.HasTraits.__init__(self)
        self.ID = ID
        self.label = label
        self.platforms = []
        self.track_segments = track_segments
        self.storage_entrance_delay = storage_entrance_delay
        self.storage_exit_delay = storage_exit_delay

        # Keyed by the VehicleModel name (string) with FIFO queues as the values.
        self._storage_dict = storage_dict

        self._pax_arrivals_count = 0
        self._pax_departures_count = 0
        self._pax_times = [(Sim.now(), len(self._passengers))
                           ]  # elements are (time, num_pax)
        self._all_passengers = []

    def __str__(self):
        if self.label:
            return self.label
        else:
            return str(self.ID)

    def __hash__(self):
        return hash(self.ID)

    def __eq__(self, other):
        if not isinstance(other, Station):
            return False
        else:
            return self.ID == other.ID

    def __ne__(self, other):
        if not isinstance(other, Station):
            return True
        else:
            return self.ID != other.ID

    def __cmp__(self, other):
        return cmp(self.ID, other.ID)

    def startup(self):
        """Activates all the berths"""
        for platform in self.platforms:
            for berth in platform.berths:
                Sim.activate(berth, berth.run())

    def add_passenger(self, pax):
        """Add a passenger to this station."""
        assert pax not in self._passengers
        self._passengers.append(pax)
        self._all_passengers.append(pax)
        self._pax_times.append((Sim.now(), len(self._passengers)))

    def remove_passenger(self, pax):
        """Remove a passenger from this station, such as when they load into a
        vehicle, or when they storm off in disgust..."""
        self._passengers.remove(pax)
        self._pax_times.append((Sim.now(), len(self._passengers)))

    def get_num_passengers(self):
        return len(self._passengers)

    num_passengers = property(get_num_passengers)

    def get_stored_vehicle_count(self, vehicle_model):
        sv_count = 0
        store = self._storage_dict[vehicle_model]
        sv_count += store.get_stored_vehicle_count()
        return sv_count

    def all_pax_wait_times(self):
        """Returns a list of wait times for all passengers, not just the current ones."""
        times = []
        for pax in self._all_passengers:
            for start, end, loc in pax._wait_times:
                if loc is self:
                    if end is None:
                        times.append(Sim.now() - start)
                    else:
                        times.append(end - start)
        return times

    def curr_pax_wait_times(self):
        """Returns a list of wait times for passengers currently waiting in the
        station."""
        times = []
        for pax in self._passengers:
            for start, end, loc in pax._wait_times:
                if loc is self:
                    if end is None:
                        times.append(Sim.now() - start)
                    else:
                        times.append(end - start)
        return times

    def get_min_all_pax_wait(self):
        try:
            return min(self.all_pax_wait_times())
        except ValueError:  # Empty sequence
            assert len(self.all_pax_wait_times()) == 0
            return 0

    min_all_pax_wait = property(get_min_all_pax_wait)

    def get_mean_all_pax_wait(self):
        try:
            wait_times = self.all_pax_wait_times()
            return sum(wait_times) / len(wait_times)
        except ZeroDivisionError:
            return 0

    mean_all_pax_wait = property(get_mean_all_pax_wait)

    def get_max_all_pax_wait(self):
        try:
            return max(self.all_pax_wait_times())
        except ValueError:  # Empty sequence
            return 0

    max_all_pax_wait = property(get_max_all_pax_wait)

    def get_min_curr_pax_wait(self):
        try:
            return min(self.curr_pax_wait_times())
        except ValueError:  # Empty sequence
            assert len(self.curr_pax_wait_times()) == 0
            return 0

    min_curr_pax_wait = property(get_min_curr_pax_wait)

    def get_mean_curr_pax_wait(self):
        try:
            wait_times = self.curr_pax_wait_times()
            return sum(wait_times) / len(wait_times)
        except ZeroDivisionError:
            return 0

    mean_curr_pax_wait = property(get_mean_curr_pax_wait)

    def get_max_curr_pax_wait(self):
        try:
            return max(self.curr_pax_wait_times())
        except ValueError:  # Empty sequence
            return 0

    max_curr_pax_wait = property(get_max_curr_pax_wait)
Ejemplo n.º 11
0
class Marker(Artist):
    """
    An interface class between the higher level artists and the marker
    primitive that needs to talk to the renderers
    """
    _marker = traits.Instance(MarkerPrimitive, ())
    locs = Array('d')
    path = Instance(Path, ())
    model = mtraits.Model
    sequence = 'markers'
    size = Float(1.0)  # size of the marker in points

    def __init__(self):
        """
        The model is a function taking Nx2->Nx2.  This is where the
        nonlinear transformation can be used
        """
        Artist.__init__(self)
        self._markerid = primitiveID()

    def _locs_default(self):
        return npy.array([[0, 1], [0, 1]], npy.float_)

    def _path_default(self):
        bounds = npy.array([-0.5, -0.5, 1, 1]) * self.size
        return Rectangle().set(bounds=bounds)

    def _path_changed(self, old, new):
        if self.renderer is None:
            # we can't sync up to the underlying path yet
            return
        print 'MARKER _path_changed', self.path._path.pathdata, self._marker.path.pathdata
        old.sync_trait('_path', self._marker, 'path', remove=True)
        new.sync_trait('_path', self._marker, 'path', mutual=False)

    def _update_marker(self):
        'sync the Marker traits with the marker primitive'
        if self.renderer is None:
            # we can't sync up to the underlying path yet
            return

        # sync up the marker affine
        self.path.sync_trait('_path', self._marker, 'path', mutual=False)
        self._marker.affine.follow(self.affine.vec6)
        self.affine.on_trait_change(self._marker.affine.follow, 'vec6')
        self._update_locs()

        print 'MARKER _update_marker', self.path._path.pathdata, self._marker.path.pathdata

    def _update_locs(self):
        print 'MARKER: update markerdata'
        xy = self.locs
        if self.model is not None:
            xy = self.model(xy)

        self._marker.locs = xy

    def draw(self):
        if self.renderer is None or not self.visible: return
        Artist.draw(self)
        self.renderer.render_marker(self._markerid)

    def _renderer_changed(self, old, new):
        # we must make sure the contained artist gets the callback
        # first so we can update the path primitives properly
        self.path._renderer_changed(old, new)
        if old is not None:
            del old.markerd[self._markerid]

        if new is None: return

        print 'MARKER renderer_changed; updating'
        self._marker = renderer.new_marker_primitive()
        new.markerd[self._markerid] = self._marker
        self._update_marker()

    def _model_changed(self, old, new):
        self._update_locs()

    def _locs_changed(self, old, new):
        if len(new.shape) != 2:
            raise ValueError('new must be nx2 array')
        self._update_locs()
Ejemplo n.º 12
0
class Path(Artist):
    """
    An interface class between the higher level artists and the path
    primitive that needs to talk to the renderers
    """
    _path = traits.Instance(PathPrimitive, ())
    antialiased = mtraits.AntiAliased()
    color = mtraits.Color('blue')
    facecolor = mtraits.Color('yellow')
    linestyle = mtraits.LineStyle('-')
    linewidth = mtraits.LineWidth(1.0)
    model = mtraits.Model
    pathdata = traits.Tuple(Array('b'), VertexArray)
    sequence = 'paths'
    zorder = Float(1.0)

    # why have an extra layer separating the PathPrimitive from the
    # Path artist?  The reasons are severalfold, but it is still not
    # clear if this is the better solution.  Doing it this way enables
    # the backends to create their own derived primitves (eg
    # RendererAgg creates PathPrimitiveAgg, and in that class sets up
    # trait listeners to create agg colors and agg paths when the
    # PathPrimitive traits change.  Another reason is that it allows
    # us to handle nonlinear transformation (the "model") at the top
    # layer w/o making the backends understand them.  The current
    # design is create a mapping between backend primitives and
    # primitive artists (Path, Text, Image, etc...) and all of the
    # higher level Artists (Line, Polygon, Axis) will use the
    # primitive artitsts. So only a few artists will need to know how
    # to talk to the backend.  The alternative is to make the backends
    # track and understand the primitive artists themselves.

    def __init__(self):
        """
        The model is a function taking Nx2->Nx2.  This is where the
        nonlinear transformation can be used
        """
        Artist.__init__(self)
        self._pathid = primitiveID()

    def _pathdata_default(self):
        return (npy.array([0, 0], dtype=npy.uint8),
                npy.array([[0, 0], [0, 0]], npy.float_))

    def _update_path(self):
        'sync the Path traits with the path primitive'
        self.sync_trait('linewidth', self._path, mutual=False)
        self.sync_trait('color', self._path, mutual=False)
        self.sync_trait('facecolor', self._path, mutual=False)
        self.sync_trait('antialiased', self._path, mutual=False)

        # sync up the path affine
        self._path.affine.follow(self.affine.vec6)
        self.affine.on_trait_change(self._path.affine.follow, 'vec6')
        self._update_pathdata()

    def _update_pathdata(self):
        #print 'PATH: update pathdata'

        codes, xy = self.pathdata

        #print '    PATH: shapes', codes.shape, xy.shape
        if self.model is not None:
            xy = self.model(xy)

        pathdata = codes, xy

        self._path.pathdata = pathdata

    def draw(self):
        if self.renderer is None or not self.visible: return
        Artist.draw(self)
        self.renderer.render_path(self._pathid)

    def _renderer_changed(self, old, new):
        if old is not None:
            del old.pathd[self._pathid]

        if new is None: return

        #print 'PATH renderer_changed; updating'
        self._path = renderer.new_path_primitive()
        new.pathd[self._pathid] = self._path
        self._update_path()

    def _model_changed(self, old, new):
        self._update_pathdata()

    def _pathdata_changed(self, old, new):
        #print 'PATH: pathdata changed'
        self._update_pathdata()
Ejemplo n.º 13
0
class PointDraggingTool(tools.DragTool):

    component = traits.Instance(enable.Component)

    # The pixel distance from a point that the cursor is still considered
    # to be 'on' the point
    threshold = traits.Int(5)

    # The index of the point being dragged
    _drag_index = traits.Int(-1)

    # The original dataspace values of the index and value datasources
    # corresponding to _drag_index
    _orig_value = traits.Tuple

    def is_draggable(self, x, y):
        # Check to see if (x,y) are over one of the points in self.component
        if self._lookup_point(x, y) is not None:
            return True
        else:
            return False

    def normal_mouse_move(self, event):
        plot = self.component

        ndx = plot.map_index((event.x, event.y), self.threshold)
        if ndx is None:
            if plot.index.metadata.has_key('selections'):
                del plot.index.metadata['selections']
        else:
            plot.index.metadata['selections'] = [ndx]

        plot.invalidate_draw()
        plot.request_redraw()

    def drag_start(self, event):
        plot = self.component
        ndx = plot.map_index((event.x, event.y), self.threshold)
        if ndx is None:
            return
        self._drag_index = ndx
        self._orig_value = (plot.index.get_data()[ndx],
                            plot.value.get_data()[ndx])

    def dragging(self, event):
        plot = self.component

        data_x, data_y = plot.map_data((event.x, event.y))

        plot.index._data[self._drag_index] = data_x
        plot.value._data[self._drag_index] = data_y
        plot.index.data_changed = True
        plot.value.data_changed = True
        plot.request_redraw()

    def drag_cancel(self, event):
        plot = self.component
        plot.index._data[self._drag_index] = self._orig_value[0]
        plot.value._data[self._drag_index] = self._orig_value[1]
        plot.index.data_changed = True
        plot.value.data_changed = True
        plot.request_redraw()

    def drag_end(self, event):
        plot = self.component
        if plot.index.metadata.has_key('selections'):
            del plot.index.metadata['selections']
        plot.invalidate_draw()
        plot.request_redraw()

    def _lookup_point(self, x, y):
        """ Finds the point closest to a screen point if it is within self.threshold

        Parameters
        ==========
        x : float
            screen x-coordinate
        y : float
            screen y-coordinate

        Returns
        =======
        (screen_x, screen_y, distance) of datapoint nearest to the input *(x,y)*.
        If no data points are within *self.threshold* of *(x,y)*, returns None.
        """

        if hasattr(self.component, 'get_closest_point'):
            # This is on BaseXYPlots
            return self.component.get_closest_point((x, y),
                                                    threshold=self.threshold)

        return None
Ejemplo n.º 14
0
class Parameter(traits.HasTraits):
    """represents a lmfit variable in a fit. E.g. the standard deviation in a gaussian
    fit"""
    parameter = traits.Instance(lmfit.Parameter)
    name = traits.Str

    initialValue = traits.Float
    calculatedValue = traits.Float
    vary = traits.Bool(True)

    minimumEnable = traits.Bool(False)
    minimum = traits.Float

    maximumEnable = traits.Bool(False)
    maximum = traits.Float

    stdevError = traits.Float

    def __init__(self, **traitsDict):
        super(Parameter, self).__init__(**traitsDict)
        self.parameter = lmfit.Parameter(name=self.name)

    def _initialValue_changed(self):
        self.parameter.set(value=self.initialValue)

    def _vary_changed(self):
        self.parameter.set(vary=self.vary)

    def _minimum_changed(self):
        if self.minimumEnable:
            self.parameter.set(min=self.minimum)

    def _maximum_changed(self):
        if self.maximumEnabled:
            self.parameter.set(max=self.maximum)

    traits_view = traitsui.View(traitsui.VGroup(
        traitsui.HGroup(
            traitsui.Item("vary", label="vary?", resizable=True),
            traitsui.Item("name",
                          show_label=False,
                          style="readonly",
                          width=0.2,
                          resizable=True),
            traitsui.Item("initialValue",
                          label="initial",
                          show_label=True,
                          resizable=True),
            traitsui.Item("calculatedValue",
                          label="calculated",
                          show_label=True,
                          format_str="%G",
                          style="readonly",
                          width=0.2,
                          resizable=True),
            traitsui.Item("stdevError",
                          show_label=False,
                          format_str=u"\u00B1%G",
                          style="readonly",
                          resizable=True)),
        traitsui.HGroup(
            traitsui.Item("minimumEnable", label="min?", resizable=True),
            traitsui.Item("minimum",
                          label="min",
                          resizable=True,
                          visible_when="minimumEnable"),
            traitsui.Item("maximumEnable", label="max?", resizable=True),
            traitsui.Item("maximum",
                          label="max",
                          resizable=True,
                          visible_when="maximumEnable"))),
                                kind="subpanel")
Ejemplo n.º 15
0
class Fit(traits.HasTraits):

    name = traits.Str(desc="name of fit")
    function = traits.Str(desc="function we are fitting with all parameters")
    variablesList = traits.List(Parameter)
    calculatedParametersList = traits.List(CalculatedParameter)
    xs = None  # will be a scipy array
    ys = None  # will be a scipy array
    zs = None  # will be a scipy array
    performFitButton = traits.Button("Perform Fit")
    getInitialParametersButton = traits.Button("Guess Initial Values")
    usePreviousFitValuesButton = traits.Button("Use Previous Fit")
    drawRequestButton = traits.Button("Draw Fit")
    setSizeButton = traits.Button("Set Initial Size")
    chooseVariablesButtons = traits.Button("choose logged variables")
    logLibrarianButton = traits.Button("librarian")
    logLastFitButton = traits.Button("log current fit")
    removeLastFitButton = traits.Button("remove last fit")
    autoFitBool = traits.Bool(
        False,
        desc=
        "Automatically perform this Fit with current settings whenever a new image is loaded"
    )
    autoGuessBool = traits.Bool(
        False,
        desc=
        "Whenever a fit is completed replace the guess values with the calculated values (useful for increasing speed of the next fit)"
    )
    autoDrawBool = traits.Bool(
        False,
        desc=
        "Once a fit is complete update the drawing of the fit or draw the fit for the first time"
    )
    autoSizeBool = traits.Bool(
        False,
        desc=
        "If TOF variable is read from latest XML and is equal to 0.11ms (or time set in Physics) then it will automatically update the physics sizex and sizey with the Sigma x and sigma y from the gaussian fit"
    )
    logBool = traits.Bool(
        False, desc="Log the calculated and fitted values with a timestamp")
    logName = traits.String(
        desc="name of the scan - will be used in the folder name")
    logDirectory = os.path.join("\\\\ursa", "AQOGroupFolder",
                                "Experiment Humphry", "Data", "eagleLogs")
    latestSequence = os.path.join("\\\\ursa", "AQOGroupFolder",
                                  "Experiment Humphry",
                                  "Experiment Control And Software",
                                  "currentSequence", "latestSequence.xml")

    logFile = traits.File(desc="file path of logFile")

    logAnalyserBool = traits.Bool(
        False, desc="only use log analyser script when True")
    logAnalysers = [
    ]  #list containing full paths to each logAnalyser file to run
    logAnalyserDisplayString = traits.String(
        desc=
        "comma separated read only string that is a list of all logAnalyser python scripts to run. Use button to choose files"
    )
    logAnalyserSelectButton = traits.Button("sel. analyser",
                                            image='@icons:function_node',
                                            style="toolbar")
    xmlLogVariables = []
    imageInspectorReference = None  #will be a reference to the image inspector
    fitting = traits.Bool(False)  #true when performing fit
    fitted = traits.Bool(
        False)  #true when current data displayed has been fitted
    fitSubSpace = traits.Bool(
        False)  #true when current data displayed has been fitted
    startX = traits.Int
    startY = traits.Int
    endX = traits.Int
    endY = traits.Int
    fittingStatus = traits.Str()
    fitThread = None
    fitTimeLimit = traits.Float(
        10.0,
        desc=
        "Time limit in seconds for fitting function. Only has an effect when fitTimeLimitBool is True"
    )
    fitTimeLimitBool = traits.Bool(
        True,
        desc=
        "If True then fitting functions will be limited to time limit defined by fitTimeLimit "
    )
    physics = traits.Instance(
        physicsProperties.physicsProperties.PhysicsProperties)
    #status strings
    notFittedForCurrentStatus = "Not Fitted for Current Image"
    fittedForCurrentImageStatus = "Fit Complete for Current Image"
    currentlyFittingStatus = "Currently Fitting..."
    failedFitStatus = "Failed to finish fit. See logger"
    timeExceededStatus = "Fit exceeded user time limit"

    lmfitModel = traits.Instance(
        lmfit.Model
    )  #reference to the lmfit model  must be initialised in subclass
    mostRecentModelResult = None  # updated to the most recent ModelResult object from lmfit when a fit thread is performed

    fitSubSpaceGroup = traitsui.VGroup(
        traitsui.Item("fitSubSpace", label="Fit Sub Space", resizable=True),
        traitsui.VGroup(traitsui.HGroup(
            traitsui.Item("startX", resizable=True),
            traitsui.Item("startY", resizable=True)),
                        traitsui.HGroup(traitsui.Item("endX", resizable=True),
                                        traitsui.Item("endY", resizable=True)),
                        visible_when="fitSubSpace"),
        label="Fit Sub Space",
        show_border=True)

    generalGroup = traitsui.VGroup(traitsui.Item("name",
                                                 label="Fit Name",
                                                 style="readonly",
                                                 resizable=True),
                                   traitsui.Item("function",
                                                 label="Fit Function",
                                                 style="readonly",
                                                 resizable=True),
                                   fitSubSpaceGroup,
                                   label="Fit",
                                   show_border=True)

    variablesGroup = traitsui.VGroup(traitsui.Item(
        "variablesList",
        editor=traitsui.ListEditor(style="custom"),
        show_label=False,
        resizable=True),
                                     show_border=True,
                                     label="parameters")

    derivedGroup = traitsui.VGroup(traitsui.Item(
        "calculatedParametersList",
        editor=traitsui.ListEditor(style="custom"),
        show_label=False,
        resizable=True),
                                   show_border=True,
                                   label="derived values")

    buttons = traitsui.VGroup(
        traitsui.HGroup(
            traitsui.Item("autoFitBool", label="Auto fit?", resizable=True),
            traitsui.Item("performFitButton", show_label=False,
                          resizable=True)),
        traitsui.HGroup(
            traitsui.Item("autoGuessBool", label="Auto guess?",
                          resizable=True),
            traitsui.Item("getInitialParametersButton",
                          show_label=False,
                          resizable=True)),
        traitsui.HGroup(
            traitsui.Item("autoDrawBool", label="Auto draw?", resizable=True),
            traitsui.Item("drawRequestButton",
                          show_label=False,
                          resizable=True)),
        traitsui.HGroup(
            traitsui.Item("autoSizeBool", label="Auto size?", resizable=True),
            traitsui.Item("setSizeButton", show_label=False, resizable=True)),
        traitsui.HGroup(
            traitsui.Item("usePreviousFitValuesButton",
                          show_label=False,
                          resizable=True)))

    logGroup = traitsui.VGroup(traitsui.HGroup(
        traitsui.Item("logBool", resizable=True),
        traitsui.Item("chooseVariablesButtons",
                      show_label=False,
                      resizable=True)),
                               traitsui.HGroup(
                                   traitsui.Item("logName", resizable=True)),
                               traitsui.HGroup(
                                   traitsui.Item("removeLastFitButton",
                                                 show_label=False,
                                                 resizable=True),
                                   traitsui.Item("logLastFitButton",
                                                 show_label=False,
                                                 resizable=True)),
                               traitsui.HGroup(
                                   traitsui.Item("logAnalyserBool",
                                                 label="analyser?",
                                                 resizable=True),
                                   traitsui.Item("logAnalyserDisplayString",
                                                 show_label=False,
                                                 style="readonly",
                                                 resizable=True),
                                   traitsui.Item("logAnalyserSelectButton",
                                                 show_label=False,
                                                 resizable=True)),
                               label="Logging",
                               show_border=True)

    actionsGroup = traitsui.VGroup(traitsui.Item("fittingStatus",
                                                 style="readonly",
                                                 resizable=True),
                                   logGroup,
                                   buttons,
                                   label="Fit Actions",
                                   show_border=True)
    traits_view = traitsui.View(traitsui.VGroup(generalGroup, variablesGroup,
                                                derivedGroup, actionsGroup),
                                kind="subpanel")

    def __init__(self, **traitsDict):
        super(Fit, self).__init__(**traitsDict)
        self.startX = 0
        self.startY = 0
        self.lmfitModel = lmfit.Model(self.fitFunc)

    def _set_xs(self, xs):
        self.xs = xs

    def _set_ys(self, ys):
        self.ys = ys

    def _set_zs(self, zs):
        self.zs = zs

    def _fittingStatus_default(self):
        return self.notFittedForCurrentStatus

    def _getInitialValues(self):
        """returns ordered list of initial values from variables List """
        return [_.initialValue for _ in self.variablesList]

    def _getParameters(self):
        """creates an lmfit parameters object based on the user input in variablesList """
        return lmfit.Parameters(
            {_.name: _.parameter
             for _ in self.variablesList})

    def _getCalculatedValues(self):
        """returns ordered list of fitted values from variables List """
        return [_.calculatedValue for _ in self.variablesList]

    def _intelligentInitialValues(self):
        """If possible we can auto set the initial parameters to intelligent guesses user can always overwrite them """
        self._setInitialValues(self._getIntelligentInitialValues())

    def _get_subSpaceArrays(self):
        """returns the arrays of the selected sub space. If subspace is not
        activated then returns the full arrays"""
        if self.fitSubSpace:
            xs = self.xs[self.startX:self.endX]
            ys = self.ys[self.startY:self.endY]
            logger.info("xs array sliced length %s " % (xs.shape))
            logger.info("ys array sliced length %s  " % (ys.shape))
            zs = self.zs[self.startY:self.endY, self.startX:self.endX]
            logger.info("zs sub space array %s,%s " % (zs.shape))

            return xs, ys, zs
        else:
            return self.xs, self.ys, self.zs

    def _getIntelligentInitialValues(self):
        """If possible we can auto set the initial parameters to intelligent guesses user can always overwrite them """
        logger.debug("Dummy function should not be called directly")
        return
        #in python this should be a pass statement. I.e. user has to overwrite this

    def fitFunc(self, data, *p):
        """Function that we are trying to fit to. """
        logger.error("Dummy function should not be called directly")
        return
        #in python this should be a pass statement. I.e. user has to overwrite this

    def _setCalculatedValues(self, modelFitResult):
        """updates calculated values with calculated argument """
        parametersResult = modelFitResult.params
        for variable in self.variablesList:
            variable.calculatedValue = parametersResult[variable.name].value

    def _setCalculatedValuesErrors(self, modelFitResult):
        """given the covariance matrix returned by scipy optimize fit
        convert this into stdeviation errors for parameters list and updated
        the stdevError attribute of variables"""
        parametersResult = modelFitResult.params
        for variable in self.variablesList:
            variable.stdevError = parametersResult[variable.name].stderr

    def _setInitialValues(self, guesses):
        """updates calculated values with calculated argument """
        c = 0
        for variable in self.variablesList:
            variable.initialValue = guesses[c]
            c += 1

    def deriveCalculatedParameters(self):
        """Wrapper for subclass definition of deriving calculated parameters
        can put more general calls in here"""
        if self.fitted:
            self._deriveCalculatedParameters()

    def _deriveCalculatedParameters(self):
        """Should be implemented by subclass. should update all variables in calculate parameters list"""
        logger.error("Should only be called by subclass")
        return

    def _fit_routine(self):
        """This function performs the fit in an appropriate thread and 
        updates necessary values when the fit has been performed"""
        self.fitting = True
        if self.fitThread and self.fitThread.isAlive():
            logger.warning(
                "Fitting is already running. You should wait till this fit has timed out before a new thread is started...."
            )
            #logger.warning("I will start a new fitting thread but your previous thread may finish at some undetermined time. you probably had bad starting conditions :( !")
            return
        self.fitThread = FitThread()  #new fitting thread
        self.fitThread.fitReference = self
        self.fitThread.isCurrentFitThread = True  # user can create multiple fit threads on a particular fit but only the latest one will have an effect in the GUI
        self.fitThread.start()
        self.fittingStatus = self.currentlyFittingStatus

    def _perform_fit(self):
        """Perform the fit using scipy optimise curve fit.
        We must supply x and y as one argument and zs as anothger. in the form
        xs: 0 1 2 0 1 2 0 
        ys: 0 0 0 1 1 1 2
        zs: 1 5 6 1 9 8 2
        Hence the use of repeat and tile in  positions and unravel for zs
        initially xs,ys is a linspace array and zs is a 2d image array
        """
        if self.xs is None or self.ys is None or self.zs is None:
            logger.warning(
                "attempted to fit data but had no data inside the Fit object. set xs,ys,zs first"
            )
            return ([], [])
        params = self._getParameters()
        if self.fitSubSpace:  #fit only the sub space
            #create xs, ys and zs which are appropriate slices of the arrays
            xs, ys, zs = self._get_subSpaceArrays()
        else:  #fit the whole array of data (slower)
            xs, ys, zs = self.xs, self.ys, self.zs
        positions = scipy.array([
            scipy.tile(xs, len(ys)),
            scipy.repeat(ys, len(xs))
        ])  #for creating data necessary for gauss2D function
        if self.fitTimeLimitBool:
            modelFitResult = self.lmfitModel.fit(scipy.ravel(zs),
                                                 positions=positions,
                                                 params=params,
                                                 iter_cb=self.getFitCallback(
                                                     time.time()))
        else:  #no iter callback
            modelFitResult = self.lmfitModel.fit(scipy.ravel(zs),
                                                 positions=positions,
                                                 params=params)
        return modelFitResult

    def getFitCallback(self, startTime):
        """returns the callback function that is called at every iteration of fit to check if it 
        has been running too long"""
        def fitCallback(params, iter, resid, *args, **kws):
            """check the time and compare to start time """
            if time.time() - startTime > self.fitTimeLimit:
                raise FitException("Fit time exceeded user limit")

        return fitCallback

    def _performFitButton_fired(self):
        self._fit_routine()

    def _getInitialParametersButton_fired(self):
        self._intelligentInitialValues()

    def _drawRequestButton_fired(self):
        """tells the imageInspector to try and draw this fit as an overlay contour plot"""
        self.imageInspectorReference.addFitPlot(self)

    def _setSizeButton_fired(self):
        """use the sigmaX and sigmaY from the current fit to overwrite the 
        inTrapSizeX and inTrapSizeY parameters in the Physics Instance"""
        self.physics.inTrapSizeX = abs(self.sigmax.calculatedValue)
        self.physics.inTrapSizeY = abs(self.sigmay.calculatedValue)

    def _getFitFuncData(self):
        """if data has been fitted, this returns the zs data for the ideal
        fitted function using the calculated paramters"""
        positions = [
            scipy.tile(self.xs, len(self.ys)),
            scipy.repeat(self.ys, len(self.xs))
        ]  #for creating data necessary for gauss2D function
        zsravelled = self.fitFunc(positions, *self._getCalculatedValues())
        return zsravelled.reshape(self.zs.shape)

    def _logAnalyserSelectButton_fired(self):
        """open a fast file editor for selecting many files """
        fileDialog = FileDialog(action="open files")
        fileDialog.open()
        if fileDialog.return_code == pyface.constant.OK:
            self.logAnalysers = fileDialog.paths
            logger.info("selected log analysers: %s " % self.logAnalysers)
        self.logAnalyserDisplayString = str(
            [os.path.split(path)[1] for path in self.logAnalysers])

    def runSingleAnalyser(self, module):
        """runs the logAnalyser module calling the run function and returns the 
        columnNames and values as a list"""
        exec("import logAnalysers.%s as currentAnalyser" % module)
        reload(
            currentAnalyser
        )  #in case it has changed..#could make this only when user requests
        #now the array also contains the raw image as this may be different to zs if you are using a processor
        if hasattr(self.imageInspectorReference, "rawImage"):
            rawImage = self.imageInspectorReference.rawImage
        else:
            rawImage = None
        return currentAnalyser.run([self.xs, self.ys, self.zs, rawImage],
                                   self.physics.variables, self.variablesList,
                                   self.calculatedParametersList)

    def runAnalyser(self):
        """ if logAnalyserBool is true we perform runAnalyser at the end of _log_fit
        runAnalyser checks that logAnalyser exists and is a python script with a valid run()function
        it then performs the run method and passes to the run function:
        -the image data as a numpy array
        -the xml variables dictionary
        -the fitted paramaters
        -the derived values"""
        for logAnalyser in self.logAnalysers:
            if not os.path.isfile(logAnalyser):
                logger.error(
                    "attempted to runAnalyser but could not find the logAnalyser File: %s"
                    % logAnalyser)
                return
        #these will contain the final column names and values
        finalColumns = []
        finalValues = []
        #iterate over each selected logAnalyser get the column names and values and add them to the master lists
        for logAnalyser in self.logAnalysers:
            directory, module = os.path.split(logAnalyser)
            module, ext = os.path.splitext(module)
            if ext != ".py":
                logger.error("file was not a python module. %s" % logAnalyser)
            else:
                columns, values = self.runSingleAnalyser(module)
                finalColumns.extend(columns)
                finalValues.extend(values)
        return finalColumns, finalValues

    def mostRecentModelFitReport(self):
        """returns the lmfit fit report of the most recent 
        lmfit model results object"""
        if self.mostRecentModelResult is not None:
            return lmfit.fit_report(self.mostRecentModelResult) + "\n\n"
        else:
            return "No fit performed"

    def getCalculatedParameters(self):
        """useful for print returns tuple list of calculated parameter name and value """
        return [(_.name, _.value) for _ in self.calculatedParametersList]

    def _log_fit(self):

        if self.logName == "":
            logger.warning("no log file defined. Will not log")
            return
        #generate folders if they don't exist
        logFolder = os.path.join(self.logDirectory, self.logName)
        if not os.path.isdir(logFolder):
            logger.info("creating a new log folder %s" % logFolder)
            os.mkdir(logFolder)

        imagesFolder = os.path.join(logFolder, "images")
        if not os.path.isdir(imagesFolder):
            logger.info("creating a new images Folder %s" % imagesFolder)
            os.mkdir(imagesFolder)

        commentsFile = os.path.join(logFolder, "comments.txt")
        if not os.path.exists(commentsFile):
            logger.info("creating a comments file %s" % commentsFile)
            open(commentsFile,
                 "a+").close()  #create a comments file in every folder!

        firstSequenceCopy = os.path.join(logFolder,
                                         "copyOfInitialSequence.ctr")
        if not os.path.exists(firstSequenceCopy):
            logger.info("creating a copy of the first sequence %s -> %s" %
                        (self.latestSequence, firstSequenceCopy))
            shutil.copy(self.latestSequence, firstSequenceCopy)

        if self.imageInspectorReference.model.imageMode == "process raw image":  #if we are using a processor, save the details of the processor used to the log folder
            processorParamtersFile = os.path.join(logFolder,
                                                  "processorOptions.txt")
            processorPythonScript = os.path.join(logFolder,
                                                 "usedProcessor.py")  #TODO!
            if not os.path.exists(processorParamtersFile):
                with open(processorParamtersFile, "a+") as processorParamsFile:
                    string = str(self.imageInspectorReference.model.
                                 chosenProcessor) + "\n"
                    string += str(self.imageInspectorReference.model.processor.
                                  optionsDict)
                    processorParamsFile.write(string)

        logger.debug("finished all checks on log folder")
        #copy current image
        try:
            shutil.copy(self.imageInspectorReference.selectedFile,
                        imagesFolder)
        except IOError as e:
            logger.error("Could not copy image. Got IOError: %s " % e.message)
        except Exception as e:
            logger.error("Could not copy image. Got %s: %s " %
                         (type(e), e.message))
            raise e
        logger.info("copying current image")
        self.logFile = os.path.join(logFolder, self.logName + ".csv")

        #analyser logic
        if self.logAnalyserBool:  #run the analyser script as requested
            logger.info(
                "log analyser bool enabled... will attempt to run analyser script"
            )
            analyserResult = self.runAnalyser()
            logger.info("analyser result = %s " % list(analyserResult))
            if analyserResult is None:
                analyserColumnNames = []
                analyserValues = []
                #analyser failed. continue as if nothing happened
            else:
                analyserColumnNames, analyserValues = analyserResult
        else:  #no analyser enabled
            analyserColumnNames = []
            analyserValues = []

        if not os.path.exists(self.logFile):
            variables = [_.name for _ in self.variablesList]
            calculated = [_.name for _ in self.calculatedParametersList]
            times = ["datetime", "epoch seconds"]
            info = ["img file name"]
            xmlVariables = self.xmlLogVariables
            columnNames = times + info + variables + calculated + xmlVariables + analyserColumnNames
            with open(
                    self.logFile, 'ab+'
            ) as logFile:  # note use of binary file so that windows doesn't write too many /r
                writer = csv.writer(logFile)
                writer.writerow(columnNames)
        #column names already exist so...
        logger.debug("copying current image")
        variables = [_.calculatedValue for _ in self.variablesList]
        calculated = [_.value for _ in self.calculatedParametersList]
        now = time.time()  #epoch seconds
        timeTuple = time.localtime(now)
        date = time.strftime("%Y-%m-%dT%H:%M:%S", timeTuple)
        times = [date, now]
        info = [self.imageInspectorReference.selectedFile]
        xmlVariables = [
            self.physics.variables[varName] for varName in self.xmlLogVariables
        ]
        data = times + info + variables + calculated + xmlVariables + analyserValues

        with open(self.logFile, 'ab+') as logFile:
            writer = csv.writer(logFile)
            writer.writerow(data)

    def _logLastFitButton_fired(self):
        """logs the fit. User can use this for non automated logging. i.e. log
        particular fits"""
        self._log_fit()

    def _removeLastFitButton_fired(self):
        """removes the last line in the log file """
        logFolder = os.path.join(self.logDirectory, self.logName)
        self.logFile = os.path.join(logFolder, self.logName + ".csv")
        if self.logFile == "":
            logger.warning("no log file defined. Will not log")
            return
        if not os.path.exists(self.logFile):
            logger.error(
                "cant remove a line from a log file that doesn't exist")
        with open(self.logFile, 'r') as logFile:
            lines = logFile.readlines()
        with open(self.logFile, 'wb') as logFile:
            logFile.writelines(lines[:-1])

    def saveLastFit(self):
        """saves result of last fit to a txt/csv file. This can be useful for live analysis
        or for generating sequences based on result of last fit"""
        try:
            with open(
                    self.imageInspectorReference.cameraModel + "-" +
                    self.physics.species + "-" + "lastFit.csv",
                    "wb") as lastFitFile:
                writer = csv.writer(lastFitFile)
                writer.writerow(["time", time.time()])
                for variable in self.variablesList:
                    writer.writerow([variable.name, variable.calculatedValue])
                for variable in self.calculatedParametersList:
                    writer.writerow([variable.name, variable.value])
        except Exception as e:
            logger.error("failed to save last fit to text file. message %s " %
                         e.message)

    def _chooseVariablesButtons_fired(self):
        self.xmlLogVariables = self.chooseVariables()

    def _usePreviousFitValuesButton_fired(self):
        """update the guess initial values with the value from the last fit """
        logger.info(
            "use previous fit values button fired. loading previous initial values"
        )
        self._setInitialValues(self._getCalculatedValues())

    def chooseVariables(self):
        """Opens a dialog asking user to select columns from a data File that has
        been selected. THese are then returned as a string suitable for Y cols input"""
        columns = self.physics.variables.keys()
        columns.sort()
        values = zip(range(0, len(columns)), columns)

        checklist_group = traitsui.Group(
            '10',  # insert vertical space
            traitsui.Label('Select the additional variables you wish to log'),
            traitsui.UItem('columns',
                           style='custom',
                           editor=traitsui.CheckListEditor(values=values,
                                                           cols=6)),
            traitsui.UItem('selectAllButton'))

        traits_view = traitsui.View(checklist_group,
                                    title='CheckListEditor',
                                    buttons=['OK'],
                                    resizable=True,
                                    kind='livemodal')

        col = ColumnEditor(numberOfColumns=len(columns))
        try:
            col.columns = [
                columns.index(varName) for varName in self.xmlLogVariables
            ]
        except Exception as e:
            logger.error(
                "couldn't selected correct variable names. Returning empty selection"
            )
            logger.error("%s " % e.message)
            col.columns = []
        col.edit_traits(view=traits_view)
        logger.debug("value of columns selected = %s ", col.columns)
        logger.debug("value of columns selected = %s ",
                     [columns[i] for i in col.columns])
        return [columns[i] for i in col.columns]

    def _logLibrarianButton_fired(self):
        """opens log librarian for current folder in logName box. """
        logFolder = os.path.join(self.logDirectory, self.logName)
        if not os.path.isdir(logFolder):
            logger.error(
                "cant open librarian on a log that doesn't exist.... Could not find %s"
                % logFolder)
            return
        librarian = plotObjects.logLibrarian.Librarian(logFolder=logFolder)
        librarian.edit_traits()
Ejemplo n.º 16
0
class Passenger(PrtEvent):
    """A passenger."""
    mass = traits.Int()
    _loc = traits.Either(traits.Instance('pyprt.sim.station.Station'),
                         traits.Instance('pyprt.sim.vehicle.BaseVehicle'),
                         None)

    traits_view = ui.View(
        ui.Item(name='label'),
        ui.Item(name='ID'),
        ui.Item(name='loc'),
        ui.Item(name='mass'),  # in kg
        ui.Item(name='trip_success'),
        ui.Item(name='wait_time', format_func=sec_to_hms),
        ui.Item(name='walk_time', format_func=sec_to_hms),
        ui.Item(name='ride_time', format_func=sec_to_hms),
        ui.Item(name='will_share'),
        ui.Item(name='src_station'),
        ui.Item(name='dest_station'),
        ui.Item(name='load_delay', format_func=sec_to_hms),
        ui.Item(name='unload_delay', format_func=sec_to_hms),
        style='readonly',
        handler=NoWritebackOnCloseHandler())

    ##    # Subset of passenger data in table format.
    ##    table_editor = ui.TableEditor(
    ##            columns = [ui_tc.ObjectColumn(name='ID', label='ID'),
    ##                       ui_tc.ObjectColumn(name='src_station', label='Origin'),
    ##                       ui_tc.ObjectColumn(name='dest_station', label='Destination'),
    ##                       ui_tc.ExpressionColumn(label='Waiting',
    ##                                              expression='sec_to_hms(object.wait_time)',
    ##                                              globals={'sec_to_hms':sec_to_hms},
    ##                                              tooltip='Time spent waiting'),
    ##                       ui_tc.ExpressionColumn(label='Riding',
    ##                                              expression='sec_to_hms(object.ride_time)',
    ##                                              globals={'sec_to_hms':sec_to_hms},
    ##                                              tooltip='Time spent riding'),
    ##                       ui_tc.ExpressionColumn(label='Walking',
    ##                                              expression='sec_to_hms(object.walk_time)',
    ##                                              globals={'sec_to_hms':sec_to_hms},
    ##                                              tooltip='Time spent walking'),
    ##                       ui_tc.ExpressionColumn(label='Total',
    ##                                              expression='sec_to_hms(object.total_time)',
    ##                                              globals={'sec_to_hms':sec_to_hms},
    ##                                              tooltip='Total time spent on trip'),
    ##                       ui_tc.ObjectColumn(name='trip_success', label='Success',
    ##                                          tooltip='Sucessfully reached destination'),
    ##                       ui_tc.ObjectColumn(name='loc', label='Current Location')
    ##                      ],
    ##            other_columns = [ui_tc.ObjectColumn(name='label', label='Label'),
    ##                       ui_tc.ObjectColumn(name='will_share', label='Will Share',
    ##                                          tooltip='Willing to share vehicle when destinations match'),
    ##                       ui_tc.ObjectColumn(name='load_delay', label='Load Delay',
    ##                                          tooltip='Time that passenger takes to embark'),
    ##                       ui_tc.ObjectColumn(name='unload_delay', label='Unload Delay',
    ##                                          tooltip='Time that passenger takes to disembark'),
    ##                       ui_tc.ObjectColumn(name='mass', label='Mass',
    ##                                          tooltip='Includes luggage (kg)')
    ##                       ],
    ##                       # more...
    ##            deletable = False,
    ##            editable=False,
    ##            sortable = True,
    ##            sort_model = False,
    ##            auto_size = True,
    ##            orientation = 'vertical',
    ##            show_toolbar = True,
    ##            reorderable = False,
    ##            rows = 15,
    ##            row_factory = traits.This)

    def __init__(self, time, ID, src_station, dest_station, load_delay,
                 unload_delay, will_share, mass):
        super(Passenger, self).__init__(time, ID)
        self.src_station = src_station
        self.dest_station = dest_station
        self.load_delay = load_delay
        self.unload_delay = unload_delay
        self.will_share = will_share  # Willing to share pod (if same dest)
        self.mass = mass
        self.trip_success = False
        self._loc = src_station

        # For the following, where start and end are times in seconds, with 0 being the start of the sim.
        self._wait_times = [[time, None, self._loc]
                            ]  # contains triples: [[start, end, loc], ...]
        self._walk_times = [
        ]  # containing pairs:  [[start, end], [start, end], ...]
        self._ride_times = [
        ]  # contains triples: [[start, end, vehicle], [start, end, vehicle], ...]

        self._start_time = time
        self._end_time = None

    @property
    def wait_time(self):  # in seconds
        total = 0
        for start, end, loc in self._wait_times:
            if end is None:
                total += Sim.now() - start
            else:
                total += end - start
        return total

    @property
    def ride_time(self):  # in seconds
        total = 0
        for start, end, vehicle in self._ride_times:
            if end is None:
                total += Sim.now() - start
            else:
                total += end - start
        return total

    @property
    def walk_time(self):  # in seconds
        total = 0
        for start, end in self._walk_times:
            if end is None:
                total += Sim.now() - start
            else:
                total += end - start
        return total

    @property
    def total_time(self):
        if self._end_time is None:
            return Sim.now() - self._start_time
        else:
            return self._end_time - self._start_time

    def get_loc(self):
        return self._loc

    def set_loc(self, loc):
        """Changes the loc, and keeps track of how much time is spent in each
        mode of transit: waiting, riding, or walking."""
        ### Track time spent in each mode of transit ###
        if self._loc is None:  # Was walking
            self._walk_times[-1][1] = Sim.now()
        elif hasattr(self._loc, 'vehicle_mass'):  # was in vehicle
            self._ride_times[-1][1] = Sim.now()
        elif hasattr(self._loc, 'platforms'):  # was at station
            self._wait_times[-1][1] = Sim.now()
        else:
            raise Exception("Unknown loc type")

        ### Note if trip is completed. ###
        if loc is self.dest_station:
            self._end_time = Sim.now()
            self.trip_success = True

        ### More time tracking ###
        if not self.trip_success:
            if loc is None: self._walk_times.append([Sim.now(), None])
            elif hasattr(loc, 'vehicle_mass'):
                self._ride_times.append([Sim.now(), None,
                                         loc])  # isinstance(loc, BaseVehicle)
            elif hasattr(loc, 'platforms'):
                self._wait_times.append([Sim.now(), None,
                                         loc])  # isinstance(loc, TrackSegment)
            else:
                raise Exception("Unknown loc type")

        self._loc = loc

    loc = property(
        get_loc,
        set_loc,
        doc="loc is expected to be a Station, a "
        "Vehicle, or None (which indicates walking from one station "
        "to another). Setting the loc has side-effects, see set_loc.")

    def walk(self, origin_station, dest_station, travel_time, cmd_msg, cmd_id):
        assert self._loc is origin_station
        assert travel_time >= 0
        assert isinstance(cmd_msg, api.CtrlCmdPassengerWalk)
        assert isinstance(cmd_id, int)
        self.loc = None
        common.AlarmClock(Sim.now() + travel_time, self._post_walk,
                          dest_station, cmd_msg, cmd_id)

    def _post_walk(self, dest_station, cmd_msg, cmd_id):
        """Updates stats, changes location, and sends a SimCompletePassengerWalk
        message. To be called once the walk is complete."""
        assert self._loc is None
        self.loc = dest_station

        msg = api.SimCompletePassengerWalk()
        msg.msgID = cmd_id
        msg.cmd.CopyFrom(cmd_msg)
        msg.time = Sim.now()
        common.interface.send(api.SIM_COMPLETE_PASSENGER_WALK, msg)

    def fill_PassengerStatus(self, ps):
        ps.pID = self.ID
        # I'd much rather use isinstance checks, but circular imports are killing me
        if self._loc is None:
            ps.loc_type = api.WALKING
            ps.locID = api.NONE_ID
        elif hasattr(self._loc, 'vehicle_mass'):  # a vehicle
            ps.loc_type = api.VEHICLE
            ps.locID = self._loc.ID
        elif hasattr(self._loc, 'platforms'):  # a station
            ps.loc_type = api.STATION
            ps.locID = self._loc.ID
        else:
            raise Exception, "Unknown passenger location type: %s" % self._loc

        ps.src_stationID = self.src_station.ID
        ps.dest_stationID = self.dest_station.ID
        ps.creation_time = self._start_time
        ps.mass = self.mass
        ps.trip_success = self.trip_success
Ejemplo n.º 17
0
class Berth(Sim.Process, traits.HasTraits):
    ID = traits.Int
    platform = traits.Instance('Platform')
    station = traits.Instance('Station')
    start_pos = traits.Float  # The 'tail' end of the berth
    end_pos = traits.Float  # The 'nose' end of the berth
    unloading = traits.Bool(False)
    loading = traits.Bool(False)
    storage_entrance = traits.Bool(False)
    storage_exit = traits.Bool(False)

    DISEMBARK = "DISEMBARK"
    EMBARK = "EMBARK"
    ENTER_STORAGE = "ENTER_STORAGE"
    EXIT_STORAGE = "EXIT_STORAGE"

    _action = traits.Enum(None, DISEMBARK, EMBARK, ENTER_STORAGE, EXIT_STORAGE)
    _error_continue = traits.Bool(False)

    ##    traits_view =  ui.View(ui.HGroup(ui.Item(name='vehicle',
    ##                                             editor = ui.TextEditor()),
    ##                                     ui.Item('busy')))

    def __init__(self, ID, station, platform, start_pos, end_pos, unloading,
                 loading, storage_entrance, storage_exit):
        Sim.Process.__init__(self, name='berth_' + str(ID))
        traits.HasTraits.__init__(self)
        self.ID = ID
        self.station = station
        self.platform = platform
        self.start_pos = start_pos
        self.end_pos = end_pos
        self.unloading = unloading
        self.loading = loading
        self.storage_entrance = storage_entrance
        self.storage_exit = storage_exit

        # Record keeping for statistics
        ##        self._occupied_times = [Sim.now(), self._vehicles[:]] # elements are (time, list_of_occupying_vehicle_refs)
        self._busy_times = []  # elements are: (time, busy_state)
        self._all_passengers = [
        ]  # record of all passengers, including those who have departed

        # Control flags/settings for the run loop
        self._busy = False  # use the self._busy property to enable record gathering

        self._action = None
        self._fnc_args = None
        self._error_continue = False

    def __str__(self):
        return self.name


##    def is_empty(self):
##        """Returns True if the berth is not occupied by a vehicle."""
##        return False if self.vehicle else True

    def disembark(self, vehicle, passengers, cmd_msg, cmd_msg_id):
        """If ordering matters, note that passengers at the end of the list
        are serviced first."""
        assert not self._busy
        self._action = Berth.DISEMBARK
        self._fnc_args = (vehicle, passengers, cmd_msg, cmd_msg_id)
        if self.passive:
            Sim.reactivate(self, prior=True)

    def embark(self, vehicle, passengers, cmd_msg, cmd_msg_id):
        """If ordering matters, note that passengers at the end of the list
        are serviced first."""
        assert not self._busy
        self._action = Berth.EMBARK
        self._fnc_args = (vehicle, passengers, cmd_msg, cmd_msg_id)
        if self.passive:
            Sim.reactivate(self, prior=True)

    def enter_storage(self, vehicle, cmd_msg, cmd_msg_id):
        assert not self._busy
        self._action = Berth.ENTER_STORAGE
        self._fnc_args = (vehicle, cmd_msg, cmd_msg_id)
        if self.passive:
            Sim.reactivate(self, prior=True)

    def exit_storage(self, position, model_name, cmd_msg, cmd_msg_id):
        assert not self._busy
        self._action = Berth.EXIT_STORAGE
        self._fnc_args = (position, model_name, cmd_msg, cmd_msg_id)
        if self.passive:
            Sim.reactivate(self, prior=True)

    def get_busy(self):
        return self.__busy

    def set_busy(self, value):
        self._busy_times.append((Sim.now(), value))
        self.__busy = value

    _busy = property(get_busy, set_busy)

    def is_busy(self):
        return self.__busy

    def run(self):
        """ The main loop for the Berth."""
        # A Berth has four different tasks to accomplish but only one active loop.
        while True:
            try:
                if self._action is Berth.DISEMBARK:
                    for disembark_delay in self._do_disembark(*self._fnc_args):
                        yield Sim.hold, self, disembark_delay  # Wait while passenger disembarks

                elif self._action is Berth.EMBARK:
                    for embark_delay in self._do_embark(*self._fnc_args):
                        yield Sim.hold, self, embark_delay

                elif self._action is Berth.ENTER_STORAGE:
                    for enter_delay in self._do_enter_storage(*self._fnc_args):
                        yield Sim.hold, self, enter_delay

                elif self._action is Berth.EXIT_STORAGE:
                    for exit_delay in self._do_exit_storage(*self._fnc_args):
                        yield Sim.hold, self, exit_delay

            except VehicleOutOfPositionError as err:
                nose_pos, tail_pos = err.vehicle.get_positions()
                logging.info(
                    "T=%4.3f Vehicle not in berth for attempted %s. Vehicle: %s, Berth: %s, Platform: %s, Station: %s, DisembarkCmdId: %s, vNosePos: %s, vNoseLoc %s, vTailPos: %s, vTailLoc: %s, berth.start_pos: %s, berth.end_pos: %s",
                    Sim.now(), self._action, err.vehicle.ID, self.ID,
                    self.platform.ID, self.station.ID, err.msg_id, nose_pos,
                    err.vehicle.loc, tail_pos, err.vehicle.tail_loc,
                    self.start_pos, self.end_pos)
                error_msg = api.SimMsgBodyInvalidId()
                error_msg.id_type = api.VEHICLE
                error_msg.msgID = err.msg_id
                error_msg.ID = err.vehicle.ID
                common.interface.send(api.SIM_MSG_BODY_INVALID_ID, error_msg)
                self._busy = False

            except PassengerNotAvailableError as err:
                logging.info(
                    "T=%4.3f Passenger not available for attempted %s. Vehicle: %s, Berth: %s, Platform: %s, Station: %s, DisembarkCmdId: %s, Passenger: %s",
                    Sim.now(), self._action, err.vehicle.ID, self.ID,
                    self.platform.ID, self.station.ID, err.msg_id, err.pax.ID)
                error_msg = api.SimMsgBodyInvalidId()
                error_msg.msgID = err.msg_id
                error_msg.id_type = api.PASSENGER
                error_msg.ID = err.pax.ID
                common.interface.send(api.SIM_MSG_BODY_INVALID_ID, error_msg)
                self._error_continue = True  # process other passengers

            except VehicleFullError as err:
                logging.info(
                    "T=%4.3f Action %s failed since vehicle is at max passenger capacity. Vehicle: %s, Berth: %s, Platform: %s, Station: %s, EmbarkCmdId: %s, Passenger: %s",
                    Sim.now(), self._action, err.vehicle.ID, self.ID,
                    self.platform.ID, self.station.ID, err.msg_id, err.pax.ID)
                error_msg = api.SimMsgBodyInvalidId()
                error_msg.msgID = err.msg_id
                error_msg.id_type = api.PASSENGER
                error_msg.ID = err.pax.ID
                common.interface.send(api.SIM_MSG_BODY_INVALID_ID, error_msg)
                self._error_continue = True  # process other passengers

            if not self._error_continue:
                # Reset state
                self._action = None
                self._fnc_args = None
                assert not self._busy
                yield Sim.passivate, self
            else:
                # Go through the loop again
                self._error_continue = False

    def _do_disembark(self, vehicle, passengers, cmd_msg, cmd_msg_id):
        self._busy = True
        while passengers:
            pax = passengers.pop()
            self._do_disembark_pax_start(pax, vehicle, cmd_msg_id)
            yield pax.unload_delay  # Wait while passenger disembarks
            self._do_disembark_pax_finish(pax, vehicle, cmd_msg_id)

        self._busy = False

        # Notify controller that all passenger disembarkments are done.
        cmd_complete = api.SimCompletePassengersDisembark()
        cmd_complete.msgID = cmd_msg_id
        cmd_complete.cmd.CopyFrom(cmd_msg)
        cmd_complete.time = Sim.now()
        common.interface.send(api.SIM_COMPLETE_PASSENGERS_DISEMBARK,
                              cmd_complete)

    def _do_disembark_pax_start(self, pax, vehicle, cmd_msg_id):
        # Error if vehicle not parked in berth
        if not vehicle.is_parked_between(self.start_pos, self.end_pos,
                                         self.platform.track_segment):
            raise VehicleOutOfPositionError(vehicle, cmd_msg_id)

        # Error if pax not in the vehicle
        if pax not in vehicle.passengers:
            raise PassengerNotAvailableError(pax, vehicle, cmd_msg_id)

        # Notify controller that disembark of this passenger is starting
        start_msg = api.SimNotifyPassengerDisembarkStart()
        start_msg.vID = vehicle.ID
        start_msg.sID = self.station.ID
        start_msg.platformID = self.platform.ID
        start_msg.pID = pax.ID
        start_msg.berthID = self.ID
        start_msg.time = Sim.now()
        common.interface.send(api.SIM_NOTIFY_PASSENGER_DISEMBARK_START,
                              start_msg)

    def _do_disembark_pax_finish(self, pax, vehicle, cmd_msg_id):
        # Error if vehicle is not still parked in berth
        if not vehicle.is_parked_between(self.start_pos, self.end_pos,
                                         self.platform.track_segment):
            raise VehicleOutOfPositionError(vehicle, cmd_msg_id)

        # Move the passenger from the vehicle to the station
        vehicle.disembark(pax)
        pax.loc = self.station

        # Note if the passenger has arrived at final dest (may not be
        # the case with non-PRT systems)
        if self.station.ID == pax.dest_station.ID:
            pax.trip_end = Sim.now()
            pax.trip_success = True
            common.delivered_pax.add(pax)
            self.station._pax_arrivals_count += 1
            self.station._all_passengers.append(pax)
            logging.info(
                "T=%4.3f %s delivered to platform %s in %s by %s (%d out of %d), disembarked in berth %s",
                Sim.now(), pax, self.platform.ID, self.station.ID, vehicle.ID,
                vehicle.get_pax_count(), vehicle.max_pax_capacity, self.ID)
        else:
            self.station.add_passenger(pax)
            self.station._arrivals_count += 1

        # Notify that disembark of this passenger is complete
        end_msg = api.SimNotifyPassengerDisembarkEnd()
        end_msg.vID = vehicle.ID
        end_msg.sID = self.station.ID
        end_msg.platformID = self.platform.ID
        end_msg.pID = pax.ID
        end_msg.berthID = self.ID
        end_msg.time = Sim.now()
        common.interface.send(api.SIM_NOTIFY_PASSENGER_DISEMBARK_END, end_msg)

    def _do_embark(self, vehicle, passengers, cmd_msg, cmd_msg_id):
        self._busy = True
        while passengers:
            pax = passengers.pop()
            self._do_embark_pax_start(pax, vehicle, cmd_msg_id)
            yield pax.load_delay
            self._do_embark_pax_finish(pax, vehicle, cmd_msg_id)

        self._busy = False

        # Notify controller that all passenger embarkments are done.
        cmd_complete = api.SimCompletePassengersEmbark()
        cmd_complete.msgID = cmd_msg_id
        cmd_complete.cmd.CopyFrom(cmd_msg)
        cmd_complete.time = Sim.now()
        common.interface.send(api.SIM_COMPLETE_PASSENGERS_EMBARK, cmd_complete)

    def _do_embark_pax_start(self, pax, vehicle, cmd_msg_id):
        # Error if vehicle not parked in berth
        if not vehicle.is_parked_between(self.start_pos, self.end_pos,
                                         self.platform.track_segment):
            raise VehicleOutOfPositionError(vehicle, cmd_msg_id)

        # Error if pax not at the station
        if pax not in self.station._passengers:
            raise PassengerNotAvailableError(pax, vehicle, cmd_msg_id)

        # Error if the vehicle is at full capacity
        if vehicle.get_pax_count() >= vehicle.max_pax_capacity:
            raise VehicleFullError(pax, vehicle, cmd_msg_id)

        # Notify controller that embark of this passenger is starting
        start_msg = api.SimNotifyPassengerEmbarkStart()
        start_msg.vID = vehicle.ID
        start_msg.sID = self.station.ID
        start_msg.platformID = self.platform.ID
        start_msg.pID = pax.ID
        start_msg.berthID = self.ID
        start_msg.time = Sim.now()
        common.interface.send(api.SIM_NOTIFY_PASSENGER_EMBARK_START, start_msg)

    def _do_embark_pax_finish(self, pax, vehicle, cmd_msg_id):
        # Error if vehicle is not still parked in berth
        if not vehicle.is_parked_between(self.start_pos, self.end_pos,
                                         self.platform.track_segment):
            raise VehicleOutOfPositionError(vehicle, cmd_msg_id)

        # Move passenger's location to the vehicle
        vehicle.embark(pax)
        pax.loc = vehicle
        self.station._pax_departures_count += 1
        self.station.remove_passenger(pax)
        pax.trip_boarded = Sim.now()
        logging.info(
            "T=%4.3f %s loaded into Vehicle %s (%d out of %d) at station %s, platform %s, berth %s ",
            Sim.now(), pax, vehicle.ID, vehicle.get_pax_count(),
            vehicle.max_pax_capacity, self.station.ID, self.platform.ID,
            self.ID)

        # Notify that embark of this passenger is complete
        end_msg = api.SimNotifyPassengerEmbarkEnd()
        end_msg.vID = vehicle.ID
        end_msg.sID = self.station.ID
        end_msg.platformID = self.platform.ID
        end_msg.pID = pax.ID
        end_msg.berthID = self.ID
        end_msg.time = Sim.now()
        common.interface.send(api.SIM_NOTIFY_PASSENGER_EMBARK_END, end_msg)

    def _do_enter_storage(self, vehicle, cmd_msg, cmd_msg_id):
        if not vehicle.is_parked_between(self.start_pos, self.end_pos,
                                         self.platform.track_segment):
            raise VehicleOutOfPositionError(vehicle, cmd_msg_id)

        storage = self.station._storage_dict[vehicle.model_name]
        storage._reserve_slot()
        self._busy = True
        yield self.station.storage_entrance_delay
        if not vehicle.is_parked_between(self.start_pos, self.end_pos,
                                         self.platform.track_segment):
            raise VehicleOutOfPositionError(vehicle, cmd_msg_id)
        storage._store_vehicle(vehicle)

        self._busy = False

        # Notify controller that vehicle entering storage is done.
        cmd_complete = api.SimCompleteStorageEnter()
        cmd_complete.msgID = cmd_msg_id
        cmd_complete.cmd.CopyFrom(cmd_msg)
        cmd_complete.time = Sim.now()
        common.interface.send(api.SIM_COMPLETE_STORAGE_ENTER, cmd_complete)

    def _do_exit_storage(self, position, model_name, cmd_msg, cmd_msg_id):
        storage = self.station._storage_dict[model_name]
        storage._reserve_vehicle()
        self._busy = True
        yield self.station.storage_exit_delay
        vehicle = storage._request_vehicle(position,
                                           self.platform.track_segment)
        self._busy = False

        # Notify controller that vehicle exiting storage is done.
        cmd_complete = api.SimCompleteStorageExit()
        cmd_complete.msgID = cmd_msg_id
        cmd_complete.cmd.CopyFrom(cmd_msg)
        cmd_complete.time = Sim.now()
        vehicle.fill_VehicleStatus(cmd_complete.v_status)
        common.interface.send(api.SIM_COMPLETE_STORAGE_EXIT, cmd_complete)
        logging.info(
            "T=%4.3f Exit from Storage: Vehicle: %s, Berth: %s, Platform: %s, Station: %s",
            Sim.now(), vehicle.ID, self.ID, self.platform.ID, self.station.ID)
Ejemplo n.º 18
0
class PowerReport(enable.Component):

    SAMPLE_INTERVAL = 1 # seconds

    v_list = traits.List
    plot_data = traits.Instance(chaco.ArrayPlotData)
    plot_container = traits.Instance(enable.Component)
    plots = traits.Dict

    traits_view = ui.View(
                     ui.HGroup(
##                        ui.Item(name='v_list', editor=ui.EnumEditor(values=[str(v) for v in self.v_list])),
                        ui.Item(name='plot_container', editor=enable.ComponentEditor(), show_label=False)
                     ),
                     kind='live'
                  )

    def __init__(self):
        super(PowerReport, self).__init__(title='Power')

    def update(self):
        # Check if the locally cached vehicle list has gotten stale.
        if len(self.v_list) != len(common.vehicles):
            self.v_list[:] = common.vehicles.values()
            self.v_list.sort()

        self.plot_data = self.make_plot_data(self.v_list)
        self.plots, self.plot_container = self.make_plots(self.plot_data)

    def make_plot_data(self, v_list):
        """Returns a chaco.ArrayPlotData containing the following:
          v_power -- A 2D array where each row is a vehicle (indexes match
                     self.v_list), and each column is a time point.
          total_power - A 1D row array giving the network-wide power usage
                     at each time point.

        Parameters:
          v_list -- a sequence of Vehicle objects, sorted by ID

        Does not support negative velocities.
        """
        end_time = min(common.Sim.now(), common.config_manager.get_sim_end_time())
        sample_times = numpy.arange(0, end_time+self.SAMPLE_INTERVAL, self.SAMPLE_INTERVAL)
        power_array = numpy.zeros( (len(v_list), len(sample_times)), dtype=numpy.float32)

        air_density = common.air_density
        wind_speed = common.wind_speed
        wind_angle = common.wind_direction # 0 is blowing FROM the East

        g = 9.80665 # m/s^2
        PI_2 = math.pi/2
        PI_3_2 = math.pi * 1.5

        for v_idx, v in enumerate(v_list):
            masses = v.get_total_masses(sample_times)

            # The sample times may be out of the vehicle spline's valid range,
            # since the vehicle may not have been created at the beginning of
            # the simulation.
            v_start_time = v._spline.t[0]
            v_end_time = v._spline.t[-1]
            for idx, t in enumerate(sample_times):
                if t >= v_start_time:
                    v_start_idx = idx # left index
                    break
            for idx in xrange(len(sample_times)-1,-1,-1):
                if sample_times[idx] <= v_end_time:
                    v_end_idx = idx+1 # right index
                    break

            v_sample_times = sample_times[v_start_idx:v_end_idx]
            v_knots = v._spline.evaluate_sequence(v_sample_times)

            knots = [None] * len(sample_times)
            knots[v_start_idx:v_end_idx] = v_knots

            CdA = v.frontal_area * v.drag_coefficient

            path_idx = 0
            path_sum = 0
            loc = v._path[path_idx]

            last_elevation = loc.get_elevation(v_knots[0].pos)

            for sample_idx, (t, mass, knot) in enumerate(itertools.izip(sample_times, masses, knots)):
                if knot is None:
                    power_array[v_idx, sample_idx] = 0
                    continue

                # Track where we are on the vehicle's path
                pos = knot.pos - path_sum
                if pos >= loc.length:
                    path_sum += loc.length
                    path_idx += 1
                    pos = knot.pos - path_sum
                    loc = v._path[path_idx]

                # Power required to overcome rolling resistance. Ignores effect of
                # track slope and assumes that rolling resistance is constant
                # at different velocities.
                if v.rolling_coefficient:
                    rolling_power = v.rolling_coefficient * g * mass * knot.vel # Force * velocity
                else:
                    rolling_power = 0 # Rolling resistance not modelled


                # Power to accelerate / decelerate (change in kinetic energy)
                accel_power = mass * knot.accel * knot.vel

                # Power to overcome aero drag
                if wind_speed and knot.vel != 0: # No power use when stopped
                    travel_angle = loc.get_direction(knot.pos - path_sum) # 0 is travelling TOWARDS the East
                    incidence_angle = wind_angle - travel_angle
                    if PI_2 <= incidence_angle <= PI_3_2: # tail wind
                        vel = knot.vel - math.cos(incidence_angle)*wind_speed
                    else: # head wind
                        vel = knot.vel + math.cos(incidence_angle)*wind_speed
                else:
                    vel = knot.vel
                aero_power = 0.5 * air_density * vel*vel*vel * CdA

                # Power from elevation changes (change in potential energy)
                elevation = loc.get_elevation(pos)
                delta_elevation = elevation - last_elevation
                elevation_power = g * delta_elevation
                last_elevation = elevation

                # Adjust power usages by efficiency
                net_power = accel_power + rolling_power + aero_power + elevation_power
                if net_power > 0:
                    net_power /= v.powertrain_efficiency # low efficiency increases power required
                elif net_power < 0:
                    net_power *= v.regenerative_braking_efficiency # low efficiency decreases power recovered

                power_array[v_idx, sample_idx] = net_power

        power_array = numpy.divide(power_array, 1000.0) # convert from Watts to KW

        positive_power = numpy.clip(power_array, 0, numpy.inf)
        positive_total_power = numpy.sum(positive_power, axis=0)

        negative_power = numpy.clip(power_array, -numpy.inf, 0)
        negative_total_power = numpy.sum(negative_power, axis=0)

        net_total_power = positive_total_power + negative_total_power

        energy_array = numpy.cumsum(power_array, axis=1)
        energy_array = numpy.divide(energy_array, 3600/self.SAMPLE_INTERVAL) # convert to KW-hours
        total_energy_array = numpy.sum(energy_array, axis=0)

        return chaco.ArrayPlotData(
            sample_times=chaco.ArrayDataSource(sample_times, sort_order="ascending"),
            positive_total_power=chaco.ArrayDataSource(positive_total_power),
            negative_total_power=chaco.ArrayDataSource(negative_total_power),
            net_total_power=chaco.ArrayDataSource(net_total_power),
            total_energy=chaco.ArrayDataSource(total_energy_array),
            v_power=power_array,
            positive_power=positive_power,
            negative_power=negative_power,
            energy_array=energy_array
            )

    def make_plots(self, plot_data):
        """Create overlapping power and energy plots from the supplied plot_data.

        Parameters:
          plot_data -- A chaco.ArrayPlotData object. Expected to be created
              by self.make_plot_data.

        Return:
          A 2-tuple containing:
            - A dict containing plots, keyed by the plot name.
            - A chaco.OverlayPlotContainer containing the plots.
        """
        times_mapper = chaco.LinearMapper(range=chaco.DataRange1D(plot_data.get_data('sample_times'), ))

        graph_colors = {'positive_total_power':'black',
                        'negative_total_power':'red',
                        'net_total_power':'purple',
                        'total_energy':'green'}

        plots = {} # Dict of all plots

        # Power graphs
        power_names = ['positive_total_power',
                             'negative_total_power',
                             'net_total_power']
        power_data_range = chaco.DataRange1D(*[plot_data.get_data(name) for name in power_names])
        power_mapper = chaco.LinearMapper(range=power_data_range)

        power_plots = {}
        for plot_name in power_names:
            plot = chaco.LinePlot(index=plot_data.get_data('sample_times'),
                                  value=plot_data.get_data(plot_name),
                                  index_mapper=times_mapper,
                                  value_mapper=power_mapper,
                                  border_visible=False,
                                  bg_color='transparent',
                                  line_style='solid',
                                  color=graph_colors[plot_name],
                                  line_width=2)
            power_plots[plot_name] = plot
            plots[plot_name] = plot

        # Energy graphs -- use a different value scale than power
        energy_plot_names = ['total_energy']
        energy_data_range = chaco.DataRange1D(*[plot_data.get_data(name) for name in energy_plot_names])
        energy_mapper = chaco.LinearMapper(range=energy_data_range)

        energy_plots = {}
        for plot_name in energy_plot_names:
            plot = chaco.LinePlot(index=plot_data.get_data('sample_times'),
                                  value=plot_data.get_data(plot_name),
                                  index_mapper=times_mapper,
                                  value_mapper=energy_mapper,
                                  border_visible=False,
                                  bg_color='transarent',
                                  line_style='solid',
                                  color=graph_colors[plot_name],
                                  line_width=2)
            energy_plots[plot_name] = plot
            plots[plot_name] = plot


        # Blank plot -- Holds the grid and axis, and acts as a placeholder when
        # no other graphs are activated.
        blank_values = chaco.ArrayDataSource(numpy.zeros( plot_data.get_data('sample_times').get_size() ))
        blank_plot = chaco.LinePlot(index=plot_data.get_data('sample_times'),
                                    value=blank_values,
                                    index_mapper=times_mapper,
                                    value_mapper=power_mapper,
                                    border_visible=True,
                                    bg_color='transparent',
                                    line_width=0)
        plots['blank_plot'] = plot

        times_axis = chaco.PlotAxis(orientation='bottom',
                                    title="Time (seconds)",
                                    mapper=times_mapper,
                                    component=blank_plot)
        power_axis = chaco.PlotAxis(orientation='left',
                                    title="Power (KW)",
                                    mapper=power_mapper,
                                    component=blank_plot)
        energy_axis = chaco.PlotAxis(orientation='right',
                                     title="Energy (KW-hrs)",
                                     mapper=energy_mapper,
                                     component=blank_plot)
        blank_plot.underlays.append(times_axis)
        blank_plot.underlays.append(power_axis)
        blank_plot.underlays.append(energy_axis)

        # Add zoom capability
        blank_plot.overlays.append(tools.ZoomTool(blank_plot,
                                   tool_mode='range',
                                   axis='index',
                                   always_on=True,
                                   drag_button='left'))

        plot_container = chaco.OverlayPlotContainer()
        for plot in power_plots.itervalues():
            plot_container.add(plot)
        for plot in energy_plots.itervalues():
            plot_container.add(plot)
        plot_container.add(blank_plot)
        plot_container.padding_left = 60
        plot_container.padding_right = 60
        plot_container.padding_top = 20
        plot_container.padding_bottom = 50

        # Legend
        legend = chaco.Legend(component=plot_container, padding=20, align="ur")
        legend.tools.append(tools.LegendTool(legend, drag_button="right"))
        legend.plots = {}
        legend.plots.update(power_plots)
        legend.plots.update(energy_plots)
        plot_container.overlays.append(legend)

        return plots, plot_container
Ejemplo n.º 19
0
class Station(traits.HasTraits):
    """TODO: Check documentation and update if necessary. OUT OF DATE!!!

    First pass implementation will use a simple interface. Rather than
    explicitly handling passengers queuing for vehicles, the controller may
    tell any passenger to board any vehicle that's at the platform.

    The layout for this Station implementation is serial. Upon entering the
    station, a vehicle enters an unloading platform. Once unloaded, the vehicle
    moves into a empty vehicle queue. Then the vehicle enters a loading
    platform. Finally, the frontmost vehicle of the loading platform may exit
    the station.

    In this version, there is no bypass track -- empty vehicles must pass
    through the loading platform to exit the station.

    The platforms consist of a number of berths. Each berth is a SimPy Process
    object, and the Station process serves to coordinate and direct the actions
    of the berths.

    It is assumed that the vehicle speed is kept low within the station, and
    that vehicles have a fixed time to advance a slot (where a slot is either
    a queue position, or a load/unload berth). That is, advancing five slots
    always takes 5 * v_adv_time. Transfering from the front of the unload
    platform to the rear of the empty queue also takes one v_adv_time. Ditto for
    transferring to the load platform.

    All vehicles in the station advance in synchronicity. A vehicle that is not
    ready to move at the beginning of a cycle is delayed from moving until the
    beginning of the next cycle, where cycle length is the v_adv_time.

    When a vehicle enters or leaves a station, there is a v_adv_time delay. If
    a vehicle is entering, the following timeline is used:
        0: SIM_NOTIFY_VEHICLE_ARRIVE (station) msg sent
        0 - v_adv_time: Vehicle 'fully' occupies rearmost berth of platform
                        Vehicle body moves into station and off of the track
        v_adv_time: Vehicle is completely off of track.
                    SIM_NOTIFY_VEHICLE_EXIT (edge) msg sent

    When a vehicle is launched from the station:
        0: CTRL_CMD_STATION_LAUNCH received
           SIM_NOTIFY_VEHICLE_ARRIVE (edge) msg sent
        0 - v_adv_time: Vehcicle 'fully' occupies launch berth
                        Vehicle body moves onto track
        v_adv_time: Vehicle is completely out of station
                    SIM_COMPLETE_STATION_LAUNCH msg sent

    The entry and launch of a vehicle happens asynchronously.

    TODO: Use berth length and station speed limit to determine v_adv_time?
    TODO: Test / Bugfix StationSummary msgs
    """

    platforms = traits.List(traits.Instance(Platform))
    track_segments = traits.List(traits.Instance('pyprt.sim.layout.TrackSegment'))

    # Passengers waiting at the station.
    passengers = traits.List(traits.Instance('pyprt.sim.events.Passenger'))

    def __init__(self, ID, label, platforms, track_segments, **tr):
#        Sim.Process.__init__(self, name=self.label)
        self.ID = ID
        self.label = label
#        self.policy = policy # vehicle loading policy: 'QUEUE' or 'LOBBY'
#        # TODO: Remove policy, and make it a choice of the external station controller

        self.platforms = platforms
        self.track_segments = track_segments

        self.totalDepartures = 0
        self.totalArrivals = 0
        self.totalCrashes = 0

        # Set to True by another process prior to interruption.
        self.launchFlag = False

        # boarding pairs. Key is the Vehicle instance, value is the
        # Passenger instance. Only used under LOBBY policy.
        self._boarding = dict()

        self.rdy_launch_sent = False
        self.unrdy_launch_sent = False
        self.rdy_load_sent = set()

        # Every station will have a slightly different view
        self.view = self.make_view()

    def startup(self):
        # upon station activation, activate berths
        for platform in self.platforms:
            for berth in platform.berths:
                Sim.activate(berth, berth.run())

##    def is_empty(self):
##        empty = True
##        for platform in self.platforms:
##            if not platform.is_empty():
##                empty = False
##                break
##        return empty

#    def accept(self, veh):
#        """Accept a vehicle into the station. Notifies the controller of the
#        event and reactivates Station (self) if necessary.
#        """
#        self.totalArrivals += 1
###        rear_berth = self.unload_platform[-1]
###        rb_vehicle = rear_berth.vehicle # save the current rear vehicle, if any
###
###        # take vehicle even if a crash or overshoot occurred. TEMP?
###        rear_berth.vehicle = veh
#        self.unload_platform[-1].vehicle = veh
#        logging.info("T=%4.3f %s accepted at %s. Speed: %s Pos: %s",
#                     Sim.now(), veh, self, veh.speed, veh.pos)
#        if self.passive():
#            Sim.reactivate(self, prior = True)
###        # Can only return one type of crash. Overshoot is considered to be
###        # the more serious one right now.
###        if veh.speed > self.max_speed:
###            self.totalCrashes += 1
###            print "CRASH T=%4.3f %s entered %s at too high of a speed: %f." %\
###                          (Sim.now(), veh, self, veh.speed)
###            logging.error("T=%4.3f %s entered %s at too high of a speed: %f.",
###                          Sim.now(), veh, self, veh.speed)
###            raise common.StationOvershootError
###        if rb_vehicle:
###            self.totalCrashes += 1
###            print "CRASH T=%4.3f %s entered %s without room to accept. Hit %s" %\
###                          (Sim.now(), veh, self, rb_vehicle)
###            logging.error("T=%4.3f %s entered %s without room to accept. Hit %s",
###                          Sim.now(), veh, self, rb_vehicle)
###            raise common.StationFullError

#    def ctrl_loop(self):
#        # Startup Code
#        # upon station activation, activate berths
#        for platform in self.platforms:
#            for berth in platform.berths:
#                Sim.activate(berth, berth.run())
#
#        if self.is_empty():
#            yield Sim.passivate, self  # no vehicles, nothing to do.
#        self.next_heartbeat = Sim.now() + self.v_adv_time
#
#        # Main Loop
#        while True:
#            self.send_rdy_load(self.load_platform)
#            self.send_rdy_launch()
#
#            # Allow communication with controller (no time passes)
#            yield Sim.hold, self
#
#            self.unload_passengers(self.unload_platform)
#            self.load_passengers(self.load_platform)
#
#            self.send_rdy_load(self.load_platform)
#            self.send_rdy_launch()
#
#            # Slide everything forward one slot. Don't update positions until
#            # after the time cost is paid.
#            yield Sim.hold, self, self.next_heartbeat - Sim.now()
#
#            # Once the vehicle clears the exit berth, it interrupts the station
#            while self.interrupted():
#                assert self.interruptCause.ID is flb.vehicle.ID # vehicle is interruptCause
#                self.next_heartbeat = Sim.now() + self.interruptLeft
#                flb.vehicle = None
#                # If station is now empty, passivate
#                if self.is_empty():
#                    logging.info("T=%4.3f %s now empty, passivating.", Sim.now(), self)
#                    self.interruptReset()
#                    yield Sim.passivate, self
#                else:
#                    logging.info("T=%4.3f %s still contains a vehicle, staying active",
#                                 Sim.now(), self)
#                    t_left = self.interruptLeft
#                    self.interruptReset()
#                    yield Sim.hold, self, t_left
#
#            # Advance the front-most platform first, then work back.
#            for platform in self.platforms.reverse():
#                platform.advance()
#
#            self.next_heartbeat = Sim.now() + self.v_adv_time

    def add_passenger(self, pax):
        """Add a passenger to this station."""
        assert pax not in self.passengers
        self.passengers.append(pax)

##    def unload_passengers(self, platform):
##        packed = True
##        for b in platform:
##            # If there's a vehicle with a passenger in the berth, and it's
##            # not already unloading, and it's as far forward as it can go
##            # (packed), then unload.
##            if not b.vehicle:
##                packed = False
##            if b.vehicle and b.vehicle.passengers and not b.is_busy() and packed:
##                b._unload = True
##                Sim.reactivate(b, prior = True)

#    def send_rdy_load(self, platform):
#        packed = True
#        for b in platform:
#            v = b.vehicle
#            if not v:
#                packed = False
#            elif b.is_busy():
#                packed = True
#            elif packed and not v.passengers and not b.is_busy():
#                if v not in self._boarding and \
#                   v not in self.rdy_load_sent:
#                    self.rdy_load_sent.add(v)
#                    rl_msg = api.SimNotifyVehicleReadyLoad()
#                    rl_msg.vID = v.ID
#                    rl_msg.sID = self.ID
#                    common.interface.send(api.SIM_NOTIFY_VEHICLE_READY_LOAD,
#                                          rl_msg)

#    def load_passengers(self, platform):
#        """If under a LOBBY boarding policy, uses boarding information
#        previously provided by the board_passenger function to load passengers
#        into the appropriate vehicles, if available.
#
#        WARNING, TODO: LOBBY does not currently support multiple passengers
#        on one vehicle.
#
#        If under a QUEUE boarding policy, passengers are loaded on a first-come/
#        first-served basis. If frontmost passenger in queue is willing to share,
#        other passengers with the same destination (and also willing to share)
#        will board with the frontmost passenger. Time for loading and unloading
#        multiple passengers is the sum of the individual times.
#        """
#        if len(self.passengers) == 0:
#            return
#
#        packed = True
#        for b in platform:
#            # If there's an empty vehicle in the berth, and it's
#            # not already loading, and it's as far forward as it can go
#            # (packed), then check if there is a passenger associated with it.
#            # Load the passenger if there is.
#            v = b.vehicle
#            if not v:
#                packed = False
#            elif b.is_busy():
#                packed = True
#            elif v and not v.passengers and not b.is_busy() and packed:
#                if self.policy == 'LOBBY':
#                    try:
#                        pax, msgID = self._boarding[v]
#                        idx = self.passengers.index(pax)
#                        del self.passengers[idx]
#                        del self._boarding[v]
#                        b._load_msgID = msgID
#                    except KeyError: # No boarding info for that vehicle
#                        continue
#                elif self.policy == 'QUEUE':
#                    if self.passengers:
#                        pax = [self.passengers.pop(0)]
#                        if pax[0].will_share:
#                            share_list = [(idx, p) for idx, p in
#                                        enumerate(self.passengers) if
#                                        p.dest_station is pax[0].dest_station and
#                                        p.will_share]
#                            pax.extend([p for idx, p in share_list[:v.max_pax_capacity-1]])
#                            for idx, p in reversed(share_list[:v.max_pax_capacity-1]):
#                                del self.passengers[idx]
#
#                else:
#                    raise Exception, 'Unknown pax loading policy'
#
#                b.load(msg_id, pax)
#
#                self.rdy_load_sent.discard(v)

##    def board_passenger(self, vehicle, passenger, msgID):
##        """Tell a passenger which vehicle to board. Command is queued until
##        vehicle is ready. MsgID refers to the CtrlCmdPassengerBoardVehicle msg.
##
##        Passenger is required to be at the station already, vehicle is not.
##
##        For now, only one passenger may be told to board a vehicle. If
##        board_passenger is called twice for the same vehicle, the old
##        passenger will no longer board that vehicle.
##        """
##        self._boarding[vehicle] = (passenger, msgID)

##    def is_launchable(self, vehicle):
##        """Returns True if vehicle is launchable, otherwise returns false.
##        """
##        front_berth = self.load_platform[0]
##        if front_berth.vehicle and not front_berth.is_busy():
##            return True
##        else:
##            return False

#    def launch(self, vehicle, target_speed, max_accel, max_decel, max_jerk, msgID):
#        """Intended for use by other objects (e.g. a comm instance).
#        Immediately launches the vehicle if ready. Otherwise raises
#        a common.InvalidVehicleID exception.
#
#        Note that vehicle is a vehicle instance, not an ID.
#        """
#        if not self.is_launchable(vehicle):
#            raise common.InvalidVehicleID, vehicle.ID
#        front_berth = self.load_platform[0]
#        vehicle.set_speed(target_speed, max_accel, max_decel, max_jerk, 0)
#        self.totalDepartures += 1
#        self.rdy_launch_sent = False
#        self.unrdy_launch_sent = False
#        self.rdy_load_sent.discard(vehicle)
#        l_msg = api.SimCompleteStationLaunch()
#        l_msg.msgID = msgID
#        l_msg.vID = vehicle.ID
#        l_msg.sID = self.ID
#        common.interface.send(api.SIM_COMPLETE_STATION_LAUNCH, l_msg)
#        logging.info("T=%4.3f %s launched from berth %s of %s with pax %s",
#                     Sim.now(), vehicle, front_berth.ID, self,
#                     [pax.ID for pax in vehicle.passengers if pax])
#
#        if vehicle.passive():
#            Sim.reactivate(vehicle, prior = True)
#        else:
#            Sim.activate(vehicle, vehicle.ctrl_loop())

#    def send_rdy_launch(self):
#        flb = self.load_platform[0] # front loading berth
#        if flb.vehicle and flb.vehicle.passengers \
#                and not flb.is_busy() and not self.rdy_launch_sent:
#            self.rdy_launch_sent = True
#            rdy_launch_msg = api.SimNotifyStationReadyLaunch()
#            rdy_launch_msg.vID = flb.vehicle.ID
#            rdy_launch_msg.sID = self.ID
#            for pax in flb.vehicle.passengers:
#                rdy_launch_msg.pID.append(pax.ID)
#            common.interface.send(api.SIM_NOTIFY_STATION_READY_LAUNCH,
#                                  rdy_launch_msg)
#        elif flb.vehicle and flb.is_busy() and self.rdy_launch_sent \
#                 and not self.unrdy_launch_sent:
#            self.rdy_launch_sent = False
#            self.unrdy_launch_sent = True
#            urdy_launch_msg = api.SimNotifyStationUnreadyLaunch()
#            urdy_launch_msg.vID = flb.vehicle.ID
#            urdy_launch_msg.sID = self.ID
#            common.interface.send(api.SIM_NOTIFY_STATION_UNREADY_LAUNCH,
#                                  urdy_launch_msg)

#    def fill_StationStatus(self, s_status):
#        """Fills an api.StationStatus instance with current information."""
#        s_status.sID = self.ID
#        if self.label:
#            s_status.label = self.label
#
#        # TODO: Only support for one loading/unloading plat right now.
#        ulp_status = s_status.unloading_plat.add()
#        ulp_status.platID = 1
#        for idx, b in enumerate(self.unload_platform):
#            b_status = ulp_status.berths.add()
#            b_status.bID = idx + 1 # first ID is 1
#            b_status.vID = (b.vehicle.ID if b.vehicle else api.NONE_ID)
#            b_status.busy = b.is_busy()
#
#        lp_status = s_status.loading_plat.add()
#        lp_status.platID = 2
#        for idx, b in enumerate(self.load_platform):
#            b_status = lp_status.berths.add()
#            b_status.bID = idx + 1 # first ID is 1
#            b_status.vID = (b.vehicle.ID if b.vehicle else api.NONE_ID)
#            b_status.busy = b.is_busy()
#
#
#        for v in self.queue:
#            s_status.emptyQueue.append(v.ID if v else api.NONE_ID)
#
#        for p in self.passengers:
#            s_status.pID.append(p.ID)
#
#        s_status.v_adv_time = int(round(self.v_adv_time,3)*1000) # in millisec
#        if self.policy == 'QUEUE':
#            s_status.policy = api.QUEUE
#        elif self.policy == 'LOBBY':
#            s_status.policy = api.LOBBY
#        else:
#            raise Exception, "Unknown station policy"
#
#    def fill_StationSummary(self, s_sum):
#        """Fills an api.StationSummary instance with current information."""
#        s_sum.sID = self.ID
#        if self.label:
#            s_sum.label = self.label
#        # TODO: extend to multiple load platforms
#        flb = self.load_platform[0] # front loading berth
#        s_sum.loaded_ready_launch.append(flb.vehicle.ID if flb.vehicle
#                                         and flb.vehicle.passengers
#                                         and not flb.is_busy()
#                                         else 0)
#        # TODO: extend to multiple load platforms, and empty queue bypass
#        s_sum.unloaded_ready_launch.append(flb.vehicle.ID if flb.vehicle
#                                           and not flb.vehicle.passengers
#                                           and not flb.is_busy()
#                                           else 0)
#
#        for p in self.passengers:
#            s_sum.pID.append(p.ID) # passengers waiting at station
#
#        entry_berth = self.unload_platform[-1]
##
##        entering_v = None
##        if self.resource.activeQ[-1].pos <= self.berth_length
#
#        # entry berth is immediately available
#        if not entry_berth.vehicle:
#            s_sum.next_accept_time = int(round(Sim.now()*1000)) # s -> ms
#        # test if entry berth will be available after next heartbeat
#        else:
#            avail = False
#            # look from back to front
#            for b in reversed(self.unload_platform):
#                # if found a busy berth, it's non-deterministic
#                if b.is_busy():
#                    break
#                # if found an empty berth, then vehicles will slide forward.
#                elif not b.vehicle:
#                    avail = True
#                    break
#                else:
#                    continue
#
#            if avail:
#                s_sum.next_accept_time = int(round(self.next_heartbeat*1000)) # s -> ms
#            else:
#                s_sum.next_accept_time = -1 # non-determinstic
#
#        # vehicles_needed
#        v_avail = 0 # empty and unloading vehicles
#        for v in self.queue:
#            if v:
#                v_avail += 1
#        for b in self.unload_platform:
#            if b.vehicle:
#                v_avail += 1
#        v_needed = len(self.passengers)
#        s_sum.vehicles_needed = max(0, v_needed - v_avail)

    def __str__(self):
        return self.label

    def __hash__(self):
        return self.ID.__hash__()

    def make_view(self):
        """Make a traits view (popup window) for this station."""
        pax_table_editor = ui.TableEditor(
                            # Only the passenger data relevant when looking at a station.
                            columns = [ui_tc.ObjectColumn(name='label', label='Name'),
                                       ui_tc.ObjectColumn(name='_start_time'),
                                       ui_tc.ObjectColumn(name='dest_station', label='Destination'),
                                       ui_tc.ObjectColumn(name='wait_time', label='Waiting (sec)', format="%.2f"),
                                       ui_tc.ObjectColumn(name='will_share', label='Will Share'),
                                       ui_tc.ObjectColumn(name='load_delay', label='Time to Board (sec)')],
                                       # more...
                            deletable = True,
#                            sort_model = True,
                            auto_size = True,
                            orientation = 'vertical',
                            show_toolbar = True,
                            reorderable = True, # Does this affect the actual boarding order (think no...)
                            rows = 5,
                            row_factory = events.Passenger)


        groups = ui.VGroup(
                           ui.Group(
                                ui.Label('Waiting Passengers'),
                                ui.Item(name='passengers',
                                        show_label = False,
                                        editor=pax_table_editor
                                        ),
                                show_border = True),
#                           ui.Group(
#                                ui.Label('Load Platform'),
#                                ui.Item(name='load_platform',
#                                        show_label = False,
#                                        editor=ui.ListEditor(style='custom',
#                                                             rows=len(self.load_platform)),
#                                        style='readonly'),
#                                show_border = True
#                                ),
                           ui.Group(
                                ui.Label('Queue'),
                                ui.Item(name='queue',
                                        show_label=False,
                                        editor=ui.ListEditor(editor=ui.TextEditor()),
                                        style='readonly'),
                                show_border = True
#                                ),
#                           ui.Group(
#                                ui.Label('Unload Platform'),
#                                ui.Item(name='unload_platform',
#                                        show_label = False,
#                                        editor=ui.ListEditor(style='custom',
#                                                             rows=len(self.unload_platform)),
#                                        style='readonly',
#                                        ),
#                                show_border = True,
                                ))

        view = ui.View(groups,
                       title=self.label,
#                       scrollable = True,
                       resizable = True,
                       height = 700,
                       width = 470
                       )

        return view
Ejemplo n.º 20
0
class Reports(traits.HasTraits):
    """A user interface that displays all the reports in a tabbed notebook."""

    summary_report = traits.Instance(SummaryReport)
    vehicle_report = traits.Instance(VehicleReport)
    pax_report = traits.Instance(PaxReport)
    station_report = traits.Instance(StationReport)
    power_report = traits.Instance(PowerReport)

    refresh = menu.Action(name="Refresh", action="refresh")

    view = ui.View(
                ui.Tabbed(
                    ui.Item('summary_report',
                            label='Summary',
                            editor=ui.TextEditor(),
                            style='readonly'),
                    ui.Item('vehicle_report',
                            label='Vehicles',
                            editor=ui.InstanceEditor(),
                            style='custom'
                            ),
                    ui.Item('pax_report',
                            label='Passengers',
                            editor=ui.InstanceEditor(),
                            style='custom'
                            ),
                    ui.Item('station_report',
                            label='Stations',
                            editor=ui.InstanceEditor(),
                            style='custom'
                            ),
                    ui.Item('power_report',
                            label='Power',
                            editor=ui.InstanceEditor(),
                            style='custom'),
                    show_labels=False,
                 ),
                title = 'Simulation Reports',
                width=1000,
                resizable=True,
                handler=ReportsHandler(),
                buttons= [], #[refresh], #TODO: Disabling the refresh button until I can get it to refresh all reports properly
                kind='live')

    def __init__(self):
        super(Reports, self).__init__()
        self.summary_report = SummaryReport()
        self.pax_report = PaxReport()
        self.vehicle_report = VehicleReport()
        self.station_report = StationReport()
        self.power_report = PowerReport()

        self._last_update_time = None

    def update(self):
        if self._last_update_time == Sim.now():
            return

        self.pax_report.update()
        self.vehicle_report.update()
        self.station_report.update()
        self.power_report.update()
        self.summary_report.update(self.pax_report,
                                   self.vehicle_report,
                                   self.station_report,
                                   self.power_report)

        self._last_update_time = Sim.now()

    def display(self, evt=None):
        self.update()
        self.edit_traits()

    def write(self, report_path, update=True):
        """Writes the report to the filename specified by report_path. Use '-'
        to write to stdout."""
        if update:
            self.update()

        if report_path == '-':
            out = stdout
        else:
            out = open(report_path, 'w')

        out.write(str(self.summary_report))
        out.write('\n\n')
        out.write(str(self.pax_report))
        out.write('\n\n')
        out.write(str(self.vehicle_report))
        out.write('\n\n')
        out.write(str(self.station_report))
Ejemplo n.º 21
0
class DeviceModel(traits.HasTraits):
    """Represent the trigger device in the host computer, and push any state

    We keep a local copy of the state of the device in memory on the
    host computer, and any state changes to the device to through this
    class, also allowing us to update our copy of the state.

    """
    # Private runtime details
    _libusb_handle = traits.Any(None,transient=True)
    _lock = traits.Any(None,transient=True) # lock access to the handle
    real_device = traits.Bool(False,transient=True) # real USB device present
    FOSC = traits.Float(8000000.0,transient=True)

    ignore_version_mismatch = traits.Bool(False, transient=True)

    # A couple properties
    frames_per_second = RemoteFpsFloat
    frames_per_second_actual = traits.Property(depends_on='_t3_state')
    timer3_top = traits.Property(depends_on='_t3_state')

    # Timer 3 state:
    _t3_state = traits.Instance(DeviceTimer3State) # atomic updates

    # LEDs state
    _led_state = traits.Int

    led1 = traits.Property(depends_on='_led_state')
    led2 = traits.Property(depends_on='_led_state')
    led3 = traits.Property(depends_on='_led_state')
    led4 = traits.Property(depends_on='_led_state')

    # Event would be fine for these, but use Button to get nice editor
    reset_framecount_A = traits.Button
    reset_AIN_overflow = traits.Button
    do_single_frame_pulse = traits.Button

    ext_trig1 = traits.Button
    ext_trig2 = traits.Button
    ext_trig3 = traits.Button

    # Analog input state:
    _ain_state = traits.Instance(DeviceAnalogInState) # atomic updates
    Vcc = traits.Property(depends_on='_ain_state')

    AIN_running = traits.Property(depends_on='_ain_state')
    enabled_channels = traits.Property(depends_on='_ain_state')
    enabled_channel_names = traits.Property(depends_on='_ain_state')

    # The view:
    traits_view = View(Group( Group(Item('frames_per_second',
                                         label='frame rate',
                                         ),
                                    Item('frames_per_second_actual',
                                         show_label=False,
                                         style='readonly',
                                         ),
                                    orientation='horizontal',),
                              Group(Item('ext_trig1',show_label=False),
                                    Item('ext_trig2',show_label=False),
                                    Item('ext_trig3',show_label=False),
                                    orientation='horizontal'),
                              Item('_ain_state',show_label=False,
                                   style='custom'),
                              Item('reset_AIN_overflow',show_label=False),
                              ))

    def __init__(self,*a,**k):
        super(DeviceModel,self).__init__(*a,**k)
        self._t3_state = DeviceTimer3State()
        self._ain_state = DeviceAnalogInState(trigger_device=self)

    def __new__(cls,*args,**kwargs):
        """Set the transient object state

        This must be done outside of __init__, because instances can
        get created without calling __init__. In particular, when
        being loaded from a pickle.
        """
        self = super(DeviceModel, cls).__new__(cls,*args,**kwargs)
        self._lock = threading.Lock()
        self._open_device()
        # force the USBKEY's state to our idea of its state
        self.__led_state_changed()
        self.__t3_state_changed()
        self.__ain_state_changed()
        self.reset_AIN_overflow = True # reset ain overflow

        #self.rand_pulse_enable()
        #self.rand_pulse_disable()
        #self.set_aout_values(300,250)

        return self

    def _set_led_mask(self,led_mask,value):
        if value:
            self._led_state = self._led_state | led_mask
        else:
            self._led_state = self._led_state & ~led_mask

    def __led_state_changed(self):
        buf = ctypes.create_string_buffer(2)
        buf[0] = chr(CAMTRIG_SET_LED_STATE)
        buf[1] = chr(self._led_state)
        self._send_buf(buf)

    @traits.cached_property
    def _get_led1(self):
        return bool(self._led_state & LEDS_LED1)
    def _set_led1(self,value):
        self._set_led_mask(LEDS_LED1,value)

    @traits.cached_property
    def _get_led2(self):
        return bool(self._led_state & LEDS_LED2)
    def _set_led2(self,value):
        self._set_led_mask(LEDS_LED2,value)

    @traits.cached_property
    def _get_led3(self):
        return bool(self._led_state & LEDS_LED3)
    def _set_led3(self,value):
        self._set_led_mask(LEDS_LED3,value)

    @traits.cached_property
    def _get_led4(self):
        return bool(self._led_state & LEDS_LED4)
    def _set_led4(self,value):
        self._set_led_mask(LEDS_LED4,value)

    @traits.cached_property
    def _get_Vcc(self):
        return self._ain_state.Vcc

    @traits.cached_property
    def _get_AIN_running(self):
        return self._ain_state.AIN_running

    @traits.cached_property
    def _get_enabled_channels(self):
        result = []
        if self._ain_state.AIN0_enabled:
            result.append(0)
        if self._ain_state.AIN1_enabled:
            result.append(1)
        if self._ain_state.AIN2_enabled:
            result.append(2)
        if self._ain_state.AIN3_enabled:
            result.append(3)
        return result

    @traits.cached_property
    def _get_enabled_channel_names(self):
        result = []
        if self._ain_state.AIN0_enabled:
            result.append(self._ain_state.AIN0_name)
        if self._ain_state.AIN1_enabled:
            result.append(self._ain_state.AIN1_name)
        if self._ain_state.AIN2_enabled:
            result.append(self._ain_state.AIN2_name)
        if self._ain_state.AIN3_enabled:
            result.append(self._ain_state.AIN3_name)
        return result

    @traits.cached_property
    def _get_timer3_top(self):
        return self._t3_state.timer3_top

    @traits.cached_property
    def _get_frames_per_second_actual(self):
        if self._t3_state.timer3_CS==0:
            return 0
        return self.FOSC/self._t3_state.timer3_CS/self._t3_state.timer3_top

    def set_frames_per_second_approximate(self,value):
        """Set the framerate as close as possible to the desired value"""
        new_t3_state = DeviceTimer3State()
        if value==0:
            new_t3_state.timer3_CS=0
        else:
            # For all possible clock select values
            CSs = np.array([1.0,8.0,64.0,256.0,1024.0])
            # find the value of top that to gives the desired framerate
            best_top = np.clip(np.round(self.FOSC/CSs/value),0,2**16-1).astype(np.int)
            # and find the what the framerate would be at that top value
            best_rate = self.FOSC/CSs/best_top
            # and choose the best one.
            idx = np.argmin(abs(best_rate-value))
            expected_rate = best_rate[idx]
            new_t3_state.timer3_CS = CSs[idx]
            new_t3_state.timer3_top = best_top[idx]

            ideal_ocr3a = 0.02 * new_t3_state.timer3_top # 2% duty cycle
            ocr3a = int(np.round(ideal_ocr3a))
            if ocr3a==0:
                ocr3a=1
            if ocr3a >= new_t3_state.timer3_top:
                ocr3a-=1
                if ocr3a <= 0:
                    raise ValueError('impossible combination for ocr3a')
            new_t3_state.ocr3a = ocr3a
        self._t3_state = new_t3_state # atomic update

    def get_framestamp(self,full_output=False):
        """Get the framestamp and the value of PORTC

        The framestamp includes fraction of IFI until next frame.

        The inter-frame counter counts up from 0 to self.timer3_top
        between frame ticks.
        """
        if not self.real_device:
            now = time.time()
            if full_output:
                framecount = now//1
                tcnt3 = now%1.0
                results = now, framecount, tcnt3
            else:
                results = now
            return results
        buf = ctypes.create_string_buffer(1)
        buf[0] = chr(CAMTRIG_GET_FRAMESTAMP_NOW)
        self._send_buf(buf)
        data = self._read_buf()
        if data is None:
            raise NoDataError('no data available from device')
        framecount = 0
        for i in range(8):
            framecount += ord(data[i]) << (i*8)
        tcnt3 = ord(data[8]) + (ord(data[9]) << 8)
        frac = tcnt3/float(self._t3_state.timer3_top)
        if frac>1:
            print('In ttriger.DeviceModel.get_framestamp(): '
                  'large fractional value in framestamp. resetting')
            frac=1
        framestamp = framecount+frac
        # WBD  
        #if full_output:
        #    results = framestamp, framecount, tcnt3
        #else:
        #    results = framestamp
        pulse_width = ord(data[10])
        if full_output:
            results = framestamp, pulse_width, framecount, tcnt3
        else:
            results = framestamp, pulse_width

        return results

    def get_analog_input_buffer_rawLE(self):
        if not self.real_device:
            outbuf = np.array([],dtype='<u2') # unsigned 2 byte little endian
            return outbuf
        EP_LEN = 256
        INPUT_BUFFER = ctypes.create_string_buffer(EP_LEN)

        bufs = []
        got_bytes = False
        timeout = 50 # msec

        cnt = 0 # Count number of times endpoint has been read
        min_cnt = 2 # Minimum number of times end point should be read

        while 1:
            # keep pumping until no more data
            try:
                with self._lock:
                    n_bytes = usb.bulk_read(self._libusb_handle, (ENDPOINT_DIR_IN|ANALOG_EPNUM), INPUT_BUFFER, timeout)
            except usb.USBNoDataAvailableError:
                break
            cnt += 1
            n_elements = n_bytes//2
            buf = np.fromstring(INPUT_BUFFER.raw,dtype='<u2') # unsigned 2 byte little endian
            buf = buf[:n_elements]
            bufs.append(buf)
            if (n_bytes < EP_LEN) and (cnt >= min_cnt):
                break # don't bother waiting for data to dribble in

        if len(bufs):
            outbuf = np.hstack(bufs)
        else:
            outbuf = np.array([],dtype='<u2') # unsigned 2 byte little endian
        return outbuf

    def __t3_state_changed(self):
        # A value was assigned to self._t3_state.
        # 1. Send its contents to device
        self._send_t3_state()
        # 2. Ensure updates to it also get sent to device
        if self._t3_state is None:
            return
        self._t3_state.on_trait_change(self._send_t3_state)

    def _send_t3_state(self):
        """ensure our concept of the device's state is correct by setting it"""
        t3 = self._t3_state # shorthand
        if t3 is None:
            return
        buf = ctypes.create_string_buffer(10)
        buf[0] = chr(CAMTRIG_NEW_TIMER3_DATA)

        buf[1] = chr(t3.ocr3a//0x100)
        buf[2] = chr(t3.ocr3a%0x100)
        buf[3] = chr(t3.ocr3b//0x100)
        buf[4] = chr(t3.ocr3b%0x100)

        buf[5] = chr(t3.ocr3c//0x100)
        buf[6] = chr(t3.ocr3c%0x100)
        buf[7] = chr(t3.timer3_top//0x100) # icr3a
        buf[8] = chr(t3.timer3_top%0x100)  # icr3a

        buf[9] = chr(t3.timer3_CS_)
        self._send_buf(buf)

    def __ain_state_changed(self):
        # A value was assigned to self._ain_state.
        # 1. Send its contents to device
        self._send_ain_state()
        # 2. Ensure updates to it also get sent to device
        if self._ain_state is None:
            return
        self._ain_state.on_trait_change(self._send_ain_state)

    def _send_ain_state(self):
        """ensure our concept of the device's state is correct by setting it"""
        ain_state = self._ain_state # shorthand
        if ain_state is None:
            return
        if ain_state.AIN_running:
            # analog_cmd_flags
            channel_list = 0
            if ain_state.AIN0_enabled:
                channel_list |= ENABLE_ADC_CHAN0
            if ain_state.AIN1_enabled:
                channel_list |= ENABLE_ADC_CHAN1
            if ain_state.AIN2_enabled:
                channel_list |= ENABLE_ADC_CHAN2
            if ain_state.AIN3_enabled:
                channel_list |= ENABLE_ADC_CHAN3
            analog_cmd_flags = ADC_START_STREAMING | channel_list
            analog_sample_bits = ain_state.adc_prescaler_ | (ain_state.downsample_bits<<3)
        else:
            analog_cmd_flags = ADC_STOP_STREAMING
            analog_sample_bits = 0

        buf = ctypes.create_string_buffer(3)
        buf[0] = chr(CAMTRIG_AIN_SERVICE)
        buf[1] = chr(analog_cmd_flags)
        buf[2] = chr(analog_sample_bits)
        self._send_buf(buf)

    def enter_dfu_mode(self):
        buf = ctypes.create_string_buffer(1)
        buf[0] = chr(CAMTRIG_ENTER_DFU)
        self._send_buf(buf)

    def _do_single_frame_pulse_fired(self):
        buf = ctypes.create_string_buffer(1)
        buf[0] = chr(CAMTRIG_DO_TRIG_ONCE)
        self._send_buf(buf)

    def _ext_trig1_fired(self):
        buf = ctypes.create_string_buffer(2)
        buf[0] = chr(CAMTRIG_SET_EXT_TRIG)
        buf[1] = chr(EXT_TRIG1)
        self._send_buf(buf)

    def _ext_trig2_fired(self):
        buf = ctypes.create_string_buffer(2)
        buf[0] = chr(CAMTRIG_SET_EXT_TRIG)
        buf[1] = chr(EXT_TRIG2)
        self._send_buf(buf)

    def _ext_trig3_fired(self):
        buf = ctypes.create_string_buffer(2)
        buf[0] = chr(CAMTRIG_SET_EXT_TRIG)
        buf[1] = chr(EXT_TRIG3)
        self._send_buf(buf)

    def _reset_framecount_A_fired(self):
        buf = ctypes.create_string_buffer(1)
        buf[0] = chr(CAMTRIG_RESET_FRAMECOUNT_A)
        self._send_buf(buf)

    def _reset_AIN_overflow_fired(self):
        buf = ctypes.create_string_buffer(3)
        buf[0] = chr(CAMTRIG_AIN_SERVICE)
        buf[1] = chr(ADC_RESET_AIN)
        # 3rd byte doesn't matter
        self._send_buf(buf)

    # WBD - functions for enabling and disabling random pulses
    # --------------------------------------------------------
    def rand_pulse_enable(self):
        buf = ctypes.create_string_buffer(2)
        buf[0] = chr(CAMTRIG_RAND_PULSE)
        buf[1] = chr(RAND_PULSE_ENABLE)
        self._send_buf(buf)

    def rand_pulse_disable(self):
        buf = ctypes.create_string_buffer(2)
        buf[0] = chr(CAMTRIG_RAND_PULSE)
        buf[1] = chr(RAND_PULSE_DISABLE)
        self._send_buf(buf)

    # WBD - function for setting analog output values
    # -------------------------------------------------------
    def set_aout_values(self,val0, val1):
        buf = ctypes.create_string_buffer(5)
        buf[0] = chr(CAMTRIG_SET_AOUT)
        buf[1] = chr(val0//0x100)
        buf[2] = chr(val0%0x100)
        buf[3] = chr(val1//0x100) 
        buf[4] = chr(val1%0x100) 
        self._send_buf(buf)

    # WBD - get pulse width from frame count
    # -------------------------------------------------------
    def get_width_from_framecnt(self,framecnt):
        buf = ctypes.create_string_buffer(5)
        buf[0] = chr(CAMTRIG_GET_PULSE_WIDTH)
        for i in range(1,5):
            buf[i] = chr((framecnt >> ((i-1)*8)) & 0b11111111)
        self._send_buf(buf)
        data = self._read_buf()
        val = ord(data[0])
        return val

    # WBD - modified read_buf functions for multiple epnum in buffers
    # ---------------------------------------------------------------
    def _read_buf(self):
        if not self.real_device:
            return None
        buf = ctypes.create_string_buffer(16)
        timeout = 1000
        epnum = (ENDPOINT_DIR_IN|CAMTRIG_EPNUM)
        with self._lock:
            try:
                val = usb.bulk_read(self._libusb_handle, epnum, buf, timeout)
            except usb.USBNoDataAvailableError:
                return None
        return buf
    # ---------------------------------------------------------------

    def _send_buf(self,buf):
        if not self.real_device:
            return
        with self._lock:
            val = usb.bulk_write(self._libusb_handle, 0x06, buf, 9999)

    def _open_device(self):
        require_trigger = int(os.environ.get('REQUIRE_TRIGGER','1'))
        if require_trigger:

            usb.init()
            if not usb.get_busses():
                usb.find_busses()
                usb.find_devices()

            busses = usb.get_busses()

            found = False
            for bus in busses:
                for dev in bus.devices:
                    debug('idVendor: 0x%04x idProduct: 0x%04x'%
                          (dev.descriptor.idVendor,dev.descriptor.idProduct))
                    if (dev.descriptor.idVendor == 0x1781 and
                        dev.descriptor.idProduct == 0x0BAF):
                        found = True
                        break
                if found:
                    break
            if not found:
                raise RuntimeError("Cannot find device. (Perhaps run with "
                                   "environment variable REQUIRE_TRIGGER=0.)")
        else:
            self.real_device = False
            return
        with self._lock:
            self._libusb_handle = usb.open(dev)

            manufacturer = usb.get_string_simple(self._libusb_handle,dev.descriptor.iManufacturer)
            product = usb.get_string_simple(self._libusb_handle,dev.descriptor.iProduct)
            serial = usb.get_string_simple(self._libusb_handle,dev.descriptor.iSerialNumber)

            assert manufacturer == 'Strawman', 'Wrong manufacturer: %s'%manufacturer
            valid_product = 'Camera Trigger 1.0'
            if product == valid_product:
                self.FOSC = 8000000.0
            elif product.startswith('Camera Trigger 1.01'):
                osc_re = r'Camera Trigger 1.01 \(F_CPU = (.*)\)\w*'
                match = re.search(osc_re,product)
                fosc_str = match.groups()[0]
                if fosc_str.endswith('UL'):
                    fosc_str = fosc_str[:-2]
                self.FOSC = float(fosc_str)
            else:
                errmsg = 'Expected product "%s", but you have "%s"'%(
                    valid_product,product)
                if self.ignore_version_mismatch:
                    print 'WARNING:',errmsg
                    self.FOSC = 8000000.0
                    print ' assuming FOSC=',self.FOSC
                else:
                    raise ValueError(errmsg)

            interface_nr = 0
            if hasattr(usb,'get_driver_np'):
                # non-portable libusb extension
                name = usb.get_driver_np(self._libusb_handle,interface_nr)
                if name != '':
                    usb.detach_kernel_driver_np(self._libusb_handle,interface_nr)

            if dev.descriptor.bNumConfigurations > 1:
                debug("WARNING: more than one configuration, choosing first")

            config = dev.config[0]
            usb.set_configuration(self._libusb_handle, config.bConfigurationValue)
            usb.claim_interface(self._libusb_handle, interface_nr)
        self.real_device = True
Ejemplo n.º 22
0
class TestPlotter(traits.HasTraits):
    plot = traits.Instance(enable.Component)
    v_list = traits.List
    vehicle_str = traits.Str
    vehicle = traits.Instance(MockVehicle)

    def __init__(self, v_list):
        super(TestPlotter, self).__init__()
        self.v_list = v_list
        self.plot = self.make_plot(self.make_data())
        self.view = self.make_view()
        self.configure_traits(view=self.view)

    def make_data(self):
        x_data = chaco.ArrayDataSource(numpy.arange(0, 25, 1))
        pow_data_1 = chaco.ArrayDataSource(numpy.arange(0, 25, 2))
        pow_data_2 = chaco.ArrayDataSource(numpy.arange(100, 50, -1))
        pow_data_3 = chaco.ArrayDataSource(numpy.arange(-100, 0, 2))
        energy_data_1 = chaco.ArrayDataSource(numpy.arange(1000, 0, -3))

        data = chaco.ArrayPlotData(
            sample_times=x_data,
            positive_total_power=pow_data_1,
            negative_total_power=pow_data_2,
            net_total_power=pow_data_3,
            total_energy=energy_data_1
        )

        return data

    def make_plot(self, plot_data):
        times_mapper = chaco.LinearMapper(range=chaco.DataRange1D(plot_data.get_data('sample_times'), ))

        graph_colors = {'positive_total_power':'black',
                        'negative_total_power':'red',
                        'net_total_power':'purple',
                        'total_energy':'green'}

        # Power graphs
        power_names = ['positive_total_power',
                             'negative_total_power',
                             'net_total_power']
        power_data_range = chaco.DataRange1D(*[plot_data.get_data(name) for name in power_names])
        power_mapper = chaco.LinearMapper(range=power_data_range)


        power_plots = {}
        for plot_name in power_names:
            plot = chaco.LinePlot(index=plot_data.get_data('sample_times'),
                                  value=plot_data.get_data(plot_name),
                                  index_mapper=times_mapper,
                                  value_mapper=power_mapper,
                                  border_visible=False,
                                  bg_color='transparent',
                                  line_style='solid',
                                  color=graph_colors[plot_name],
                                  line_width=2)
            power_plots[plot_name] = plot

        # Energy graphs -- use a different value scale than power
        energy_plot_names = ['total_energy']
        energy_data_range = chaco.DataRange1D(*[plot_data.get_data(name) for name in energy_plot_names])
        energy_mapper = chaco.LinearMapper(range=energy_data_range)

        energy_plots = {}
        for plot_name in energy_plot_names:
            plot = chaco.LinePlot(index=plot_data.get_data('sample_times'),
                                  value=plot_data.get_data(plot_name),
                                  index_mapper=times_mapper,
                                  value_mapper=energy_mapper,
                                  border_visible=False,
                                  bg_color='transarent',
                                  line_style='solid',
                                  color=graph_colors[plot_name],
                                  line_width=2)
            energy_plots[plot_name] = plot


        # Blank plot -- Holds the grid and axis, and acts as a placeholder when
        # no other graphs are activated.
        blank_values = chaco.ArrayDataSource(numpy.zeros( plot_data.get_data('sample_times').get_size() ))
        blank_plot = chaco.LinePlot(index=plot_data.get_data('sample_times'),
                                    value=blank_values,
                                    index_mapper=times_mapper,
                                    value_mapper=power_mapper,
                                    border_visible=True,
                                    bg_color='transparent',
                                    line_width=0)
        times_axis = chaco.PlotAxis(orientation='bottom',
                                    title="Time (seconds)",
                                    mapper=times_mapper,
                                    component=blank_plot)
        power_axis = chaco.PlotAxis(orientation='left',
                                    title="Power (KW)",
                                    mapper=power_mapper,
                                    component=blank_plot)
        energy_axis = chaco.PlotAxis(orientation='right',
                                     title="Energy (KW-hrs)",
                                     mapper=energy_mapper,
                                     component=blank_plot)
        blank_plot.underlays.append(times_axis)
        blank_plot.underlays.append(power_axis)
        blank_plot.underlays.append(energy_axis)

        # Add zoom capability
        blank_plot.overlays.append(tools.ZoomTool(plot,
                                   tool_mode='range',
                                   axis='index',
                                   always_on=True,
                                   drag_button='left'))

        container = chaco.OverlayPlotContainer()
        for plot in power_plots.itervalues():
            container.add(plot)
        for plot in energy_plots.itervalues():
            container.add(plot)
        container.add(blank_plot)
        container.padding = 50

        # Legend
        legend = chaco.Legend(component=container, padding=20, align="ur")
        legend.tools.append(tools.LegendTool(legend, drag_button="right"))
        legend.plots = {}
        legend.plots.update(power_plots)
        legend.plots.update(energy_plots)
        container.overlays.append(legend)

        return container

    def make_view(self):
        return ui.View(
                     ui.HGroup(
                        ui.Item(name='v_list', editor=ui.EnumEditor(values=[str(v) for v in self.v_list])),
                        ui.Item(name='plot', label="", editor=enable.ComponentEditor(), show_label=False)

                     )
                  )
Ejemplo n.º 23
0
class DataAxis(t.HasTraits):
    name = t.Str()
    units = t.Str()
    scale = t.Float()
    offset = t.Float()
    size = t.Int()
    index_in_array = t.Int()
    low_value = t.Float()
    high_value = t.Float()
    value = t.Range('low_value', 'high_value')
    low_index = t.Int(0)
    high_index = t.Int()
    slice = t.Instance(slice)
    slice_bool = t.Bool(False)

    index = t.Range('low_index', 'high_index')
    axis = t.Array()

    def __init__(self,
                 size,
                 index_in_array,
                 name='',
                 scale=1.,
                 offset=0.,
                 units='undefined',
                 slice_bool=False):
        super(DataAxis, self).__init__()

        self.name = name
        self.units = units
        self.scale = scale
        self.offset = offset
        self.size = size
        self.high_index = self.size - 1
        self.low_index = 0
        self.index = 0
        self.index_in_array = index_in_array
        self.update_axis()

        self.on_trait_change(self.update_axis, ['scale', 'offset', 'size'])
        self.on_trait_change(self.update_value, 'index')
        self.on_trait_change(self.set_index_from_value, 'value')
        self.on_trait_change(self._update_slice, 'slice_bool')
        self.on_trait_change(self.update_index_bounds, 'size')
        self.slice_bool = slice_bool

    def __repr__(self):
        if self.name is not None:
            return self.name + ' index: ' + str(self.index_in_array)

    def update_index_bounds(self):
        self.high_index = self.size - 1

    def update_axis(self):
        self.axis = generate_axis(self.offset, self.scale, self.size)
        self.low_value, self.high_value = self.axis.min(), self.axis.max()


#        self.update_value()

    def _update_slice(self, value):
        if value is True:
            self.slice = slice(None)
        else:
            self.slice = None

    def get_axis_dictionary(self):
        adict = {
            'name': self.name,
            'scale': self.scale,
            'offset': self.offset,
            'size': self.size,
            'units': self.units,
            'index_in_array': self.index_in_array,
            'slice_bool': self.slice_bool
        }
        return adict

    def update_value(self):
        self.value = self.axis[self.index]

    def value2index(self, value):
        """Return the closest index to the given value if between the limits,
        otherwise it will return either the upper or lower limits

        Parameters
        ----------
        value : float

        Returns
        -------
        int
        """
        if value is None:
            return None
        else:
            index = int(round((value - self.offset) / \
            self.scale))
            if self.size > index >= 0:
                return index
            elif index < 0:
                messages.warning("The given value is below the axis limits")
                return 0
            else:
                messages.warning("The given value is above the axis limits")
                return int(self.size - 1)

    def index2value(self, index):
        return self.axis[index]

    def set_index_from_value(self, value):
        self.index = self.value2index(value)
        # If the value is above the limits we must correct the value
        self.value = self.index2value(self.index)

    def calibrate(self, value_tuple, index_tuple, modify_calibration=True):
        scale = (value_tuple[1] - value_tuple[0]) /\
        (index_tuple[1] - index_tuple[0])
        offset = value_tuple[0] - scale * index_tuple[0]
        if modify_calibration is True:
            self.offset = offset
            self.scale = scale
        else:
            return offset, scale

    traits_view = \
    tui.View(
        tui.Group(
            tui.Group(
                tui.Item(name = 'name'),
                tui.Item(name = 'size', style = 'readonly'),
                tui.Item(name = 'index_in_array', style = 'readonly'),
                tui.Item(name = 'index'),
                tui.Item(name = 'value', style = 'readonly'),
                tui.Item(name = 'units'),
                tui.Item(name = 'slice_bool', label = 'slice'),
            show_border = True,),
            tui.Group(
                tui.Item(name = 'scale'),
                tui.Item(name = 'offset'),
            label = 'Calibration',
            show_border = True,),
        label = "Data Axis properties",
        show_border = True,),
    )
Ejemplo n.º 24
0
class LiveTimestampModeler(traits.HasTraits):
    _trigger_device = traits.Instance(ttrigger.DeviceModel)

    sync_interval = traits.Float(2.0)
    has_ever_synchronized = traits.Bool(False, transient=True)

    frame_offset_changed = traits.Event

    timestamps_framestamps = traits.Array(shape=(None, 2), dtype=np.float)

    timestamp_data = traits.Any()
    block_activity = traits.Bool(False, transient=True)

    synchronize = traits.Button(label='Synchronize')
    synchronizing_info = traits.Any(None)

    gain_offset_residuals = traits.Property(
        depends_on=['timestamps_framestamps'])

    residual_error = traits.Property(depends_on='gain_offset_residuals')

    gain = traits.Property(depends_on='gain_offset_residuals')

    offset = traits.Property(depends_on='gain_offset_residuals')

    frame_offsets = traits.Dict()
    last_frame = traits.Dict()

    view_time_model_plot = traits.Button

    traits_view = View(
        Group(
            Item(
                name='gain',
                style='readonly',
                editor=TextEditor(evaluate=float, format_func=myformat),
            ),
            Item(
                name='offset',
                style='readonly',
                editor=TextEditor(evaluate=float, format_func=myformat2),
            ),
            Item(
                name='residual_error',
                style='readonly',
                editor=TextEditor(evaluate=float, format_func=myformat),
            ),
            Item('synchronize', show_label=False),
            Item('view_time_model_plot', show_label=False),
        ),
        title='Timestamp modeler',
    )

    def _block_activity_changed(self):
        if self.block_activity:
            print('Do not change frame rate or AIN parameters. '
                  'Automatic prevention of doing '
                  'so is not currently implemented.')
        else:
            print('You may change frame rate again')

    def _view_time_model_plot_fired(self):
        raise NotImplementedError('')

    def _synchronize_fired(self):
        if self.block_activity:
            print('Not synchronizing because activity is blocked. '
                  '(Perhaps because you are saving data now.')
            return

        orig_fps = self._trigger_device.frames_per_second_actual
        self._trigger_device.set_frames_per_second_approximate(0.0)
        self._trigger_device.reset_framecount_A = True  # trigger reset event
        self.synchronizing_info = (time.time() + self.sync_interval + 0.1,
                                   orig_fps)

    @traits.cached_property
    def _get_gain(self):
        result = self.gain_offset_residuals
        if result is None:
            # not enought data
            return None
        gain, offset, residuals = result
        return gain

    @traits.cached_property
    def _get_offset(self):
        result = self.gain_offset_residuals
        if result is None:
            # not enought data
            return None
        gain, offset, residuals = result
        return offset

    @traits.cached_property
    def _get_residual_error(self):
        result = self.gain_offset_residuals
        if result is None:
            # not enought data
            return None
        gain, offset, residuals = result
        if residuals is None or len(residuals) == 0:
            # not enought data
            return None
        assert len(residuals) == 1
        return residuals[0]

    @traits.cached_property
    def _get_gain_offset_residuals(self):
        if self.timestamps_framestamps is None:
            return None

        timestamps = self.timestamps_framestamps[:, 0]
        framestamps = self.timestamps_framestamps[:, 1]

        if len(timestamps) < 2:
            return None

        # like model_remote_to_local in flydra.analysis
        remote_timestamps = framestamps
        local_timestamps = timestamps

        a1 = remote_timestamps[:, np.newaxis]
        a2 = np.ones((len(remote_timestamps), 1))
        A = np.hstack((a1, a2))
        b = local_timestamps[:, np.newaxis]
        x, resids, rank, s = np.linalg.lstsq(A, b)

        gain = x[0, 0]
        offset = x[1, 0]
        return gain, offset, resids

    def set_trigger_device(self, device):
        self._trigger_device = device
        self._trigger_device.on_trait_event(
            self._on_trigger_device_reset_AIN_overflow_fired,
            name='reset_AIN_overflow')

    def _on_trigger_device_reset_AIN_overflow_fired(self):
        self.ain_overflowed = 0

    def _get_now_framestamp(self, max_error_seconds=0.003, full_output=False):
        count = 0
        while count <= 10:
            now1 = time.time()
            try:
                results = self._trigger_device.get_framestamp(
                    full_output=full_output)
            except ttrigger.NoDataError:
                raise ImpreciseMeasurementError('no data available')
            now2 = time.time()
            if full_output:
                framestamp, framecount, tcnt = results
            else:
                framestamp = results
            count += 1
            measurement_error = abs(now2 - now1)
            if framestamp % 1.0 < 0.1:
                warnings.warn('workaround of TCNT race condition on MCU...')
                continue
            if measurement_error < max_error_seconds:
                break
            time.sleep(0.01)  # wait 10 msec before trying again
        if not measurement_error < max_error_seconds:
            raise ImpreciseMeasurementError(
                'could not obtain low error measurement')
        if framestamp % 1.0 < 0.1:
            raise ImpreciseMeasurementError('workaround MCU bug')

        now = (now1 + now2) * 0.5
        if full_output:
            results = now, framestamp, now1, now2, framecount, tcnt
        else:
            results = now, framestamp
        return results

    def clear_samples(self, call_update=True):
        self.timestamps_framestamps = np.empty((0, 2))
        if call_update:
            self.update()

    def update(self, return_last_measurement_info=False):
        """call this function fairly often to pump information from the USB device"""
        if self.synchronizing_info is not None:
            done_time, orig_fps = self.synchronizing_info
            # suspended trigger pulses to re-synchronize
            if time.time() >= done_time:
                # we've waited the sync duration, restart
                self._trigger_device.set_frames_per_second_approximate(
                    orig_fps)
                self.clear_samples(call_update=False)  # avoid recursion
                self.synchronizing_info = None
                self.has_ever_synchronized = True

        results = self._get_now_framestamp(
            full_output=return_last_measurement_info)
        now, framestamp = results[:2]
        if return_last_measurement_info:
            start_timestamp, stop_timestamp, framecount, tcnt = results[2:]

        self.timestamps_framestamps = np.vstack(
            (self.timestamps_framestamps, [now, framestamp]))

        # If more than 100 samples,
        if len(self.timestamps_framestamps) > 100:
            # keep only the most recent 50.
            self.timestamps_framestamps = self.timestamps_framestamps[-50:]

        if return_last_measurement_info:
            return start_timestamp, stop_timestamp, framecount, tcnt

    def get_frame_offset(self, id_string):
        return self.frame_offsets[id_string]

    def register_frame(self,
                       id_string,
                       framenumber,
                       frame_timestamp,
                       full_output=False):
        """note that a frame happened and return start-of-frame time"""

        # This may get called from another thread (e.g. the realtime
        # image processing thread).

        # An important note about locking and thread safety: This code
        # relies on the Python interpreter to lock data structures
        # across threads. To do this internally, a lock would be made
        # for each variable in this instance and acquired before each
        # access. Because the data structures are simple Python
        # objects, I believe the operations are atomic and thus this
        # function is OK.

        # Don't trust camera drivers with giving a good timestamp. We
        # only use this to reset our framenumber-to-time data
        # gathering, anyway.
        frame_timestamp = time.time()

        if frame_timestamp is not None:
            last_frame_timestamp = self.last_frame.get(id_string, -np.inf)
            this_interval = frame_timestamp - last_frame_timestamp

            did_frame_offset_change = False
            if this_interval > self.sync_interval:
                if self.block_activity:
                    print(
                        'changing frame offset is disallowed, but you attempted to do it. ignoring.'
                    )
                else:
                    # re-synchronize camera

                    # XXX need to figure out where frame offset of two comes from:
                    self.frame_offsets[id_string] = framenumber - 2
                    did_frame_offset_change = True

            self.last_frame[id_string] = frame_timestamp

            if did_frame_offset_change:
                self.frame_offset_changed = True  # fire any listeners

        result = self.gain_offset_residuals
        if result is None:
            # not enough data
            if full_output:
                results = None, None, did_frame_offset_change
            else:
                results = None
            return results

        gain, offset, residuals = result
        corrected_framenumber = framenumber - self.frame_offsets[id_string]
        trigger_timestamp = corrected_framenumber * gain + offset

        if full_output:
            results = trigger_timestamp, corrected_framenumber, did_frame_offset_change
        else:
            results = trigger_timestamp
        return results
Ejemplo n.º 25
0
def create_int_multirange_feature(name, index, **kw):
    return traits.Instance(IntRangeFeature(name = name, index = index),(), **kw) 
Ejemplo n.º 26
0
class LiveTimestampModelerWithAnalogInput(LiveTimestampModeler):
    view_AIN = traits.Button(label='view analog input (AIN)')
    viewer = traits.Instance(AnalogInputViewer)

    # the actual analog data (as a wordstream)
    ain_data_raw = traits.Array(dtype=np.uint16, transient=True)
    old_data_raw = traits.Array(dtype=np.uint16, transient=True)

    timer3_top = traits.Property(
    )  # necessary to calculate precise timestamps for AIN data
    channel_names = traits.Property()
    Vcc = traits.Property(depends_on='_trigger_device')
    ain_overflowed = traits.Int(
        0,
        transient=True)  # integer for display (boolean readonly editor ugly)

    ain_wordstream_buffer = traits.Any()
    traits_view = View(
        Group(
            Item('synchronize', show_label=False),
            Item('view_time_model_plot', show_label=False),
            Item('ain_overflowed', style='readonly'),
            Item(
                name='gain',
                style='readonly',
                editor=TextEditor(evaluate=float, format_func=myformat),
            ),
            Item(
                name='offset',
                style='readonly',
                editor=TextEditor(evaluate=float, format_func=myformat2),
            ),
            Item(
                name='residual_error',
                style='readonly',
                editor=TextEditor(evaluate=float, format_func=myformat),
            ),
            Item('view_AIN', show_label=False),
        ),
        title='Timestamp modeler',
    )

    @traits.cached_property
    def _get_Vcc(self):
        return self._trigger_device.Vcc

    def _get_timer3_top(self):
        return self._trigger_device.timer3_top

    def _get_channel_names(self):
        return self._trigger_device.enabled_channel_names

    def update_analog_input(self):
        """call this function frequently to avoid overruns"""
        new_data_raw = self._trigger_device.get_analog_input_buffer_rawLE()
        data_raw = np.hstack((new_data_raw, self.old_data_raw))
        self.ain_data_raw = new_data_raw
        newdata_all = []
        chan_all = []
        any_overflow = False
        #cum_framestamps = []
        while len(data_raw):
            result = cDecode.process(data_raw)
            (N, samples, channels, did_overflow, framestamp) = result
            if N == 0:
                # no data was able to be processed
                break
            data_raw = data_raw[N:]
            newdata_all.append(samples)
            chan_all.append(channels)
            if did_overflow:
                any_overflow = True
            # Save framestamp data.
            # This is not done yet:
            ## if framestamp is not None:
            ##     cum_framestamps.append( framestamp )
        self.old_data_raw = data_raw  # save unprocessed data for next run

        if any_overflow:
            # XXX should move to logging the error.
            self.ain_overflowed = 1
            raise AnalogDataOverflowedError()

        if len(chan_all) == 0:
            # no data
            return
        chan_all = np.hstack(chan_all)
        newdata_all = np.hstack(newdata_all)
        USB_channel_numbers = np.unique(chan_all)
        #print len(newdata_all),'new samples on channels',USB_channel_numbers

        ## F_OSC = 8000000.0 # 8 MHz
        ## adc_prescaler = 128
        ## downsample = 20 # maybe 21?
        ## n_chan = 3
        ## F_samp = F_OSC/adc_prescaler/downsample/n_chan
        ## dt=1.0/F_samp
        ## ## print '%.1f Hz sampling. %.3f msec dt'%(F_samp,dt*1e3)
        ## MAXLEN_SEC=0.3
        ## #MAXLEN = int(MAXLEN_SEC/dt)
        MAXLEN = 5000  #int(MAXLEN_SEC/dt)
        ## ## print 'MAXLEN',MAXLEN
        ## ## print

        for USB_chan in USB_channel_numbers:
            vi = self.viewer.usb_device_number2index[USB_chan]
            cond = chan_all == USB_chan
            newdata = newdata_all[cond]

            oldidx = self.viewer.channels[vi].index
            olddata = self.viewer.channels[vi].data

            if len(oldidx):
                baseidx = oldidx[-1] + 1
            else:
                baseidx = 0.0
            newidx = np.arange(len(newdata), dtype=np.float) + baseidx

            tmpidx = np.hstack((oldidx, newidx))
            tmpdata = np.hstack((olddata, newdata))

            if len(tmpidx) > MAXLEN:
                # clip to MAXLEN
                self.viewer.channels[vi].index = tmpidx[-MAXLEN:]
                self.viewer.channels[vi].data = tmpdata[-MAXLEN:]
            else:
                self.viewer.channels[vi].index = tmpidx
                self.viewer.channels[vi].data = tmpdata

    def _view_AIN_fired(self):
        self.viewer.edit_traits()
Ejemplo n.º 27
0
class CameraUI(traits.HasTraits):
    """Camera settings defines basic camera settings
    """
    camera_control = traits.Instance(Camera, transient = True)
    
    cameras = traits.List([_NO_CAMERAS],transient = True)
    camera = traits.Any(value = _NO_CAMERAS, desc = 'camera serial number', editor = ui.EnumEditor(name = 'cameras'))
    
    search = traits.Button(desc = 'camera search action')

    _is_initialized= traits.Bool(False, transient = True)
    
    play = traits.Button(desc = 'display preview action')
    stop = traits.Button(desc = 'close preview action')
    on_off = traits.Button('On/Off', desc = 'initiate/Uninitiate camera action')

    gain = create_range_feature('gain',desc = 'camera gain',transient = True)
    shutter = create_range_feature('shutter', desc = 'camera exposure time',transient = True)
    format = create_mapped_feature('format',_FORMAT, desc = 'image format',transient = True)
    roi = traits.Instance(ROI,transient = True)
    
    im_shape = traits.Property(depends_on = 'format.value,roi.values')
    im_dtype = traits.Property(depends_on = 'format.value')
    
    capture = traits.Button()
    save_button = traits.Button('Save as...')
    
    message = traits.Str(transient = True)
    
    view = ui.View(ui.Group(ui.HGroup(ui.Item('camera', springy = True),
                           ui.Item('search', show_label = False, springy = True),
                           ui.Item('on_off', show_label = False, springy = True),
                           ui.Item('play', show_label = False, enabled_when = 'is_initialized', springy = True),
                           ui.Item('stop', show_label = False, enabled_when = 'is_initialized', springy = True),
                           ),
                    ui.Group(
                        ui.Item('gain', style = 'custom'),
                        ui.Item('shutter', style = 'custom'),
                        ui.Item('format', style = 'custom'),
                        ui.Item('roi', style = 'custom'),
                        ui.HGroup(ui.Item('capture',show_label = False),
                        ui.Item('save_button',show_label = False)),
                        enabled_when = 'is_initialized',
                        ),
                        ),
                resizable = True,
                statusbar = [ ui.StatusItem( name = 'message')],
                buttons = ['OK'])
    
    #default initialization    
    def __init__(self, **kw):
        super(CameraUI, self).__init__(**kw)
        self.search_cameras()

    def _camera_control_default(self):
        return Camera()

    def _roi_default(self):
        return ROI()
        
    #@display_cls_error 
    def _get_im_shape(self):
        top, left, width, height = self.roi.values
        shape = (height, width)
        try:
            colors = _COLORS[self.format.value] 
            if colors > 1:
                shape += (colors,)
        except KeyError:
            raise NotImplementedError('Unsupported format')  
        return shape
    
    #@display_cls_error    
    def _get_im_dtype(self):
        try:        
            return _DTYPE[self.format.value]
        except KeyError:
            raise NotImplementedError('Unsupported format')        
        
   
    def _search_fired(self):
        self.search_cameras()
        
    #@display_cls_error
    def search_cameras(self):
        """
        Finds cameras if any and selects first from list
        """
        try:
            cameras = get_number_cameras()
        except Exception as e:
            cameras = []
            raise e
        finally:
            if len(cameras) == 0:
                cameras = [_NO_CAMERAS]
            self.cameras = cameras
            self.camera = cameras[0]

    #@display_cls_error
    def _camera_changed(self):
        if self._is_initialized:
            self._is_initialized= False
            self.camera_control.close()
            self.message = 'Camera uninitialized'
    
    #@display_cls_error
    def init_camera(self):
        self._is_initialized= False
        if self.camera != _NO_CAMERAS:
            self.camera_control.init(self.camera)
            self.init_features()
            self._is_initialized= True
            self.message = 'Camera initialized'
            
    #@display_cls_error
    def _on_off_fired(self):
        if self._is_initialized:
            self._is_initialized= False
            self.camera_control.close()
            self.message = 'Camera uninitialized'
        else:
            self.init_camera()
            
    #@display_cls_error
    def init_features(self):
        """
        Initializes all features to values given by the camera
        """
        features = self.camera_control.get_camera_features()
        self._init_single_valued_features(features)
        self._init_roi(features)
    
    #@display_cls_error
    def _init_single_valued_features(self, features):
        """
        Initializes all single valued features to camera values
        """
        for name, id in list(_SINGLE_VALUED_FEATURES.items()):
            feature = getattr(self, name)
            feature.low, feature.high = features[id]['params'][0]
            feature.value = self.camera_control.get_feature(id)[0]
            
    #@display_cls_error
    def _init_roi(self, features):
        for i,name in enumerate(('top','left','width','height')):
            feature = getattr(self.roi, name)
            low, high = features[FEATURE_ROI]['params'][i]
            value = self.camera_control.get_feature(FEATURE_ROI)[i]
            try:
                feature.value = value
            finally:
                feature.low, feature.high = low, high
                       
    @traits.on_trait_change('format.value')
    def _on_format_change(self, object, name, value):
        if self._is_initialized:
            self.camera_control.set_preview_state(STOP_PREVIEW)
            self.camera_control.set_stream_state(STOP_STREAM)
            self.set_feature(FEATURE_PIXEL_FORMAT, [value])
            
    @traits.on_trait_change('gain.value,shutter.value')
    def _single_valued_feature_changed(self, object, name, value):
        if self._is_initialized:
            self.set_feature(object.id, [value])

    #@display_cls_error
    def set_feature(self, id, values, flags = 2):
        self.camera_control.set_feature(id, values, flags = flags)
            
    @traits.on_trait_change('roi.values')
    def a_roi_feature_changed(self, object, name, value):
        if self._is_initialized:
            self.set_feature(FEATURE_ROI, value)
            try:
                self._is_initialized= False
                self.init_features()
            finally:
                self._is_initialized= True
        
    #@display_cls_error                    
    def _play_fired(self):
        self.camera_control.set_preview_state(STOP_PREVIEW)
        self.camera_control.set_stream_state(STOP_STREAM)
        self.camera_control.set_stream_state(START_STREAM)
        self.camera_control.set_preview_state(START_PREVIEW)
        
    #@display_cls_error
    def _stop_fired(self): 
        self.camera_control.set_preview_state(STOP_PREVIEW)
        self.camera_control.set_stream_state(STOP_STREAM)
        self.error = ''
 
    #@display_cls_error
    def _format_changed(self, value):
        self.camera_control.set_preview_state(STOP_PREVIEW)
        self.camera_control.set_stream_state(STOP_STREAM)
        self.camera_control.set_feature(FEATURE_PIXEL_FORMAT, [value],2)
    
    #@display_cls_error
    def _capture_fired(self):
        self.camera_control.set_stream_state(STOP_STREAM)
        self.camera_control.set_stream_state(START_STREAM)
        im = self.capture_image()
        plt.imshow(im)
        plt.show()

    def capture_image(self):
        im = numpy.empty(shape = self.im_shape, dtype = self.im_dtype)
        self.camera_control.get_next_frame(im)
        return im.newbyteorder('>')
        
    def save_image(self, fname):
        """Captures image and saves to format guessed from filename extension"""
        im = self.capture_image()
        base, ext = os.path.splitext(fname)
        if ext == '.npy':
            numpy.save(fname, im)
        else:
            im = toimage(im)
            im.save(fname)

    def _save_button_fired(self):
        f = pyface.FileDialog(action = 'save as') 
                       #wildcard = self.filter)
        if f.open() == pyface.OK: 
            self.save_image(f.path)                 

    def capture_HDR(self):
        pass
                
    def __del__(self):
        try:
            self.camera_control.set_preview_state(STOP_PREVIEW)
            self.camera_control.set_stream_state(STOP_STREAM)
        except:
            pass
Ejemplo n.º 28
0
class CSplinePlotter(traits.HasTraits):
    """Generates and displays a plot for a cubic spline."""
    container = traits.Instance(chaco.OverlayPlotContainer)
    plotdata = traits.Instance(chaco.ArrayPlotData)
    traits_view = ui.View(ui.Item('container',
                                  editor=ComponentEditor(),
                                  show_label=False),
                          width=500,
                          height=500,
                          resizable=True,
                          title='CubicSpline Plot')

    def __init__(self,
                 cubic_spline,
                 velocity_max=0,
                 acceleration_max=0,
                 jerk_max=0,
                 velocity_min=None,
                 acceleration_min=None,
                 jerk_min=None,
                 title="",
                 start_idx=0,
                 end_idx=-1,
                 mass=None,
                 plot_pos=True,
                 plot_vel=True,
                 plot_accel=True,
                 plot_jerk=True,
                 plot_power=True):
        """If a 'mass' argument is supplied, then the power will be plotted."""

        super(CSplinePlotter, self).__init__()

        self.cspline = cubic_spline

        self.v_max = velocity_max
        self.a_max = acceleration_max
        self.j_max = jerk_max

        self.v_min = 0 if velocity_min is None else velocity_min
        self.a_min = -self.a_max if acceleration_min is None else acceleration_min
        self.j_min = -self.j_max if jerk_min is None else jerk_min
        self.title = title
        self.mass = mass

        self.plot_pos = plot_pos
        self.plot_vel = plot_vel
        self.plot_accel = plot_accel
        self.plot_jerk = plot_jerk
        self.plot_power = plot_power and mass is not None

        self.container = chaco.OverlayPlotContainer(padding=52,
                                                    fill_padding=True,
                                                    bgcolor="transparent")
        self.make_plotdata(start_idx, end_idx)
        self.make_plots()

    def make_plotdata(self, start_idx, end_idx):
        if end_idx < 0:
            end_idx = len(
                self.cspline.t) + end_idx  # convert to absolute index

        knot_times = self.cspline.t[start_idx:end_idx + 1]
        sample_times = numpy.linspace(self.cspline.t[start_idx],
                                      self.cspline.t[end_idx], 200)
        endpoint_times = numpy.array(
            [self.cspline.t[start_idx], self.cspline.t[end_idx]])

        positions = []
        velocities = []
        powers = []
        samples = self.cspline.evaluate_sequence(sample_times)
        for sample in samples:
            if self.plot_pos: positions.append(sample.pos)
            if self.plot_vel: velocities.append(sample.vel)
            if self.plot_power:
                powers.append(self.mass * sample.accel * sample.vel /
                              1000.0)  # In KWs

        if self.plot_accel:
            accelerations = numpy.array(self.cspline.a[start_idx:end_idx + 1])
        else:
            accelerations = []

        if self.plot_jerk and len(self.cspline.j):
            jerks = numpy.array(self.cspline.j[start_idx:end_idx] +
                                [self.cspline.j[end_idx - 1]])
        else:
            jerks = []

        max_vel = numpy.array([self.v_max for t in endpoint_times])
        min_vel = numpy.array([self.v_min for t in endpoint_times])

        max_accel = numpy.array([self.a_max for t in endpoint_times])
        min_accel = numpy.array([self.a_min for t in endpoint_times])

        max_jerk = numpy.array([self.j_max for t in endpoint_times])
        min_jerk = numpy.array([self.j_min for t in endpoint_times])

        self.plotdata = chaco.ArrayPlotData(positions=positions,
                                            endpoint_times=endpoint_times,
                                            knot_times=knot_times,
                                            sample_times=sample_times,
                                            velocities=velocities,
                                            accelerations=accelerations,
                                            jerks=jerks,
                                            powers=powers,
                                            max_vel=max_vel,
                                            min_vel=min_vel,
                                            max_accel=max_accel,
                                            min_accel=min_accel,
                                            max_jerk=max_jerk,
                                            min_jerk=min_jerk)

    def make_plots(self):
        main_plot = chaco.Plot(self.plotdata, padding=0)

        colors = {
            'pos': 'black',
            'vel': 'blue',
            'accel': 'red',
            'jerk': 'green',
            'power': 'purple'
        }
        left_y_axis_title_list = []
        legend_dict = {}
        if self.plot_vel:
            vel_plot = main_plot.plot(("sample_times", "velocities"),
                                      type="line",
                                      color=colors['vel'],
                                      line_width=2)
            max_vel_plot = main_plot.plot(("endpoint_times", "max_vel"),
                                          color=colors['vel'],
                                          line_style='dash',
                                          line_width=0.60)
            min_vel_plot = main_plot.plot(("endpoint_times", "min_vel"),
                                          color=colors['vel'],
                                          line_style='dash',
                                          line_width=0.60)
            left_y_axis_title_list.append("Velocity (m/s)")
            legend_dict['vel'] = vel_plot

        if self.plot_accel:
            accel_plot = main_plot.plot(("knot_times", "accelerations"),
                                        type="line",
                                        color=colors['accel'],
                                        line_width=2)
            max_accel_plot = main_plot.plot(("endpoint_times", "max_accel"),
                                            color=colors['accel'],
                                            line_style='dash',
                                            line_width=0.55)
            min_accel_plot = main_plot.plot(("endpoint_times", "min_accel"),
                                            color=colors['accel'],
                                            line_style='dash',
                                            line_width=0.55)
            left_y_axis_title_list.append("Accel (m/s2)")
            legend_dict['accel'] = accel_plot

        if self.plot_jerk:
            jerk_plot = main_plot.plot(("knot_times", "jerks"),
                                       type="line",
                                       color=colors['jerk'],
                                       line_width=2,
                                       render_style="connectedhold")
            max_jerk_plot = main_plot.plot(("endpoint_times", "max_jerk"),
                                           color=colors['jerk'],
                                           line_style='dash',
                                           line_width=0.45)
            min_jerk_plot = main_plot.plot(("endpoint_times", "min_jerk"),
                                           color=colors['jerk'],
                                           line_style='dash',
                                           line_width=0.45)
            left_y_axis_title_list.append("Jerk (m/s3)")
            legend_dict['jerk'] = jerk_plot

        if self.plot_power:
            power_plot = main_plot.plot(("sample_times", "powers"),
                                        type="line",
                                        color=colors['power'],
                                        line_width=2)
            left_y_axis_title_list.append("Power (KW)")
            legend_dict['power'] = power_plot

        main_plot.y_axis.title = ", ".join(left_y_axis_title_list)
        self.container.add(main_plot)

        # plot positions (on a separate scale from the others)
        if self.plot_pos:
            pos_plot = chaco.create_line_plot([
                self.plotdata.arrays["sample_times"],
                self.plotdata.arrays["positions"]
            ],
                                              color=colors['pos'],
                                              width=2)
            legend_dict['pos'] = pos_plot
            self.container.add(pos_plot)

            # add a second y-axis for the positions
            pos_y_axis = chaco.PlotAxis(pos_plot,
                                        orientation="right",
                                        title="Position (meters)")
            self.container.overlays.append(pos_y_axis)

        # make Legend
        legend = chaco.Legend(component=self.container, padding=20, align="ul")
        legend.plots = legend_dict
        legend.tools.append(tools.LegendTool(legend, drag_button="left"))
        self.container.overlays.append(legend)

        # Add title, if any
        if self.title:
            main_plot.title = self.title
            main_plot.title_position = "inside top"

    def display_plot(self):
        self.configure_traits()