From 8e3b3bb099f8c10982b2a394dfa0b2f5b921868d Mon Sep 17 00:00:00 2001 From: Pilar-TG0 Date: Fri, 23 Dec 2022 13:42:34 +0000 Subject: [PATCH 01/10] Add Package Source Code and Essential Files Upload all relevant files, including: - etee_api pkg source files - example code - requirements.txt - setup scripts for package installation (setup.py) and venv (setup_repo.py) - license - gitignore --- .gitignore | 50 + LICENSE.txt | 202 +++ etee/__init__.py | 8 + etee/_version.py | 1 + etee/ahrs.py | 234 +++ etee/config/etee_controller.yaml | 218 +++ etee/driver_eteecontroller.py | 1520 +++++++++++++++++ etee/quaternion.py | 206 +++ etee/tangio_for_etee/__init__.py | 2 + etee/tangio_for_etee/driver_base.py | 643 +++++++ etee/tangio_for_etee/utilities.py | 90 + .../right_index_events_based.py | 68 + .../right_index_getter_function.py | 77 + .../02_Print_Data/print_etee_euler_angles.py | 99 ++ .../02_Print_Data/print_etee_finger_data.py | 114 ++ .../print_etee_fingers_all_data.py | 96 ++ examples/02_Print_Data/print_etee_imu.py | 119 ++ .../02_Print_Data/print_etee_quaternions.py | 94 + .../02_Print_Data/print_etee_slider_data.py | 102 ++ .../print_etee_sliders_buttons.py | 116 ++ .../02_Print_Data/print_etee_trackpad_data.py | 101 ++ .../03_Plot_Data/plot_etee_euler_angles.py | 113 ++ .../03_Plot_Data/plot_etee_quaternions.py | 108 ++ requirements.txt | 6 + setup.py | 33 + setup_repo.py | 67 + 26 files changed, 4487 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 etee/__init__.py create mode 100644 etee/_version.py create mode 100644 etee/ahrs.py create mode 100644 etee/config/etee_controller.yaml create mode 100644 etee/driver_eteecontroller.py create mode 100644 etee/quaternion.py create mode 100644 etee/tangio_for_etee/__init__.py create mode 100644 etee/tangio_for_etee/driver_base.py create mode 100644 etee/tangio_for_etee/utilities.py create mode 100644 examples/01_Retrieving_Data/right_index_events_based.py create mode 100644 examples/01_Retrieving_Data/right_index_getter_function.py create mode 100644 examples/02_Print_Data/print_etee_euler_angles.py create mode 100644 examples/02_Print_Data/print_etee_finger_data.py create mode 100644 examples/02_Print_Data/print_etee_fingers_all_data.py create mode 100644 examples/02_Print_Data/print_etee_imu.py create mode 100644 examples/02_Print_Data/print_etee_quaternions.py create mode 100644 examples/02_Print_Data/print_etee_slider_data.py create mode 100644 examples/02_Print_Data/print_etee_sliders_buttons.py create mode 100644 examples/02_Print_Data/print_etee_trackpad_data.py create mode 100644 examples/03_Plot_Data/plot_etee_euler_angles.py create mode 100644 examples/03_Plot_Data/plot_etee_quaternions.py create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 setup_repo.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b24d71e --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# These are some examples of commonly ignored file patterns. +# You should customize this list as applicable to your project. +# Learn more about .gitignore: +# https://www.atlassian.com/git/tutorials/saving-changes/gitignore + +# Node artifact files +node_modules/ +dist/ + +# Compiled Java class files +*.class + +# Compiled Python bytecode +*.py[cod] + +# Log files +*.log + +# Package files +*.jar + +# Maven +target/ +dist/ + +# JetBrains IDE +.idea/ + +# Unit test reports +TEST*.xml + +# Generated by MacOS +.DS_Store + +# Generated by Windows +Thumbs.db + +# Applications +*.app +*.exe +*.war + +# Large media files +*.mp4 +*.tiff +*.avi +*.flv +*.mov +*.wmv + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/etee/__init__.py b/etee/__init__.py new file mode 100644 index 0000000..8d70ac6 --- /dev/null +++ b/etee/__init__.py @@ -0,0 +1,8 @@ +# Import subpackage methods +from .tangio_for_etee import * + +# Import main package methods +from .quaternion import * +from .ahrs import * +from .driver_eteecontroller import * +from ._version import __version__ diff --git a/etee/_version.py b/etee/_version.py new file mode 100644 index 0000000..5becc17 --- /dev/null +++ b/etee/_version.py @@ -0,0 +1 @@ +__version__ = "1.0.0" diff --git a/etee/ahrs.py b/etee/ahrs.py new file mode 100644 index 0000000..e3676e6 --- /dev/null +++ b/etee/ahrs.py @@ -0,0 +1,234 @@ +""" +License: +-------- +Copyright 2022 Tangi0 Ltd. (trading as TG0) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +File description: +----------------- +Class and methods for the Attitude and Heading Reference System (AHRS) calculations. +Includes methods to set the IMU sensor offsets, update the AHRS or calculate the device quaternions. + +""" + +import math +from queue import Queue +import time +import warnings + +import numpy as np +from numpy.linalg import norm +from etee import Quaternion + + +class Ahrs: + """ + Class implementing Attitude and Heading Reference System (AHRS) calculations. + """ + beta = 0.0315 + accel_sensitivity = 4.0 / 32768.0 + gyro_sensitivity = (2000.0 / 32768.0) * (math.pi/180) + mag_sensitivity = [0.38, 0.38, 0.61] + + @staticmethod + def _current_seconds_time(): + """ + Get current time. + + :return: current time as float + """ + return time.time() + + def __init__(self, sampleperiod=None, quaternion=None, beta=None): + """ + Initialize the class with the given parameters. + + :param float sampleperiod: the sample period + :param list[float] quaternion: initial quaternion + :param float beta: algorithm gain beta + """ + if sampleperiod is not None: + self.samplePeriod = sampleperiod + if quaternion is not None: + self.quaternion = quaternion + if beta is not None: + self.beta = beta + + self.samplePeriod = 1/97 + self.quaternion = Quaternion(1, 0, 0, 0) + self.euler = [0, 0, 0] + self.dynamicFrequencyQueue = Queue(100) + + self.gyro_offset = [0, 0, 0] + self.mag_offset = [0, 0, 0] + + def set_gyro_offset(self, value): + """ + Set gyroscope offsets to given values. + + :param list[float] value: New gyroscope offset values + """ + self.gyro_offset = value + + def set_mag_offset(self, value): + """ + Set magnetometer offsets to given values. + + :param list[float] value: New magnetometer offset values + """ + self.mag_offset = value + + def update(self, gyroscope, accelerometer, magnetometer): + """ + Perform one update step with data from a AHRS sensor array. + + :param list[float] gyroscope: A three-element array containing the gyroscope data in radians per second. + :param list[float] accelerometer: A three-element array containing the accelerometer data. + Can be any unit since a normalized value is used. + :param list[float] magnetometer: A three-element array containing the magnetometer data. + Can be any unit since a normalized value is used. + """ + q = self.quaternion + + gyroscope = np.array(gyroscope, dtype=float).flatten() + accelerometer = np.array(accelerometer, dtype=float).flatten() + magnetometer = np.array(magnetometer, dtype=float).flatten() + + # Normalise accelerometer measurement + if norm(accelerometer) == 0: + warnings.warn("accelerometer is zero") + return + accelerometer /= norm(accelerometer) + + # Normalise magnetometer measurement + if norm(magnetometer) == 0: + warnings.warn("magnetometer is zero") + return + magnetometer /= norm(magnetometer) + + h = q * (Quaternion(0, magnetometer[0], magnetometer[1], magnetometer[2]) * q.conj()) + b = np.array([0, norm(h[1:3]), 0, h[3]]) + + # Gradient descent algorithm corrective step + f = np.array([ + 2*(q[1]*q[3] - q[0]*q[2]) - accelerometer[0], + 2*(q[0]*q[1] + q[2]*q[3]) - accelerometer[1], + 2*(0.5 - q[1]**2 - q[2]**2) - accelerometer[2], + 2*b[1]*(0.5 - q[2]**2 - q[3]**2) + 2*b[3]*(q[1]*q[3] - q[0]*q[2]) - magnetometer[0], + 2*b[1]*(q[1]*q[2] - q[0]*q[3]) + 2*b[3]*(q[0]*q[1] + q[2]*q[3]) - magnetometer[1], + 2*b[1]*(q[0]*q[2] + q[1]*q[3]) + 2*b[3]*(0.5 - q[1]**2 - q[2]**2) - magnetometer[2] + ]) + j = np.array([ + [-2*q[2], 2*q[3], -2*q[0], 2*q[1]], + [2*q[1], 2*q[0], 2*q[3], 2*q[2]], + [0, -4*q[1], -4*q[2], 0], + [-2*b[3]*q[2], 2*b[3]*q[3], -4*b[1]*q[2]-2*b[3]*q[0], -4*b[1]*q[3]+2*b[3]*q[1]], + [-2*b[1]*q[3]+2*b[3]*q[1], 2*b[1]*q[2]+2*b[3]*q[0], 2*b[1]*q[1]+2*b[3]*q[3], -2*b[1]*q[0]+2*b[3]*q[2]], + [2*b[1]*q[2], 2*b[1]*q[3]-4*b[3]*q[1], 2*b[1]*q[0]-4*b[3]*q[2], 2*b[1]*q[1]] + ]) + step = j.T.dot(f) + step /= norm(step) # normalise step magnitude + + # Compute rate of change of quaternion + qdot = (q * Quaternion(0, gyroscope[0], gyroscope[1], gyroscope[2])) * 0.5 - self.beta * step.T + + # Integrate to yield quaternion + q += qdot * self.samplePeriod + self.quaternion = Quaternion(q / norm(q)) # normalise quaternion + + def update_imu(self, gyroscope, accelerometer): + """ + Perform one update step with data from an IMU sensor array. + + :param list[float] gyroscope: A three-element array containing the gyroscope data in radians per second. + :param list[float] accelerometer: A three-element array containing the accelerometer data. + Can be any unit since a normalized value is used. + """ + q = self.quaternion + + gyroscope = np.array(gyroscope, dtype=float).flatten() + accelerometer = np.array(accelerometer, dtype=float).flatten() + + # Normalise accelerometer measurement + if norm(accelerometer) == 0: + return + accelerometer /= norm(accelerometer) + + # Gradient descent algorithm corrective step + f = np.array([ + 2*(q[1]*q[3] - q[0]*q[2]) - accelerometer[0], + 2*(q[0]*q[1] + q[2]*q[3]) - accelerometer[1], + 2*(0.5 - q[1]**2 - q[2]**2) - accelerometer[2] + ]) + j = np.array([ + [-2*q[2], 2*q[3], -2*q[0], 2*q[1]], + [2*q[1], 2*q[0], 2*q[3], 2*q[2]], + [0, -4*q[1], -4*q[2], 0] + ]) + step = j.T.dot(f) + step /= norm(step) # normalise step magnitude + + # Compute rate of change of quaternion + qdot = (q * Quaternion(0, gyroscope[0], gyroscope[1], gyroscope[2])) * 0.5 - self.beta * step.T + + # Integrate to yield quaternion + q += qdot * self.samplePeriod + self.quaternion = Quaternion(q / norm(q)) # normalise quaternion + + def get_quaternion(self, gyroscope, accelerometer, magnetometer=None): + """ + Calculate and return the quaternion values given the IMU sensors values. + + :param list[float] gyroscope: A three-element array containing the gyroscope data in radians per second. + :param list[float] accelerometer: A three-element array containing the accelerometer data. + Can be any unit since a normalized value is used. + :param list[float] magnetometer: A three-element array containing the magnetometer data. + Can be any unit since a normalized value is used. + :return: Quaternion calculated from the given data. + """ + if self.dynamicFrequencyQueue.full(): + start_time = self.dynamicFrequencyQueue.get() + current_time = self._current_seconds_time() + self.dynamicFrequencyQueue.put(current_time) + self.samplePeriod = (current_time - start_time)/100 + else: + self.dynamicFrequencyQueue.put(self._current_seconds_time()) + + gyroscope = np.array(gyroscope) - np.array(self.gyro_offset) + gyroscope = gyroscope * self.gyro_sensitivity + accelerometer = np.array(accelerometer) * self.accel_sensitivity + + if magnetometer is None: + self.update_imu(gyroscope, accelerometer) + else: + magnetometer = np.array(magnetometer) - np.array(self.mag_offset) + magnetometer = magnetometer * np.array(self.mag_sensitivity) + self.update(gyroscope, accelerometer, magnetometer) + return self.quaternion + + def get_euler(self, gyroscope, accelerometer, magnetometer=None): + """ + Estimate and return the euler angles for the given IMU sensor values. + + :param list[float] gyroscope: A three-element array containing the gyroscope data in radians per second. + :param list[float] accelerometer: A three-element array containing the accelerometer data. + Can be any unit since a normalized value is used. + :param list[float] magnetometer: A three-element array containing the magnetometer data. + Can be any unit since a normalized value is used. + :return: Euler angles estimated from the given data. + """ + self.get_quaternion(gyroscope, accelerometer, magnetometer) + self.euler = self.quaternion.to_euler() + return self.euler diff --git a/etee/config/etee_controller.yaml b/etee/config/etee_controller.yaml new file mode 100644 index 0000000..1b1294a --- /dev/null +++ b/etee/config/etee_controller.yaml @@ -0,0 +1,218 @@ +total_bytes: + data_bytes: 42 + end_bytes: 2 + +widgets: + # -------------- Finger data -------------- + # Pinky + pinky_pull: + bit: [47, 46, 45, 44, 43, 42, 41] + pinky_force: + bit: [ 183, 182, 181, 180, 179, 178, 177 ] + pinky_touched: + byte: 5 + bit: 0 + pinky_clicked: + byte: 0 + bit: 7 + + # Ring + ring_pull: + bit: [39, 38, 37, 36, 35, 34, 33] + ring_force: + bit: [ 175, 174, 173, 172, 171, 170, 169 ] + ring_touched: + byte: 4 + bit: 0 + ring_clicked: + byte: 0 + bit: 6 + + # Middle + middle_pull: + bit: [31, 30, 29, 28, 27, 26, 25] + middle_force: + bit: [ 167, 166, 165, 164, 163, 162, 161 ] + middle_touched: + byte: 3 + bit: 0 + middle_clicked: + byte: 0 + bit: 5 + + # Index + index_pull: + bit: [23, 22, 21, 20, 19, 18, 17] + index_force: + bit: [ 159, 158, 157, 156, 155, 154, 153 ] + index_touched: + byte: 2 + bit: 0 + index_clicked: + byte: 0 + bit: 4 + + # Thumb + thumb_pull: + bit: [15, 14, 13, 12, 11, 10, 9] + thumb_force: + bit: [ 151, 150, 149, 148, 147, 146, 145 ] + thumb_touched: + byte: 1 + bit: 0 + thumb_clicked: + byte: 0 + bit: 3 + + # -------------- Trackpad data -------------- + trackpad_x: # Location + byte: 6 + trackpad_y: + byte: 7 + + trackpad_pull: # Pressure + bit: [111, 110, 109, 108, 107, 106, 105] + trackpad_force: + bit: [ 143, 142, 141, 140, 139, 138, 137 ] + + trackpad_touched: # Touch/Click + byte: 0 + bit: 2 + trackpad_clicked: + byte: 0 + bit: 1 + + # -------------- Slider -------------- + slider_value: # Y-axis location + bit: [79, 78, 77, 76, 75, 74, 73] + slider_touched: # Touch + byte: 9 + bit: 0 + slider_up_touched: # Slider Up/Down Buttons + byte: 11 + bit: 5 + slider_down_touched: + byte: 11 + bit: 6 + + # -------------- Proximity sensor (in tracker) -------------- + tracker_on: # Whether a VR tracker is connected + byte: 11 + bit: 2 + proximity_value: # Analog range + bit: [71, 70, 69, 68, 67, 66, 65] + proximity_touched: # Touch/Click + byte: 8 + bit: 0 + proximity_clicked: + byte: 11 + bit: 1 + + # -------------- IMU -------------- + # Accelerometer + accel_x: + byte: [23, 24] + single_value: + signed: + + accel_y: + byte: [25, 26] + single_value: + signed: + + accel_z: + byte: [27, 28] + single_value: + signed: + + # Magnetometer + mag_x: + byte: [29, 30] + single_value: + signed: + + mag_y: + byte: [31, 32] + single_value: + signed: + + mag_z: + byte: [33, 34] + single_value: + signed: + + # Gyroscope + gyro_x: + byte: [35, 36] + single_value: + signed: + + gyro_y: + byte: [37, 38] + single_value: + signed: + + gyro_z: + byte: [39, 40] + single_value: + signed: + + # -------------- Gestures -------------- + # Grip gesture + grip_pull: + bit: [87, 86, 85, 84, 83, 82, 81] + grip_force: + bit: [ 119, 118, 117, 116, 115, 114, 113 ] + grip_touched: # Gestures + byte: 10 + bit: 0 + grip_clicked: + byte: 11 + bit: 0 + + # Pinch with trackpad + pinch_trackpad_pull: + bit: [ 127, 126, 125, 124, 123, 122, 121 ] + pinch_trackpad_clicked: + byte: 15 + bit: 0 + + # Pinch with thumb finger + pinch_thumbfinger_pull: + bit: [ 135, 134, 133, 132, 131, 130, 129 ] + pinch_thumbfinger_clicked: + byte: 16 + bit: 0 + + # Point where trackpad can be used alongside the gesture. Main point gesture use in VR and XBOX-controller based games. + point_independent_clicked: + byte: 14 + bit: 0 + + # Point where trackpad must not be touched or clicked. Alternative point. + point_exclude_trackpad_clicked: + byte: 13 + bit: 0 + + # -------------- Battery -------------- + battery_level: + bit: [103, 102, 101, 100, 99, 98, 97] + battery_charging: + byte: 11 + bit: 4 + battery_charging_complete: + byte: 12 + bit: 0 + + # -------------- Others -------------- + hand: # Controller in right (1) or left (0) hand + byte: 11 + bit: 3 + system_button: # System/Power Button + byte: 0 + bit: 0 + numeric_point: + bit: [ 335, 334, 333, 332, 331, 330, 329 ] + squeeze: + byte: 41 + bit: 0 diff --git a/etee/driver_eteecontroller.py b/etee/driver_eteecontroller.py new file mode 100644 index 0000000..ab7cb8e --- /dev/null +++ b/etee/driver_eteecontroller.py @@ -0,0 +1,1520 @@ +""" +License: +-------- +Copyright 2022 Tangi0 Ltd. (trading as TG0) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +File description: +----------------- +Main file for package: etee. +Classes and methods for eteeController events, communication and data retrieval. + +""" + +import os +import time + +from .tangio_for_etee import TG0Driver, serial_ports, parse_utf8 +from . import Ahrs + +ETEE_CONTROLLER_DATA_CONFIG = os.path.join(os.path.dirname(__file__), "config", "etee_controller.yaml") + + +class EteeControllerEvent: + """ + This class manages eteeController driver events by allowing callback functions to be connected or disconnected from them. + """ + def __init__(self): + """ + Class constructor method. + """ + self.callbacks = list() + + def connect(self, callback): + """ + Connect a callback function to the event. + + :param callable callback: Callback function. + """ + self.callbacks.append(callback) + + def disconnect(self, callback): + """ + Remove a function from the event callbacks. + + :param callable callback: Callback function. + """ + self.callbacks.remove(callback) + + def emit(self): + """ + Emit event. + """ + for cb in self.callbacks: + cb() + + +class EteeController: + """ + This class manages the communication between the driver and the eteeControllers, through the eteeDongle. + It also handles the data loop which retrieves and stores the eteeControllers data into an internal buffer, + allowing for their retrieval through its class methods. + """ + ETEE_DONGLE_VID = 9114 + ETEE_DONGLE_PID = None + + def __init__(self): + """ + Class constructor method. + """ + self._hand_last_on_left = 0 + self._hand_last_on_right = 0 + self._api_data_left = None + self._api_data_right = None + self._frameno_left = 0 + self._frameno_right = 0 + self._ahrs_left = Ahrs() + self._ahrs_right = Ahrs() + self._quaternion_left = None + self._quaternion_right = None + self._absolute_imu_on = False + self._euler_left = None + self._euler_right = None + + self.driver = TG0Driver(ETEE_CONTROLLER_DATA_CONFIG) + self.driver.add_callback(self._api_data_callback) + self.driver.add_print_callback(self._print_callback) + self.driver.add_serial_exception_callbacks(self._serial_exception_callback) + + self.connection_port = None + self.dongle_connection = False + + # ---------------- Events ---------------- + self.left_hand_received = EteeControllerEvent() + """Event for receiving left controller data. + + :type: Event """ + + self.right_hand_received = EteeControllerEvent() + """Event for receiving right controller data. + + :type: Event """ + + self.hand_received = EteeControllerEvent() + """Event for receiving data from any controller. + + :type: Event """ + + self.left_hand_lost = EteeControllerEvent() + """Event for losing left controller connection. Occurs when data is not received for more than 0.5 seconds. + + :type: Event """ + + self.right_hand_lost = EteeControllerEvent() + """Event for losing right controller connection. Occurs when data is not received for more than 0.5 seconds. + + :type: Event """ + + self.data_lost = EteeControllerEvent() + """Event for losing data from both controllers. Occurs when data is not received for more than 0.5 seconds. + + :type: Event """ + + self.left_connected = EteeControllerEvent() + """Event for left controller connection detected by the dongle. + + :type: Event """ + + self.right_connected = EteeControllerEvent() + """Event for right controller connection detected by the dongle. + + :type: Event """ + + self.left_disconnected = EteeControllerEvent() + """Event for left controller disconnection from the dongle. + + :type: Event """ + + self.right_disconnected = EteeControllerEvent() + """Event for right controller disconnection from the dongle. + + :type: Event """ + + self.dongle_disconnected = EteeControllerEvent() + """Event for dongle disconnection. + + :type: Event """ + + # ---------------- Utility ---------------- + def connect_port(self, port=None): + """ + Attempt to establish serial connection to an etee dongle port. If a COM port argument is provided, + connection is attempted with the specified port. If the port argument is None, the driver automatically detects + any COM ports with an etee dongle and connects to the first available one. Default port value is None. + + :param str or None port: etee dongle COM port. + :return: Success flag - True if the connection is successful, False if otherwise + :rtype: bool + """ + if port[0:3] == 'COM': + return self.driver.connect(port) + else: + raise ValueError("The port value should be of the form 'COMx', where x is the COM port number.") + + def connect(self): + """ + Establish serial connection to an etee dongle. This function automatically detects etee dongles connected to a COM port + and connects to the first available one. + """ + available_ports = self.get_available_etee_ports() + print("The following ports found: {}".format(available_ports)) + if len(available_ports) > 0: + port = available_ports[0] + connected = self.connect_port(port) + if connected: + self.connection_port = port + self.dongle_connection = True + print("Connection to etee dongle successful.") + else: + self.connection_port = None + self.dongle_connection = False + print("Connection to etee dongle unsuccessful.") + else: + self.dongle_connection = False + + def get_number_available_etee_ports(self): + """ + Get the number of available etee dongle COM ports. + + :return: Number of available etee dongle ports. + :rtype: int + """ + available_ports = self.get_available_etee_ports() + return len(available_ports) + + def get_available_etee_ports(self): + """ + Get all available etee dongle COM ports. Other devices are automatically filtered out through a + VID and PID filtering method. + + :return: List of COM port names with etee dongles connected. + :rtype: list[str] + """ + return [x[0] for x in serial_ports(self.ETEE_DONGLE_VID, self.ETEE_DONGLE_PID)] + + def disconnect(self): + """ + Close serial connection to etee dongle. + + :return: Success flag - True if the connection was closed successfully, False if otherwise + :rtype: bool + """ + return self.driver.disconnect() + + def run(self): + """ + Initiates the data loop in a separate thread. The data loop reads serial data, parses it and stores it in an internal buffer. + The data loop also listens to serial and data events and manages event callback functions. + """ + self.driver.run() + + def stop(self): + """ + Stops the data loop. + """ + self.driver.stop() + + def start_data(self): + """ + Sends command to the etee controller to start the data stream. + """ + self.driver.send_command(b"BP+AG\r\n") + + def stop_data(self): + """ + Sends command to the etee controller to stop the data stream. + """ + self.driver.send_command(b"BP+AS\r\n") + + def _api_data_callback(self, frameno, data): + """ + Manages part of the data loop. Parses the argument data and stores it in the corresponding hand's + internal buffer and update the API events (e.g. hand received or hand lost). + + :param dict data: Dictionary of the parsed controller data. + """ + if data["hand"] == 0: + # print(time.time() - self.hand_last_on_left) + self._api_data_left = data + self._hand_last_on_left = time.time() + self._frameno_left += 1 + self._update_quaternion_left() + self.left_hand_received.emit() + + elif data["hand"] == 1: + self._api_data_right = data + self._hand_last_on_right = time.time() + self._frameno_right += 1 + self._update_quaternion_right() + self.right_hand_received.emit() + + self.hand_received.emit() + + if time.time() - self._hand_last_on_left > 0.1 and self._api_data_left is not None: + self._api_data_left = None + self.left_hand_lost.emit() + if time.time() - self._hand_last_on_right > 0.1 and self._api_data_left is not None: + self._api_data_right = None + self.right_hand_lost.emit() + + def _serial_exception_callback(self): + """ + Emit a disconnection event if the etee dongle connection is lost. + """ + ports = self.get_available_etee_ports() + if self.driver.serial_reader.port not in ports: + self.driver.disconnect() + self.dongle_disconnected.emit() + + def _print_callback(self, reading): + """ + Print messages received from the dongle, which are not data packets, and emits the corresponding connection events. + + :param bytes reading: Print message received from dongle. + """ + if reading == b"R connection complete\r\n": + self.right_connected.emit() + elif reading == b"L connection complete\r\n": + self.left_connected.emit() + elif reading == b"R disconnected\r\n": + self._api_data_right = None + self.right_disconnected.emit() + elif reading == b"L disconnected\r\n": + self._api_data_left = None + self.left_disconnected.emit() + + def _rest_callback(self, reading): + """ + Handles controller connection loss events. + Either controller's connection is lost if no data from the controller is received in 100ms. + + :param bytes reading: Print message received from dongle. + """ + if reading is not None: + return + left_lost = time.time() - self._hand_last_on_left > 0.1 and self._api_data_left is not None + right_lost = time.time() - self._hand_last_on_right > 0.1 and self._api_data_right is not None + if left_lost: + self._api_data_left = None + self.left_hand_lost.emit() + if right_lost: + self._api_data_right = None + self.right_hand_lost.emit() + if left_lost and right_lost: + self.data_lost.emit() + + # ---------------- IMU Processing ---------------- + def absolute_imu_enabled(self, on): + """ + Enables or disables absolute orientation. Absolute orientation uses data from the accelerometer, + gyroscope and magnetometer sensors for quaternion calculations. If disabled, the default mode will be enabled, + which uses relative orientation, calculated only though the accelerometer and gyroscope data. + + :param bool on: True to switch to absolute orientation, False for relative orientation. + """ + self._absolute_imu_on = on + + def _update_quaternion_left(self): + """ + Calculates and updates the left controller's quaternion and euler. + """ + accel = [ + self.get_left("accel_x"), + self.get_left("accel_y"), + self.get_left("accel_z")] + gyro = [ + self.get_left("gyro_x"), + self.get_left("gyro_y"), + self.get_left("gyro_z")] + if None in gyro or None in accel: + return + if self._absolute_imu_on: + mag = [ + self.get_left("mag_x"), + self.get_left("mag_y"), + self.get_left("mag_z")] + if None in mag: + return + else: + mag = None + self._quaternion_left = self._ahrs_left.get_quaternion(gyro, accel, mag) + self._euler_left = self._ahrs_left.get_euler(gyro, accel, mag) + + def _update_quaternion_right(self): + """ + Calculates and updates the right controller's quaternion and euler. + """ + accel = [self.get_right("accel_x"), self.get_right("accel_y"), self.get_right("accel_z")] + gyro = [self.get_right("gyro_x"), self.get_right("gyro_y"), self.get_right("gyro_z")] + if None in gyro or None in accel: + return + if self._absolute_imu_on: + mag = [self.get_right("mag_x"), self.get_right("mag_y"), self.get_right("mag_z")] + if None in mag: + return + else: + mag = None + self._quaternion_right = self._ahrs_right.get_quaternion(gyro, accel, mag) + self._euler_right = self._ahrs_right.get_euler(gyro, accel, mag) + + def update_gyro_offset_left(self): + """ + Retrieves the gyroscope calibration parameters saved on the left etee controller, and updates the calibration + offsets in the driver model. + """ + response = self.driver.send_command(b"BL+gf\r\n") + try: + x = float(response.split(b"X:")[1].split(b" ")[0].decode()) + y = float(response.split(b"Y:")[1].split(b" ")[0].decode()) + z = float(response.split(b"Z:")[1].split(b"\r\n")[0].decode()) + self._ahrs_left.set_gyro_offset([x, y, z]) + except: + pass + + def update_gyro_offset_right(self): + """ + Retrieves the gyroscope calibration parameters saved on the right etee controller, and updates the calibration + offsets in the driver model. + """ + response = self.driver.send_command(b"BR+gf\r\n") + try: + x = float(response.split(b"X:")[1].split(b" ")[0].decode()) + y = float(response.split(b"Y:")[1].split(b" ")[0].decode()) + z = float(response.split(b"Z:")[1].split(b"\r\n")[0].decode()) + self._ahrs_right.set_gyro_offset([x, y, z]) + except: + pass + + def update_mag_offset_left(self): + """ + Retrieves the magnetometer calibration parameters saved on the left etee controller, and updates the calibration + offsets in the driver model. + """ + response = self.driver.send_command(b"BL+mf\r\n") + try: + x = float(response.split(b"X:")[1].split(b" ")[0].decode()) + y = float(response.split(b"Y:")[1].split(b" ")[0].decode()) + z = float(response.split(b"Z:")[1].split(b"\r\n")[0].decode()) + self._ahrs_left.set_mag_offset([x, y, z]) + except: + pass + + def update_mag_offset_right(self): + """ + Retrieves the magnetometer calibration parameters saved on the right etee controller, and updates the calibration + offsets in the driver model. + """ + response = self.driver.send_command(b"BR+mf\r\n") + try: + x = float(response.split(b"X:")[1].split(b" ")[0].decode()) + y = float(response.split(b"Y:")[1].split(b" ")[0].decode()) + z = float(response.split(b"Z:")[1].split(b"\r\n")[0].decode()) + self._ahrs_right.set_mag_offset([x, y, z]) + except: + pass + + def update_imu_offsets(self): + """ + Retrieves the gyroscope and magnetometer calibration parameters from both controllers, and updates the calibration + offsets in the driver model. + """ + print("Updating gyro and magnetometer offsets. Please, wait...") + time.sleep(2) + self.update_gyro_offset_left() + self.update_gyro_offset_right() + self.update_mag_offset_left() + self.update_mag_offset_right() + print("Gyro and magnetometer offsets updated!") + + # ---------------- Firmware Versions ---------------- + def get_dongle_version(self): + """ + Retrieve the firmware version of the connected dongle. + + :return: Returns the dongle firmware version if a dongle is connected. + If no dongle is connected, the firmware version value will be None. + :rtype: str + """ + ret = None + response = self.driver.send_command(b"AT+AB\r\n") + response = parse_utf8(response) + if "NRF" in response: + ret = response.split("NRF")[1].split("\r\n")[0] + return ret + + def get_etee_versions(self): + """ + Retrieve the firmware version from the connected controllers. + + :return: Returns the firmware versions of the connected controllers. + If a controller is not connected, its firmware version value will be None. + :rtype: list[str] + """ + ret = [None, None] + response = self.driver.send_command(b"BP+AB\r\n", response_keys=[b"R:AB=etee", b"L:AB=etee"], verbose=True) + if response is None: + return ret + if response[b"R:AB=etee"] is not None and b'-' in response[b"R:AB=etee"]: + ret[1] = response[b"R:AB=etee"].decode().split("-")[1] + if response[b"L:AB=etee"] is not None and b'-' in response[b"L:AB=etee"]: + ret[0] = response[b"L:AB=etee"].decode().split("-")[1] + return ret + + # ---------------- Get controller data by key ---------------- + def get_left(self, w): + """ + Get a key value in the current internal data buffer for the left device. + + :param str w: Key for the device data to be retrieved, as defined in the YAML file. + :return: Left controller's value for the key provided. + """ + if self._api_data_left: + return self._api_data_left[w] + else: + return None + + def get_right(self, w): + """ + Get a key value in the current internal data buffer for the right device. + + :param str w: Key for the device data to be retrieved, as defined in the YAML file. + :return: Right controller's value for the key provided. + """ + if self._api_data_right: + return self._api_data_right[w] + else: + return None + + def get_data(self, dev, w): + """ + Get a key value in the current internal data buffer for the specified device (left or right). + + :param str dev: Selected controller hand. Possible values: "left", "right". + :param str w: Key for the device data to be retrieved, as defined in the YAML file. + :return: Selected controller's value for the key provided. + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left(w) + elif dev == "right": + return self.get_right(w) + else: + raise ValueError("Input 'dev' must be: 'left' or 'right'") + + # ---------------- Get hand/controller connection status ---------------- + def all_hands_on(self): + """ + Check if both left and right controllers are connected. + + :return: Returns true if data has been recently received from both controllers. + :rtype: bool + """ + return (self.get_left is not None) and (self.get_right is not None) + + def any_hand_on(self): + """ + Check if either left or right controller is connected. + + :return: Returns true if data has been recently received from any of the controller. + :rtype: bool + """ + return (self._api_data_left is not None) or (self._api_data_right is not None) + + def left_hand_on(self): + """ + Check if the left controller is connected. + + :return: Returns true if data has been recently received from the left controller. + :rtype: bool + """ + return self._api_data_left is not None + + def right_hand_on(self): + """ + Check if the right controller is connected. + + :return: Returns true if data has been recently received from the right controller. + :rtype: bool + """ + return self._api_data_right is not None + + # ================ Getter functions for sensor data ================ + # ---------------- Get pinky finger data ---------------- + def get_pinky_pull(self, dev): + """ + Returns the pinky finger pull value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Pinky finger pull pressure (i.e. first pressure range, corresponding to light touch) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("pinky_pull") + elif dev == "right": + return self.get_right("pinky_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_pinky_force(self, dev): + """ + Returns the pinky finger force value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Pinky finger force pressure (i.e. second pressure range, corresponding to squeeze levels) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("pinky_force") + elif dev == "right": + return self.get_right("pinky_force") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_pinky_touched(self, dev): + """ + Returns the pinky finger touch value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's pinky finger is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("pinky_touched") + elif dev == "right": + return self.get_right("pinky_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_pinky_clicked(self, dev): + """ + Returns the pinky finger click value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's pinky finger is clicked. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("pinky_clicked") + elif dev == "right": + return self.get_right("pinky_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get ring finger data ---------------- + def get_ring_pull(self, dev): + """ + Returns the ring finger pull value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Ring finger pull pressure (i.e. first pressure range, corresponding to light touch) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("ring_pull") + elif dev == "right": + return self.get_right("ring_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_ring_force(self, dev): + """ + Returns the ring finger force value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Ring finger force pressure (i.e. second pressure range, corresponding to squeeze levels) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("ring_force") + elif dev == "right": + return self.get_right("ring_force") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_ring_touched(self, dev): + """ + Returns the ring finger touch value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's ring finger is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("ring_touched") + elif dev == "right": + return self.get_right("ring_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_ring_clicked(self, dev): + """ + Returns the ring finger click value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's ring finger is clicked. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("ring_clicked") + elif dev == "right": + return self.get_right("ring_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get middle finger data ---------------- + def get_middle_pull(self, dev): + """ + Returns the middle finger pull value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Middle finger pull pressure (i.e. first pressure range, corresponding to light touch) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("middle_pull") + elif dev == "right": + return self.get_right("middle_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_middle_force(self, dev): + """ + Returns the middle finger force value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Middle finger force pressure (i.e. second pressure range, corresponding to squeeze levels) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("middle_force") + elif dev == "right": + return self.get_right("middle_force") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_middle_touched(self, dev): + """ + Returns the middle finger touch value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's middle finger is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("middle_touched") + elif dev == "right": + return self.get_right("middle_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_middle_clicked(self, dev): + """ + Returns the middle finger click value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's middle finger is clicked. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("middle_clicked") + elif dev == "right": + return self.get_right("middle_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get index finger data ---------------- + def get_index_pull(self, dev): + """ + Returns the index finger pull value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Index finger pull pressure (i.e. first pressure range, corresponding to light touch) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("index_pull") + elif dev == "right": + return self.get_right("index_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_index_force(self, dev): + """ + Returns the index finger force value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Index finger force pressure (i.e. second pressure range, corresponding to squeeze levels) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("index_force") + elif dev == "right": + return self.get_right("index_force") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_index_touched(self, dev): + """ + Returns the index finger touch value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's index finger is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("index_touched") + elif dev == "right": + return self.get_right("index_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_index_clicked(self, dev): + """ + Returns the index finger click value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's index finger is clicked. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("index_clicked") + elif dev == "right": + return self.get_right("index_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get thumb finger data ---------------- + def get_thumb_pull(self, dev): + """ + Returns the thumb finger pull value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Thumb finger pull pressure (i.e. first pressure range, corresponding to light touch) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("thumb_pull") + elif dev == "right": + return self.get_right("thumb_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_thumb_force(self, dev): + """ + Returns the thumb finger force value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Thumb finger force pressure (i.e. second pressure range, corresponding to squeeze levels) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("thumb_force") + elif dev == "right": + return self.get_right("thumb_force") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_thumb_touched(self, dev): + """ + Returns the thumb finger touch value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's thumb finger is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("thumb_touched") + elif dev == "right": + return self.get_right("thumb_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_thumb_clicked(self, dev): + """ + Returns the thumb finger click value for the selected device/controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected controller's thumb finger is clicked. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("thumb_clicked") + elif dev == "right": + return self.get_right("thumb_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get all fingers data ---------------- + def get_device_finger_pressures(self, dev): + """ + Returns all the fingers pull and force pressure values. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Two arrays containing the pull and force pressures for the selected controller's five finger sensors. + The first element of each array will be the thumb, and the las the pinky. For example: fingers_pull[2] = index finger pull. + Pull and force values range: 0-126. Base values: 0. + :rtype: list[int], list[int] + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left" or "right": + fingers_pull = [self.get_data(dev, "thumb_pull"), self.get_data(dev, "index_pull"), + self.get_data(dev, "middle_pull"), + self.get_data(dev, "ring_pull"), self.get_data(dev, "pinky_pull")] + fingers_force = [self.get_data(dev, "thumb_force"), self.get_data(dev, "index_force"), + self.get_data(dev, "middle_force"), + self.get_data(dev, "ring_force"), self.get_data(dev, "pinky_force")] + return fingers_pull, fingers_force + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get tracker data ---------------- + def get_tracker_connections(self): + """ + Checks if both eteeTrackers are connected to the controllers. + + :return: Returns True if both trackers are connected. + :rtype: bool + """ + return self.get_left("tracker_on") and self.get_right("tracker_on") + + def get_tracker_connection(self, dev): + """ + Checks if the selected controller has an eteeTracker connected. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Returns True if the selected tracker is connected. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("tracker_on") + elif dev == "right": + return self.get_right("tracker_on") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get proximity sensor data from tracker ---------------- + def get_proximity(self, dev): + """ + Returns the proximity sensor analog value for the selected controller. + This sensor is only available when an eteeTracker is connected. + If disconnected, the value will always be 0, even when the sensor is interacted with. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Value of the selected tracker's proximity sensor. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("proximity_value") + elif dev == "right": + return self.get_right("proximity_value") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_proximity_touched(self, dev): + """ + Returns the proximity sensor touch value for the selected controller. + This sensor is only available when an eteeTracker is connected. + If disconnected, the value will always be false, even when the sensor is touched. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected tracker's proximity sensor value is at touch level. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("proximity_touched") + elif dev == "right": + return self.get_right("proximity_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_proximity_clicked(self, dev): + """ + Returns the proximity sensor click value for the selected controller. + This sensor is only available when an eteeTracker is connected. + If disconnected, the value will always be false, even when the sensor is touched. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected tracker's proximity sensor value is at click level. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("proximity_clicked") + elif dev == "right": + return self.get_right("proximity_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get trackpad data ---------------- + def get_trackpad_x(self, dev): # Location + """ + Returns the trackpad x-axis position for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Trackpad X (horizontal) coordinate for the selected controller. + Range: 0-255. If not touched, the value is 126. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("trackpad_x") + elif dev == "right": + return self.get_right("trackpad_x") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_trackpad_y(self, dev): + """ + Returns the trackpad y-axis position for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Trackpad Y (vertical) coordinate for the selected controller. + Range: 0-255. If not touched, the value is 126. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("trackpad_y") + elif dev == "right": + return self.get_right("trackpad_y") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_trackpad_xy(self, dev): + """ + Returns the trackpad x-axis and y-axis positions for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Trackpad XY coordinates for the selected controller. + Range for each axis coordinate: 0-255. If not touched, the value is 126. + :rtype: list[int] + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left" or "right": + xy = [self.get_data(dev, "trackpad_x"), self.get_data(dev, "trackpad_y")] + if None in xy: + return None + else: + return xy + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_trackpad_pull(self, dev): # Pressure + """ + Returns the trackpad pull pressure value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Trackpad pull pressure (i.e. first pressure range, corresponding to light touch) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("trackpad_pull") + elif dev == "right": + return self.get_right("trackpad_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_trackpad_force(self, dev): + """ + Returns the trackpad force pressure value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Trackpad force pressure (i.e. second pressure range, corresponding to hard press) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("trackpad_force") + elif dev == "right": + return self.get_right("trackpad_force") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_trackpad_touched(self, dev): + """ + Returns the trackpad touch value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected trackpad is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("trackpad_touched") + elif dev == "right": + return self.get_right("trackpad_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_trackpad_clicked(self, dev): + """ + Returns the trackpad click value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected trackpad is clicked. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("trackpad_clicked") + elif dev == "right": + return self.get_right("trackpad_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get slider data ---------------- + def get_slider_value(self, dev): # Location in Y-axis + """ + Returns the slider positional value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: LED slider position, alongside its Y-axis (vertical), for the selected controller. + Range: 0-126. If not touched, the slider value is 126. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("slider_value") + elif dev == "right": + return self.get_right("slider_value") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_slider_touched(self, dev): # Touch + """ + Returns the slider touch value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected LED light is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("slider_touched") + elif dev == "right": + return self.get_right("slider_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_slider_up_button(self, dev): # Slider Up/Down Button + """ + Returns the slider UP button value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the upper part of selected LED is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("slider_up_touched") + elif dev == "right": + return self.get_right("slider_up_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_slider_down_button(self, dev): + """ + Returns the slider DOWN button value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the lower part of selected LED is touched. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("slider_down_touched") + elif dev == "right": + return self.get_right("slider_down_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get grip gesture data ---------------- + def get_grip_pull(self, dev): + """ + Returns the grip gesture's pull pressure value for the selected controller. + If the gesture is not performed, the pull value will be 0. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Grip gesture's pull pressure (i.e. first pressure range, corresponding to light touch) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("grip_pull") + elif dev == "right": + return self.get_right("grip_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_grip_force(self, dev): + """ + Returns the grip gesture's force pressure value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Grip gesture's force pressure (i.e. second pressure range, corresponding to squeeze levels) for the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("grip_force") + elif dev == "right": + return self.get_right("grip_force") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_grip_touched(self, dev): + """ + Returns the grip gesture's touch value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the grip gesture reaches touch level in the selected controller. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("grip_touched") + elif dev == "right": + return self.get_right("grip_touched") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_grip_clicked(self, dev): + """ + Returns the grip gesture's click value for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the grip gesture reaches click level in the selected controller. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("grip_clicked") + elif dev == "right": + return self.get_right("grip_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get standard pinch (using trackpad) gesture data ---------------- + def get_pinch_trackpad_pull(self, dev): + """ + Returns the pull pressure value for the pinch with trackpad gesture in the selected controller. + If the gesture is not performed, the pull value will be 0. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Pull pressure for the pinch gesture (trackpad variation) in the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("pinch_trackpad_pull") + elif dev == "right": + return self.get_right("pinch_trackpad_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_pinch_trackpad_clicked(self, dev): + """ + Returns the click value for the pinch with trackpad gesture in the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the pinch gesture (trackpad variation) reaches click level in the selected controller. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("pinch_trackpad_clicked") + elif dev == "right": + return self.get_right("pinch_trackpad_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get alternative pinch (using thumb finger) gesture data ---------------- + def get_pinch_thumbfinger_pull(self, dev): + """ + Returns the pull pressure value for the pinch with thumb finger gesture in the selected controller. + If the gesture is not performed, the pull value will be 0. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Pull pressure for the pinch gesture (thumb finger variation) in the selected controller. + Range: 0-126. Base value: 0. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("pinch_thumbfinger_pull") + elif dev == "right": + return self.get_right("pinch_thumbfinger_pull") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_pinch_thumbfinger_clicked(self, dev): + """ + Returns the click value for the pinch with thumb finger gesture in the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the pinch gesture (thumb finger variation) reaches click level in the selected controller. + Range: 0-126. Base value: 0. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("pinch_thumbfinger_clicked") + elif dev == "right": + return self.get_right("pinch_thumbfinger_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get independent point (trackpad can be used) gesture data ---------------- + def get_point_independent_clicked(self, dev): + """ + This is the main point gesture used in VR and XBOX-controller based games. + + Returns the click value for the independent point (trackpad can be touched) gesture in the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the independent point gesture variation is detected in the selected controller. In this variation, the trackpad can be used alongside the point gesture. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("point_independent_clicked") + elif dev == "right": + return self.get_right("point_independent_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get exclude-trackpad point (trackpad must not be touched) gesture data ---------------- + def get_point_excl_tp_clicked(self, dev): + """ + This is the alternative point gesture. + + Returns the click value for the exclude-trackpad point (trackpad must not be touched) gesture in the selected controller. + In this variation, if the user touches the trackpad while doing the point gesture, the gesture will be cancelled. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the exclude-trackpad point gesture variation (i.e. where the trackpad is not touched) is detected in the selected controller. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("point_exclude_trackpad_clicked") + elif dev == "right": + return self.get_right("point_exclude_trackpad_clicked") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get IMU and quaternions ---------------- + def get_quaternion(self, dev): + """ + Returns the quaternion for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Rotation quaternion for the selected controller. + :rtype: list[int] + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self._quaternion_left + elif dev == "right": + return self._quaternion_right + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_euler(self, dev): + """ + Returns the euler angles for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Euler angles (roll, pitch, yaw) for the selected controller. + :rtype: list[int] + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self._euler_left + elif dev == "right": + return self._euler_right + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_accel(self, dev): + """ + Returns the accelerometer values for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Acceleration vector for the selected controller. + :rtype: list[int] + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left" or "right": + accel = [self.get_data(dev, "accel_x"), self.get_data(dev, "accel_y"), self.get_data(dev, "accel_z")] + if None in accel: + return None + else: + return accel + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_gyro(self, dev): + """ + Returns the gyroscope values for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Angular acceleration vector for the selected controller. + :rtype: list[int] + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left" or "right": + gyro = [self.get_data(dev, "gyro_x"), self.get_data(dev, "gyro_y"), self.get_data(dev, "gyro_z")] + if None in gyro: + return None + else: + return gyro + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_mag(self, dev): + """ + Returns the magnetometer values for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Magnetic flux density vector for the selected controller. + :rtype: list[int] + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left" or "right": + mag = [self.get_data(dev, "mag_x"), self.get_data(dev, "mag_y"), self.get_data(dev, "mag_z")] + if None in mag: + return None + else: + return mag + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get battery data ---------------- + def get_battery_level(self, dev): + """ + Returns the battery level for the selected controller. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: Battery fuel gauge level for the selected controller. + Range: 0-100. + :rtype: int + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("battery_level") + elif dev == "right": + return self.get_right("battery_level") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_charging_in_progress_status(self, dev): + """ + Checks if the selected controller is charging. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected battery is charging. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("battery_charging") + elif dev == "right": + return self.get_right("battery_charging") + else: + raise ValueError("Input must be: 'left' or 'right'") + + def get_charging_complete_status(self, dev): + """ + Checks if the selected controller has finished charging (battery level is 100%). + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected battery charging has been completed. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("battery_charging_complete") + elif dev == "right": + return self.get_right("battery_charging_complete") + else: + raise ValueError("Input must be: 'left' or 'right'") + + # ---------------- Get system/power button data ---------------- + def get_system_button_pressed(self, dev): + """ + Checks if the system button is pressed. + + :param str dev: Selected device hand. Possible values: "left", "right". + :return: True if the selected system button is pressed. + :rtype: bool + :raises ValueError: if the dev input is not "left" or "right" + """ + if dev == "left": + return self.get_left("system_button") + elif dev == "right": + return self.get_right("system_button") + else: + raise ValueError("Input must be: 'left' or 'right'") diff --git a/etee/quaternion.py b/etee/quaternion.py new file mode 100644 index 0000000..db467da --- /dev/null +++ b/etee/quaternion.py @@ -0,0 +1,206 @@ +""" +License: +-------- +Copyright 2022 Tangi0 Ltd. (trading as TG0) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +File description: +----------------- +Class and methods for quaternion arithmetics and euler angle estimations. + +""" + +import numbers +import numpy as np +import math + + +class Quaternion: + """ + Class implementing basic quaternion arithmetics and euler angle estimations. + """ + def __init__(self, w_or_q, x=None, y=None, z=None): + """ + Initializes a Quaternion object. + + :param float w_or_q: A scalar representing the real part of the quaternion, another Quaternion object or a + four-element array containing the quaternion values. + :param float x: The first imaginary part if w_or_q is a scalar. + :param float y: The second imaginary part if w_or_q is a scalar. + :param float z: The third imaginary part if w_or_q is a scalar. + """ + self._q = np.array([1, 0, 0, 0]) + + if x is not None and y is not None and z is not None: + w = w_or_q + q = np.array([w, x, y, z]) + elif isinstance(w_or_q, Quaternion): + q = np.array(w_or_q.q) + else: + q = np.array(w_or_q) + if len(q) != 4: + raise ValueError("Expecting a 4-element array or w x y z as parameters") + + self._set_q(q) + + # ---------------- Quaternion specific interfaces ---------------- + def conj(self): + """ + Returns the conjugate of the quaternion + + :return: conjugate of the quaternion + """ + return Quaternion(self._q[0], -self._q[1], -self._q[2], -self._q[3]) + + def to_angle_axis(self): + """ + Returns the quaternion's 3D rotation in an axis-angle representation. + + If the quaternion is the identity quaternion (1, 0, 0, 0), a rotation along the x-axis with angle 0 is returned. + + :return: rad, x, y, z --> where rad is the magnitude of rotation around the rotation axis, and x,y,z represent + the rotation axis' direction vector. + """ + if self[0] == 1 and self[1] == 0 and self[2] == 0 and self[3] == 0: + return 0, 1, 0, 0 + rad = np.arccos(self[0]) * 2 + imaginary_factor = np.sin(rad / 2) + if abs(imaginary_factor) < 1e-8: + return 0, 1, 0, 0 + x = self._q[1] / imaginary_factor + y = self._q[2] / imaginary_factor + z = self._q[3] / imaginary_factor + return rad, x, y, z + + @staticmethod + def from_angle_axis(rad, x, y, z): + """ + Returns the quaternion given the axis-angle representation. + + :param float rad: Magnitude of rotation about the rotation axis, in radians. + :param float x: x-component of the rotation axis' direction vector. + :param float y: y-component of the rotation axis' direction vector. + :param float z: z-component of the rotation axis' direction vector. + :return: quaternion + """ + s = np.sin(rad / 2) + return Quaternion(np.cos(rad / 2), x*s, y*s, z*s) + + def to_euler(self): + """ + Convert the quaternion into euler angles (roll, pitch, yaw). + + Roll is rotation around x in radians (counterclockwise), + pitch is rotation around y in radians (counterclockwise), and + yaw is rotation around z in radians (counterclockwise) + + :return: roll, pitch, yaw + """ + w, x, y, z = self._q[0], self._q[1], self._q[2], self._q[3] + + t0 = +2.0 * (w * x + y * z) + t1 = +1.0 - 2.0 * (x * x + y * y) + roll_x = math.atan2(t0, t1) + + t2 = +2.0 * (w * y - z * x) + t2 = +1.0 if t2 > +1.0 else t2 + t2 = -1.0 if t2 < -1.0 else t2 + pitch_y = math.asin(t2) * 2 + + t3 = +2.0 * (w * z + x * y) + t4 = +1.0 - 2.0 * (y * y + z * z) + yaw_z = math.atan2(t3, t4) + + return roll_x, pitch_y, yaw_z # in radians + + # ---------------- Quaternion operations ---------------- + def __mul__(self, other): + """ + Multiply the given quaternion with another quaternion or a scalar + + :param other: Quaternion object or number. + :return: Resultant Quaternion from the operation. + """ + if isinstance(other, Quaternion): + w = self._q[0]*other._q[0] - self._q[1]*other._q[1] - self._q[2]*other._q[2] - self._q[3]*other._q[3] + x = self._q[0]*other._q[1] + self._q[1]*other._q[0] + self._q[2]*other._q[3] - self._q[3]*other._q[2] + y = self._q[0]*other._q[2] - self._q[1]*other._q[3] + self._q[2]*other._q[0] + self._q[3]*other._q[1] + z = self._q[0]*other._q[3] + self._q[1]*other._q[2] - self._q[2]*other._q[1] + self._q[3]*other._q[0] + + return Quaternion(w, x, y, z) + elif isinstance(other, numbers.Number): + q = self._q * other + return Quaternion(q) + + def __add__(self, other): + """ + Add two quaternions element-wise or add a scalar to each element of the quaternion. + + :param other: Quaternion object or number. + :return: Resultant Quaternion from the operation. + """ + if not isinstance(other, Quaternion): + if len(other) != 4: + raise TypeError("Quaternions must be added to other quaternions or a 4-element array") + q = self.q + other + else: + q = self.q + other.q + + return Quaternion(q) + + # ---------------- Implementing other interfaces to ease working with the class ---------------- + def _set_q(self, q): + """ + Set quaternion values. + + :param q: New quaternion value, in ndarray format. + """ + self._q = q + + def _get_q(self): + """ + Return the current quaternion + + :return: current quaternion + """ + return self._q + + q = property(_get_q, _set_q) + + def __getitem__(self, item): + """ + Return specified quaternion component. + + :param item: key for quaternion item to be retrieved + :return: quaternion item value + """ + return self._q[item] + + def __array__(self): + """ + Return a copy of the quaternion ndarray. + + :return: quaternion + """ + return self._q + + def tolist(self): + """ + Convert and return quaternion as a list. + + :return: quaternion as list + """ + return self._q.tolist() + diff --git a/etee/tangio_for_etee/__init__.py b/etee/tangio_for_etee/__init__.py new file mode 100644 index 0000000..0b9f43c --- /dev/null +++ b/etee/tangio_for_etee/__init__.py @@ -0,0 +1,2 @@ +from .utilities import * +from .driver_base import * diff --git a/etee/tangio_for_etee/driver_base.py b/etee/tangio_for_etee/driver_base.py new file mode 100644 index 0000000..3456487 --- /dev/null +++ b/etee/tangio_for_etee/driver_base.py @@ -0,0 +1,643 @@ +""" +License: +-------- +Copyright 2022 Tangi0 Ltd. (trading as TG0) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +File description: +----------------- +Main file for subpackage: tangio_for_etee. +Methods for low-level TG0 device communication. + +""" + +from builtins import object +import threading +from bitstring import BitArray +import atexit +import serial +import warnings +import yaml +import os +import time + +from . import serial_ports + +yaml.warnings({'YAMLLoadWarning': False}) +warnings.filterwarnings("ignore", category=DeprecationWarning) +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' + +DEFAULT_READLINE_TIMEOUT = 1 +DEFAULT_READ_DATA_TIMEOUT = 1 +DEFAULT_READ_SERIAL_TIMEOUT = 1 +DEFAULT_READ_RESPONSE_TIMEOUT = 1 + + +class SerialReader(object): + """ + This class is in charge of connecting to the hardware device through serial communication, and start reading and + parsing the data it receives to the defined data structure. + """ + + def __init__(self, config_file=None, *, baud_rate=115200, data_bytes=None, end_bytes=None, widgets=None): + """ + Initializes SerialReader class with the given parameters. + + :param str config_file: path to the file which contains the data structure definition. + :param int baud_rate: serial connection baud_rate. + :param int data_bytes: byte length of data to be received from the device. + :param int end_bytes: length of data packet delimiter characters (\xff). + :param dict widgets: dictionary defining the data structure. + """ + super().__init__() + self.serial = None + self.baud_rate = baud_rate + self.serial_lock = threading.Lock() + self.port = None + + if config_file is not None or widgets is not None: + self.data_bytes = None + self.end_bytes = None + self.widgets = None + self.configure(config_file, data_bytes=data_bytes, end_bytes=end_bytes, widgets=widgets) + + def connect(self, port=None): + """ + Opens a connection through a serial reader instance through a specified port or the first available TG0 port. + + :param str port: string representation the port of connection. + If None is passed, the driver will connect to the first available TG0 port. + """ + available_ports = serial_ports() + if not available_ports: + raise Exception("No TG0 device is found!") + if port is None: + for i in range(len(available_ports)): + port = available_ports[i][0] + try: + self.serial = serial.Serial(port, self.baud_rate, timeout=0.1, write_timeout=0.1) + print("Connected to port {}".format(port)) + self.port = port + break + except: + print("Failed to connect to port {}, try other port...".format(port)) + elif port in [i[0] for i in available_ports]: + try: + self.serial = serial.Serial(port, self.baud_rate, timeout=1, write_timeout=1) + print("connected to port {}".format(port)) + self.port = port + except: + print("Failed to connect to port {}, try other port...".format(port)) + else: + raise Exception("No TG0 device found at port {}!".format(port)) + + def close_connection(self): + """ + Closes the connection to hardware through the serial port maintained by the serial reader instance. + """ + if self.serial is not None: + self.serial.close() + if self.serial_lock.locked(): + self.serial_lock.release() + self.port = None + print("Connection closed") + + def reset_input(self): + """ + Empty the driver input buffer, which stores the device transmitted data. + """ + self.serial.reset_input_buffer() + + def readline(self, delim=b"\r\n", num=None, timeout=DEFAULT_READ_DATA_TIMEOUT): + """ + Reads bytes from the serial until a delimiter. + + :param bytes delim: delimiter until to which bytes are read. + :param int num: maximum number of characters to be read. + :param float timeout: timeout duration in seconds. + :return: read byte string. + """ + line = b"" + elapsed = 0 + time_start = time.time() + self.serial_lock.acquire() + while elapsed < timeout: + try: + char = self.serial.read() + line = line + char + except serial.SerialException as e: + self.serial_lock.release() + raise e + if isinstance(delim, list): + delim_found = False + for item in delim: + if line[-len(item):] == item: + delim_found = True + break + if delim_found: + break + else: + if line[-len(delim):] == delim: + break + if len(line) == num: + break + elapsed = time.time() - time_start + self.serial_lock.release() + return line + + def write(self, message): + """ + Writes a message to the serial, but does not read the device's response. + + :param bytes message: message written to the serial. + """ + self.serial.write(message) + + def send_command(self, message, response_start=b"OK", response_end=b"END", response_keys=None, timeout=DEFAULT_READ_RESPONSE_TIMEOUT, verbose=False): + """ + Writes a message to the serial, and reads response. + + :param bytes message: message written to the serial. + :param bytes response_start: a delimiter starting from which the response is read. + :param bytes response_end: a delimiter until which the response is read. + :param bytes response_keys: the commands leaves as soon as all these keys are read. + :param float timeout: read timeout duration in seconds. + :param bool verbose: enable or disable verbose mode, which prints messages sent and received through serial. + :return: read response + """ + if verbose: + print("write: ", message) + self.serial.write(message) + previous_timeout = self.serial.timeout + self.serial.timeout = 1 + response = b"" + response_started = (response_start is None) + if response_keys is not None: + keys_received = [False] * len(response_keys) + elapsed = 0 + time_start = time.time() + while elapsed < timeout: + elapsed = time.time() - time_start + line = self.readline(delim=b"\r\n") + if verbose: + print("readline: ", line) + if line == b'': + continue + elif b"\r\n" not in line: + print("Line was not read.") + continue + elif (response_start + b"\r\n") in line: + response += (response_start + b"\r\n") + response_started = True + else: + if response_started: + response += line + if response_end is not None: + if (response_end + b"\r\n") in line: + break + if response_keys is not None: + for i, key in enumerate(response_keys): + if key in line: + keys_received[i] = True + if all(keys_received): + break + if response_keys is not None: + response_dict = dict() + for key in response_keys: + if key in response: + response_dict[key] = response.split(key)[1].split(b"\r\n")[0] + else: + response_dict[key] = None + print(key, response_dict[key]) + response = response_dict + + self.serial.timeout = previous_timeout + + return response + + def configure(self, config_file=None, *, data_bytes=None, end_bytes=None, widgets=None): + """ + Update the information on data structure configuration. + This provides information on data packet structures, and byte-bit location of specific device parameters, + allowing for their retrieval during device communication. + + :param str config_file: path to the file which contains the data structure definition. + :param int data_bytes: byte length of data to be received from the device. + :param int end_bytes: length of data packet delimiter characters (\xff). + :param dict widgets: dictionary defining the data structure. + """ + if config_file is not None: + with open(config_file, 'r') as fstream: + conf_dict = yaml.safe_load(fstream) + self.data_bytes = conf_dict["total_bytes"]["data_bytes"] + self.end_bytes = conf_dict["total_bytes"]["end_bytes"] + self.widgets = conf_dict["widgets"] + else: + if data_bytes is not None and end_bytes is not None and widgets is not None: + self.data_bytes = data_bytes + self.end_bytes = end_bytes + self.widgets = widgets + else: + raise Exception("Since config_file is None, data_bytes, end_bytes and widgets are required arguments.") + + def raw2data(self, raw): + """ + Parses raw data to the specified data structure. + + :param bytes raw: raw binary data from the serial port. + :return: data structure parsed from the raw data following the data structure specification. + """ + events = {} + for name, properties in self.widgets.items(): + if "byte" in properties: + indices = properties["byte"] + is_signed = "signed" in properties + if isinstance(indices, list): + if any([x > len(raw) for x in indices]): + raise Exception("Widget index our of range") + if "single_value" in properties: + event = int.from_bytes(b"".join([raw[x:x + 1] for x in indices]), byteorder='little', signed=is_signed) + else: + event = [int.from_bytes(raw[x:x + 1], byteorder='big', signed=is_signed) for x in indices] + else: + if indices > len(raw): + raise Exception("Widget index our of range") + if 'bit' not in properties: + event = int.from_bytes(raw[indices:indices + 1], byteorder='big', signed=is_signed) + else: + event = BitArray(hex="0x" + raw[indices:indices + 1].hex()).bin[::-1] + event = int(event[properties['bit']]) + elif "bit" in properties: + indices = properties["bit"] + if any([x > len(raw) * 8 for x in indices]): + raise Exception("Widget index our of range") + bit_array = BitArray(hex="0x" + raw[::-1].hex()).bin[::-1] + event = int("".join([bit_array[i] for i in indices]), 2) + + events[name] = event + return events + + def read_widgets_and_text(self, timeout=DEFAULT_READ_SERIAL_TIMEOUT): + """ + Reads binary data from the serial port and calls raw2data to parse it. + + :param float timeout: time after which the method will stop trying to read the widget value, in seconds. + :return: data structure instance parsed using the raw2data method from a serial port raw data reading. + """ + events = None + start_time = time.time() + while time.time() - start_time < timeout: + data = self.readline(delim=[b"\xff\xff", b"\r\n"]) + if b"\xff\xff" == data[-self.end_bytes:] and len(data) == self.data_bytes + self.end_bytes: + data = data[0:-self.end_bytes] + events = self.raw2data(data) + break + if b"\r\n" == data[-2:]: + events = data + break + return events + + +class TG0Driver: + """ + This driver connects to TG0 devices through serial communication and retrieves data in a loop. It then stores this + data in a separate container for each device, overwriting them periodically as new data is fetched from the hardware. + This class is usually used for inheritance from a child class specific to the hardware device that it's been used for. + """ + def __init__(self, config_file=None, *, data_bytes=None, end_bytes=None, widgets=None, keep_alive_period=5): + """ + Initializes the TG0Driver class with the given parameters. + + :param str config_file: path to the file which contains the data structure definition. + :param int data_bytes: byte length of data to be received from the device. + :param int end_bytes: length of data packet delimiter characters (\xff). + :param dict widgets: dictionary defining the data structure. + :param float keep_alive_period: duration of time to keep the connection alive even when no data is transmitted. + """ + self.baud_rate = 115200 + self.serial_reader = None + self.port = None + self.thread = None + self.keep_alive_period = keep_alive_period + self.last_alive_time = 0 + self.config_file = config_file + self.data_bytes = data_bytes + self.end_bytes = end_bytes + self.widgets = widgets + self.run_mode = False + self.sleep_mode = False + self.callbacks = list() + self.print_callbacks = list() + self.rest_callbacks = list() + self.serial_exception_callbacks = list() + self.connection_callbacks = list() + self.current_data = None + self.frameno = -1 + self.read_text = False + self.loop_is_running = False + + def connect(self, port=None, close_at_exit=True): + """ + Attempts to open a connection between the driver and the hardware device. + + :param str port: string representation of the hardware port to be used to establish the connection to hardware. + This is an optional parameter. connect2hardware can be invoked with None port, and it will choose the + first available COM port. + :param bool close_at_exit: boolean to define whether the connection to the device will be closed at exit. + :return: success flag; true if the connection was successful, false otherwise. + """ + try: + self.serial_reader = SerialReader(self.config_file, baud_rate=self.baud_rate, data_bytes=self.data_bytes, + end_bytes=self.end_bytes, widgets=self.widgets) + self.serial_reader.connect(port=port) + if close_at_exit: + self.close_connection_at_exit() + self.last_alive_time = time.time() + self.connection_handler(state=1) + self.port = self.serial_reader.port + return True + except: + self.connection_handler(state=2) + raise Exception("Could not connect to hardware!") + + def disconnect(self): + """ + Commands the SerialReader instance to close the connection to hardware. + + :return: true if the connection to hardware was closed correctly, false otherwise. + """ + self.stop() + try: + self.serial_reader.close_connection() + self.connection_handler(state=0) + return True + except: + return False + + def configure(self, config_file=None, data_bytes=None, end_bytes=None, widgets=None): + """ + Update the information on data structure definition for specific data retrieval during device communication. + + :param str config_file: path to the file which contains the data structure definition. + :param int data_bytes: byte length of data to be received from the device. + :param int end_bytes: length of data packet delimiter characters (\xff). + :param dict widgets: dictionary defining the data structure. + """ + self.serial_reader.configure(config_file, data_bytes=data_bytes, end_bytes=end_bytes, widgets=widgets) + + def run(self): + """ + Launches a separate thread that reads data from the serial connection cyclically, and makes it available + through the getter methods. + """ + self.read_text = True + self.run_mode = True + self.thread = threading.Thread(target=self.loop) + self.thread.daemon = True + self.thread.start() + + def stop(self): + """ + Stops the data thread. + """ + self.run_mode = False + + def sleep(self, timeout=3): + """ + Suspends the data thread. During sleep, no data is read from serial. + + :param timeout: duration of the driver sleep. + :return: false is the driver loop is running. + """ + self.sleep_mode = True + start_time = time.time() + while (time.time() - start_time < timeout): + if not self.loop_is_running: + break + time.sleep(0.01) + return not self.loop_is_running + + def clear_callbacks(self): + """ + Clear all active callbacks. + """ + self.callbacks.clear() + + def add_callback(self, cb): + """ + Adds a specific callback for handling data messages received from the dongle. Note: Data messages contain sensor + information from the controller, and can be identified by their '\\\\xff\\\\xff' end flag. + + :param cb: callback. + """ + self.callbacks.append(cb) + + def add_print_callback(self, cb): + """ + Adds a specific callback for handling print messages received from the dongle, which are not data packets. + Note: Print messages contain information such as connection status or commands, and can be identified by + their '\\\\r\\\\n' end flag. + + :param cb: callback. + """ + self.print_callbacks.append(cb) + + def add_connection_callback(self, cb): + """ + Adds a specific callback for driver to device connection events. + + :param cb: callback. + """ + self.connection_callbacks.append(cb) + + def add_serial_exception_callbacks(self, cb): + """ + Adds a specific callback for raising exceptions errors. + + :param cb: callback. + """ + self.serial_exception_callbacks.append(cb) + + def add_rest_callback(self, cb): + """ + Adds any other callback that is not a data, print, connection or exception callback. + + :param cb: callback. + """ + self.rest_callbacks.append(cb) + + def data_handler(self, frameno, data): + """ + Handles the active callbacks for data parsing. Data received from the devices can be classified in data and + print messages. Data messages contain sensor information from the controller, and can be identified by their + b'\\\\xff\\\\xff' end flag. + + :param int frameno: Frame number. + :param bytes data: Data message received from the device. + """ + for cb in self.callbacks: + cb(frameno, data) + + def print_handler(self, reading): + """ + Handles the active callbacks for device status and command prints. Data received from the devices can be + classified in data and print messages. Print messages contain information such as connection status or + commands, and can be identified by their b'\r\n' end flag. + + :param int frameno: Frame number. + :param bytes data: Print message received from the device. + """ + for cb in self.print_callbacks: + cb(reading) + + def connection_handler(self, state): + """ + Handles the active connection callbacks. + + :param int state: + state 0: not connected, + state 1: connected, + state 2: failed building connection, + state 3: waiting/trying to rebuild previously connected COM. + """ + for cb in self.connection_callbacks: + cb(state) + + def serial_exception_handler(self): + """ + Handles the active callbacks for exception errors. + """ + for cb in self.serial_exception_callbacks: + cb() + + def rest_handler(self, reading): + """ + Handles any other active callbacks that are not data, print, connection or exception callbacks. + + :param bytes reading: Message received. + """ + for cb in self.rest_callbacks: + cb(reading) + + def next(self): + """ + Reads widget data from the data as specified by the data structure and stores it in a data holder. + """ + try: + reading = self.serial_reader.read_widgets_and_text() + except serial.SerialException: + self.serial_exception_handler() + return + + if reading is not None: + self.last_alive_time = time.time() + + if isinstance(reading, dict): + self.current_data = reading + self.frameno += 1 + self.data_handler(self.frameno, reading) + elif isinstance(reading, bytes): + self.print_handler(reading) + else: + self.rest_handler(reading) + + def loop(self): + """ + Method that loops infinitely parsing data read from the serial connection and storing it. + """ + while self.run_mode: + if not self.sleep_mode: + self.next() + self.loop_is_running = True + else: + self.loop_is_running = False + time.sleep(0.01) + + def write(self, message): + """ + Send the message to the hardware device. This is usually an instruction or command to be executed. + + :param bytes message: message to be passed. + """ + self.serial_reader.write(message) + + def send_command(self, command, sleep=True, *args, **kargs): + """ + Suspends reading data from the device. Sends command and reads response. + + :param bytes command: Command to be sent. + :param bool sleep: Boolean to toggle a sleep routine before the command is sent. True by default. + :return: Read response. + """ + if sleep and self.run_mode: + self.sleep() + try: + response = self.serial_reader.send_command(command, *args, **kargs) + except Exception as e: + self.sleep_mode = False + print("Sending command failed") + return + self.sleep_mode = False + return response + + def close_connection_at_exit(self): + """ + Register a callback to close the connection to hardware when the program is about to exit. + """ + atexit.register(self.disconnect) + + def set_alive_period(self, period): + """ + Set a different value for the duration of time to keep the connection alive even when no data is transmitted. + + :param float period: duration of alive period, in seconds. + """ + self.keep_alive_period = period + + def is_alive(self): + """ + Returns if the device connection is alive. + + :return: false if data hs not been received for a time longer than the alive period time. + """ + return (time.time() - self.last_alive_time) < self.keep_alive_period + + def last_alive(self): + """ + Returns the last time when hardware device data was received. + + :return: how long ago the data was last read, in seconds. + """ + return time.time() - self.last_alive_time + + +class _TG0DataQueue: + """ + This class handles the queue for data received from the hardware device. + """ + def __init__(self, queue): + """ + Initialise the class. + """ + self.queue = queue + + def put_frame(self, frameno, data): + """ + Put items into the queue. + + :param int frameno: frame number + :param bytes data: data passed + """ + self.queue.put((frameno, data)) diff --git a/etee/tangio_for_etee/utilities.py b/etee/tangio_for_etee/utilities.py new file mode 100644 index 0000000..d7b8800 --- /dev/null +++ b/etee/tangio_for_etee/utilities.py @@ -0,0 +1,90 @@ +""" +License: +-------- +Copyright 2022 Tangi0 Ltd. (trading as TG0) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +File description: +----------------- +Utility methods to retrieve COM port data and perform parsing/decoding. + +""" + +from serial.tools import list_ports + + +# ------------------------- COM Ports ------------------------- +def get_ports(predicate=None): + """ + Returns list available COM ports and their information. + + :param function predicate: predicate filtering criteria for ports. + :return: list of available COM ports meeting the filtering criteria + """ + if predicate is None: + predicate = lambda x: True + infos = list(filter(predicate, list_ports.comports())) + ports = [(info.device, info.vid) for info in infos] + return ports + + +def _get_port_info_predicate(vid, pid): + """ + Filter available COM ports by VID and/or PID + + :param int vid: device VID to filter. + :param int pid: device PID to filter. + + :return: true if a port fits the VID and/or PID criteria. + """ + if isinstance(vid, list): + return lambda port_info: port_info.vid in vid + elif isinstance(pid, list): + return lambda port_info: port_info.pid in pid + elif vid is not None and pid is not None: + return lambda port_info: port_info.vid == vid and port_info.pid == pid + elif vid is not None: + return lambda port_info: port_info.vid == vid + elif pid is not None: + return lambda port_info: port_info == pid + else: + return None + + +def serial_ports(vid=None, pid=None): + """ + Returns the available serial ports. + If VID and/or PID values are passed, the method will filter out COM ports that do not meet the VID and/or PID criteria. + + :param int vid: Device VID to filter. By default, it is None. + :param int pid: Device PID to filter. By default, it is None. + :return: List of available COM ports after VID/PID filtering. + """ + return get_ports(_get_port_info_predicate(vid, pid)) + + +# ------------------------- Parse Bytestring ------------------------- +def parse_utf8(bytestring): + """ + Decodes and parses a given bytestring. + + :param byte bytestring: bytestring to be parsed + :return: parsed data as string + """ + parsed = b"" + for i, byta in enumerate(bytestring): + if byta < 0x80: + parsed += bytestring[i:i + 1] + return parsed.decode() diff --git a/examples/01_Retrieving_Data/right_index_events_based.py b/examples/01_Retrieving_Data/right_index_events_based.py new file mode 100644 index 0000000..495d709 --- /dev/null +++ b/examples/01_Retrieving_Data/right_index_events_based.py @@ -0,0 +1,68 @@ +""" +Example code: +------------- +This script shows how to use event-based methods to retrieve controller data. +In this case, the process_right_index() callback function is connected to the right_hand_received event. +This will cause the callback function (which prints controller data) to be called every time that data from the +right controller is received by the dongle, and subsequently transmitted to the driver. +""" + +import time +import sys +import keyboard +from datetime import datetime +from etee import EteeController + + +def process_right_index(): + right_index_pull = etee.get_index_pull('right') + right_index_force = etee.get_index_force('right') + current_time = datetime.now().strftime("%H:%M:%S.%f") + print(current_time, f"The right index pressure is: pull = {right_index_pull:>3} | force = {right_index_force:>3}") + + +if __name__ == "__main__": + # Initialise the etee driver + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.right_hand_received.connect(process_right_index) # Add the process_right_index() function + # as callbacks when right controller data is received + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("---") + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + # If no data received from controller, retry controller connection + # If no dongle is connected, exit application + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available == 0: + print("---") + print("Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") \ No newline at end of file diff --git a/examples/01_Retrieving_Data/right_index_getter_function.py b/examples/01_Retrieving_Data/right_index_getter_function.py new file mode 100644 index 0000000..cc3da89 --- /dev/null +++ b/examples/01_Retrieving_Data/right_index_getter_function.py @@ -0,0 +1,77 @@ +""" +Example code: +------------- +This script shows how to use getter functions to retrieve controller data. +In this case, getter functions are called every loop. Getter functions pull data from the driver internal buffer, which +gets updated every time data is received from the controllers. If event-based methods are used, the frequency of data +received from the controllers can be irregular (e.g. 5ms, or 10ms). Unlike the event-based method, getter functions +allow the user to have better control in the timing of data retrieval. +""" + +import time +import sys +import keyboard +from datetime import datetime +from etee import EteeController + + +def process_right_index(): + right_index_pull = etee.get_index_pull('right') + right_index_force = etee.get_index_force('right') + return right_index_pull, right_index_force + + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("---") + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + + # If dongle is connected, print index values + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + num_dongles_available = etee.get_number_available_etee_ports() + current_time = datetime.now().strftime("%H:%M:%S.%f") + + if num_dongles_available > 0: + right_index_pull, right_index_force = process_right_index() + if right_index_pull is None: + print("---") + print(current_time, "Right etee controller not detected. Please reconnect controller.") + etee.start_data() # Retry reconnection and data stream access in controllers + time.sleep(0.05) + else: + print(current_time, f"The right index pressure is: pull = {right_index_pull:>3} | force = {right_index_force:>3}") + time.sleep(0.05) + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/02_Print_Data/print_etee_euler_angles.py b/examples/02_Print_Data/print_etee_euler_angles.py new file mode 100644 index 0000000..8a5e102 --- /dev/null +++ b/examples/02_Print_Data/print_etee_euler_angles.py @@ -0,0 +1,99 @@ +""" +Example code: +------------- +This script prints the estimated Euler angles (roll, pitch, yaw) from the selected eteeController, +using getter functions. +""" + +import time +import sys +import keyboard +from math import pi +from datetime import datetime +from etee import EteeController + +rad2deg_factor = 180/pi + + +def print_title(): + print("======================================================") + print(r" __ ___ ____ ____") + print(r" ___ / /____ ___ / | / __ \/ _/") + print(r" / _ \/ __/ _ \/ _ \ / /| | / /_/ // / ") + print(r"/ __/ /_/ __/ __/ / ___ |/ ____// / ") + print(r"\___/\__/\___/\___/ /_/ |_/_/ /___/ ") + print(" ") + print("Welcome to this etee CLI application.\nYou can print the Euler angles from one controller here.") + print("======================================================") + + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + print_title() + + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("Your selected controller hand: ", controller_selected) + + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + current_time = datetime.now().strftime("%H:%M:%S.%f") + num_dongles_available = etee.get_number_available_etee_ports() + + # If a dongle is connected, try to print finger pressure values + if num_dongles_available > 0: + euler_angles_rad = etee.get_euler(controller_selected) + if euler_angles_rad is None: + print("---") + print(current_time, f"The {controller_selected} etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, + # it will start the etee controller data stream + time.sleep(0.5) + else: + euler_angles_deg = [euler_angles_rad[0] * rad2deg_factor + 180, + euler_angles_rad[1] * rad2deg_factor + 180, + euler_angles_rad[2] * rad2deg_factor + 180] + print(current_time, f"Controller: {controller_selected} --> " + f"Euler Angles: roll({round(euler_angles_deg[0],3):>8}), " + f"pitch({round(euler_angles_deg[1],3):>8}), " + f"yaw({round(euler_angles_deg[2],3):>8})") + time.sleep(0.05) + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/02_Print_Data/print_etee_finger_data.py b/examples/02_Print_Data/print_etee_finger_data.py new file mode 100644 index 0000000..1fd8f70 --- /dev/null +++ b/examples/02_Print_Data/print_etee_finger_data.py @@ -0,0 +1,114 @@ +""" +Example code: +------------- +This script prints the finger values (pressure: pull and force, touch, click) from the selected finger in an +eteeController, using getter functions. +""" + +import time +import sys +import keyboard +from datetime import datetime +from etee import EteeController + + +def process_finger(dev, finger): + finger_pull = finger + "_pull" + finger_force = finger + "_force" + finger_touch = finger + "_touched" + finger_click = finger + "_clicked" + + finger_data = [etee.get_data(dev, finger_pull), etee.get_data(dev, finger_force), + etee.get_data(dev, finger_touch), etee.get_data(dev, finger_click)] + return finger_data + + +def print_title(): + print("======================================================") + print(r" __ ___ ____ ____") + print(r" ___ / /____ ___ / | / __ \/ _/") + print(r" / _ \/ __/ _ \/ _ \ / /| | / /_/ // / ") + print(r"/ __/ /_/ __/ __/ / ___ |/ ____// / ") + print(r"\___/\__/\___/\___/ /_/ |_/_/ /___/ ") + print(" ") + print("Welcome to this etee CLI application.\nYou can print an individual finger data here.") + print("======================================================") + + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + + print_title() + + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("Your selected controller hand: ", controller_selected) + + # Prompt for user to select the finger to print values from + print("---") + print("Please, enter a finger name. Valid options: thumb, index, middle, ring, pinky.") + valid_fingers = ["thumb", "index", "middle", "ring", "pinky"] + finger_selected = input("Enter finger name: ") + while finger_selected not in valid_fingers: + print("Input not valid! Please enter a valid input: thumb, index, middle, ring, pinky.") + finger_selected = input("--> Enter finger name: ") + print("Your selected finger: ", finger_selected) + + # If dongle is connected, print index values + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + current_time = datetime.now().strftime("%H:%M:%S.%f") + num_dongles_available = etee.get_number_available_etee_ports() + + if num_dongles_available > 0: + selected_finger = process_finger(controller_selected, finger_selected) + if selected_finger[0] == None: + print("---") + print(current_time, f"The {controller_selected} etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, it will start etee controller data stream + time.sleep(0.05) + else: + print(current_time, f"Selected: {controller_selected} {finger_selected} --> " + f"Pressure (pull: {selected_finger[0]:<3}, force: {selected_finger[1]:<3}) | " + f"Touch: {'True' if selected_finger[2] else 'False':<5} | " + f"Click: {'True' if selected_finger[3] else 'False':<5}") + time.sleep(0.05) + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/02_Print_Data/print_etee_fingers_all_data.py b/examples/02_Print_Data/print_etee_fingers_all_data.py new file mode 100644 index 0000000..7eef2fe --- /dev/null +++ b/examples/02_Print_Data/print_etee_fingers_all_data.py @@ -0,0 +1,96 @@ +""" +Example code: +------------- +This script prints the finger pressure values (pull and force) from all the fingers in the selected eteeController, +using getter functions. +""" + +import time +import sys +import keyboard + +from datetime import datetime +from etee import EteeController + + +def print_title(): + print("======================================================") + print(r" __ ___ ____ ____") + print(r" ___ / /____ ___ / | / __ \/ _/") + print(r" / _ \/ __/ _ \/ _ \ / /| | / /_/ // / ") + print(r"/ __/ /_/ __/ __/ / ___ |/ ____// / ") + print(r"\___/\__/\___/\___/ /_/ |_/_/ /___/ ") + print(" ") + print("Welcome to this etee CLI application.\nYou can print the data for all fingers in one controller here.") + print("======================================================") + + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + print_title() + + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("Your selected controller hand: ", controller_selected) + + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + current_time = datetime.now().strftime("%H:%M:%S.%f") + num_dongles_available = etee.get_number_available_etee_ports() + + # If a dongle is connected, try to print finger pressure values + if num_dongles_available > 0: + selected_pull, selected_force = etee.get_device_finger_pressures(controller_selected) + if selected_pull[0] is None: + print("---") + print(current_time, f"The {controller_selected} etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, + # it will start the etee controller data stream + time.sleep(0.1) + else: + print(current_time, f"Controller: {controller_selected} --> " + f"Thumb: p({selected_pull[0]:>3}), f({selected_force[0]:>3}) " + f"| Index: p({selected_pull[1]:>3}), f({selected_force[1]:>3}) " + f"| Middle: p({selected_pull[2]:>3}), f({selected_force[2]:>3}) " + f"| Ring: p({selected_pull[3]:>3}), f({selected_force[3]:>3}) " + f"| Pinky: p({selected_pull[4]:>3}), f({selected_force[4]:>3})") + time.sleep(0.05) + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/02_Print_Data/print_etee_imu.py b/examples/02_Print_Data/print_etee_imu.py new file mode 100644 index 0000000..ce7b360 --- /dev/null +++ b/examples/02_Print_Data/print_etee_imu.py @@ -0,0 +1,119 @@ +""" +Example code: +------------- +This script prints the IMU sensor values from the selected eteeController, using getter functions. +""" + +import time +import sys +import keyboard + +from datetime import datetime +from etee import EteeController + + +def print_title(): + print("======================================================") + print(r" __ ___ ____ ____") + print(r" ___ / /____ ___ / | / __ \/ _/") + print(r" / _ \/ __/ _ \/ _ \ / /| | / /_/ // / ") + print(r"/ __/ /_/ __/ __/ / ___ |/ ____// / ") + print(r"\___/\__/\___/\___/ /_/ |_/_/ /___/ ") + print(" ") + print("Welcome to this etee CLI application.\nYou can print the IMU data from one controller here.") + print("======================================================") + + +def adjust_imu(original_arr, offsets_arr): + adj_arr = [0, 0, 0] + if original_arr is None or offsets_arr is None: + pass + else: + for i in range(3): + adj_arr[i] = original_arr[i] - offsets_arr[i] + return adj_arr + + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + print_title() + + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("Your selected controller hand: ", controller_selected) + + # IMU offsets + print("Don't move, calculating IMU offsets...") + time.sleep(2) + accel_offset = etee.get_accel(controller_selected) + gyro_offset = etee.get_gyro(controller_selected) + mag_offset = etee.get_mag(controller_selected) + print("Offsets calculated and applied.") + + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + current_time = datetime.now().strftime("%H:%M:%S.%f") + num_dongles_available = etee.get_number_available_etee_ports() + + # If a dongle is connected, try to print finger pressure values + if num_dongles_available > 0: + accel = etee.get_accel(controller_selected) + adj_accel = adjust_imu(accel, accel_offset) + + gyro = etee.get_gyro(controller_selected) + adj_gyro = adjust_imu(gyro, gyro_offset) + + mag = etee.get_mag(controller_selected) + adj_mag = adjust_imu(mag, mag_offset) + + if adj_accel is None: + print("---") + print(current_time, f"The {controller_selected} etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, + # it will start the etee controller data stream + time.sleep(0.1) + else: + print(current_time, f"Controller: {controller_selected} --> " + f"Accel: X({adj_accel[0]:>6}), Y({adj_accel[1]:>6}), Z({adj_accel[2]:>6}) " + f"| Gyro: X({adj_gyro[0]:>6}), Y({adj_gyro[1]:>6}), Z({adj_gyro[2]:>6}) " + f"| Magnetometer: X({adj_mag[0]:>6}), Y({adj_mag[1]:>6}), Z({adj_mag[2]:>6})") + time.sleep(0.05) + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/02_Print_Data/print_etee_quaternions.py b/examples/02_Print_Data/print_etee_quaternions.py new file mode 100644 index 0000000..475a737 --- /dev/null +++ b/examples/02_Print_Data/print_etee_quaternions.py @@ -0,0 +1,94 @@ +""" +Example code: +------------- +This script prints the quaternions (representing spatial orientation and rotation) from the selected eteeController, +using getter functions. +""" + +import time +import sys +import keyboard + +from datetime import datetime +from etee import EteeController + + +def print_title(): + print("======================================================") + print(r" __ ___ ____ ____") + print(r" ___ / /____ ___ / | / __ \/ _/") + print(r" / _ \/ __/ _ \/ _ \ / /| | / /_/ // / ") + print(r"/ __/ /_/ __/ __/ / ___ |/ ____// / ") + print(r"\___/\__/\___/\___/ /_/ |_/_/ /___/ ") + print(" ") + print("Welcome to this etee CLI application.\nYou can print the quaternion data from one controller here.") + print("======================================================") + + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + print_title() + + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("Your selected controller hand: ", controller_selected) + + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + current_time = datetime.now().strftime("%H:%M:%S.%f") + num_dongles_available = etee.get_number_available_etee_ports() + + # If a dongle is connected, try to print finger pressure values + if num_dongles_available > 0: + quaternions = etee.get_quaternion(controller_selected) + + if quaternions is None: + print("---") + print(current_time, f"The {controller_selected} etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, + # it will start the etee controller data stream + time.sleep(0.1) + else: + print(current_time, f"Controller: {controller_selected} --> " + f"Quaternions: q0({quaternions[0]:>6}), q1({quaternions[1]:>6}), " + f"q2({quaternions[2]:>6}), q3({quaternions[3]:>6})") + time.sleep(0.05) + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/02_Print_Data/print_etee_slider_data.py b/examples/02_Print_Data/print_etee_slider_data.py new file mode 100644 index 0000000..a73dbba --- /dev/null +++ b/examples/02_Print_Data/print_etee_slider_data.py @@ -0,0 +1,102 @@ +""" +Example code: +------------- +This script prints the slider values (position, touch, UP and DOWN buttons) from the selected eteeController, +using getter functions. +""" + +import time +import sys +import keyboard +from datetime import datetime +from etee import EteeController + + +def process_slider(dev): + slider = [etee.get_slider_value(dev), + etee.get_slider_touched(dev), + etee.get_slider_up_button(dev), etee.get_slider_down_button(dev)] + return slider + + +def print_title(): + print("======================================================") + print(r" __ ___ ____ ____") + print(r" ___ / /____ ___ / | / __ \/ _/") + print(r" / _ \/ __/ _ \/ _ \ / /| | / /_/ // / ") + print(r"/ __/ /_/ __/ __/ / ___ |/ ____// / ") + print(r"\___/\__/\___/\___/ /_/ |_/_/ /___/ ") + print(" ") + print("Welcome to this etee CLI application.\nYou can print a controller's slider data here.") + print("======================================================") + + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + + print_title() + + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("You selected controller hand: ", controller_selected) + + # If dongle is connected, print index values + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + current_time = datetime.now().strftime("%H:%M:%S.%f") + num_dongles_available = etee.get_number_available_etee_ports() + + if num_dongles_available > 0: + selected_value = process_slider(controller_selected) + if selected_value[0] == None: + print("---") + print(current_time, f"The {controller_selected} etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, it will start etee controller data stream + time.sleep(0.05) + + else: + print(current_time, f"Controller: {controller_selected} --> " + f"LED Slider Y-Value: {selected_value[0]:>3} " + f"| Slider Touched: {'True' if selected_value[1] else 'False':<5} " + f"| Slider Up Button: {'True' if selected_value[2] else 'False':<5} " + f"| Slider Down Button: {'True' if selected_value[3] else 'False':<5}") + time.sleep(0.05) + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/02_Print_Data/print_etee_sliders_buttons.py b/examples/02_Print_Data/print_etee_sliders_buttons.py new file mode 100644 index 0000000..c352736 --- /dev/null +++ b/examples/02_Print_Data/print_etee_sliders_buttons.py @@ -0,0 +1,116 @@ +""" +Example code: +------------- +This script prints the slider UP/DOWN button values from both eteeControllers, using getter functions. +""" + +import time +import sys +import keyboard +from datetime import datetime +from etee import EteeController +# -*- coding: utf-8 -*- + + +def process_left_slider_buttons(): + left_slider = [etee.get_slider_up_button('left'), etee.get_slider_down_button('left')] + return left_slider + + +def process_right_slider_buttons(): + right_slider = [etee.get_slider_up_button('right'), etee.get_slider_down_button('right')] + return right_slider + + +def print_title(): + print("======================================================") + print(r" __ ___ ____ ____") + print(r" ___ / /____ ___ / | / __ \/ _/") + print(r" / _ \/ __/ _ \/ _ \ / /| | / /_/ // / ") + print(r"/ __/ /_/ __/ __/ / ___ |/ ____// / ") + print(r"\___/\__/\___/\___/ /_/ |_/_/ /___/ ") + print(" ") + print("Welcome to this etee CLI application.\nYou can print both controller's slider UP/DOWN data here.") + print("======================================================") + + +def check_controller_connection(left_data, right_data): + connection = True + if left_data[0] == None and right_data[0] == None: + print("---") + print(current_time, f"The left and right etee controllers were not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, it will start etee controller data stream + time.sleep(0.05) + connection = False + elif left_data[0] == None: + print("---") + print(current_time, f"The left etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, it will start etee controller data stream + time.sleep(0.05) + connection = False + elif right_data[0] == None: + print("---") + print(current_time, f"The right etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, it will start etee controller data stream + time.sleep(0.05) + connection = False + return connection + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + + print_title() + + # If dongle is connected, print index values + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + current_time = datetime.now().strftime("%H:%M:%S.%f") + num_dongles_available = etee.get_number_available_etee_ports() + + if num_dongles_available > 0: + left_buttons = process_left_slider_buttons() + right_buttons = process_right_slider_buttons() + controllers_connected = check_controller_connection(left_buttons, right_buttons) + if controllers_connected: + print(current_time, f"| Left slider - Up {'●' if left_buttons[0] else '○'} " + f", Down {'●' if left_buttons[1] else '○'} " + f"| Right slider - Up {'●' if right_buttons[0] else '○'} " + f", Down {'●' if right_buttons[1] else '○'}") + time.sleep(0.05) + else: + print("Please, connect both controllers.") + + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/02_Print_Data/print_etee_trackpad_data.py b/examples/02_Print_Data/print_etee_trackpad_data.py new file mode 100644 index 0000000..c389b2b --- /dev/null +++ b/examples/02_Print_Data/print_etee_trackpad_data.py @@ -0,0 +1,101 @@ +""" +Example code: +------------- +This script prints the trackpad values (location: x-position and y-position, pressure: pull and force, touch, click) +from the selected eteeController, using getter functions. +""" + +import time +import sys +import keyboard +from datetime import datetime +from etee import EteeController + + +def process_trackpad(dev): + loc = [etee.get_data(dev, "trackpad_x"), etee.get_data(dev, "trackpad_y")] + pressure = [etee.get_data(dev, "trackpad_pull"), etee.get_data(dev, "trackpad_force"), + etee.get_data(dev, "trackpad_touched"), etee.get_data(dev, "trackpad_clicked")] + return loc, pressure + + +def print_title(): + print("======================================================") + print(r" __ ___ ____ ____") + print(r" ___ / /____ ___ / | / __ \/ _/") + print(r" / _ \/ __/ _ \/ _ \ / /| | / /_/ // / ") + print(r"/ __/ /_/ __/ __/ / ___ |/ ____// / ") + print(r"\___/\__/\___/\___/ /_/ |_/_/ /___/ ") + print(" ") + print("Welcome to this etee CLI application.\nYou can print trackpad data here.") + print("======================================================") + + +if __name__ == "__main__": + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + + print_title() + + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("Your selected controller hand: ", controller_selected) + + # If dongle is connected, print index values + while True: + # If 'Esc' key is pressed while printing data, stop controller data stream, data loop and exit application + if keyboard.is_pressed('Esc'): + print("\n'Esc' key was pressed. Exiting application...") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit(0) # Exit driver + + # Else continue printing controller data + else: + current_time = datetime.now().strftime("%H:%M:%S.%f") + num_dongles_available = etee.get_number_available_etee_ports() + + if num_dongles_available > 0: + selected_loc, selected_pressure = process_trackpad(controller_selected) + if selected_loc[0] == None: + print("---") + print(current_time, f"The {controller_selected} etee controller was not detected. Please reconnect controller.") + etee.start_data() # If a controller has reconnected with the dongle, it will start etee controller data stream + time.sleep(0.05) + else: + print(current_time, f"Selected Trackpad: {controller_selected} --> " + f"X-Axis: {selected_loc[0]:<3}, Y-Axis: {selected_loc[1]:<3} |" + f"Pressure (pull: {selected_pressure[0]:<3}, force: {selected_pressure[1]:<3}), " + f"Touch: {'True' if selected_pressure[2] else 'False':<5}, " + f"Click: {'True' if selected_pressure[3] else 'False':<5}") + time.sleep(0.05) + else: + print("---") + print(current_time, "Dongle disconnected. Please, re-insert the dongle and re-run the application.") + + etee.stop_data() # Stop controller data stream + print("Controller data stream stopped.") + etee.stop() # Stop data loop + print("Data loop stopped.") + + time.sleep(0.05) + sys.exit("Exiting application...") diff --git a/examples/03_Plot_Data/plot_etee_euler_angles.py b/examples/03_Plot_Data/plot_etee_euler_angles.py new file mode 100644 index 0000000..3ad8718 --- /dev/null +++ b/examples/03_Plot_Data/plot_etee_euler_angles.py @@ -0,0 +1,113 @@ +""" +Example code: +------------- +This script starts a liveplot of the selected eteeController's Euler angles. +""" + +import matplotlib.pyplot as plt +import matplotlib.animation as animation +import sys +import time +from etee import EteeController +from math import pi +rad2deg_factor = 180/pi + + +# Ask user to select between left or right controller +def select_hand(): + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("Your selected controller hand: ", controller_selected) + return controller_selected + + +# This function is called periodically from FuncAnimation +def animate(i, y_1, y_2, y_3): + # Retrieve euler angles + euler_angles_rad = etee.get_euler(device) + + # Get euler angles + if euler_angles_rad is not None: + euler_angles_deg = [euler_angles_rad[0] * rad2deg_factor + 180, + euler_angles_rad[1] * rad2deg_factor + 180, + euler_angles_rad[2] * rad2deg_factor + 180] + else: + euler_angles_deg = [0, 0, 0] + + roll = round(euler_angles_deg[0], 3) + pitch = round(euler_angles_deg[1], 3) + yaw = round(euler_angles_deg[2], 3) + + # Add y to list + y_1.append(roll) + y_2.append(pitch) + y_3.append(yaw) + + # Limit y list to set number of items + y_1 = y_1[-x_len:] + y_2 = y_2[-x_len:] + y_3 = y_3[-x_len:] + + # Update line with new Y values + line_1.set_ydata(y_1) + line_2.set_ydata(y_2) + line_3.set_ydata(y_3) + + return line_1, line_2, line_3, + + +if __name__ == "__main__": + # ------------ Initialise etee ------------ + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + + # Ask user for the hand data to plot + device = select_hand() + + # Turn absolute IMU off + etee.absolute_imu_enabled(False) + + # ------------ Initialise graph ------------ + # Parameters + x_len = 200 # Number of points to display + y_range = [0, 360] # Range of possible Y values to display + + # Create figure for plotting + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + xs = list(range(0, 200)) + ys_1 = [0] * x_len + ys_2 = [0] * x_len + ys_3 = [0] * x_len + + ax.set_ylim(y_range) + + # Create a blank line. We will update the line in animate + line_1, = ax.plot(xs, ys_1) + line_2, = ax.plot(xs, ys_2) + line_3, = ax.plot(xs, ys_3) + + # Add labels + plt.title('Live Plot of Euler angles') + plt.xlabel('Samples') + plt.ylabel('Euler angles (degrees)') + plt.legend([line_1, line_2, line_3], ['Roll', 'Pitch', 'Yaw']) + + # ------------ Plot live values ------------ + # Set up plot to call animate() function periodically + ani = animation.FuncAnimation(fig, animate, fargs=(ys_1, ys_2, ys_3,), interval=50, blit=True) + plt.show() diff --git a/examples/03_Plot_Data/plot_etee_quaternions.py b/examples/03_Plot_Data/plot_etee_quaternions.py new file mode 100644 index 0000000..192f27c --- /dev/null +++ b/examples/03_Plot_Data/plot_etee_quaternions.py @@ -0,0 +1,108 @@ +""" +Example code: +------------- +This script starts a liveplot of the selected eteeController's quaternion values. +""" + +import matplotlib.pyplot as plt +import matplotlib.animation as animation +import sys +import time +from etee import EteeController + + +# Ask user to select between left or right controller +def select_hand(): + # Prompt for user to select the hand to print values from + print("Please, enter what controller hand you would like to print the values from. Valid options: right, left.") + valid_controllers = ["right", "left"] + controller_selected = input("--> Enter controller hand: ") + while controller_selected not in valid_controllers: + print("Input not valid! Please enter a valid input: right, left.") + controller_selected = input("--> Enter controller hand: ") + print("Your selected controller hand: ", controller_selected) + return controller_selected + + +# This function is called periodically from FuncAnimation +def animate(i, y_1, y_2, y_3, y_4): + # Retrieve euler angles + quaternions = etee.get_quaternion(device) + + # Get euler angles + if quaternions is None: + quaternions = [0, 0, 0, 0] + + # Add y to list + y_1.append(quaternions[0]) + y_2.append(quaternions[1]) + y_3.append(quaternions[2]) + y_4.append(quaternions[3]) + + # Limit y list to set number of items + y_1 = y_1[-x_len:] + y_2 = y_2[-x_len:] + y_3 = y_3[-x_len:] + y_4 = y_4[-x_len:] + + # Update line with new Y values + line_1.set_ydata(y_1) + line_2.set_ydata(y_2) + line_3.set_ydata(y_3) + line_4.set_ydata(y_4) + + return line_1, line_2, line_3, line_4 + + +if __name__ == "__main__": + # ------------ Initialise etee ------------ + # Initialise the etee driver and find dongle + etee = EteeController() + num_dongles_available = etee.get_number_available_etee_ports() + if num_dongles_available > 0: + etee.connect() # Attempt connection to etee dongle + time.sleep(1) + etee.start_data() # Attempt to send a command to etee controllers to start data stream + etee.run() # Start data loop + else: + print("No dongle found. Please, insert an etee dongle and re-run the application.") + sys.exit("Exiting application...") + + # Ask user for the hand data to plot + device = select_hand() + + # Turn absolute IMU off + etee.absolute_imu_enabled(False) + + # ------------ Initialise graph ------------ + # Parameters + x_len = 200 # Number of points to display + y_range = [-1, 1] # Range of possible Y values to display + + # Create figure for plotting + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + xs = list(range(0, 200)) + ys_1 = [0] * x_len + ys_2 = [0] * x_len + ys_3 = [0] * x_len + ys_4 = [0] * x_len + + ax.set_ylim(y_range) + + # Create a blank line. We will update the line in animate + line_1, = ax.plot(xs, ys_1) + line_2, = ax.plot(xs, ys_2) + line_3, = ax.plot(xs, ys_3) + line_4, = ax.plot(xs, ys_4) + + # Add labels + plt.title('Live Plot of Quaternion Values') + plt.xlabel('Samples') + plt.ylabel('Quaternion Values') + plt.legend([line_1, line_2, line_3, line_4], ['q0', 'q1', 'q2', 'q3']) + + # ------------ Plot live values ------------ + # Set up plot to call animate() function periodically + ani = animation.FuncAnimation(fig, animate, fargs=(ys_1, ys_2, ys_3, ys_4), interval=50, blit=True) + plt.show() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4b8cf7a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +numpy>=1.22.3 +bitstring>=3.1.9 +pyserial>=3.5 +PyYAML>=3.12 +keyboard>=0.13.5 +matplotlib>=3.6.2 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..bbbdc8a --- /dev/null +++ b/setup.py @@ -0,0 +1,33 @@ +""" +Script to automate the etee_api package installation. +To use this file, navigate to the directory where setup.py is located and use the command:. + ---------------- + $ pip install . + ---------------- +This will automatically install the package in your environment. +""" + +from setuptools import setup, find_packages +from etee import __version__ + +setup( + name="etee-api", + version=__version__, + python_requires='>=3.8, <4', + packages=find_packages(), + install_requires=[ + 'numpy>=1.22.3', + 'bitstring>=3.1.9', + 'pyserial>=3.5', + 'PyYAML>=3.12', + ], + package_data={'etee': ['config/*.yaml']}, + author="Dimitri Chikhladze, Pilar Zhang Qiu", + author_email="pilar@tg0.co.uk", + description="Official Python API for the eteeController devices. " + "This API enables easy device data reading and communication. " + "To learn more about the controllers, visit: eteexr.com .", + url='https://github.com/eteexr', + license='Apache-2.0', + license_files='LICENSE.txt' +) diff --git a/setup_repo.py b/setup_repo.py new file mode 100644 index 0000000..e91fe35 --- /dev/null +++ b/setup_repo.py @@ -0,0 +1,67 @@ +""" +Script to automate setting up of skeleton repository by setting up a virtual environment and installing requirements. +By default this is a python 3 virtual environment called venv. The script will install any missing dependencies. +""" + +import os +import subprocess +import sys +import platform + + +# Check OS and use proper command +if platform.system() == "Windows": + which_command = "where" +else: + which_command = "which" + +# Check that virtualenv is installed, and if not install it. +print("Looking for virtualenv...") +which_venv = subprocess.call([which_command, "virtualenv"], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) +if which_venv == 1: + print("Virtualenv not installed, installing...") + install_venv = subprocess.run(["pip", "install", "virtualenv"]) + if install_venv.returncode != 0: + print("Virtualenv installation failed, please install manually and then run this script again") + exit(1) + else: + print("Virtualenv installation successful") +elif which_venv == 0: + which_venv = subprocess.check_output([which_command, "virtualenv"]) + print("Virtualenv found at {}".format(which_venv.decode('utf-8').rstrip())) +else: + print("ERROR") + exit(1) + +# If it doesn't exist already, create the virtual environment +print("Looking for venv...") +if not os.path.isdir("venv/"): + print("Not found, creating...") + # System independent way of finding the python executable + # Linux should return /usr/bin/python3 + python_source = "--python={}".format(str(sys.executable)) + create_venv = subprocess.run(["virtualenv", python_source, "venv"]) + if create_venv.returncode != 0: + print("Virtual environment 'venv' creation failed, please create it manually and then run this script again") + exit(1) + else: + print("Virtual environment 'venv' created successfully") +else: + print("Virtual environment 'venv' already exists") + +# Activate the virtual environment (so we're running using the python in venv rather than the global one) +print("Activating virtual environment venv") +if platform.system() == "Windows": + exec(open("venv/Scripts/activate_this.py").read(), dict(__file__="venv/Scripts/activate_this.py")) +else: + # Linux and MacOS (untested) + exec(open("venv/bin/activate_this.py").read(), dict(__file__="venv/bin/activate_this.py")) + +# Install the requirements from the file 'requirements.txt' +install_reqs = subprocess.run(["pip", "install", "-r", "requirements.txt"]) +if install_reqs.returncode != 0: + print("Failed to install some requirements, please install them manually and then run this script again") + exit(1) +else: + print("Requirements successfully installed") + From b4bf9d02c1b1651805b7d0b8273570db2fe705e8 Mon Sep 17 00:00:00 2001 From: Pilar-TG0 Date: Thu, 29 Dec 2022 22:28:22 +0000 Subject: [PATCH 02/10] docs: Update the README.md & Add Images Updated the README.md to include the following sections: - About the Project - Getting Started - Usage - Contributing - License - Authors - Contact Added /img folder and pictures used in docs. --- README.md | 300 +++++++++++++++++++++++++++++++- img/etee-gestures.jpg | Bin 0 -> 108466 bytes img/etee_controller_sensors.jpg | Bin 0 -> 111611 bytes img/logo-dark-mode.png | Bin 0 -> 22339 bytes img/logo-light-mode.png | Bin 0 -> 21374 bytes 5 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 img/etee-gestures.jpg create mode 100644 img/etee_controller_sensors.jpg create mode 100644 img/logo-dark-mode.png create mode 100644 img/logo-light-mode.png diff --git a/README.md b/README.md index 69eeac5..e1774af 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,298 @@ -# etee-Python-API -Official Python API for the eteeController devices. This API enables easy device data reading and communication. To learn more about the controllers, visit: eteexr.com . + + + + + + +
+
+ + Logo + + +

