From e5cd33495f48c42c4548c68d5dd4b3ff395a478b Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 17 Apr 2020 20:28:27 +0200 Subject: [PATCH 01/13] upgrade test runner to use more yaml and less hardcoding --- tools/odrive/tests/calibration_test.py | 116 ++- tools/odrive/tests/can_test.py | 14 +- tools/odrive/tests/enc0_sim_-4096cps.ino.hex | 795 ----------------- tools/odrive/tests/enc1_sim_-4096cps.ino.hex | 795 ----------------- .../odrive/tests/encoder_pass_through.ino.hex | 792 ----------------- tools/odrive/tests/encoder_test.py | 68 +- tools/odrive/tests/nvm_test.py | 19 +- tools/odrive/tests/pwm_input_test.py | 77 +- tools/odrive/tests/pwm_sim.ino.hex | 801 ------------------ tools/odrive/tests/test_runner.py | 550 +++++++++--- tools/odrive/tests/uart_ascii_test.py | 182 ++-- tools/odrive/tests/uart_pass_through.ino.hex | 786 ----------------- tools/test-rig-rpi.yaml | 49 +- 13 files changed, 785 insertions(+), 4259 deletions(-) delete mode 100644 tools/odrive/tests/enc0_sim_-4096cps.ino.hex delete mode 100644 tools/odrive/tests/enc1_sim_-4096cps.ino.hex delete mode 100644 tools/odrive/tests/encoder_pass_through.ino.hex delete mode 100644 tools/odrive/tests/pwm_sim.ino.hex delete mode 100644 tools/odrive/tests/uart_pass_through.ino.hex diff --git a/tools/odrive/tests/calibration_test.py b/tools/odrive/tests/calibration_test.py index 5316f8d4..595b3b92 100644 --- a/tools/odrive/tests/calibration_test.py +++ b/tools/odrive/tests/calibration_test.py @@ -6,7 +6,7 @@ from math import pi import os from fibre.utils import Logger -from test_runner import AxisTestContext, MotorTestContext, EncoderTestContext, test_assert_eq, test_assert_no_error, request_state, program_teensy +from test_runner import * from odrive.enums import * def modpm(val, range): @@ -19,14 +19,19 @@ class TestMotorCalibration(): and checks if the measurements match the expectation. """ - def is_compatible(self, axis_ctx: AxisTestContext, motor_ctx: MotorTestContext): - return axis_ctx.yaml == motor_ctx.yaml['name'] # check if connected + def get_test_cases(self, testrig: TestRig): + """Returns all axes that are connected to a motor, along with the corresponding motor(s)""" + for odrive in testrig.get_components(ODriveComponent): + for axis in odrive.axes: + for motor in testrig.get_connected_components(axis, MotorComponent): + yield (axis, motor) - def run_test(self, axis_ctx: AxisTestContext, motor_ctx: MotorTestContext, logger: Logger): + def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, logger: Logger): # reset old calibration values axis_ctx.handle.motor.config.phase_resistance = 0.0 axis_ctx.handle.motor.config.phase_inductance = 0.0 axis_ctx.handle.motor.config.pre_calibrated = False + axis_ctx.handle.config.enable_watchdog = False axis_ctx.handle.clear_errors() @@ -47,10 +52,14 @@ class TestDisconnectedMotorCalibration(): Tests if the motor calibration fails as expected if the phases are floating. """ - def is_compatible(self, axis_ctx: AxisTestContext): - return axis_ctx.yaml == 'floating' + def get_test_cases(self, testrig: TestRig): + """Returns all axes that are disconnected""" + for odrive in testrig.get_components(ODriveComponent): + for axis in odrive.axes: + if axis.yaml == 'floating': + yield (axis,) - def run_test(self, axis_ctx: AxisTestContext, logger: Logger): + def run_test(self, axis_ctx: ODriveAxisComponent, logger: Logger): axis = axis_ctx.handle # reset old calibration values @@ -73,14 +82,21 @@ class TestEncoderDirFind(): Runs the encoder index search. """ - def is_compatible(self, axis_ctx: AxisTestContext, motor_ctx: MotorTestContext, enc_ctx: EncoderTestContext): - return (axis_ctx.yaml == motor_ctx.yaml['name']) and (axis_ctx.num == enc_ctx.num) # check if connected + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + for num in range(2): + encoders = testrig.get_connected_components({ + 'a': (odrive.encoders[num].a, False), + 'b': (odrive.encoders[num].b, False) + }, EncoderComponent) + motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) - def run_test(self, axis_ctx: AxisTestContext, motor_ctx: MotorTestContext, enc_ctx: EncoderTestContext, logger: Logger): + for motor, encoder in itertools.product(motors, encoders): + if encoder.impl in testrig.get_connected_components(motor): + yield (odrive.axes[num], motor, encoder) + + def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): axis = axis_ctx.handle - # TODO: read teensy config from YAML file - hexfile = 'encoder_pass_through.ino.hex' - program_teensy(os.path.join(os.path.dirname(__file__), hexfile), 26, logger) time.sleep(1.0) # wait for PLLs to stabilize # Set motor calibration values @@ -110,14 +126,21 @@ class TestEncoderOffsetCalibration(): Runs the encoder index search. """ - def is_compatible(self, axis_ctx: AxisTestContext, motor_ctx: MotorTestContext, enc_ctx: EncoderTestContext): - return (axis_ctx.yaml == motor_ctx.yaml['name']) and (axis_ctx.num == enc_ctx.num) # check if connected + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + for num in range(2): + encoders = testrig.get_connected_components({ + 'a': (odrive.encoders[num].a, False), + 'b': (odrive.encoders[num].b, False) + }, EncoderComponent) + motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) - def run_test(self, axis_ctx: AxisTestContext, motor_ctx: MotorTestContext, enc_ctx: EncoderTestContext, logger: Logger): + for motor, encoder in itertools.product(motors, encoders): + if encoder.impl in testrig.get_connected_components(motor): + yield (odrive.axes[num], motor, encoder) + + def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): axis = axis_ctx.handle - # TODO: read teensy config from YAML file - hexfile = 'encoder_pass_through.ino.hex' - program_teensy(os.path.join(os.path.dirname(__file__), hexfile), 26, logger) time.sleep(1.0) # wait for PLLs to stabilize # Set motor calibration values @@ -127,9 +150,9 @@ class TestEncoderOffsetCalibration(): # Set calibration settings axis_ctx.handle.motor.config.direction = 0 - enc_ctx.handle.config.use_index = False - enc_ctx.handle.config.calib_scan_omega = 12.566 # 2 electrical revolutions per second - enc_ctx.handle.config.calib_scan_distance = 50.265 # 8 revolutions + axis_ctx.handle.encoder.config.use_index = False + axis_ctx.handle.encoder.config.calib_scan_omega = 12.566 # 2 electrical revolutions per second + axis_ctx.handle.encoder.config.calib_scan_distance = 50.265 # 8 revolutions axis_ctx.handle.clear_errors() @@ -141,7 +164,7 @@ class TestEncoderOffsetCalibration(): test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) test_assert_no_error(axis_ctx) - test_assert_eq(enc_ctx.handle.is_ready, True) + test_assert_eq(axis_ctx.handle.encoder.is_ready, True) test_assert_eq(axis_ctx.handle.motor.config.direction in [-1, 1], True) @@ -152,14 +175,27 @@ class TestEncoderIndexSearch(): host's GPIO. """ - def is_compatible(self, axis_ctx: AxisTestContext, motor_ctx: MotorTestContext, enc_ctx: EncoderTestContext): - return (axis_ctx.yaml == motor_ctx.yaml['name']) and (axis_ctx.num == enc_ctx.num) # check if connected + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + for num in range(2): + encoders = testrig.get_connected_components({ + 'a': (odrive.encoders[num].a, False), + 'b': (odrive.encoders[num].b, False) + }, EncoderComponent) + motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) + z_gpio = list(testrig.get_connected_components((odrive.encoders[num].z, False), LinuxGpioComponent)) - def run_test(self, axis_ctx: AxisTestContext, motor_ctx: MotorTestContext, enc_ctx: EncoderTestContext, logger: Logger): + for motor, encoder in itertools.product(motors, encoders): + if encoder.impl in testrig.get_connected_components(motor): + yield (odrive.axes[num], motor, encoder, z_gpio) + + def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, z_gpio: LinuxGpioComponent, logger: Logger): axis = axis_ctx.handle - # TODO: read teensy config from YAML file - hexfile = 'encoder_pass_through.ino.hex' - program_teensy(os.path.join(os.path.dirname(__file__), hexfile), 26, logger) + cpr = int(enc_ctx.yaml['cpr']) + + z_gpio.config(output=True) + z_gpio.write(False) + time.sleep(1.0) # wait for PLLs to stabilize # Set motor calibration values @@ -177,24 +213,20 @@ class TestEncoderIndexSearch(): time.sleep(3) - test_assert_eq(enc_ctx.handle.index_found, False) - with open("/sys/class/gpio/gpio{}/direction".format(20), "w") as fp: - fp.write("out") - with open("/sys/class/gpio/gpio{}/value".format(20), "w") as gpio: - gpio.write("0") + test_assert_eq(axis_ctx.handle.encoder.index_found, False) time.sleep(0.1) - with open("/sys/class/gpio/gpio{}/value".format(20), "w") as gpio: - gpio.write("1") - test_assert_eq(enc_ctx.handle.index_found, True) + z_gpio.write(True) + test_assert_eq(axis_ctx.handle.encoder.index_found, True) + z_gpio.write(False) test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) test_assert_no_error(axis_ctx) - test_assert_eq(enc_ctx.handle.shadow_count, 0.0, range=20) - test_assert_eq(enc_ctx.handle.count_in_cpr, 0.0, range=20) - test_assert_eq(enc_ctx.handle.pos_estimate, 0.0, range=20) - test_assert_eq(enc_ctx.handle.pos_cpr, 0.0, range=20) - test_assert_eq(enc_ctx.handle.pos_abs, 0.0, range=20) + test_assert_eq(axis_ctx.handle.encoder.shadow_count, 0.0, range=20) + test_assert_eq(modpm(axis_ctx.handle.encoder.count_in_cpr, cpr), 0.0, range=20) + test_assert_eq(axis_ctx.handle.encoder.pos_estimate, 0.0, range=20) + test_assert_eq(axis_ctx.handle.encoder.pos_cpr, 0.0, range=20) + test_assert_eq(axis_ctx.handle.encoder.pos_abs, 0.0, range=20) if __name__ == '__main__': diff --git a/tools/odrive/tests/can_test.py b/tools/odrive/tests/can_test.py index 344d94fe..7db9c5a6 100644 --- a/tools/odrive/tests/can_test.py +++ b/tools/odrive/tests/can_test.py @@ -7,8 +7,8 @@ import asyncio import time from fibre.utils import Logger -from odrive.enums import errors -from test_runner import CANTestContext, ODriveTestContext, test_assert_eq +from odrive.enums import * +from test_runner import * # Each argument is described as tuple (name, format, scale). # Struct format codes: https://docs.python.org/2/library/struct.html @@ -81,10 +81,12 @@ async def request(bus, node_id, cmd_name, timeout = 1.0): class TestSimpleCAN(): - def is_compatible(self, canbus: CANTestContext, odrive: ODriveTestContext): - return canbus.yaml['bus'] == odrive.yaml['can'] # check if connected + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + can_interfaces = testrig.get_connected_components(odrive.can, CanInterfaceComponent) + yield (odrive, list(can_interfaces)) - def run_test(self, canbus: CANTestContext, odrive: ODriveTestContext, logger: Logger): + def run_test(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, logger: Logger): node_id = 0 axis = odrive.handle.axis0 axis.config.can_node_id = node_id @@ -139,11 +141,13 @@ class TestSimpleCAN(): test_assert_eq(axis.controller.input_vel, 2.0, range=0.01) test_assert_eq(axis.controller.input_current, 3.0, range=0.001) + axis.controller.config.control_mode = CTRL_MODE_VELOCITY_CONTROL my_cmd('set_input_vel', input_vel=-10.0, cur_ff=30.1234) fence() test_assert_eq(axis.controller.input_vel, -10.0, range=0.01) test_assert_eq(axis.controller.input_current, 30.1234, range=0.01) + axis.controller.config.control_mode = CTRL_MODE_CURRENT_CONTROL my_cmd('set_input_current', input_current=3.1415) fence() test_assert_eq(axis.controller.input_current, 3.1415, range=0.01) diff --git a/tools/odrive/tests/enc0_sim_-4096cps.ino.hex b/tools/odrive/tests/enc0_sim_-4096cps.ino.hex deleted file mode 100644 index 78b4d734..00000000 --- a/tools/odrive/tests/enc0_sim_-4096cps.ino.hex +++ /dev/null @@ -1,795 +0,0 @@ -:0200000460009A -:100000004643464200000156000000000101020084 -:1000100000000000000000000000000000000000E0 -:1000200000000000000000000000000000000000D0 -:1000300000000000000000000000000000000000C0 -:1000400000000000010403000000000000000000A8 -:100050000000200000000000000000000000000080 -:100060000000000000000000000000000000000090 -:100070000000000000000000000000000000000080 -:10008000EB04180A063204260000000000000000FD -:10009000050404240000000000000000000000002F -:1000A0000000000000000000000000000000000050 -:1000B0000604000000000000000000000000000036 -:1000C0000000000000000000000000000000000030 -:1000D00020041808000000000000000000000000DC -:1000E0000000000000000000000000000000000010 -:1000F0000000000000000000000000000000000000 -:10010000D8041808000000000000000000000000F3 -:100110000204180804200000000000000000000095 -:1001200000000000000000000000000000000000CF -:10013000600400000000000000000000000000005B -:1001400000000000000000000000000000000000AF -:10015000000000000000000000000000000000009F -:10016000000000000000000000000000000000008F -:10017000000000000000000000000000000000007F -:10018000000000000000000000000000000000006F -:10019000000000000000000000000000000000005F -:1001A000000000000000000000000000000000004F -:1001B000000000000000000000000000000000003F -:1001C000000100000010000001000000000000001D -:1001D000000001000000000000000000000000001E -:1001E000000000000000000000000000000000000F -:1001F00000000000000000000000000000000000FF -:10020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -:10021000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE -:10022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE -:10023000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE -:10024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE -:10025000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE -:10026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E -:10027000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E -:10028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E -:10029000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E -:1002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E -:1002B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E -:1002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E -:1002D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E -:1002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E -:1002F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E -:10030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD -:10031000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED -:10032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD -:10033000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD -:10034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD -:10035000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD -:10036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D -:10037000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D -:10038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D -:10039000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D -:1003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D -:1003B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D -:1003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D -:1003D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D -:1003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D -:1003F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D -:10040000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC -:10041000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC -:10042000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC -:10043000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC -:10044000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC -:10045000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC -:10046000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C -:10047000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C -:10048000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C -:10049000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C -:1004A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C -:1004B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C -:1004C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C -:1004D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C -:1004E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C -:1004F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C -:10050000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB -:10051000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB -:10052000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB -:10053000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB -:10054000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB -:10055000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB -:10056000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B -:10057000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B -:10058000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B -:10059000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B -:1005A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B -:1005B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B -:1005C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B -:1005D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B -:1005E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B -:1005F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B -:10060000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA -:10061000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA -:10062000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA -:10063000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA -:10064000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA -:10065000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA -:10066000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A -:10067000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A -:10068000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A -:10069000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A -:1006A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A -:1006B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A -:1006C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A -:1006D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A -:1006E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A -:1006F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A -:10070000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 -:10071000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9 -:10072000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9 -:10073000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9 -:10074000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9 -:10075000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9 -:10076000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99 -:10077000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89 -:10078000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79 -:10079000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69 -:1007A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59 -:1007B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49 -:1007C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39 -:1007D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29 -:1007E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19 -:1007F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09 -:10080000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8 -:10081000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8 -:10082000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8 -:10083000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8 -:10084000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8 -:10085000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8 -:10086000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98 -:10087000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88 -:10088000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78 -:10089000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68 -:1008A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58 -:1008B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48 -:1008C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38 -:1008D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28 -:1008E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18 -:1008F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08 -:10090000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7 -:10091000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7 -:10092000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7 -:10093000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7 -:10094000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7 -:10095000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7 -:10096000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97 -:10097000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87 -:10098000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77 -:10099000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67 -:1009A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57 -:1009B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47 -:1009C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37 -:1009D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27 -:1009E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17 -:1009F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07 -:100A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6 -:100A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6 -:100A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6 -:100A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6 -:100A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6 -:100A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6 -:100A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96 -:100A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86 -:100A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76 -:100A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66 -:100AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56 -:100AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46 -:100AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36 -:100AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26 -:100AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16 -:100AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06 -:100B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5 -:100B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5 -:100B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5 -:100B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5 -:100B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5 -:100B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5 -:100B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95 -:100B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85 -:100B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75 -:100B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65 -:100BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55 -:100BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45 -:100BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35 -:100BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25 -:100BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15 -:100BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05 -:100C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4 -:100C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4 -:100C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4 -:100C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4 -:100C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4 -:100C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4 -:100C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94 -:100C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84 -:100C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74 -:100C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64 -:100CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54 -:100CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44 -:100CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34 -:100CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24 -:100CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14 -:100CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04 -:100D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 -:100D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3 -:100D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3 -:100D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3 -:100D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3 -:100D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3 -:100D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93 -:100D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83 -:100D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73 -:100D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63 -:100DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53 -:100DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43 -:100DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33 -:100DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23 -:100DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13 -:100DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03 -:100E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2 -:100E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2 -:100E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2 -:100E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2 -:100E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2 -:100E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2 -:100E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92 -:100E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82 -:100E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72 -:100E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62 -:100EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52 -:100EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42 -:100EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32 -:100ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22 -:100EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12 -:100EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02 -:100F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1 -:100F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1 -:100F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1 -:100F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1 -:100F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1 -:100F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1 -:100F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91 -:100F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81 -:100F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71 -:100F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61 -:100FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51 -:100FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41 -:100FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31 -:100FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21 -:100FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11 -:100FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01 -:10100000D10020402C100060000000000000000013 -:1010100020100060001000600000000000000000D0 -:10102000000000607031000000000000000001209E -:1010300035100060764B0720764C4FF42A01764A33 -:101040005C64186499639546744A75498A420FD066 -:10105000744B9A420CD2D4430846234423F0030332 -:1010600004330B4450F8041B984242F8041BF9D196 -:101070006D4A6E498A420FD06D4B9A420CD2D443CE -:101080000846234423F0030304330B4450F8041BA5 -:10109000984242F8041BF9D1664A674B9A420BD238 -:1010A000D04311460024034423F0030304331344C4 -:1010B00041F8044B8B42FBD1604A4FF47001604B06 -:1010C000116003F530715F4A43F8042F9942FBD158 -:1010D0005D4B80215D4A03F8011B9342FBD15C4CC0 -:1010E0004FF080375B4E4FF0FF32DFF8A8A10025AC -:1010F000DFF8A491DFF8A481574B4549C3F800A05D -:10110000C4F80471C4F80091C4F8F470C4F8F08015 -:10111000F36923F07F0343F04003F361736A23F024 -:101120007F0343F0400373628A66CA660A674A67B0 -:1011300000F0B6F8494A6320494B4A49106003223F -:101140001D60CAF8381043F8082C4749474A4848F8 -:10115000C3F8082D0B68474A43F08073CAF83C0077 -:1011600045480B601368454943F001031360036869 -:101170000B6000F0E5F8C4F804714148C4F8009130 -:10118000C4F8F470C4F8F08000F03AFA00BF00BF71 -:1011900000BF00BFF16E3B4A41F440513A4BF1664B -:1011A0001560C2F80851C2F81851C2F82851C2F8A7 -:1011B00038519A6BD20708D442F615623349596503 -:1011C0001A659A6B42F001029A632F4A304C936879 -:1011D00043F00113936000F01BFA2368132BFCD932 -:1011E00000F05CF900F0D0F900F016FA00F0DAF83F -:1011F00000F0FEF92368B3F5967FFBD300F0FCF90D -:1012000000F00EFA00F018FA00F012FAFAE700BF48 -:1012100000C00A40ABAAAAAA008007200000000074 -:10122000501600605017000000000020A42D006040 -:10123000D0030020D0030020C032002088ED00E061 -:10124000FC0F00201D05000000E400E0A0E400E029 -:1012500000800D4000C00F4008ED00E014E000E009 -:1012600018E000E095100000FCED00E000002020F8 -:1012700099110000001000E0041000E0A40D00200F -:101280000046C3230040084000400D400000C05607 -:10129000A80D0020001000201B1018200C0D1113A9 -:1012A000F0B5194A0021194B4FF0100E18480124CF -:1012B000184E194D0160194FC2F800E01E6015600C -:1012C000174E184D1F601660174F1D60174E184DB2 -:1012D00017601E60174F1560174E184D1F6016607F -:1012E0001D600460BFF34F8FBFF36F8FC3F8B01161 -:1012F000BFF34F8FBFF36F8F52F8883C43F44033F6 -:1013000042F8883CF0BD00BF9CED00E0A0ED00E09D -:1013100094ED00E0250008031100200021000207E1 -:1013200012000020250008131300202027000B13B3 -:101330001400004033001013150000602F000B074D -:10134000F0B4174A40274FF480314FF480564FF4E1 -:1013500000554FF4404443F24200136913F0020F6A -:1013600006D0946151619061136913F0020FF8D1B6 -:1013700013F4005F01D15561EFE713F4805F01D1F1 -:101380005661EAE7002BE8DA13F4803F01D091615F -:10139000E3E75B0601D45761DFE7F0BC704700BFAD -:1013A00000800D40364A03203649F3EE096A13687F -:1013B00023F00103F0B51360C2F89000D1F8E030DB -:1013C000DFF8D4E0DCB2C3F30B202F4FC0EB135394 -:1013D00007EE904AA4F15501CEF80040B8EEE77A46 -:1013E00003EB830407EE900A03FB01F13B6003EB80 -:1013F0008313B8EEE75A07EE901A091B77EE666A78 -:10140000B8EE677A214D07EE901A0B44C5ED006ADD -:10141000F8EE677A1E4EC7EE265A1E4930601068F5 -:1014200087EEA66A07EE903AF8EE677A87EEA67A1C -:1014300075EE855A76EE056AFCEEE55AFCEEE66A34 -:1014400015EE903A40EA035316EE900A77EE057ACD -:10145000136001EA0041D2F81031FCEEE77A0B4349 -:10146000C2F8103117EE903AD2F81011C3F30B0303 -:101470000B43C2F81031136843F002031360F0BD50 -:1014800080810D4000441F40F8030020F403002039 -:10149000FC0300200000FF0FF0030020304B40F65B -:1014A000617270B5C3F8202140F2044500F0ACF839 -:1014B0002C492D48D1F880202C4C42F003022C4BB3 -:1014C000C1F88020C0F86051226813401BB9D0F8E1 -:1014D000A8319A071AD0244B4FF00041234A516398 -:1014E0001A46D3F8401141F00201C3F84011D2F876 -:1014F00040319B07FBD44FF400301E491B4B4FF08B -:101500000042086019209A6300F09AF81A4D0022F0 -:10151000164B4FF08041144C0A26996328461A60F6 -:101520001146C4F8A8614FF4207200F07DF84FF422 -:1015300081064FF4800040F24313104A10492E6098 -:101540002864C4F85851C2F80412C4F848310D4A4E -:101550004FF4003101231160C4F8403170BD00BF69 -:1015600000800D4000C00F4000002E4000900D4054 -:10157000001C1E008CE200E0003000200010002063 -:10158000F90600000CE100E0114B1249D86E0A4642 -:1015900040F4403030B4D86640F2B765D86EA0242D -:1015A00040F44070D8664D648C64936C1B06FCD488 -:1015B000094B40F2B760A021064A58649964936CC5 -:1015C00013F08003FBD1054A137030BC704700BF95 -:1015D00000C00F4000400C4000800C40A10D0020D6 -:1015E000F8B500BFF8BC08BC9E467047FFFFFFFF80 -:1015F0005FF800F0390500005FF800F055160000B4 -:101600005FF800F0D90100005FF800F0F90D00006C -:101610005FF800F0350500005FF800F06913000086 -:101620005FF800F0010100005FF800F0A51600006F -:101630005FF800F0B91100005FF800F0050100004C -:101640005FF800F05912000045000000FFFFFFFFA7 -:10165000000000000000000000000000000000008A -:10166000000000000000000000000000000000007A -:1016700010B5054C237833B9044B13B10448AFF3CC -:1016800000800123237010BDD00300200000000063 -:1016900054170000084B10B51BB108490848AFF3B8 -:1016A00000800848036803B910BD074B002BFBD02E -:1016B000BDE81040184700BF00000000D403002020 -:1016C00054170000D00300200000000008B5174B9D -:1016D0000121187800F00AFA154B0121187800F062 -:1016E00005FA144B0121187800F000FA124B012181 -:1016F000187800F0FBF9114B0121187800F0F6F989 -:101700000F4B0121187800F0F1F90E4B00211878E9 -:1017100000F0ECF90C4B0021187800F0E7F90B4BC6 -:1017200000211878BDE8084000F0E0B9F40200207C -:10173000FC0200200003002010030020080300200A -:10174000F8020020EC030020EC020020F002002050 -:10175000FFF7BCBF2C4A2D4B2DE9F04714682C4AEB -:10176000DFF8B8A012685746DFF8B490294EDFF8CA -:10177000B480294D93FBF4F393FBF2F4012199F823 -:10178000000000F0ADF9DAF800103268A5FB023273 -:10179000920C04FB02F2DAF800305B1A9A42FAD893 -:1017A000012198F8000000F09BF939683268A5FB28 -:1017B0000232920C04FB02F2DAF800305B1A9A4211 -:1017C000FAD8002199F8000000F08AF939683268E7 -:1017D000A5FB0232920C04FB02F2DAF800305B1A2D -:1017E0009A42FAD8002198F8000000F079F9396897 -:1017F0003268A5FB0232920C04FB02F2DAF80030E8 -:101800005B1A9A42FAD8B9E70C03002000879303C9 -:10181000040300201803002083DE1B43041000E0B3 -:10182000FC020020000300208C4A8D4B90422DE9E1 -:10183000F0438C4D5C699969EF681DD98A4B9842D9 -:1018400040F20181894B40F22766DFF860E20344F1 -:10185000874D1A0AAEFB0232D30903EB830303EB75 -:10186000830202F2E243B34228BF3346A3F5487332 -:10187000A5FB0336F60804E07E4EB04294BF062670 -:101880000E26774A07F01F0ED2F88030B64543F097 -:10189000C003C2F880300AD2724B27F01F071A46E5 -:1018A0003743DF601368002BFCDA07F01F0E14F0DB -:1018B00000732ED1704D714AD5F810C015460CEA50 -:1018C0000202AA420ABF4FF0C0534FF480520022D6 -:1018D00084EA030515F0605F06D024F0605403F03D -:1018E00060535F4D1C436C6181EA020313F4405F57 -:1018F00008D05B4B21F4405111431A469961936C17 -:101900001D07FCD444F00074554A5461936C990649 -:10191000FCD401215A4D0A4601FB02F300FB03F3FC -:10192000AB4209D8072A00F28480013201FB02F39E -:1019300000FB03F3AB42F5D95248534D1844A5FBC5 -:101940000030030D6C2B79D8352B7ED8DFF8608101 -:1019500036234E48DFF820C14D4DDCF80090B0FB37 -:10196000F2F009EA05054545B0FBF1F00BD043F470 -:1019700000534FF480586546CCF80080CCF8003016 -:101980002B68002BFCDADFF8D8C0013ADCF8103005 -:1019900003F00703934207D002F007026546CCF834 -:1019A0001020AB6CDB03FCD40139890284EA01030B -:1019B00013F4E05F0AD02A4B24F4E05401F4E05120 -:1019C0001A460C435C61936C9907FCD4314B324945 -:1019D0000344DB09A1FB0331090B042928BF0421BF -:1019E0004B1E1B0284EA030212F4407F06D024F44B -:1019F000407403F440731A4A1C435461184B24F09A -:101A000000741A465C61936C9B06FCD4B0FBF1F148 -:101A1000224A7645224B1060196008D2114B27F0FC -:101A20001F071A463743DF601368002BFCDABDE856 -:101A3000F083042980D8013101226DE7DFF874803A -:101A40006C23184886E712261BE71748DFF86880E2 -:101A500000FB03F043EA08087CE700BF00A4781FFE -:101A600000C00F40000008400046C32300BA3CDC21 -:101A70001F85EB5100366E0100800D404030008024 -:101A8000FFB19F26808D5B00819F5E1600B29F266E -:101A90007F3001807FD1F0089F10E50018030020FF -:101AA0001403002000643F4D001BB70023B24C001C -:101AB000362000806C200080002000800001074B51 -:101AC0001A181B58D2685868104202D011B9C3F8CE -:101AD00088207047C3F88420704700BF00000020B2 -:101AE000272801D8FFF7EABF704700BF27281CD876 -:101AF00000011A4A012902EB0003105810B415D056 -:101B0000042913D0DC68426822EA040242609A6821 -:101B1000E9B102291ED003295B685DF8044B0CBFB4 -:101B20000F491049116015221A607047DA680129BF -:101B3000446842EA040242609A6808D040F63801DC -:101B400011605B6815225DF8044B1A6070473821FC -:101B50001160F6E704491160F3E700BF00000020C0 -:101B6000383001003800010038F0010004207146CF -:101B7000084203D0EFF3098000F008B8EFF30880C3 -:101B800000F004B8704700BF704700BF1B4B052131 -:101B90001B4A382030B5C2F848110821C2F8380372 -:101BA00083B05A68174C0A4317485A60C3F8841028 -:101BB000C3F888102368834202D91448FFF734FE23 -:101BC0000E4D08240020124A1249C5F884400190A5 -:101BD000019B934205D8019B01330193019B9342E2 -:101BE000F9D9C5F888400190019B8B42EDD8019B43 -:101BF00001330193019B8B42F9D9E6E700C01B40FA -:101C000000801F4018030020FF45C32300A3E111FB -:101C10003F420F003F548900836B30B41BB15368BF -:101C200043F40043536072B6446B9CB1104B226086 -:101C3000D3F8B0410C4217D1D3F8404144F480446A -:101C4000C3F84041D3F8B851D3F840416404F3D508 -:101C5000294209D10023064C8260C360D4F8B03118 -:101C60001943C4F8B0110263426362B630BC7047D6 -:101C700000002E4038B50546036B06E0AA6B1C68D1 -:101C800090476A6B2346944208D0184633B1012B23 -:101C900004D05A681206F1D52B6338BD00232B639C -:101CA0006B6338BDF0B5F1B9224C23490020234BBA -:101CB00001228025A1600A601A464D60E060D3F8D9 -:101CC000BC41886044F001141D4DC3F8BC41D3F8F9 -:101CD000B01141F00101C3F8B0112860D2F8B03161 -:101CE000002BFBD1F0BD0904164B144D0126114CFD -:101CF00041F0800100221E60596000F5805CE264C2 -:101D000000F5005EA36400F54057D5F8B01100F56A -:101D100080462A46986041F48031C3F80CC0C3F86D -:101D200010E05F619E61C5F8B011D2F8B031002BB0 -:101D3000FBD1BAE7003000202020002000002E4018 -:101D40000C040020002000202DE9F04FBB4C83B094 -:101D5000D4F8448118F0010FC4F844815ED0D4F85F -:101D6000AC31002B55D04FF00119DFF8F0B2264608 -:101D7000B34FCA46B96AFA6AC4F8AC31D4F84031F4 -:101D800043F40053C4F84031D4F840319D04F5D5F4 -:101D9000D6F8403123F40053C6F84031C6F8B4A158 -:101DA000D4F8B43113F00113FAD188B240F281654E -:101DB000CBF80030A84200F29680B0F5D06F80F0EA -:101DC000D881B0F5817F00F0578100F2D58080285E -:101DD00000F02681822840F0C78092B202F07F0195 -:101DE000072900F2C18089009648974D0844016890 -:101DF00010062B706B7040F1E581C90301D50123FA -:101E00002B7002219048FFF74DFFD4F8AC31002B26 -:101E1000B0D18A4AD2F8BC31002B44D118F0400F1F -:101E200018D0864BD3F8AC111A46C3F8AC11D3F8CE -:101E3000BC11C3F8BC11D2F8B041804B002CFAD1D0 -:101E40004FF0FF32C3F8B421D3F8843100F074FBB3 -:101E50007E4B1C6018F0807F03D07D4B1B6803B164 -:101E6000984718F0007F03D07A4B1B6803B198475E -:101E700018F0040F02D0714BD3F884316F4BD3F8B4 -:101E8000482112060CD518F0800F09D072490A7843 -:101E9000002A00F08E81531EDBB20B7003B9FBBE2B -:101EA00003B0BDE8F08F6D49C2F8BC310868034249 -:101EB00040F0CC81654A14681C40AFD0630700F144 -:101EC000E281670300F1EF81260700F1E881250335 -:101ED00000F1E181E00600F1DA81E1029ED560487F -:101EE000FFF7C8FE9AE742F22105A84238D06FD822 -:101EF000B0F5086F00F02F81B0F5106F34D1C1F349 -:101F00000741584A584811705849594A0193C6F830 -:101F1000C801C6F8CC11C6F8D02100F00FFB554A15 -:101F2000019B80210120FB6410605160D6F8BC1138 -:101F3000936041F00111BA64C6F8BC11D6F8B02123 -:101F400042F48032C6F8B021444A1360D4F8B0316C -:101F5000002BFBD1D4F8AC31002B7FF40BAF58E74A -:101F6000100C072800F03281C4F8C091D4F8AC31CD -:101F7000002B7FF4FFAE4CE740F20235A84200F0A0 -:101F8000B780B0F5A06FEFD13A4A80200125FB64FD -:101F900050601560D6F8BC01936040F00113BA643C -:101FA000C6F8BC31D6F8B03143F48033C6F8B0314E -:101FB000D4F8B031002BFBD10B0C5B0643F08073DF -:101FC000C6F85431D4F8AC31002B7FF4D3AE20E7FF -:101FD00042F22123984200F0828042F2213398425B -:101FE000C2D1244B01218022196000215A60D4F80B -:101FF000BC21BB6442F001129960F964C4F8BC21B1 -:10200000D4F8B03143F48033C4F8B031D4F8B031EF -:10201000002BFBD1D4F8AC31002B7FF4ABAEF8E64B -:10202000094A0221104613705370FFF73BFED4F8A3 -:10203000AC31002B7FF49EAEEBE600BF00002E40DB -:1020400000300020C0012E402004002004040020A5 -:102050000004002008040020100400200C040020CC -:1020600040320020300400200200CC00C8000200F2 -:102070000200C8002020002092B202F07F03072B4C -:102080003FF672AF12F0800F4FEA8303884A4FF099 -:1020900001011A44136814BF23F4803323F00103B1 -:1020A00013608022834B196000215A60D4F8BC2150 -:1020B000BB6442F001129960F964C4F8BC21D4F801 -:1020C000B03143F48033C4F8B031D4F8B031002BD0 -:1020D000FBD1D4F8AC31002B7FF44CAE99E6764BB3 -:1020E000C1F30741754A1868754B1060197078E79D -:1020F00092B202F07F03072B3FF636AF12F0800F4B -:102100004FEA83036A4A4FF001011A44136814BF6F -:1021100043F4803343F0010313608022654B196060 -:1021200000215A60D4F8BC21BB6442F001129960CE -:10213000F964C4F8BC21D4F8B03143F48033C4F856 -:10214000B031D4F8B031002BFBD1D4F8AC31002B36 -:102150007FF410AE5DE65B4A01215B4B12781846B6 -:102160001A70FFF79FFDD4F8AC31002B7FF402AE5C -:102170004FE6564B586800283FF4F6AE090C1FFA9C -:1021800082FE04E00C33586800283FF4EDAE1D8851 -:102190008D42F7D15D887545F4D1090A120C0329E7 -:1021A0000CBF01781989914228BF1146FFF77AFDCB -:1021B0002BE6D3F8482122F08002C3F8482103B06F -:1021C000BDE8F08FCA077FF51CAE18E6404D012030 -:1021D000FB6029603F4B4049186059603F49D6F881 -:1021E000B0016A6001F5005E40F00102A1F5005007 -:1021F00001F58055BB609860A1F58050D860C6F8A5 -:10220000B02119615D61C3F818E0D4F8B031002B3A -:10221000FBD1284A012048F28001FB6410604FF492 -:1022200080305160D6F8BC11936041F00113BA645C -:10223000C6F8BC31D6F8B0310343C6F8B031CBF89C -:102240000000D4F8B031002BFBD1DEE5204C42F287 -:102250002100002524880D6084427FF42BAE2049A4 -:10226000204C03C90D0C86282060A180A5717FF445 -:1022700021AED2F8481150241B4841F08001C2F829 -:102280004811047016E61948FFF7F4FC19E61848DF -:10229000FFF7F0FC21E61748FFF7ECFC1AE61648BA -:1022A000FFF7E8FC13E61548FFF7E4FC0CE600BF77 -:1022B000C0012E4020200020A80D002088320020E0 -:1022C000800D002030040020200400208002002027 -:1022D000180400200020002080000700282400208F -:1022E000280400208032002010040020003100204B -:1022F00000320020C0310020803100204031002019 -:10230000002AA0F102022DE9F04714BF00274FF088 -:102310000057022A01D9BDE8F0874FEAC01ADFF85A -:1023200040900D4604460AEB0906002140229846DB -:10233000304600F0DFFC012047EA05414AF8091069 -:10234000C6F83880B060B8F1000FE4D0034BA0406D -:102350001C6820431860BDE8F08700BF040400201B -:1023600000300020002AA0F102022DE9F04714BF3E -:1023700000274FF00057022A01D9BDE8F08740221C -:10238000C501DFF8449088461544002104461E46E6 -:1023900005EB090A504600F0ADFC012247EA08416E -:1023A00045F80910CAF83860CAF80820002EE4D0B1 -:1023B00004F11000034B8240186802431A60BDE824 -:1023C000F08700BF04040020003000201204816068 -:1023D000C36142F08002F0B44260012701F58056EB -:1023E00001F5005501F5405401F580420760C660D3 -:1023F000056144618261F0BC704700BF831E022BFF -:1024000000D9704730B4064B00F1100401250A468C -:1024100003EBC01005FA04F130BCFFF7FDBB00BFB1 -:1024200040300020831E022B00D9704710B4054BAA -:1024300001240A4604FA00F103EBC0105DF8044BD6 -:10244000FFF7EABB00300020124A134BD2F82002FB -:1024500020F07F40984210B584B002D800EB800095 -:1024600040000E4C01A90A2200F07EFA01A90023C7 -:10247000204611F8012B01333AB10A2B20F8022F24 -:10248000F7D11623237004B010BD5B00DBB22370BC -:1024900004B010BD00441F407F969800B403002094 -:1024A0004368C269C3F30E43054930B4C3F1400326 -:1024B000044C002521F8123024F8125030BC70472B -:1024C000000C0020F80B0020F8B5154B1B783BB929 -:1024D00003F0FF04134B1B7813B1134D2A8802B984 -:1024E000F8BD124F2346124EC2F58072397811485A -:1024F00006EB411600EB01213046FFF767FF31463E -:102500000420FFF77BFF3B780133DBB2062B98BF3B -:102510003B704FF0000388BF3C702B80F8BD00BFBC -:10252000340B002030040020800C0020350B0020EC -:10253000A00C002034040020704700BF0021E022DE -:102540002048F8B50C46204E204D00F0D3FB204F1C -:102550002146204B6022347028461F4E1C8000F01C -:10256000C9FB23462246102102203C60BC80346017 -:10257000B480FFF7F7FE2246184B40210320FFF7F7 -:10258000BFFE2346224640210420FFF7EBFE2346F0 -:10259000402228461249FFF719FF29460320FFF77A -:1025A00041FF104B4A22104910480860C3F884408C -:1025B000C3F88020D3F8482142F08072C3F8482144 -:1025C000F8BD00BFA00C0020350B0020200C00201F -:1025D000000C0020800C0020F80B0020510E0000A1 -:1025E000380B002000002E4000040020790E00006F -:1025F000024A034B10881B88C01A7047000C002049 -:10260000F80B002010B4EFF3108272B6437F33B999 -:10261000017F012908D0032910D00123437702B993 -:1026200062B65DF8044B7047114C2168A1B11149A5 -:1026300043610B68086083615861EEE70E4C2168C6 -:1026400081B10E4943610B680860836158610C4B8E -:102650004FF080511960E0E7064B416181612060D5 -:102660001860DAE7054B4161816120601860EEE790 -:10267000940D0020900D0020840D0020880D002076 -:1026800004ED00E010B4047F4160022CC26003D06E -:102690005DF8044BFFF7B6BF83685DF8044B18473D -:1026A00070B5EFF3108172B60C4C23688BB10C4EF1 -:1026B00000255A6922607AB1956101B962B65D77E9 -:1026C00018469B689847EFF3108172B62368002B79 -:1026D000EFD101B962B670BD3260EEE7840D002023 -:1026E000880D0020FFF7DCBF184A30B41468002CB6 -:1026F00028D0036821688B420FD2CB1A0021846056 -:10270000C1602360E0601060022330BC0375704735 -:102710000360144611688B4208D3A2685B1A002A32 -:10272000F6D18260C4600360A060EDE7D568CB1A83 -:1027300082600222C560E060C16888602360027523 -:1027400030BC70478460C4601060DDE78C0D0020F1 -:10275000F8B5224E34682CB32368002B3AD11D46BD -:102760001F4F04E03468ECB12368002B32D1A3681A -:1027700003B1DD6020693360036825751B68BB42C7 -:1027800021D1037F4560022BC46020D0FFF73AFFC0 -:102790006368002BE6D023602046FFF7A5FF34686E -:1027A000002CE1D1EFF3108372B60E4A00211068BD -:1027B000116003B962B628B18468FFF795FF20461F -:1027C0000028F9D1F8BD224600219847E0E7836848 -:1027D0009847DDE7013B2360E4E700BF8C0D002054 -:1027E000351000009C0D0020044A054B1168054A75 -:1027F0001960136801331360FFF7AABF041000E0EB -:10280000A40D0020A80D002070B5214C237883B9B9 -:10281000204B01221B7822701BBB1F4B1B78002B07 -:1028200029D11E4B00211A68217012B1EFF30582E5 -:1028300002B170BDEFF3108072B61A68F2B1184C95 -:102840002178D9B90126556926701D60D5B1A961D5 -:1028500000B962B6002593681046557798472570F1 -:1028600070BDFFF7C5FE0028D7D000F015FA0A4B5F -:102870001B78002BD5D000F0FBF9D2E70028D8D187 -:1028800062B670BD074B1D600028E3D1E1E700BFD1 -:10289000A00D0020CA030020C80D0020940D0020C8 -:1028A000980D0020900D0020002852D02DE9F04F07 -:1028B000814683B0274C0120274D284E54E8003F25 -:1028C0002A68316844E80003002BF7D1244F4FF405 -:1028D0007A7E2448D7F800C0BB4607F1C647036894 -:1028E000C1EB0C0107F5DE1707F67F67A7FB03C3F3 -:1028F000BA4601279B0CB1FBF3F30EFB023854E8F8 -:10290000003F2A68316844E80073002BF7D1DBF8F8 -:1029100000C04FF47A7E03680EFB02F2C1EB0C019B -:10292000AAFB033EC8EB02034FEA9E42B1FBF2F161 -:10293000CA18B2F57A7F07D3B9F1010908F57A7898 -:10294000DDD103B0BDE8F08F0190FFF75DFF019886 -:10295000D5E770478C320020A80D0020A40D002080 -:10296000041000E018030020F0B44E1E0025374686 -:1029700000E00135B0FBF2F302FB130000F1370475 -:10298000092800F13000E4B298BFC4B2184607F835 -:10299000014F002BEDD14A1953704DB1013316F898 -:1029A000014F1778E81A3770834202F80149F5DBC6 -:1029B0000846F0BC704700BFA4484FF00F0CA44B72 -:1029C000826F42F47F02F0B582670025D0F8802044 -:1029D0004FF470469F4C4FF4604E29464FF4806789 -:1029E00014432A46C0F88040A3F88C6148F2B82608 -:1029F000A3F88EC1A3F89051B3F8880180B240F0DB -:102A0000F000A3F8880101EB4100914B0131002552 -:102A100040011C4604290344A3F804E0DF805A84E3 -:102A20001A865A805A81DE815A82DA825A83DA8380 -:102A3000E9D1B4F888014FF00F0C874B4FF4704682 -:102A400080B229464FF460472A4640EA0C004FF412 -:102A5000806EA4F88801B4F8880180B240F47060F8 -:102A6000A4F88801A3F88C6148F2B826A3F88EC1B7 -:102A7000A3F89051B3F8880180B240F0F000A3F8B9 -:102A8000880101EB4100744B0131002540011C46D7 -:102A9000042903449F80A3F806E05A841A865A80CA -:102AA0005A81DE815A82DA825A83DA83E9D1B4F814 -:102AB00088014FF00F0C694B4FF4704680B22946E5 -:102AC0004FF460472A4640EA0C004FF4806EA4F8A9 -:102AD0008801B4F8880180B240F47060A4F88801DD -:102AE000A3F88C6148F2B826A3F88EC1A3F89051E0 -:102AF000B3F8880180B240F0F000A3F8880101EB40 -:102B00004100564B0131002540011C460429034475 -:102B10009F80A3F806E05A841A865A805A81DE8183 -:102B20005A82DA825A83DA83E9D1B4F888014FF005 -:102B30000F0C4B4B4FF4704780B229464FF4604660 -:102B40002A4640EA0C004FF4806EA4F88801B4F8DD -:102B5000880180B240F47060A4F88801A3F88C71F9 -:102B600048F2B827A3F88EC1A3F89051B3F88801B2 -:102B700080B240F0F000A3F8880101EB4100384B2F -:102B8000013140011C46042903449E80A3F806E05D -:102B90005A841A865A805A81DF815A82DA825A838D -:102BA000DA83EAD1B4F888310F27002241F2010616 -:102BB0009BB245F6C05E114643F226053B43A4F89E -:102BC0008831B4F888319BB243F47063A4F888313B -:102BD0005001244B01320344042A99815981DF8139 -:102BE0009E82A3F806E0198019829D81F0D100220F -:102BF0000F2741F2010645F6C055114643F226045F -:102C00005001194B01320344042A99815981DF8113 -:102C10009E82DD80198019829C81F1D100220F27CC -:102C200041F2010645F6C055114643F22604500113 -:102C30000E4B01320344042A99815981DF819E821F -:102C4000DD80198019829C81F1D1F0BD00C00F4058 -:102C500000C03D40000003FC00003E4000403E40FC -:102C600000803E4000C01D4000001E4000401E404D -:102C700038B5074B1C784CB1064D55F8043F002B76 -:102C8000FBD09847631E13F0FF04F6D138BD00BF98 -:102C9000C80D0020A80D0020014B00221A707047BB -:102CA000CA03002070B50F4E0F4D761BB61018BF2B -:102CB000002405D0013455F8043B9847A642F9D1C9 -:102CC0000A4E0B4D761B00F063F8B61018BF0024B7 -:102CD00006D0013455F8043B9847A642F9D170BD9F -:102CE00070BD00BF48160060481600604C160060BA -:102CF0004816006070B4840746D0541E002A41D0A4 -:102D0000CDB2034602E0621EE4B3144603F8015B51 -:102D10009A07F8D1032C2ED9CDB245EA05250F2C00 -:102D200045EA054519D903F110022646103E0F2E3B -:102D300042F8105C42F80C5C42F8085C42F8045C13 -:102D400002F11002F2D8A4F1100222F00F0204F0F6 -:102D50000F041032032C13440DD91E462246043AA8 -:102D6000032A46F8045BFAD8221F22F00302043239 -:102D7000134404F003042CB1C9B21C4403F8011B32 -:102D8000A342FBD170BC704714460346C2E700BFA4 -:102D90005FF800F0E1150060000000000000000096 -:042DA000F8B500BFC3 -:102DA40000000042C8801F40B8821F400800000095 -:102DB40000000042C4801F40B4821F400400000091 -:102DC40000C0004224801F4014821F4010000000F5 -:102DD40000C0004228801F4018821F4020000000CD -:102DE40000C000422C801F401C821F404000000095 -:102DF40000C0004234801F4024821F4000010000B4 -:102E04000040004264811F4054831F4000040000BE -:102E14000040004280811F4070831F400000020078 -:102E2400004000427C811F406C831F400000010071 -:102E34000040004268811F4058831F400008000082 -:102E4400004000423C811F402C831F4001000000D1 -:102E54000040004244811F4034831F4004000000AE -:102E64000040004240811F4030831F4002000000A8 -:102E74000040004248811F4038831F400800000082 -:102E84000000004204811F40F4821F40000004003F -:102E94000000004208811F40F8821F400000080023 -:102EA4000000004218811F4008831F40000080007A -:102EB4000000004214811F4004831F4000004000B2 -:102EC4000000004200811F40F0821F400000020009 -:102ED40000000042FC801F40EC821F400000010003 -:102EE4000000004224811F4014831F40000000049E -:102EF4000000004228811F4018831F400000000882 -:102F0400000000421C811F400C831F400000000190 -:102F14000000004220811F4010831F400000000277 -:102F240000000042EC801F40DC821F4000100000C3 -:102F340000000042F0801F40E0821F40002000009B -:102F44000000004234811F4024831F4000000040E1 -:102F54000000004238811F4028831F400000008089 -:102F64000080004294801F4084821F4000000400BF -:102F740000C0004290801F4080821F4000000080FB -:102F840000800042A8801F4098821F4000008000FB -:102F940000800042A4801F4094821F400000400033 -:102FA400004000426C811F405C831F400010000001 -:102FB40000C0004230801F4020821F40800000007B -:102FC40000800042C8811F40B8831F400080000079 -:102FD40000800042C4811F40B4831F4000400000B1 -:102FE40000800042C0811F40B0831F4000200000C9 -:102FF40000800042BC811F40AC831F4000100000D1 -:1030040000800042D0811F40C0831F4000000200A6 -:1030140000800042CC811F40BC831F40000001009F -:1030240000010000840300201200000000060000DC -:103034001C0300200A0000000002000040030020DE -:10304400430000000007000040030020430000008C -:1030540000030000B0030020000000000103090485 -:103064002803002000000000020309049803002044 -:103074000000000003030904B40300200000000062 -:10308400000000000000000000000000010000003B -:1030940002000000170000001200000016000000EB -:1030A400150000001E0000001300000000200000B6 -:1030B400140000000029DE07007B9A170A060002AC -:1030C4000200004001000000180354006500650080 -:1030D4006E00730079006400750069006E006F0073 -:1030E40009024300020100C0320904000001020287 -:1030F4000100052400100105240101010424020635 -:1031040005240600010705820310001009040100CC -:10311400020A0000000705030240000007058402BC -:10312400400000001201000202000040C0168304A7 -:103134007902010203010000160355005300420006 -:103144002000530065007200690061006C000000FB -:10315400040309040C030000000000000000000048 -:10316400000000000000000000000100000000005A -:040000056000100087 -:00000001FF diff --git a/tools/odrive/tests/enc1_sim_-4096cps.ino.hex b/tools/odrive/tests/enc1_sim_-4096cps.ino.hex deleted file mode 100644 index 43eabc4b..00000000 --- a/tools/odrive/tests/enc1_sim_-4096cps.ino.hex +++ /dev/null @@ -1,795 +0,0 @@ -:0200000460009A -:100000004643464200000156000000000101020084 -:1000100000000000000000000000000000000000E0 -:1000200000000000000000000000000000000000D0 -:1000300000000000000000000000000000000000C0 -:1000400000000000010403000000000000000000A8 -:100050000000200000000000000000000000000080 -:100060000000000000000000000000000000000090 -:100070000000000000000000000000000000000080 -:10008000EB04180A063204260000000000000000FD -:10009000050404240000000000000000000000002F -:1000A0000000000000000000000000000000000050 -:1000B0000604000000000000000000000000000036 -:1000C0000000000000000000000000000000000030 -:1000D00020041808000000000000000000000000DC -:1000E0000000000000000000000000000000000010 -:1000F0000000000000000000000000000000000000 -:10010000D8041808000000000000000000000000F3 -:100110000204180804200000000000000000000095 -:1001200000000000000000000000000000000000CF -:10013000600400000000000000000000000000005B -:1001400000000000000000000000000000000000AF -:10015000000000000000000000000000000000009F -:10016000000000000000000000000000000000008F -:10017000000000000000000000000000000000007F -:10018000000000000000000000000000000000006F -:10019000000000000000000000000000000000005F -:1001A000000000000000000000000000000000004F -:1001B000000000000000000000000000000000003F -:1001C000000100000010000001000000000000001D -:1001D000000001000000000000000000000000001E -:1001E000000000000000000000000000000000000F -:1001F00000000000000000000000000000000000FF -:10020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -:10021000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE -:10022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE -:10023000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE -:10024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE -:10025000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE -:10026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E -:10027000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E -:10028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E -:10029000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E -:1002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E -:1002B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E -:1002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E -:1002D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E -:1002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E -:1002F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E -:10030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD -:10031000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED -:10032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD -:10033000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD -:10034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD -:10035000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD -:10036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D -:10037000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D -:10038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D -:10039000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D -:1003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D -:1003B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D -:1003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D -:1003D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D -:1003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D -:1003F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D -:10040000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC -:10041000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC -:10042000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC -:10043000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC -:10044000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC -:10045000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC -:10046000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C -:10047000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C -:10048000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C -:10049000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C -:1004A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C -:1004B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C -:1004C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C -:1004D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C -:1004E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C -:1004F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C -:10050000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB -:10051000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB -:10052000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB -:10053000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB -:10054000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB -:10055000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB -:10056000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B -:10057000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B -:10058000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B -:10059000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B -:1005A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B -:1005B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B -:1005C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B -:1005D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B -:1005E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B -:1005F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B -:10060000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA -:10061000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA -:10062000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA -:10063000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA -:10064000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA -:10065000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA -:10066000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A -:10067000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A -:10068000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A -:10069000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A -:1006A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A -:1006B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A -:1006C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A -:1006D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A -:1006E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A -:1006F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A -:10070000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 -:10071000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9 -:10072000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9 -:10073000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9 -:10074000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9 -:10075000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9 -:10076000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99 -:10077000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89 -:10078000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79 -:10079000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69 -:1007A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59 -:1007B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49 -:1007C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39 -:1007D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29 -:1007E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19 -:1007F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09 -:10080000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8 -:10081000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8 -:10082000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8 -:10083000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8 -:10084000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8 -:10085000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8 -:10086000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98 -:10087000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88 -:10088000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78 -:10089000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68 -:1008A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58 -:1008B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48 -:1008C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38 -:1008D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28 -:1008E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18 -:1008F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08 -:10090000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7 -:10091000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7 -:10092000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7 -:10093000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7 -:10094000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7 -:10095000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7 -:10096000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97 -:10097000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87 -:10098000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77 -:10099000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67 -:1009A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57 -:1009B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47 -:1009C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37 -:1009D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27 -:1009E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17 -:1009F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07 -:100A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6 -:100A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6 -:100A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6 -:100A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6 -:100A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6 -:100A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6 -:100A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96 -:100A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86 -:100A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76 -:100A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66 -:100AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56 -:100AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46 -:100AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36 -:100AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26 -:100AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16 -:100AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06 -:100B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5 -:100B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5 -:100B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5 -:100B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5 -:100B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5 -:100B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5 -:100B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95 -:100B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85 -:100B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75 -:100B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65 -:100BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55 -:100BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45 -:100BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35 -:100BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25 -:100BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15 -:100BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05 -:100C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4 -:100C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4 -:100C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4 -:100C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4 -:100C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4 -:100C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4 -:100C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94 -:100C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84 -:100C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74 -:100C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64 -:100CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54 -:100CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44 -:100CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34 -:100CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24 -:100CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14 -:100CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04 -:100D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 -:100D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3 -:100D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3 -:100D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3 -:100D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3 -:100D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3 -:100D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93 -:100D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83 -:100D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73 -:100D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63 -:100DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53 -:100DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43 -:100DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33 -:100DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23 -:100DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13 -:100DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03 -:100E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2 -:100E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2 -:100E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2 -:100E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2 -:100E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2 -:100E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2 -:100E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92 -:100E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82 -:100E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72 -:100E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62 -:100EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52 -:100EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42 -:100EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32 -:100ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22 -:100EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12 -:100EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02 -:100F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1 -:100F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1 -:100F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1 -:100F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1 -:100F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1 -:100F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1 -:100F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91 -:100F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81 -:100F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71 -:100F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61 -:100FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51 -:100FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41 -:100FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31 -:100FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21 -:100FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11 -:100FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01 -:10100000D10020402C100060000000000000000013 -:1010100020100060001000600000000000000000D0 -:10102000000000607031000000000000000001209E -:1010300035100060764B0720764C4FF42A01764A33 -:101040005C64186499639546744A75498A420FD066 -:10105000744B9A420CD2D4430846234423F0030332 -:1010600004330B4450F8041B984242F8041BF9D196 -:101070006D4A6E498A420FD06D4B9A420CD2D443CE -:101080000846234423F0030304330B4450F8041BA5 -:10109000984242F8041BF9D1664A674B9A420BD238 -:1010A000D04311460024034423F0030304331344C4 -:1010B00041F8044B8B42FBD1604A4FF47001604B06 -:1010C000116003F530715F4A43F8042F9942FBD158 -:1010D0005D4B80215D4A03F8011B9342FBD15C4CC0 -:1010E0004FF080375B4E4FF0FF32DFF8A8A10025AC -:1010F000DFF8A491DFF8A481574B4549C3F800A05D -:10110000C4F80471C4F80091C4F8F470C4F8F08015 -:10111000F36923F07F0343F04003F361736A23F024 -:101120007F0343F0400373628A66CA660A674A67B0 -:1011300000F0B6F8494A6320494B4A49106003223F -:101140001D60CAF8381043F8082C4749474A4848F8 -:10115000C3F8082D0B68474A43F08073CAF83C0077 -:1011600045480B601368454943F001031360036869 -:101170000B6000F0E5F8C4F804714148C4F8009130 -:10118000C4F8F470C4F8F08000F03AFA00BF00BF71 -:1011900000BF00BFF16E3B4A41F440513A4BF1664B -:1011A0001560C2F80851C2F81851C2F82851C2F8A7 -:1011B00038519A6BD20708D442F615623349596503 -:1011C0001A659A6B42F001029A632F4A304C936879 -:1011D00043F00113936000F01BFA2368132BFCD932 -:1011E00000F05CF900F0D0F900F016FA00F0DAF83F -:1011F00000F0FEF92368B3F5967FFBD300F0FCF90D -:1012000000F00EFA00F018FA00F012FAFAE700BF48 -:1012100000C00A40ABAAAAAA008007200000000074 -:10122000501600605017000000000020A42D006040 -:10123000D0030020D0030020C032002088ED00E061 -:10124000FC0F00201D05000000E400E0A0E400E029 -:1012500000800D4000C00F4008ED00E014E000E009 -:1012600018E000E095100000FCED00E000002020F8 -:1012700099110000001000E0041000E0A40D00200F -:101280000046C3230040084000400D400000C05607 -:10129000A80D0020001000201B1018200C0D1113A9 -:1012A000F0B5194A0021194B4FF0100E18480124CF -:1012B000184E194D0160194FC2F800E01E6015600C -:1012C000174E184D1F601660174F1D60174E184DB2 -:1012D00017601E60174F1560174E184D1F6016607F -:1012E0001D600460BFF34F8FBFF36F8FC3F8B01161 -:1012F000BFF34F8FBFF36F8F52F8883C43F44033F6 -:1013000042F8883CF0BD00BF9CED00E0A0ED00E09D -:1013100094ED00E0250008031100200021000207E1 -:1013200012000020250008131300202027000B13B3 -:101330001400004033001013150000602F000B074D -:10134000F0B4174A40274FF480314FF480564FF4E1 -:1013500000554FF4404443F24200136913F0020F6A -:1013600006D0946151619061136913F0020FF8D1B6 -:1013700013F4005F01D15561EFE713F4805F01D1F1 -:101380005661EAE7002BE8DA13F4803F01D091615F -:10139000E3E75B0601D45761DFE7F0BC704700BFAD -:1013A00000800D40364A03203649F3EE096A13687F -:1013B00023F00103F0B51360C2F89000D1F8E030DB -:1013C000DFF8D4E0DCB2C3F30B202F4FC0EB135394 -:1013D00007EE904AA4F15501CEF80040B8EEE77A46 -:1013E00003EB830407EE900A03FB01F13B6003EB80 -:1013F0008313B8EEE75A07EE901A091B77EE666A78 -:10140000B8EE677A214D07EE901A0B44C5ED006ADD -:10141000F8EE677A1E4EC7EE265A1E4930601068F5 -:1014200087EEA66A07EE903AF8EE677A87EEA67A1C -:1014300075EE855A76EE056AFCEEE55AFCEEE66A34 -:1014400015EE903A40EA035316EE900A77EE057ACD -:10145000136001EA0041D2F81031FCEEE77A0B4349 -:10146000C2F8103117EE903AD2F81011C3F30B0303 -:101470000B43C2F81031136843F002031360F0BD50 -:1014800080810D4000441F40F8030020F403002039 -:10149000FC0300200000FF0FF0030020304B40F65B -:1014A000617270B5C3F8202140F2044500F0ACF839 -:1014B0002C492D48D1F880202C4C42F003022C4BB3 -:1014C000C1F88020C0F86051226813401BB9D0F8E1 -:1014D000A8319A071AD0244B4FF00041234A516398 -:1014E0001A46D3F8401141F00201C3F84011D2F876 -:1014F00040319B07FBD44FF400301E491B4B4FF08B -:101500000042086019209A6300F09AF81A4D0022F0 -:10151000164B4FF08041144C0A26996328461A60F6 -:101520001146C4F8A8614FF4207200F07DF84FF422 -:1015300081064FF4800040F24313104A10492E6098 -:101540002864C4F85851C2F80412C4F848310D4A4E -:101550004FF4003101231160C4F8403170BD00BF69 -:1015600000800D4000C00F4000002E4000900D4054 -:10157000001C1E008CE200E0003000200010002063 -:10158000F90600000CE100E0114B1249D86E0A4642 -:1015900040F4403030B4D86640F2B765D86EA0242D -:1015A00040F44070D8664D648C64936C1B06FCD488 -:1015B000094B40F2B760A021064A58649964936CC5 -:1015C00013F08003FBD1054A137030BC704700BF95 -:1015D00000C00F4000400C4000800C40A10D0020D6 -:1015E000F8B500BFF8BC08BC9E467047FFFFFFFF80 -:1015F0005FF800F0390500005FF800F055160000B4 -:101600005FF800F0D90100005FF800F0F90D00006C -:101610005FF800F0350500005FF800F06913000086 -:101620005FF800F0010100005FF800F0A51600006F -:101630005FF800F0B91100005FF800F0050100004C -:101640005FF800F05912000045000000FFFFFFFFA7 -:10165000000000000000000000000000000000008A -:10166000000000000000000000000000000000007A -:1016700010B5054C237833B9044B13B10448AFF3CC -:1016800000800123237010BDD00300200000000063 -:1016900054170000084B10B51BB108490848AFF3B8 -:1016A00000800848036803B910BD074B002BFBD02E -:1016B000BDE81040184700BF00000000D403002020 -:1016C00054170000D00300200000000008B5174B9D -:1016D0000121187800F00AFA154B0121187800F062 -:1016E00005FA144B0121187800F000FA124B012181 -:1016F000187800F0FBF9114B0121187800F0F6F989 -:101700000F4B0121187800F0F1F90E4B00211878E9 -:1017100000F0ECF90C4B0021187800F0E7F90B4BC6 -:1017200000211878BDE8084000F0E0B9F40200207C -:10173000FC0200200003002010030020080300200A -:10174000F8020020EC030020EC020020F002002050 -:10175000FFF7BCBF2C4A2D4B2DE9F04714682C4AEB -:10176000DFF8B8A012685746DFF8B490294EDFF8CA -:10177000B480294D93FBF4F393FBF2F4012199F823 -:10178000000000F0ADF9DAF800103268A5FB023273 -:10179000920C04FB02F2DAF800305B1A9A42FAD893 -:1017A000012198F8000000F09BF939683268A5FB28 -:1017B0000232920C04FB02F2DAF800305B1A9A4211 -:1017C000FAD8002199F8000000F08AF939683268E7 -:1017D000A5FB0232920C04FB02F2DAF800305B1A2D -:1017E0009A42FAD8002198F8000000F079F9396897 -:1017F0003268A5FB0232920C04FB02F2DAF80030E8 -:101800005B1A9A42FAD8B9E70C03002000879303C9 -:10181000040300201803002083DE1B43041000E0B3 -:1018200008030020F80200208C4A8D4B90422DE9DD -:10183000F0438C4D5C699969EF681DD98A4B9842D9 -:1018400040F20181894B40F22766DFF860E20344F1 -:10185000874D1A0AAEFB0232D30903EB830303EB75 -:10186000830202F2E243B34228BF3346A3F5487332 -:10187000A5FB0336F60804E07E4EB04294BF062670 -:101880000E26774A07F01F0ED2F88030B64543F097 -:10189000C003C2F880300AD2724B27F01F071A46E5 -:1018A0003743DF601368002BFCDA07F01F0E14F0DB -:1018B00000732ED1704D714AD5F810C015460CEA50 -:1018C0000202AA420ABF4FF0C0534FF480520022D6 -:1018D00084EA030515F0605F06D024F0605403F03D -:1018E00060535F4D1C436C6181EA020313F4405F57 -:1018F00008D05B4B21F4405111431A469961936C17 -:101900001D07FCD444F00074554A5461936C990649 -:10191000FCD401215A4D0A4601FB02F300FB03F3FC -:10192000AB4209D8072A00F28480013201FB02F39E -:1019300000FB03F3AB42F5D95248534D1844A5FBC5 -:101940000030030D6C2B79D8352B7ED8DFF8608101 -:1019500036234E48DFF820C14D4DDCF80090B0FB37 -:10196000F2F009EA05054545B0FBF1F00BD043F470 -:1019700000534FF480586546CCF80080CCF8003016 -:101980002B68002BFCDADFF8D8C0013ADCF8103005 -:1019900003F00703934207D002F007026546CCF834 -:1019A0001020AB6CDB03FCD40139890284EA01030B -:1019B00013F4E05F0AD02A4B24F4E05401F4E05120 -:1019C0001A460C435C61936C9907FCD4314B324945 -:1019D0000344DB09A1FB0331090B042928BF0421BF -:1019E0004B1E1B0284EA030212F4407F06D024F44B -:1019F000407403F440731A4A1C435461184B24F09A -:101A000000741A465C61936C9B06FCD4B0FBF1F148 -:101A1000224A7645224B1060196008D2114B27F0FC -:101A20001F071A463743DF601368002BFCDABDE856 -:101A3000F083042980D8013101226DE7DFF874803A -:101A40006C23184886E712261BE71748DFF86880E2 -:101A500000FB03F043EA08087CE700BF00A4781FFE -:101A600000C00F40000008400046C32300BA3CDC21 -:101A70001F85EB5100366E0100800D404030008024 -:101A8000FFB19F26808D5B00819F5E1600B29F266E -:101A90007F3001807FD1F0089F10E50018030020FF -:101AA0001403002000643F4D001BB70023B24C001C -:101AB000362000806C200080002000800001074B51 -:101AC0001A181B58D2685868104202D011B9C3F8CE -:101AD00088207047C3F88420704700BF00000020B2 -:101AE000272801D8FFF7EABF704700BF27281CD876 -:101AF00000011A4A012902EB0003105810B415D056 -:101B0000042913D0DC68426822EA040242609A6821 -:101B1000E9B102291ED003295B685DF8044B0CBFB4 -:101B20000F491049116015221A607047DA680129BF -:101B3000446842EA040242609A6808D040F63801DC -:101B400011605B6815225DF8044B1A6070473821FC -:101B50001160F6E704491160F3E700BF00000020C0 -:101B6000383001003800010038F0010004207146CF -:101B7000084203D0EFF3098000F008B8EFF30880C3 -:101B800000F004B8704700BF704700BF1B4B052131 -:101B90001B4A382030B5C2F848110821C2F8380372 -:101BA00083B05A68174C0A4317485A60C3F8841028 -:101BB000C3F888102368834202D91448FFF734FE23 -:101BC0000E4D08240020124A1249C5F884400190A5 -:101BD000019B934205D8019B01330193019B9342E2 -:101BE000F9D9C5F888400190019B8B42EDD8019B43 -:101BF00001330193019B8B42F9D9E6E700C01B40FA -:101C000000801F4018030020FF45C32300A3E111FB -:101C10003F420F003F548900836B30B41BB15368BF -:101C200043F40043536072B6446B9CB1104B226086 -:101C3000D3F8B0410C4217D1D3F8404144F480446A -:101C4000C3F84041D3F8B851D3F840416404F3D508 -:101C5000294209D10023064C8260C360D4F8B03118 -:101C60001943C4F8B0110263426362B630BC7047D6 -:101C700000002E4038B50546036B06E0AA6B1C68D1 -:101C800090476A6B2346944208D0184633B1012B23 -:101C900004D05A681206F1D52B6338BD00232B639C -:101CA0006B6338BDF0B5F1B9224C23490020234BBA -:101CB00001228025A1600A601A464D60E060D3F8D9 -:101CC000BC41886044F001141D4DC3F8BC41D3F8F9 -:101CD000B01141F00101C3F8B0112860D2F8B03161 -:101CE000002BFBD1F0BD0904164B144D0126114CFD -:101CF00041F0800100221E60596000F5805CE264C2 -:101D000000F5005EA36400F54057D5F8B01100F56A -:101D100080462A46986041F48031C3F80CC0C3F86D -:101D200010E05F619E61C5F8B011D2F8B031002BB0 -:101D3000FBD1BAE7003000202020002000002E4018 -:101D40000C040020002000202DE9F04FBB4C83B094 -:101D5000D4F8448118F0010FC4F844815ED0D4F85F -:101D6000AC31002B55D04FF00119DFF8F0B2264608 -:101D7000B34FCA46B96AFA6AC4F8AC31D4F84031F4 -:101D800043F40053C4F84031D4F840319D04F5D5F4 -:101D9000D6F8403123F40053C6F84031C6F8B4A158 -:101DA000D4F8B43113F00113FAD188B240F281654E -:101DB000CBF80030A84200F29680B0F5D06F80F0EA -:101DC000D881B0F5817F00F0578100F2D58080285E -:101DD00000F02681822840F0C78092B202F07F0195 -:101DE000072900F2C18089009648974D0844016890 -:101DF00010062B706B7040F1E581C90301D50123FA -:101E00002B7002219048FFF74DFFD4F8AC31002B26 -:101E1000B0D18A4AD2F8BC31002B44D118F0400F1F -:101E200018D0864BD3F8AC111A46C3F8AC11D3F8CE -:101E3000BC11C3F8BC11D2F8B041804B002CFAD1D0 -:101E40004FF0FF32C3F8B421D3F8843100F074FBB3 -:101E50007E4B1C6018F0807F03D07D4B1B6803B164 -:101E6000984718F0007F03D07A4B1B6803B198475E -:101E700018F0040F02D0714BD3F884316F4BD3F8B4 -:101E8000482112060CD518F0800F09D072490A7843 -:101E9000002A00F08E81531EDBB20B7003B9FBBE2B -:101EA00003B0BDE8F08F6D49C2F8BC310868034249 -:101EB00040F0CC81654A14681C40AFD0630700F144 -:101EC000E281670300F1EF81260700F1E881250335 -:101ED00000F1E181E00600F1DA81E1029ED560487F -:101EE000FFF7C8FE9AE742F22105A84238D06FD822 -:101EF000B0F5086F00F02F81B0F5106F34D1C1F349 -:101F00000741584A584811705849594A0193C6F830 -:101F1000C801C6F8CC11C6F8D02100F00FFB554A15 -:101F2000019B80210120FB6410605160D6F8BC1138 -:101F3000936041F00111BA64C6F8BC11D6F8B02123 -:101F400042F48032C6F8B021444A1360D4F8B0316C -:101F5000002BFBD1D4F8AC31002B7FF40BAF58E74A -:101F6000100C072800F03281C4F8C091D4F8AC31CD -:101F7000002B7FF4FFAE4CE740F20235A84200F0A0 -:101F8000B780B0F5A06FEFD13A4A80200125FB64FD -:101F900050601560D6F8BC01936040F00113BA643C -:101FA000C6F8BC31D6F8B03143F48033C6F8B0314E -:101FB000D4F8B031002BFBD10B0C5B0643F08073DF -:101FC000C6F85431D4F8AC31002B7FF4D3AE20E7FF -:101FD00042F22123984200F0828042F2213398425B -:101FE000C2D1244B01218022196000215A60D4F80B -:101FF000BC21BB6442F001129960F964C4F8BC21B1 -:10200000D4F8B03143F48033C4F8B031D4F8B031EF -:10201000002BFBD1D4F8AC31002B7FF4ABAEF8E64B -:10202000094A0221104613705370FFF73BFED4F8A3 -:10203000AC31002B7FF49EAEEBE600BF00002E40DB -:1020400000300020C0012E402004002004040020A5 -:102050000004002008040020100400200C040020CC -:1020600040320020300400200200CC00C8000200F2 -:102070000200C8002020002092B202F07F03072B4C -:102080003FF672AF12F0800F4FEA8303884A4FF099 -:1020900001011A44136814BF23F4803323F00103B1 -:1020A00013608022834B196000215A60D4F8BC2150 -:1020B000BB6442F001129960F964C4F8BC21D4F801 -:1020C000B03143F48033C4F8B031D4F8B031002BD0 -:1020D000FBD1D4F8AC31002B7FF44CAE99E6764BB3 -:1020E000C1F30741754A1868754B1060197078E79D -:1020F00092B202F07F03072B3FF636AF12F0800F4B -:102100004FEA83036A4A4FF001011A44136814BF6F -:1021100043F4803343F0010313608022654B196060 -:1021200000215A60D4F8BC21BB6442F001129960CE -:10213000F964C4F8BC21D4F8B03143F48033C4F856 -:10214000B031D4F8B031002BFBD1D4F8AC31002B36 -:102150007FF410AE5DE65B4A01215B4B12781846B6 -:102160001A70FFF79FFDD4F8AC31002B7FF402AE5C -:102170004FE6564B586800283FF4F6AE090C1FFA9C -:1021800082FE04E00C33586800283FF4EDAE1D8851 -:102190008D42F7D15D887545F4D1090A120C0329E7 -:1021A0000CBF01781989914228BF1146FFF77AFDCB -:1021B0002BE6D3F8482122F08002C3F8482103B06F -:1021C000BDE8F08FCA077FF51CAE18E6404D012030 -:1021D000FB6029603F4B4049186059603F49D6F881 -:1021E000B0016A6001F5005E40F00102A1F5005007 -:1021F00001F58055BB609860A1F58050D860C6F8A5 -:10220000B02119615D61C3F818E0D4F8B031002B3A -:10221000FBD1284A012048F28001FB6410604FF492 -:1022200080305160D6F8BC11936041F00113BA645C -:10223000C6F8BC31D6F8B0310343C6F8B031CBF89C -:102240000000D4F8B031002BFBD1DEE5204C42F287 -:102250002100002524880D6084427FF42BAE2049A4 -:10226000204C03C90D0C86282060A180A5717FF445 -:1022700021AED2F8481150241B4841F08001C2F829 -:102280004811047016E61948FFF7F4FC19E61848DF -:10229000FFF7F0FC21E61748FFF7ECFC1AE61648BA -:1022A000FFF7E8FC13E61548FFF7E4FC0CE600BF77 -:1022B000C0012E4020200020A80D002088320020E0 -:1022C000800D002030040020200400208002002027 -:1022D000180400200020002080000700282400208F -:1022E000280400208032002010040020003100204B -:1022F00000320020C0310020803100204031002019 -:10230000002AA0F102022DE9F04714BF00274FF088 -:102310000057022A01D9BDE8F0874FEAC01ADFF85A -:1023200040900D4604460AEB0906002140229846DB -:10233000304600F0DFFC012047EA05414AF8091069 -:10234000C6F83880B060B8F1000FE4D0034BA0406D -:102350001C6820431860BDE8F08700BF040400201B -:1023600000300020002AA0F102022DE9F04714BF3E -:1023700000274FF00057022A01D9BDE8F08740221C -:10238000C501DFF8449088461544002104461E46E6 -:1023900005EB090A504600F0ADFC012247EA08416E -:1023A00045F80910CAF83860CAF80820002EE4D0B1 -:1023B00004F11000034B8240186802431A60BDE824 -:1023C000F08700BF04040020003000201204816068 -:1023D000C36142F08002F0B44260012701F58056EB -:1023E00001F5005501F5405401F580420760C660D3 -:1023F000056144618261F0BC704700BF831E022BFF -:1024000000D9704730B4064B00F1100401250A468C -:1024100003EBC01005FA04F130BCFFF7FDBB00BFB1 -:1024200040300020831E022B00D9704710B4054BAA -:1024300001240A4604FA00F103EBC0105DF8044BD6 -:10244000FFF7EABB00300020124A134BD2F82002FB -:1024500020F07F40984210B584B002D800EB800095 -:1024600040000E4C01A90A2200F07EFA01A90023C7 -:10247000204611F8012B01333AB10A2B20F8022F24 -:10248000F7D11623237004B010BD5B00DBB22370BC -:1024900004B010BD00441F407F969800B403002094 -:1024A0004368C269C3F30E43054930B4C3F1400326 -:1024B000044C002521F8123024F8125030BC70472B -:1024C000000C0020F80B0020F8B5154B1B783BB929 -:1024D00003F0FF04134B1B7813B1134D2A8802B984 -:1024E000F8BD124F2346124EC2F58072397811485A -:1024F00006EB411600EB01213046FFF767FF31463E -:102500000420FFF77BFF3B780133DBB2062B98BF3B -:102510003B704FF0000388BF3C702B80F8BD00BFBC -:10252000340B002030040020800C0020350B0020EC -:10253000A00C002034040020704700BF0021E022DE -:102540002048F8B50C46204E204D00F0D3FB204F1C -:102550002146204B6022347028461F4E1C8000F01C -:10256000C9FB23462246102102203C60BC80346017 -:10257000B480FFF7F7FE2246184B40210320FFF7F7 -:10258000BFFE2346224640210420FFF7EBFE2346F0 -:10259000402228461249FFF719FF29460320FFF77A -:1025A00041FF104B4A22104910480860C3F884408C -:1025B000C3F88020D3F8482142F08072C3F8482144 -:1025C000F8BD00BFA00C0020350B0020200C00201F -:1025D000000C0020800C0020F80B0020510E0000A1 -:1025E000380B002000002E4000040020790E00006F -:1025F000024A034B10881B88C01A7047000C002049 -:10260000F80B002010B4EFF3108272B6437F33B999 -:10261000017F012908D0032910D00123437702B993 -:1026200062B65DF8044B7047114C2168A1B11149A5 -:1026300043610B68086083615861EEE70E4C2168C6 -:1026400081B10E4943610B680860836158610C4B8E -:102650004FF080511960E0E7064B416181612060D5 -:102660001860DAE7054B4161816120601860EEE790 -:10267000940D0020900D0020840D0020880D002076 -:1026800004ED00E010B4047F4160022CC26003D06E -:102690005DF8044BFFF7B6BF83685DF8044B18473D -:1026A00070B5EFF3108172B60C4C23688BB10C4EF1 -:1026B00000255A6922607AB1956101B962B65D77E9 -:1026C00018469B689847EFF3108172B62368002B79 -:1026D000EFD101B962B670BD3260EEE7840D002023 -:1026E000880D0020FFF7DCBF184A30B41468002CB6 -:1026F00028D0036821688B420FD2CB1A0021846056 -:10270000C1602360E0601060022330BC0375704735 -:102710000360144611688B4208D3A2685B1A002A32 -:10272000F6D18260C4600360A060EDE7D568CB1A83 -:1027300082600222C560E060C16888602360027523 -:1027400030BC70478460C4601060DDE78C0D0020F1 -:10275000F8B5224E34682CB32368002B3AD11D46BD -:102760001F4F04E03468ECB12368002B32D1A3681A -:1027700003B1DD6020693360036825751B68BB42C7 -:1027800021D1037F4560022BC46020D0FFF73AFFC0 -:102790006368002BE6D023602046FFF7A5FF34686E -:1027A000002CE1D1EFF3108372B60E4A00211068BD -:1027B000116003B962B628B18468FFF795FF20461F -:1027C0000028F9D1F8BD224600219847E0E7836848 -:1027D0009847DDE7013B2360E4E700BF8C0D002054 -:1027E000351000009C0D0020044A054B1168054A75 -:1027F0001960136801331360FFF7AABF041000E0EB -:10280000A40D0020A80D002070B5214C237883B9B9 -:10281000204B01221B7822701BBB1F4B1B78002B07 -:1028200029D11E4B00211A68217012B1EFF30582E5 -:1028300002B170BDEFF3108072B61A68F2B1184C95 -:102840002178D9B90126556926701D60D5B1A961D5 -:1028500000B962B6002593681046557798472570F1 -:1028600070BDFFF7C5FE0028D7D000F015FA0A4B5F -:102870001B78002BD5D000F0FBF9D2E70028D8D187 -:1028800062B670BD074B1D600028E3D1E1E700BFD1 -:10289000A00D0020CA030020C80D0020940D0020C8 -:1028A000980D0020900D0020002852D02DE9F04F07 -:1028B000814683B0274C0120274D284E54E8003F25 -:1028C0002A68316844E80003002BF7D1244F4FF405 -:1028D0007A7E2448D7F800C0BB4607F1C647036894 -:1028E000C1EB0C0107F5DE1707F67F67A7FB03C3F3 -:1028F000BA4601279B0CB1FBF3F30EFB023854E8F8 -:10290000003F2A68316844E80073002BF7D1DBF8F8 -:1029100000C04FF47A7E03680EFB02F2C1EB0C019B -:10292000AAFB033EC8EB02034FEA9E42B1FBF2F161 -:10293000CA18B2F57A7F07D3B9F1010908F57A7898 -:10294000DDD103B0BDE8F08F0190FFF75DFF019886 -:10295000D5E770478C320020A80D0020A40D002080 -:10296000041000E018030020F0B44E1E0025374686 -:1029700000E00135B0FBF2F302FB130000F1370475 -:10298000092800F13000E4B298BFC4B2184607F835 -:10299000014F002BEDD14A1953704DB1013316F898 -:1029A000014F1778E81A3770834202F80149F5DBC6 -:1029B0000846F0BC704700BFA4484FF00F0CA44B72 -:1029C000826F42F47F02F0B582670025D0F8802044 -:1029D0004FF470469F4C4FF4604E29464FF4806789 -:1029E00014432A46C0F88040A3F88C6148F2B82608 -:1029F000A3F88EC1A3F89051B3F8880180B240F0DB -:102A0000F000A3F8880101EB4100914B0131002552 -:102A100040011C4604290344A3F804E0DF805A84E3 -:102A20001A865A805A81DE815A82DA825A83DA8380 -:102A3000E9D1B4F888014FF00F0C874B4FF4704682 -:102A400080B229464FF460472A4640EA0C004FF412 -:102A5000806EA4F88801B4F8880180B240F47060F8 -:102A6000A4F88801A3F88C6148F2B826A3F88EC1B7 -:102A7000A3F89051B3F8880180B240F0F000A3F8B9 -:102A8000880101EB4100744B0131002540011C46D7 -:102A9000042903449F80A3F806E05A841A865A80CA -:102AA0005A81DE815A82DA825A83DA83E9D1B4F814 -:102AB00088014FF00F0C694B4FF4704680B22946E5 -:102AC0004FF460472A4640EA0C004FF4806EA4F8A9 -:102AD0008801B4F8880180B240F47060A4F88801DD -:102AE000A3F88C6148F2B826A3F88EC1A3F89051E0 -:102AF000B3F8880180B240F0F000A3F8880101EB40 -:102B00004100564B0131002540011C460429034475 -:102B10009F80A3F806E05A841A865A805A81DE8183 -:102B20005A82DA825A83DA83E9D1B4F888014FF005 -:102B30000F0C4B4B4FF4704780B229464FF4604660 -:102B40002A4640EA0C004FF4806EA4F88801B4F8DD -:102B5000880180B240F47060A4F88801A3F88C71F9 -:102B600048F2B827A3F88EC1A3F89051B3F88801B2 -:102B700080B240F0F000A3F8880101EB4100384B2F -:102B8000013140011C46042903449E80A3F806E05D -:102B90005A841A865A805A81DF815A82DA825A838D -:102BA000DA83EAD1B4F888310F27002241F2010616 -:102BB0009BB245F6C05E114643F226053B43A4F89E -:102BC0008831B4F888319BB243F47063A4F888313B -:102BD0005001244B01320344042A99815981DF8139 -:102BE0009E82A3F806E0198019829D81F0D100220F -:102BF0000F2741F2010645F6C055114643F226045F -:102C00005001194B01320344042A99815981DF8113 -:102C10009E82DD80198019829C81F1D100220F27CC -:102C200041F2010645F6C055114643F22604500113 -:102C30000E4B01320344042A99815981DF819E821F -:102C4000DD80198019829C81F1D1F0BD00C00F4058 -:102C500000C03D40000003FC00003E4000403E40FC -:102C600000803E4000C01D4000001E4000401E404D -:102C700038B5074B1C784CB1064D55F8043F002B76 -:102C8000FBD09847631E13F0FF04F6D138BD00BF98 -:102C9000C80D0020A80D0020014B00221A707047BB -:102CA000CA03002070B50F4E0F4D761BB61018BF2B -:102CB000002405D0013455F8043B9847A642F9D1C9 -:102CC0000A4E0B4D761B00F063F8B61018BF0024B7 -:102CD00006D0013455F8043B9847A642F9D170BD9F -:102CE00070BD00BF48160060481600604C160060BA -:102CF0004816006070B4840746D0541E002A41D0A4 -:102D0000CDB2034602E0621EE4B3144603F8015B51 -:102D10009A07F8D1032C2ED9CDB245EA05250F2C00 -:102D200045EA054519D903F110022646103E0F2E3B -:102D300042F8105C42F80C5C42F8085C42F8045C13 -:102D400002F11002F2D8A4F1100222F00F0204F0F6 -:102D50000F041032032C13440DD91E462246043AA8 -:102D6000032A46F8045BFAD8221F22F00302043239 -:102D7000134404F003042CB1C9B21C4403F8011B32 -:102D8000A342FBD170BC704714460346C2E700BFA4 -:102D90005FF800F0E1150060000000000000000096 -:042DA000F8B500BFC3 -:102DA40000000042C8801F40B8821F400800000095 -:102DB40000000042C4801F40B4821F400400000091 -:102DC40000C0004224801F4014821F4010000000F5 -:102DD40000C0004228801F4018821F4020000000CD -:102DE40000C000422C801F401C821F404000000095 -:102DF40000C0004234801F4024821F4000010000B4 -:102E04000040004264811F4054831F4000040000BE -:102E14000040004280811F4070831F400000020078 -:102E2400004000427C811F406C831F400000010071 -:102E34000040004268811F4058831F400008000082 -:102E4400004000423C811F402C831F4001000000D1 -:102E54000040004244811F4034831F4004000000AE -:102E64000040004240811F4030831F4002000000A8 -:102E74000040004248811F4038831F400800000082 -:102E84000000004204811F40F4821F40000004003F -:102E94000000004208811F40F8821F400000080023 -:102EA4000000004218811F4008831F40000080007A -:102EB4000000004214811F4004831F4000004000B2 -:102EC4000000004200811F40F0821F400000020009 -:102ED40000000042FC801F40EC821F400000010003 -:102EE4000000004224811F4014831F40000000049E -:102EF4000000004228811F4018831F400000000882 -:102F0400000000421C811F400C831F400000000190 -:102F14000000004220811F4010831F400000000277 -:102F240000000042EC801F40DC821F4000100000C3 -:102F340000000042F0801F40E0821F40002000009B -:102F44000000004234811F4024831F4000000040E1 -:102F54000000004238811F4028831F400000008089 -:102F64000080004294801F4084821F4000000400BF -:102F740000C0004290801F4080821F4000000080FB -:102F840000800042A8801F4098821F4000008000FB -:102F940000800042A4801F4094821F400000400033 -:102FA400004000426C811F405C831F400010000001 -:102FB40000C0004230801F4020821F40800000007B -:102FC40000800042C8811F40B8831F400080000079 -:102FD40000800042C4811F40B4831F4000400000B1 -:102FE40000800042C0811F40B0831F4000200000C9 -:102FF40000800042BC811F40AC831F4000100000D1 -:1030040000800042D0811F40C0831F4000000200A6 -:1030140000800042CC811F40BC831F40000001009F -:1030240000010000840300201200000000060000DC -:103034001C0300200A0000000002000040030020DE -:10304400430000000007000040030020430000008C -:1030540000030000B0030020000000000103090485 -:103064002803002000000000020309049803002044 -:103074000000000003030904B40300200000000062 -:10308400000000000000000000000000010000003B -:1030940002000000170000001200000016000000EB -:1030A400150000001E0000001300000000200000B6 -:1030B400140000000029DE07007B9A170A060002AC -:1030C4000200004001000000180354006500650080 -:1030D4006E00730079006400750069006E006F0073 -:1030E40009024300020100C0320904000001020287 -:1030F4000100052400100105240101010424020635 -:1031040005240600010705820310001009040100CC -:10311400020A0000000705030240000007058402BC -:10312400400000001201000202000040C0168304A7 -:103134007902010203010000160355005300420006 -:103144002000530065007200690061006C000000FB -:10315400040309040C030000000000000000000048 -:10316400000000000000000000000100000000005A -:040000056000100087 -:00000001FF diff --git a/tools/odrive/tests/encoder_pass_through.ino.hex b/tools/odrive/tests/encoder_pass_through.ino.hex deleted file mode 100644 index 2f0798bf..00000000 --- a/tools/odrive/tests/encoder_pass_through.ino.hex +++ /dev/null @@ -1,792 +0,0 @@ -:0200000460009A -:100000004643464200000156000000000101020084 -:1000100000000000000000000000000000000000E0 -:1000200000000000000000000000000000000000D0 -:1000300000000000000000000000000000000000C0 -:1000400000000000010403000000000000000000A8 -:100050000000200000000000000000000000000080 -:100060000000000000000000000000000000000090 -:100070000000000000000000000000000000000080 -:10008000EB04180A063204260000000000000000FD -:10009000050404240000000000000000000000002F -:1000A0000000000000000000000000000000000050 -:1000B0000604000000000000000000000000000036 -:1000C0000000000000000000000000000000000030 -:1000D00020041808000000000000000000000000DC -:1000E0000000000000000000000000000000000010 -:1000F0000000000000000000000000000000000000 -:10010000D8041808000000000000000000000000F3 -:100110000204180804200000000000000000000095 -:1001200000000000000000000000000000000000CF -:10013000600400000000000000000000000000005B -:1001400000000000000000000000000000000000AF -:10015000000000000000000000000000000000009F -:10016000000000000000000000000000000000008F -:10017000000000000000000000000000000000007F -:10018000000000000000000000000000000000006F -:10019000000000000000000000000000000000005F -:1001A000000000000000000000000000000000004F -:1001B000000000000000000000000000000000003F -:1001C000000100000010000001000000000000001D -:1001D000000001000000000000000000000000001E -:1001E000000000000000000000000000000000000F -:1001F00000000000000000000000000000000000FF -:10020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -:10021000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE -:10022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE -:10023000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE -:10024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE -:10025000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE -:10026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E -:10027000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E -:10028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E -:10029000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E -:1002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E -:1002B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E -:1002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E -:1002D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E -:1002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E -:1002F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E -:10030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD -:10031000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED -:10032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD -:10033000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD -:10034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD -:10035000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD -:10036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D -:10037000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D -:10038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D -:10039000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D -:1003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D -:1003B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D -:1003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D -:1003D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D -:1003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D -:1003F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D -:10040000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC -:10041000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC -:10042000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC -:10043000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC -:10044000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC -:10045000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC -:10046000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C -:10047000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C -:10048000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C -:10049000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C -:1004A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C -:1004B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C -:1004C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C -:1004D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C -:1004E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C -:1004F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C -:10050000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB -:10051000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB -:10052000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB -:10053000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB -:10054000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB -:10055000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB -:10056000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B -:10057000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B -:10058000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B -:10059000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B -:1005A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B -:1005B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B -:1005C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B -:1005D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B -:1005E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B -:1005F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B -:10060000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA -:10061000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA -:10062000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA -:10063000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA -:10064000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA -:10065000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA -:10066000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A -:10067000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A -:10068000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A -:10069000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A -:1006A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A -:1006B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A -:1006C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A -:1006D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A -:1006E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A -:1006F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A -:10070000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 -:10071000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9 -:10072000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9 -:10073000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9 -:10074000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9 -:10075000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9 -:10076000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99 -:10077000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89 -:10078000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79 -:10079000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69 -:1007A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59 -:1007B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49 -:1007C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39 -:1007D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29 -:1007E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19 -:1007F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09 -:10080000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8 -:10081000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8 -:10082000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8 -:10083000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8 -:10084000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8 -:10085000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8 -:10086000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98 -:10087000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88 -:10088000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78 -:10089000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68 -:1008A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58 -:1008B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48 -:1008C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38 -:1008D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28 -:1008E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18 -:1008F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08 -:10090000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7 -:10091000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7 -:10092000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7 -:10093000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7 -:10094000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7 -:10095000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7 -:10096000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97 -:10097000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87 -:10098000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77 -:10099000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67 -:1009A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57 -:1009B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47 -:1009C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37 -:1009D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27 -:1009E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17 -:1009F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07 -:100A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6 -:100A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6 -:100A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6 -:100A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6 -:100A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6 -:100A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6 -:100A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96 -:100A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86 -:100A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76 -:100A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66 -:100AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56 -:100AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46 -:100AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36 -:100AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26 -:100AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16 -:100AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06 -:100B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5 -:100B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5 -:100B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5 -:100B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5 -:100B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5 -:100B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5 -:100B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95 -:100B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85 -:100B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75 -:100B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65 -:100BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55 -:100BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45 -:100BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35 -:100BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25 -:100BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15 -:100BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05 -:100C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4 -:100C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4 -:100C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4 -:100C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4 -:100C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4 -:100C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4 -:100C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94 -:100C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84 -:100C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74 -:100C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64 -:100CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54 -:100CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44 -:100CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34 -:100CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24 -:100CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14 -:100CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04 -:100D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 -:100D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3 -:100D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3 -:100D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3 -:100D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3 -:100D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3 -:100D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93 -:100D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83 -:100D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73 -:100D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63 -:100DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53 -:100DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43 -:100DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33 -:100DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23 -:100DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13 -:100DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03 -:100E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2 -:100E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2 -:100E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2 -:100E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2 -:100E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2 -:100E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2 -:100E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92 -:100E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82 -:100E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72 -:100E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62 -:100EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52 -:100EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42 -:100EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32 -:100ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22 -:100EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12 -:100EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02 -:100F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1 -:100F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1 -:100F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1 -:100F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1 -:100F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1 -:100F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1 -:100F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91 -:100F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81 -:100F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71 -:100F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61 -:100FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51 -:100FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41 -:100FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31 -:100FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21 -:100FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11 -:100FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01 -:10100000D10020402C100060000000000000000013 -:1010100020100060001000600000000000000000D0 -:1010200000000060403100000000000000000120CE -:1010300035100060764B0720764C4FF42A01764A33 -:101040005C64186499639546744A75498A420FD066 -:10105000744B9A420CD2D4430846234423F0030332 -:1010600004330B4450F8041B984242F8041BF9D196 -:101070006D4A6E498A420FD06D4B9A420CD2D443CE -:101080000846234423F0030304330B4450F8041BA5 -:10109000984242F8041BF9D1664A674B9A420BD238 -:1010A000D04311460024034423F0030304331344C4 -:1010B00041F8044B8B42FBD1604A4FF47001604B06 -:1010C000116003F530715F4A43F8042F9942FBD158 -:1010D0005D4B80215D4A03F8011B9342FBD15C4CC0 -:1010E0004FF080375B4E4FF0FF32DFF8A8A10025AC -:1010F000DFF8A491DFF8A481574B4549C3F800A05D -:10110000C4F80471C4F80091C4F8F470C4F8F08015 -:10111000F36923F07F0343F04003F361736A23F024 -:101120007F0343F0400373628A66CA660A674A67B0 -:1011300000F0B6F8494A6320494B4A49106003223F -:101140001D60CAF8381043F8082C4749474A4848F8 -:10115000C3F8082D0B68474A43F08073CAF83C0077 -:1011600045480B601368454943F001031360036869 -:101170000B6000F0E5F8C4F804714148C4F8009130 -:10118000C4F8F470C4F8F08000F036FA00BF00BF75 -:1011900000BF00BFF16E3B4A41F440513A4BF1664B -:1011A0001560C2F80851C2F81851C2F82851C2F8A7 -:1011B00038519A6BD20708D442F615623349596503 -:1011C0001A659A6B42F001029A632F4A304C936879 -:1011D00043F00113936000F033FA2368132BFCD91A -:1011E00000F05CF900F0D0F900F022FA00F0DAF833 -:1011F00000F006FA2368B3F5967FFBD300F010FAEF -:1012000000F012FA00F008FA00F016FAFAE700BF50 -:1012100000C00A40ABAAAAAA008007200000000074 -:10122000501600602017000000000020742D0060A0 -:10123000D0030020D0030020C032002088ED00E061 -:10124000FC0F00206102000000E400E0A0E400E0E8 -:1012500000800D4000C00F4008ED00E014E000E009 -:1012600018E000E0D90D0000FCED00E000002020B7 -:10127000DD0E0000001000E0041000E0A40D0020CE -:101280000046C3230040084000400D400000C05607 -:10129000A80D0020001000201B1018200C0D1113A9 -:1012A000F0B5194A0021194B4FF0100E18480124CF -:1012B000184E194D0160194FC2F800E01E6015600C -:1012C000174E184D1F601660174F1D60174E184DB2 -:1012D00017601E60174F1560174E184D1F6016607F -:1012E0001D600460BFF34F8FBFF36F8FC3F8B01161 -:1012F000BFF34F8FBFF36F8F52F8883C43F44033F6 -:1013000042F8883CF0BD00BF9CED00E0A0ED00E09D -:1013100094ED00E0250008031100200021000207E1 -:1013200012000020250008131300202027000B13B3 -:101330001400004033001013150000602F000B074D -:10134000F0B4174A40274FF480314FF480564FF4E1 -:1013500000554FF4404443F24200136913F0020F6A -:1013600006D0946151619061136913F0020FF8D1B6 -:1013700013F4005F01D15561EFE713F4805F01D1F1 -:101380005661EAE7002BE8DA13F4803F01D091615F -:10139000E3E75B0601D45761DFE7F0BC704700BFAD -:1013A00000800D40364A03203649F3EE096A13687F -:1013B00023F00103F0B51360C2F89000D1F8E030DB -:1013C000DFF8D4E0DCB2C3F30B202F4FC0EB135394 -:1013D00007EE904AA4F15501CEF80040B8EEE77A46 -:1013E00003EB830407EE900A03FB01F13B6003EB80 -:1013F0008313B8EEE75A07EE901A091B77EE666A78 -:10140000B8EE677A214D07EE901A0B44C5ED006ADD -:10141000F8EE677A1E4EC7EE265A1E4930601068F5 -:1014200087EEA66A07EE903AF8EE677A87EEA67A1C -:1014300075EE855A76EE056AFCEEE55AFCEEE66A34 -:1014400015EE903A40EA035316EE900A77EE057ACD -:10145000136001EA0041D2F81031FCEEE77A0B4349 -:10146000C2F8103117EE903AD2F81011C3F30B0303 -:101470000B43C2F81031136843F002031360F0BD50 -:1014800080810D4000441F40F8030020F403002039 -:10149000FC0300200000FF0FF0030020304B40F65B -:1014A000617270B5C3F8202140F2044500F0B0F835 -:1014B0002C492D48D1F880202C4C42F003022C4BB3 -:1014C000C1F88020C0F86051226813401BB9D0F8E1 -:1014D000A8319A071AD0244B4FF00041234A516398 -:1014E0001A46D3F8401141F00201C3F84011D2F876 -:1014F00040319B07FBD44FF400301E491B4B4FF08B -:101500000042086019209A6300F072F81A4D002218 -:10151000164B4FF08041144C0A26996328461A60F6 -:101520001146C4F8A8614FF4207200F06DF84FF432 -:1015300081064FF4800040F24313104A10492E6098 -:101540002864C4F85851C2F80412C4F848310D4A4E -:101550004FF4003101231160C4F8403170BD00BF69 -:1015600000800D4000C00F4000002E4000900D4054 -:10157000001C1E008CE200E0003000200010002063 -:101580003D0400000CE100E0114B1249D86E0A4600 -:1015900040F4403030B4D86640F2B765D86EA0242D -:1015A00040F44070D8664D648C64936C1B06FCD488 -:1015B000094B40F2B760A021064A58649964936CC5 -:1015C00013F08003FBD1054A137030BC704700BF95 -:1015D00000C00F4000400C4000800C40A10D0020D6 -:1015E000F8B500BFF8BC08BC9E467047FFFFFFFF80 -:1015F0005FF800F0311200005FF800F09D0F00006E -:101600005FF800F07D0200005FF800F07D1600003A -:101610005FF800F03D0B00005FF800F005010000EE -:101620005FF800F02D1600005FF800F001010000E7 -:101630005FF800F0411300005FF800F0FD0E0000BD -:101640005FF800F07902000045000000FFFFFFFF97 -:10165000000000000000000000000000000000008A -:10166000000000000000000000000000000000007A -:1016700010B5054C237833B9044B13B10448AFF3CC -:1016800000800123237010BDD00300200000000063 -:1016900024170000084B10B51BB108490848AFF3E8 -:1016A00000800848036803B910BD074B002BFBD02E -:1016B000BDE81040184700BF00000000D403002020 -:1016C00024170000D00300200000000008B5174BCD -:1016D0000121187800F0ACF8154B0121187800F0C2 -:1016E000A7F8144B0121187800F0A2F8124B012141 -:1016F000187800F09DF8114B0121187800F098F847 -:101700000F4B0121187800F093F80E4B0021187848 -:1017100000F08EF80C4B0021187800F089F80B4B84 -:1017200000211878BDE8084000F082B8F4020020DB -:1017300000030020040300200C0300200803002005 -:10174000F8020020EC030020EC020020F002002050 -:10175000FFF7BCBF174B70B5187800F063F8164B55 -:101760000646187800F05EF8144B0546187800F02D -:1017700059F8134B04463146187800F04DF8114BD8 -:101780002946187800F048F80F4B2146187800F0E9 -:1017900043F80E4B3146187800F03EF80C4B2946C2 -:1017A000187800F039F80B4B21461878BDE87040E6 -:1017B00000F032B8FC020020EC020020F002002011 -:1017C000F402002000030020040300200C0300208A -:1017D00008030020F80200200001074B1A181B58CC -:1017E000D2685868104202D011B9C3F888207047F7 -:1017F000C3F88420704700BF000000200001054BA3 -:101800001A5819189268CB681A4214BF0120002098 -:10181000704700BF00000020272801D8FFF7DCBF79 -:10182000704700BF272801D8FFF7E8BF00207047A6 -:1018300027281CD800011A4A012902EB000310587E -:1018400010B415D0042913D0DC68426822EA0402DF -:1018500042609A68E9B102291ED003295B685DF8ED -:10186000044B0CBF0F491049116015221A607047D4 -:10187000DA680129446842EA040242609A6808D0A2 -:1018800040F6380111605B6815225DF8044B1A6060 -:10189000704738211160F6E704491160F3E700BF93 -:1018A00000000020383001003800010038F001004D -:1018B00004207146084203D0EFF3098000F008B815 -:1018C000EFF3088000F004B8704700BF704700BF16 -:1018D0001B4B05211B4A382030B5C2F8481108219E -:1018E000C2F8380383B05A68174C0A4317485A6045 -:1018F000C3F88410C3F888102368834202D91448BF -:1019000000F074FE0E4D08240020124A1249C5F85A -:1019100084400190019B934205D8019B01330193C0 -:10192000019B9342F9D9C5F888400190019B8B42F5 -:10193000EDD8019B01330193019B8B42F9D9E6E776 -:1019400000C01B4000801F40C4030020FF45C3238C -:1019500000A3E1113F420F003F548900836B30B474 -:101960001BB1536843F40043536072B6446B9CB19F -:10197000104B2260D3F8B0410C4217D1D3F840414C -:1019800044F48044C3F84041D3F8B851D3F84041FF -:101990006404F3D5294209D10023064C8260C36058 -:1019A000D4F8B0311943C4F8B0110263426362B68F -:1019B00030BC704700002E4038B50546036B06E08A -:1019C000AA6B1C6890476A6B2346944208D018465D -:1019D00033B1012B04D05A681206F1D52B6338BD00 -:1019E00000232B636B6338BDF0B5F1B9224C23495A -:1019F0000020234B01228025A1600A601A464D6019 -:101A0000E060D3F8BC41886044F001141D4DC3F878 -:101A1000BC41D3F8B01141F00101C3F8B011286006 -:101A2000D2F8B031002BFBD1F0BD0904164B144D98 -:101A30000126114C41F0800100221E60596000F522 -:101A4000805CE26400F5005EA36400F54057D5F8C1 -:101A5000B01100F580462A46986041F48031C3F801 -:101A60000CC0C3F810E05F619E61C5F8B011D2F8F8 -:101A7000B031002BFBD1BAE700300020202000203D -:101A800000002E400C040020002000202DE9F04F23 -:101A9000BB4C83B0D4F8448118F0010FC4F84481E2 -:101AA0005ED0D4F8AC31002B55D04FF00119DFF8DF -:101AB000F0B22646B34FCA46B96AFA6AC4F8AC31E6 -:101AC000D4F8403143F40053C4F84031D4F84031E5 -:101AD0009D04F5D5D6F8403123F40053C6F84031C3 -:101AE000C6F8B4A1D4F8B43113F00113FAD188B216 -:101AF00040F28165CBF80030A84200F29680B0F544 -:101B0000D06F80F0D881B0F5817F00F0578100F26E -:101B1000D580802800F02681822840F0C78092B2CC -:101B200002F07F01072900F2C18089009648974D95 -:101B30000844016810062B706B7040F1E581C90301 -:101B400001D501232B7002219048FFF74DFFD4F8F7 -:101B5000AC31002BB0D18A4AD2F8BC31002B44D131 -:101B600018F0400F18D0864BD3F8AC111A46C3F8C2 -:101B7000AC11D3F8BC11C3F8BC11D2F8B041804B02 -:101B8000002CFAD14FF0FF32C3F8B421D3F88431DE -:101B900000F074FB7E4B1C6018F0807F03D07D4BFF -:101BA0001B6803B1984718F0007F03D07A4B1B687D -:101BB00003B1984718F0040F02D0714BD3F8843169 -:101BC0006F4BD3F8482112060CD518F0800F09D0BE -:101BD00072490A78002A00F08E81531EDBB20B7026 -:101BE00003B9FBBE03B0BDE8F08F6D49C2F8BC314C -:101BF0000868034240F0CC81654A14681C40AFD0AD -:101C0000630700F1E281670300F1EF81260700F12D -:101C1000E881250300F1E181E00600F1DA81E102CB -:101C20009ED56048FFF7C8FE9AE742F22105A84218 -:101C300038D06FD8B0F5086F00F02F81B0F5106F75 -:101C400034D1C1F30741584A584811705849594A8C -:101C50000193C6F8C801C6F8CC11C6F8D02100F02F -:101C60000FFB554A019B80210120FB6410605160ED -:101C7000D6F8BC11936041F00111BA64C6F8BC11EA -:101C8000D6F8B02142F48032C6F8B021444A13603D -:101C9000D4F8B031002BFBD1D4F8AC31002B7FF459 -:101CA0000BAF58E7100C072800F03281C4F8C09140 -:101CB000D4F8AC31002B7FF4FFAE4CE740F2023594 -:101CC000A84200F0B780B0F5A06FEFD13A4A80206B -:101CD0000125FB6450601560D6F8BC01936040F0AC -:101CE0000113BA64C6F8BC31D6F8B03143F480337E -:101CF000C6F8B031D4F8B031002BFBD10B0C5B0629 -:101D000043F08073C6F85431D4F8AC31002B7FF423 -:101D1000D3AE20E742F22123984200F0828042F2C3 -:101D200021339842C2D1244B012180221960002125 -:101D30005A60D4F8BC21BB6442F001129960F96486 -:101D4000C4F8BC21D4F8B03143F48033C4F8B031C6 -:101D5000D4F8B031002BFBD1D4F8AC31002B7FF498 -:101D6000ABAEF8E6094A0221104613705370FFF734 -:101D70003BFED4F8AC31002B7FF49EAEEBE600BF07 -:101D800000002E4000300020C0012E402004002022 -:101D90000404002000040020080400201004002097 -:101DA0000C04002040320020300400200200CC004F -:101DB000C80002000200C8002020002092B202F0F9 -:101DC0007F03072B3FF672AF12F0800F4FEA8303B9 -:101DD000884A4FF001011A44136814BF23F480337A -:101DE00023F0010313608022834B196000215A60A5 -:101DF000D4F8BC21BB6442F001129960F964C4F8C4 -:101E0000BC21D4F8B03143F48033C4F8B031D4F8F5 -:101E1000B031002BFBD1D4F8AC31002B7FF44CAEA9 -:101E200099E6764BC1F30741754A1868754B106007 -:101E3000197078E792B202F07F03072B3FF636AFB6 -:101E400012F0800F4FEA83036A4A4FF001011A44EF -:101E5000136814BF43F4803343F0010313608022FE -:101E6000654B196000215A60D4F8BC21BB6442F074 -:101E700001129960F964C4F8BC21D4F8B03143F47C -:101E80008033C4F8B031D4F8B031002BFBD1D4F892 -:101E9000AC31002B7FF410AE5DE65B4A01215B4B59 -:101EA000127818461A70FFF79FFDD4F8AC31002B5A -:101EB0007FF402AE4FE6564B586800283FF4F6AE6A -:101EC000090C1FFA82FE04E00C33586800283FF426 -:101ED000EDAE1D888D42F7D15D887545F4D1090AB4 -:101EE000120C03290CBF01781989914228BF1146B1 -:101EF000FFF77AFD2BE6D3F8482122F08002C3F8E1 -:101F0000482103B0BDE8F08FCA077FF51CAE18E684 -:101F1000404D0120FB6029603F4B404918605960EB -:101F20003F49D6F8B0016A6001F5005E40F0010259 -:101F3000A1F5005001F58055BB609860A1F5805077 -:101F4000D860C6F8B02119615D61C3F818E0D4F813 -:101F5000B031002BFBD1284A012048F28001FB64FC -:101F600010604FF480305160D6F8BC11936041F09E -:101F70000113BA64C6F8BC31D6F8B0310343C6F8D1 -:101F8000B031CBF80000D4F8B031002BFBD1DEE546 -:101F9000204C42F22100002524880D6084427FF409 -:101FA0002BAE2049204C03C90D0C86282060A1804F -:101FB000A5717FF421AED2F8481150241B4841F09E -:101FC0008001C2F84811047016E61948FFF7F4FCC6 -:101FD00019E61848FFF7F0FC21E61748FFF7ECFC7C -:101FE0001AE61648FFF7E8FC13E61548FFF7E4FC8D -:101FF0000CE600BFC0012E4020200020A80D0020CC -:1020000088320020800D00203004002020040020B1 -:10201000800200201804002000200020800007001B -:1020200028240020280400208032002010040020F2 -:102030000031002000320020C0310020803100201B -:1020400040310020002AA0F102022DE9F04714BF20 -:1020500000274FF00057022A01D9BDE8F0874FEA68 -:10206000C01ADFF840900D4604460AEB090600212D -:1020700040229846304600F029FE012047EA0541FB -:102080004AF80910C6F83880B060B8F1000FE4D003 -:10209000034BA0401C6820431860BDE8F08700BFD8 -:1020A0000404002000300020002AA0F102022DE9E3 -:1020B000F04714BF00274FF00057022A01D9BDE8AE -:1020C000F0874022C501DFF844908846154400217E -:1020D00004461E4605EB090A504600F0F7FD0122B2 -:1020E00047EA084145F80910CAF83860CAF80820DC -:1020F000002EE4D004F11000034B82401868024324 -:102100001A60BDE8F08700BF040400200030002002 -:1021100012048160C36142F08002F0B44260012782 -:1021200001F5805601F5005501F5405401F5804256 -:102130000760C660056144618261F0BC704700BF02 -:10214000831E022B00D9704730B4064B00F11004F7 -:1021500001250A4603EBC01005FA04F130BCFFF775 -:10216000FDBB00BF40300020831E022B00D970470A -:1021700010B4054B01240A4604FA00F103EBC01029 -:102180005DF8044BFFF7EABB00300020124A134B06 -:10219000D2F8200220F07F40984210B584B002D8D7 -:1021A00000EB800040000E4C01A90A2200F0C8FBA1 -:1021B00001A90023204611F8012B01333AB10A2B63 -:1021C00020F8022FF7D11623237004B010BD5B0056 -:1021D000DBB2237004B010BD00441F407F9698000E -:1021E000A80300204368C269C3F30E43054930B415 -:1021F000C3F14003044C002521F8123024F812509A -:1022000030BC7047000C0020F80B0020F8B5154BCF -:102210001B783BB903F0FF04134B1B7813B1134D2C -:102220002A8802B9F8BD124F2346124EC2F58072B9 -:102230003978114806EB411600EB01213046FFF7D3 -:1022400067FF31460420FFF77BFF3B780133DBB2A9 -:10225000062B98BF3B704FF0000388BF3C702B806B -:10226000F8BD00BF340B002030040020800C00209B -:10227000350B0020A00C002034040020704700BF64 -:102280000021E0222048F8B50C46204E204D00F0F9 -:102290001DFD204F2146204B6022347028461F4EE2 -:1022A0001C8000F013FD23462246102102203C60D2 -:1022B000BC803460B480FFF7F7FE2246184B402103 -:1022C0000320FFF7BFFE2346224640210420FFF7EC -:1022D000EBFE2346402228461249FFF719FF294604 -:1022E0000320FFF741FF104B4A22104910480860B5 -:1022F000C3F88440C3F88020D3F8482142F08072AC -:10230000C3F84821F8BD00BFA00C0020350B002009 -:10231000200C0020000C0020800C0020F80B002076 -:10232000950B0000380B002000002E400004002018 -:10233000BD0B0000024A034B10881B88C01A70476F -:10234000000C0020F80B002010B4EFF3108272B6DE -:10235000437F33B9017F012908D0032910D001231D -:10236000437702B962B65DF8044B7047114C21689F -:10237000A1B1114943610B68086083615861EEE7C0 -:102380000E4C216881B10E4943610B68086083617E -:1023900058610C4B4FF080511960E0E7064B4161EA -:1023A000816120601860DAE7054B4161816120603E -:1023B0001860EEE7940D0020900D0020840D0020A1 -:1023C000880D002004ED00E010B4047F4160022C71 -:1023D000C26003D05DF8044BFFF7B6BF83685DF8B9 -:1023E000044B184770B5EFF3108172B60C4C23689C -:1023F0008BB10C4E00255A6922607AB1956101B902 -:1024000062B65D7718469B689847EFF3108172B605 -:102410002368002BEFD101B962B670BD3260EEE7E0 -:10242000840D0020880D0020FFF7DCBF184A30B46F -:102430001468002C28D0036821688B420FD2CB1A75 -:1024400000218460C1602360E0601060022330BC22 -:10245000037570470360144611688B4208D3A26865 -:102460005B1A002AF6D18260C4600360A060EDE7C9 -:10247000D568CB1A82600222C560E060C1688860BE -:102480002360027530BC70478460C4601060DDE773 -:102490008C0D0020F8B5224E34682CB32368002B35 -:1024A0003AD11D461F4F04E03468ECB12368002B7D -:1024B00032D1A36803B1DD602069336003682575FC -:1024C0001B68BB4221D1037F4560022BC46020D032 -:1024D000FFF73AFF6368002BE6D023602046FFF742 -:1024E000A5FF3468002CE1D1EFF3108372B60E4AD9 -:1024F00000211068116003B962B628B18468FFF743 -:1025000095FF20460028F9D1F8BD224600219847C2 -:10251000E0E783689847DDE7013B2360E4E700BF1D -:102520008C0D0020790D00009C0D0020044A054B05 -:102530001168054A1960136801331360FFF7AABFD9 -:10254000041000E0A40D0020A80D002070B5214C5F -:10255000237883B9204B01221B7822701BBB1F4BB1 -:102560001B78002B29D11E4B00211A68217012B153 -:10257000EFF3058202B170BDEFF3108072B61A68F6 -:10258000F2B1184C2178D9B90126556926701D6021 -:10259000D5B1A96100B962B6002593681046557798 -:1025A0009847257070BDFFF7C5FE0028D7D000F012 -:1025B0005FFB0A4B1B78002BD5D000F045FBD2E720 -:1025C0000028D8D162B670BD074B1D600028E3D14A -:1025D000E1E700BFA00D0020C8030020C80D0020C7 -:1025E000940D0020980D0020900D00208C4A8D4BFA -:1025F00090422DE9F0438C4D5C699969EF681DD9D3 -:102600008A4B984240F20181894B40F22766DFF8FD -:1026100060E20344874D1A0AAEFB0232D30903EB92 -:10262000830303EB830202F2E243B34228BF334643 -:10263000A3F54873A5FB0336F60804E07E4EB042CE -:1026400094BF06260E26774A07F01F0ED2F8803078 -:10265000B64543F0C003C2F880300AD2724B27F06F -:102660001F071A463743DF601368002BFCDA07F0B8 -:102670001F0E14F000732ED1704D714AD5F810C0A2 -:1026800015460CEA0202AA420ABF4FF0C0534FF4AB -:102690008052002284EA030515F0605F06D024F022 -:1026A000605403F060535F4D1C436C6181EA020388 -:1026B00013F4405F08D05B4B21F4405111431A469C -:1026C0009961936C1D07FCD444F00074554A546121 -:1026D000936C9906FCD401215A4D0A4601FB02F382 -:1026E00000FB03F3AB4209D8072A00F284800132D1 -:1026F00001FB02F300FB03F3AB42F5D95248534D03 -:102700001844A5FB0030030D6C2B79D8352B7ED8EF -:10271000DFF8608136234E48DFF820C14D4DDCF8EC -:102720000090B0FBF2F009EA05054545B0FBF1F079 -:102730000BD043F400534FF480586546CCF800802A -:10274000CCF800302B68002BFCDADFF8D8C0013A57 -:10275000DCF8103003F00703934207D002F00702C1 -:102760006546CCF81020AB6CDB03FCD40139890240 -:1027700084EA010313F4E05F0AD02A4B24F4E05406 -:1027800001F4E0511A460C435C61936C9907FCD448 -:10279000314B32490344DB09A1FB0331090B042906 -:1027A00028BF04214B1E1B0284EA030212F4407F5F -:1027B00006D024F4407403F440731A4A1C43546155 -:1027C000184B24F000741A465C61936C9B06FCD491 -:1027D000B0FBF1F1224A7645224B1060196008D215 -:1027E000114B27F01F071A463743DF601368002B91 -:1027F000FCDABDE8F083042980D8013101226DE7BD -:10280000DFF874806C23184886E712261BE7174808 -:10281000DFF8688000FB03F043EA08087CE700BFAC -:1028200000A4781F00C00F40000008400046C323EA -:1028300000BA3CDC1F85EB5100366E0100800D4074 -:1028400040300080FFB19F26808D5B00819F5E1627 -:1028500000B29F267F3001807FD1F0089F10E500F5 -:10286000C4030020C003002000643F4D001BB700DC -:1028700023B24C00362000806C20008000200080B5 -:10288000002852D02DE9F04F814683B0274C01201B -:10289000274D284E54E8003F2A68316844E8000379 -:1028A000002BF7D1244F4FF47A7E2448D7F800C08C -:1028B000BB4607F1C6470368C1EB0C0107F5DE17FD -:1028C00007F67F67A7FB03C3BA4601279B0CB1FB42 -:1028D000F3F30EFB023854E8003F2A68316844E8FD -:1028E0000073002BF7D1DBF800C04FF47A7E036849 -:1028F0000EFB02F2C1EB0C01AAFB033EC8EB020384 -:102900004FEA9E42B1FBF2F1CA18B2F57A7F07D3C3 -:10291000B9F1010908F57A78DDD103B0BDE8F08F8F -:102920000190FFF713FE0198D5E770478C32002025 -:10293000A80D0020A40D0020041000E0C403002016 -:10294000F0B44E1E0025374600E00135B0FBF2F32F -:1029500002FB130000F13704092800F13000E4B253 -:1029600098BFC4B2184607F8014F002BEDD14A19A1 -:1029700053704DB1013316F8014F1778E81A3770CC -:10298000834202F80149F5DB0846F0BC704700BFFE -:10299000A4484FF00F0CA44B826F42F47F02F0B5B5 -:1029A00082670025D0F880204FF470469F4C4FF48A -:1029B000604E29464FF4806714432A46C0F8804091 -:1029C000A3F88C6148F2B826A3F88EC1A3F8905101 -:1029D000B3F8880180B240F0F000A3F8880101EB61 -:1029E0004100914B0131002540011C46042903445C -:1029F000A3F804E0DF805A841A865A805A81DE8167 -:102A00005A82DA825A83DA83E9D1B4F888014FF026 -:102A10000F0C874B4FF4704680B229464FF4604745 -:102A20002A4640EA0C004FF4806EA4F88801B4F8FE -:102A3000880180B240F47060A4F88801A3F88C612A -:102A400048F2B826A3F88EC1A3F89051B3F88801D4 -:102A500080B240F0F000A3F8880101EB4100744B14 -:102A60000131002540011C46042903449F80A3F83E -:102A700006E05A841A865A805A81DE815A82DA82A6 -:102A80005A83DA83E9D1B4F888014FF00F0C694B0F -:102A90004FF4704680B229464FF460472A4640EA18 -:102AA0000C004FF4806EA4F88801B4F8880180B25D -:102AB00040F47060A4F88801A3F88C6148F2B8264D -:102AC000A3F88EC1A3F89051B3F8880180B240F00A -:102AD000F000A3F8880101EB4100564B01310025BD -:102AE00040011C46042903449F80A3F806E05A8451 -:102AF0001A865A805A81DE815A82DA825A83DA83B0 -:102B0000E9D1B4F888014FF00F0C4B4B4FF47047EC -:102B100080B229464FF460462A4640EA0C004FF442 -:102B2000806EA4F88801B4F8880180B240F4706027 -:102B3000A4F88801A3F88C7148F2B827A3F88EC1D5 -:102B4000A3F89051B3F8880180B240F0F000A3F8E8 -:102B5000880101EB4100384B013140011C4604293A -:102B600003449E80A3F806E05A841A865A805A814C -:102B7000DF815A82DA825A83DA83EAD1B4F8883163 -:102B80000F27002241F201069BB245F6C05E1146B6 -:102B900043F226053B43A4F88831B4F888319BB250 -:102BA00043F47063A4F888315001244B013203448C -:102BB000042A99815981DF819E82A3F806E0198059 -:102BC00019829D81F0D100220F2741F2010645F6BE -:102BD000C055114643F226045001194B01320344FB -:102BE000042A99815981DF819E82DD8019801982B2 -:102BF0009C81F1D100220F2741F2010645F6C05514 -:102C0000114643F2260450010E4B01320344042ABC -:102C100099815981DF819E82DD80198019829C8192 -:102C2000F1D1F0BD00C00F4000C03D40000003FCEA -:102C300000003E4000403E4000803E4000C01D403D -:102C400000001E4000401E4038B5074B1C784CB1B8 -:102C5000064D55F8043F002BFBD09847631E13F038 -:102C6000FF04F6D138BD00BFC80D0020A80D00201C -:102C7000014B00221A707047C803002070B50F4E38 -:102C80000F4D761BB61018BF002405D0013455F83F -:102C9000043B9847A642F9D10A4E0B4D761B00F033 -:102CA00063F8B61018BF002406D0013455F8043B71 -:102CB0009847A642F9D170BD70BD00BF48160060AC -:102CC000481600604C1600604816006070B4840717 -:102CD00046D0541E002A41D0CDB2034602E0621E07 -:102CE000E4B3144603F8015B9A07F8D1032C2ED9FC -:102CF000CDB245EA05250F2C45EA054519D903F162 -:102D000010022646103E0F2E42F8105C42F80C5C72 -:102D100042F8085C42F8045C02F11002F2D8A4F117 -:102D2000100222F00F0204F00F041032032C13449F -:102D30000DD91E462246043A032A46F8045BFAD807 -:102D4000221F22F003020432134404F003042CB1C6 -:102D5000C9B21C4403F8011BA342FBD170BC7047ED -:102D600014460346C2E700BF5FF800F0E1150060BB -:042D7000F8B500BFF3 -:102D740000000042C8801F40B8821F4008000000C5 -:102D840000000042C4801F40B4821F4004000000C1 -:102D940000C0004224801F4014821F401000000025 -:102DA40000C0004228801F4018821F4020000000FD -:102DB40000C000422C801F401C821F4040000000C5 -:102DC40000C0004234801F4024821F4000010000E4 -:102DD4000040004264811F4054831F4000040000EF -:102DE4000040004280811F4070831F4000000200A9 -:102DF400004000427C811F406C831F4000000100A2 -:102E04000040004268811F4058831F4000080000B2 -:102E1400004000423C811F402C831F400100000001 -:102E24000040004244811F4034831F4004000000DE -:102E34000040004240811F4030831F4002000000D8 -:102E44000040004248811F4038831F4008000000B2 -:102E54000000004204811F40F4821F40000004006F -:102E64000000004208811F40F8821F400000080053 -:102E74000000004218811F4008831F4000008000AA -:102E84000000004214811F4004831F4000004000E2 -:102E94000000004200811F40F0821F400000020039 -:102EA40000000042FC801F40EC821F400000010033 -:102EB4000000004224811F4014831F4000000004CE -:102EC4000000004228811F4018831F4000000008B2 -:102ED400000000421C811F400C831F4000000001C1 -:102EE4000000004220811F4010831F4000000002A8 -:102EF40000000042EC801F40DC821F4000100000F4 -:102F040000000042F0801F40E0821F4000200000CB -:102F14000000004234811F4024831F400000004011 -:102F24000000004238811F4028831F4000000080B9 -:102F34000080004294801F4084821F4000000400EF -:102F440000C0004290801F4080821F40000000802B -:102F540000800042A8801F4098821F40000080002B -:102F640000800042A4801F4094821F400000400063 -:102F7400004000426C811F405C831F400010000031 -:102F840000C0004230801F4020821F4080000000AB -:102F940000800042C8811F40B8831F4000800000A9 -:102FA40000800042C4811F40B4831F4000400000E1 -:102FB40000800042C0811F40B0831F4000200000F9 -:102FC40000800042BC811F40AC831F400010000001 -:102FD40000800042D0811F40C0831F4000000200D7 -:102FE40000800042CC811F40BC831F4000000100D0 -:102FF4000001000078030020120000000006000019 -:10300400100300200A000000000200003403002026 -:1030140043000000000700003403002043000000C8 -:1030240000030000A40300200000000001030904C1 -:103034001C03002000000000020309048C0300208C -:103044000000000003030904A8030020000000009E -:10305400000000000000000000000000010000006B -:10306400020000001700000012000000040000002D -:1030740016000000150000001300000014000000FA -:103084000A06000202000040010000001803540078 -:10309400650065006E0073007900640075006900C6 -:1030A4006E006F0009024300020100C032090400EF -:1030B40000010202010005240010010524010101A0 -:1030C40004240206052406000107058203100010EB -:1030D40009040100020A0000000705030240000081 -:1030E40007058402400000001201000202000040B3 -:1030F400C01683047902010203010000160355007F -:103104005300420020005300650072006900610012 -:103114006C000000040309040C030000000000001C -:10312400000000000000000000000000000000009B -:103134000029DE07007B9A17010000000000000050 -:040000056000100087 -:00000001FF diff --git a/tools/odrive/tests/encoder_test.py b/tools/odrive/tests/encoder_test.py index efde608b..016f39da 100644 --- a/tools/odrive/tests/encoder_test.py +++ b/tools/odrive/tests/encoder_test.py @@ -6,15 +6,59 @@ from math import pi import os from fibre.utils import Logger -from test_runner import EncoderTestContext, test_assert_eq, program_teensy +from test_runner import * + + +teensy_code_template = """ +void setup() { + pinMode({enc_a}, OUTPUT); + pinMode({enc_b}, OUTPUT); +} + +int cpr = 8192; +int rpm = 30; + +// the loop routine runs over and over again forever: +void loop() { + int microseconds_per_count = (1000000 * 60 / cpr / rpm); + + for (;;) { + digitalWrite({enc_a}, HIGH); + delayMicroseconds(microseconds_per_count); + digitalWrite({enc_b}, HIGH); + delayMicroseconds(microseconds_per_count); + digitalWrite({enc_a}, LOW); + delayMicroseconds(microseconds_per_count); + digitalWrite({enc_b}, LOW); + delayMicroseconds(microseconds_per_count); + } +} + +""" + def modpm(val, range): return ((val + (range / 2)) % range) - (range / 2) class TestIncrementalEncoder(): - def is_compatible(self, enc_ctx: EncoderTestContext): - return True + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + for encoder in odrive.encoders: + # Find the Teensy that is connected to the encoder pins and the corresponding Teensy GPIOs + + gpio_conns = [ + testrig.get_directly_connected_components(encoder.a), + testrig.get_directly_connected_components(encoder.b), + ] + + valid_combinations = [ + [combination[0].parent] + list(combination) + for combination in itertools.product(*gpio_conns) + if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) + ] + + yield (encoder, valid_combinations) def run_delta_test(self, encoder, true_cps, with_cpr): encoder.config.cpr = with_cpr @@ -45,17 +89,15 @@ class TestIncrementalEncoder(): time.sleep(0.01) - def run_test(self, enc_ctx: EncoderTestContext, logger: Logger): + def run_test(self, enc: EncoderComponent, teensy: TeensyComponent, teensy_gpio_a: int, teensy_gpio_b: int, logger: Logger): true_cps = 8192*-0.5 # counts per second generated by the virtual encoder - # TODO: read teensy config from YAML file - if enc_ctx.num == 0: - hexfile = 'enc0_sim_-4096cps.ino.hex' - else: - hexfile = 'enc1_sim_-4096cps.ino.hex' - program_teensy(os.path.join(os.path.dirname(__file__), hexfile), 26, logger) + + code = teensy_code_template.replace("{enc_a}", str(teensy_gpio_a.num)).replace("{enc_b}", str(teensy_gpio_b.num)) + teensy.compile_and_program(code) + time.sleep(1.0) # wait for PLLs to stabilize - encoder = enc_ctx.handle + encoder = enc.handle # The true encoder count and PLL output should be roughly the same. # At 8192 CPR and 0.5 RPM, the delta because of sequential reading is @@ -67,8 +109,8 @@ class TestIncrementalEncoder(): #encoder.config.cpr = configured_cpr #expected_delta = true_cps/1200 #for _ in range(1000): - # first = enc_ctx.handle.axis0.encoder.count_in_cpr - # second = enc_ctx.handle.axis0.encoder.pos_cpr + # first = enc.handle.axis0.encoder.count_in_cpr + # second = enc.handle.axis0.encoder.pos_cpr # test_assert_eq(modpm(second - first, configured_cpr), expected_delta, range=abs(true_cps/500)) # time.sleep(0.001) diff --git a/tools/odrive/tests/nvm_test.py b/tools/odrive/tests/nvm_test.py index 99da037f..72ed2061 100644 --- a/tools/odrive/tests/nvm_test.py +++ b/tools/odrive/tests/nvm_test.py @@ -7,17 +7,18 @@ import os import fibre from fibre.utils import Logger -from test_runner import ODriveTestContext, test_assert_eq +from test_runner import * class TestStoreAndReboot(): """ Stores the current configuration to NVM and reboots. """ - def is_compatible(self, odrive: ODriveTestContext): - return True + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + yield (odrive,) - def run_with_values(self, values, odrive: ODriveTestContext, logger: Logger): + def run_with_values(self, odrive: ODriveComponent, values: list, logger: Logger): logger.debug("storing configuration and rebooting...") for value in values: @@ -31,15 +32,15 @@ class TestStoreAndReboot(): odrive.handle = None time.sleep(2) - odrive.make_available(logger) + odrive.prepare(logger) logger.debug("verifying configuration after reboot...") test_assert_eq(odrive.handle.config.brake_resistance, values[-1], accuracy=0.01) - def run_test(self, odrive: ODriveTestContext, logger: Logger): - self.run_with_values([0.5, 1.0, 1.5], odrive, logger) - self.run_with_values([2.5, 3.7], odrive, logger) - self.run_with_values([0.47], odrive, logger) + def run_test(self, odrive: ODriveComponent, logger: Logger): + self.run_with_values(odrive, [0.5, 1.0, 1.5], logger) + self.run_with_values(odrive, [2.5, 3.7], logger) + self.run_with_values(odrive, [0.47], logger) if __name__ == '__main__': test_runner.run(TestStoreAndReboot()) diff --git a/tools/odrive/tests/pwm_input_test.py b/tools/odrive/tests/pwm_input_test.py index e70f8e1e..fc603eb3 100644 --- a/tools/odrive/tests/pwm_input_test.py +++ b/tools/odrive/tests/pwm_input_test.py @@ -5,13 +5,38 @@ import time import math import os -import fibre -from fibre.utils import Logger from odrive.enums import errors -from test_runner import ODriveTestContext, test_assert_eq, program_teensy +from test_runner import * + + +teensy_code_template = """ +float position = 0; // between 0 and 1 +float velocity = 1; // [position per second] + +void setup() { +{setup_code} +} + +// the loop routine runs over and over again forever: +void loop() { + int high_microseconds = 1000 + (int)(position * 1000.0f); + +{set_high_code} + delayMicroseconds(high_microseconds); +{set_low_code} + + // Wait for a total of 20ms. + // delayMicroseconds() only works well for values <= 16383 + delayMicroseconds(10000 - high_microseconds); + delayMicroseconds(10000); + + position += velocity * 0.02; + while (position > 1.0) + position -= 1.0; +} +""" + -#def modpm(val, lower_bound, upper_bound): -# return ((val - lower_bound) % (upper_bound - lower_bound)) - lower_bound def modpm(val, range): return ((val + (range / 2)) % range) - (range / 2) @@ -28,8 +53,24 @@ class TestPwmInput(): Note: this test is currently only written for ODrive 3.6 (or similar GPIO layout). """ - def is_compatible(self, odrive: ODriveTestContext): - return True + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + # Find the Teensy that is connected to gpios 1-4 of the ODrive and the corresponding Teensy GPIOs + + gpio_conns = [ + testrig.get_directly_connected_components(odrive.gpio1), + testrig.get_directly_connected_components(odrive.gpio2), + testrig.get_directly_connected_components(odrive.gpio3), + testrig.get_directly_connected_components(odrive.gpio4) + ] + + valid_combinations = [ + [combination[0].parent] + list(combination) + for combination in itertools.product(*gpio_conns) + if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) + ] + + yield (odrive, valid_combinations) def run_delta_test(self, attr, with_min, with_max, timeout = 5.0): rounds_per_s = 1.0 @@ -84,10 +125,16 @@ class TestPwmInput(): test_assert_eq(min_val, with_min, range = step_size) test_assert_eq(max_val, with_max, range = step_size) - def run_test(self, odrive: ODriveTestContext, logger: Logger): + def run_test(self, odrive: ODriveComponent, teensy: TeensyComponent, teensy_gpio1: int, teensy_gpio2: int, teensy_gpio3: int, teensy_gpio4: int, logger: Logger): # TODO: test each GPIO separately - hexfile = 'pwm_sim.ino.hex' - program_teensy(os.path.join(os.path.dirname(__file__), hexfile), 26, logger) + + setup_code = "\n".join(" pinMode(" + str(gpio.num) + ", OUTPUT);" for gpio in [teensy_gpio1, teensy_gpio2, teensy_gpio3, teensy_gpio4]) + set_high_code = "\n".join(" digitalWrite(" + str(gpio.num) + ", HIGH);" for gpio in [teensy_gpio1, teensy_gpio2, teensy_gpio3, teensy_gpio4]) + set_low_code = "\n".join(" digitalWrite(" + str(gpio.num) + ", LOW);" for gpio in [teensy_gpio1, teensy_gpio2, teensy_gpio3, teensy_gpio4]) + + code = teensy_code_template.replace("{setup_code}", setup_code).replace("{set_high_code}", set_high_code).replace("{set_low_code}", set_low_code) + teensy.compile_and_program(code) + time.sleep(1.0) # wait for PLLs to stabilize logger.debug("Set up PWM input...") @@ -106,15 +153,7 @@ class TestPwmInput(): odrive.handle.config.gpio4_pwm_mapping.min = -20000 odrive.handle.config.gpio4_pwm_mapping.max = 20000 - # Save and reboot - odrive.handle.save_configuration() - try: - odrive.handle.reboot() - except fibre.ChannelBrokenException: - pass # this is expected - odrive.handle = None - time.sleep(2) - odrive.make_available(logger) + odrive.save_config_and_reboot() logger.debug("Check if PWM on GPIO1 works...") self.run_delta_test(odrive.handle.axis0.controller._remote_attributes['input_pos'], -50, 200) diff --git a/tools/odrive/tests/pwm_sim.ino.hex b/tools/odrive/tests/pwm_sim.ino.hex deleted file mode 100644 index efd5bf65..00000000 --- a/tools/odrive/tests/pwm_sim.ino.hex +++ /dev/null @@ -1,801 +0,0 @@ -:0200000460009A -:100000004643464200000156000000000101020084 -:1000100000000000000000000000000000000000E0 -:1000200000000000000000000000000000000000D0 -:1000300000000000000000000000000000000000C0 -:1000400000000000010403000000000000000000A8 -:100050000000200000000000000000000000000080 -:100060000000000000000000000000000000000090 -:100070000000000000000000000000000000000080 -:10008000EB04180A063204260000000000000000FD -:10009000050404240000000000000000000000002F -:1000A0000000000000000000000000000000000050 -:1000B0000604000000000000000000000000000036 -:1000C0000000000000000000000000000000000030 -:1000D00020041808000000000000000000000000DC -:1000E0000000000000000000000000000000000010 -:1000F0000000000000000000000000000000000000 -:10010000D8041808000000000000000000000000F3 -:100110000204180804200000000000000000000095 -:1001200000000000000000000000000000000000CF -:10013000600400000000000000000000000000005B -:1001400000000000000000000000000000000000AF -:10015000000000000000000000000000000000009F -:10016000000000000000000000000000000000008F -:10017000000000000000000000000000000000007F -:10018000000000000000000000000000000000006F -:10019000000000000000000000000000000000005F -:1001A000000000000000000000000000000000004F -:1001B000000000000000000000000000000000003F -:1001C000000100000010000001000000000000001D -:1001D000000001000000000000000000000000001E -:1001E000000000000000000000000000000000000F -:1001F00000000000000000000000000000000000FF -:10020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -:10021000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE -:10022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE -:10023000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE -:10024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE -:10025000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE -:10026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E -:10027000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E -:10028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E -:10029000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E -:1002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E -:1002B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E -:1002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E -:1002D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E -:1002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E -:1002F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E -:10030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD -:10031000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED -:10032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD -:10033000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD -:10034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD -:10035000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD -:10036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D -:10037000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D -:10038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D -:10039000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D -:1003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D -:1003B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D -:1003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D -:1003D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D -:1003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D -:1003F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D -:10040000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC -:10041000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC -:10042000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC -:10043000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC -:10044000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC -:10045000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC -:10046000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C -:10047000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C -:10048000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C -:10049000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C -:1004A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C -:1004B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C -:1004C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C -:1004D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C -:1004E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C -:1004F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C -:10050000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB -:10051000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB -:10052000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB -:10053000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB -:10054000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB -:10055000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB -:10056000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B -:10057000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B -:10058000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B -:10059000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B -:1005A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B -:1005B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B -:1005C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B -:1005D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B -:1005E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B -:1005F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B -:10060000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA -:10061000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA -:10062000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA -:10063000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA -:10064000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA -:10065000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA -:10066000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A -:10067000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A -:10068000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A -:10069000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A -:1006A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A -:1006B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A -:1006C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A -:1006D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A -:1006E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A -:1006F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A -:10070000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 -:10071000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9 -:10072000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9 -:10073000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9 -:10074000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9 -:10075000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9 -:10076000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99 -:10077000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89 -:10078000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79 -:10079000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69 -:1007A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59 -:1007B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49 -:1007C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39 -:1007D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29 -:1007E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19 -:1007F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09 -:10080000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8 -:10081000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8 -:10082000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8 -:10083000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8 -:10084000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8 -:10085000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8 -:10086000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98 -:10087000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88 -:10088000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78 -:10089000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68 -:1008A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58 -:1008B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48 -:1008C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38 -:1008D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28 -:1008E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18 -:1008F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08 -:10090000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7 -:10091000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7 -:10092000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7 -:10093000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7 -:10094000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7 -:10095000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7 -:10096000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97 -:10097000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87 -:10098000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77 -:10099000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67 -:1009A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57 -:1009B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47 -:1009C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37 -:1009D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27 -:1009E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17 -:1009F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07 -:100A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6 -:100A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6 -:100A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6 -:100A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6 -:100A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6 -:100A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6 -:100A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96 -:100A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86 -:100A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76 -:100A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66 -:100AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56 -:100AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46 -:100AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36 -:100AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26 -:100AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16 -:100AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06 -:100B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5 -:100B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5 -:100B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5 -:100B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5 -:100B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5 -:100B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5 -:100B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95 -:100B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85 -:100B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75 -:100B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65 -:100BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55 -:100BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45 -:100BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35 -:100BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25 -:100BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15 -:100BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05 -:100C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4 -:100C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4 -:100C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4 -:100C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4 -:100C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4 -:100C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4 -:100C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94 -:100C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84 -:100C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74 -:100C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64 -:100CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54 -:100CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44 -:100CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34 -:100CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24 -:100CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14 -:100CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04 -:100D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 -:100D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3 -:100D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3 -:100D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3 -:100D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3 -:100D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3 -:100D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93 -:100D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83 -:100D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73 -:100D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63 -:100DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53 -:100DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43 -:100DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33 -:100DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23 -:100DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13 -:100DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03 -:100E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2 -:100E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2 -:100E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2 -:100E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2 -:100E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2 -:100E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2 -:100E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92 -:100E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82 -:100E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72 -:100E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62 -:100EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52 -:100EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42 -:100EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32 -:100ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22 -:100EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12 -:100EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02 -:100F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1 -:100F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1 -:100F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1 -:100F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1 -:100F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1 -:100F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1 -:100F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91 -:100F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81 -:100F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71 -:100F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61 -:100FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51 -:100FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41 -:100FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31 -:100FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21 -:100FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11 -:100FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01 -:10100000D10020402C100060000000000000000013 -:1010100020100060001000600000000000000000D0 -:1010200000000060D031000000000000000001203E -:1010300035100060764B0720764C4FF42A01764A33 -:101040005C64186499639546744A75498A420FD066 -:10105000744B9A420CD2D4430846234423F0030332 -:1010600004330B4450F8041B984242F8041BF9D196 -:101070006D4A6E498A420FD06D4B9A420CD2D443CE -:101080000846234423F0030304330B4450F8041BA5 -:10109000984242F8041BF9D1664A674B9A420BD238 -:1010A000D04311460024034423F0030304331344C4 -:1010B00041F8044B8B42FBD1604A4FF47001604B06 -:1010C000116003F530715F4A43F8042F9942FBD158 -:1010D0005D4B80215D4A03F8011B9342FBD15C4CC0 -:1010E0004FF080375B4E4FF0FF32DFF8A8A10025AC -:1010F000DFF8A491DFF8A481574B4549C3F800A05D -:10110000C4F80471C4F80091C4F8F470C4F8F08015 -:10111000F36923F07F0343F04003F361736A23F024 -:101120007F0343F0400373628A66CA660A674A67B0 -:1011300000F0B6F8494A6320494B4A49106003223F -:101140001D60CAF8381043F8082C4749474A4848F8 -:10115000C3F8082D0B68474A43F08073CAF83C0077 -:1011600045480B601368454943F001031360036869 -:101170000B6000F0E5F8C4F804714148C4F8009130 -:10118000C4F8F470C4F8F08000F04AFA00BF00BF61 -:1011900000BF00BFF16E3B4A41F440513A4BF1664B -:1011A0001560C2F80851C2F81851C2F82851C2F8A7 -:1011B00038519A6BD20708D442F615623349596503 -:1011C0001A659A6B42F001029A632F4A304C936879 -:1011D00043F00113936000F01BFA2368132BFCD932 -:1011E00000F05CF900F0D0F900F00EFA00F0DAF847 -:1011F00000F002FA2368B3F5967FFBD300F018FAEB -:1012000000F0FEF900F01CFA00F016FAFAE700BF51 -:1012100000C00A40ABAAAAAA008007200000000074 -:1012200050160060C017000000000020142E00605F -:10123000C0030020C0030020C032002088ED00E081 -:10124000FC0F00208905000000E400E0A0E400E0BD -:1012500000800D4000C00F4008ED00E014E000E009 -:1012600018E000E001110000FCED00E0000020208B -:1012700005120000001000E0041000E0840D0020C2 -:101280000046C3230040084000400D400000C05607 -:10129000880D0020001000201B1018200C0D1113C9 -:1012A000F0B5194A0021194B4FF0100E18480124CF -:1012B000184E194D0160194FC2F800E01E6015600C -:1012C000174E184D1F601660174F1D60174E184DB2 -:1012D00017601E60174F1560174E184D1F6016607F -:1012E0001D600460BFF34F8FBFF36F8FC3F8B01161 -:1012F000BFF34F8FBFF36F8F52F8883C43F44033F6 -:1013000042F8883CF0BD00BF9CED00E0A0ED00E09D -:1013100094ED00E0250008031100200021000207E1 -:1013200012000020250008131300202027000B13B3 -:101330001400004033001013150000602F000B074D -:10134000F0B4174A40274FF480314FF480564FF4E1 -:1013500000554FF4404443F24200136913F0020F6A -:1013600006D0946151619061136913F0020FF8D1B6 -:1013700013F4005F01D15561EFE713F4805F01D1F1 -:101380005661EAE7002BE8DA13F4803F01D091615F -:10139000E3E75B0601D45761DFE7F0BC704700BFAD -:1013A00000800D40364A03203649F3EE096A13687F -:1013B00023F00103F0B51360C2F89000D1F8E030DB -:1013C000DFF8D4E0DCB2C3F30B202F4FC0EB135394 -:1013D00007EE904AA4F15501CEF80040B8EEE77A46 -:1013E00003EB830407EE900A03FB01F13B6003EB80 -:1013F0008313B8EEE75A07EE901A091B77EE666A78 -:10140000B8EE677A214D07EE901A0B44C5ED006ADD -:10141000F8EE677A1E4EC7EE265A1E4930601068F5 -:1014200087EEA66A07EE903AF8EE677A87EEA67A1C -:1014300075EE855A76EE056AFCEEE55AFCEEE66A34 -:1014400015EE903A40EA035316EE900A77EE057ACD -:10145000136001EA0041D2F81031FCEEE77A0B4349 -:10146000C2F8103117EE903AD2F81011C3F30B0303 -:101470000B43C2F81031136843F002031360F0BD50 -:1014800080810D4000441F40E8030020E403002059 -:10149000EC0300200000FF0FE0030020304B40F67B -:1014A000617270B5C3F8202140F2044500F0B4F831 -:1014B0002C492D48D1F880202C4C42F003022C4BB3 -:1014C000C1F88020C0F86051226813401BB9D0F8E1 -:1014D000A8319A071AD0244B4FF00041234A516398 -:1014E0001A46D3F8401141F00201C3F84011D2F876 -:1014F00040319B07FBD44FF400301E491B4B4FF08B -:101500000042086019209A6300F08EF81A4D0022FC -:10151000164B4FF08041144C0A26996328461A60F6 -:101520001146C4F8A8614FF4207200F061F84FF43E -:1015300081064FF4800040F24313104A10492E6098 -:101540002864C4F85851C2F80412C4F848310D4A4E -:101550004FF4003101231160C4F8403170BD00BF69 -:1015600000800D4000C00F4000002E4000900D4054 -:10157000001C1E008CE200E0003000200010002063 -:10158000650700000CE100E0114B1249D86E0A46D5 -:1015900040F4403030B4D86640F2B765D86EA0242D -:1015A00040F44070D8664D648C64936C1B06FCD488 -:1015B000094B40F2B760A021064A58649964936CC5 -:1015C00013F08003FBD1054A137030BC704700BF95 -:1015D00000C00F4000400C4000800C40810D0020F6 -:1015E000F8B500BFF8BC08BC9E467047FFFFFFFF80 -:1015F0005FF800F0111700005FF800F0A50500008B -:101600005FF800F0C90000005FF800F0D51300009B -:101610005FF800F0A10500005FF800F0650E000023 -:101620005FF800F0450200005FF800F0C51200000E -:101630005FF800F0C11600005FF800F0251200000E -:101640005FF800F0D100000045000000FFFFFFFF41 -:10165000000000000000000000000000000000008A -:10166000000000000000000000000000000000007A -:1016700010B5054C237833B9044B13B10448AFF3CC -:1016800000800123237010BDC00300200000000073 -:10169000C4170000084B10B51BB108490848AFF348 -:1016A00000800848036803B910BD074B002BFBD02E -:1016B000BDE81040184700BF00000000C403002030 -:1016C000C4170000C00300200000000008B50D4B47 -:1016D0000121187800F040FA0B4B0121187800F036 -:1016E0003BFA0A4B0121187800F036FA084B012129 -:1016F000187800F031FA074B01211878BDE808404E -:1017000000F02ABAEC020020F0020020F4020020CF -:10171000F8020020FC020020FFF7D8BF0000000004 -:101720002DE9F04F504D83B09FED507A0121D5ED5A -:10173000007ADFF850B167EE877ADFF84CA19BF8AA -:101740000000DFF84891FDEEE77ADFF84481484F6A -:10175000484E17EE904A00F0F9F99AF8000001217E -:1017600000F0F4F999F80000012104F57A7400F012 -:10177000EDF998F80000012100F0E8F9387801212E -:1017800000F0E4F93C483D49D0F800E03368A1FBA3 -:101790000331890C04FB01F10368374ACEEB0303E4 -:1017A0009942F9D89BF800000021019200F0CEF98F -:1017B0009AF80000002100F0C9F999F80000002112 -:1017C00000F0C4F998F80000002100F0BFF9387863 -:1017D000002100F0BBF9019A284942F21070176805 -:1017E0003368001BA1FB03139B0C03FB00F0136881 -:1017F0002149DB1B9842FAD8086842F2107332681C -:101800001E4CA4FB0242920C03FB02F20B681B1A53 -:101810009A42FBD81A4BF7EE006A95ED007A93EDE9 -:10182000005AB7EEC77AB7EEC55A9FED0D4BA5EE3D -:10183000047BB7EEC77BB4EEE67A85ED007AF1EE75 -:1018400010FA0ADDF0EE667A37EE677AB4EEE77AE0 -:10185000F1EE10FAF8DC85ED007A03B0BDE8F08F08 -:101860007B14AE47E17A943FDC03002000007A4409 -:10187000FC02002008030020041000E083DE1B436C -:1018800000030020EC020020F0020020F4020020FF -:10189000F80200208C4A8D4B90422DE9F0438C4D8C -:1018A0005C699969EF681DD98A4B984240F20181C1 -:1018B000894B40F22766DFF860E20344874D1A0A3D -:1018C000AEFB0232D30903EB830303EB830202F284 -:1018D000E243B34228BF3346A3F54873A5FB033662 -:1018E000F60804E07E4EB04294BF06260E26774AE4 -:1018F00007F01F0ED2F88030B64543F0C003C2F89F -:1019000080300AD2724B27F01F071A463743DF6038 -:101910001368002BFCDA07F01F0E14F000732ED1B1 -:10192000704D714AD5F810C015460CEA0202AA4261 -:101930000ABF4FF0C0534FF48052002284EA0305DF -:1019400015F0605F06D024F0605403F060535F4DE3 -:101950001C436C6181EA020313F4405F08D05B4BC7 -:1019600021F4405111431A469961936C1D07FCD430 -:1019700044F00074554A5461936C9906FCD40121DB -:101980005A4D0A4601FB02F300FB03F3AB4209D8B0 -:10199000072A00F28480013201FB02F300FB03F30B -:1019A000AB42F5D95248534D1844A5FB0030030D06 -:1019B0006C2B79D8352B7ED8DFF8608136234E48E2 -:1019C000DFF820C14D4DDCF80090B0FBF2F009EAE1 -:1019D00005054545B0FBF1F00BD043F400534FF43F -:1019E00080586546CCF80080CCF800302B68002B7E -:1019F000FCDADFF8D8C0013ADCF8103003F0070356 -:101A0000934207D002F007026546CCF81020AB6C79 -:101A1000DB03FCD40139890284EA010313F4E05F9B -:101A20000AD02A4B24F4E05401F4E0511A460C4346 -:101A30005C61936C9907FCD4314B32490344DB0958 -:101A4000A1FB0331090B042928BF04214B1E1B02F3 -:101A500084EA030212F4407F06D024F4407403F4B5 -:101A600040731A4A1C435461184B24F000741A4600 -:101A70005C61936C9B06FCD4B0FBF1F1224A764585 -:101A8000224B1060196008D2114B27F01F071A462D -:101A90003743DF601368002BFCDABDE8F0830429CC -:101AA00080D8013101226DE7DFF874806C2318487B -:101AB00086E712261BE71748DFF8688000FB03F073 -:101AC00043EA08087CE700BF00A4781F00C00F406D -:101AD000000008400046C32300BA3CDC1F85EB51E0 -:101AE00000366E0100800D4040300080FFB19F261F -:101AF000808D5B00819F5E1600B29F267F30018043 -:101B00007FD1F0089F10E5000803002004030020A7 -:101B100000643F4D001BB70023B24C00362000800C -:101B20006C200080002000800001074B1A181B5811 -:101B3000D2685868104202D011B9C3F888207047A3 -:101B4000C3F88420704700BF00000020272801D878 -:101B5000FFF7EABF704700BF27281CD800011A4AC8 -:101B6000012902EB0003105810B415D0042913D03A -:101B7000DC68426822EA040242609A68E9B10229FC -:101B80001ED003295B685DF8044B0CBF0F49104958 -:101B9000116015221A607047DA680129446842EA28 -:101BA000040242609A6808D040F6380111605B6810 -:101BB00015225DF8044B1A60704738211160F6E772 -:101BC00004491160F3E700BF000000203830010035 -:101BD0003800010038F0010004207146084203D0AB -:101BE000EFF3098000F008B8EFF3088000F004B8C4 -:101BF000704700BF704700BF1B4B05211B4A3820B0 -:101C000030B5C2F848110821C2F8380383B05A68C9 -:101C1000174C0A4317485A60C3F88410C3F8881059 -:101C20002368834202D91448FFF734FE0E4D08247E -:101C30000020124A1249C5F884400190019B93424A -:101C400005D8019B01330193019B9342F9D9C5F853 -:101C500088400190019B8B42EDD8019B0133019399 -:101C6000019B8B42F9D9E6E700C01B4000801F4072 -:101C700008030020FF45C32300A3E1113F420F00EA -:101C80003F548900836B30B41BB1536843F4004365 -:101C9000536072B6446B9CB1104B2260D3F8B041D4 -:101CA0000C4217D1D3F8404144F48044C3F840417A -:101CB000D3F8B851D3F840416404F3D5294209D18F -:101CC0000023064C8260C360D4F8B0311943C4F8D5 -:101CD000B0110263426362B630BC704700002E4010 -:101CE00038B50546036B06E0AA6B1C6890476A6B23 -:101CF0002346944208D0184633B1012B04D05A68C9 -:101D00001206F1D52B6338BD00232B636B6338BDFE -:101D1000F0B5F1B9224C23490020234B0122802544 -:101D2000A1600A601A464D60E060D3F8BC4188604B -:101D300044F001141D4DC3F8BC41D3F8B01141F07B -:101D40000101C3F8B0112860D2F8B031002BFBD1EB -:101D5000F0BD0904164B144D0126114C41F08001D1 -:101D600000221E60596000F5805CE26400F5005EB0 -:101D7000A36400F54057D5F8B01100F580462A4617 -:101D8000986041F48031C3F80CC0C3F810E05F6183 -:101D90009E61C5F8B011D2F8B031002BFBD1BAE783 -:101DA000003000202020002000002E40FC030020F6 -:101DB000002000202DE9F04FBB4C83B0D4F84481C3 -:101DC00018F0010FC4F844815ED0D4F8AC31002B78 -:101DD00055D04FF00119DFF8F0B22646B34FCA468E -:101DE000B96AFA6AC4F8AC31D4F8403143F400530C -:101DF000C4F84031D4F840319D04F5D5D6F84031CF -:101E000023F40053C6F84031C6F8B4A1D4F8B43175 -:101E100013F00113FAD188B240F28165CBF800309B -:101E2000A84200F29680B0F5D06F80F0D881B0F56E -:101E3000817F00F0578100F2D580802800F0268154 -:101E4000822840F0C78092B202F07F01072900F299 -:101E5000C18089009648974D0844016810062B7090 -:101E60006B7040F1E581C90301D501232B7002217C -:101E70009048FFF74DFFD4F8AC31002BB0D18A4A1F -:101E8000D2F8BC31002B44D118F0400F18D0864B4B -:101E9000D3F8AC111A46C3F8AC11D3F8BC11C3F88F -:101EA000BC11D2F8B041804B002CFAD14FF0FF3278 -:101EB000C3F8B421D3F8843100F074FB7E4B1C606E -:101EC00018F0807F03D07D4B1B6803B1984718F052 -:101ED000007F03D07A4B1B6803B1984718F0040FBA -:101EE00002D0714BD3F884316F4BD3F848211206DE -:101EF0000CD518F0800F09D072490A78002A00F03A -:101F00008E81531EDBB20B7003B9FBBE03B0BDE87C -:101F1000F08F6D49C2F8BC310868034240F0CC81B3 -:101F2000654A14681C40AFD0630700F1E281670383 -:101F300000F1EF81260700F1E881250300F1E1813E -:101F4000E00600F1DA81E1029ED56048FFF7C8FEA5 -:101F50009AE742F22105A84238D06FD8B0F5086F51 -:101F600000F02F81B0F5106F34D1C1F30741584A0A -:101F7000584811705849594A0193C6F8C801C6F823 -:101F8000CC11C6F8D02100F00FFB554A019B8021EF -:101F90000120FB6410605160D6F8BC11936041F0E1 -:101FA0000111BA64C6F8BC11D6F8B02142F48032EF -:101FB000C6F8B021444A1360D4F8B031002BFBD1ED -:101FC000D4F8AC31002B7FF40BAF58E7100C072886 -:101FD00000F03281C4F8C091D4F8AC31002B7FF40A -:101FE000FFAE4CE740F20235A84200F0B780B0F5F2 -:101FF000A06FEFD13A4A80200125FB645060156044 -:10200000D6F8BC01936040F00113BA64C6F8BC3145 -:10201000D6F8B03143F48033C6F8B031D4F8B031DB -:10202000002BFBD10B0C5B0643F08073C6F85431D8 -:10203000D4F8AC31002B7FF4D3AE20E742F2212359 -:10204000984200F0828042F221339842C2D1244B60 -:1020500001218022196000215A60D4F8BC21BB64A0 -:1020600042F001129960F964C4F8BC21D4F8B0318F -:1020700043F48033C4F8B031D4F8B031002BFBD135 -:10208000D4F8AC31002B7FF4ABAEF8E6094A02215C -:10209000104613705370FFF73BFED4F8AC31002BA1 -:1020A0007FF49EAEEBE600BF00002E400030002023 -:1020B000C0012E4010040020F4030020F003002093 -:1020C000F803002000040020FC0300204032002020 -:1020D000200400200200CC00C80002000200C8005A -:1020E0002020002092B202F07F03072B3FF672AF50 -:1020F00012F0800F4FEA8303884A4FF001011A441F -:10210000136814BF23F4803323F00103136080228B -:10211000834B196000215A60D4F8BC21BB6442F0A3 -:1021200001129960F964C4F8BC21D4F8B03143F4C9 -:102130008033C4F8B031D4F8B031002BFBD1D4F8DF -:10214000AC31002B7FF44CAE99E6764BC1F30741DE -:10215000754A1868754B1060197078E792B202F0F2 -:102160007F03072B3FF636AF12F0800F4FEA830351 -:102170006A4A4FF001011A44136814BF43F48033D4 -:1021800043F0010313608022654B196000215A60FF -:10219000D4F8BC21BB6442F001129960F964C4F820 -:1021A000BC21D4F8B03143F48033C4F8B031D4F852 -:1021B000B031002BFBD1D4F8AC31002B7FF410AE42 -:1021C0005DE65B4A01215B4B127818461A70FFF7F7 -:1021D0009FFDD4F8AC31002B7FF402AE4FE6564B96 -:1021E000586800283FF4F6AE090C1FFA82FE04E09E -:1021F0000C33586800283FF4EDAE1D888D42F7D1AE -:102200005D887545F4D1090A120C03290CBF0178C9 -:102210001989914228BF1146FFF77AFD2BE6D3F8C2 -:10222000482122F08002C3F8482103B0BDE8F08FB6 -:10223000CA077FF51CAE18E6404D0120FB602960FF -:102240003F4B4049186059603F49D6F8B0016A6079 -:1022500001F5005E40F00102A1F5005001F5805546 -:10226000BB609860A1F58050D860C6F8B0211961B4 -:102270005D61C3F818E0D4F8B031002BFBD1284AD7 -:10228000012048F28001FB6410604FF480305160FF -:10229000D6F8BC11936041F00113BA64C6F8BC31A2 -:1022A000D6F8B0310343C6F8B031CBF80000D4F80B -:1022B000B031002BFBD1DEE5204C42F2210000259D -:1022C00024880D6084427FF42BAE2049204C03C942 -:1022D0000D0C86282060A180A5717FF421AED2F874 -:1022E000481150241B4841F08001C2F84811047085 -:1022F00016E61948FFF7F4FC19E61848FFF7F0FC5A -:1023000021E61748FFF7ECFC1AE61648FFF7E8FC51 -:1023100013E61548FFF7E4FC0CE600BFC0012E40B1 -:1023200020200020880D002088320020600D002031 -:102330002004002010040020800200200804002057 -:10234000002000208000070018240020180400202E -:1023500080320020000400200031002000320020E4 -:10236000C03100208031002040310020002AA0F13F -:1023700002022DE9F04714BF00274FF00057022A50 -:1023800001D9BDE8F0874FEAC01ADFF840900D464A -:1023900004460AEB0906002140229846304600F028 -:1023A000DFFC012047EA05414AF80910C6F83880E9 -:1023B000B060B8F1000FE4D0034BA0401C6820438C -:1023C0001860BDE8F08700BFF40300200030002053 -:1023D000002AA0F102022DE9F04714BF00274FF0B8 -:1023E0000057022A01D9BDE8F0874022C501DFF875 -:1023F000449088461544002104461E4605EB090A10 -:10240000504600F0ADFC012247EA084145F80910AA -:10241000CAF83860CAF80820002EE4D004F1100091 -:10242000034B8240186802431A60BDE8F08700BF82 -:10243000F40300200030002012048160C36142F0E8 -:102440008002F0B44260012701F5805601F5005585 -:1024500001F5405401F580420760C66005614461A2 -:102460008261F0BC704700BF831E022B00D9704709 -:1024700030B4064B00F1100401250A4603EBC010EE -:1024800005FA04F130BCFFF7FDBB00BF403000206F -:10249000831E022B00D9704710B4054B01240A4655 -:1024A00004FA00F103EBC0105DF8044BFFF7EABB40 -:1024B00000300020124A134BD2F8200220F07F4057 -:1024C000984210B584B002D800EB800040000E4C5A -:1024D00001A90A2200F07EFA01A90023204611F882 -:1024E000012B01333AB10A2B20F8022FF7D1162322 -:1024F000237004B010BD5B00DBB2237004B010BDCC -:1025000000441F407F969800A40300204368C269DE -:10251000C3F30E43054930B4C3F14003044C002516 -:1025200021F8123024F8125030BC7047F00B002014 -:10253000E80B0020F8B5154B1B783BB903F0FF04FE -:10254000134B1B7813B1134D2A8802B9F8BD124FF3 -:102550002346124EC2F580723978114806EB4116B7 -:1025600000EB01213046FFF767FF31460420FFF7FB -:102570007BFF3B780133DBB2062B98BF3B704FF0FB -:10258000000388BF3C702B80F8BD00BF240B0020E7 -:1025900020040020600C0020250B0020800C00206F -:1025A00024040020704700BF0021E0222048F8B535 -:1025B0000C46204E204D00F0D3FB204F2146204BEF -:1025C0006022347028461F4E1C8000F0C9FB234651 -:1025D0002246102102203C60BC803460B480FFF7AA -:1025E000F7FE2246184B40210320FFF7BFFE23468B -:1025F000224640210420FFF7EBFE234640222846D6 -:102600001249FFF719FF29460320FFF741FF104B3E -:102610004A22104910480860C3F88440C3F880205B -:10262000D3F8482142F08072C3F84821F8BD00BFBA -:10263000800C0020250B0020000C0020F00B002057 -:10264000600C0020E80B0020BD0E0000280B0020CD -:1026500000002E40F0030020E50E0000024A034B6C -:1026600010881B88C01A7047F00B0020E80B002070 -:1026700010B4EFF3108272B6437F33B9017F0129A2 -:1026800008D0032910D00123437702B962B65DF860 -:10269000044B7047114C2168A1B1114943610B688B -:1026A000086083615861EEE70E4C216881B10E49E4 -:1026B00043610B680860836158610C4B4FF0805197 -:1026C0001960E0E7064B4161816120601860DAE73C -:1026D000054B4161816120601860EEE7740D0020B8 -:1026E000700D0020640D0020680D002004ED00E056 -:1026F00010B4047F4160022CC26003D05DF8044B2B -:10270000FFF7B6BF83685DF8044B184770B5EFF369 -:10271000108172B60C4C23688BB10C4E00255A699F -:1027200022607AB1956101B962B65D7718469B68FF -:102730009847EFF3108172B62368002BEFD101B9EF -:1027400062B670BD3260EEE7640D0020680D0020B7 -:10275000FFF7DCBF184A30B41468002C28D0036897 -:1027600021688B420FD2CB1A00218460C1602360A4 -:10277000E0601060022330BC0375704703601446AC -:1027800011688B4208D3A2685B1A002AF6D18260D6 -:10279000C4600360A060EDE7D568CB1A82600222B6 -:1027A000C560E060C16888602360027530BC704716 -:1027B0008460C4601060DDE76C0D0020F8B5224E27 -:1027C00034682CB32368002B3AD11D461F4F04E018 -:1027D0003468ECB12368002B32D1A36803B1DD600B -:1027E00020693360036825751B68BB4221D1037FD4 -:1027F0004560022BC46020D0FFF73AFF6368002BCE -:10280000E6D023602046FFF7A5FF3468002CE1D115 -:10281000EFF3108372B60E4A00211068116003B9FD -:1028200062B628B18468FFF795FF20460028F9D1E9 -:10283000F8BD224600219847E0E783689847DDE726 -:10284000013B2360E4E700BF6C0D0020A1100000F5 -:102850007C0D0020044A054B1168054A1960136875 -:1028600001331360FFF7AABF041000E0840D0020BD -:10287000880D002070B5214C237883B9204B0122AC -:102880001B7822701BBB1F4B1B78002B29D11E4BC2 -:1028900000211A68217012B1EFF3058202B170BDF8 -:1028A000EFF3108072B61A68F2B1184C2178D9B9DA -:1028B0000126556926701D60D5B1A96100B962B6BF -:1028C00000259368104655779847257070BDFFF72F -:1028D000C5FE0028D7D000F015FA0A4B1B78002B54 -:1028E000D5D000F0FBF9D2E70028D8D162B670BD90 -:1028F000074B1D600028E3D1E1E700BF800D0020F9 -:10290000BA030020A80D0020740D0020780D0020CF -:10291000700D0020002852D02DE9F04F814683B081 -:10292000274C0120274D284E54E8003F2A68316883 -:1029300044E80003002BF7D1244F4FF47A7E24485B -:10294000D7F800C0BB4607F1C6470368C1EB0C01CE -:1029500007F5DE1707F67F67A7FB03C3BA46012713 -:102960009B0CB1FBF3F30EFB023854E8003F2A68DE -:10297000316844E80073002BF7D1DBF800C04FF456 -:102980007A7E03680EFB02F2C1EB0C01AAFB033E48 -:10299000C8EB02034FEA9E42B1FBF2F1CA18B2F54E -:1029A0007A7F07D3B9F1010908F57A78DDD103B050 -:1029B000BDE8F08F0190FFF75DFF0198D5E7704704 -:1029C0008C320020880D0020840D0020041000E0CF -:1029D00008030020F0B44E1E0025374600E0013504 -:1029E000B0FBF2F302FB130000F13704092800F1F9 -:1029F0003000E4B298BFC4B2184607F8014F002B6C -:102A0000EDD14A1953704DB1013316F8014F1778C3 -:102A1000E81A3770834202F80149F5DB0846F0BC3A -:102A2000704700BFA4484FF00F0CA44B826F42F4D4 -:102A30007F02F0B582670025D0F880204FF4704601 -:102A40009F4C4FF4604E29464FF4806714432A464A -:102A5000C0F88040A3F88C6148F2B826A3F88EC174 -:102A6000A3F89051B3F8880180B240F0F000A3F8C9 -:102A7000880101EB4100914B0131002540011C46CA -:102A800004290344A3F804E0DF805A841A865A809C -:102A90005A81DE815A82DA825A83DA83E9D1B4F824 -:102AA00088014FF00F0C874B4FF4704680B22946D7 -:102AB0004FF460472A4640EA0C004FF4806EA4F8B9 -:102AC0008801B4F8880180B240F47060A4F88801ED -:102AD000A3F88C6148F2B826A3F88EC1A3F89051F0 -:102AE000B3F8880180B240F0F000A3F8880101EB50 -:102AF0004100744B0131002540011C460429034468 -:102B00009F80A3F806E05A841A865A805A81DE8193 -:102B10005A82DA825A83DA83E9D1B4F888014FF015 -:102B20000F0C694B4FF4704680B229464FF4604752 -:102B30002A4640EA0C004FF4806EA4F88801B4F8ED -:102B4000880180B240F47060A4F88801A3F88C6119 -:102B500048F2B826A3F88EC1A3F89051B3F88801C3 -:102B600080B240F0F000A3F8880101EB4100564B21 -:102B70000131002540011C46042903449F80A3F82D -:102B800006E05A841A865A805A81DE815A82DA8295 -:102B90005A83DA83E9D1B4F888014FF00F0C4B4B1C -:102BA0004FF4704780B229464FF460462A4640EA07 -:102BB0000C004FF4806EA4F88801B4F8880180B24C -:102BC00040F47060A4F88801A3F88C7148F2B8272B -:102BD000A3F88EC1A3F89051B3F8880180B240F0F9 -:102BE000F000A3F8880101EB4100384B01314001AE -:102BF0001C46042903449E80A3F806E05A841A86E2 -:102C00005A805A81DF815A82DA825A83DA83EAD182 -:102C1000B4F888310F27002241F201069BB245F635 -:102C2000C05E114643F226053B43A4F88831B4F850 -:102C300088319BB243F47063A4F888315001244B6F -:102C400001320344042A99815981DF819E82A3F8CD -:102C500006E0198019829D81F0D100220F2741F2F0 -:102C6000010645F6C055114643F226045001194BA2 -:102C700001320344042A99815981DF819E82DD80DB -:102C8000198019829C81F1D100220F2741F201069F -:102C900045F6C055114643F2260450010E4B013251 -:102CA0000344042A99815981DF819E82DD80198045 -:102CB00019829C81F1D1F0BD00C00F4000C03D40A1 -:102CC000000003FC00003E4000403E4000803E40CB -:102CD00000C01D4000001E4000401E4038B5074B9C -:102CE0001C784CB1064D55F8043F002BFBD098479B -:102CF000631E13F0FF04F6D138BD00BFA80D0020FD -:102D0000880D0020014B00221A707047BA03002082 -:102D100070B50F4E0F4D761BB61018BF002405D0AE -:102D2000013455F8043B9847A642F9D10A4E0B4DA1 -:102D3000761B00F065F8B61018BF002406D00134E9 -:102D400055F8043B9847A642F9D170BD70BD00BF4D -:102D500048160060481600604C1600604816006077 -:102D600070B4840746D0541E002A41D0CDB2034629 -:102D700002E0621EE4B3144603F8015B9A07F8D13F -:102D8000032C2ED9CDB245EA05250F2C45EA054581 -:102D900019D903F110022646103E0F2E42F8105C9E -:102DA00042F80C5C42F8085C42F8045C02F1100244 -:102DB000F2D8A4F1100222F00F0204F00F04103236 -:102DC000032C13440DD91E462246043A032A46F822 -:102DD000045BFAD8221F22F003020432134404F0E9 -:102DE00003042CB1C9B21C4403F8011BA342FBD15C -:102DF00070BC704714460346C2E700BF00000000E5 -:102E00005FF800F0E1150060000000000000000025 -:042E1000F8B500BF52 -:102E140000000042C8801F40B8821F400800000024 -:102E240000000042C4801F40B4821F400400000020 -:102E340000C0004224801F4014821F401000000084 -:102E440000C0004228801F4018821F40200000005C -:102E540000C000422C801F401C821F404000000024 -:102E640000C0004234801F4024821F400001000043 -:102E74000040004264811F4054831F40000400004E -:102E84000040004280811F4070831F400000020008 -:102E9400004000427C811F406C831F400000010001 -:102EA4000040004268811F4058831F400008000012 -:102EB400004000423C811F402C831F400100000061 -:102EC4000040004244811F4034831F40040000003E -:102ED4000040004240811F4030831F400200000038 -:102EE4000040004248811F4038831F400800000012 -:102EF4000000004204811F40F4821F4000000400CF -:102F04000000004208811F40F8821F4000000800B2 -:102F14000000004218811F4008831F400000800009 -:102F24000000004214811F4004831F400000400041 -:102F34000000004200811F40F0821F400000020098 -:102F440000000042FC801F40EC821F400000010092 -:102F54000000004224811F4014831F40000000042D -:102F64000000004228811F4018831F400000000811 -:102F7400000000421C811F400C831F400000000120 -:102F84000000004220811F4010831F400000000207 -:102F940000000042EC801F40DC821F400010000053 -:102FA40000000042F0801F40E0821F40002000002B -:102FB4000000004234811F4024831F400000004071 -:102FC4000000004238811F4028831F400000008019 -:102FD4000080004294801F4084821F40000004004F -:102FE40000C0004290801F4080821F40000000808B -:102FF40000800042A8801F4098821F40000080008B -:1030040000800042A4801F4094821F4000004000C2 -:10301400004000426C811F405C831F400010000090 -:1030240000C0004230801F4020821F40800000000A -:1030340000800042C8811F40B8831F400080000008 -:1030440000800042C4811F40B4831F400040000040 -:1030540000800042C0811F40B0831F400020000058 -:1030640000800042BC811F40AC831F400010000060 -:1030740000800042D0811F40C0831F400000020036 -:1030840000800042CC811F40BC831F40000001002F -:10309400000100007403002012000000000600007C -:1030A4000C0300200A00000000020000300300208E -:1030B400430000000007000030030020430000002C -:1030C40000030000A0030020000000000103090425 -:1030D40018030020000000000203090488030020F4 -:1030E4000000000003030904A40300200000000002 -:1030F4000000000000000000000000000D000000BF -:103104000E0000000F00000010000000110000007D -:103114000000803F0029DE07007B9A170A060002A0 -:10312400020000400100000018035400650065001F -:103134006E00730079006400750069006E006F0012 -:1031440009024300020100C0320904000001020226 -:1031540001000524001001052401010104240206D4 -:10316400052406000107058203100010090401006C -:10317400020A00000007050302400000070584025C -:10318400400000001201000202000040C016830447 -:1031940079020102030100001603550053004200A6 -:1031A4002000530065007200690061006C0000009B -:1031B400040309040C0300000000000000000000E8 -:1031C40000000000000000000000010000000000FA -:040000056000100087 -:00000001FF diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py index cab97478..ecd56e9d 100644 --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -6,12 +6,16 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) import stat import odrive +import fibre from fibre import Logger, Event import argparse import yaml from inspect import signature import itertools import time +import tempfile +import io +from typing import Union, Tuple # Assert utils ----------------------------------------------------------------# @@ -39,20 +43,51 @@ def test_assert_eq(observed, expected, range=None, accuracy=None): raise TestFailed("value mismatch: expected {} but observed {}".format(expected, observed)) +# Other utils -----------------------------------------------------------------# + +def disjoint_sets(list_of_sets: list): + while len(list_of_sets): + current_set, list_of_sets = list_of_sets[0], list_of_sets[1:] + did_update = True + while did_update: + did_update = False + for i, s in enumerate(list_of_sets): + if len(current_set.intersection(s)): + current_set = current_set.union(s) + list_of_sets = list_of_sets[:i] + list_of_sets[(i+1):] + did_update = True + yield current_set + +def is_list_like(arg): + return hasattr(arg, '__iter__') and not isinstance(arg, str) + # Test Components -------------------------------------------------------------# -class ODriveTestContext(): +class Component(object): + def __init__(self, parent): + self.parent = parent + +class ODriveComponent(Component): def __init__(self, yaml: dict): self.handle = None self.yaml = yaml - #self.axes = [AxisTestContext(None), AxisTestContext(None)] - self.encoders = [EncoderTestContext(self, 0, None), EncoderTestContext(self, 1, None)] - self.axes = [AxisTestContext(self, 0, None), AxisTestContext(self, 1, None)] + #self.axes = [ODriveAxisComponent(None), ODriveAxisComponent(None)] + self.encoders = [ODriveEncoderComponent(self, 0, None), ODriveEncoderComponent(self, 1, None)] + self.axes = [ODriveAxisComponent(self, 0, None), ODriveAxisComponent(self, 1, None)] + for i in range(1,9): + self.__setattr__('gpio' + str(i), Component(self)) + self.can = Component(self) - def __repr__(self): - return self.yaml['name'] + def get_subcomponents(self): + for enc_ctx in self.encoders: + yield 'encoder' + str(enc_ctx.num), enc_ctx + for axis_ctx in self.axes: + yield 'axis' + str(axis_ctx.num), axis_ctx + for i in range(1,9): + yield ('gpio' + str(i)), getattr(self, 'gpio' + str(i)) + yield 'can', self.can - def make_available(self, logger: Logger): + def prepare(self, logger: Logger): """ Connects to the ODrive """ @@ -70,58 +105,334 @@ class ODriveTestContext(): for axis_idx, axis_ctx in enumerate(self.axes): axis_ctx.handle = self.handle.__dict__['axis{}'.format(axis_idx)] -class MotorTestContext(): + def save_config_and_reboot(self): + self.handle.save_configuration() + try: + self.handle.reboot() + except fibre.ChannelBrokenException: + pass # this is expected + self.handle = None + time.sleep(2) + self.prepare(logger) + +class MotorComponent(Component): def __init__(self, yaml: dict): self.yaml = yaml - - def __repr__(self): - return self.yaml['name'] - def make_available(self, logger: Logger): + def prepare(self, logger: Logger): pass -class AxisTestContext(): - def __init__(self, odrv_ctx: ODriveTestContext, num: int, yaml: dict): +class ODriveAxisComponent(Component): + def __init__(self, odrv_ctx: ODriveComponent, num: int, yaml: dict): self.handle = None self.yaml = odrv_ctx.yaml[f'motor{num}'] # TODO: this is bad naming self.odrv_ctx = odrv_ctx self.num = num - - def __repr__(self): - return str(self.odrv_ctx) + '.axis' + str(self.num) - def make_available(self, logger: Logger): - self.odrv_ctx.make_available(logger) + def prepare(self, logger: Logger): + self.odrv_ctx.prepare(logger) -class EncoderTestContext(): - def __init__(self, odrv_ctx: ODriveTestContext, num: int, yaml: dict): +class ODriveEncoderComponent(Component): + def __init__(self, odrv_ctx: ODriveComponent, num: int, yaml: dict): self.handle = None self.odrv_ctx = odrv_ctx self.num = num - - def __repr__(self): - return str(self.odrv_ctx) + '.encoder' + str(self.num) + self.z = Component(self) + self.a = Component(self) + self.b = Component(self) - def make_available(self, logger: Logger): - self.odrv_ctx.make_available(logger) + def get_subcomponents(self): + return [('z', self.z), ('a', self.a), ('b', self.b)] -class CANTestContext(): + def prepare(self, logger: Logger): + self.odrv_ctx.prepare(logger) + +class EncoderComponent(Component): + def __init__(self, parent: Component, yaml: dict): + Component.__init__(self, parent) + self.yaml = yaml + self.z = Component(self) + self.a = Component(self) + self.b = Component(self) + + def get_subcomponents(self): + return [('z', self.z), ('a', self.a), ('b', self.b)] + +class GeneralPurposeComponent(Component): def __init__(self, yaml: dict): + self.components = {} + for component_yaml in yaml.get('components', []): + if component_yaml['type'] == 'can': + self.components[component_yaml['name']] = CanInterfaceComponent(self, component_yaml) + if component_yaml['type'] == 'uart': + self.components[component_yaml['name']] = SerialPortComponent(self, component_yaml) + if component_yaml['type'] == 'gpio': + self.components['gpio' + str(component_yaml['num'])] = LinuxGpioComponent(self, component_yaml) + + def get_subcomponents(self): + return self.components.items() + +class LinuxGpioComponent(Component): + def __init__(self, parent: Component, yaml: dict): + Component.__init__(self, parent) + self.num = int(yaml['num']) + + def config(self, output: bool): + with open("/sys/class/gpio/gpio{}/direction".format(self.num), "w") as fp: + fp.write('out' if output else '0') + + def write(self, state: bool): + with open("/sys/class/gpio/gpio{}/value".format(self.num), "w") as fp: + fp.write('1' if state else '0') + + +class SerialPortComponent(Component): + def __init__(self, parent: Component, yaml: dict): + Component.__init__(self, parent) + self.yaml = yaml + + def get_subcomponents(self): + yield 'tx', Component(self) + yield 'rx', Component(self) + + def open(self, baudrate: int): + import serial + return serial.Serial(self.yaml['port'], baudrate, timeout=1) + +class CanInterfaceComponent(Component): + def __init__(self, parent: Component, yaml: dict): + Component.__init__(self, parent) self.handle = None self.yaml = yaml - def make_available(self, logger: Logger): + def prepare(self, logger: Logger): if not self.handle is None: return - # TODO: read bus name from yaml import can - self.handle = can.interface.Bus(bustype='socketcan', channel=self.yaml['id'], bitrate=250000) + self.handle = can.interface.Bus(bustype='socketcan', channel=self.yaml['interface'], bitrate=250000) + +class TeensyGpio(Component): + def __init__(self, parent: Component, num: int): + Component.__init__(self, parent) + self.num = num + +class TeensyComponent(Component): + def __init__(self, testrig, yaml: dict): + self.testrig = testrig + self.yaml = yaml + self.gpios = [TeensyGpio(self, i) for i in range(24)] + self.routes = [] + self.previous_routes = object() + + def get_subcomponents(self): + for i, gpio in enumerate(self.gpios): + yield ('gpio' + str(i)), gpio + yield 'program', Component(self) + + def add_route(self, input: TeensyGpio, output: TeensyGpio, noise_enable: TeensyGpio): + self.routes.append((input, output, noise_enable)) + + def commit_routing_config(self, logger: Logger): + if self.previous_routes == self.routes: + self.routes = [] + return + + code = '' + code += 'bool noise = false;\n' + code += 'void setup() {\n' + for i, o, n in self.routes: + code += ' pinMode({}, OUTPUT);\n'.format(o.num) + code += '}\n' + code += 'void loop() {\n' + code += ' noise = !noise;\n' + for i, o, n in self.routes: + if n: + # with noise enable + code += ' digitalWrite({}, digitalRead({}) ? noise : digitalRead({}));\n'.format(o.num, n.num, i.num) + else: + # no noise enable + code += ' digitalWrite({}, digitalRead({}));\n'.format(o.num, i.num) + code += '}\n' + + self.compile_and_program(code) + + self.previous_routes = self.routes + self.routes = [] + + def compile(self, sketchfile, hexfile): + env = os.environ.copy() + env['ARDUINO_COMPILE_DESTINATION'] = hexfile + run_shell( + ['arduino', '--board', 'teensy:avr:teensy40', '--verify', sketchfile], + logger, env = env, timeout = 60) + + def program(self, hex_file_path: str, logger: Logger): + """ + Programs the specified hex file onto the Teensy. + To reset the Teensy, a GPIO of the local system must be connected to the + Teensy's "Program" pin. + """ + + # todo: this should be treated like a regular setup resource + program_gpio = self.testrig.get_directly_connected_components(self.testrig.get_component_name(self) + '.program')[0] + + # Put Teensy into program mode by pulling it's program pin down + program_gpio.config(output = True) + program_gpio.write(False) + time.sleep(0.1) + program_gpio.write(True) + + run_shell(["teensy-loader-cli", "-mmcu=imxrt1062", "-w", hex_file_path], logger, timeout = 5) + time.sleep(0.5) # give it some time to boot + + def compile_and_program(self, code: str): + with io.TextIOWrapper(tempfile.NamedTemporaryFile(suffix='.ino')) as code_fp: + code_fp.write(code) + code_fp.flush() + code_fp.seek(0) + print('Writing code to teensy: ') + print(code_fp.read()) + with tempfile.NamedTemporaryFile(suffix='.hex') as hex_fp: + self.compile(code_fp.name, hex_fp.name) + self.program(hex_fp.name, logger) + +class ProxiedComponent(Component): + def __init__(self, impl, *gpio_tuples): + """ + Each element in gpio_tuples should be a tuple of the form: + (teensy: TeensyComponent, gpio_in, gpio_out, gpio_noise_enable) + """ + Component.__init__(self, getattr(impl, 'parent', None)) + self.impl = impl + assert(all([len(t) == 4 for t in gpio_tuples])) + self.gpio_tuples = list(gpio_tuples) + + def __repr__(self): + return testrig.get_component_name(self.impl) + ' (routed via ' + ', '.join((testrig.get_component_name(t) + ': ' + str(i.num) + ' => ' + str(o.num)) for t, i, o, n in self.gpio_tuples) + ')' + + def prepare(self): + for teensy, gpio_in, gpio_out, gpio_noise_enable in self.gpio_tuples: + teensy.add_route(gpio_in, gpio_out, gpio_noise_enable) + +class TestRig(): + def __init__(self, yaml: dict, logger: Logger): + # Contains all components (including subcomponents). + # Ports are components too. + self.components_by_name = {} # {'name': object, ...} + self.names_by_component = {} # {'name': object, ...} + + def add_component(name, component): + self.components_by_name[name] = component + self.names_by_component[component] = name + if hasattr(component, 'get_subcomponents'): + for subname, subcomponent in component.get_subcomponents(): + add_component(name + '.' + subname, subcomponent) + + for component_yaml in yaml['components']: + if component_yaml['type'] == 'odrive': + add_component(component_yaml['name'], ODriveComponent(component_yaml)) + elif component_yaml['type'] == 'generalpurpose': + add_component(component_yaml['name'], GeneralPurposeComponent(component_yaml)) + elif component_yaml['type'] == 'teensy': + add_component(component_yaml['name'], TeensyComponent(self, component_yaml)) + elif component_yaml['type'] == 'motor': + add_component(component_yaml['name'], MotorComponent(component_yaml)) + elif component_yaml['type'] == 'encoder': + add_component(component_yaml['name'], EncoderComponent(self, component_yaml)) + else: + logger.warn('test rig has unsupported component ' + component_yaml['type']) + continue + + # List of disjunct sets, where each set holds references of the mutually connected components + self.connections = [] + for connection_yaml in yaml['connections']: + self.connections.append(set(self.components_by_name[name] for name in connection_yaml)) + self.connections = list(disjoint_sets(self.connections)) + + # Dict for fast lookup of the connection sets for each port + self.net_by_component = {} + for s in self.connections: + for port in s: + self.net_by_component[port] = s + + def get_components(self, t: type): + """Returns a tuple (name, component) for all components that are of the specified type""" + return (comp for comp in self.names_by_component.keys() if isinstance(comp, t)) + + def get_component_name(self, component: Component): + if isinstance(component, ProxiedComponent): + return self.names_by_component[component.impl] + else: + return self.names_by_component[component] + + def get_directly_connected_components(self, component: Union[str, Component]): + """ + Returns all components that are directly connected to the specified + component, excluding the specified component itself. + """ + if isinstance(component, str): + component = self.components_by_name[component] + result = self.net_by_component.get(component, set([component])) + return [c for c in result if (c != component)] + + def get_connected_components(self, src: Union[dict, Tuple[Union[Component, str], bool]], comp_type: type = None): + """ + Returns all components that are either directly or indirectly (through a + Teensy) connected to the specified component(s). + + component: Either: + - A component object. + - A component name given as string. + - A tuple of the form (comp, dir) where comp is a component object + or name and dir specifies the data direction. + The direction is required if routing through a Teensy should be + considered. + - A dict {sumcomponent: val} where subcomponent is a string + such as 'tx' or 'rx' and val is of one of the forms described above. + + A type can be specified to filter the connected components. + """ + + if isinstance(src, dict): + component_list = [] + for name, subsrc in src.items(): + component_list.append([c for c in self.get_connected_components(subsrc) if self.get_component_name(c).endswith('.' + name)]) + + for combination in itertools.product(*component_list): + if len(set(c.parent for c in combination)) != 1: + continue # parent of the components don't match + proxied_dst = combination[0].parent + if comp_type and not isinstance(proxied_dst, comp_type): + continue # not the requested type + gpio_tuples = [c2 for c in combination for c2 in c.gpio_tuples if isinstance(c, ProxiedComponent)] + if len(gpio_tuples): + yield ProxiedComponent(proxied_dst, *gpio_tuples) + else: + yield proxied_dst + + else: + + if isinstance(src, tuple): + src, dir = src + else: + dir = None + + for dst in self.get_directly_connected_components(src): + if (not comp_type) or isinstance(dst, comp_type): + yield dst + + if (not dir is None) and isinstance(getattr(dst, 'parent', None), TeensyComponent): + teensy = dst.parent + for gpio2 in teensy.gpios: + for proxied_dst in self.get_directly_connected_components(gpio2): + if (not comp_type) or isinstance(proxied_dst, comp_type): + yield ProxiedComponent(proxied_dst, (teensy, dst if dir else gpio2, gpio2 if dir else dst, None)) # Helper functions ------------------------------------------------------------# -def request_state(axis_ctx: AxisTestContext, state, expect_success=True): +def request_state(axis_ctx: ODriveAxisComponent, state, expect_success=True): axis_ctx.handle.requested_state = state time.sleep(0.001) if expect_success: @@ -131,7 +442,7 @@ def request_state(axis_ctx: AxisTestContext, state, expect_success=True): test_assert_eq(axis_ctx.handle.error, AXIS_ERROR_INVALID_STATE) axis_ctx.handle.error = AXIS_ERROR_NONE # reset error -def get_errors(axis_ctx: AxisTestContext): +def get_errors(axis_ctx: ODriveAxisComponent): errors = [] if axis_ctx.handle.motor.error != 0: errors.append("motor failed with error 0x{:04X}".format(axis_ctx.handle.motor.error)) @@ -145,41 +456,12 @@ def get_errors(axis_ctx: AxisTestContext): errors.append("and by the way: axis reports no error even though there is one") return errors -def test_assert_no_error(axis_ctx: AxisTestContext): +def test_assert_no_error(axis_ctx: ODriveAxisComponent): errors = get_errors(axis_ctx) if len(errors) > 0: raise TestFailed("\n".join(errors)) -def yaml_to_test_objects(test_rig_yaml: dict, logger: Logger): - available_test_objects = {} - - def add_component(component): - available_test_objects[type(component)] = available_test_objects.get(type(component), []) - available_test_objects[type(component)].append(component) - - for component_yaml in test_rig_yaml['components']: - if component_yaml['type'] == 'odrive': - odrv_ctx = ODriveTestContext(component_yaml) - add_component(odrv_ctx) - for enc_ctx in odrv_ctx.encoders: - add_component(enc_ctx) - for axis_ctx in odrv_ctx.axes: - add_component(axis_ctx) - elif component_yaml['type'] == 'generalpurpose': - for (k, v) in [(k, v) for (k, v) in component_yaml.items() if k.startswith("can")]: - can_ctx = CANTestContext({'id': k, 'bus': v}) - add_component(can_ctx) - elif component_yaml['type'] == 'motor': - motor_ctx = MotorTestContext(component_yaml) - add_component(motor_ctx) - else: - logger.warn('test rig has unsupported component ' + component_yaml['type']) - continue - - - return available_test_objects - -def run_shell(command_line, logger, timeout=None): +def run_shell(command_line, logger, env=None, timeout=None): """ Runs a shell command in the current directory """ @@ -192,60 +474,95 @@ def run_shell(command_line, logger, timeout=None): cmd = shlex.split(command_line) result = subprocess.run(cmd, timeout=timeout, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, + env=env) if result.returncode != 0: logger.error(result.stdout.decode(sys.stdout.encoding)) raise TestFailed("command {} failed".format(command_line)) -def program_teensy(hex_file_path, program_gpio: int, logger: Logger): - """ - Programs the specified hex file onto the Teensy. - To reset the Teensy, a GPIO of the local system must be connected to the - Teensy's "Program" pin. This pin must first be manually made available to - user space: - - echo 26 | sudo tee /sys/class/gpio/export - sudo chmod a+rw /sys/class/gpio/gpio26/* - echo out > /sys/class/gpio/gpio26/direction +def select_params(param_options): + params = [] - """ + # Select parameters from the resource list + # (this could be arbitrarily complex to improve parallelization of the tests) + for param in param_options: + if is_list_like(param): + if len(param) == 0: + return None + else: + selection = param[0] + if is_list_like(selection): + params = params + list(selection) + else: + params.append(selection) + else: + params.append(param) + return params - # Put Teensy into program mode by pulling it's program pin down - with open("/sys/class/gpio/gpio{}/direction".format(program_gpio), "w") as fp: - fp.write("out") - with open("/sys/class/gpio/gpio{}/value".format(program_gpio), "w") as gpio: - gpio.write("0") - time.sleep(0.1) - with open("/sys/class/gpio/gpio{}/value".format(program_gpio), "w") as gpio: - gpio.write("1") - - run_shell(["teensy_loader_cli", "-mmcu=imxrt1062", "-w", hex_file_path], logger, timeout = 5) - time.sleep(0.5) # give it some time to boot +def run(tests): + if not isinstance(tests, list): + tests = [tests] -def run(test_cases): - if not isinstance(test_cases, list): - test_cases = [test_cases] + for test in tests: + # The result of get_test_cases can be described in ABNF grammar: + # test-case-list = *arglist + # arglist = *flexible-arg + # flexible-arg = component / *argvariant + # argvariant = component / arglist + # + # If for a particular test-case, the components are not given plainly + # but in some selectable form, the test driver will select exactly one + # of those options. + # In other words, it will bring arglist from the form *flexible-arg + # into the form *component before calling the test. + # + # All of the provided test-cases are executed. If none is provided, + # a warning is reported. A warning is also reported if for a particular + # test case no component combination can be resolved. - for test_case in test_cases: - # Compile a list of list of potential objects that might be compatible with this - # test - possible_parameters = [] - sig = signature(test_case.is_compatible) - for param_name in sig.parameters: - param_type = sig.parameters[param_name].annotation - possible_parameters.append(available_test_objects[param_type]) + test_cases = list(test.get_test_cases(testrig)) - # For each combination, check if the test is compatible with these objects - for param_combination in itertools.product(*possible_parameters): - if not test_case.is_compatible(*param_combination): + if len(test_cases) == 0: + logger.warn('no resources are available to conduct the test {}'.format(type(test).__name__)) + continue + + for test_case in test_cases: + params = select_params(test_case) + if params is None: + logger.warn('no resources are available to conduct the test {}'.format(type(test).__name__)) continue - - for param in param_combination: - param.make_available(logger) - logger.notify('* running {} on {}...'.format(type(test_case).__name__, - [str(p) for p in param_combination])) - test_case.run_test(*param_combination, logger) + logger.notify('* preparing {} with {}...'.format(type(test).__name__, + [(testrig.get_component_name(p) if isinstance(p, Component) else str(p)) for p in params])) + + teensies = set() + for param in params: + if isinstance(param, ProxiedComponent): + param.prepare() + for teensy, _, _, _ in param.gpio_tuples: + teensies.add(teensy) + + for teensy in teensies: + teensy.commit_routing_config(logger) + + # prepare all components + teensies = set() + for param in params: + if isinstance(param, ProxiedComponent): + continue + if hasattr(param, 'prepare'): + param.prepare(logger) + + logger.notify('* running {} on {}...'.format(type(test).__name__, + [(testrig.get_component_name(p) if isinstance(p, Component) else str(p)) for p in params])) + + # Resolve routed components + for i, param in enumerate(params): + if isinstance(param, ProxiedComponent): + params[i] = param.impl + + test.run_test(*params, logger) + logger.success('All tests passed!') @@ -269,7 +586,7 @@ args = parser.parse_args() test_rig_yaml = yaml.load(args.test_rig_yaml, Loader=yaml.BaseLoader) logger = Logger() -available_test_objects = yaml_to_test_objects(test_rig_yaml, logger) +testrig = TestRig(test_rig_yaml, logger) if args.setup_host: @@ -285,3 +602,22 @@ if args.setup_host: export_gpio(26) # connected to Teensy Program pin os.chmod("/dev/ttyS0", stat.S_IROTH | stat.S_IWOTH) + + # This breaks the retarded teensy loader that shows up on every compile + if not os.path.isfile('/usr/share/arduino/hardware/tools/teensy_post_compile_old'): + os.rename('/usr/share/arduino/hardware/tools/teensy_post_compile', '/usr/share/arduino/hardware/tools/teensy_post_compile_old') + with open('/usr/share/arduino/hardware/tools/teensy_post_compile', 'w') as scr: + scr.write('#!/bin/bash\n') + scr.write('if [ "$ARDUINO_COMPILE_DESTINATION" != "" ]; then\n') + scr.write(' cp -r ${2#-path=}/*.ino.hex ${ARDUINO_COMPILE_DESTINATION}\n') + scr.write('fi\n') + os.chmod('/usr/share/arduino/hardware/tools/teensy_post_compile', stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) + + # Bring up CAN interface(s) + for intf in testrig.get_components(CanInterfaceComponent): + name = intf.yaml['interface'] + run_shell('ip link set dev {} down'.format(intf)) + run_shell('ip link set dev {} type can bitrate 250000'.format(intf)) + run_shell('ip link set dev {} type can loopback off'.format(intf)) + run_shell('ip link set dev {} up'.format(intf)) + diff --git a/tools/odrive/tests/uart_ascii_test.py b/tools/odrive/tests/uart_ascii_test.py index a4e78a49..71c8bd7d 100644 --- a/tools/odrive/tests/uart_ascii_test.py +++ b/tools/odrive/tests/uart_ascii_test.py @@ -5,13 +5,12 @@ import struct import time import os import io -import serial import functools import operator from fibre.utils import Logger from odrive.enums import * -from test_runner import ODriveTestContext, test_assert_eq, test_assert_no_error, program_teensy +from test_runner import * def append_checksum(command): @@ -32,28 +31,29 @@ def reset_state(ser): ser.flushInput() # discard response class TestUartAscii(): - def is_compatible(self, odrive: ODriveTestContext): - return True + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + ports = list(testrig.get_connected_components({ + 'rx': (odrive.gpio1, True), + 'tx': (odrive.gpio2, False) + }, SerialPortComponent)) + yield (odrive, ports) - def run_test(self, odrive: ODriveTestContext, logger: Logger): + def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): """ Tests the most important functions of the ASCII protocol. """ - # Disable noise - with open("/sys/class/gpio/gpio{}/direction".format(20), "w") as fp: - fp.write("out") - with open("/sys/class/gpio/gpio{}/value".format(20), "w") as gpio: - gpio.write("0") - - hexfile = 'uart_pass_through.ino.hex' - program_teensy(os.path.join(os.path.dirname(__file__), hexfile), 26, logger) - time.sleep(1.0) + if (odrive.handle.config.gpio1_pwm_mapping.endpoint != (0,0)) or (odrive.handle.config.gpio2_pwm_mapping.endpoint != (0,0)): + logger.debug('UART pins in use. Reconfiguring...') + odrive.handle.config.gpio1_pwm_mapping.endpoint = None + odrive.handle.config.gpio2_pwm_mapping.endpoint = None + odrive.save_config_and_reboot() odrive.handle.axis0.config.enable_step_dir = False odrive.handle.config.enable_uart = True - with serial.Serial('/dev/ttyS0', 115200, timeout=1) as ser: + with port.open(115200) as ser: # reset port to known state reset_state(ser) @@ -159,84 +159,24 @@ class TestUartAscii(): # TODO: test cases for 't', 'ss', 'se', 'sr' commands - -class TestUartNoise(): - def is_compatible(self, odrive: ODriveTestContext): - return True - - def run_test(self, odrive: ODriveTestContext, logger: Logger): - """ - Tests if the UART can handle invalid signals. - """ - - # Disable noise - with open("/sys/class/gpio/gpio{}/direction".format(20), "w") as fp: - fp.write("out") - with open("/sys/class/gpio/gpio{}/value".format(20), "w") as gpio: - gpio.write("0") - - hexfile = 'uart_pass_through.ino.hex' - program_teensy(os.path.join(os.path.dirname(__file__), hexfile), 26, logger) - time.sleep(1.0) - - odrive.handle.axis0.config.enable_step_dir = False - odrive.handle.config.enable_uart = True - - with serial.Serial('/dev/ttyS0', 115200, timeout=1) as ser: - # reset port to known state - reset_state(ser) - - # Enable square wave of ~1.6MHz on the ODrive's RX line - with open("/sys/class/gpio/gpio{}/value".format(20), "w") as gpio: - gpio.write("1") - - time.sleep(0.1) - reset_state(ser) - - # Read an attribute (should fail because the command is not passed through) - ser.write(b'r vbus_voltage\n') - test_assert_eq(ser.readline(), b'') - - # Disable square wave - with open("/sys/class/gpio/gpio{}/value".format(20), "w") as gpio: - gpio.write("0") - - # Give receiver some time to recover - time.sleep(0.1) - - # reset port to known state - reset_state(ser) - - # Try again - ser.write(b'r vbus_voltage\n') - response = float(ser.readline().strip()) - test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) - - - class TestUartBurnIn(): - def is_compatible(self, odrive: ODriveTestContext): - return True + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + ports = list(testrig.get_connected_components({ + 'rx': (odrive.gpio1, True), + 'tx': (odrive.gpio2, False) + }, SerialPortComponent)) + yield (odrive, ports) - def run_test(self, odrive: ODriveTestContext, logger: Logger): + def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): """ Tests if the ASCII protocol can handle 64kB of random data being thrown at it. """ - # Disable noise - with open("/sys/class/gpio/gpio{}/direction".format(20), "w") as fp: - fp.write("out") - with open("/sys/class/gpio/gpio{}/value".format(20), "w") as gpio: - gpio.write("0") - - hexfile = 'uart_pass_through.ino.hex' - program_teensy(os.path.join(os.path.dirname(__file__), hexfile), 26, logger) - time.sleep(1.0) - odrive.handle.axis0.config.enable_step_dir = False odrive.handle.config.enable_uart = True - with serial.Serial('/dev/ttyS0', 115200, timeout=1) as ser: + with port.open(115200) as ser: with open('/dev/random', 'rb') as rand: buf = rand.read(65536) ser.write(buf) @@ -250,9 +190,81 @@ class TestUartBurnIn(): test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) +class TestUartNoise(): + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + # For every ODrive, find a connected serial port which has a teensy + # in between, so that we can inject noise, + + ports = list(testrig.get_connected_components({ + 'rx': (odrive.gpio1, True), + 'tx': (odrive.gpio2, False) + }, SerialPortComponent)) + + # Hack the bus objects to enable noise_enable functionality on the TX line. + + def get_noise_gpio(bus): + teensy = bus.gpio_tuples[1][0] + for teensy_gpio in teensy.gpios: + for other_gpio in testrig.get_directly_connected_components(teensy_gpio): + if isinstance(other_gpio, LinuxGpioComponent): + return teensy_gpio, other_gpio + return None + + for idx, bus in enumerate(ports): + noise_gpio_on_teensy, noise_gpio_on_rpi = get_noise_gpio(bus) + assert(noise_gpio_on_rpi) + t, i, o, _ = bus.gpio_tuples[1] + bus.gpio_tuples[1] = (t, i, o, noise_gpio_on_teensy) + ports[idx] = (bus, noise_gpio_on_rpi) + + yield (odrive, ports) + + def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, noise_enable: LinuxGpioComponent, logger: Logger): + """ + Tests if the UART can handle invalid signals. + """ + noise_enable.config(output=True) + noise_enable.write(False) + time.sleep(0.1) + + odrive.handle.axis0.config.enable_step_dir = False + odrive.handle.config.enable_uart = True + + with port.open(115200) as ser: + # reset port to known state + reset_state(ser) + + # Enable square wave of ~1.6MHz on the ODrive's RX line + noise_enable.write(True) + + time.sleep(0.1) + reset_state(ser) + + time.sleep(1.0) + + # Read an attribute (should fail because the command is not passed through) + ser.write(b'r vbus_voltage\n') + test_assert_eq(ser.readline(), b'') + + # Disable square wave + noise_enable.write(False) + + # Give receiver some time to recover + time.sleep(0.1) + + # reset port to known state + reset_state(ser) + + # Try again + ser.write(b'r vbus_voltage\n') + response = float(ser.readline().strip()) + test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) + + if __name__ == '__main__': test_runner.run([ TestUartAscii(), - TestUartNoise(), TestUartBurnIn(), + TestUartNoise(), ]) diff --git a/tools/odrive/tests/uart_pass_through.ino.hex b/tools/odrive/tests/uart_pass_through.ino.hex deleted file mode 100644 index 8175c58e..00000000 --- a/tools/odrive/tests/uart_pass_through.ino.hex +++ /dev/null @@ -1,786 +0,0 @@ -:0200000460009A -:100000004643464200000156000000000101020084 -:1000100000000000000000000000000000000000E0 -:1000200000000000000000000000000000000000D0 -:1000300000000000000000000000000000000000C0 -:1000400000000000010403000000000000000000A8 -:100050000000200000000000000000000000000080 -:100060000000000000000000000000000000000090 -:100070000000000000000000000000000000000080 -:10008000EB04180A063204260000000000000000FD -:10009000050404240000000000000000000000002F -:1000A0000000000000000000000000000000000050 -:1000B0000604000000000000000000000000000036 -:1000C0000000000000000000000000000000000030 -:1000D00020041808000000000000000000000000DC -:1000E0000000000000000000000000000000000010 -:1000F0000000000000000000000000000000000000 -:10010000D8041808000000000000000000000000F3 -:100110000204180804200000000000000000000095 -:1001200000000000000000000000000000000000CF -:10013000600400000000000000000000000000005B -:1001400000000000000000000000000000000000AF -:10015000000000000000000000000000000000009F -:10016000000000000000000000000000000000008F -:10017000000000000000000000000000000000007F -:10018000000000000000000000000000000000006F -:10019000000000000000000000000000000000005F -:1001A000000000000000000000000000000000004F -:1001B000000000000000000000000000000000003F -:1001C000000100000010000001000000000000001D -:1001D000000001000000000000000000000000001E -:1001E000000000000000000000000000000000000F -:1001F00000000000000000000000000000000000FF -:10020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -:10021000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE -:10022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE -:10023000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE -:10024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE -:10025000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE -:10026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E -:10027000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E -:10028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E -:10029000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E -:1002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E -:1002B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E -:1002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E -:1002D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E -:1002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E -:1002F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E -:10030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD -:10031000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED -:10032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD -:10033000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD -:10034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD -:10035000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD -:10036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D -:10037000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D -:10038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D -:10039000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D -:1003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D -:1003B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D -:1003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D -:1003D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D -:1003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D -:1003F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D -:10040000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC -:10041000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC -:10042000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC -:10043000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC -:10044000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC -:10045000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC -:10046000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C -:10047000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C -:10048000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C -:10049000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C -:1004A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C -:1004B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C -:1004C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C -:1004D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C -:1004E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C -:1004F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C -:10050000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB -:10051000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB -:10052000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB -:10053000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB -:10054000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB -:10055000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB -:10056000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B -:10057000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B -:10058000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B -:10059000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B -:1005A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B -:1005B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B -:1005C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B -:1005D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B -:1005E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B -:1005F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B -:10060000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA -:10061000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA -:10062000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA -:10063000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA -:10064000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA -:10065000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA -:10066000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A -:10067000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A -:10068000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A -:10069000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A -:1006A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A -:1006B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A -:1006C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A -:1006D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A -:1006E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A -:1006F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A -:10070000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 -:10071000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9 -:10072000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9 -:10073000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9 -:10074000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9 -:10075000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9 -:10076000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99 -:10077000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89 -:10078000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79 -:10079000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69 -:1007A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59 -:1007B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49 -:1007C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39 -:1007D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29 -:1007E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19 -:1007F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09 -:10080000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8 -:10081000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8 -:10082000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8 -:10083000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8 -:10084000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8 -:10085000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8 -:10086000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98 -:10087000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88 -:10088000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78 -:10089000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68 -:1008A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58 -:1008B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48 -:1008C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38 -:1008D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28 -:1008E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18 -:1008F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08 -:10090000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7 -:10091000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7 -:10092000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7 -:10093000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7 -:10094000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7 -:10095000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7 -:10096000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97 -:10097000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87 -:10098000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77 -:10099000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67 -:1009A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57 -:1009B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47 -:1009C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37 -:1009D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27 -:1009E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17 -:1009F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07 -:100A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6 -:100A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6 -:100A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6 -:100A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6 -:100A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6 -:100A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6 -:100A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96 -:100A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86 -:100A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76 -:100A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66 -:100AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56 -:100AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46 -:100AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36 -:100AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26 -:100AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16 -:100AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06 -:100B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5 -:100B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5 -:100B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5 -:100B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5 -:100B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5 -:100B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5 -:100B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95 -:100B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85 -:100B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75 -:100B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65 -:100BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55 -:100BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45 -:100BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35 -:100BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25 -:100BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15 -:100BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05 -:100C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4 -:100C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4 -:100C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4 -:100C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4 -:100C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4 -:100C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4 -:100C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94 -:100C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84 -:100C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74 -:100C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64 -:100CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54 -:100CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44 -:100CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34 -:100CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24 -:100CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14 -:100CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04 -:100D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 -:100D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3 -:100D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3 -:100D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3 -:100D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3 -:100D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3 -:100D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93 -:100D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83 -:100D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73 -:100D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63 -:100DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53 -:100DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43 -:100DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33 -:100DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23 -:100DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13 -:100DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03 -:100E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2 -:100E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2 -:100E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2 -:100E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2 -:100E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2 -:100E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2 -:100E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92 -:100E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82 -:100E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72 -:100E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62 -:100EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52 -:100EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42 -:100EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32 -:100ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22 -:100EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12 -:100EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02 -:100F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1 -:100F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1 -:100F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1 -:100F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1 -:100F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1 -:100F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1 -:100F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91 -:100F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81 -:100F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71 -:100F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61 -:100FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51 -:100FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41 -:100FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31 -:100FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21 -:100FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11 -:100FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01 -:10100000D10020402C100060000000000000000013 -:1010100020100060001000600000000000000000D0 -:1010200000000060E030000000000000000001202F -:1010300035100060764B0720764C4FF42A01764A33 -:101040005C64186499639546744A75498A420FD066 -:10105000744B9A420CD2D4430846234423F0030332 -:1010600004330B4450F8041B984242F8041BF9D196 -:101070006D4A6E498A420FD06D4B9A420CD2D443CE -:101080000846234423F0030304330B4450F8041BA5 -:10109000984242F8041BF9D1664A674B9A420BD238 -:1010A000D04311460024034423F0030304331344C4 -:1010B00041F8044B8B42FBD1604A4FF47001604B06 -:1010C000116003F530715F4A43F8042F9942FBD158 -:1010D0005D4B80215D4A03F8011B9342FBD15C4CC0 -:1010E0004FF080375B4E4FF0FF32DFF8A8A10025AC -:1010F000DFF8A491DFF8A481574B4549C3F800A05D -:10110000C4F80471C4F80091C4F8F470C4F8F08015 -:10111000F36923F07F0343F04003F361736A23F024 -:101120007F0343F0400373628A66CA660A674A67B0 -:1011300000F0B6F8494A6320494B4A49106003223F -:101140001D60CAF8381043F8082C4749474A4848F8 -:10115000C3F8082D0B68474A43F08073CAF83C0077 -:1011600045480B601368454943F001031360036869 -:101170000B6000F0E5F8C4F804714148C4F8009130 -:10118000C4F8F470C4F8F08000F056FA00BF00BF55 -:1011900000BF00BFF16E3B4A41F440513A4BF1664B -:1011A0001560C2F80851C2F81851C2F82851C2F8A7 -:1011B00038519A6BD20708D442F615623349596503 -:1011C0001A659A6B42F001029A632F4A304C936879 -:1011D00043F00113936000F013FA2368132BFCD93A -:1011E00000F05CF900F0D0F900F012FA00F0DAF843 -:1011F00000F00AFA2368B3F5967FFBD300F020FADB -:1012000000F012FA00F0F4F900F0F6F9FAE700BF86 -:1012100000C00A40ABAAAAAA008007200000000074 -:1012200050160060D016000000000020242D006041 -:10123000C0030020C0030020C032002088ED00E081 -:10124000FC0F00201102000000E400E0A0E400E038 -:1012500000800D4000C00F4008ED00E014E000E009 -:1012600018E000E0890D0000FCED00E00000202007 -:101270008D0E0000001000E0041000E0840D00203E -:101280000046C3230040084000400D400000C05607 -:10129000880D0020001000201B1018200C0D1113C9 -:1012A000F0B5194A0021194B4FF0100E18480124CF -:1012B000184E194D0160194FC2F800E01E6015600C -:1012C000174E184D1F601660174F1D60174E184DB2 -:1012D00017601E60174F1560174E184D1F6016607F -:1012E0001D600460BFF34F8FBFF36F8FC3F8B01161 -:1012F000BFF34F8FBFF36F8F52F8883C43F44033F6 -:1013000042F8883CF0BD00BF9CED00E0A0ED00E09D -:1013100094ED00E0250008031100200021000207E1 -:1013200012000020250008131300202027000B13B3 -:101330001400004033001013150000602F000B074D -:10134000F0B4174A40274FF480314FF480564FF4E1 -:1013500000554FF4404443F24200136913F0020F6A -:1013600006D0946151619061136913F0020FF8D1B6 -:1013700013F4005F01D15561EFE713F4805F01D1F1 -:101380005661EAE7002BE8DA13F4803F01D091615F -:10139000E3E75B0601D45761DFE7F0BC704700BFAD -:1013A00000800D40364A03203649F3EE096A13687F -:1013B00023F00103F0B51360C2F89000D1F8E030DB -:1013C000DFF8D4E0DCB2C3F30B202F4FC0EB135394 -:1013D00007EE904AA4F15501CEF80040B8EEE77A46 -:1013E00003EB830407EE900A03FB01F13B6003EB80 -:1013F0008313B8EEE75A07EE901A091B77EE666A78 -:10140000B8EE677A214D07EE901A0B44C5ED006ADD -:10141000F8EE677A1E4EC7EE265A1E4930601068F5 -:1014200087EEA66A07EE903AF8EE677A87EEA67A1C -:1014300075EE855A76EE056AFCEEE55AFCEEE66A34 -:1014400015EE903A40EA035316EE900A77EE057ACD -:10145000136001EA0041D2F81031FCEEE77A0B4349 -:10146000C2F8103117EE903AD2F81011C3F30B0303 -:101470000B43C2F81031136843F002031360F0BD50 -:1014800080810D4000441F40F0030020EC03002049 -:10149000F40300200000FF0FE8030020304B40F66B -:1014A000617270B5C3F8202140F2044500F0B8F82D -:1014B0002C492D48D1F880202C4C42F003022C4BB3 -:1014C000C1F88020C0F86051226813401BB9D0F8E1 -:1014D000A8319A071AD0244B4FF00041234A516398 -:1014E0001A46D3F8401141F00201C3F84011D2F876 -:1014F00040319B07FBD44FF400301E491B4B4FF08B -:101500000042086019209A6300F092F81A4D0022F8 -:10151000164B4FF08041144C0A26996328461A60F6 -:101520001146C4F8A8614FF4207200F075F84FF42A -:1015300081064FF4800040F24313104A10492E6098 -:101540002864C4F85851C2F80412C4F848310D4A4E -:101550004FF4003101231160C4F8403170BD00BF69 -:1015600000800D4000C00F4000002E4000900D4054 -:10157000001C1E008CE200E0003000200010002063 -:10158000ED0300000CE100E0114B1249D86E0A4651 -:1015900040F4403030B4D86640F2B765D86EA0242D -:1015A00040F44070D8664D648C64936C1B06FCD488 -:1015B000094B40F2B760A021064A58649964936CC5 -:1015C00013F08003FBD1054A137030BC704700BF95 -:1015D00000C00F4000400C4000800C40810D0020F6 -:1015E000F8B500BFF8BC08BC9E467047FFFFFFFF80 -:1015F0005FF800F0B10000005FF800F0AD0E0000F1 -:101600005FF800F0290200005FF800F02D020000F2 -:101610005FF800F0F11200005FF800F02D160000F6 -:101620005FF800F0ED0A00005FF800F0AD00000088 -:101630005FF800F0E11100005FF800F04D0F0000CE -:101640005FF800F0DD150000450000001501000006 -:10165000000000000000000000000000000000008A -:10166000000000000000000000000000000000007A -:1016700010B5054C237833B9044B13B10448AFF3CC -:1016800000800123237010BDC00300200000000073 -:10169000D4160000084B10B51BB108490848AFF339 -:1016A00000800848036803B910BD074B002BFBD02E -:1016B000BDE81040184700BF00000000C403002030 -:1016C000D4160000C00300200000000008B5084B3D -:1016D0000121187800F084F8064B0121187800F0F9 -:1016E0007FF8054B00211878BDE8084000F078B875 -:1016F000DC030020F0020020EC020020FFF7E6BF30 -:10170000124B70B5187800F065F8114B114C054676 -:10171000187800F05FF823780F4A064683F001033B -:101720001078237000F056F80C4B50B931461878F9 -:1017300000F04AF80A4B29461878BDE8704000F0DE -:1017400043B82178187800F03FF8F3E7E40300206D -:10175000F8020020E0030020EC020020DC0300205F -:10176000F0020020044A054B106805491A68054B31 -:1017700008601A60704700BFFC020020F4020020DD -:10178000E4030020DC0300200001074B1A181B585B -:10179000D2685868104202D011B9C3F88820704747 -:1017A000C3F88420704700BF000000200001054BF3 -:1017B0001A5819189268CB681A4214BF01200020E9 -:1017C000704700BF00000020272801D8FFF7DCBFCA -:1017D000704700BF272801D8FFF7E8BF00207047F7 -:1017E00027281CD800011A4A012902EB00031058CF -:1017F00010B415D0042913D0DC68426822EA040230 -:1018000042609A68E9B102291ED003295B685DF83D -:10181000044B0CBF0F491049116015221A60704724 -:10182000DA680129446842EA040242609A6808D0F2 -:1018300040F6380111605B6815225DF8044B1A60B0 -:10184000704738211160F6E704491160F3E700BFE3 -:1018500000000020383001003800010038F001009D -:1018600004207146084203D0EFF3098000F008B865 -:10187000EFF3088000F004B8704700BF704700BF66 -:101880001B4B05211B4A382030B5C2F848110821EE -:10189000C2F8380383B05A68174C0A4317485A6095 -:1018A000C3F88410C3F888102368834202D914480F -:1018B00000F074FE0E4D08240020124A1249C5F8AB -:1018C00084400190019B934205D8019B0133019311 -:1018D000019B9342F9D9C5F888400190019B8B4246 -:1018E000EDD8019B01330193019B8B42F9D9E6E7C7 -:1018F00000C01B4000801F40B4030020FF45C323ED -:1019000000A3E1113F420F003F548900836B30B4C4 -:101910001BB1536843F40043536072B6446B9CB1EF -:10192000104B2260D3F8B0410C4217D1D3F840419C -:1019300044F48044C3F84041D3F8B851D3F840414F -:101940006404F3D5294209D10023064C8260C360A8 -:10195000D4F8B0311943C4F8B0110263426362B6DF -:1019600030BC704700002E4038B50546036B06E0DA -:10197000AA6B1C6890476A6B2346944208D01846AD -:1019800033B1012B04D05A681206F1D52B6338BD50 -:1019900000232B636B6338BDF0B5F1B9224C2349AA -:1019A0000020234B01228025A1600A601A464D6069 -:1019B000E060D3F8BC41886044F001141D4DC3F8C9 -:1019C000BC41D3F8B01141F00101C3F8B011286057 -:1019D000D2F8B031002BFBD1F0BD0904164B144DE9 -:1019E0000126114C41F0800100221E60596000F573 -:1019F000805CE26400F5005EA36400F54057D5F812 -:101A0000B01100F580462A46986041F48031C3F851 -:101A10000CC0C3F810E05F619E61C5F8B011D2F848 -:101A2000B031002BFBD1BAE700300020202000208D -:101A300000002E4004040020002000202DE9F04F7B -:101A4000BB4C83B0D4F8448118F0010FC4F8448132 -:101A50005ED0D4F8AC31002B55D04FF00119DFF82F -:101A6000F0B22646B34FCA46B96AFA6AC4F8AC3136 -:101A7000D4F8403143F40053C4F84031D4F8403135 -:101A80009D04F5D5D6F8403123F40053C6F8403113 -:101A9000C6F8B4A1D4F8B43113F00113FAD188B266 -:101AA00040F28165CBF80030A84200F29680B0F594 -:101AB000D06F80F0D881B0F5817F00F0578100F2BF -:101AC000D580802800F02681822840F0C78092B21D -:101AD00002F07F01072900F2C18089009648974DE6 -:101AE0000844016810062B706B7040F1E581C90352 -:101AF00001D501232B7002219048FFF74DFFD4F848 -:101B0000AC31002BB0D18A4AD2F8BC31002B44D181 -:101B100018F0400F18D0864BD3F8AC111A46C3F812 -:101B2000AC11D3F8BC11C3F8BC11D2F8B041804B52 -:101B3000002CFAD14FF0FF32C3F8B421D3F884312E -:101B400000F074FB7E4B1C6018F0807F03D07D4B4F -:101B50001B6803B1984718F0007F03D07A4B1B68CD -:101B600003B1984718F0040F02D0714BD3F88431B9 -:101B70006F4BD3F8482112060CD518F0800F09D00E -:101B800072490A78002A00F08E81531EDBB20B7076 -:101B900003B9FBBE03B0BDE8F08F6D49C2F8BC319C -:101BA0000868034240F0CC81654A14681C40AFD0FD -:101BB000630700F1E281670300F1EF81260700F17E -:101BC000E881250300F1E181E00600F1DA81E1021C -:101BD0009ED56048FFF7C8FE9AE742F22105A84269 -:101BE00038D06FD8B0F5086F00F02F81B0F5106FC6 -:101BF00034D1C1F30741584A584811705849594ADD -:101C00000193C6F8C801C6F8CC11C6F8D02100F07F -:101C10000FFB554A019B80210120FB64106051603D -:101C2000D6F8BC11936041F00111BA64C6F8BC113A -:101C3000D6F8B02142F48032C6F8B021444A13608D -:101C4000D4F8B031002BFBD1D4F8AC31002B7FF4A9 -:101C50000BAF58E7100C072800F03281C4F8C09190 -:101C6000D4F8AC31002B7FF4FFAE4CE740F20235E4 -:101C7000A84200F0B780B0F5A06FEFD13A4A8020BB -:101C80000125FB6450601560D6F8BC01936040F0FC -:101C90000113BA64C6F8BC31D6F8B03143F48033CE -:101CA000C6F8B031D4F8B031002BFBD10B0C5B0679 -:101CB00043F08073C6F85431D4F8AC31002B7FF474 -:101CC000D3AE20E742F22123984200F0828042F214 -:101CD00021339842C2D1244B012180221960002176 -:101CE0005A60D4F8BC21BB6442F001129960F964D7 -:101CF000C4F8BC21D4F8B03143F48033C4F8B03117 -:101D0000D4F8B031002BFBD1D4F8AC31002B7FF4E8 -:101D1000ABAEF8E6094A0221104613705370FFF784 -:101D20003BFED4F8AC31002B7FF49EAEEBE600BF57 -:101D300000002E4000300020C0012E40180400207A -:101D4000FC030020F8030020000400200804002009 -:101D50000404002040320020280400200200CC00AF -:101D6000C80002000200C8002020002092B202F049 -:101D70007F03072B3FF672AF12F0800F4FEA830309 -:101D8000884A4FF001011A44136814BF23F48033CA -:101D900023F0010313608022834B196000215A60F5 -:101DA000D4F8BC21BB6442F001129960F964C4F814 -:101DB000BC21D4F8B03143F48033C4F8B031D4F846 -:101DC000B031002BFBD1D4F8AC31002B7FF44CAEFA -:101DD00099E6764BC1F30741754A1868754B106058 -:101DE000197078E792B202F07F03072B3FF636AF07 -:101DF00012F0800F4FEA83036A4A4FF001011A4440 -:101E0000136814BF43F4803343F00103136080224E -:101E1000654B196000215A60D4F8BC21BB6442F0C4 -:101E200001129960F964C4F8BC21D4F8B03143F4CC -:101E30008033C4F8B031D4F8B031002BFBD1D4F8E2 -:101E4000AC31002B7FF410AE5DE65B4A01215B4BA9 -:101E5000127818461A70FFF79FFDD4F8AC31002BAA -:101E60007FF402AE4FE6564B586800283FF4F6AEBA -:101E7000090C1FFA82FE04E00C33586800283FF476 -:101E8000EDAE1D888D42F7D15D887545F4D1090A04 -:101E9000120C03290CBF01781989914228BF114601 -:101EA000FFF77AFD2BE6D3F8482122F08002C3F831 -:101EB000482103B0BDE8F08FCA077FF51CAE18E6D5 -:101EC000404D0120FB6029603F4B4049186059603C -:101ED0003F49D6F8B0016A6001F5005E40F00102AA -:101EE000A1F5005001F58055BB609860A1F58050C8 -:101EF000D860C6F8B02119615D61C3F818E0D4F864 -:101F0000B031002BFBD1284A012048F28001FB644C -:101F100010604FF480305160D6F8BC11936041F0EE -:101F20000113BA64C6F8BC31D6F8B0310343C6F821 -:101F3000B031CBF80000D4F8B031002BFBD1DEE596 -:101F4000204C42F22100002524880D6084427FF459 -:101F50002BAE2049204C03C90D0C86282060A1809F -:101F6000A5717FF421AED2F8481150241B4841F0EE -:101F70008001C2F84811047016E61948FFF7F4FC16 -:101F800019E61848FFF7F0FC21E61748FFF7ECFCCC -:101F90001AE61648FFF7E8FC13E61548FFF7E4FCDD -:101FA0000CE600BFC0012E4020200020880D00203C -:101FB00088320020600D0020280400201804002032 -:101FC0008002002010040020002000208000070074 -:101FD000202400202004002080320020080400205B -:101FE0000031002000320020C0310020803100206C -:101FF00040310020002AA0F102022DE9F04714BF71 -:1020000000274FF00057022A01D9BDE8F0874FEAB8 -:10201000C01ADFF840900D4604460AEB090600217D -:1020200040229846304600F029FE012047EA05414B -:102030004AF80910C6F83880B060B8F1000FE4D053 -:10204000034BA0401C6820431860BDE8F08700BF28 -:10205000FC03002000300020002AA0F102022DE93C -:10206000F04714BF00274FF00057022A01D9BDE8FE -:10207000F0874022C501DFF84490884615440021CE -:1020800004461E4605EB090A504600F0F7FD012202 -:1020900047EA084145F80910CAF83860CAF808202C -:1020A000002EE4D004F11000034B82401868024374 -:1020B0001A60BDE8F08700BFFC030020003000205C -:1020C00012048160C36142F08002F0B442600127D3 -:1020D00001F5805601F5005501F5405401F58042A7 -:1020E0000760C660056144618261F0BC704700BF53 -:1020F000831E022B00D9704730B4064B00F1100448 -:1021000001250A4603EBC01005FA04F130BCFFF7C5 -:10211000FDBB00BF40300020831E022B00D970475A -:1021200010B4054B01240A4604FA00F103EBC01079 -:102130005DF8044BFFF7EABB00300020124A134B56 -:10214000D2F8200220F07F40984210B584B002D827 -:1021500000EB800040000E4C01A90A2200F0C8FBF1 -:1021600001A90023204611F8012B01333AB10A2BB3 -:1021700020F8022FF7D11623237004B010BD5B00A6 -:10218000DBB2237004B010BD00441F407F9698005E -:10219000980300204368C269C3F30E43054930B475 -:1021A000C3F14003044C002521F8123024F81250EA -:1021B00030BC7047F80B0020F00B0020F8B5154B31 -:1021C0001B783BB903F0FF04134B1B7813B1134D7D -:1021D0002A8802B9F8BD124F2346124EC2F580720A -:1021E0003978114806EB411600EB01213046FFF724 -:1021F00067FF31460420FFF77BFF3B780133DBB2FA -:10220000062B98BF3B704FF0000388BF3C702B80BB -:10221000F8BD00BF2C0B002028040020600C00201B -:102220002D0B0020800C00202C040020704700BFE4 -:102230000021E0222048F8B50C46204E204D00F049 -:102240001DFD204F2146204B6022347028461F4E32 -:102250001C8000F013FD23462246102102203C6022 -:10226000BC803460B480FFF7F7FE2246184B402153 -:102270000320FFF7BFFE2346224640210420FFF73C -:10228000EBFE2346402228461249FFF719FF294654 -:102290000320FFF741FF104B4A2210491048086005 -:1022A000C3F88440C3F88020D3F8482142F08072FC -:1022B000C3F84821F8BD00BF800C00202D0B002082 -:1022C000000C0020F80B0020600C0020F00B002018 -:1022D000450B0000300B002000002E40F8030020CA -:1022E0006D0B0000024A034B10881B88C01A704710 -:1022F000F80B0020F00B002010B4EFF3108272B640 -:10230000437F33B9017F012908D0032910D001236D -:10231000437702B962B65DF8044B7047114C2168EF -:10232000A1B1114943610B68086083615861EEE710 -:102330000E4C216881B10E4943610B6808608361CE -:1023400058610C4B4FF080511960E0E7064B41613A -:10235000816120601860DAE7054B4161816120608E -:102360001860EEE7740D0020700D0020640D002051 -:10237000680D002004ED00E010B4047F4160022CE1 -:10238000C26003D05DF8044BFFF7B6BF83685DF809 -:10239000044B184770B5EFF3108172B60C4C2368EC -:1023A0008BB10C4E00255A6922607AB1956101B952 -:1023B00062B65D7718469B689847EFF3108172B656 -:1023C0002368002BEFD101B962B670BD3260EEE731 -:1023D000640D0020680D0020FFF7DCBF184A30B400 -:1023E0001468002C28D0036821688B420FD2CB1AC6 -:1023F00000218460C1602360E0601060022330BC73 -:10240000037570470360144611688B4208D3A268B5 -:102410005B1A002AF6D18260C4600360A060EDE719 -:10242000D568CB1A82600222C560E060C16888600E -:102430002360027530BC70478460C4601060DDE7C3 -:102440006C0D0020F8B5224E34682CB32368002BA5 -:102450003AD11D461F4F04E03468ECB12368002BCD -:1024600032D1A36803B1DD6020693360036825754C -:102470001B68BB4221D1037F4560022BC46020D082 -:10248000FFF73AFF6368002BE6D023602046FFF792 -:10249000A5FF3468002CE1D1EFF3108372B60E4A29 -:1024A00000211068116003B962B628B18468FFF793 -:1024B00095FF20460028F9D1F8BD22460021984713 -:1024C000E0E783689847DDE7013B2360E4E700BF6E -:1024D0006C0D0020290D00007C0D0020044A054BE6 -:1024E0001168054A1960136801331360FFF7AABF2A -:1024F000041000E0840D0020880D002070B5214CF0 -:10250000237883B9204B01221B7822701BBB1F4B01 -:102510001B78002B29D11E4B00211A68217012B1A3 -:10252000EFF3058202B170BDEFF3108072B61A6846 -:10253000F2B1184C2178D9B90126556926701D6071 -:10254000D5B1A96100B962B60025936810465577E8 -:102550009847257070BDFFF7C5FE0028D7D000F062 -:102560005FFB0A4B1B78002BD5D000F045FBD2E770 -:102570000028D8D162B670BD074B1D600028E3D19A -:10258000E1E700BF800D0020B8030020A80D002067 -:10259000740D0020780D0020700D00208C4A8D4BAA -:1025A00090422DE9F0438C4D5C699969EF681DD923 -:1025B0008A4B984240F20181894B40F22766DFF84E -:1025C00060E20344874D1A0AAEFB0232D30903EBE3 -:1025D000830303EB830202F2E243B34228BF334694 -:1025E000A3F54873A5FB0336F60804E07E4EB0421F -:1025F00094BF06260E26774A07F01F0ED2F88030C9 -:10260000B64543F0C003C2F880300AD2724B27F0BF -:102610001F071A463743DF601368002BFCDA07F008 -:102620001F0E14F000732ED1704D714AD5F810C0F2 -:1026300015460CEA0202AA420ABF4FF0C0534FF4FB -:102640008052002284EA030515F0605F06D024F072 -:10265000605403F060535F4D1C436C6181EA0203D8 -:1026600013F4405F08D05B4B21F4405111431A46EC -:102670009961936C1D07FCD444F00074554A546171 -:10268000936C9906FCD401215A4D0A4601FB02F3D2 -:1026900000FB03F3AB4209D8072A00F28480013221 -:1026A00001FB02F300FB03F3AB42F5D95248534D53 -:1026B0001844A5FB0030030D6C2B79D8352B7ED840 -:1026C000DFF8608136234E48DFF820C14D4DDCF83D -:1026D0000090B0FBF2F009EA05054545B0FBF1F0CA -:1026E0000BD043F400534FF480586546CCF800807B -:1026F000CCF800302B68002BFCDADFF8D8C0013AA8 -:10270000DCF8103003F00703934207D002F0070211 -:102710006546CCF81020AB6CDB03FCD40139890290 -:1027200084EA010313F4E05F0AD02A4B24F4E05456 -:1027300001F4E0511A460C435C61936C9907FCD498 -:10274000314B32490344DB09A1FB0331090B042956 -:1027500028BF04214B1E1B0284EA030212F4407FAF -:1027600006D024F4407403F440731A4A1C435461A5 -:10277000184B24F000741A465C61936C9B06FCD4E1 -:10278000B0FBF1F1224A7645224B1060196008D265 -:10279000114B27F01F071A463743DF601368002BE1 -:1027A000FCDABDE8F083042980D8013101226DE70D -:1027B000DFF874806C23184886E712261BE7174859 -:1027C000DFF8688000FB03F043EA08087CE700BFFD -:1027D00000A4781F00C00F40000008400046C3233B -:1027E00000BA3CDC1F85EB5100366E0100800D40C5 -:1027F00040300080FFB19F26808D5B00819F5E1678 -:1028000000B29F267F3001807FD1F0089F10E50045 -:10281000B4030020B003002000643F4D001BB7004C -:1028200023B24C00362000806C2000800020008005 -:10283000002852D02DE9F04F814683B0274C01206B -:10284000274D284E54E8003F2A68316844E80003C9 -:10285000002BF7D1244F4FF47A7E2448D7F800C0DC -:10286000BB4607F1C6470368C1EB0C0107F5DE174D -:1028700007F67F67A7FB03C3BA4601279B0CB1FB92 -:10288000F3F30EFB023854E8003F2A68316844E84D -:102890000073002BF7D1DBF800C04FF47A7E036899 -:1028A0000EFB02F2C1EB0C01AAFB033EC8EB0203D4 -:1028B0004FEA9E42B1FBF2F1CA18B2F57A7F07D314 -:1028C000B9F1010908F57A78DDD103B0BDE8F08FE0 -:1028D0000190FFF713FE0198D5E770478C32002076 -:1028E000880D0020840D0020041000E0B4030020B7 -:1028F000F0B44E1E0025374600E00135B0FBF2F380 -:1029000002FB130000F13704092800F13000E4B2A3 -:1029100098BFC4B2184607F8014F002BEDD14A19F1 -:1029200053704DB1013316F8014F1778E81A37701C -:10293000834202F80149F5DB0846F0BC704700BF4E -:10294000A4484FF00F0CA44B826F42F47F02F0B505 -:1029500082670025D0F880204FF470469F4C4FF4DA -:10296000604E29464FF4806714432A46C0F88040E1 -:10297000A3F88C6148F2B826A3F88EC1A3F8905151 -:10298000B3F8880180B240F0F000A3F8880101EBB1 -:102990004100914B0131002540011C4604290344AC -:1029A000A3F804E0DF805A841A865A805A81DE81B7 -:1029B0005A82DA825A83DA83E9D1B4F888014FF077 -:1029C0000F0C874B4FF4704680B229464FF4604796 -:1029D0002A4640EA0C004FF4806EA4F88801B4F84F -:1029E000880180B240F47060A4F88801A3F88C617B -:1029F00048F2B826A3F88EC1A3F89051B3F8880125 -:102A000080B240F0F000A3F8880101EB4100744B64 -:102A10000131002540011C46042903449F80A3F88E -:102A200006E05A841A865A805A81DE815A82DA82F6 -:102A30005A83DA83E9D1B4F888014FF00F0C694B5F -:102A40004FF4704680B229464FF460472A4640EA68 -:102A50000C004FF4806EA4F88801B4F8880180B2AD -:102A600040F47060A4F88801A3F88C6148F2B8269D -:102A7000A3F88EC1A3F89051B3F8880180B240F05A -:102A8000F000A3F8880101EB4100564B013100250D -:102A900040011C46042903449F80A3F806E05A84A1 -:102AA0001A865A805A81DE815A82DA825A83DA8300 -:102AB000E9D1B4F888014FF00F0C4B4B4FF470473D -:102AC00080B229464FF460462A4640EA0C004FF493 -:102AD000806EA4F88801B4F8880180B240F4706078 -:102AE000A4F88801A3F88C7148F2B827A3F88EC126 -:102AF000A3F89051B3F8880180B240F0F000A3F839 -:102B0000880101EB4100384B013140011C4604298A -:102B100003449E80A3F806E05A841A865A805A819C -:102B2000DF815A82DA825A83DA83EAD1B4F88831B3 -:102B30000F27002241F201069BB245F6C05E114606 -:102B400043F226053B43A4F88831B4F888319BB2A0 -:102B500043F47063A4F888315001244B01320344DC -:102B6000042A99815981DF819E82A3F806E01980A9 -:102B700019829D81F0D100220F2741F2010645F60E -:102B8000C055114643F226045001194B013203444B -:102B9000042A99815981DF819E82DD801980198202 -:102BA0009C81F1D100220F2741F2010645F6C05564 -:102BB000114643F2260450010E4B01320344042A0D -:102BC00099815981DF819E82DD80198019829C81E3 -:102BD000F1D1F0BD00C00F4000C03D40000003FC3B -:102BE00000003E4000403E4000803E4000C01D408E -:102BF00000001E4000401E4038B5074B1C784CB109 -:102C0000064D55F8043F002BFBD09847631E13F088 -:102C1000FF04F6D138BD00BFA80D0020880D0020AC -:102C2000014B00221A707047B803002070B50F4E98 -:102C30000F4D761BB61018BF002405D0013455F88F -:102C4000043B9847A642F9D10A4E0B4D761B00F083 -:102C500063F8B61018BF002406D0013455F8043BC1 -:102C60009847A642F9D170BD70BD00BF48160060FC -:102C700048160060501600604816006070B4840763 -:102C800046D0541E002A41D0CDB2034602E0621E57 -:102C9000E4B3144603F8015B9A07F8D1032C2ED94C -:102CA000CDB245EA05250F2C45EA054519D903F1B2 -:102CB00010022646103E0F2E42F8105C42F80C5CC3 -:102CC00042F8085C42F8045C02F11002F2D8A4F168 -:102CD000100222F00F0204F00F041032032C1344F0 -:102CE0000DD91E462246043A032A46F8045BFAD858 -:102CF000221F22F003020432134404F003042CB117 -:102D0000C9B21C4403F8011BA342FBD170BC70473D -:102D100014460346C2E700BF5FF800F0E11500600B -:042D2000F8B500BF43 -:102D240000000042C8801F40B8821F400800000015 -:102D340000000042C4801F40B4821F400400000011 -:102D440000C0004224801F4014821F401000000075 -:102D540000C0004228801F4018821F40200000004D -:102D640000C000422C801F401C821F404000000015 -:102D740000C0004234801F4024821F400001000034 -:102D84000040004264811F4054831F40000400003F -:102D94000040004280811F4070831F4000000200F9 -:102DA400004000427C811F406C831F4000000100F2 -:102DB4000040004268811F4058831F400008000003 -:102DC400004000423C811F402C831F400100000052 -:102DD4000040004244811F4034831F40040000002F -:102DE4000040004240811F4030831F400200000029 -:102DF4000040004248811F4038831F400800000003 -:102E04000000004204811F40F4821F4000000400BF -:102E14000000004208811F40F8821F4000000800A3 -:102E24000000004218811F4008831F4000008000FA -:102E34000000004214811F4004831F400000400032 -:102E44000000004200811F40F0821F400000020089 -:102E540000000042FC801F40EC821F400000010083 -:102E64000000004224811F4014831F40000000041E -:102E74000000004228811F4018831F400000000802 -:102E8400000000421C811F400C831F400000000111 -:102E94000000004220811F4010831F4000000002F8 -:102EA40000000042EC801F40DC821F400010000044 -:102EB40000000042F0801F40E0821F40002000001C -:102EC4000000004234811F4024831F400000004062 -:102ED4000000004238811F4028831F40000000800A -:102EE4000080004294801F4084821F400000040040 -:102EF40000C0004290801F4080821F40000000807C -:102F040000800042A8801F4098821F40000080007B -:102F140000800042A4801F4094821F4000004000B3 -:102F2400004000426C811F405C831F400010000081 -:102F340000C0004230801F4020821F4080000000FB -:102F440000800042C8811F40B8831F4000800000F9 -:102F540000800042C4811F40B4831F400040000031 -:102F640000800042C0811F40B0831F400020000049 -:102F740000800042BC811F40AC831F400010000051 -:102F840000800042D0811F40C0831F400000020027 -:102F940000800042CC811F40BC831F400000010020 -:102FA4000001000068030020120000000006000079 -:102FB400000300200A000000000200002403002097 -:102FC4004300000000070000240300204300000029 -:102FD4000003000094030020000000000103090422 -:102FE4000C03002000000000020309047C030020FD -:102FF40000000000030309049803002000000000FF -:1030040000000000000000000000000004000000B8 -:103014000C000000090000000B0000000A00000082 -:103024000A060002020000400100000018035400D8 -:10303400650065006E007300790064007500690026 -:103044006E006F0009024300020100C0320904004F -:103054000001020201000524001001052401010100 -:10306400042402060524060001070582031000104B -:1030740009040100020A00000007050302400000E1 -:103084000705840240000000120100020200004013 -:10309400C0168304790201020301000016035500DF -:1030A4005300420020005300650072006900610073 -:1030B4006C000000040309040C030000000000007D -:1030C40000000000000000000000000000000000FC -:1030D4000029DE07007B9A170100000000000000B1 -:040000056000100087 -:00000001FF diff --git a/tools/test-rig-rpi.yaml b/tools/test-rig-rpi.yaml index 7078aad2..ed4f7f7d 100644 --- a/tools/test-rig-rpi.yaml +++ b/tools/test-rig-rpi.yaml @@ -8,15 +8,24 @@ components: name: rpi ssh: odrv net: homenet - can0: main_canbus - uart0: /dev/serial/by-id/[not-yet-used] + components: + - type: uart + name: uart0 + port: /dev/ttyS0 + connected-to: main_uart + - type: can + name: can0 + interface: can0 + connected-to: odrive.can + - {type: gpio, num: 20} # need to specify GPIOs explicitly for the generalpurpose type + - {type: gpio, num: 26} - type: programmer name: The Blue STLink/v2 id: '\x53\x3f\x75\x06\x49\x3f\x49\x51\x44\x54\x19\x3f' - type: odrive - name: ODrive + name: odrive board-version: v3.6-58V serial-number: "20703595524B" brake-resistance: 0.47 @@ -40,15 +49,35 @@ components: max-voltage: 40 - type: encoder - name: real_encoder_0 - cpr: 8192 - max-rpm: 7000 - - - type: encoder - name: real_encoder_1 + name: real_encoder cpr: 8192 max-rpm: 7000 - type: teensy name: teensy - + +connections: + - ['odrive.can', 'rpi.can0'] + - ['teensy.program', 'rpi.gpio26'] + - ['teensy.gpio11', 'rpi.uart0.tx'] + - ['teensy.gpio12', 'rpi.uart0.rx'] + - ['teensy.gpio10', 'odrive.gpio1'] + - ['teensy.gpio9', 'odrive.gpio2'] + - ['teensy.gpio8', 'odrive.gpio3'] + - ['teensy.gpio7', 'odrive.gpio4'] + - ['teensy.gpio15', 'odrive.gpio5'] + - ['teensy.gpio16', 'odrive.gpio6'] + - ['teensy.gpio17', 'odrive.gpio7'] + - ['teensy.gpio18', 'odrive.gpio8'] + - ['teensy.gpio4', 'rpi.gpio20'] + - ['teensy.gpio23', 'odrive.encoder0.z'] + - ['teensy.gpio22', 'odrive.encoder0.a'] + - ['teensy.gpio21', 'odrive.encoder0.b'] + - ['teensy.gpio20', 'odrive.encoder1.z'] + - ['teensy.gpio19', 'odrive.encoder1.a'] + - ['teensy.gpio18', 'odrive.encoder1.b'] + - ['teensy.gpio0', 'real_encoder.z'] + - ['teensy.gpio1', 'real_encoder.a'] + - ['teensy.gpio2', 'real_encoder.b'] + - ['odrive.axis0', 'D5065-270KV_0'] + - ['D5065-270KV_0', 'real_encoder'] From 237a50bfc94ba1c0dd51e73506fd04000a43305c Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 21 Apr 2020 12:25:18 +0200 Subject: [PATCH 02/13] add step/dir input tests --- Firmware/MotorControl/axis.cpp | 3 +- Firmware/MotorControl/axis.hpp | 8 ++- tools/odrive/tests/step_dir_test.py | 92 ++++++++++++++++++++++++ tools/odrive/tests/test_runner.py | 107 ++++++++++++++++------------ tools/test-rig-rpi.yaml | 18 ++--- 5 files changed, 171 insertions(+), 57 deletions(-) create mode 100644 tools/odrive/tests/step_dir_test.py diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 5635c70c..0e6f64dc 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -338,7 +338,7 @@ bool Axis::run_closed_loop_control_loop() { return true; }); - set_step_dir_active(false); + set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on); return check_for_errors(); } @@ -428,6 +428,7 @@ bool Axis::run_idle_loop() { // run_control_loop ignores missed modulation timing updates // if and only if we're in AXIS_STATE_IDLE safety_critical_disarm_motor_pwm(motor_); + set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on); run_control_loop([this]() { return true; }); diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 50e72d00..9f99f33a 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -68,8 +68,14 @@ public: bool startup_closed_loop_control = false; // ' + str(o.num)) for t, i, o, n in self.gpio_tuples) + ')' + def __eq__(self, obj): + return isinstance(obj, ProxiedComponent) and (self.impl == obj.impl) # and (self.gpio_tuples == obj.gpio_tuples) + def prepare(self): for teensy, gpio_in, gpio_out, gpio_noise_enable in self.gpio_tuples: teensy.add_route(gpio_in, gpio_out, gpio_noise_enable) @@ -480,24 +488,26 @@ def run_shell(command_line, logger, env=None, timeout=None): logger.error(result.stdout.decode(sys.stdout.encoding)) raise TestFailed("command {} failed".format(command_line)) -def select_params(param_options): - params = [] +def get_combinations(param_options): + if len(param_options) > 0: + param = param_options[0] + if not is_list_like(param): + param = [param] + + for part1, part2 in itertools.product(param, get_combinations(param_options[1:]) if (len(param_options) > 1) else [()]): + if not isinstance(part1, tuple): + part1 = (part1,) + yield part1 + part2 + +def select_params(param_options): # Select parameters from the resource list # (this could be arbitrarily complex to improve parallelization of the tests) - for param in param_options: - if is_list_like(param): - if len(param) == 0: - return None - else: - selection = param[0] - if is_list_like(selection): - params = params + list(selection) - else: - params.append(selection) - else: - params.append(param) - return params + for combination in get_combinations(param_options): + if all_unique(combination): + return list(combination) + + return None def run(tests): if not isinstance(tests, list): @@ -590,34 +600,37 @@ testrig = TestRig(test_rig_yaml, logger) if args.setup_host: - def export_gpio(gpio): - if not os.path.isdir("/sys/class/gpio/gpio{}".format(gpio)): + for gpio in testrig.get_components(LinuxGpioComponent): + num = gpio.num + logger.debug('exporting GPIO ' + str(num) + ' to user space...') + if not os.path.isdir("/sys/class/gpio/gpio{}".format(num)): with open("/sys/class/gpio/export", "w") as fp: - fp.write(str(gpio)) - os.chmod("/sys/class/gpio/gpio{}/value".format(gpio), stat.S_IROTH | stat.S_IWOTH) - os.chmod("/sys/class/gpio/gpio{}/direction".format(gpio), stat.S_IROTH | stat.S_IWOTH) + fp.write(str(num)) + os.chmod("/sys/class/gpio/gpio{}/value".format(num), stat.S_IROTH | stat.S_IWOTH) + os.chmod("/sys/class/gpio/gpio{}/direction".format(num), stat.S_IROTH | stat.S_IWOTH) - # TODO: read configuration from yaml file - export_gpio(20) # connected to Teensy GPIO - export_gpio(26) # connected to Teensy Program pin + for port in testrig.get_components(SerialPortComponent): + logger.debug('changing permissions on ' + port.yaml['port'] + '...') + os.chmod(port.yaml['port'], stat.S_IROTH | stat.S_IWOTH) - os.chmod("/dev/ttyS0", stat.S_IROTH | stat.S_IWOTH) - - # This breaks the retarded teensy loader that shows up on every compile - if not os.path.isfile('/usr/share/arduino/hardware/tools/teensy_post_compile_old'): - os.rename('/usr/share/arduino/hardware/tools/teensy_post_compile', '/usr/share/arduino/hardware/tools/teensy_post_compile_old') - with open('/usr/share/arduino/hardware/tools/teensy_post_compile', 'w') as scr: - scr.write('#!/bin/bash\n') - scr.write('if [ "$ARDUINO_COMPILE_DESTINATION" != "" ]; then\n') - scr.write(' cp -r ${2#-path=}/*.ino.hex ${ARDUINO_COMPILE_DESTINATION}\n') - scr.write('fi\n') - os.chmod('/usr/share/arduino/hardware/tools/teensy_post_compile', stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) + if len(list(testrig.get_components(TeensyComponent))): + # This breaks the annoying teensy loader that shows up on every compile + logger.debug('modifying teensyduino installation...') + if not os.path.isfile('/usr/share/arduino/hardware/tools/teensy_post_compile_old'): + os.rename('/usr/share/arduino/hardware/tools/teensy_post_compile', '/usr/share/arduino/hardware/tools/teensy_post_compile_old') + with open('/usr/share/arduino/hardware/tools/teensy_post_compile', 'w') as scr: + scr.write('#!/bin/bash\n') + scr.write('if [ "$ARDUINO_COMPILE_DESTINATION" != "" ]; then\n') + scr.write(' cp -r ${2#-path=}/*.ino.hex ${ARDUINO_COMPILE_DESTINATION}\n') + scr.write('fi\n') + os.chmod('/usr/share/arduino/hardware/tools/teensy_post_compile', stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) # Bring up CAN interface(s) for intf in testrig.get_components(CanInterfaceComponent): name = intf.yaml['interface'] - run_shell('ip link set dev {} down'.format(intf)) - run_shell('ip link set dev {} type can bitrate 250000'.format(intf)) - run_shell('ip link set dev {} type can loopback off'.format(intf)) - run_shell('ip link set dev {} up'.format(intf)) + logger.debug('bringing up {}...'.format(name)) + run_shell('ip link set dev {} down'.format(name), logger) + run_shell('ip link set dev {} type can bitrate 250000'.format(name), logger) + run_shell('ip link set dev {} type can loopback off'.format(name), logger) + run_shell('ip link set dev {} up'.format(name), logger) diff --git a/tools/test-rig-rpi.yaml b/tools/test-rig-rpi.yaml index ed4f7f7d..3321263f 100644 --- a/tools/test-rig-rpi.yaml +++ b/tools/test-rig-rpi.yaml @@ -17,12 +17,13 @@ components: name: can0 interface: can0 connected-to: odrive.can - - {type: gpio, num: 20} # need to specify GPIOs explicitly for the generalpurpose type + - {type: gpio, num: 19} # need to specify GPIOs explicitly for the generalpurpose type + - {type: gpio, num: 20} - {type: gpio, num: 26} - - type: programmer - name: The Blue STLink/v2 - id: '\x53\x3f\x75\x06\x49\x3f\x49\x51\x44\x54\x19\x3f' +# - type: programmer +# name: The Blue STLink/v2 +# id: '\x53\x3f\x75\x06\x49\x3f\x49\x51\x44\x54\x19\x3f' - type: odrive name: odrive @@ -65,11 +66,12 @@ connections: - ['teensy.gpio9', 'odrive.gpio2'] - ['teensy.gpio8', 'odrive.gpio3'] - ['teensy.gpio7', 'odrive.gpio4'] - - ['teensy.gpio15', 'odrive.gpio5'] - - ['teensy.gpio16', 'odrive.gpio6'] - - ['teensy.gpio17', 'odrive.gpio7'] - - ['teensy.gpio18', 'odrive.gpio8'] + - ['teensy.gpio14', 'odrive.gpio5'] + - ['teensy.gpio15', 'odrive.gpio6'] + - ['teensy.gpio16', 'odrive.gpio7'] + - ['teensy.gpio17', 'odrive.gpio8'] - ['teensy.gpio4', 'rpi.gpio20'] + - ['teensy.gpio5', 'rpi.gpio19'] - ['teensy.gpio23', 'odrive.encoder0.z'] - ['teensy.gpio22', 'odrive.encoder0.a'] - ['teensy.gpio21', 'odrive.encoder0.b'] From 574190e433dba1c0568db3a30a87eab2a73eb4c7 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Apr 2020 20:39:10 -0400 Subject: [PATCH 03/13] Change trap_traj timer to be more robust --- Firmware/MotorControl/controller.cpp | 11 +++++------ Firmware/MotorControl/controller.hpp | 1 - Firmware/MotorControl/trapTraj.hpp | 2 ++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index c88fe4a7..498e03e5 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -54,7 +54,7 @@ void Controller::move_to_pos(float goal_point) { axis_->trap_.config_.vel_limit, axis_->trap_.config_.accel_limit, axis_->trap_.config_.decel_limit); - traj_start_loop_count_ = axis_->loop_counter_; + axis_->trap_.t_ = 0.0f; trajectory_done_ = false; } @@ -198,10 +198,8 @@ bool Controller::update(float* current_setpoint_output) { // Avoid updating uninitialized trajectory if (trajectory_done_) break; - // Note: uint32_t loop count delta is OK across overflow - // Beware of negative deltas, as they will not be well behaved due to uint! - float t = (axis_->loop_counter_ - traj_start_loop_count_) * current_meas_period; - if (t > axis_->trap_.Tf_) { + + if (axis_->trap_.t_ > axis_->trap_.Tf_) { // Drop into position control mode when done to avoid problems on loop counter delta overflow config_.control_mode = CTRL_MODE_POSITION_CONTROL; pos_setpoint_ = input_pos_; @@ -209,10 +207,11 @@ bool Controller::update(float* current_setpoint_output) { current_setpoint_ = 0.0f; trajectory_done_ = true; } else { - TrapezoidalTrajectory::Step_t traj_step = axis_->trap_.eval(t); + TrapezoidalTrajectory::Step_t traj_step = axis_->trap_.eval(axis_->trap_.t_); pos_setpoint_ = traj_step.Y; vel_setpoint_ = traj_step.Yd; current_setpoint_ = traj_step.Ydd * config_.inertia; + axis_->trap_.t_ += current_meas_period; } anticogging_pos = pos_setpoint_; // FF the position setpoint instead of the pos_estimate } break; diff --git a/Firmware/MotorControl/controller.hpp b/Firmware/MotorControl/controller.hpp index afbb4edd..56a60020 100644 --- a/Firmware/MotorControl/controller.hpp +++ b/Firmware/MotorControl/controller.hpp @@ -117,7 +117,6 @@ public: bool input_pos_updated_ = false; - uint32_t traj_start_loop_count_ = 0; bool trajectory_done_ = true; bool anticogging_valid_ = false; diff --git a/Firmware/MotorControl/trapTraj.hpp b/Firmware/MotorControl/trapTraj.hpp index 6c343c42..fd142da5 100644 --- a/Firmware/MotorControl/trapTraj.hpp +++ b/Firmware/MotorControl/trapTraj.hpp @@ -47,6 +47,8 @@ public: float Tf_; float yAccel_; + + float t_; }; #endif \ No newline at end of file From a5d991e712c26d7957ad16b9f3d54224fc5159bc Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Apr 2020 20:39:39 -0400 Subject: [PATCH 04/13] Clarify current_setpoint calculation --- Firmware/MotorControl/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 498e03e5..3b450277 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -160,7 +160,7 @@ bool Controller::update(float* current_setpoint_output) { float step = std::clamp(full_step, -max_step_size, max_step_size); vel_setpoint_ += step; - current_setpoint_ = step / current_meas_period * config_.inertia; + current_setpoint_ = (step / current_meas_period) * config_.inertia; } break; case INPUT_MODE_CURRENT_RAMP: { float max_step_size = std::abs(current_meas_period * config_.current_ramp_rate); From c2a5ce821a167aabc0ecfd5542b34fdac4e6ec3c Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Apr 2020 20:39:48 -0400 Subject: [PATCH 05/13] Add input_modes.md --- docs/commands.md | 15 +++++++ docs/input_modes.md | 106 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 docs/input_modes.md diff --git a/docs/commands.md b/docs/commands.md index 2d26586a..c9769e8c 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -60,6 +60,21 @@ Possible values are: * `CTRL_MODE_CURRENT_CONTROL` * `CTRL_MODE_VOLTAGE_CONTROL` - this one is not normally used. +### Input Mode +The default input mode is `INPUT_MODE_PASSTHROUGH`. +Modes can be selected by changing `.controller.config.input_mode`. +Possible values are: +* `INPUT_MODE_INACTIVE` +* `INPUT_MODE_PASSTHROUGH` +* `INPUT_MODE_VEL_RAMP` +* `INPUT_MODE_POS_FILTER` +* `INPUT_MODE_MIX_CHANNELS` +* `INPUT_MODE_TRAP_TRAJ` +* `INPUT_MODE_CURRENT_RAMP` +* `INPUT_MODE_MIRROR` + +For more information, see [input_modes](input_modes.md). + # Control Commands * `.controller.input_pos = ` * `.controller.input_vel = ` diff --git a/docs/input_modes.md b/docs/input_modes.md new file mode 100644 index 00000000..ccb88206 --- /dev/null +++ b/docs/input_modes.md @@ -0,0 +1,106 @@ +# Input Modes +As of version ###, ODrive now intercepts the incoming commands and can apply filters to them. The old protocol values `pos_setpoint`, `vel_setpoint`, and `current_setpoint` are still used internally by the closed-loop cascade control, but the user cannot write to them directly. This allows us to condense the number of ways the ODrive accepts motion commands. The new commands are: + +* `.controller.config.input_mode` +* `.controller.input_pos` +* `.controller.input_vel` +* `.controller.input_current` + +The Input Modes currently valid are: +* `INPUT_MODE_INACTIVE` +* `INPUT_MODE_PASSTHROUGH` +* `INPUT_MODE_VEL_RAMP` +* `INPUT_MODE_POS_FILTER` +* `INPUT_MODE_MIX_CHANNELS` +* `INPUT_MODE_TRAP_TRAJ` +* `INPUT_MODE_CURRENT_RAMP` +* `INPUT_MODE_MIRROR` + +--- + +## INPUT_MODE_INACTIVE +Disable inputs. Setpoints retain their last value. + +## INPUT_MODE_PASSTHROUGH +Pass `input_xxx` through to `xxx_setpoint` directly. + +### Valid Inputs: +* `input_pos` +* `input_vel` +* `input_current` + +### Valid Control modes: +* `CTRL_MODE_VOLTAGE_CONTROL` +* `CTRL_MODE_CURRENT_CONTROL` +* `CTRL_MODE_VELOCITY_CONTROL` +* `CTRL_MODE_POSITION_CONTROL` + +## INPUT_MODE_VEL_RAMP +Ramps a velocity command from the current value to the target value. + +### Configuration Values: +* `.controller.config.vel_ramp_rate` [cpr/sec] +* `.controller.config.inertia` [A/(count/s^2))] + +### Valid inputs: +* `input_vel` + +### Valid Control Modes: +* `CTRL_MODE_VELOCITY_CONTROL` + +## INPUT_MODE_POS_FILTER +Implements a 2nd order position tracking filter. Inteded for use with step/dir interface, but can also be used with position-only commands. + +### Configuration Values: +* `.controller.config.input_filter_bandwidth` +* `.controller.config.inertia` + +### Valid inputs: +* `input_pos` + +### Valid Control modes: +* `CTRL_MODE_POSITION_CONTROL` + +## INPUT_MODE_MIX_CHANNELS +Not Implemented. + + +## INPUT_MODE_TRAP_TRAJ +Implementes an online trapezoidal trajectory planner. + +### Configuration Values: +* `.trap_traj.config.vel_limit` +* `.trap_traj.config.accel_limit` +* `.trap_traj.config.decel_limit` +* `.controller.config.inertia` + +### Valid Inputs: +* `input_pos` + +### Valid Control Modes: +* `CTRL_MODE_POSITION_CONTROL` + +## INPUT_MODE_CURRENT_RAMP +Ramp a current command from the current value to the target value. + +### Configuration Values: +* `.controller.config.current_ramp_rate` + +### Valid Inputs: +* `input_current` + +### Valid Control Modes: +* `CTRL_MODE_CURRENT_CONTROL` + +## INPUT_MODE_MIRROR +Implements "electronic mirroring". This is like electronic camming, but you can only mirror exactly the movements of the other motor, according to a fixed ratio + +### Configuration Values +* `.controller.config.axis_to_mirror` +* `.controller.config.mirror_ratio` + +### Valid Inputs +* None. Inputs are taken directly from the other axis encoder estimates + +### Valid Control modes +* `CTRL_MODE_POSITION_CONTROL` From f4f4be0d6dfdcad5f883a6937833007ae77323fc Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Apr 2020 20:46:38 -0400 Subject: [PATCH 06/13] Replace select instances of pos_setpoint by input_pos in docs --- docs/ascii-protocol.md | 2 +- docs/getting-started.md | 6 +++--- docs/interfaces.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index a0757e2f..5e1dc236 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -131,7 +131,7 @@ Not all parameters can be accessed via the ASCII protocol but at least all param ``` * `property` name of the property, as seen in ODrive Tool * `value` text representation of the value to be written - * Example: `w axis0.controller.pos_setpoint -123.456` + * Example: `w axis0.controller.input_pos -123.456` #### System commands: * `ss` - Save config diff --git a/docs/getting-started.md b/docs/getting-started.md index df3a8ad9..623e85da 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -247,7 +247,7 @@ Let's get motor 0 up and running. The procedure for motor 1 is exactly the same, 2. Type `odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL` Enter. From now on the ODrive will try to hold the motor's position. If you try to turn it by hand, it will fight you gently. That is unless you bump up `odrv0.axis0.motor.config.current_lim`, in which case it will fight you more fiercely. If the motor begins to vibrate either immediately or after being disturbed you will need to [lower the controller gains](control.md). -3. Send the motor a new position setpoint. `odrv0.axis0.controller.pos_setpoint = 10000` Enter. The units are in encoder counts. +3. Send the motor a new position setpoint. `odrv0.axis0.controller.input_pos = 10000` Enter. The units are in encoder counts. 4. At this point you will probably want to [Properly tune](control.md) the motor controller in order to maximize system performance. ## Other control modes @@ -328,9 +328,9 @@ You can also execute a move with the [appropriate ascii command](ascii-protocol. To enable Circular position control, set `axis.controller.config.setpoints_in_cpr = True` This mode is useful for continuos incremental position movement. For example a robot rolling indefinitely, or an extruder motor or conveyor belt moving with controlled increments indefinitely. -In the regular position mode, the `pos_setpoint` would grow to a very large value and would lose precision due to floating point rounding. +In the regular position mode, the `input_pos` would grow to a very large value and would lose precision due to floating point rounding. -In this mode, the controller will try to track the position within only one turn of the motor. Specifically, `pos_setpoint` is expected in the range `[0, cpr-1]`, where `cpr` is the number of encoder counts in one revolution. If the `pos_setpoint` is incremented to outside this range (say via step/dir input), it is automatically wrapped around into the correct value. +In this mode, the controller will try to track the position within only one turn of the motor. Specifically, `input_pos` is expected in the range `[0, cpr-1]`, where `cpr` is the number of encoder counts in one revolution. If the `input_pos` is incremented to outside this range (say via step/dir input), it is automatically wrapped around into the correct value. Note that in this mode `encoder.pos_cpr` is used for feedback in stead of `encoder.pos_estimate`. If you try to increment the axis with a large step in one go that exceeds `cpr/2` steps, the motor will go to the same angle around the wrong way. This is also the case if there is a large disturbance. If you have an application where you would like to handle larger steps, you can use a virtual CPR that is an integer times larger than your encoder's actual CPR. Set `encoder.config.cpr = N * your_enc_cpr`, where N is some integer. Choose N to give you an appropriate circular space for your application. diff --git a/docs/interfaces.md b/docs/interfaces.md index ca9db588..13dccec4 100644 --- a/docs/interfaces.md +++ b/docs/interfaces.md @@ -115,13 +115,13 @@ Some GPIO pins can be used for PWM input, if they are not allocated to other fun Any of the numerical parameters that are writable from the ODrive Tool can be hooked up to a PWM input. As an example, we'll configure GPIO4 to control the angle of axis 0. We want the axis to move within a range of -1500 to 1500 encoder counts. -1. Make sure you're able control the axis 0 angle by writing to `odrv0.axis0.controller.pos_setpoint`. If you need help with this follow the [getting started guide](getting-started.md). +1. Make sure you're able control the axis 0 angle by writing to `odrv0.axis0.controller.input_pos`. If you need help with this follow the [getting started guide](getting-started.md). 2. If you want to control your ODrive with the PWM input without using anything else to activate the ODrive, you can configure the ODrive such that axis 0 automatically goes operational at startup. See [here](commands.md#startup-procedure) for more information. 3. In ODrive Tool, configure the PWM input mapping ``` In [1]: odrv0.config.gpio4_pwm_mapping.min = -1500 In [2]: odrv0.config.gpio4_pwm_mapping.max = 1500 - In [3]: odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['pos_setpoint'] + In [3]: odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['input_pos'] ``` Note: you can disable the input by setting `odrv0.config.gpio4_pwm_mapping.endpoint = None` 4. Save the configuration and reboot From 9b2df645f9760debfa08572c41cfee0e19bc2e78 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Apr 2020 20:49:18 -0400 Subject: [PATCH 07/13] Replace select instances of vel_setpoint by input_vel in docs --- docs/commands.md | 4 ++-- docs/encoders.md | 2 +- docs/getting-started.md | 2 +- docs/hoverboard.md | 20 ++++++++++---------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index c9769e8c..ae5e9e8f 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -111,7 +111,7 @@ All variables that are part of a `[...].config` object can be saved to non-volat The ODrive can run without encoder/hall feedback, but there is a minimum speed, usually around a few hunderd RPM. However the units of this mode is different from when using an encoder. Velocities are not measured in counts/s, instead it is electrical rad/s. This also applies to the gains. For example, `vel_gain` is in units of `A / (rad/s)` instead of `A / (count/s)`. -To give an example, suppose you have a motor with 7 pole pairs, and you want to spin it at 3000 RPM. Then you would set the `vel_setpoint` to `3000 * 2*pi/60 * 7 = 2199 rad/s electrical`. +To give an example, suppose you have a motor with 7 pole pairs, and you want to spin it at 3000 RPM. Then you would set the `input_vel` to `3000 * 2*pi/60 * 7 = 2199 rad/s electrical`. Below are some suggested starting parameters that you can use. Note that you _must_ set the `pm_flux_linkage` correctly for sensorless mode to work. @@ -119,7 +119,7 @@ Below are some suggested starting parameters that you can use. Note that you _mu odrv0.axis0.controller.config.vel_gain = 0.01 odrv0.axis0.controller.config.vel_integrator_gain = 0.05 odrv0.axis0.controller.config.control_mode = 2 -odrv0.axis0.controller.vel_setpoint = 400 +odrv0.axis0.controller.input_vel = 400 odrv0.axis0.motor.config.direction = 1 odrv0.axis0.sensorless_estimator.config.pm_flux_linkage = 5.51328895422 / ( * ) ``` diff --git a/docs/encoders.md b/docs/encoders.md index cb038850..d0b60bdf 100644 --- a/docs/encoders.md +++ b/docs/encoders.md @@ -70,7 +70,7 @@ If calibration works, congratulations. Now try: * `.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL` -* `.controller.set_vel_setpoint(3000,0) ` +* `.controller.input_vel = 3000` let it loop a few times and then set: * `.requested_state = AXIS_STATE_IDLE` diff --git a/docs/getting-started.md b/docs/getting-started.md index 623e85da..fa532abf 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -337,7 +337,7 @@ If you try to increment the axis with a large step in one go that exceeds `cpr/2 ### Velocity control Set `axis.controller.config.control_mode = CTRL_MODE_VELOCITY_CONTROL`.
-You can now control the velocity with `axis.controller.vel_setpoint = 5000` [count/s]. +You can now control the velocity with `axis.controller.input_vel = 5000` [count/s]. ### Ramped velocity control Set `axis.controller.config.control_mode = CTRL_MODE_VELOCITY_CONTROL`.
diff --git a/docs/hoverboard.md b/docs/hoverboard.md index 730391fd..91b50764 100644 --- a/docs/hoverboard.md +++ b/docs/hoverboard.md @@ -112,9 +112,9 @@ The ODrive starts in idle (we will look at changing this later) so we can enable odrv0.save_configuration() odrv0.reboot() odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL -odrv0.axis0.controller.vel_setpoint = 120 +odrv0.axis0.controller.input_vel = 120 # Your motor should spin here -odrv0.axis0.controller.vel_setpoint = 0 +odrv0.axis0.controller.input_vel = 0 odrv0.axis0.requested_state = AXIS_STATE_IDLE ``` @@ -128,31 +128,31 @@ We also have to reboot to activate the PWM input. ```txt odrv0.config.gpio3_pwm_mapping.min = -200 odrv0.config.gpio3_pwm_mapping.max = 200 -odrv0.config.gpio3_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['vel_setpoint'] +odrv0.config.gpio3_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['input_vel'] odrv0.config.gpio4_pwm_mapping.min = -200 odrv0.config.gpio4_pwm_mapping.max = 200 -odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis1.controller._remote_attributes['vel_setpoint'] +odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis1.controller._remote_attributes['input_vel'] odrv0.save_configuration() odrv0.reboot() ``` -Now we can check that the sticks are writing to the velocity setpoint. Move the stick, print `vel_setpoint`, move to a different position, check again. +Now we can check that the sticks are writing to the velocity setpoint. Move the stick, print `input_vel`, move to a different position, check again. ```txt -In [1]: odrv0.axis1.controller.vel_setpoint +In [1]: odrv0.axis1.controller.input_vel Out[1]: 0.1904754638671875 -In [2]: odrv0.axis1.controller.vel_setpoint +In [2]: odrv0.axis1.controller.input_vel Out[2]: 0.1904754638671875 -In [3]: odrv0.axis1.controller.vel_setpoint +In [3]: odrv0.axis1.controller.input_vel Out[3]: 28.152389526367188 -In [4]: odrv0.axis1.controller.vel_setpoint +In [4]: odrv0.axis1.controller.input_vel Out[4]: 61.21905517578125 -In [5]: odrv0.axis1.controller.vel_setpoint +In [5]: odrv0.axis1.controller.input_vel Out[5]: -52.990474700927734 ``` From 36c7b4dbd7287cec83bd85f79908e8c8ef52da07 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Apr 2020 20:49:26 -0400 Subject: [PATCH 08/13] Replace current_setpoint by input_current in docs --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index fa532abf..146ed201 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -347,7 +347,7 @@ You can now control the velocity with `axis.controller.input_vel = 5000` [count/ ### Current control Set `axis.controller.config.control_mode = CTRL_MODE_CURRENT_CONTROL`.
-You can now control the current with `axis.controller.current_setpoint = 3` [A]. +You can now control the current with `axis.controller.input_current = 3` [A]. Note: If you exceed `vel_limit` in current control mode, the current is reduced. To disable this, set `axis.controller.enable_current_vel_limit = False`. From af5ee564b3f37365a37f8db6fd47ab52e7a5168b Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Apr 2020 20:58:18 -0400 Subject: [PATCH 09/13] Add some flashy media to input_modes.md --- docs/control.md | 2 +- docs/input_modes.md | 7 +++++++ docs/test.md | 1 - 3 files changed, 8 insertions(+), 2 deletions(-) delete mode 100644 docs/test.md diff --git a/docs/control.md b/docs/control.md index 8af90a91..267049fd 100644 --- a/docs/control.md +++ b/docs/control.md @@ -2,7 +2,7 @@ The motor controller is a cascaded style position, velocity and current control loop, as per the diagram below. When the control mode is set to position control, the whole loop runs. When running in velocity control mode, the position control part is removed and the velocity command is fed directly in to the second stage input. In current control mode, only the current controller is used. -![Cascaded pos vel I loops](https://github.com/madcowswe/ODrive/blob/master/docs/controller_with_ff.png?raw=true) +![Cascaded pos vel I loops](controller_with_ff.png) Each stage of the control loop is a variation on a [PID controller](https://en.wikipedia.org/wiki/PID_controller). A PID controller is a mathematical model that can be adapted to control a wide variety of systems. This flexibility is essential as it allows the ODrive to be used to control all kinds of mechanical systems. diff --git a/docs/input_modes.md b/docs/input_modes.md index ccb88206..32d7df86 100644 --- a/docs/input_modes.md +++ b/docs/input_modes.md @@ -51,6 +51,9 @@ Ramps a velocity command from the current value to the target value. ## INPUT_MODE_POS_FILTER Implements a 2nd order position tracking filter. Inteded for use with step/dir interface, but can also be used with position-only commands. +![POS Filter Response](secondOrderResponse.png) +Result of a step command from 1000 to 0 + ### Configuration Values: * `.controller.config.input_filter_bandwidth` * `.controller.config.inertia` @@ -68,6 +71,8 @@ Not Implemented. ## INPUT_MODE_TRAP_TRAJ Implementes an online trapezoidal trajectory planner. +![Trapezoidal Planner Response](TrapTrajPosVel.png) + ### Configuration Values: * `.trap_traj.config.vel_limit` * `.trap_traj.config.accel_limit` @@ -95,6 +100,8 @@ Ramp a current command from the current value to the target value. ## INPUT_MODE_MIRROR Implements "electronic mirroring". This is like electronic camming, but you can only mirror exactly the movements of the other motor, according to a fixed ratio +[![](http://img.youtube.com/vi/D4_vBtyVVzM/0.jpg)](http://www.youtube.com/watch?v=D4_vBtyVVzM "Example Mirroring Video") + ### Configuration Values * `.controller.config.axis_to_mirror` * `.controller.config.mirror_ratio` diff --git a/docs/test.md b/docs/test.md deleted file mode 100644 index 9daeafb9..00000000 --- a/docs/test.md +++ /dev/null @@ -1 +0,0 @@ -test From 7e2434f7d84b1085b106e320a429d192c84900ec Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 21 Apr 2020 20:11:33 +0200 Subject: [PATCH 10/13] add analog input tests (still require manual intervention to enable low pass filter) --- tools/odrive/tests/analog_input_test.py | 133 ++++++++++++++++++++++++ tools/odrive/tests/calibration_test.py | 3 - tools/odrive/tests/encoder_test.py | 3 - tools/odrive/tests/pwm_input_test.py | 6 +- tools/odrive/tests/test_runner.py | 51 +++++++-- tools/test-rig-rpi.yaml | 10 +- 6 files changed, 184 insertions(+), 22 deletions(-) create mode 100644 tools/odrive/tests/analog_input_test.py diff --git a/tools/odrive/tests/analog_input_test.py b/tools/odrive/tests/analog_input_test.py new file mode 100644 index 00000000..6a3c73e7 --- /dev/null +++ b/tools/odrive/tests/analog_input_test.py @@ -0,0 +1,133 @@ + +import test_runner + +import time +import math +import os +import numpy as np +import scipy.optimize + +from odrive.enums import errors +from test_runner import * + + +teensy_code_template = """ +void setup() { + analogWriteResolution(10); + // base clock of the PWM timer is 150MHz (on Teensy 4.0) + int freq = 150000000/1024; // ~146.5kHz PWM frequency + analogWriteFrequency({analog_out}, freq); + + // for filtering, assuming we have a 150 Ohm resistor, we need a capacitor of + // 1/(150000000/1024)*2*pi/150 = 2.85954744646751e-07 F, that's ~0.33uF + + //pinMode({lpf_enable}, OUTPUT); +} + +int i = 0; +void loop() { + i++; + i = i & 0x3ff; + if (digitalRead({analog_reset})) + i = 0; + analogWrite({analog_out}, i); + delay(1); +} +""" + + +def fit_sawtooth(data, min_val, max_val, period, range): + """ + Returns the average absolute error and the number of outliers + """ + func = lambda x, a: np.mod((max_val - min_val) / a * (x - a/2) - min_val, max_val - min_val) + min_val + params = scipy.optimize.curve_fit(func, data[:,0], data[:,1], [period])[0] + diffs = data[:,1] - func(data[:,0], *params) + return np.abs(diffs).mean(), np.count_nonzero((diffs > range) | (diffs < -range)) + + +class TestAnalogInput(): + """ + Verifies the Analog input. + + The Teensy generates a PWM signal with a duty cycle that follows a sawtooth signal + with a period of 1 second. The signal should be connected to the ODrive's + analog input through a low-pass-filter. + + ___ ___ + Teensy PWM ----|___|-------o---------|___|----- ODrive Analog Input + 150 Ohm | 150 Ohm + === + | 330nF + | + GND + + """ + + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + for odrive_gpio_num, odrive_gpio in [(2, odrive.gpio3), (3, odrive.gpio4)]: + analog_out_options = [] + lpf_gpio = [gpio for lpf in testrig.get_connected_components(odrive_gpio, LowPassFilterComponent) + for gpio in testrig.get_connected_components(lpf.en, LinuxGpioComponent)] + for teensy_gpio in testrig.get_connected_components(odrive_gpio, TeensyGpio): + teensy = teensy_gpio.parent + analog_reset_options = [] + for gpio in teensy.gpios: + for local_gpio in testrig.get_connected_components(gpio, LinuxGpioComponent): + analog_reset_options.append((gpio, local_gpio)) + analog_out_options.append((teensy, teensy_gpio, analog_reset_options)) + yield (odrive, lpf_gpio, odrive_gpio_num, analog_out_options) + + + def run_test(self, odrive: ODriveComponent, lpf_enable: LinuxGpioComponent, analog_in_num: int, teensy: TeensyComponent, teensy_analog_out: Component, teensy_analog_reset: Component, analog_reset_gpio: LinuxGpioComponent, logger: Logger): + code = teensy_code_template.replace("{analog_out}", str(teensy_analog_out.num)).replace("{analog_reset}", str(teensy_analog_reset.num)) #.replace("lpf_enable", str(lpf_enable.num)) + teensy.compile_and_program(code) + analog_reset_gpio.config(output=True) + analog_reset_gpio.write(True) + lpf_enable.config(output=True) + lpf_enable.write(False) + + logger.debug("Set up analog input...") + + min_val = -20000 + max_val = 20000 + + analog_mapping = [ + None, #odrive.handle.config.gpio1_analog_mapping, + None, #odrive.handle.config.gpio2_analog_mapping, + odrive.handle.config.gpio3_analog_mapping, + odrive.handle.config.gpio4_analog_mapping, + None, #odrive.handle.config.gpio5_analog_mapping, + ][analog_in_num] + + odrive.unuse_gpios() + analog_mapping.endpoint = odrive.handle.axis0.controller._remote_attributes['input_pos'] + analog_mapping.min = min_val + analog_mapping.max = max_val + odrive.save_config_and_reboot() + + + logger.debug("Log test_property...") + log_x = [] + log_y = [] + start = time.monotonic() + analog_reset_gpio.write(False) + while time.monotonic() - start < 5.0: + log_x.append(time.monotonic() - start) + log_y.append(odrive.handle.axis0.controller.input_pos) + + + # Expect mean error to be at most 2% (of the full scale). + # Expect there to be less than 1% outliers, where an outlier is anything that is more than 5% (of full scale) away from the expected value. + full_range = abs(max_val - min_val) + data = np.array([log_x, log_y]).transpose() + mean_error, n_outliers = fit_sawtooth(data, min_val, max_val, 1.05, full_range * 0.05) + + test_assert_eq(mean_error, 0, range = full_range * 0.02) + test_assert_eq(n_outliers, 0, range = len(log_x) * 0.01) + + + +if __name__ == '__main__': + test_runner.run(TestAnalogInput()) diff --git a/tools/odrive/tests/calibration_test.py b/tools/odrive/tests/calibration_test.py index 595b3b92..3cd60740 100644 --- a/tools/odrive/tests/calibration_test.py +++ b/tools/odrive/tests/calibration_test.py @@ -9,9 +9,6 @@ from fibre.utils import Logger from test_runner import * from odrive.enums import * -def modpm(val, range): - return ((val + (range / 2)) % range) - (range / 2) - class TestMotorCalibration(): """ diff --git a/tools/odrive/tests/encoder_test.py b/tools/odrive/tests/encoder_test.py index 016f39da..d1ffa061 100644 --- a/tools/odrive/tests/encoder_test.py +++ b/tools/odrive/tests/encoder_test.py @@ -37,9 +37,6 @@ void loop() { """ -def modpm(val, range): - return ((val + (range / 2)) % range) - (range / 2) - class TestIncrementalEncoder(): def get_test_cases(self, testrig: TestRig): diff --git a/tools/odrive/tests/pwm_input_test.py b/tools/odrive/tests/pwm_input_test.py index fc603eb3..e1dbddd9 100644 --- a/tools/odrive/tests/pwm_input_test.py +++ b/tools/odrive/tests/pwm_input_test.py @@ -37,10 +37,6 @@ void loop() { """ - -def modpm(val, range): - return ((val + (range / 2)) % range) - (range / 2) - class TestPwmInput(): """ Verifies the PWM input. @@ -125,7 +121,7 @@ class TestPwmInput(): test_assert_eq(min_val, with_min, range = step_size) test_assert_eq(max_val, with_max, range = step_size) - def run_test(self, odrive: ODriveComponent, teensy: TeensyComponent, teensy_gpio1: int, teensy_gpio2: int, teensy_gpio3: int, teensy_gpio4: int, logger: Logger): + def run_test(self, odrive: ODriveComponent, teensy: TeensyComponent, teensy_gpio1: Component, teensy_gpio2: Component, teensy_gpio3: Component, teensy_gpio4: Component, logger: Logger): # TODO: test each GPIO separately setup_code = "\n".join(" pinMode(" + str(gpio.num) + ", OUTPUT);" for gpio in [teensy_gpio1, teensy_gpio2, teensy_gpio3, teensy_gpio4]) diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py index d76b32ae..82298f3f 100644 --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -65,6 +65,9 @@ def all_unique(lst): seen = list() return not any(i in seen or seen.append(i) for i in lst) +def modpm(val, range): + return ((val + (range / 2)) % range) - (range / 2) + # Test Components -------------------------------------------------------------# class Component(object): @@ -100,7 +103,8 @@ class ODriveComponent(Component): logger.debug('waiting for {} ({})'.format(self.yaml['name'], self.yaml['serial-number'])) self.handle = odrive.find_any( - path="usb", serial_number=self.yaml['serial-number'], timeout=30)#, printer=print) + path="usb", serial_number=self.yaml['serial-number'], timeout=60)#, printer=print) + assert(self.handle) #for axis_idx, axis_ctx in enumerate(self.axes): # axis_ctx.handle = self.handle.__dict__['axis{}'.format(axis_idx)] for encoder_idx, encoder_ctx in enumerate(self.encoders): @@ -109,6 +113,17 @@ class ODriveComponent(Component): for axis_idx, axis_ctx in enumerate(self.axes): axis_ctx.handle = self.handle.__dict__['axis{}'.format(axis_idx)] + def unuse_gpios(self): + self.handle.config.enable_uart = False + self.handle.axis0.config.enable_step_dir = False + self.handle.axis1.config.enable_step_dir = False + self.handle.config.gpio1_pwm_mapping.endpoint = None + self.handle.config.gpio2_pwm_mapping.endpoint = None + self.handle.config.gpio3_pwm_mapping.endpoint = None + self.handle.config.gpio4_pwm_mapping.endpoint = None + self.handle.config.gpio3_analog_mapping.endpoint = None + self.handle.config.gpio4_analog_mapping.endpoint = None + def save_config_and_reboot(self): self.handle.save_configuration() try: @@ -302,6 +317,15 @@ class TeensyComponent(Component): self.compile(code_fp.name, hex_fp.name) self.program(hex_fp.name, logger) +class LowPassFilterComponent(Component): + def __init__(self, parent: Component): + Component.__init__(self, parent) + self.en = Component(self) + + def get_subcomponents(self): + yield 'en', self.en + + class ProxiedComponent(Component): def __init__(self, impl, *gpio_tuples): """ @@ -348,6 +372,8 @@ class TestRig(): add_component(component_yaml['name'], MotorComponent(component_yaml)) elif component_yaml['type'] == 'encoder': add_component(component_yaml['name'], EncoderComponent(self, component_yaml)) + elif component_yaml['type'] == 'lpf': + add_component(component_yaml['name'], LowPassFilterComponent(self)) else: logger.warn('test rig has unsupported component ' + component_yaml['type']) continue @@ -490,15 +516,20 @@ def run_shell(command_line, logger, env=None, timeout=None): def get_combinations(param_options): - if len(param_options) > 0: - param = param_options[0] - if not is_list_like(param): - param = [param] - - for part1, part2 in itertools.product(param, get_combinations(param_options[1:]) if (len(param_options) > 1) else [()]): - if not isinstance(part1, tuple): - part1 = (part1,) - yield part1 + part2 + if isinstance(param_options, tuple): + if len(param_options) > 0: + for part1, part2 in itertools.product( + get_combinations(param_options[0]), + get_combinations(param_options[1:]) if (len(param_options) > 1) else [()]): + assert(isinstance(part1, tuple)) + assert(isinstance(part2, tuple)) + yield part1 + part2 + elif is_list_like(param_options): + for item in param_options: + for c in get_combinations(item): + yield c + else: + yield (param_options,) def select_params(param_options): # Select parameters from the resource list diff --git a/tools/test-rig-rpi.yaml b/tools/test-rig-rpi.yaml index 3321263f..33588274 100644 --- a/tools/test-rig-rpi.yaml +++ b/tools/test-rig-rpi.yaml @@ -17,7 +17,9 @@ components: name: can0 interface: can0 connected-to: odrive.can - - {type: gpio, num: 19} # need to specify GPIOs explicitly for the generalpurpose type + # need to specify GPIOs explicitly for the generalpurpose type + - {type: gpio, num: 16} + - {type: gpio, num: 19} - {type: gpio, num: 20} - {type: gpio, num: 26} @@ -57,6 +59,9 @@ components: - type: teensy name: teensy + - {type: lpf, name: lpf0} + - {type: lpf, name: lpf1} + connections: - ['odrive.can', 'rpi.can0'] - ['teensy.program', 'rpi.gpio26'] @@ -83,3 +88,6 @@ connections: - ['teensy.gpio2', 'real_encoder.b'] - ['odrive.axis0', 'D5065-270KV_0'] - ['D5065-270KV_0', 'real_encoder'] + - ['odrive.gpio3', 'lpf0'] + - ['odrive.gpio4', 'lpf1'] + - ['lpf0.en', 'lpf1.en', 'rpi.gpio16'] From 71d640e562a275cbe633eb60e851e55d4a276e3f Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 23 Apr 2020 11:10:24 +0200 Subject: [PATCH 11/13] add sin/cos encoder test, improve sawtooth fitting --- tools/odrive/tests/analog_input_test.py | 34 ++--- tools/odrive/tests/encoder_test.py | 187 +++++++++++++++++------- tools/odrive/tests/pwm_input_test.py | 2 +- tools/odrive/tests/test_runner.py | 97 ++++++++++-- 4 files changed, 239 insertions(+), 81 deletions(-) diff --git a/tools/odrive/tests/analog_input_test.py b/tools/odrive/tests/analog_input_test.py index 6a3c73e7..d1f83774 100644 --- a/tools/odrive/tests/analog_input_test.py +++ b/tools/odrive/tests/analog_input_test.py @@ -5,7 +5,6 @@ import time import math import os import numpy as np -import scipy.optimize from odrive.enums import errors from test_runner import * @@ -36,16 +35,6 @@ void loop() { """ -def fit_sawtooth(data, min_val, max_val, period, range): - """ - Returns the average absolute error and the number of outliers - """ - func = lambda x, a: np.mod((max_val - min_val) / a * (x - a/2) - min_val, max_val - min_val) + min_val - params = scipy.optimize.curve_fit(func, data[:,0], data[:,1], [period])[0] - diffs = data[:,1] - func(data[:,0], *params) - return np.abs(diffs).mean(), np.count_nonzero((diffs > range) | (diffs < -range)) - - class TestAnalogInput(): """ Verifies the Analog input. @@ -92,6 +81,7 @@ class TestAnalogInput(): min_val = -20000 max_val = 20000 + period = 1.025 # period in teensy code is 1s, but due to tiny overhead it's a bit longer analog_mapping = [ None, #odrive.handle.config.gpio1_analog_mapping, @@ -108,24 +98,24 @@ class TestAnalogInput(): odrive.save_config_and_reboot() - logger.debug("Log test_property...") - log_x = [] - log_y = [] + logger.debug("Recording log...") + data = [] start = time.monotonic() analog_reset_gpio.write(False) while time.monotonic() - start < 5.0: - log_x.append(time.monotonic() - start) - log_y.append(odrive.handle.axis0.controller.input_pos) + data.append(( + time.monotonic() - start, + odrive.handle.axis0.controller.input_pos + )) + data = np.array(data) # Expect mean error to be at most 2% (of the full scale). - # Expect there to be less than 1% outliers, where an outlier is anything that is more than 5% (of full scale) away from the expected value. + # Expect there to be less than 2% outliers, where an outlier is anything that is more than 5% (of full scale) away from the expected value. full_range = abs(max_val - min_val) - data = np.array([log_x, log_y]).transpose() - mean_error, n_outliers = fit_sawtooth(data, min_val, max_val, 1.05, full_range * 0.05) - - test_assert_eq(mean_error, 0, range = full_range * 0.02) - test_assert_eq(n_outliers, 0, range = len(log_x) * 0.01) + slope, offset, fitted_curve = fit_sawtooth(data, min_val, max_val) + test_assert_eq(slope, (max_val - min_val) / period, accuracy=0.005) + test_curve_fit(data, fitted_curve, max_mean_err = full_range * 0.02, inlier_range = full_range * 0.05, max_outliers = len(data[:,0]) * 0.02) diff --git a/tools/odrive/tests/encoder_test.py b/tools/odrive/tests/encoder_test.py index d1ffa061..53335716 100644 --- a/tools/odrive/tests/encoder_test.py +++ b/tools/odrive/tests/encoder_test.py @@ -6,6 +6,7 @@ from math import pi import os from fibre.utils import Logger +from odrive.enums import * from test_runner import * @@ -36,8 +37,100 @@ void loop() { """ +teensy_code_template2 = """ +void setup() { + analogWriteResolution(10); + int freq = 150000000/1024; // ~146.5kHz PWM frequency + analogWriteFrequency({enc_sin}, freq); + analogWriteFrequency({enc_cos}, freq); +} -class TestIncrementalEncoder(): +int rpm = 60; +float pos = 0; + +void loop() { + pos += 0.001f * ((float)rpm / 60.0f); + if (pos > 1.0f) + pos -= 1.0f; + analogWrite({enc_sin}, (int)(512.0f + 512.0f * sin(2.0f * M_PI * pos))); + analogWrite({enc_cos}, (int)(512.0f + 512.0f * cos(2.0f * M_PI * pos))); + delay(1); +} +""" + + +class TestEncoderBase(): + """ + Base class for encoder tests. + TODO: incremental encoder doesn't use this yet. + + All encoder tests expect the encoder to run at a constant velocity. + This can be achieved by generating an encoder signal with a Teensy. + + During 5 seconds, several variables are recorded and then compared against + the expected waveform. This is either a straight line, a sawtooth function + or a constant. + """ + + def run_generic_encoder_test(self, encoder, true_cpr, true_rps): + encoder.config.cpr = true_cpr + true_cps = true_cpr * true_rps + + logger.debug("Recording log...") + data = [] + start = time.monotonic() + encoder.set_linear_count(0) # prevent numerical errors + while time.monotonic() - start < 5.0: + data.append(( + time.monotonic() - start, + encoder.shadow_count, + encoder.count_in_cpr, + encoder.phase, + encoder.pos_estimate, + encoder.pos_cpr, + encoder.vel_estimate, + )) + + data = np.array(data) + + short_period = (abs(1 / true_rps) < 5.0) + reverse = (true_rps < 0) + + # encoder.shadow_count + slope, offset, fitted_curve = fit_line(data[:,(0,1)]) + test_assert_eq(slope, true_cps, accuracy=0.005) + test_curve_fit(data[:,(0,1)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.01, max_outliers = len(data[:,0]) * 0.02) + + # encoder.count_in_cpr + slope, offset, fitted_curve = fit_sawtooth(data[:,(0,2)], true_cpr if reverse else 0, 0 if reverse else true_cpr) + test_assert_eq(slope, true_cps, accuracy=0.005) + test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.01, max_outliers = len(data[:,0]) * 0.02) + + # encoder.phase + slope, offset, fitted_curve = fit_sawtooth(data[:,(0,3)], -pi, pi, sigma=5) + test_assert_eq(slope / 7, 2*pi*abs(true_rps), accuracy=0.01) + test_curve_fit(data[:,(0,3)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.01, max_outliers = len(data[:,0]) * 0.02) + + # encoder.pos_estimate + slope, offset, fitted_curve = fit_line(data[:,(0,4)]) + test_assert_eq(slope, true_cps, accuracy=0.005) + test_curve_fit(data[:,(0,4)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.01, max_outliers = len(data[:,0]) * 0.02) + + # encoder.pos_cpr + slope, offset, fitted_curve = fit_sawtooth(data[:,(0,5)], true_cpr if reverse else 0, 0 if reverse else true_cpr) + test_assert_eq(slope, true_cps, accuracy=0.005) + test_curve_fit(data[:,(0,5)], fitted_curve, max_mean_err = true_cpr * 0.05, inlier_range = true_cpr * 0.05, max_outliers = len(data[:,0]) * 0.02) + + # encoder.vel_estimate + slope, offset, fitted_curve = fit_line(data[:,(0,6)]) + test_assert_eq(slope, 0.0, range = true_cpr * abs(true_rps) * 0.005) + test_assert_eq(offset, true_cpr * true_rps, accuracy = 0.005) + test_curve_fit(data[:,(0,6)], fitted_curve, max_mean_err = true_cpr * 0.05, inlier_range = true_cpr * 0.05, max_outliers = len(data[:,0]) * 0.02) + + + + +class TestIncrementalEncoder(TestEncoderBase): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): @@ -50,41 +143,13 @@ class TestIncrementalEncoder(): ] valid_combinations = [ - [combination[0].parent] + list(combination) + (combination[0].parent,) + tuple(combination) for combination in itertools.product(*gpio_conns) if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) ] yield (encoder, valid_combinations) - def run_delta_test(self, encoder, true_cps, with_cpr): - encoder.config.cpr = with_cpr - - for i in range(100): - now = time.monotonic() - new_shadow_count = encoder.shadow_count - new_count_in_cpr = encoder.count_in_cpr - new_phase = encoder.phase - new_pos_estimate = encoder.pos_estimate - new_pos_cpr = encoder.pos_cpr - - if i > 0: - dt = now - before - test_assert_eq((new_shadow_count - last_shadow_count) / dt, true_cps, accuracy = 0.05) - test_assert_eq(modpm(new_count_in_cpr - last_count_in_cpr, with_cpr) / dt, true_cps, accuracy = 0.3) - #test_assert_eq(modpm(new_phase - last_phase, 2*pi) / dt, 2*pi*true_rps, accuracy = 0.1) - test_assert_eq((new_pos_estimate - last_pos_estimate) / dt, true_cps, accuracy = 0.3) - test_assert_eq(modpm(new_pos_cpr - last_pos_cpr, with_cpr) / dt, true_cps, accuracy = 0.3) - test_assert_eq(encoder.vel_estimate, true_cps, accuracy = 0.05) - - before = now - last_shadow_count = new_shadow_count - last_count_in_cpr = new_count_in_cpr - last_phase = new_phase - last_pos_estimate = new_pos_estimate - last_pos_cpr = new_pos_cpr - - time.sleep(0.01) def run_test(self, enc: EncoderComponent, teensy: TeensyComponent, teensy_gpio_a: int, teensy_gpio_b: int, logger: Logger): true_cps = 8192*-0.5 # counts per second generated by the virtual encoder @@ -92,32 +157,56 @@ class TestIncrementalEncoder(): code = teensy_code_template.replace("{enc_a}", str(teensy_gpio_a.num)).replace("{enc_b}", str(teensy_gpio_b.num)) teensy.compile_and_program(code) - time.sleep(1.0) # wait for PLLs to stabilize + if enc.handle.config.mode != ENCODER_MODE_INCREMENTAL: + enc.handle.config.mode = ENCODER_MODE_INCREMENTAL + enc.parent.save_config_and_reboot() + else: + time.sleep(1.0) # wait for PLLs to stabilize encoder = enc.handle - # The true encoder count and PLL output should be roughly the same. - # At 8192 CPR and 0.5 RPM, the delta because of sequential reading is - # around 3.25 counts. The exact value depends on the connection. - # The tracking error of the PLL is below 1 count. - - #logger.debug("check if count_in_cpr == pos_cpr") - #configured_cpr = 8192 - #encoder.config.cpr = configured_cpr - #expected_delta = true_cps/1200 - #for _ in range(1000): - # first = enc.handle.axis0.encoder.count_in_cpr - # second = enc.handle.axis0.encoder.pos_cpr - # test_assert_eq(modpm(second - first, configured_cpr), expected_delta, range=abs(true_cps/500)) - # time.sleep(0.001) - logger.debug("check if variables move at the correct velocity (8192 CPR)...") - self.run_delta_test(encoder, true_cps, 8192) + self.run_generic_encoder_test(enc.handle, 8192, true_cps / 8192) logger.debug("check if variables move at the correct velocity (65536 CPR)...") - self.run_delta_test(encoder, true_cps, 65536) + self.run_generic_encoder_test(enc.handle, 65536, true_cps / 65536) encoder.config.cpr = 8192 +class TestSinCosEncoder(TestEncoderBase): + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + gpio_conns = [ + testrig.get_directly_connected_components(odrive.gpio3), + testrig.get_directly_connected_components(odrive.gpio4), + ] + + valid_combinations = [ + (combination[0].parent,) + tuple(combination) + for combination in itertools.product(*gpio_conns) + if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) + ] + + yield (odrive.encoders[0], valid_combinations) + + + def run_test(self, enc: EncoderComponent, teensy: TeensyComponent, teensy_gpio_sin: TeensyGpio, teensy_gpio_cos: TeensyGpio, logger: Logger): + code = teensy_code_template2.replace("{enc_sin}", str(teensy_gpio_sin.num)).replace("{enc_cos}", str(teensy_gpio_cos.num)) + teensy.compile_and_program(code) + + if enc.handle.config.mode != ENCODER_MODE_SINCOS: + enc.parent.unuse_gpios() + enc.handle.config.mode = ENCODER_MODE_SINCOS + enc.parent.save_config_and_reboot() + else: + time.sleep(1.0) # wait for PLLs to stabilize + + self.run_generic_encoder_test(enc.handle, 6283, 1.0) + + + if __name__ == '__main__': - test_runner.run(TestIncrementalEncoder()) + test_runner.run([ + TestIncrementalEncoder(), + TestSinCosEncoder(), + ]) diff --git a/tools/odrive/tests/pwm_input_test.py b/tools/odrive/tests/pwm_input_test.py index e1dbddd9..e601bd72 100644 --- a/tools/odrive/tests/pwm_input_test.py +++ b/tools/odrive/tests/pwm_input_test.py @@ -61,7 +61,7 @@ class TestPwmInput(): ] valid_combinations = [ - [combination[0].parent] + list(combination) + (combination[0].parent,) + tuple(combination) for combination in itertools.product(*gpio_conns) if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) ] diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py index 82298f3f..c3787641 100644 --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -17,6 +17,11 @@ import tempfile import io from typing import Union, Tuple +# needed for curve fitting +import numpy as np +import scipy.optimize +import scipy.ndimage.filters + # Assert utils ----------------------------------------------------------------# @@ -68,6 +73,79 @@ def all_unique(lst): def modpm(val, range): return ((val + (range / 2)) % range) - (range / 2) +def fit_line(data): + func = lambda x, a, b: x*a + b + slope, offset = scipy.optimize.curve_fit(func, data[:,0], data[:,1], [1.0, 0])[0] + return slope, offset, func(data[:,0], slope, offset) + +def fit_sawtooth(data, min_val, max_val, sigma=10): + """ + Fits the data to a sawtooth function. + Returns the average absolute error and the number of outliers. + The sample data must span at least one full period. + data is expected to contain one row (t, y) for each sample. + """ + + # Sawtooth function with free parameters for period and x-shift + func = lambda x, a, b: np.mod(a * x + b, max_val - min_val) + min_val + + # Fit period and x-shift + mid_point = (min_val + max_val) / 2 + filtered_data = scipy.ndimage.filters.gaussian_filter(data[:,1], sigma=sigma) + if max_val > min_val: + zero_crossings = data[np.where((filtered_data[:-1] > mid_point) & (filtered_data[1:] < mid_point))[0], 0] + else: + zero_crossings = data[np.where((filtered_data[:-1] < mid_point) & (filtered_data[1:] > mid_point))[0], 0] + + + if len(zero_crossings) == 0: + # No zero-crossing - fit simple line + slope, offset, _ = fit_line(data) + + elif len(zero_crossings) == 1: + # One zero-crossing - fit line based on the longer half + z_index = np.where(data[:,0] > zero_crossings[0])[0][0] + if z_index > len(data[:,0]): + slope, offset, _ = fit_line(data[:z_index]) + else: + slope, offset, _ = fit_line(data[z_index:]) + + else: + # Two or more zero-crossings - determine period based on average distance between zero-crossings + + period = (zero_crossings[1:] - zero_crossings[:-1]).mean() + slope = (max_val - min_val) / period + + #shift = scipy.optimize.curve_fit(lambda x, b: func(x, period, b), data[:,0], data[:,1], [0.0])[0][0] + if np.std(np.mod(zero_crossings, period)) < np.std(np.mod(zero_crossings + period/2, period)): + shift = np.mean(np.mod(zero_crossings, period)) + else: + shift = np.mean(np.mod(zero_crossings + period/2, period)) - period/2 + offset = -slope * shift + + return slope, offset, func(data[:,0], slope, offset) + +def test_curve_fit(data, fitted_curve, max_mean_err, inlier_range, max_outliers): + def save(): + import json + filename = '/tmp/log.json' + print('saving data to ' + filename) + with open(filename, 'w+') as fp: + json.dump(np.concatenate([data, np.array([fitted_curve]).transpose()], 1).tolist(), fp, indent=2) + + diffs = data[:,1] - fitted_curve + + mean_err = np.abs(diffs).mean() + if mean_err > max_mean_err: + save() + raise TestFailed("curve fit has too large mean error: {} > {}".format(mean_err, max_mean_err)) + + outliers = np.count_nonzero((diffs > inlier_range) | (diffs < -inlier_range)) + if outliers > max_outliers: + save() + raise TestFailed("curve fit has too many outliers (err > {}): {} > {}".format(inlier_range, outliers, max_outliers)) + + # Test Components -------------------------------------------------------------# class Component(object): @@ -307,15 +385,16 @@ class TeensyComponent(Component): time.sleep(0.5) # give it some time to boot def compile_and_program(self, code: str): - with io.TextIOWrapper(tempfile.NamedTemporaryFile(suffix='.ino')) as code_fp: - code_fp.write(code) - code_fp.flush() - code_fp.seek(0) - print('Writing code to teensy: ') - print(code_fp.read()) - with tempfile.NamedTemporaryFile(suffix='.hex') as hex_fp: - self.compile(code_fp.name, hex_fp.name) - self.program(hex_fp.name, logger) + with tempfile.TemporaryDirectory() as temp_dir: + with open(os.path.join(temp_dir, 'code.ino'), 'w+') as code_fp: + code_fp.write(code) + code_fp.flush() + code_fp.seek(0) + print('Writing code to teensy: ') + print(code_fp.read()) + with tempfile.NamedTemporaryFile(suffix='.hex') as hex_fp: + self.compile(code_fp.name, hex_fp.name) + self.program(hex_fp.name, logger) class LowPassFilterComponent(Component): def __init__(self, parent: Component): From c45ef8934b80e30d5689712dca515af68c3afa1f Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 23 Apr 2020 14:35:49 +0200 Subject: [PATCH 12/13] Change wrap_pm to be based on fmodf Before this commit, bad user input could cause the device to hang, that is, no longer respond on USB (other side effects are likely, though not tested for). An example of such an input is: odrv0.axis0.encoder.config.cpr = 10 odrv0.axis0.encoder.config.offset = 20000 --- CHANGELOG.md | 2 ++ Firmware/MotorControl/utils.hpp | 26 ++++++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1918483f..13d5fa31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ Please add a note of your changes below this heading if you make a Pull Request. * Some Encoder settings have been made read-only * Cleaned up VSCode C/C++ Configuration settings on Windows with recursive includePath * Now compiling with C++17 +* Fixed a firmware hang that could occur from unlikely but possible user input + # Releases ## [0.4.11] - 2019-07-25 ### Added diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 574aa800..224f3716 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -69,18 +69,6 @@ static const float one_by_sqrt3 = 0.57735026919f; static const float two_by_sqrt3 = 1.15470053838f; static const float sqrt3_by_2 = 0.86602540378f; -//beware of inserting large values! -static inline float wrap_pm(float x, float pm_range) { - while (x >= pm_range) x -= (2.0f * pm_range); - while (x < -pm_range) x += (2.0f * pm_range); - return x; -} - -//beware of inserting large angles! -static inline float wrap_pm_pi(float theta) { - return wrap_pm(theta, M_PI); -} - // like fmodf, but always positive static inline float fmodf_pos(float x, float y) { float out = fmodf(x, y); @@ -89,6 +77,20 @@ static inline float fmodf_pos(float x, float y) { return out; } +/** + * @brief Similar to modulo operator, except that the output range is centered + * around zero. + * The returned value is always in the range [-pm_range, pm_range). + */ +static inline float wrap_pm(float x, float pm_range) { + return fmodf_pos(x + pm_range, 2.0f * pm_range) - pm_range; +} + +//beware of inserting large angles! +static inline float wrap_pm_pi(float theta) { + return wrap_pm(theta, M_PI); +} + // Compute rising edge timings (0.0 - 1.0) as a function of alpha-beta // as per the magnitude invariant clarke transform // The magnitude of the alpha-beta vector may not be larger than sqrt(3)/2 From 826c21267c82d02567cf344c07ec097ea59ede3f Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 23 Apr 2020 16:26:22 +0200 Subject: [PATCH 13/13] add hall effect encoder test --- tools/odrive/tests/encoder_test.py | 97 ++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 13 deletions(-) diff --git a/tools/odrive/tests/encoder_test.py b/tools/odrive/tests/encoder_test.py index 53335716..723432a6 100644 --- a/tools/odrive/tests/encoder_test.py +++ b/tools/odrive/tests/encoder_test.py @@ -34,7 +34,6 @@ void loop() { delayMicroseconds(microseconds_per_count); } } - """ teensy_code_template2 = """ @@ -58,6 +57,34 @@ void loop() { } """ +teensy_code_template3 = """ +void setup() { + pinMode({hall_a}, OUTPUT); + pinMode({hall_b}, OUTPUT); + pinMode({hall_c}, OUTPUT); + digitalWrite({hall_a}, HIGH); +} + +int cpr = 90; // 15 pole-pairs. Value suggested in hoverboard.md +int rpm = 60; +int microseconds_per_count = (1000000 * 60 / cpr / rpm); + +void loop() { + digitalWrite({hall_b}, HIGH); + delayMicroseconds(microseconds_per_count); + digitalWrite({hall_a}, LOW); + delayMicroseconds(microseconds_per_count); + digitalWrite({hall_c}, HIGH); + delayMicroseconds(microseconds_per_count); + digitalWrite({hall_b}, LOW); + delayMicroseconds(microseconds_per_count); + digitalWrite({hall_a}, HIGH); + delayMicroseconds(microseconds_per_count); + digitalWrite({hall_c}, LOW); + delayMicroseconds(microseconds_per_count); +} +""" + class TestEncoderBase(): """ @@ -99,22 +126,22 @@ class TestEncoderBase(): # encoder.shadow_count slope, offset, fitted_curve = fit_line(data[:,(0,1)]) test_assert_eq(slope, true_cps, accuracy=0.005) - test_curve_fit(data[:,(0,1)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.01, max_outliers = len(data[:,0]) * 0.02) + test_curve_fit(data[:,(0,1)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) # encoder.count_in_cpr slope, offset, fitted_curve = fit_sawtooth(data[:,(0,2)], true_cpr if reverse else 0, 0 if reverse else true_cpr) test_assert_eq(slope, true_cps, accuracy=0.005) - test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.01, max_outliers = len(data[:,0]) * 0.02) + test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) # encoder.phase - slope, offset, fitted_curve = fit_sawtooth(data[:,(0,3)], -pi, pi, sigma=5) - test_assert_eq(slope / 7, 2*pi*abs(true_rps), accuracy=0.01) - test_curve_fit(data[:,(0,3)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.01, max_outliers = len(data[:,0]) * 0.02) + slope, offset, fitted_curve = fit_sawtooth(data[:,(0,3)], pi if reverse else -pi, -pi if reverse else pi, sigma=5) + test_assert_eq(slope / 7, 2*pi*true_rps, accuracy=0.01) + test_curve_fit(data[:,(0,3)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) # encoder.pos_estimate slope, offset, fitted_curve = fit_line(data[:,(0,4)]) test_assert_eq(slope, true_cps, accuracy=0.005) - test_curve_fit(data[:,(0,4)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.01, max_outliers = len(data[:,0]) * 0.02) + test_curve_fit(data[:,(0,4)], fitted_curve, max_mean_err = true_cpr * 0.01, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) # encoder.pos_cpr slope, offset, fitted_curve = fit_sawtooth(data[:,(0,5)], true_cpr if reverse else 0, 0 if reverse else true_cpr) @@ -123,8 +150,8 @@ class TestEncoderBase(): # encoder.vel_estimate slope, offset, fitted_curve = fit_line(data[:,(0,6)]) - test_assert_eq(slope, 0.0, range = true_cpr * abs(true_rps) * 0.005) - test_assert_eq(offset, true_cpr * true_rps, accuracy = 0.005) + test_assert_eq(slope, 0.0, range = true_cpr * abs(true_rps) * 0.01) + test_assert_eq(offset, true_cpr * true_rps, accuracy = 0.01) test_curve_fit(data[:,(0,6)], fitted_curve, max_mean_err = true_cpr * 0.05, inlier_range = true_cpr * 0.05, max_outliers = len(data[:,0]) * 0.02) @@ -163,13 +190,13 @@ class TestIncrementalEncoder(TestEncoderBase): else: time.sleep(1.0) # wait for PLLs to stabilize - encoder = enc.handle + enc.handle.config.bandwidth = 1000 - logger.debug("check if variables move at the correct velocity (8192 CPR)...") + logger.debug("testing with 8192 CPR...") self.run_generic_encoder_test(enc.handle, 8192, true_cps / 8192) - logger.debug("check if variables move at the correct velocity (65536 CPR)...") + logger.debug("testing with 65536 CPR...") self.run_generic_encoder_test(enc.handle, 65536, true_cps / 65536) - encoder.config.cpr = 8192 + enc.handle.config.cpr = 8192 @@ -201,12 +228,56 @@ class TestSinCosEncoder(TestEncoderBase): else: time.sleep(1.0) # wait for PLLs to stabilize + enc.handle.config.bandwidth = 100 + self.run_generic_encoder_test(enc.handle, 6283, 1.0) +class TestHallEffectEncoder(TestEncoderBase): + + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + for encoder in odrive.encoders: + # Find the Teensy that is connected to the encoder pins and the corresponding Teensy GPIOs + + gpio_conns = [ + testrig.get_directly_connected_components(encoder.a), + testrig.get_directly_connected_components(encoder.b), + testrig.get_directly_connected_components(encoder.z), + ] + + valid_combinations = [ + (combination[0].parent,) + tuple(combination) + for combination in itertools.product(*gpio_conns) + if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) + ] + + yield (encoder, valid_combinations) + + + def run_test(self, enc: EncoderComponent, teensy: TeensyComponent, teensy_gpio_a: int, teensy_gpio_b: int, teensy_gpio_c: int, logger: Logger): + true_cpr = 90 + true_rps = -1.0 + + code = teensy_code_template3.replace("{hall_a}", str(teensy_gpio_a.num)).replace("{hall_b}", str(teensy_gpio_b.num)).replace("{hall_c}", str(teensy_gpio_c.num)) + teensy.compile_and_program(code) + + if enc.handle.config.mode != ENCODER_MODE_HALL: + enc.handle.config.mode = ENCODER_MODE_HALL + enc.parent.save_config_and_reboot() + else: + time.sleep(1.0) # wait for PLLs to stabilize + + enc.handle.config.bandwidth = 100 + + self.run_generic_encoder_test(enc.handle, true_cpr, true_rps) + enc.handle.config.cpr = 8192 + + if __name__ == '__main__': test_runner.run([ TestIncrementalEncoder(), TestSinCosEncoder(), + TestHallEffectEncoder(), ])