Drive Data Capture
This AeroScript program shows how to configure two different drive data capture configurations on one axis, plot the data, and put the data into a text file.
// Drive Data Capture Example: // Demonstrates how to configure two different Drive Data Capture configurations on one axis, plot // the data stored in the Drive Array by Drive Data Capture, and put that data into a text file. // The two configurations use the same trigger (a PSO event), but have different inputs (primary // feedback and auxiliary feedback). Data collection is configured programmatically to collect the // applicable signals in a plot. The plot should show that the two axes are moving and the data // read back from the Drive Array is being assigned into global variables $rglobal[0] and // $rglobal[1]. // // Hardware setup: // X Axis - An XC4e connected to a motor with square-wave feedback. // Y Axis - An XC4 connected to a motor with square-wave feedback. // The auxiliary feedback pins of the two drives are connected to let the second axis echo its // encoder signals to the first. // Note: AuxiliaryFeedbackType on X should be changed to "IncrementalEncoderSquareWave". // These defines are used by this program for Drive Data Capture. The starting address of the first // array is arbitrary. The starting address of the second array is the byte address after the end // of the first array. Because the Drive Data Capture positions are 8 bytes, the size of the array // is DRIVE_DATA_CAPTURE_NUM_SAMPLES * 8 bytes. #define DRIVE_DATA_CAPTURE_NUM_SAMPLES 2000 #define DRIVE_DATA_CAPTURE_FIRST_ARRAY_START_ADDRESS 800 #define DRIVE_DATA_CAPTURE_SECOND_ARRAY_START_ADDRESS (DRIVE_DATA_CAPTURE_FIRST_ARRAY_START_ADDRESS + (DRIVE_DATA_CAPTURE_NUM_SAMPLES * 8)) // These defines are used by this program for data collection. #define DATA_COLLECTION_SAMPLE_TIME_MSEC 1 // Collect data at 1 kHz #define DATA_COLLECTION_NUM_SAMPLES 10000 // Collect up to 10 seconds worth of data // These are other defines used by this program. #define STRING_SIZE 1000 // Declare a global variable for the program to use. var $driveDataCaptureSamples[2][DRIVE_DATA_CAPTURE_NUM_SAMPLES] as real program var $axes[2] as axis = [X, Y] // Clear the controller global variables used by this program. $iglobal[0] = 0 $rglobal[0] = 0 $rglobal[1] = 0 // Enable and home the axes before the PSO is configured. Enable($axes) Home($axes) Dwell(0.5) PsoSetup($axes, 50) DataCollectionSetup($axes[0]) // The drive array might contain old data if this program runs many times without a controller // resest. To clear old data, write zeros to the drive array space allocated to Drive Data // Capture. The $driveDataCaptureSamples variable is still all zeros at this time. DriveArrayWrite($axes[0], $driveDataCaptureSamples[0], DRIVE_DATA_CAPTURE_FIRST_ARRAY_START_ADDRESS, DRIVE_DATA_CAPTURE_NUM_SAMPLES, DriveArrayType.DataCapturePositions) DriveArrayWrite($axes[0], $driveDataCaptureSamples[1], DRIVE_DATA_CAPTURE_SECOND_ARRAY_START_ADDRESS, DRIVE_DATA_CAPTURE_NUM_SAMPLES, DriveArrayType.DataCapturePositions) // Configure Drive Data Capture to use the PSO event as the trigger. To make sure each captured // value is the feedback value from precisely when the PSO event occurred, use the same PSO // event to trigger the two captures. Because the second axis does not have access to the PSO // event on the first axis, the program must use two configurations on one axis. It cannot use // one configuration on each of the two axes. DriveDataCaptureReset($axes[0], 0) DriveDataCaptureConfigureTrigger($axes[0], 0, DriveDataCaptureTrigger.PsoEvent) DriveDataCaptureConfigureInput($axes[0], 0, DriveDataCaptureInput.PrimaryFeedback) DriveDataCaptureConfigureArray($axes[0], 0, DRIVE_DATA_CAPTURE_FIRST_ARRAY_START_ADDRESS, DRIVE_DATA_CAPTURE_NUM_SAMPLES) DriveDataCaptureOn($axes[0], 0) DriveDataCaptureReset($axes[0], 1) DriveDataCaptureConfigureTrigger($axes[0], 1, DriveDataCaptureTrigger.PsoEvent) DriveDataCaptureConfigureInput($axes[0], 1, DriveDataCaptureInput.AuxiliaryFeedback) DriveDataCaptureConfigureArray($axes[0], 1, DRIVE_DATA_CAPTURE_SECOND_ARRAY_START_ADDRESS, DRIVE_DATA_CAPTURE_NUM_SAMPLES) DriveDataCaptureOn($axes[0], 1) AppDataCollectionSnapshot() // Move the axes forward. Expect a PSO Distance event every 50 counts. MoveLinear($axes, [CountsToUnits($axes[0], 49500), CountsToUnits($axes[1], 49500)], CountsToUnits($axes[0], 50000)) // Process the data in the drive array from Drive Data Capture. Use global variables to plot the // data. Put the data in a text file. DriveDataCaptureProcessing($axes[0]) Cleanup($axes) end // Configure a set of data collection signals. function DataCollectionSetup($axis as axis) AppDataCollectionReset() AppDataCollectionConfigure(DATA_COLLECTION_NUM_SAMPLES, DATA_COLLECTION_SAMPLE_TIME_MSEC) // Collect the primary and auxiliary feedback to make sure that the first axis is tracking the // two axes correctly. AuxiliaryFeedbackType on X must be set to "IncrementalEncoderSquareWave" // for the feedback signals to be plotted correctly. AppDataCollectionAddAxisSignal($axis, AxisDataSignal.PositionFeedback) AppDataCollectionAddAxisSignal($axis, AxisDataSignal.AuxiliaryFeedback) // Collect the PSO counters because there is a PSO event when they reach the specified distance. AppDataCollectionAddAxisSignal($axis, AxisDataSignal.PsoCounter0) AppDataCollectionAddAxisSignal($axis, AxisDataSignal.PsoCounter1) // Collect the number of Drive Data Capture Samples to make sure that each configuration // captures the feedback value when a PSO event occurs. AppDataCollectionAddAxisSignal($axis, AxisDataSignal.DriveDataCaptureSamples, 0) AppDataCollectionAddAxisSignal($axis, AxisDataSignal.DriveDataCaptureSamples, 1) // Collect these global variables because the end of the program copies the captured values // into the global reals and the sample number into the global integer. AppDataCollectionAddSystemSignal(SystemDataSignal.GlobalInteger, 0) AppDataCollectionAddSystemSignal(SystemDataSignal.GlobalReal, 0) AppDataCollectionAddSystemSignal(SystemDataSignal.GlobalReal, 1) end // Configure PSO on the first axis to track feedback from each axis. Primary feedback from the // second axis is being echoed out of the auxiliary encoder of the second axis into the auxiliary // encoder of the first axis. This is to let the first axis to track the two axes. function PsoSetup($axes[2] as axis, $psoEventDistance as integer) // Configure the second axis to echo its primary encoder signals out of the auxiliary encoder. DriveEncoderOutputOff($axes[1], EncoderOutputChannel.AuxiliaryEncoder) DriveEncoderOutputConfigureInput($axes[1], EncoderOutputChannel.AuxiliaryEncoder, EncoderInputChannel.PrimaryEncoder) DriveEncoderOutputOn($axes[1], EncoderOutputChannel.AuxiliaryEncoder) // Reset PSO before it is configured. PsoReset($axes[0]) // Configure the PSO distance module to track the two axes. Primary feedback from the second // axis is being echoed out of the auxiliary encoder of the second axis into the auxiliary // encoder of the first axis. PsoDistanceConfigureInputs($axes[0], [PsoDistanceInput.XC4ePrimaryFeedback, PsoDistanceInput.XC4eAuxiliaryFeedback]) PsoDistanceConfigureFixedDistance($axes[0], $psoEventDistance) PsoDistanceCounterOn($axes[0]) PsoDistanceEventsOn($axes[0]) // Configure the PSO waveform module to produce one 10 us pulse each time a PSO event occurs. PsoWaveformConfigurePulseFixedTotalTime($axes[0], 10) PsoWaveformConfigurePulseFixedOnTime($axes[0], 10) PsoWaveformConfigurePulseFixedCount($axes[0], 1) PsoWaveformApplyPulseConfiguration($axes[0]) PsoWaveformOn($axes[0]) // Configure the PSO output to output the waveform onto the auxiliary marker. PsoOutputConfigureSource($axes[0], PsoOutputSource.Waveform) PsoOutputConfigureOutput($axes[0], PsoOutputPin.XC4eAuxiliaryMarkerSingleEnded) end // Retrieve and process the valid Drive Data Capture samples from the drive array. The move was // short enough that fewer than DRIVE_DATA_CAPTURE_NUM_SAMPLES values were captured and not all the // memory locations in the drive array space allocated to each data capture configuration were // used. Thus, this function should read only the memory locations in the drive array that have // actual (valid) feedback values. function DriveDataCaptureProcessing($axis as axis) var $driveDataCaptureNumberOfSamplesTaken[2] as integer var $loopIndex as integer // Read only the valid Drive Data Capture samples from the drive array. $driveDataCaptureNumberOfSamplesTaken[0] = DriveGetItem($axis, DriveItem.DriveDataCaptureSamples, 0) $driveDataCaptureNumberOfSamplesTaken[1] = DriveGetItem($axis, DriveItem.DriveDataCaptureSamples, 1) DriveArrayRead($axis, $driveDataCaptureSamples[0], DRIVE_DATA_CAPTURE_FIRST_ARRAY_START_ADDRESS, $driveDataCaptureNumberOfSamplesTaken[0], DriveArrayType.DataCapturePositions) DriveArrayRead($axis, $driveDataCaptureSamples[1], DRIVE_DATA_CAPTURE_SECOND_ARRAY_START_ADDRESS, $driveDataCaptureNumberOfSamplesTaken[1], DriveArrayType.DataCapturePositions) // This loop is in a critical section to make sure all the global variables are assigned their // values at the same time. CriticalSectionStart() // Loop over the data capture samples, assigning them into global variables that are being // plotted. This is for convenience because program variables cannot be plotted. for $loopIndex = 0 to (DRIVE_DATA_CAPTURE_NUM_SAMPLES - 1) // Set $iglobal[0] to the sample number. $iglobal[0] = $loopIndex // Set $rglobal[0] to the value of the sample from the first Drive Data Capture configuration. $rglobal[0] = $driveDataCaptureSamples[0][$loopIndex] // Set $rglobal[1] to the value of the sample from the second Drive Data Capture configuration. $rglobal[1] = $driveDataCaptureSamples[1][$loopIndex] Dwell(0.001) end CriticalSectionEnd() WriteSamplesToFile() end // Write the samples to a text file quickly and efficiently. To decrease file operation time and // reduce the number of calls to FileTextWriteString(), this function uses a local string variable // as a buffer to hold a large amount of text before the function writes it to the file. function WriteSamplesToFile() var $loopIndex as integer var $fileHandle as handle var $buffer as string(STRING_SIZE) // Open the file and write the header to it. $fileHandle = FileOpenText("/DataCaptureSamples.csv", FileMode.Overwrite) FileTextWriteString($fileHandle, "SampleNumber,FirstAxisFeedback,SecondAxisFeedback\n") // This loop is in a critical section to reduce the amount of time it takes to execute. CriticalSectionStart() // Loop over the data capture samples and write them to the output file. for $loopIndex = 0 to (DRIVE_DATA_CAPTURE_NUM_SAMPLES - 1) $buffer = $buffer + IntegerToString($loopIndex) + "," + IntegerToString($driveDataCaptureSamples[0][$loopIndex]) + "," + IntegerToString($driveDataCaptureSamples[1][$loopIndex]) + "\n" // Write to the file when the string buffer is at 95% of its maximum size or the loop index // is a multiple of 50. This prevents the critical section from causing the application to // stop responding. if ((StringLength($buffer) > (STRING_SIZE * 0.95)) || (($loopIndex % 50) == 0)) FileTextWriteString($fileHandle, $buffer) $buffer = "" end end // Write the remaining part of the string buffer to the file. FileTextWriteString($fileHandle, $buffer) CriticalSectionEnd() FileClose($fileHandle) end // Disable the axes and PSO. function Cleanup($axes[2] as axis) // Wait for data collection to finish before the program continues. wait (StatusGetSystemItem(SystemStatusItem.DataCollectionStatus) & DataCollectionFlags.Done) Disable($axes) PsoOutputOff($axes[0]) end
DriveDataCaptureConfigureTrigger()
DriveDataCaptureConfigureInput()
DriveDataCaptureConfigureArray()
AppDataCollectionAddAxisSignal()
AppDataCollectionAddSystemSignal()
DriveEncoderOutputConfigureInput()
PsoDistanceConfigureFixedDistance()
PsoWaveformConfigurePulseFixedTotalTime()
PsoWaveformConfigurePulseFixedOnTime()
PsoWaveformConfigurePulseFixedCount()