Improve tracking significantly
This commit is contained in:
		
							parent
							
								
									2544de6857
								
							
						
					
					
						commit
						d7f71a41fb
					
				
					 3 changed files with 82 additions and 65 deletions
				
			
		
							
								
								
									
										3
									
								
								dance.py
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								dance.py
									
										
									
									
									
								
							|  | @ -165,9 +165,6 @@ async def connect_joycon(app, ws, data): | |||
|         host_ip_addr=host_ip_addr, | ||||
|         console_ip_addr=console_ip_addr, | ||||
|         on_state_changed=on_joydance_state_changed, | ||||
|         accel_acquisition_freq_hz=config['accel_acquisition_freq_hz'], | ||||
|         accel_acquisition_latency=config['accel_acquisition_latency'], | ||||
|         accel_max_range=config['accel_max_range'], | ||||
|     ) | ||||
|     app['joydance_connections'][serial] = joydance | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import random | |||
| import socket | ||||
| import ssl | ||||
| import time | ||||
| import traceback | ||||
| from enum import Enum | ||||
| from urllib.parse import urlparse | ||||
| 
 | ||||
|  | @ -11,10 +12,9 @@ import aiohttp | |||
| import websockets | ||||
| 
 | ||||
| from .constants import (ACCEL_ACQUISITION_FREQ_HZ, ACCEL_ACQUISITION_LATENCY, | ||||
|                         ACCEL_MAX_RANGE, ACCEL_SEND_RATE, JOYCON_UPDATE_RATE, | ||||
|                         SHORTCUT_MAPPING, UBI_APP_ID, UBI_SKU_ID, | ||||
|                         WS_SUBPROTOCOLS, Command, JoyConButton, | ||||
|                         WsSubprotocolVersion) | ||||
|                         ACCEL_MAX_RANGE, FRAME_DURATION, SHORTCUT_MAPPING, | ||||
|                         UBI_APP_ID, UBI_SKU_ID, WS_SUBPROTOCOLS, Command, | ||||
|                         JoyConButton, WsSubprotocolVersion) | ||||
| 
 | ||||
| 
 | ||||
| class PairingState(Enum): | ||||
|  | @ -68,6 +68,8 @@ class JoyDance: | |||
|         self.is_input_allowed = False | ||||
|         self.available_shortcuts = set() | ||||
| 
 | ||||
|         self.accel_data = [] | ||||
| 
 | ||||
|         self.ws = None | ||||
|         self.disconnected = False | ||||
| 
 | ||||
|  | @ -239,52 +241,81 @@ class JoyDance: | |||
|         async for message in self.ws: | ||||
|             await self.on_message(message) | ||||
| 
 | ||||
|     async def send_accelerometer_data(self): | ||||
|         accel_data = [] | ||||
|         delta_time = 0 | ||||
| 
 | ||||
|         end = time.time() | ||||
|     async def tick(self): | ||||
|         sleep_duration = FRAME_DURATION * 0.75 | ||||
|         last_time = time.time() | ||||
|         frames = 0 | ||||
| 
 | ||||
|         while True: | ||||
|             if self.disconnected: | ||||
|                 break | ||||
| 
 | ||||
|             # Make sure it runs at exactly 60 FPS | ||||
|             while True: | ||||
|                 time_now = time.time() | ||||
|                 dt = time_now - last_time | ||||
|                 if dt >= FRAME_DURATION: | ||||
|                     break | ||||
|             last_time = time_now | ||||
|             frames = frames + 1 if frames < 5 else 1 | ||||
| 
 | ||||
|             if not self.should_start_accelerometer: | ||||
|                 await asyncio.sleep(0.5) | ||||
|                 await asyncio.sleep(sleep_duration), | ||||
|                 continue | ||||
| 
 | ||||
|             start = time.time() | ||||
|             if delta_time > ACCEL_SEND_RATE: | ||||
|                 delta_time = 0 | ||||
|                 while len(accel_data) > 0: | ||||
|                     accels_num = min(len(accel_data), 10) | ||||
|             await asyncio.gather( | ||||
|                 asyncio.sleep(sleep_duration), | ||||
|                 self.collect_accelerometer_data(), | ||||
|                 self.send_accelerometer_data(frames), | ||||
|             ) | ||||
| 
 | ||||
|                     await self.send_message('JD_PhoneScoringData', { | ||||
|                         'accelData': accel_data[:accels_num], | ||||
|                         'timeStamp': self.number_of_accels_sent, | ||||
|                     }) | ||||
|     async def collect_accelerometer_data(self): | ||||
|         if self.disconnected: | ||||
|             return | ||||
| 
 | ||||
