Data recorder GCS 3.0

A PI device has one or more data recorder(s) with one ore more trace(s) that can be filled with data (i.e. values). The typical workflow is as follows.

  • Set the record rate.
  • Configure the data to be recorded.
  • Configure the trigger event that starts the recording.
  • Perform the action that should be recorded.
  • Wait until the action is finished.
  • Wait until the data has been recorded.
  • Start reading out the data from the device.
  • Wait until all data has been read out from the device.
  • Process the data.

Please find an according sample in samples/datarecorder_gcs21.py.

Preliminary

Call qREC_STATE to get the current state of a data recorder. Possible states:

  • Configuration (CFG): Configuration of the data recorder is only possible in this state. Call REC_STOP to set the data recorder to the configuration state.
  • Waiting (WAIT): In this state the data recorder waits for the occurrence
    of a trigger which starts the recording. Call REC_START to set the data recorder from the configuration state to the waiting state.
  • Running (RUN): In this state the data recorder is recording signals according to the configuration settings.

PIPython supports data recorder tools (pipython.datarectools) which simplify the work with the data recorder. The following description assumes that these data recorder tools will be used.

Prepare the data recorder

Set the record rate

With the property record_rate you can configure the record rate in multiples of the device-specific servo cycle time. The higher the record rate, the slower the data is recorded.

from pipython import GCSDevice
from pipython import datarectools, pitools
pidevice = GCSDevice()
pidevice.InterfaceSetupDlg()
...
drec = datarectools.Datarecorder(pidevice)
drec.record_rate = 1  # servo cycles

Configure data recorder

With the traces property you can configure which signal is recorded in a specified trace.

In the following sample the recording of signals 0x102 (the target position) and 0x103 (the current position) of AXIS_1 is configured for traces 1 and 2.

from pipython import GCSDevice
from pipython import datarectools, pitools
pidevice = GCSDevice()
pidevice.InterfaceSetupDlg()
...
drec = datarectools.Datarecorder(pidevice)
...
#             {<trace_id>: [<container unit>, <function_unit>, <parameter>], }
drec.traces = { 1: [ 'AXIS_1', '-', '0x102' ], 2: [ 'AXIS_1', '-', '0x103' ]}

Configure the trigger event

With the trigger property you can configure the start of the recording, e.g. immediately or with the next command that changes a position, i.e. that causes a motion.

In the following sample the recording ist triggered by the next MOV command on AXIS_1

from pipython import GCSDevice
from pipython import datarectools, pitools
pidevice = GCSDevice()
pidevice.InterfaceSetupDlg()
...
drec = datarectools.Datarecorder(pidevice)
...
#              [<triggge_name>, <option_1>, <optino_2>]
drec.trigger = [ 'MOV', 'AXIS_1', '0' ] # Trigger on next MOV command of AXIS_1

Start the data recorder

Call the arm() method to start the data recorder. This includes the following steps:

  • Checks if the data recorder is in configuration state CFG (by querying qREC_STATE)
  • If it is not in configuration state, REC_STOP is sent to set it to the configuration state
  • Sends the record rate configuration to the device using REC_RATE
  • Sends the trace configuration to the device using REC_TRACE
  • Sends the trigger configuration to the device using REC_TRG
  • Calls REC_START to set the data recorder to the waiting state WAIT. In this state it waits until the occurrence of a trigger. After receiving the trigger, the data recorder changes to the running state RUN and starts recording.
from pipython import GCSDevice
from pipython import datarectools, pitools
pidevice = GCSDevice()
pidevice.InterfaceSetupDlg()
...
drec = datarectools.Datarecorder(pidevice)
...
drec.arm()

Get the data

Wait for the motion to finish

After the trigger occurrence (for example, start of a motion) you can wait until the motion has finished with the "wait" helper functions in pipython.pitools.

pidevice.MOV('AXIS_1', 1.0)
pitools.waitontarget(pidevice, 'AXIS_1')

Read the data using the data recorder tools

The first time you read the header or data property, qREC_DAT is sent to the device, which starts the reading of the data. By default this reads the maximum possible number of data values starting with the first value. To limit the number of data values to read, you can set the number_of_values property. You can also set the offset property to start the reading with a specific offset in the trace.

In the following sample 1000 data values are read, starting with the 100th value in the trace. This will read the values 100 to 1100 from the trace.

