SDK Model

The System Model

In order for the SDK to control a system, it needs to know what hardware it consists of. The SDK achieves this by constructing a system model that mirrors the hardware components. The system model is entirely modular, in the same way as the spectroradiometer system. It is the system model that coordinates the activities of the various pieces of hardware and allows almost any system, however large and complicated, to be controlled by the same set of simple, high-level functions.

The system model is built from a system configuration file. This describes the spectroradiometer in terms of what components it consists of, how the PC can communicate with them, and how they interact. System configuration files rarely need to be changed, and where possible they are pre-written and supplied with the SDK.

Example 1: Simple TM300 System

The following example shows a system configuration file for a simple TM300 based DC system:

#
# Demo TM300 system
#
USB_COMMS    comms      vid 1240, pid 5892
USB          msd        vid 1240, pid 5893
Amp487       dc_amp     address 64
ADC487       adc        address 72
FW252        fwheel     drive 2
SAM          exit_sam   drive 1
TM300        mono       use fwheel, use exit_sam

This configuration describes the following system:

  • 1 USB_COMMS - USB electronics controller at vid 1240 and pid 5892

  • 1 USB - USB mechanical controller at vid 1240 and pid 5893

  • 1 Amp487 - DC amplifier at address 64 on the I2C bus in the electronics bin

  • 1 ADC487 - ADC at address 72 on the I2C bus in the electronics bin

  • 1 252 filter wheel controlled by drive 2 of the MSD

  • 1 SAM controlled by drive 1 of the MSD

  • 1 TM300 monochromator housing the previously described SAM and filter wheel

System Configuration File Format

The format of a system configuration file is as follows:

  • One component is described per line

  • For each line:

    • The first column declares the type of the component

    • The second column gives the component an identifier

    • The third column contains any parameters (e.g. address), with multiple parameters separated by commas

  • Anything following a ‘#’ on a line is a comment and is ignored

The system configuration file is the means by which the SDK knows what hardware is present and how it is connected to the PC. It must contain one line for each PC controlled hardware module.

Building the System Model

The first task when writing a system configuration is to declare how the PC communicates with any electronics in the system. These lines defining how the PC communicates must appear in the file before any other system component so the SDK knows how to communicate with the hardware in the system.

The next step is to add a line describing each hardware module in turn. A good ‘bottom-up’ approach is to start with each electronics module (e.g. amplifiers and ADC), then each mechanical/stepper motor drive controlled unit (e.g. filter wheel, SAMs, SOBs, MVSSs), and finally the monochromator.

Component Identifiers and Parameters

The identifier can contain any alphanumeric characters and the ‘_’ (underscore) character, but the first character must be a letter or ‘_’. Each identifier must be unique and cannot be any of the system configuration keywords (i.e. a type or parameter). The identifier is case sensitive.

In systems with more than one electronics controller, the entry for each device must specify which controller the component is connected to. This is achieved by the use parameter. Inserting the identifier of a controller as the value for the use parameter instructs the system model to operate the component via that controller.

In systems with more than one stepper motor drive (SMD), the entry for each SMD controlled component must specify which SMD the component is connected to in a similar way. When there is more than one SMD in a system and the SMD is not specified for a component, the SDK will assign a default, but it may not be the correct one.

The use parameter also allows some components (specifically filter wheels, SAMs, and MVSSs) to be attached to the monochromator. The system model needs to know what components are part of the monochromator so that it can correctly coordinate all of their operations.

The use parameter is the reason for the suggested bottom-up order of adding components to the system configuration. This is because one component cannot be used by another unless it has been declared first.

Example 2: Advanced DTM300 System

A more sophisticated example shows an AC system based on a DTM300 with a filter wheel, 2 SAMs and 3 MVSSs:

#-------------------------------------------------------------------
# Example system configuration file 2
#-------------------------------------------------------------------

# Specifies CEC488 controlled system
PC488        comms        address 21

# Defines an MSD3 connected to COM1 rather than the IEEE bus
MSD3         mono_drive   port COM1

# Defines a MAC at address 20
MAC          slit_drive   address 20, cards 3

# Defines a 277 pre-amplifier at address 28
Amp277       pre_amp      address 28

# Defines a 225 lock-in amplifier at address 27
Amp225       ac_amp       address 27

# Defines a 228A ADC at address 29
ADC228A      adc          address 29

# Defines 3 MVSSs, all controlled by the same MAC
MVSS         entrance_slit   drive 1, use slit_drive
MVSS         middle_slit     drive 2, use slit_drive
MVSS         exit_slit       drive 3, use slit_drive

