Source code for pyheadtracker.out

"""Module for sending head tracking data to various target applications.

Modules:
- `IEMSceneRotator`: Class to send data to IEM Scene Rotator via OSC.
"""

from pythonosc.udp_client import SimpleUDPClient
from abc import abstractmethod
import warnings
from .dtypes import YPR, Quaternion, Position
from .utils import ypr2quat, quat2ypr


[docs] class OutBase: """Base class for outputting data to a target application. Methods ------- send_orientation(orientation: YPR | Quaternion) Send orientation data to the desired target application. send_position(position: Position) Send position data to the desired target application. """
[docs] @abstractmethod def send_orientation(self, orientation: YPR | Quaternion): """Send orientation data to the desired target application. Parameters ---------- orientation : YPR | Quaternion The orientation data to send. """ warnings.warn("send_orientation method not implemented.", UserWarning) pass
[docs] @abstractmethod def send_position(self, position: Position): """Send position data to the desired target application. Parameters ---------- position : Position The position data to send. """ warnings.warn("send_position method not implemented.", UserWarning) pass
[docs] class IEMSceneRotator(OutBase): """Class for sending head tracking data to IEM SceneRotator via OSC. This class is used to transmit data via OSC to the IEM SceneRotator of the IEM Plug-In Suite [1]. It supports sending orientation data in both YPR (Yaw, Pitch, Roll) and Quaternion formats. If YPR data is provided, it is automatically converted to Quaternion format before transmission. Attributes ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 8000. OSC_address : str The OSC address prefix for sending messages. Default is "/SceneRotator/". Methods ------- send_orientation(orientation: YPR | Quaternion | None) Send orientation data to the IEM SceneRotator. References ---------- [1] https://plugins.iem.at """
[docs] def __init__( self, ip: str = "127.0.0.1", port: int = 8000, OSC_address: str = "/SceneRotator/", ): """ Parameters ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 8000. OSC_address : str The OSC address prefix for sending messages. Default is "/SceneRotator/". """ self.ip = ip self.port = port self.OSC_address = OSC_address self.client = SimpleUDPClient(self.ip, self.port)
[docs] def send_orientation(self, orientation: YPR | Quaternion | None): """Send orientation data to the IEM SceneRotator. Parameters ---------- orientation : YPR | Quaternion The orientation data to send. """ if isinstance(orientation, YPR): orientation = ypr2quat(orientation) if isinstance(orientation, Quaternion): w, x, y, z = orientation.inverse() self.client.send_message(self.OSC_address + "quaternions", [w, x, y, z]) else: return
[docs] class IEMStereoEncoder(OutBase): """Class for sending head tracking data to IEM StereoEncoder via OSC. This class is used to transmit data via OSC to the IEM StereoEncoder of the IEM Plug-In Suite [1]. It supports sending orientation data in both YPR (Yaw, Pitch, Roll) and Quaternion formats. If Quaternion data is provided, it is automatically converted to YPR format before transmission. Attributes ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 8000. OSC_address : str The OSC address prefix for sending messages. Default is "/DirectivityShaper/". offset_az : float Offset in degrees to be added to the azimuth (yaw) angle. Default is 0.0. offset_el : float Offset in degrees to be added to the elevation (pitch) angle. Default is 0.0. offset_roll : float Offset in degrees to be added to the roll angle. Default is 0.0. invert_az : bool If True, invert the azimuth (yaw) angle. Default is False. invert_el : bool If True, invert the elevation (pitch) angle. Default is False. invert_roll : bool If True, invert the roll angle. Default is False. Methods ------- send_orientation(orientation: YPR | Quaternion | None) Send orientation data to the IEM StereoEncoder. References ---------- [1] https://plugins.iem.at """
[docs] def __init__( self, ip: str = "127.0.0.1", port: int = 8000, OSC_address: str = "/StereoEncoder/", offset_az: float = 0.0, offset_el: float = 0.0, offset_roll: float = 0.0, invert_az: bool = False, invert_el: bool = False, invert_roll: bool = False, ): """ Parameters ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 8000. OSC_address : str The OSC address prefix for sending messages. Default is "/StereoEncoder/". offset_az : float Offset in degrees to be added to the azimuth (yaw) angle. Default is 0.0. offset_el : float Offset in degrees to be added to the elevation (pitch) angle. Default is 0.0. offset_roll : float Offset in degrees to be added to the roll angle. Default is 0.0. invert_az : bool If True, invert the azimuth (yaw) angle. Default is False. invert_el : bool If True, invert the elevation (pitch) angle. Default is False. invert_roll : bool If True, invert the roll angle. Default is False. """ self.ip = ip self.port = port self.OSC_address = OSC_address self.client = SimpleUDPClient(self.ip, self.port) self.offset_az = offset_az self.offset_el = offset_el self.offset_roll = offset_roll self.invert_az = invert_az self.invert_el = invert_el self.invert_roll = invert_roll # Engage probe lock self.client.send_message(self.OSC_address + "probeLock", 1.0)
[docs] def send_orientation(self, orientation: YPR | Quaternion | None): """Send orientation data to the IEM StereoEncoder. Parameters ---------- orientation : YPR | Quaternion The orientation data to send. """ if not isinstance(orientation, (YPR, Quaternion)): return if isinstance(orientation, Quaternion): orientation = quat2ypr(orientation) y, p, r = orientation.to_degrees() y += self.offset_az p += self.offset_el r += self.offset_roll if self.invert_az: y = -y if self.invert_el: p = -p if self.invert_roll: r = -r self.client.send_message(self.OSC_address + "azimuth", y) self.client.send_message(self.OSC_address + "elevation", -p) self.client.send_message(self.OSC_address + "roll", r)
[docs] class IEMDirectivityShaper(OutBase): """Class for sending head tracking data to IEM DirectivityShaper via OSC. This class is used to transmit data via OSC to the IEM DirectivityShaper of the IEM Plug-In Suite [1]. It supports sending orientation data in both YPR (Yaw, Pitch, Roll) and Quaternion formats. If Quaternion data is provided, it is automatically converted to YPR format before transmission. Attributes ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 8000. OSC_address : str The OSC address prefix for sending messages. Default is "/DirectivityShaper/". offset_az : float Offset in degrees to be added to the azimuth (yaw) angle. Default is 0.0. offset_el : float Offset in degrees to be added to the elevation (pitch) angle. Default is 0.0. offset_roll : float Offset in degrees to be added to the roll angle. Default is 0.0. invert_az : bool If True, invert the azimuth (yaw) angle. Default is False. invert_el : bool If True, invert the elevation (pitch) angle. Default is False. invert_roll : bool If True, invert the roll angle. Default is False. Methods ------- send_orientation(orientation: YPR | Quaternion | None) Send orientation data to the IEM DirectivityShaper. References ---------- [1] https://plugins.iem.at """
[docs] def __init__( self, ip: str = "127.0.0.1", port: int = 8000, OSC_address: str = "/DirectivityShaper/", offset_az: float = 0.0, offset_el: float = 0.0, offset_roll: float = 0.0, invert_az: bool = False, invert_el: bool = False, invert_roll: bool = False, ): """ Parameters ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 8000. OSC_address : str The OSC address prefix for sending messages. Default is "/DirectivityShaper/". offset_az : float Offset in degrees to be added to the azimuth (yaw) angle. Default is 0.0. offset_el : float Offset in degrees to be added to the elevation (pitch) angle. Default is 0.0. offset_roll : float Offset in degrees to be added to the roll angle. Default is 0.0. invert_az : bool If True, invert the azimuth (yaw) angle. Default is False. invert_el : bool If True, invert the elevation (pitch) angle. Default is False. invert_roll : bool If True, invert the roll angle. Default is False. """ self.ip = ip self.port = port self.OSC_address = OSC_address self.client = SimpleUDPClient(self.ip, self.port) self.offset_az = offset_az self.offset_el = offset_el self.offset_roll = offset_roll self.invert_az = invert_az self.invert_el = invert_el self.invert_roll = invert_roll # Engage probe lock self.client.send_message(self.OSC_address + "probeLock", 1.0)
[docs] def send_orientation(self, orientation: YPR | Quaternion | None): """Send orientation data to the IEM DirectivityShaper. Parameters ---------- orientation : YPR | Quaternion The orientation data to send. """ if not isinstance(orientation, (YPR, Quaternion)): return if isinstance(orientation, Quaternion): orientation = quat2ypr(orientation) y, p, r = orientation.to_degrees() y += self.offset_az p += self.offset_el r += self.offset_roll if self.invert_az: y = -y if self.invert_el: p = -p if self.invert_roll: r = -r self.client.send_message(self.OSC_address + "probeAzimuth", y) self.client.send_message(self.OSC_address + "probeElevation", -p) self.client.send_message(self.OSC_address + "probeRoll", r)
[docs] class IEMRoomEncoder(OutBase): """Class for sending head tracking data to IEM RoomEncoder via OSC. This class is used to transmit position via OSC to the IEM RoomEncoder of the IEM Plug-In Suite [1]. Attributes ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 8000. OSC_address : str The OSC address prefix for sending messages. Default is "/RoomEncoder/listener" or "/RoomEncoder/source". offset_x : float Offset to be added to the x position. Default is 0.0. offset_y : float Offset to be added to the y position. Default is 0.0. offset_z : float Offset to be added to the z position. Default is 0.0. Methods ------- send_position(position: Position) Send position data to the IEM RoomEncoder. References ---------- [1] https://plugins.iem.at/docs/plugindescriptions/#roomencoder """
[docs] def __init__( self, ip: str = "127.0.0.1", port: int = 8000, OSC_address: str = "/RoomEncoder/", mode: str = "listener", offset_x: float = 0.0, offset_y: float = 0.0, offset_z: float = 0.0, ): """ Parameters ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 8000. OSC_address : str The OSC address prefix for sending messages. Default is "/RoomEncoder/". mode: str Setting to control either the listener or source position. Possible values are "listener" or "source". Default is "listener". offset_x : float Offset to be added to the x position. Default is 0.0. offset_y : float Offset to be added to the y position. Default is 0.0. offset_z : float Offset to be added to the z position. Default is 0.0. """ self.ip = ip self.port = port self.OSC_address = OSC_address self.client = SimpleUDPClient(self.ip, self.port) if mode.lower() in ["listener", "source"]: self.OSC_address += mode.lower() else: raise ValueError('mode must be either "listener" or "source"') self.offset_x = offset_x self.offset_y = offset_y self.offset_z = offset_z
[docs] def send_position(self, position: Position): """Send position data to the IEM RoomEncoder. Parameters ---------- position : Position The position data to send. """ if not isinstance(position, Position): return x, y, z = position x += self.offset_x y += self.offset_y z += self.offset_z self.client.send_message(self.OSC_address + "X", x) self.client.send_message(self.OSC_address + "Y", y) self.client.send_message(self.OSC_address + "Z", z)
[docs] class SPARTA(OutBase): """Class for sending head tracking data to SPARTA Plug-Ins via OSC. This class is used to transmit data via OSC to the SPARTA applications [1]. It supports sending orientation data in both YPR (Yaw, Pitch, Roll) and Quaternion formats. If Quaternion data is provided, it is automatically converted to YPR format before transmission. Attributes ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 9000. offset_az : float Offset in degrees to be added to the azimuth (yaw) angle. Default is 0.0. offset_el : float Offset in degrees to be added to the elevation (pitch) angle. Default is 0.0. offset_roll : float Offset in degrees to be added to the roll angle. Default is 0.0. invert_az : bool If True, invert the azimuth (yaw) angle. Default is False. invert_el : bool If True, invert the elevation (pitch) angle. Default is False. invert_roll : bool If True, invert the roll angle. Default is False. Methods ------- send_orientation(orientation: YPR | Quaternion | None) Send orientation data to SPARTA Plug-Ins. References ---------- [1] https://leomccormack.github.io/sparta-site/ """
[docs] def __init__( self, ip: str = "127.0.0.1", port: int = 9000, offset_az: float = 0.0, offset_el: float = 0.0, offset_roll: float = 0.0, invert_az: bool = False, invert_el: bool = False, invert_roll: bool = False, ): """ Parameters ---------- ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 9000. offset_az : float Offset in degrees to be added to the azimuth (yaw) angle. Default is 0.0. offset_el : float Offset in degrees to be added to the elevation (pitch) angle. Default is 0.0. offset_roll : float Offset in degrees to be added to the roll angle. Default is 0.0. invert_az : bool If True, invert the azimuth (yaw) angle. Default is False. invert_el : bool If True, invert the elevation (pitch) angle. Default is False. invert_roll : bool If True, invert the roll angle. Default is False. """ self.ip = ip self.port = port self.client = SimpleUDPClient(self.ip, self.port) self.offset_az = offset_az self.offset_el = offset_el self.offset_roll = offset_roll self.invert_az = invert_az self.invert_el = invert_el self.invert_roll = invert_roll
[docs] def send_orientation(self, orientation: YPR | Quaternion | None): """Send orientation data to SPARTA Plug-Ins. Parameters ---------- orientation : YPR | Quaternion The orientation data to send. """ if not isinstance(orientation, (YPR, Quaternion)): return if isinstance(orientation, Quaternion): orientation = quat2ypr(orientation) y, p, r = orientation.to_degrees() y += self.offset_az p += self.offset_el r += self.offset_roll if self.invert_az: y = -y if self.invert_el: p = -p if self.invert_roll: r = -r self.client.send_message("/ypr", [y, p, r])
[docs] class TASCAR(OutBase): """Class for sending head tracking data to TASCAR. This class is used to transmit data via OSC to TASCAR [1]. It supports sending orientation data in both YPR (Yaw, Pitch, Roll) and Quaternion formats. If Quaternion data is provided, it is automatically converted to YPR format before transmission. Attributes ---------- OSC_address : str The OSC address prefix for sending messages, without tailing pos or zyxeuler. ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 9000. offset_az : float Offset in degrees to be added to the azimuth (yaw) angle. Default is 0.0. offset_el : float Offset in degrees to be added to the elevation (pitch) angle. Default is 0.0. offset_roll : float Offset in degrees to be added to the roll angle. Default is 0.0. invert_az : bool If True, invert the azimuth (yaw) angle. Default is False. invert_el : bool If True, invert the elevation (pitch) angle. Default is False. invert_roll : bool If True, invert the roll angle. Default is False. Methods ------- send_orientation(orientation: YPR | Quaternion | None) Send orientation data to TASCAR. send_position(position: Position) Send position data to TASCAR. References ---------- [1] https://tascar.org/ """
[docs] def __init__( self, OSC_address: str, ip: str = "127.0.0.1", port: int = 9000, offset_az: float = 0.0, offset_el: float = 0.0, offset_roll: float = 0.0, invert_az: bool = False, invert_el: bool = False, invert_roll: bool = False, ): """ Parameters ---------- OSC_address : str The OSC address prefix for sending messages, without tailing pos or zyxeuler. ip : str The IP address of the target application. Default is "127.0.0.1". port : int The port number of the target application. Default is 9000. offset_az : float Offset in degrees to be added to the azimuth (yaw) angle. Default is 0.0. offset_el : float Offset in degrees to be added to the elevation (pitch) angle. Default is 0.0. offset_roll : float Offset in degrees to be added to the roll angle. Default is 0.0. invert_az : bool If True, invert the azimuth (yaw) angle. Default is False. invert_el : bool If True, invert the elevation (pitch) angle. Default is False. invert_roll : bool If True, invert the roll angle. Default is False. """ self.OSC_address_position = OSC_address.rstrip("/") + "/pos" self.OSC_address_orientation = OSC_address.rstrip("/") + "/zyxeuler" self.ip = ip self.port = port self.client = SimpleUDPClient(self.ip, self.port) self.offset_az = offset_az self.offset_el = offset_el self.offset_roll = offset_roll self.invert_az = invert_az self.invert_el = invert_el self.invert_roll = invert_roll
[docs] def send_orientation(self, orientation: YPR | Quaternion | None): """Send orientation data to TASCAR. Parameters ---------- orientation : YPR | Quaternion The orientation data to send. """ if not isinstance(orientation, (YPR, Quaternion)): return if isinstance(orientation, Quaternion): orientation = quat2ypr(orientation) y, p, r = orientation.to_degrees() y += self.offset_az p += self.offset_el r += self.offset_roll if self.invert_az: y = -y if self.invert_el: p = -p if self.invert_roll: r = -r self.client.send_message(self.OSC_address_orientation, [y, p, r])
[docs] def send_position(self, position: Position): """Send position data to TASCAR. Parameters ---------- position : Position The position data to send. """ if not isinstance(position, Position): return x, y, z = position self.client.send_message(self.OSC_address_position, [x, y, z])
# TODO: Implement generic OSC sender class?