from pipython import GCSDevice
from pipython import datarectools, pitools
pidevice = GCSDevice()
pidevice.InterfaceSetupDlg()
...
drec = datarectools.Datarecorder(pidevice)
...
drec.number_of_values = 1000 # read 1000 values
drec.offset = 100 # start reading with offset 100 
data = drec.data # start reading the values and wait until 1000 values have been read

Process data

The sample below shows how to create a plot using the header and the data from a recording that comprises two traces. (This requires matplotlib.)

    pyplot.plot(drec.timescale, drec.data[0], color='red')
    pyplot.plot(drec.timescale, drec.data[1], color='blue')
    pyplot.xlabel('time (s)')
    pyplot.ylabel(', '.join((drec.header['NAME0'], drec.header['NAME1'])))
    pyplot.title('Datarecorder data over time')
    pyplot.grid(True)
    pyplot.show()
    print('save GCSArray to file "gcsarray.dat"')
    pitools.savegcsarray('gcsarray.dat', drec.header, drec.data)

If you are used to NumPy you can easily convert the recorded data into a NumPy array.

import numpy as np
...
header, data = drec.getdata()
npdata = np.array(data)

Get the recorded data without the data recorder tools

Read out the recorded data from the device with qREC_DAT. This returns immediately with the GCS header containing information on the recorded data. Then it starts a background task that keeps on storing the data still coming from the device in an internal buffer. Check the current state of this buffer with the bufstate property. It will turn True when the task has finished. Prior to that it is a float value in the range 0..1 indicating the progress of the data transfer. Hence end a loop with while bufstate is not True and not with while not bufstate.

header = pidevice.qREC_DAT('REC_1', 'ASCII', [1, 2], 100, 1000)
while pidevice.bufstate is not True:
    print('read data {:.1f}%...'.format(pidevice.bufstate * 100))
    sleep(0.1)

Remember that the task running in the background will lock the communication to the device. Hence your application indeed is able to continue after calling qREC_DAT, but when you try to communicate to the device during data readout this will result in a deadlock! To prevent this always check the GCSDevice.locked property.

Examples

The following sample will configure two traces for the same axis, set the MOV command trigger, and arm the data recorder.

from pipython import GCSDevice
from pipython import datarectools, pitools
pidevice = GCSDevice()
pidevice.InterfaceSetupDlg()
...
drec = datarectools.Datarecorder(pidevice)

# Configure the data recorder
drec.record_rate = 1
drec.traces = { 1: [ 'AXIS_1', '-', '0x102' ], 2: [ 'AXIS_1', '-', '0x103' ]}
drec.trigger = [ 'MOV', 'AXIS_1', '0' ] # Trigger on next MOV command on AXIS_1
drec.arm()

# Trigger the recording and wait until the motion has finished
pidevice.MOV('AXIS_1', 1.0)
pitools.waitontarget(pidevice, 'AXIS_1')

# Read the recorded values and the header
data = drec.data
header = drec.header

The following sample will configure two traces for different axes, set the MOV command trigger on AXIS_1, and arm the data recorder.

from pipython import GCSDevice
from pipython import datarectools, pitools
pidevice = GCSDevice()
pidevice.InterfaceSetupDlg()
...
drec = datarectools.Datarecorder(pidevice)

# Configure the data recorder
drec.record_rate = 1
drec.traces = { 1: [ 'AXIS_1', '-', '0x102' ], 2: [ 'AXIS_2', '-', '0x103' ]}
drec.trigger = [ 'MOV', 'AXIS_1', '0' ]
drec.arm()

# Trigger the recording and wait until the motion has finished
pidevice.MOV('AXIS_1', 1.0)
pitools.waitontarget(pidevice, 'AXIS_1')

# Read the recorded values and the header
data = drec.data
header = drec.header

The following sample will configure four traces for two analog inputs and two axes. Furthermore, it will set the 'NOW' trigger which triggers immediately after starting the data recorder.

from pipython import GCSDevice
from pipython import datarectools, pitools
pidevice = GCSDevice()
pidevice.InterfaceSetupDlg()
...
drec = datarectools.Datarecorder(pidevice)
drec.record_rate = 1
drec.traces = { 1: [ 'CON_1', 'SENS_1', '0x1021 ], 2: [ 'CON_2', 'SENS_1', '0x101' ]
                3: [ 'AXIS_1', '-', '0x102' ], 4: [ 'AXIS_2', '-', '0x103' ]}
drec.trigger = [ 'NOW', '0', '0' ]
drec.arm()

# No specific trigger is required, because the data recorder triggers itself
# automatically after `REC_START`, which ist called within the `arm()` method.

# Read the recorded values and the header
data = drec.data
header = drec.header