+ Official Python API for the eteeController devices. +
+ Explore the docs » +
+ Report Bug + · + Request Feature +

+
+ + + +
+

+ + Check our Docs! + + Tag for latest stable release + + License +

+ + +

+ + Check our Website! + + Support e-mail + + Follow us in Twitter! +

+ + + +
+ Table of Contents +
    +
  1. + About The Project +
  2. +
  3. + Getting Started + +
  4. +
  5. Usage
  6. +
  7. Contributing + +
  8. +
  9. License
  10. +
  11. Authors
  12. +
  13. Contact
  14. +
+
+ + + +## About the Project + +This API enables easy device data reading and communication. + +In this repository, you can access: +- etee Python API package source code +- etee Python API example scripts +- Setup files + +For more information, please, visit our documentation site: +[Developer Documentation - etee Python API][url-documentation] + + + +## Getting Started + + +### Step 1: Clone the repository + +1. Open a command prompt. +2. Change the current working directory to the location where you want the cloned directory. +3. Type git clone, and then paste the URL for the Github repository. + ```sh + git clone https://github.com/eteeXR/etee-Python-API.git + ``` + + +### Step 2: Package Installation & Environment +This repository comes ready with two different setup files: +- `setup_repo.py` will create a [virtual environment (venv)][url-python-venv] using the dependencies listed in `requirements.txt`. +- `setup.py` will automate the ***etee-api*** package installation. + +> **Note:** You will need the pip package to run the installation. +> If you do not have Python or pip installed, follow the +> [Python guide on package installation][url-python-package-installation]. + + + +#### 2.1. Setting Up a Virtual Environment + +> **If you do not want to run the example scripts**, please skip this step and move to: +> 2.2. Installing the Package. + +After cloning the repository, you will need to install all the project dependencies to run the example code. +Unlike *step 2.2*., these steps will install extra dependencies required only when running the example scripts +(e.g. `keyboard`, `matplotlib`). + +1. Open a command prompt. +2. Navigate to the directory where the `setup_repo.py` is located. +3. Run the script. + ```sh + python setup_repo.py + ``` +4. This should have created a venv inside your project directory. You can activate it at any time by navigating +to the directory containing the /venv folder and entering the following command line: + ```sh + .venv\Scripts\activate.bat + ``` + + +#### 2.2. Installing the Package + +You can automate the installation of the ***etee-api*** package and all its dependencies in +your Python environment, by using the `setup.py` script. + +1. Open a command prompt. +2. If you followed *step 2.1.*, make sure that you activate the venv (see previous section). +3. Navigate to the directory where the `setup.py` is located. +4. Use the pip method to run the installation file. + ```sh + pip install . + ``` +5. To check that the package has been successfully installed, you can run the pip list command. + ```sh + pip list + ``` + If the package was installed, you will see its name and version listed: + ```text + Package Version + --------------- ------- + etee-api 1.0.0 + ``` + +

(back to top)

+ + + +## Usage + +To get started, [set up the hardware][url-documentation-setup-hw]. This involves connecting the device dongle to your +PC or laptop, and turning ON the controllers. You will know they are connected if the device dongle keeps blinking in +the following pattern: pink-pink-blue. + +If you followed the previous steps on installation, you should be able to run any example scripts. + +1. Open a command prompt. +2. In case you followed *step 2.1.*, make sure that you activate the venv. +3. Navigate to the directory where the example script is located. +4. Use the python method to run the example script. + ```sh + python example_script_name.py + ``` + +We also have [quickstart][url-documentation-quickstart] and [more detailed developer guides][url-documentation-api-functions] +in our documentation page. This might help you understand the different API functionalities and how to integrate them +in your custom applications. + +_To learn more about the API and the controller data, visit our [Developer Documentation Site][url-documentation]_ + + + Check our Docs! + + +

(back to top)

+ + + +## Contributing + +### How to Contribute + +Contributions are what make the open source community such an amazing place to learn, inspire, and create. +Any contributions you make are **greatly appreciated**. + +If you have a suggestion that would make this better, please fork the repo and create a pull request. +You can also simply open an issue to describe your suggestion or report a bug. + +1. Fork the Project +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +### Semantic Type Prefixes + +To help us and the community easily identify the nature of your *commit* or *issue*, use **semantic type prefixes** +to precede your message / title. + +Some common type prefixes include: + +- `feat`: A new feature for the user, not a new feature for a build script. +- `fix`: Bug fix for the user, not a fix for a build scripts. +- `enhanc`: Enhancement or improvement to existing feature. +- `perf`: Code improved in terms of processing performance. +- `refactor`: Refactoring production code (e.g. renaming a variable). +- `chore`: Changes to the build process or auxiliary tools and libraries. +- `docs`: Changes to documentation. +- `style`: Formatting, missing semicolons, etc. No code change. +- `vendor`: Update version for dependencies and other packages. +- `test`: Adding missing tests or refactoring tests. No production code change. + +**Format**: `(): `, where < scope > is optional. + +For example, your commit message header might look like this: +```text +feat(imu): Implemented Euler angles estimation from quaternion +``` + +For more references and guides on semantic code messages, see: + +- [How are you writing a commit message?][url-semantic-type-prefixes-1] - by Darkø Tasevski +- [Git Commit Msg][url-semantic-type-prefixes-2] - Karma by Friedel Ziegelmayer + +

(back to top)

+ + + +## License + +Distributed under the Apache 2.0 License. See `LICENSE.txt` for more information. + +

(back to top)

+ + + +## Authors + +This repository was created by the [TG0][url-tg0-website] team, for the [etee][url-eteexr-website] brand. + +Code and documentation authors include: +- **Dimitri Chikhladze** (API development and documentation) +- **Pilar Zhang Qiu** (API development, documentation and release) + +

(back to top)

+ + + +## Contact + +For any queries or reports about the API, please use the [**Issues tab**][url-issues-tab] in this repository. +When possible, use an identifier to help us describe your query, report or request. +See more at: Semantic Type Prefixes + +For further support or queries, you can contact us: +- Support e-mail: [customer@eteexr.com](customer@eteexr.com) +- Support Form: [https://eteexr.com/pages/support-form](https://eteexr.com/pages/support-form) + +

(back to top)

+ + + + +[url-documentation]: https://tg0-etee-python-api.readthedocs-hosted.com/en/latest/index.html# +[url-documentation-setup-hw]: https://tg0-etee-python-api.readthedocs-hosted.com/en/latest/setup.html +[url-documentation-quickstart]: https://tg0-etee-python-api.readthedocs-hosted.com/en/latest/quickstart.html +[url-documentation-api-functions]: https://tg0-etee-python-api.readthedocs-hosted.com/en/latest/guide.html +[url-issues-tab]: https://github.com/eteeXR/etee-Python-API/issues +[url-python-venv]: https://docs.python.org/3/tutorial/venv.html +[url-python-package-installation]: https://packaging.python.org/en/latest/tutorials/installing-packages/ +[url-tg0-website]: https://tg0.co.uk/ +[url-eteexr-website]: https://eteexr.com/ +[url-semantic-type-prefixes-1]: https://dev.to/puritanic/how-are-you-writing-a-commit-message-1ih7 +[url-semantic-type-prefixes-2]: http://karma-runner.github.io/1.0/dev/git-commit-msg.html diff --git a/img/etee-gestures.jpg b/img/etee-gestures.jpg new file mode 100644 index 0000000000000000000000000000000000000000..26f271c031e75998cce31d6af4e1de93ecf77f4d GIT binary patch literal 108466 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 z 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

  • cuPZXJ(XdU`=ToP%^<0w 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 L3Q3jRUqk-~FZT;i literal 0 HcmV?d00001 diff --git a/img/logo-dark-mode.png b/img/logo-dark-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c5dd14048d1c263956678e9094430c2670d250 GIT binary patch literal 22339 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 literal 0 HcmV?d00001 diff --git a/img/logo-light-mode.png b/img/logo-light-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..99e7a98084c310d30ac855c56f3d41a0b520daa2 GIT binary patch literal 21374 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ⅈ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 BZr=a^ literal 0 HcmV?d00001 From 4df5c2e84e9f7cbcebfc228b9cb1376c06e71987 Mon Sep 17 00:00:00 2001 From: Pilar-TG0 Date: Thu, 5 Jan 2023 11:18:23 +0000 Subject: [PATCH 03/10] docs: Logo Changes with Dark/Light Mode Updated the README.md so that the etee Python API logo changes when dark or light mode Github is detected. --- README.md | 30 ++++++++++++++---------------- img/logo-banner-dark-mode.png | Bin 0 -> 17517 bytes img/logo-banner-light-mode.png | Bin 0 -> 18689 bytes 3 files changed, 14 insertions(+), 16 deletions(-) create mode 100644 img/logo-banner-dark-mode.png create mode 100644 img/logo-banner-light-mode.png diff --git a/README.md b/README.md index e1774af..0afbed3 100644 --- a/README.md +++ b/README.md @@ -8,21 +8,19 @@
    -
    + +![Logo Dark](./img/logo-banner-dark-mode.png#gh-dark-mode-only) +![Logo Light](./img/logo-banner-light-mode.png#gh-light-mode-only) + +

    + Official Python API for the eteeController devices. +
    + Explore the docs » +
    + Report Bug + · + Request Feature +

    @@ -264,7 +262,7 @@ This repository was created by the [TG0][url-tg0-website] team, for the [etee][u Code and documentation authors include: - **Dimitri Chikhladze** (API development and documentation) -- **Pilar Zhang Qiu** (API development, documentation and release) +- **Pilar Zhang Qiu** (API development, documentation and releases)

    (back to top)

    diff --git a/img/logo-banner-dark-mode.png b/img/logo-banner-dark-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..401a848694ba0ef8841eafbd8659ff21b63bbc00 GIT binary patch literal 17517 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 z zJ}4JyiA_Dxo_pydC-^=IK?&ze3N_5>oT+kYU3JD2vk1g#Lh%0!zH=VkjT*842s1x&i;;b?IJfI;a*kDUdHW#zOwXtSkk9#X9^cIGJVNSQ zwhe(7O9(IcJ-fI?-l(e4*suR)I#;fp;P4>axRNudOC zjKOE5@z6sSZcMYeb=%8*(UdhT8eJXOOi5x9c{|J#$auwS6tjye&>V*&z|iM`Vedt`RnDrM_jguRp-$f@tGp)X?fQ*OQoi3`WOfY6fsP_=v=7=ZM41Z z^O!Cz3|BV`vU8zF@Z>8S`{?DK^1gy@D*(By#pjjF;MX|Em+q6|T#Bs=Kwik6aZ8#; z40y=QOB^lF2fTb@V?T%6+Ck5{?v7>;&hFn#YVILRYYTbE8K7aJZ@FmNeBmr~6i{pj z@4TGB&mk$WTcT9LSqXE{$GQ~oIb7y>$E$1{iORP8cME+GB!u`AQ!d~~MsJTZ#oRB= zog4a;8*vA|Tb0o}Qsf+ob+1^HU|Jq_2l;{c1i#X~lxptBr`Osu@oEBWMZHsmF1P|x zw8M9S*Tm_6)PW+{&}|G9%BbVn$T-_#yXVLLl?$qch|q8qyMj#p=9$Fa;OI9YMGJk# zi}+MILk2<ul@j)Nu&_Ay$A vX#hSWE}e!dNaz`H8@>(se<5h%O<6xOjLX?PZLS#wDwMygd?)v|k?;QkQaJX+ literal 0 HcmV?d00001 diff --git a/img/logo-banner-light-mode.png b/img/logo-banner-light-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..e2708cd4727b059e9c186c414f75aa8d6fe0eb9d GIT binary patch literal 18689 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 literal 0 HcmV?d00001 From f9d4391131cb1afe7d7273c354dfc94f1ff21ef9 Mon Sep 17 00:00:00 2001 From: Pilar-TG0 Date: Thu, 5 Jan 2023 11:38:35 +0000 Subject: [PATCH 04/10] docs: Add Code of Conduct Code of Conduct adapted from the Contributor Covenant. --- CODE_OF_CONDUCT.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..adbade6 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [customer@eteexr.com](customer@eteexr.com). All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org From 1dbd702105da52e234bbc21c7599fbf8d846b118 Mon Sep 17 00:00:00 2001 From: Pilar-TG0 Date: Thu, 5 Jan 2023 13:56:09 +0000 Subject: [PATCH 05/10] docs: Update README.md Organised and added images to the "Usage" section. --- README.md | 37 +++++++++++++++++- ...ensors.jpg => etee-controller-sensors.jpg} | Bin 2 files changed, 36 insertions(+), 1 deletion(-) rename img/{etee_controller_sensors.jpg => etee-controller-sensors.jpg} (100%) diff --git a/README.md b/README.md index 0afbed3..925ebdd 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,14 @@
    - - Logo - - -

    - Official Python API for the eteeController devices. -
    - Explore the docs » -
    - Report Bug - · - Request Feature -

    -
  • Step 2: Package Installation & Environment
  • -
  • Usage
  • +
  • + Usage + +
  • Contributing