# Defines a filter wheel controlled by card 3 of the MSD
FW252        fwheel       card 3, use mono_drive

# Defines a SAM controlled as SAM1 on card 1 of the MSD
SAM          exit1_sam    card 11, use mono_drive

# Defines a SAM controlled as SAM2 on card 1 of the MSD
SAM          exit2_sam    card 12, use mono_drive

# Defines a DTM300 containing all of the slits, SAMs and filter wheel
# Note that long lines are OK, but cannot contain any line breaks
DTM300       mono         use fwheel, use exit1_sam, use exit2_sam, use entrance_slit, use middle_slit, use exit_slit, use mono_drive

The number of component types along with all of their different parameters may seem daunting, but once set up, the system configuration file should never need altering. As the SDK functions access components via their identifiers as defined in the configuration file, the only usual reason for looking at it is to find out what these identifiers are.

Once a system configuration file has been written, it is passed to the SDK using the build_system_model function. This instructs the SDK to compile a system model from the system configuration file. The function reports its success or failure and gives the reason for any failure to compile.

Component Groups

Simple spectroradiometer systems contain the same basic set of components: a monochromator, a stepper motor drive, an amplifier, possibly a pre-amplifier and an ADC. Measurements are made by repeatedly selecting a wavelength and taking a reading. In this situation all of the components are used together.

More sophisticated systems are possible. For example, consider a system designed to take both AC and DC measurements, possibly during the same scan. This time measurements would be made by selecting a wavelength and taking a reading using just one of the sets of detection electronics. In this situation the system model needs to know what set of components it should be using to perform the required operation.

To allow this, the system model includes the concept of component groups. A component group is a set of components that are used together to perform operations. At any given time there is one active group; this is the component group that is used for all operations, such as selecting a wavelength or taking a measurement. A component group is a sub-set of the system model.

Building and Managing Groups

New component groups are added with the build_group function. This constructs a new, empty component group and returns its sequence number. The first group is group 1, the next group created is group 2, the next group 3, and so on. There can be up to 10 component groups. When the system model is built, the SDK constructs a single empty default component group (group 1).

Existing component groups can be edited by using the group_add and group_remove functions to add and remove components to and from groups. For each function the component is referred to by its identifier as set in the system configuration file and the group by its sequence number.

The function use_group sets the active group. All of the hardware operation functions (initialise, close_shutter, park, zero_calibration, select_wavelength, autorange and measurement) work with the active group. Obviously for simple systems the active group will always be the default group (group 1).

Example 3: System with AC and DC Detection

The following example shows a system configuration for a TM300 based system with both AC and DC detection electronics:

#-------------------------------------------------
# Example system configuration
#-------------------------------------------------

PC488        comms        address 21
MAC          smd          address 30, cards 2

# Note DC and AC detection electronics
Amp267       dc_amp       address 26
Amp277       pre_amp      address 28
Amp225       ac_amp       address 25
ADC228A      adc          address 29

# SOB to select DC or AC input to ADC
SOB          adc_input    drive 11
FW252        fwheel       drive 2
TM300        mono         use fwheel

The input to the ADC (from the 267 or 225) is selected by the SOB, which is controlled by the MAC. In order to make AC and DC measurements, two groups must be created:

  1. One containing the monochromator, SMD, SOB, ADC, 277 and 225

  2. One containing the monochromator, SMD, SOB, ADC and 267

This can be achieved using the following sequence of SDK function calls:

# Build first group (AC)
i = build_group()
group_add("mono", i)
group_add("smd", i)
group_add("adc_input", i)
group_add("adc", i)
group_add("pre_amp", i)
group_add("ac_amp", i)

# Build second group (DC)
i = build_group()
group_add("mono", i)
group_add("smd", i)
group_add("adc_input", i)
group_add("adc", i)
group_add("dc_amp", i)

Assuming that when the SOB is relaxed/off the ADC is reading from the 225, the following sequence of commands is a simple example of a measurement scan using both groups:

for wl in range(start_wl, stop_wl, increment):
    # Measurement with group 1 (AC)
    use_group(1)
    set("adc_input", SOBState, 0, 0)
    select_wavelength(wl, settle_time)
    wait(settle_time)
    autorange()
    measurement(reading)

    # Measurement with group 2 (DC)
    use_group(2)
    set("adc_input", SOBState, 0, 1)
    select_wavelength(wl, settle_time)
    wait(settle_time)
    autorange()
    measurement(reading)

Important Notes on Groups

