Table of Contents

Scan Point Generator

Build Status Test coverage Code Health Latest PyPI version Documentation

Scan point generator contains a number of python iterators that are used in GDA and malcolm to determine the motor demand positions and dataset indexes that various scan types will produce

Installation

To install the latest release, type:

pip install scanpointgenerator

To install the latest code directly from source, type:

pip install git+git://github.com/dls-controls/scanpointgenerator.git

Changelog

See CHANGELOG

Contributing

See CONTRIBUTING

License

APACHE License. (see LICENSE)

Documentation

Full documentation is available at http://scanpointgenerator.readthedocs.org

Architecture

Every scan point generator inherits from the ScanPointGenerator baseclass. This baseclass provides the following API:

class scanpointgenerator.Generator[source]

Base class for all malcolm scan point generators

Variables:
  • position_units (dict) – Dict of str position_name -> str position_unit for each scannable dimension. E.g. {“x”: “mm”, “y”: “mm”}
  • index_dims (list) – List of the int dimension sizes for the dataset. This will have the same length as the position_units list for square scans but will be shorter for things like spiral scans. E.g. [15]
  • index_names (list) – List of the str dimension names for the dataset. This will have the same length as the index_dims. E.g. [“spiral_i”]
  • axes (list) – List of scannable names, used in GDA to reconstruct Point in CompoundGenerators
iterator()[source]

An iterator yielding positions at each scan point

Yields:Point – The next scan Point
to_dict()[source]

Abstract method to convert object attributes into a dictionary

classmethod from_dict(d)[source]

Abstract method to create a ScanPointGenerator instance from a serialised dictionary

Parameters:d (dict) – Dictionary of attributes
Returns:New ScanPointGenerator instance
Return type:Generator
classmethod register_subclass(generator_type)[source]

Register a subclass so from_dict() works

Parameters:generator_type (Generator) – Subclass to register

Each point produced by the iterator represents a scan point, with the following API:

class scanpointgenerator.Point[source]

Contains information about for each scan point

Variables:
  • positions (dict) – Dict of str position_name -> float position for each scannable dimension. E.g. {“x”: 0.1, “y”: 2.2}
  • lower (dict) – Dict of str position_name -> float lower_bound for each scannable dimension. E.g. {“x”: 0.95, “y”: 2.15}
  • upper (dict) – Dict of str position_name -> float upper_bound for each scannable dimension. E.g. {“x”: 1.05, “y”: 2.25}
  • indexes (list) – List of int indexes for each dataset dimension, fastest changing last. E.g. [15]
  • duration (int) – Int or None for duration of the point exposure

Using the API

You would use a generator in a step scan like this:

>>> for point in generator.iterator():
>>>     for mname, mpos in point.positions():
>>>         motors[mname].move(mpos)
>>>     det.write_data_to_index(point.indexes)

Line Generator

class scanpointgenerator.LineGenerator(name, units, start, stop, num, alternate_direction=False)[source]

Generate a line of equally spaced N-dimensional points

