1047 encoder: setDataRate(50) is 20% slower than requested
Posted: Thu Oct 12, 2023 9:28 pm
When running the 1047 encoder at a datarate of 125 and collecting 125 samples the actual datarate as measured by accumulating the timeChange values passed to the onPositionChange() callback and dividing by the number of samples is 125.6 -- which is very close to the requested 125.
However when requesting a datarate of 50 and collecting 50 samples the effective datarate is 42.4.
And confusingly when requesting a datarate of 25 and collecting 50 samples the effective datarate is 25.5.
Examples of two runs, first at a datarate of 125 and second at 50. I only included the first and last three entries in each runs log output.
Updated version of encoder_1047.py with additional metrics included at end.
NOTE: I did try upgrading the firmware for the encoder from 200 to 301 -- no change in the results.
datarate=125
datarate=50
updated version of encoder_1047.py
However when requesting a datarate of 50 and collecting 50 samples the effective datarate is 42.4.
And confusingly when requesting a datarate of 25 and collecting 50 samples the effective datarate is 25.5.
Examples of two runs, first at a datarate of 125 and second at 50. I only included the first and last three entries in each runs log output.
Updated version of encoder_1047.py with additional metrics included at end.
NOTE: I did try upgrading the firmware for the encoder from 200 to 301 -- no change in the results.
datarate=125
Code: Select all
% python3 encoder_1047.py --datarate=125 --samples=125 --channel=0
Press Enter to Start Test
Channel: 0 of 1047 4-channel high-speed encoder: 'PhidgetEncoder', 125 samples at 125 samples/s
sample PositionChange Position Encoder Interval System Interval
1 0 0 3.78 18.0
2 0 0 7.63 7.9
3 0 0 7.63 8.1
...
123 0 0 8.90 8.0
124 0 0 7.63 8.0
125 0 0 7.63 8.0
Channel: 0 of 1047 4-channel high-speed encoder: 'PhidgetEncoder', 125 samples at 125 samples/s
duration (system): 1.013
duration (encoder): 0.995
requested data rate: 125
effective data rate: 125.6
Code: Select all
% python3 encoder_1047.py --datarate=50 --samples=50 --channel=0
Press Enter to Start Test
Channel: 0 of 1047 4-channel high-speed encoder: 'PhidgetEncoder', 50 samples at 50 samples/s
sample PositionChange Position Encoder Interval System Interval
1 0 0 3.78 19.0
2 0 0 24.13 23.8
3 0 0 24.15 24.2
...
48 0 0 24.16 24.0
49 0 0 24.14 24.0
50 0 0 24.14 24.0
Channel: 0 of 1047 4-channel high-speed encoder: 'PhidgetEncoder', 50 samples at 50 samples/s
duration (system): 1.198
duration (encoder): 1.180
requested data rate: 50
effective data rate: 42.4
Code: Select all
from Phidget22.PhidgetException import *
from Phidget22.Phidget import *
from Phidget22.Devices.Encoder import *
import argparse
import time
import json
# connect phidget 1047 high-speed 4-channel encoder to usb on computer
# When True this program uses ENC1000_0 encoder connected to VINT
# Useful for development/testing without encoder_1047 available
use_encoder_ENC1000 = False
encoder_channel = 0
datarate = 125
max_samples = 2 * datarate
encoder_pulley_hubport = 0
#
# Parse optional command line arguments
#
parser = argparse.ArgumentParser(
description='test communication with Phidget 1047 4-channel high-speed encoder')
parser.add_argument('--enc1000', default=False, action=argparse.BooleanOptionalAction,
help=f"use ENC1000_0 encoder connected to VINT hub, default: {False}")
parser.add_argument('--channel', type=int, default=encoder_channel,
help=f"encoder channel (0-3), default: {encoder_channel}")
parser.add_argument('--datarate', type=int, default=datarate,
help=f"encoder data rate (1-125), default: {datarate}")
parser.add_argument('--samples', type=int, default=max_samples,
help=f"encoder samples to collect, default: {max_samples}")
args = parser.parse_args()
encoder_channel = args.channel
datarate = args.datarate
max_samples = args.samples
use_encoder_ENC1000 = args.enc1000
if use_encoder_ENC1000:
datarate = min(50, datarate)
else:
datarate = min(125, datarate)
encoder_counts_per_revolution = 2000
encoderPosition = 0
#
# Load saved vint and encoder_1047 serial numbers
#
# example json: phidget-serial-numbers.json
#
# {
# "vint": 538832,
# "encoder_1047": 538833
# }
#
try:
filename = "phidget-serial-numbers.json"
sample_filename = "phidget-serial-numbers-sample.json"
serial_numbers = json.load(open(filename))
vint_serial_number = serial_numbers['vint']
encoder_1047_serial_number = serial_numbers['encoder_1047']
except FileNotFoundError:
print(f"""
***
*** Required file: '{filename}' not found
*** Copy file: '{sample_filename}' to '{filename}'
***
*** cp {sample_filename} {filename}'
***
*** and update the serial numbers for the vint hub and 1047 encoder
***
""")
raise
except json.decoder.JSONDecodeError:
print(f"""
*** JSON decoding error in file: '{filename}'
""")
raise
sample_count = 0
start_time_sys = 0
end_time_sys = 0
previous_time_sys = 0
duration = 0
duration_sys = 0
log = ""
about = ""
# log formatting
col_width = 18
header_str = f"\n{'sample':<{col_width}}{'PositionChange':<{col_width}}{'Position':<{col_width}}{'Encoder Interval':<{col_width}}{'System Interval':<{col_width}}\n"
def onPositionChange(self, positionChange, timeChange, indexTriggered):
global previous_time_sys, duration
global log, sample_count, encoderPosition
sample_count += 1
if sample_count <= max_samples:
timeChange += 0.00001
interval = timeChange / 1000
duration += interval
encoderPositionChangePerSecond = 1 / interval * positionChange
rps = encoderPositionChangePerSecond / encoder_counts_per_revolution
encoderPosition += positionChange
rotation = encoderPosition / encoder_counts_per_revolution
encoderPosition = self.getPosition()
current_time_sys = time.time()
interval_sys = (current_time_sys - previous_time_sys)
interval_ms_sys = interval_sys * 1000
previous_time_sys = current_time_sys
log += f"{sample_count:<{col_width}}{positionChange:<{col_width}.0f}{encoderPosition:<{col_width}}{timeChange:<{col_width}.2f}{interval_ms_sys:<{col_width}.1f}\n"
def main():
global log, about, start_time_sys, end_time_sys, previous_time_sys, duration, duration_sys
log = header_str
about = ""
try:
input("\nPress Enter to Start Test\n")
except (Exception, KeyboardInterrupt):
pass
en0 = Encoder()
if use_encoder_ENC1000:
en0.setDeviceSerialNumber(vint_serial_number)
en0.openWaitForAttachment(5000)
encoder_classname = en0.getDeviceClassName()
en0.setHubPort(encoder_pulley_hubport)
en0.setDataRate(datarate)
about += f"Using VINT ENC1000_0 encoder: '{encoder_classname}'"
else:
en0.setDeviceSerialNumber(encoder_1047_serial_number)
en0.setChannel(encoder_channel)
en0.openWaitForAttachment(5000)
encoder_classname = en0.getDeviceClassName()
en0.setDataRate(datarate)
about += f"Channel: {encoder_channel} of 1047 4-channel high-speed encoder: '{encoder_classname}'"
about += f", {max_samples} samples at {datarate} samples/s"
print(about)
en0.setOnPositionChangeHandler(onPositionChange)
start_time_sys = time.time()
previous_time_sys = start_time_sys
duration = 0
while sample_count < max_samples:
pass
en0.close()
end_time_sys = time.time()
duration_sys = end_time_sys - start_time_sys
main()
print(f"{log}")
print(about)
effective_datarate = 1 / (duration / max_samples)
print(f"""
duration (system): {duration_sys:.3f}
duration (encoder): {duration:.3f}
requested data rate: {datarate}
effective data rate: {effective_datarate:.1f}
""")