The zero_calibration function needs to access the monochromator in order to correctly measure the zero offset and dark current for each state that the system will be in over the specified wavelength range. The measurement function also needs to access the monochromator in order to retrieve the zero calibration values for the current wavelength. This means that if measurements performed using a group are to be zero calibrated, the group must include the monochromator.

While the SDK will not create default component groups, save_setup records which components are in which groups in the system attributes file and load_setup deletes any existing component groups and builds new ones from the information in the system attribute file.

System Attributes Files (.atr)

In addition to system configuration files (.cfg) that define the hardware structure, the SDK uses system attributes files (.atr) to store the current state and settings of all components in the system. These files work together with the load_setup and save_setup functions to allow complete system configurations to be saved and restored.

What are .atr Files?

System attributes files (.atr) are text-based files that store the runtime attributes of all components in the system model. Unlike .cfg files which define what hardware exists, .atr files contain the current settings, calibration values, and state information for each component.

Common uses for .atr files include:

  • Saving instrument calibration data (e.g., grating constants, wavelength calibration)

  • Storing component settings (e.g., amplifier gains, slit widths, filter positions)

  • Recording component group configurations

  • Preserving system state between measurement sessions

File Format and Syntax

System attributes files follow a structured text format:

  • Each component’s attributes are grouped in a component block

  • Component blocks start with the component identifier (as defined in the .cfg file)

  • Attributes are listed as attribute name = value pairs

  • Component blocks end with the end keyword

  • Comments start with # and continue to the end of the line

  • Values can be integers, floating-point numbers, or text strings

Example .atr File Structure

#------------------------------------------------------------------------------
# Bentham Instruments system model attributes file
# Created 14/11/2025 11:03:18
# This file should NOT be edited by hand
#------------------------------------------------------------------------------

mono
  cos alpha =  9.72700000000000E-0001
  ZordSwitch SAM for turret,1 = -1
end

monoTurret 1
  grating,1 = 1200
  minimum wavelength,1 = 200
  maximum wavelength,1 = 300
  zord,1,1 = 47663
  alpha,1,1 =  9.99600000000000E-0001
  high current threshold = 0
  move speed = 9
end

fwheel
  settle time = 500
  filter,1 =  0.00000000000000E+0000
  filter,2 =  4.00000000000000E+0002
end

~System
  dispersion multiplier = 1.500
  group = ,acamp,,,acadc,,,,,,mono,
end

Attribute Names and Values

Attribute names within a component block can contain:

  • Alphanumeric characters and spaces

  • Commas to indicate indexed attributes (e.g., grating,1, filter,2)

  • Multiple comma-separated indices for multi-dimensional attributes (e.g., zord,1,1)

Values are stored as:

  • Integer numbers (e.g., 1200, -1)

  • Floating-point numbers (e.g., 0.9727 or 9.72700000000000E-0001)

  • Text strings (typically for labels or names)

The ~System block at the end contains global system settings and component group information.

Using save_setup and load_setup

The save_setup Function

The save_setup function writes the current state of all components to a .atr file:

# Python example
hw.save_setup("my_system_state.atr")

When save_setup is called, the SDK:

  1. Queries all components in the system model for their current attribute values

  2. Writes these values to the specified .atr file in the proper format

  3. Includes component group membership information in the ~System block

  4. Adds a timestamp header to the file

The load_setup Function

The load_setup function restores system state from a .atr file:

# Python example
hw.load_setup("my_system_state.atr")

When load_setup is called, the SDK:

  1. Reads the .atr file and parses the component blocks

  2. Applies each attribute value to the corresponding component in the system model

  3. Rebuilds component groups according to the group information in the ~System block

  4. Validates that all referenced components exist in the current system model

Important Notes on .atr Files

  • .atr files are system-specific and may not work correctly if loaded on a different hardware configuration

  • The component identifiers in the .atr file must match those in the system’s .cfg file

  • load_setup will fail if the .atr file references components that don’t exist in the current system model

  • Always call build_system_model with a .cfg file before calling load_setup with a .atr file

  • Some components may have read-only attributes that are not saved/loaded

Typical Workflow

A typical workflow using configuration and attributes files:

# 1. Build the system model from the configuration file
hw.build_system_model("system.cfg")

# 2. Initialize the hardware
hw.initialise()

# 3. Load previously saved settings and calibration
hw.load_setup("calibrated_system.atr")

# 4. Perform measurements
hw.select_wavelength(500.0, 1000)
reading = hw.measurement()

# 5. Save current settings for next time
hw.save_setup("current_state.atr")