Parameters:
  • name (str/list(str) – The scannable name(s) E.g. “x” or [“x”, “y”]
  • units (str) – The scannable units. E.g. “mm”
  • start (float/list(float) – The first position to be generated. e.g. 1.0 or [1.0, 2.0]
  • stop (float or list(float) – The first position to be generated. e.g. 5.0 or [5.0, 10.0]
  • num (int) – The number of points to generate. E.g. 5
  • alternate_direction (bool) – Specifier to reverse direction if generator is nested
to_dict()[source]

Convert object attributes into a dictionary

classmethod from_dict(d)[source]

Create a LineGenerator instance from a serialised dictionary

Parameters:d (dict) – Dictionary of attributes
Returns:New LineGenerator instance
Return type:LineGenerator

Examples

This example defines a motor “x” with engineering units “mm” which is being scanned from 0mm to 1mm with 5 scan points inclusive of the start. Note that the capture points are as given, so the bounds will be +-0.5*step of each capture point.

from scanpointgenerator import LineGenerator, plot_generator

gen = LineGenerator("x", "mm", 0.0, 1.0, 5)
plot_generator(gen)

(Source code)

LineGenerator is N dimensional; just pass in ND lists for name, start and stop.

from scanpointgenerator import LineGenerator, plot_generator

gen = LineGenerator(["x", "y"], "mm", [1.0, 2.0], [5.0, 10.0], 5)
plot_generator(gen)

(Source code)

Spiral Generator

class scanpointgenerator.SpiralGenerator(names, units, centre, radius, scale=1.0, alternate_direction=False)[source]

Generate the points of an Archimedean spiral

Parameters:
  • names (list(str) – The scannable names e.g. [“x”, “y”]
  • units (str) – The scannable units e.g. “mm”
  • centre (list) – List of two coordinates of centre point of spiral
  • radius (float) – Radius of spiral
  • scale (float) – Rate at which spiral expands; higher scale gives fewer points for same radius
  • alternate_direction (bool) – Specifier to reverse direction if generator is nested
to_dict()[source]

Convert object attributes into a dictionary

classmethod from_dict(d)[source]

Create a SpiralGenerator instance from a serialised dictionary

Parameters:d (dict) – Dictionary of attributes
Returns:New SpiralGenerator instance
Return type:SpiralGenerator

Examples

This example defines motors “x” and “y” with engineering units “mm” which will be scanned in a spiral filling a circle of radius 5mm.

from scanpointgenerator import SpiralGenerator, plot_generator

gen = SpiralGenerator(["x", "y"], "mm", [0.0, 0.0], 5.0)
plot_generator(gen)

(Source code)

In this example the spiral is scaled to be more sparse.

from scanpointgenerator import SpiralGenerator, plot_generator

gen = SpiralGenerator(["x", "y"], "mm", [0.0, 0.0], 5.0, scale=2.0)
plot_generator(gen)

(Source code)

Lissajous Generator

class scanpointgenerator.LissajousGenerator(names, units, box, num_lobes, num_points=None)[source]

Generate the points of a Lissajous curve

Parameters:
  • names (list(str) – The scannable names e.g. [“x”, “y”]
  • units (str) – The scannable units e.g. “mm”
  • box (dict) – Dictionary of centre, width and height representing box to fill with points
  • num_lobes (int) – Number of x-direction lobes for curve; will have num_lobes+1 y-direction lobes
  • num_points (int) – The number of points to fill the Lissajous curve. Default is 250 * num_lobes
to_dict()[source]

Convert object attributes into a dictionary

classmethod from_dict(d)[source]

Create a LissajousGenerator instance from a serialised dictionary

Parameters:d (dict) – Dictionary of attributes
Returns:New LissajousGenerator instance
Return type:LissajousGenerator

Examples

This example defines motors “x” and “y” with engineering units “mm” which will be scanned over a 3x4 lobe Lissajous curve with filling a 1x1mm rectangle.

from scanpointgenerator import LissajousGenerator, plot_generator

box = dict(centre=[0.0, 0.0], width=1.0, height=1.0)
gen = LissajousGenerator(['x', 'y'], "mm", box=box, num_lobes=3, num_points=50)
plot_generator(gen)

(Source code)

The number of points has been lowered from the default to make the plot more visible. The following plot is for 10x11 lobes with the default number of points.

from scanpointgenerator import LissajousGenerator, plot_generator

box = dict(centre=[0.0, 0.0], width=1.0, height=1.0)
gen = LissajousGenerator(['x', 'y'], "mm", box=box, num_lobes=20)
plot_generator(gen, show_indexes=False)

(Source code)

Array Generator

class scanpointgenerator.ArrayGenerator(name, units, points, lower_bounds=None, upper_bounds=None)[source]

Generate a given n-dimensional array of points

Parameters:
  • name (str/list(str) – ND list of scannable names e.g. “x” or [“x”, “y”]
  • units (str) – The scannable units. E.g. “mm”
  • points (list) – List of ND lists of coordinates e.g. [1.0, 2.0, 3.0] or [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]
  • lower_bounds (list) – List of ND lists of lower bound coordinates
  • upper_bounds (list) – List of ND lists of upper bound coordinates
to_dict()[source]

Convert object attributes into a dictionary

classmethod from_dict(d)[source]

Create a ArrayGenerator instance from a serialised dictionary

Parameters:d (dict) – Dictionary of attributes
Returns:New ArrayGenerator instance
Return type:ArrayGenerator

Examples

The ArrayGenerator takes an N-Dimensional array of coordinates and creates Points with calculated upper and lower bounds. You can also provide your own bounds.

from scanpointgenerator import ArrayGenerator, plot_generator

points = [0.0, 2.0, 3.0, 5.0, 7.0, 8.0]
array = ArrayGenerator("x", "mm", points)
plot_generator(array)

(Source code)

And a 2D scan.

from scanpointgenerator import ArrayGenerator, plot_generator

points = [[0.0, 2.0], [2.0, 3.0], [3.0, 5.0], [5.0, 6.0], [7.0, 7.0], [8.0, 9.0]]
array = ArrayGenerator(["x", "y"], "mm", points)
plot_generator(array)

(Source code)

Compound Generator

class scanpointgenerator.CompoundGenerator(generators, excluders, mutators)[source]

Nest N generators, apply exclusion regions to relevant generator pairs and apply any mutators before yielding points

Parameters:
  • generators (list(Generator) – List of Generators to nest
  • excluders (list(Excluder) – List of Excluders to filter points by
  • mutators (list(Mutator) – List of Mutators to apply to each point
iterator()[source]

Top level iterator to mutate points and yield them

Yields:Point – Mutated points
contains_point(point)[source]

Filter a Point through all Excluders

Parameters:point (Point) – Point to check
Returns:Whether point is contained by all Excluders
Return type:bool
to_dict()[source]

Convert object attributes into a dictionary

classmethod from_dict(d)[source]

Create a CompoundGenerator instance from a serialised dictionary

Parameters:d (dict) – Dictionary of attributes
Returns:New CompoundGenerator instance
Return type:CompoundGenerator

Raster Scan Example

This scan will create an outer “y” line scan with 4 points, then nest an “x” line scan inside it with 5 points.

from scanpointgenerator import LineGenerator, CompoundGenerator, plot_generator

xs = LineGenerator("x", "mm", 0.0, 0.5, 5, alternate_direction=False)
ys = LineGenerator("y", "mm", 0.0, 0.5, 4)
gen = CompoundGenerator([ys, xs], [], [])
plot_generator(gen)

(Source code)

Snake Scan Example

This scan will create an outer “y” line scan with 4 points, then nest an “x” line scan inside it with 5 points. On every second row, the “x” line scan will be run in reverse to give a snake scan.

from scanpointgenerator import LineGenerator, CompoundGenerator, plot_generator

xs = LineGenerator("x", "mm", 0.0, 0.5, 5, alternate_direction=True)
ys = LineGenerator("y", "mm", 0.0, 0.5, 4)
gen = CompoundGenerator([ys, xs], [], [])
plot_generator(gen)

(Source code)

Excluders

Excluders are used to filter points in a generator based on a pair of coordinates and a region of interest.

class scanpointgenerator.Excluder(roi, scannables)[source]

A class to remove points that lie outside of a given 2D region of interest

Parameters:
  • roi (ROI) – Region of interest to filter points by
  • scannables (list) – List of two scannables to filter points by
contains_point(d)[source]

Create a 2D sub-point from the ND point d and pass the sub_point to the roi to check if it contains it.

Parameters:d (dict) – Dictionary representation of point
Returns:Whether roi contains the given point
Return type:bool
to_dict()[source]

Convert object attributes into a dictionary

classmethod from_dict(d)[source]

Create a Excluder instance from a serialised dictionary

Parameters:d (dict) – Dictionary of attributes
Returns:New Excluder instance
Return type:Excluder

CircularROI Example

Here we use a CircularROI to filter the points of a snake scan

from scanpointgenerator import LineGenerator, CompoundGenerator, \
Excluder, plot_generator
from scanpointgenerator.circular_roi import CircularROI

x = LineGenerator("x", "mm", 0.0, 4.0, 5, alternate_direction=True)
y = LineGenerator("y", "mm", 0.0, 3.0, 4)
circle = Excluder(CircularROI([2.0, 1.0], 2.0), ["x", "y"])
gen = CompoundGenerator([y, x], [], [])
plot_generator(gen, circle)

(Source code)

And with the excluder applied

from scanpointgenerator import LineGenerator, CompoundGenerator, \
Excluder, plot_generator
from scanpointgenerator.circular_roi import CircularROI

x = LineGenerator("x", "mm", 0.0, 4.0, 5, alternate_direction=True)
y = LineGenerator("y", "mm", 0.0, 3.0, 4)
circle = Excluder(CircularROI([2.0, 1.0], 2.0), ["x", "y"])
excluder = Excluder(circle, ['x', 'y'])
gen = CompoundGenerator([y, x], [circle], [])
plot_generator(gen, circle)

(Source code)

Mutators

Mutators are used for post processing points after they have been generated and filtered by any regions of interest.

class scanpointgenerator.Mutator[source]

Abstract class to apply a mutation to the points of an ND ScanPointGenerator

mutate(iterator)[source]

Abstract method to take each point from the given iterator, apply a mutation and then yield the new point

Parameters:iterator (iter) – Iterator to mutate
Yields:Point – Mutated points from generator
to_dict()[source]

Abstract method to convert object attributes into a dictionary

classmethod from_dict(d)[source]

Abstract method to create a Mutator instance from a serialised dictionary

Parameters:d (dict) – Dictionary of attributes
Returns:New Mutator instance
Return type:Mutator
classmethod register_subclass(mutator_type)[source]

Register a subclass so from_dict() works

Parameters:mutator_type (Mutator) – Subclass to register

RandomOffsetMutator

This is used to apply a random offset to each point in an iterator. Here we apply it to a snake scan

from scanpointgenerator import LineGenerator, CompoundGenerator, plot_generator

xs = LineGenerator("x", "mm", 0.0, 0.5, 5, alternate_direction=True)
ys = LineGenerator("y", "mm", 0.0, 0.5, 4)
gen = CompoundGenerator([ys, xs], [], [])
plot_generator(gen)

(Source code)

And with the random offset

from scanpointgenerator import LineGenerator, CompoundGenerator, RandomOffsetMutator, plot_generator

xs = LineGenerator("x", "mm", 0.0, 0.5, 5, alternate_direction=True)
ys = LineGenerator("y", "mm", 0.0, 0.5, 4)
random_offset = RandomOffsetMutator(seed=1, axes = ["x", "y"], max_offset=dict(x=0.05, y=0.05))
gen = CompoundGenerator([ys, xs], [], [random_offset])
plot_generator(gen)

(Source code)

Creating a Generator

The idea of CompoundGenerator is that you can combine generators, excluders and mutators arbitrarily. The following will show some more extensive examples to show the capabilities of scanpointgenerator.

A spiral scan with an offset rectangular roi overlay and randomly offset points in the y direction

from scanpointgenerator import LineGenerator, SpiralGenerator, \
CompoundGenerator, Excluder, RandomOffsetMutator, plot_generator
from scanpointgenerator.rectangular_roi import RectangularROI

spiral = SpiralGenerator(["x", "y"], "mm", [0.0, 0.0], 10.0,
                         alternate_direction=True)
rectangle = Excluder(RectangularROI([1.0, 1.0], 8.0, 8.0), ["x", "y"])
mutator = RandomOffsetMutator(2, ["x", "y"], dict(x=0.0, y=0.25))
gen = CompoundGenerator([spiral], [rectangle], [mutator])

plot_generator(gen, rectangle)

(Source code)

A spiral scan at each point of a line scan with alternating direction

from scanpointgenerator import LineGenerator, SpiralGenerator, \
CompoundGenerator

line = LineGenerator("z", "mm", 0.0, 20.0, 3)
spiral = SpiralGenerator(["x", "y"], "mm", [0.0, 0.0], 1.2,
                         alternate_direction=True)
gen = CompoundGenerator([line, spiral], [], [])

for point in gen.iterator():
    for axis, value in point.positions.items():
        point.positions[axis] = round(value, 3)
    print(point.positions)

(Source code)

{'y': -0.321, 'x': 0.237, 'z': 0.0}
{'y': -0.25, 'x': -0.644, 'z': 0.0}
{'y': 0.695, 'x': -0.56, 'z': 0.0}
{'y': 0.992, 'x': 0.361, 'z': 0.0}
{'y': 0.992, 'x': 0.361, 'z': 10.0}
{'y': 0.695, 'x': -0.56, 'z': 10.0}
{'y': -0.25, 'x': -0.644, 'z': 10.0}
{'y': -0.321, 'x': 0.237, 'z': 10.0}
{'y': -0.321, 'x': 0.237, 'z': 20.0}
{'y': -0.25, 'x': -0.644, 'z': 20.0}
{'y': 0.695, 'x': -0.56, 'z': 20.0}
{'y': 0.992, 'x': 0.361, 'z': 20.0}

Three nested line scans with an excluder operating on the innermost and outermost axes

from scanpointgenerator import LineGenerator, CompoundGenerator, \
Excluder
from scanpointgenerator.circular_roi import CircularROI

line1 = LineGenerator("x", "mm", 0.0, 2.0, 3)
line2 = LineGenerator("y", "mm", 0.0, 1.0, 2)
line3 = LineGenerator("z", "mm", 0.0, 1.0, 2)
circle = Excluder(CircularROI([1.0, 1.0], 1.0), ["x", "z"])
gen = CompoundGenerator([line3, line2, line1], [circle], [])

for point in gen.iterator():
    print(point.positions)

(Source code)

{'y': 0.0, 'x': 1.0, 'z': 0.0}
{'y': 0.0, 'x': 1.0, 'z': 1.0}
{'y': 1.0, 'x': 0.0, 'z': 0.0}
{'y': 1.0, 'x': 1.0, 'z': 0.0}
{'y': 1.0, 'x': 2.0, 'z': 0.0}
{'y': 1.0, 'x': 0.0, 'z': 1.0}
{'y': 1.0, 'x': 1.0, 'z': 1.0}
{'y': 1.0, 'x': 2.0, 'z': 1.0}

Serialisation

These generators are designed to be serialised and sent over json. The model for the CompoundGenerator is as follows:

{
    typeid: "scanpointgenerator:generator/CompoundGenerator:1.0",
    generators: [
        {
            typeid: "scanpointgenerator:generator/LineGenerator:1.0"
            name: "y"
            units: "mm"
            start: 0.0
            stop: 1.0
            num: 5
            alternate_direction = False
        },
        {
            typeid: "scanpointgenerator:generator/LineGenerator:1.0"
            name: "x"
            units: "mm"
            start: 0.0
            stop: 5.0
            num: 5
            alternate_direction = True
        }
    ],
    excluders: [
        {
            roi: {
                typeid: "scanpointgenerator:roi/CircularROI:1.0"
                centre: [0.0, 0.0]
                radius: 0.5
            }
            scannables: ["x", "y"]
        }
    ],
    mutators: [
        {
            typeid: "scanpointgenerator:mutator/RandomOffsetMutator:1.0"
            seed: 10
            axes: ["x", "y"]
            max_offset: {
                    x: 0.1
                    y: 0.2
            }
        }
    ]
}

The models for each base generator are:

ArrayGenerator (where name and points can be N-dimensional and upper_bounds and lower_bounds are optional):

{
    typeid: "scanpointgenerator:generator/ArrayGenerator:1.0"
    name: "x" or ["x", "y"]
    units: "mm"
    points: [1.0, 2.0, 3.0] or [[1.0, 2.0], [2.0, 4.0], [3.0, 6.0]]
    upper_bounds: [1.5, 2.5, 3.5]
    lower_bounds: [0.5, 1.5, 2.5]
}

LineGenerator (name, start and stop can be N-dimensional to create and ND scan):

{
    typeid: "scanpointgenerator:generator/LineGenerator:1.0"
    name: "x" or ["x", "y"]
    units: "mm"
    start: 0.0 or [0.0, 0.0]
    num: 5
    alternate_direction = True
}

LissajousGenerator (where num_points is optional):

{
    typeid: "scanpointgenerator:generator/LissajousGenerator:1.0"
    names: ["x", "y"]
    units: "mm"
    box: {
        centre: [0.0, 0.0]
        width: 10.0
        height: 10.0
    }
    num_lobes: 20
    num_points: 1000
}

SpiralGenerator (where scale is optional):

{
    typeid: "scanpointgenerator:generator/SpiralGenerator:1.0"
    names: ["x", "y"]
    units: "mm"
    centre: [0.0, 0.0]
    radius: 5.0
    scale: 2.0
    alternate_direction = True
}

And for the mutators:

RandomOffsetMutator:

{
    typeid: "scanpointgenerator:mutator/RandomOffsetMutator:1.0"
    seed: 10
    axes: ["x", "y"]
    max_offset: {
        x: 0.1
        y: 0.2
    }
}

And the excluders:

To be added...

As an example of serialising, here is a simple snake scan.

from scanpointgenerator import LineGenerator, CompoundGenerator, \
    plot_generator

x = LineGenerator("x", "mm", 0.0, 4.0, 5, alternate_direction=True)
y = LineGenerator("y", "mm", 0.0, 3.0, 4)
gen = CompoundGenerator([y, x], [], [])

plot_generator(gen)

(Source code)

It is the same after being serialised and deserialised.

from scanpointgenerator import LineGenerator, CompoundGenerator, \
    plot_generator

x = LineGenerator("x", "mm", 0.0, 4.0, 5, alternate_direction=True)
y = LineGenerator("y", "mm", 0.0, 3.0, 4)
gen = CompoundGenerator([y, x], [], [])

gen_dict = gen.to_dict()
new_gen = CompoundGenerator.from_dict(gen_dict)

plot_generator(new_gen)

(Source code)

Writing new scan point generators

Let’s walk through the simplest generator, LineGenerator, and see how it is written.

We import the baseclass Generator and the Point class that we will be generating instances of.

Our new subclass includes a docstring giving a short explanation of what it does

The initialiser stores the arguments given to it, then generates the three properties that are required by the baseclass:

  • position_units: Dict of str position_name -> str position_unit
  • index_dims: List of int dimension sizes for the dataset
  • index_names: List of str dimension names for the dataset

It is important to note that the position_units property will have the same number of elements as index_dims for grid based scans (like LineGenerator). For non grid based scans (like SpiralGenerator), index_dims will typically have less elements, because the last two or more dimensions will be unrolled into one long array. This avoids sparse datasets.

We have a repeated bit of code here, so have pulled it out into a function. It calculates the position of a point given an index

This is the entry point for external code. It is expecting us to produce a number of Point instances, one for each point in the scan. We are required to fill in the following dictionaries of str position_name -> float position:

  • positions: The capture position corresponding to the centre of the scan frame
  • lower: The lower bound of the scan frame if the scan is to be used for continuous scanning
  • upper: The upper bound of the scan frame if the scan is to be used for continuous scanning

We also fill in the list of datapoint indexes:

  • indexes: The index into the dataset that the data frame should be stored in

The yield keyword turns the python function into a generator, which can then be used by the external program to iterate through points without evaluating them all at the start.

Contributing

Contributions and issues are most welcome! All issues and pull requests are handled through github on the dls_controls repository. Also, please check for any existing issues before filing a new one. If you have a great idea but it involves big changes, please file a ticket before making a pull request! We want to make sure you don’t spend your time coding something that might not fit the scope of the project.

Running the tests

To get the source source code and run the unit tests, run:

$ git clone git://github.com/dls_controls/scanpointgenerator.git
$ cd scanpointgenerator
$ virtualenv env
$ . env/bin/activate
$ pip install nose
$ python setup.py install
$ python setup.py nosetests

While 100% code coverage does not make a library bug-free, it significantly reduces the number of easily caught bugs! Please make sure coverage is at 100% before submitting a pull request!

Code Quality

Landscape.io will test code quality when you create a pull request. Please follow PEP8.

Code Styling

Please arrange imports with the following style

# Standard library imports
import os

# Third party package imports
from mock import patch

# Local package imports
from scanpointgenerator.version import __version__

Please follow Google’s python style guide wherever possible.

Building the docs

When in the project directory:

$ pip install -r requirements/docs.txt
$ python setup.py build_sphinx
$ open docs/html/index.html

Release Checklist

Before a new release, please go through the following checklist:

  • Bump version in scanpointgenerator/version.py

  • Add a release note and diff URL in CHANGELOG.rst

  • Git tag the version

  • Upload to pypi:

    make publish
    

Change Log

All notable changes to this project will be documented in this file. This project adheres to Semantic Versioning.

Unreleased

Added:

  • Nothing yet

1-6 - 2016-10-18

Fixed:

  • CompoundGenerator to set the right number of points if excluders are used

Changed:

  • Refactored internal structure of modules

1-5 - 2016-10-07

Added:

  • Add full ROI set and FixedDurationMutator

1-4 - 2016-09-22

Added:

  • Caching of points to CompoundGenerator

1-3-1 - 2016-09-13

Added:

  • Serialisation for ROIs
  • Change type to typeid to match with Malcolm

1-3 - 2016-08-31

Added:

  • Remove OrderedDict entirely for 2.5 back-compatibility

Changed:

  • type is now typeid to make it compatible with malcolm

1-2-1 - 2016-08-17

Fixed:

  • Refactor RandomOffsetMutator to be consistent in Jython and Python without OrderedDict in Point

1-2 - 2016-08-17

Added:

  • Remove OrderedDict from Point and speed up LineGenerator

1-1 - 2016-08-16

Added:

  • Small tweaks for GDA and script to push changes to daq-eclipse on release

1-0 - 2016-07-18

Added:

  • Initial requirements for GDA and Malcolm

0-5 - 2016-06-20

Added:

  • Additions to work with GDA and Malcolm

0-4 - 2016-04-15

Added:

  • MANIFEST.in file to allow install in travis builds

0-3 - 2016-03-03

Added:

  • Documentation on writing new generators

0-2 - 2016-02-29

Added:

  • Documentation
  • Indexes to plots

0-1 - 2016-02-26

Added:

  • Initial structure with Line and Nested generators