Hardware Guide¶
There are two API’s available for hardware control: high level functions that are device independent are described in this document and lower level functions that are more device specific.
If you need lower level functions that are device specific, refer to Nion Swift Instrumentation.
Instrument Control¶
You can access and control instruments, devices, and hardware sources via the instrument control API.
Getting an Instrument Object¶
To do instrument control, you will need to get a versioned instrument
object from the api
object using an
instrument_id
(see Hardware Configuration).
autostem = api.get_instrument_by_id("autostem", version="1.0")
Instrument Properties¶
Once you have an instrument
object, you can set and get properties on the instrument.
if autostem.get_property_as_bool("ShowTuningImages"):
show_data()
Properties are typed and the following types are supported:
float
int
str
bool
float_point
You can also set properties on an instrument.
superscan.set_property_as_float_point("probe_position", (0.5, 0.5))
For more information about these methods, see nion.swift.Facade.Instrument
.
Instrument Controls¶
A set of methods to access a special subset of properties called controls is also available.
Controls are special properties that are always represented as float values and may represent combinations of other controls. Their methods have special features which allow more precise setting within the network of controls.
Autostem controls are characterized as having a internal “local” value added to weighted sum of values from zero or more input controls. Changing the value of an input control can change the output value of other controls.
Setting Output Values¶
You can set values on controls in such a way as to allow changes to propogate to dependent controls or not.
To set the output value of a control, use the set_control
method with no options.
autostem.set_control_output("d3x", d3x_value)
Confirmation¶
When setting the absolute output value of a control, you can confirm the value gets set by passing an options dict with
a value_type
key of confirm
.
autostem.set_control_output("d3x", 0.0, options={'confirm': True})
You can also add options for tolerance factor when confirming. The tolerance factor default is 1.0 and should be thought of as the nominal tolerance for that control. Passing a higher tolerance factor (for example 1.5) will increase the permitted error margin and passing lower tolerance factor (for example 0.5) will decrease the permitted error margin and consequently make a timeout more likely. The tolerance factor value 0.0 is a special value which removes all checking and only waits for any change at all and then returns.
To set d3x to within 2% of its nominal target value
autostem.set_control_output("d3x", 0.0, options={'confirm': True, 'confirm_tolerance_factor': 1.02})
You can also add timeout options when confirming. The default timeout is 16 seconds.
autostem.set_control_output("d3x", 0.0, options={'confirm': True, 'confirm_timeout': 16.0})
If the timeout occurs before the value is confirmed, a TimeoutException
will be raised.
Local Values¶
You can set the local value of a control by passing an options dict with a value_type
key of local
.
autostem.set_control_output("d3x", 0.0, options={'value_type': 'local'})
Delta Values¶
You can change a control by a delta value by passing an options dict with a value_type
key of delta
.
autostem.set_control_output("d3x", d3x_delta, options={'value_type': 'delta'})
Inform, or Keeping Dependent Outputs Constant¶
Finally, you can adjust a control in such a way that the output values of dependent controls stay constant. This is
useful during setup when you want to change the displayed value without actually changing the dependent outputs. You do
this by passing an options dict with a inform
key of True. This parameter is named inform
for historical
reasons but can also be thought of as keep dependent outputs constant.
autostem.set_control_output("d3x", d3x_value, options={'inform': True})
Control State¶
Finally, you can query the state of a control to see if it exists or to see its current state. The only defined return values at the moment are None and ‘undefined’ state.
if autostem.get_control_state("dqt") is not None:
run_dqt_adjustment()
For more information about these methods, see nion.swift.Facade.Instrument
.
Data Acquisition¶
In addition to instrument control, you can also control data acquisition on hardware sources.
Introduction¶
Acquisition can be started in View or Record mode. View mode is an ongoing acquisition whereas Record mode is a single acquisition.
Acquisition is started with frame parameters that specify the readout configuration to be used. You can configure specific frame parameters or use one of the user defined profiles available in the user interface.
In View mode, you can specify the initial frame parameters, but other scripts may be able to change the frame parameters during acquisition. The acquisition API doesn’t attempt to prevent this.
In Record mode, since you are acquiring a single frame, your frame parameters are guaranteed to be used. Record mode can be used while View mode is already in progress. When the Record is finished, the View will continue.
Acquisition code in extensions should be run on threads to prevent locking the user interface. Acquisition code in scripts will always run on threads due to the nature of scripts.
Getting a Hardware Source Object¶
To do acquisition, you will need to get a versioned hardware_source
from the api
object using a
hardware_source_id
(see Hardware Configuration).
camera = api.get_hardware_source_by_id("ronchigram", version="1.0")
Frame Parameters Overview¶
You will first configure the frame_parameters
for the hardware source. There are several ways to do this.
Frame parameters are specific to the hardware source. The easiest way to get valid frame parameters is to ask the
camera
for the default frame parameters.
frame_parameters = camera.get_default_frame_parameters()
Next you can modify the frame_parameters
(this is hardware source specific).
frame_parameters["binning"] = 4
frame_parameters["exposure_ms"] = 200
You can also get the frame parameters for one of the acquisition profiles configured by the user.
frame_parameters = camera.get_frame_parameters_for_profile_by_index(1)
You can get and set the currently selected acquisition profile in the user interface using the profile_index
property. Setting the selected acquisition profile is discouraged since it can be confusing to the user if their
selected profile suddenly changes.
old_profile_index = camera.profile_index
camera.profile_index = new_profile_index
You can also update the frame parameters associated with a profile. Again, use this ability with caution since it can be confusing to the user to lose their settings.
camera.set_frame_parameters_for_profile_by_index(1, frame_parameters)
Querying Acquisition State¶
Hardware sources can be in one of several state: idle, viewing/playing, or recording.
is_playing = camera.is_playing
is_recording = camera.is_recording
Note
Recording can occur during viewing, in which case the camera can be viewing/playing and recording simultaneously.
Acquisition Frame Parameters¶
Hardware sources have frame parameters associated with both view/play and record modes. You can query and set those frame parameters using several methods. Setting the frame parameters will apply the frame parameters to soonest possible frame.
To query and set the frame parameters for view/play mode:
frame_parameters = camera.get_frame_parameters()
camera.set_frame_parameters(frame_parameters)
To query and set the frame parameters for record mode:
frame_parameters = camera.get_record_frame_parameters()
camera.set_record_frame_parameters(frame_parameters)
Controlling Acquisition State¶
You can control the acquisition view/play state using these methods:
camera.start_playing(frame_parameters, channels_enabled)
camera.stop_playing()
camera.abort_playing()
Passing frame_parameters
and channels_enabled
are optional. Passing None
will use the existing frame
parameters and enabled channels. Not all hardware sources support channels.
Stopping will finish the current frame. Aborting will immediately stop acquisition, potentially mid-frame.
You can control acquisition record state using these methods:
camera.start_recording(frame_parameters, channels_enabled)
camera.abort_recording()
Again, frame_parameters
and channels_enabled
are optional.
Recording occurs on a frame by frame basis, so there is no need to stop recording as it will always finish at the end of
a frame. Calling abort_recording
will immediately stop recording, if desired.
Recording in this way will generate a new data item.
Note
Recording can occur during view/play. How the view is stopped (stop or abort) to begin recording is specific to the camera implementation. After recording, the view will resume with current frame parameters.
Acquiring Data¶
You can acquire data during a view. Acquired data is returned as a list of DataAndMetadata
objects.
There are a few techniques to grab data:
grab_next_to_finish
is used to grab data from view/play mode when frame parameters and other state related to the hardware source is already known.
grab_next_to_start
is used to grab data from view/play mode when you need to ensure that the next frame represents data with new frame parameters or other state related to the hardware source.
record
is used to grab data in record mode.
You can pass frame parameters and enabled channels to grab_next_to_start
and record
methods. There is no need
to pass them to grab_next_to_finish
since that method will be grabbing data from acquisition that is already in
progress.
Only a single record can occur at once but there is no defined coordination technique to avoid multiple records from occuring simultaneously. If two records are requested simultaneously, the latest one will override.
All three methods will start either view/play mode or record mode if not already started.
Some example code to demonstate calling these methods.
data_and_metadata_list = camera.grab_next_to_finish(timeout)
data_and_metadata = data_and_metadata_list[0]
data_and_metadata_list = camera.grab_next_to_start(frame_parameters, channels_enabled, timeout)
data_and_metadata = data_and_metadata_list[0]
data_and_metadata_list = hardware_source_api.record(frame_parameters, channels_enabled, timeout)
data_and_metadata = data_and_metadata_list[0]
The frame_parameters
, channels_enabled
, and timeout
parameters are all optional.
For more information about these methods, see nion.swift.Facade.HardwareSource
.
Record Tasks¶
For a Record data acquisition, you can also use an acquisition task.
with contextlib.closing(hardware_source_api.create_record_task(frame_parameters)) as record_task:
do_concurrent_task()
data_and_metadata_list = record_task.grab()
The acquisition is started as soon as the Record task is created. The grab
method will wait until the
recording is done and then return.
Record tasks are useful to do concurrent work while the recording is taking place.
For more information about these methods, see nion.swift.Facade.HardwareSource
.
Hardware Configuration¶
With a hardware_source
object, you can set and get properties on the instrument.
if camera.get_property_as_bool("use_gain"):
do_gain_image_processing()
Properties are typed and the following types are supported:
float
int
str
bool
float_point
You can also set properties on a hardware source.
superscan.set_property_as_float_point("probe_position", (0.5, 0.5))
For more information about these methods, see nion.swift.Facade.Instrument
.
Hardware Source Identifiers¶
Instruments and hardware sources are identified by id’s. Id’s are divided into direct id’s and aliases. Aliases are
configurable in .ini files. For instance, the direct hardware might have a hardware_source_id
of nionccd1010
but
there might be an alias ronchigram
which points to the nionccd1010
. It is recommended to make an alias for each
application that you write, making it easy for users to configure what camera to use for your application.