|                     self.number_of_accels_sent += accels_num | ||||
|                     accel_data = accel_data[accels_num:] | ||||
|         if not self.should_start_accelerometer: | ||||
|             self.accel_data = [] | ||||
|             return | ||||
| 
 | ||||
|         try: | ||||
|                 await asyncio.sleep(JOYCON_UPDATE_RATE) | ||||
|                 joycon_status = self.joycon.get_status() | ||||
|             except OSError: | ||||
|                 self.disconnect() | ||||
|                 return | ||||
|             accel = { | ||||
|                 'x': self.joycon.get_accel_x(), | ||||
|                 'y': self.joycon.get_accel_y(), | ||||
|                 'z': self.joycon.get_accel_z(), | ||||
|             } | ||||
| 
 | ||||
|             # Accelerator axes on phone & Joy-Con are different so we need to swap some axes | ||||
|             # https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/imu_sensor_notes.md | ||||
|             accel = joycon_status['accel'] | ||||
|             x = accel['y'] * -1 | ||||
|             y = accel['x'] | ||||
|             z = accel['z'] | ||||
| 
 | ||||
|             accel_data.append([x, y, z]) | ||||
|             self.accel_data.append([x, y, z]) | ||||
|         except OSError: | ||||
|             self.disconnect() | ||||
|             return | ||||
| 
 | ||||
|             end = time.time() | ||||
|             delta_time += (end - start) * 1000 | ||||
|     async def send_accelerometer_data(self, frames): | ||||
|         if not self.should_start_accelerometer: | ||||
|             return | ||||
| 
 | ||||
|         if frames < 5: | ||||
|             return | ||||
| 
 | ||||
|         tmp_accel_data = [] | ||||
|         while len(self.accel_data): | ||||
|             tmp_accel_data.append(self.accel_data.pop(0)) | ||||
| 
 | ||||
|         while len(tmp_accel_data) > 0: | ||||
|             accels_num = min(len(tmp_accel_data), 10) | ||||
| 
 | ||||
|             await self.send_message('JD_PhoneScoringData', { | ||||
|                 'accelData': tmp_accel_data[:accels_num], | ||||
|                 'timeStamp': self.number_of_accels_sent, | ||||
|             }) | ||||
| 
 | ||||
|             self.number_of_accels_sent += accels_num | ||||
|             tmp_accel_data = tmp_accel_data[accels_num:] | ||||
| 
 | ||||
|     async def send_command(self): | ||||
|         ''' Capture Joycon's input and send to console. Only works on protocol v2 ''' | ||||
|  | @ -292,16 +323,14 @@ class JoyDance: | |||
|             return | ||||
| 
 | ||||
|         while True: | ||||
|             try: | ||||
|             if self.disconnected: | ||||
|                 return | ||||
| 
 | ||||
|             try: | ||||
|                 await asyncio.sleep(FRAME_DURATION) | ||||
|                 if not self.is_input_allowed and not self.should_start_accelerometer: | ||||
|                     await asyncio.sleep(JOYCON_UPDATE_RATE * 5) | ||||
|                     continue | ||||
| 
 | ||||
|                 await asyncio.sleep(JOYCON_UPDATE_RATE * 5) | ||||
| 
 | ||||
|                 cmd = None | ||||
|                 # Get pressed button | ||||
|                 for event_type, status in self.joycon.events(): | ||||
|  | @ -309,7 +338,7 @@ class JoyDance: | |||
|                         continue | ||||
| 
 | ||||
|                     joycon_button = JoyConButton(event_type) | ||||
|                     if self.should_start_accelerometer:  # Can only send Pause command while playing | ||||
|                     if self.should_start_accelerometer:  # Only allow to send Pause command while playing | ||||
|                         if joycon_button == JoyConButton.PLUS or joycon_button == JoyConButton.MINUS: | ||||
|                             cmd = Command.PAUSE | ||||
|                     else: | ||||
|  | @ -318,7 +347,7 @@ class JoyDance: | |||
|                         elif joycon_button == JoyConButton.B or joycon_button == JoyConButton.DOWN: | ||||
|                             cmd = Command.BACK | ||||
|                         elif joycon_button in SHORTCUT_MAPPING: | ||||
|                             # Get command depends on which button was pressed & which shortcuts are available | ||||
|                             # Get command depends on which button is being pressed & which shortcuts are available | ||||
|                             for shortcut in SHORTCUT_MAPPING[joycon_button]: | ||||
|                                 if shortcut in self.available_shortcuts: | ||||
|                                     cmd = shortcut | ||||
|  | @ -344,27 +373,21 @@ class JoyDance: | |||
| 
 | ||||
|                 # Send command to server | ||||
|                 if cmd: | ||||
|                     data = {} | ||||
|                     if cmd == Command.PAUSE: | ||||
|                         __class = 'JD_Pause_PhoneCommandData' | ||||
|                         data = {} | ||||
|                     elif type(cmd.value) == str: | ||||
|                         __class = 'JD_Custom_PhoneCommandData' | ||||
|                         data = { | ||||
|                             'identifier': cmd.value, | ||||
|                         } | ||||
|                         data['identifier'] = cmd.value | ||||
|                     else: | ||||
|                         __class = 'JD_Input_PhoneCommandData' | ||||
|                         data = { | ||||
|                             'input': cmd.value, | ||||
|                         } | ||||
|                         data['input'] = cmd.value | ||||
| 
 | ||||
|                     # Only send input when it's allowed to, otherwise we might get a disconnection | ||||
|                     if self.is_input_allowed: | ||||
|                         await self.send_message(__class, data) | ||||
|                         await asyncio.sleep(0.01) | ||||
|             except Exception as e: | ||||
|                 print(e) | ||||
|                 import traceback | ||||
|                         await asyncio.sleep(FRAME_DURATION * 5) | ||||
|             except Exception: | ||||
|                 traceback.print_exc() | ||||
|                 await self.disconnect() | ||||
| 
 | ||||
|  | @ -403,17 +426,18 @@ class JoyDance: | |||
|             ) as websocket: | ||||
|                 try: | ||||
|                     self.ws = websocket | ||||
| 
 | ||||
|                     await asyncio.gather( | ||||
|                         self.send_hello(), | ||||
|                         self.send_accelerometer_data(), | ||||
|                         self.tick(), | ||||
|                         self.send_command(), | ||||
|                     ) | ||||
| 
 | ||||
|                 except websockets.ConnectionClosed: | ||||
|                     await self.on_state_changed(self.joycon.serial, PairingState.ERROR_CONSOLE_CONNECTION) | ||||
|                     await self.disconnect(close_ws=False) | ||||
|         except Exception as e: | ||||
|             print(e) | ||||
|         except Exception: | ||||
|             traceback.print_exc() | ||||
|             await self.on_state_changed(self.joycon.serial, PairingState.ERROR_CONSOLE_CONNECTION) | ||||
|             await self.disconnect(close_ws=False) | ||||
| 
 | ||||
|  | @ -449,6 +473,6 @@ class JoyDance: | |||
|                     await self.hole_punching() | ||||
| 
 | ||||
|             await self.connect_ws() | ||||
|         except Exception as e: | ||||
|         except Exception: | ||||
|             await self.disconnect() | ||||
|             print(e) | ||||
|             traceback.print_exc() | ||||
|  |  | |||
|  | @ -14,10 +14,9 @@ WS_SUBPROTOCOLS = {} | |||
| WS_SUBPROTOCOLS[WsSubprotocolVersion.V1.value] = 'v1.phonescoring.jd.ubisoft.com' | ||||
| WS_SUBPROTOCOLS[WsSubprotocolVersion.V2.value] = 'v2.phonescoring.jd.ubisoft.com' | ||||
| 
 | ||||
| JOYCON_UPDATE_RATE = 0.02  # 50Hz | ||||
| ACCEL_SEND_RATE = 40  # ms | ||||
| ACCEL_ACQUISITION_FREQ_HZ = 50  # Hz | ||||
| ACCEL_ACQUISITION_LATENCY = 20  # ms | ||||
| FRAME_DURATION = 1 / 60 | ||||
| ACCEL_ACQUISITION_FREQ_HZ = 60  # Hz | ||||
| ACCEL_ACQUISITION_LATENCY = 40  # ms | ||||
| ACCEL_MAX_RANGE = 8  # ±G | ||||
| 
 | ||||
| DEFAULT_CONFIG = { | ||||
|  | @ -25,9 +24,6 @@ DEFAULT_CONFIG = { | |||
|     'host_ip_addr': '', | ||||
|     'console_ip_addr': '', | ||||
|     'pairing_code': '', | ||||
|     'accel_acquisition_freq_hz': ACCEL_ACQUISITION_FREQ_HZ, | ||||
|     'accel_acquisition_latency': ACCEL_ACQUISITION_LATENCY, | ||||
|     'accel_max_range': ACCEL_MAX_RANGE, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue