From 6522919cd8baa14951b7829bdd5f657250052f14 Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Tue, 26 Nov 2024 14:49:51 +0100 Subject: [PATCH 01/17] updated docstings --- examples/simple_send_and_receive/send_psn.py | 143 ++++++---- pypsn/__init__.py | 273 ++++++++++--------- 2 files changed, 234 insertions(+), 182 deletions(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index 8cef861..80d3017 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -1,19 +1,24 @@ #! /bin/env python3 """ -Usage: python send_psn.py 192.168.1.4 <--- change IP address + Usage: python send_psn.py 192.168.1.4 1 + + Change the IP address. + The 2nd arg is the number of tracker to generate. """ import sys +import time import pypsn +start_time_us = time.time_ns()//1000 psn_info = pypsn.PsnInfoPacket( info=pypsn.PsnInfo( - timestamp=1312, + timestamp=start_time_us, version_high=2, version_low=0, - frame_id=56, + frame_id=1, packet_count=1, ), name="system_name_001", @@ -22,48 +27,7 @@ pypsn.PsnTrackerInfo( tracker_id=i, tracker_name="tracker_" + str(i), - ) - for i in range(0, 7) - ] - ), -) - -psn_data = pypsn.PsnDataPacket( - info=psn_info.info, - trackers=( - [ - pypsn.PsnTracker( - tracker_id=tracker.tracker_id, - info=psn_info.info, - pos=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - speed=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - ori=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - accel=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - trgtpos=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - status=0, - timestamp=psn_info.info.timestamp, - ) - for tracker in psn_info.trackers + ) for i in range(0, int(sys.argv[2])) ] ), ) @@ -107,9 +71,7 @@ + " / " + str(tracker.timestamp) ) - psn_info_packet_bytes = pypsn.prepare_psn_info_packet_bytes(psn_info) -psn_data_packet_bytes = pypsn.prepare_psn_data_packet_bytes(psn_data) pypsn.send_psn_packet( psn_packet=psn_info_packet_bytes, @@ -118,9 +80,84 @@ mcast_port=56565, ) -pypsn.send_psn_packet( - psn_packet=psn_data_packet_bytes, - mcast_ip="236.10.10.10", - ip_addr=sys.argv[1], - mcast_port=56565, -) +print("\n--- Sendin PSN data in loop ---") + +while True: + time.sleep(0.1) + elapsed_time_us = time.time_ns()//1000 - start_time_us + + psn_data = pypsn.PsnDataPacket( + info=pypsn.PsnInfo( + timestamp=elapsed_time_us, + version_high=2, + version_low=0, + frame_id=1, + packet_count=1, + ), + trackers=( + [ + pypsn.PsnTracker( + tracker_id=tracker.tracker_id, + info=psn_info.info, + pos=pypsn.PsnVector3( + x=1.0, + y=1.0, + z=0.0, + ), + speed=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + ori=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + status=1.0, + accel=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + trgtpos=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + timestamp=elapsed_time_us, + ) for tracker in psn_info.trackers + ] + ) + ) + + # print("\n--- PSN data to be sent ---") + # print("timestamp: " + str(psn_data.info.timestamp)) + # print("version_high: " + str(psn_data.info.version_high)) + # print("version_low: " + str(psn_data.info.version_low)) + # print("frame_id: " + str(psn_data.info.frame_id)) + # print("packet_count: " + str(psn_data.info.packet_count)) + + # for tracker in psn_data.trackers: + # print( + # "tracker ID: " + # + str(tracker.tracker_id) + # + " tracker info: " + # + str(tracker.info) + # + " / " + str(tracker.pos) + # + " / " + str(tracker.speed) + # + " / " + str(tracker.ori) + # + " / " + str(tracker.accel) + # + " / " + str(tracker.trgtpos) + # + " / " + str(tracker.status) + # + " / " + str(tracker.timestamp) + # ) + + psn_data_packet_bytes = pypsn.prepare_psn_data_packet_bytes(psn_data) + + pypsn.send_psn_packet( + psn_packet=psn_data_packet_bytes, + mcast_ip='236.10.10.10', + ip_addr=sys.argv[1], + mcast_port=56565, + ) diff --git a/pypsn/__init__.py b/pypsn/__init__.py index ad94867..c74d622 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -15,8 +15,9 @@ class PsnVector3: - """_summary_""" - + """ + PSN vector variable structure + """ def __init__(self, x: float, y: float, z: float): self.x = x self.y = y @@ -33,8 +34,9 @@ def __iter__(self): class PsnInfo: - """_summary_""" - + """ + PSN info packet variable structure + """ def __init__( self, timestamp: int, @@ -51,16 +53,18 @@ def __init__( class PsnTrackerInfo: - """_summary_""" - + """ + PSN tracker info variable structure + """ def __init__(self, tracker_id: int, tracker_name: str): self.tracker_id = tracker_id self.tracker_name = tracker_name class PsnTracker: - """_summary_""" - + """ + PSN tracker data variable structure + """ def __init__( self, tracker_id: int, @@ -85,16 +89,18 @@ def __init__( class PsnDataPacket: - """_summary_""" - + """ + PSN data packet variable structure + """ def __init__(self, info: "PsnInfo", trackers: List["PsnTracker"]): self.info = info self.trackers = trackers class PsnInfoPacket: - """_summary_""" - + """ + PSN info packet variable structure + """ def __init__( self, info: "PsnInfo", @@ -107,10 +113,8 @@ def __init__( class PsnV1Chunk(IntEnum): - """_summary_ - - Args: - IntEnum (_type_): _description_ + """ + V1 main PSN ID """ PSN_V1_INFO_PACKET = 0x503C @@ -118,10 +122,8 @@ class PsnV1Chunk(IntEnum): class PsnV2Chunck(IntEnum): - """_summary_ - - Args: - IntEnum (_type_): _description_ + """ + V2 main PSN ID """ PSN_INFO_PACKET = 0x6756 @@ -129,10 +131,8 @@ class PsnV2Chunck(IntEnum): class PsnInfoChunk(IntEnum): - """_summary_ - - Args: - IntEnum (_type_): _description_ + """ + PSN ID for info packet """ PSN_INFO_PACKET_HEADER = 0x0000 @@ -141,10 +141,8 @@ class PsnInfoChunk(IntEnum): class PsnDataChunk(IntEnum): - """_summary_ - - Args: - IntEnum (_type_): _description_ + """ + PSN ID for data packet """ PSN_DATA_PACKET_HEADER = 0x0000 @@ -152,20 +150,16 @@ class PsnDataChunk(IntEnum): class PasnTrackerListChunk(IntEnum): - """_summary_ - - Args: - IntEnum (_type_): _description_ + """ + PSN ID for trackers name """ PSN_INFO_TRACKER_NAME = 0x0000 class PsnTrackerChunk(IntEnum): - """_summary_ - - Args: - IntEnum (_type_): _description_ + """ + PSN ID for trackers coordinates """ PSN_DATA_TRACKER_POS = 0x0000 @@ -176,10 +170,8 @@ class PsnTrackerChunk(IntEnum): class PsnTrackerChunkInfo(IntEnum): - """_summary_ - - Args: - IntEnum (_type_): _description_ + """ + PSN ID for trackers status and timestamp """ PSN_DATA_TRACKER_STATUS = 0x0003 @@ -187,15 +179,16 @@ class PsnTrackerChunkInfo(IntEnum): def join_multicast_windows(mcast_grp, mcast_port, if_ip): - """_summary_ + """ + Join multicast on Windows Args: - mcast_grp (_type_): _description_ - mcast_port (_type_): _description_ - if_ip (_type_): _description_ + mcast_grp (str): multicast ip + mcast_port (str): multicast port + if_ip (str): iface ip Returns: - _type_: _description_ + socket """ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -209,15 +202,16 @@ def join_multicast_windows(mcast_grp, mcast_port, if_ip): def join_multicast_posix(mcast_grp, mcast_port, if_ip): - """_summary_ + """ + Join multicast on posix OS Args: - mcast_grp (_type_): _description_ - mcast_port (_type_): _description_ - if_ip (_type_): _description_ + mcast_grp (str): multicast ip + mcast_port (str): multicast port + if_ip (str): iface ip Returns: - _type_: _description_ + socket """ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) try: @@ -238,10 +232,11 @@ def join_multicast_posix(mcast_grp, mcast_port, if_ip): def determine_os(): - """_summary_ + """ + Get local OS Returns: - _type_: _description_ + OS name """ if os.name == "nt": return str(os.name) @@ -254,10 +249,8 @@ def determine_os(): class Receiver(Thread): - """_summary_ - - Args: - Thread (_type_): _description_ + """ + PSN receiver class """ def __init__(self, callback, ip_addr="0.0.0.0", mcast_port=56565, timeout=2): @@ -269,14 +262,18 @@ def __init__(self, callback, ip_addr="0.0.0.0", mcast_port=56565, timeout=2): self.socket.settimeout(timeout) def stop(self): - """_summary_""" + """ + Stop listening. + """ self.running = False if self.socket is not None: self.socket.close() self.join() def run(self): - """_summary_""" + """ + Start listnening. + """ data = "" if self.socket is None: return @@ -291,14 +288,15 @@ def run(self): def get_socket(ip_addr, mcast_port): - """_summary_ + """ + Listening for incomming psn packets Args: - ip_addr (_type_): _description_ - mcast_port (_type_): _description_ + ip_addr (str): iface ip + mcast_port (str): multicast port Returns: - _type_: _description_ + socket """ mcast_grp = "236.10.10.10" sock = None @@ -314,13 +312,14 @@ def get_socket(ip_addr, mcast_port): def parse_psn_packet(buffer): - """_summary_ + """ + Parse received data buffer Args: - buffer (_type_): _description_ + buffer (bytes): Received PSN data Returns: - _type_: _description_ + psn packet """ psn_id = unpack(" Date: Tue, 26 Nov 2024 15:16:50 +0100 Subject: [PATCH 02/17] better send example --- examples/simple_send_and_receive/send_psn.py | 163 +++++++++---------- 1 file changed, 74 insertions(+), 89 deletions(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index 80d3017..afa015b 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -32,7 +32,52 @@ ), ) -print("\n--- PSN infos to be sent ---") +psn_data = pypsn.PsnDataPacket( + info=pypsn.PsnInfo( + timestamp=start_time_us, + version_high=2, + version_low=0, + frame_id=1, + packet_count=1, + ), + trackers=( + [ + pypsn.PsnTracker( + tracker_id=tracker.tracker_id, + info=psn_info.info, + pos=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + speed=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + ori=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + status=1.0, + accel=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + trgtpos=pypsn.PsnVector3( + x=0.0, + y=0.0, + z=0.0, + ), + timestamp=start_time_us, + ) for tracker in psn_info.trackers + ] + ) +) + +print("\n--- PSN infos to be sent at 1Hz ---") print("system name: " + psn_info.name) print("timestamp: " + str(psn_info.info.timestamp)) print("version_high: " + str(psn_info.info.version_high)) @@ -43,7 +88,7 @@ for tracker in psn_info.trackers: print(str(tracker.tracker_id) + ": " + str(tracker.tracker_name)) -print("\n--- PSN data to be sent ---") +print("\n--- PSN data to be sent at 60Hz ---") print("timestamp: " + str(psn_data.info.timestamp)) print("version_high: " + str(psn_data.info.version_high)) print("version_low: " + str(psn_data.info.version_low)) @@ -56,102 +101,42 @@ + str(tracker.tracker_id) + " tracker info: " + str(tracker.info) - + " / " - + str(tracker.pos) - + " / " - + str(tracker.speed) - + " / " - + str(tracker.ori) - + " / " - + str(tracker.accel) - + " / " - + str(tracker.trgtpos) - + " / " - + str(tracker.status) - + " / " - + str(tracker.timestamp) + + " / " + str(tracker.pos) + + " / " + str(tracker.speed) + + " / " + str(tracker.ori) + + " / " + str(tracker.accel) + + " / " + str(tracker.trgtpos) + + " / " + str(tracker.status) + + " / " + str(tracker.timestamp) ) -psn_info_packet_bytes = pypsn.prepare_psn_info_packet_bytes(psn_info) - -pypsn.send_psn_packet( - psn_packet=psn_info_packet_bytes, - mcast_ip="236.10.10.10", - ip_addr=sys.argv[1], - mcast_port=56565, -) print("\n--- Sendin PSN data in loop ---") +counter = 0.0 while True: - time.sleep(0.1) + time.sleep(1/60) elapsed_time_us = time.time_ns()//1000 - start_time_us - psn_data = pypsn.PsnDataPacket( - info=pypsn.PsnInfo( - timestamp=elapsed_time_us, - version_high=2, - version_low=0, - frame_id=1, - packet_count=1, - ), - trackers=( - [ - pypsn.PsnTracker( - tracker_id=tracker.tracker_id, - info=psn_info.info, - pos=pypsn.PsnVector3( - x=1.0, - y=1.0, - z=0.0, - ), - speed=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - ori=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - status=1.0, - accel=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - trgtpos=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, - ), - timestamp=elapsed_time_us, - ) for tracker in psn_info.trackers - ] + if counter < 6.0: + counter += 0.1 + else: + counter = 0.0 + + psn_info.info.timestamp = elapsed_time_us + psn_info_packet_bytes = pypsn.prepare_psn_info_packet_bytes(psn_info) + + pypsn.send_psn_packet( + psn_packet=psn_info_packet_bytes, + mcast_ip='236.10.10.10', + ip_addr=sys.argv[1], + mcast_port=56565, ) - ) - # print("\n--- PSN data to be sent ---") - # print("timestamp: " + str(psn_data.info.timestamp)) - # print("version_high: " + str(psn_data.info.version_high)) - # print("version_low: " + str(psn_data.info.version_low)) - # print("frame_id: " + str(psn_data.info.frame_id)) - # print("packet_count: " + str(psn_data.info.packet_count)) - - # for tracker in psn_data.trackers: - # print( - # "tracker ID: " - # + str(tracker.tracker_id) - # + " tracker info: " - # + str(tracker.info) - # + " / " + str(tracker.pos) - # + " / " + str(tracker.speed) - # + " / " + str(tracker.ori) - # + " / " + str(tracker.accel) - # + " / " + str(tracker.trgtpos) - # + " / " + str(tracker.status) - # + " / " + str(tracker.timestamp) - # ) + psn_data.info.timestamp = elapsed_time_us + for tracker in psn_data.trackers: + tracker.timestamp = elapsed_time_us + tracker.pos.x = counter + tracker.pos.y = counter psn_data_packet_bytes = pypsn.prepare_psn_data_packet_bytes(psn_data) From eed6fe2de217721b07c84cde7a282e713d8b6df3 Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Tue, 26 Nov 2024 17:32:50 +0100 Subject: [PATCH 03/17] byte encoding / decoding debug --- pypsn/__init__.py | 76 ++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/pypsn/__init__.py b/pypsn/__init__.py index c74d622..4936bf7 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -503,8 +503,11 @@ def parse_data_tracker_list(buffer): elif chunk_id == PsnTrackerChunkInfo.PSN_DATA_TRACKER_STATUS: status = unpack(" Date: Tue, 26 Nov 2024 17:36:03 +0100 Subject: [PATCH 04/17] tracker timestamp is unsigned long long too --- pypsn/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsn/__init__.py b/pypsn/__init__.py index 4936bf7..96b3870 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -507,7 +507,7 @@ def parse_data_tracker_list(buffer): chunk_id == PsnTrackerChunkInfo.PSN_DATA_TRACKER_TIMESTAMP ): - timestamp = unpack(" Date: Tue, 26 Nov 2024 18:22:19 +0100 Subject: [PATCH 05/17] fixed missing bit for tracker sub chunk --- examples/simple_send_and_receive/send_psn.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index afa015b..3ac8296 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -60,7 +60,7 @@ y=0.0, z=0.0, ), - status=1.0, + status=0.5, accel=pypsn.PsnVector3( x=0.0, y=0.0, @@ -114,7 +114,7 @@ counter = 0.0 while True: - time.sleep(1/60) + time.sleep(1/600) elapsed_time_us = time.time_ns()//1000 - start_time_us if counter < 6.0: @@ -133,10 +133,19 @@ ) psn_data.info.timestamp = elapsed_time_us + + if psn_data.info.frame_id < 255: + psn_data.info.frame_id += 1 + else: + psn_data.info.frame_id = 0 + for tracker in psn_data.trackers: tracker.timestamp = elapsed_time_us tracker.pos.x = counter - tracker.pos.y = counter + tracker.speed.x = counter + tracker.ori.x = counter + tracker.accel.x = counter + tracker.trgtpos.x = counter psn_data_packet_bytes = pypsn.prepare_psn_data_packet_bytes(psn_data) From 47decc159e8cdfbcc4abfa27a64cd3df50c2d93e Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Tue, 26 Nov 2024 18:25:20 +0100 Subject: [PATCH 06/17] fixed missing bit for tracker sub chunk --- pypsn/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsn/__init__.py b/pypsn/__init__.py index 96b3870..a896d8c 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -645,7 +645,7 @@ def prepare_psn_data_packet_bytes( pack( " Date: Tue, 26 Nov 2024 18:29:13 +0100 Subject: [PATCH 07/17] typo in example --- examples/simple_send_and_receive/send_psn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index 3ac8296..9d90727 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -114,7 +114,7 @@ counter = 0.0 while True: - time.sleep(1/600) + time.sleep(1/60) elapsed_time_us = time.time_ns()//1000 - start_time_us if counter < 6.0: From 60493a8eceb81102e2ea98cdbf35fd91e03530a6 Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Tue, 26 Nov 2024 20:09:38 +0100 Subject: [PATCH 08/17] ran ruff --- examples/simple_send_and_receive/send_psn.py | 45 +++-- pypsn/__init__.py | 169 +++++++++---------- 2 files changed, 103 insertions(+), 111 deletions(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index 9d90727..2118a8d 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -1,9 +1,9 @@ #! /bin/env python3 """ - Usage: python send_psn.py 192.168.1.4 1 +Usage: python send_psn.py 192.168.1.4 1 - Change the IP address. - The 2nd arg is the number of tracker to generate. +Change the IP address. +The 2nd arg is the number of tracker to generate. """ @@ -11,7 +11,7 @@ import time import pypsn -start_time_us = time.time_ns()//1000 +start_time_us = time.time_ns() // 1000 psn_info = pypsn.PsnInfoPacket( info=pypsn.PsnInfo( @@ -27,7 +27,8 @@ pypsn.PsnTrackerInfo( tracker_id=i, tracker_name="tracker_" + str(i), - ) for i in range(0, int(sys.argv[2])) + ) + for i in range(0, int(sys.argv[2])) ] ), ) @@ -72,9 +73,10 @@ z=0.0, ), timestamp=start_time_us, - ) for tracker in psn_info.trackers + ) + for tracker in psn_info.trackers ] - ) + ), ) print("\n--- PSN infos to be sent at 1Hz ---") @@ -101,21 +103,28 @@ + str(tracker.tracker_id) + " tracker info: " + str(tracker.info) - + " / " + str(tracker.pos) - + " / " + str(tracker.speed) - + " / " + str(tracker.ori) - + " / " + str(tracker.accel) - + " / " + str(tracker.trgtpos) - + " / " + str(tracker.status) - + " / " + str(tracker.timestamp) + + " / " + + str(tracker.pos) + + " / " + + str(tracker.speed) + + " / " + + str(tracker.ori) + + " / " + + str(tracker.accel) + + " / " + + str(tracker.trgtpos) + + " / " + + str(tracker.status) + + " / " + + str(tracker.timestamp) ) print("\n--- Sendin PSN data in loop ---") counter = 0.0 while True: - time.sleep(1/60) - elapsed_time_us = time.time_ns()//1000 - start_time_us + time.sleep(1 / 60) + elapsed_time_us = time.time_ns() // 1000 - start_time_us if counter < 6.0: counter += 0.1 @@ -127,7 +136,7 @@ pypsn.send_psn_packet( psn_packet=psn_info_packet_bytes, - mcast_ip='236.10.10.10', + mcast_ip="236.10.10.10", ip_addr=sys.argv[1], mcast_port=56565, ) @@ -151,7 +160,7 @@ pypsn.send_psn_packet( psn_packet=psn_data_packet_bytes, - mcast_ip='236.10.10.10', + mcast_ip="236.10.10.10", ip_addr=sys.argv[1], mcast_port=56565, ) diff --git a/pypsn/__init__.py b/pypsn/__init__.py index a896d8c..343d4a0 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -16,8 +16,9 @@ class PsnVector3: """ - PSN vector variable structure + PSN vector variable structure """ + def __init__(self, x: float, y: float, z: float): self.x = x self.y = y @@ -35,8 +36,9 @@ def __iter__(self): class PsnInfo: """ - PSN info packet variable structure + PSN info packet variable structure """ + def __init__( self, timestamp: int, @@ -54,8 +56,9 @@ def __init__( class PsnTrackerInfo: """ - PSN tracker info variable structure + PSN tracker info variable structure """ + def __init__(self, tracker_id: int, tracker_name: str): self.tracker_id = tracker_id self.tracker_name = tracker_name @@ -63,8 +66,9 @@ def __init__(self, tracker_id: int, tracker_name: str): class PsnTracker: """ - PSN tracker data variable structure + PSN tracker data variable structure """ + def __init__( self, tracker_id: int, @@ -90,8 +94,9 @@ def __init__( class PsnDataPacket: """ - PSN data packet variable structure + PSN data packet variable structure """ + def __init__(self, info: "PsnInfo", trackers: List["PsnTracker"]): self.info = info self.trackers = trackers @@ -99,8 +104,9 @@ def __init__(self, info: "PsnInfo", trackers: List["PsnTracker"]): class PsnInfoPacket: """ - PSN info packet variable structure + PSN info packet variable structure """ + def __init__( self, info: "PsnInfo", @@ -114,7 +120,7 @@ def __init__( class PsnV1Chunk(IntEnum): """ - V1 main PSN ID + V1 main PSN ID """ PSN_V1_INFO_PACKET = 0x503C @@ -123,7 +129,7 @@ class PsnV1Chunk(IntEnum): class PsnV2Chunck(IntEnum): """ - V2 main PSN ID + V2 main PSN ID """ PSN_INFO_PACKET = 0x6756 @@ -132,7 +138,7 @@ class PsnV2Chunck(IntEnum): class PsnInfoChunk(IntEnum): """ - PSN ID for info packet + PSN ID for info packet """ PSN_INFO_PACKET_HEADER = 0x0000 @@ -142,7 +148,7 @@ class PsnInfoChunk(IntEnum): class PsnDataChunk(IntEnum): """ - PSN ID for data packet + PSN ID for data packet """ PSN_DATA_PACKET_HEADER = 0x0000 @@ -151,7 +157,7 @@ class PsnDataChunk(IntEnum): class PasnTrackerListChunk(IntEnum): """ - PSN ID for trackers name + PSN ID for trackers name """ PSN_INFO_TRACKER_NAME = 0x0000 @@ -159,7 +165,7 @@ class PasnTrackerListChunk(IntEnum): class PsnTrackerChunk(IntEnum): """ - PSN ID for trackers coordinates + PSN ID for trackers coordinates """ PSN_DATA_TRACKER_POS = 0x0000 @@ -171,7 +177,7 @@ class PsnTrackerChunk(IntEnum): class PsnTrackerChunkInfo(IntEnum): """ - PSN ID for trackers status and timestamp + PSN ID for trackers status and timestamp """ PSN_DATA_TRACKER_STATUS = 0x0003 @@ -250,7 +256,7 @@ def determine_os(): class Receiver(Thread): """ - PSN receiver class + PSN receiver class """ def __init__(self, callback, ip_addr="0.0.0.0", mcast_port=56565, timeout=2): @@ -263,7 +269,7 @@ def __init__(self, callback, ip_addr="0.0.0.0", mcast_port=56565, timeout=2): def stop(self): """ - Stop listening. + Stop listening. """ self.running = False if self.socket is not None: @@ -272,7 +278,7 @@ def stop(self): def run(self): """ - Start listnening. + Start listnening. """ data = "" if self.socket is None: @@ -503,19 +509,14 @@ def parse_data_tracker_list(buffer): elif chunk_id == PsnTrackerChunkInfo.PSN_DATA_TRACKER_STATUS: status = unpack(" Date: Tue, 26 Nov 2024 20:23:50 +0100 Subject: [PATCH 09/17] Added sending example to readme --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ pypsn/__init__.py | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) mode change 100644 => 100755 README.md diff --git a/README.md b/README.md old mode 100644 new mode 100755 index bd8f951..a1cb98d --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ python -m pip install https://codeload.github.com/open-stage/python-psn/zip/refs ## Usage +### Receiving PSN data ```python import pypsn @@ -45,6 +46,44 @@ receiver.start() # start the receiving thread receiver.stop() # stop receiving +``` + +### Senfing PSN data +```python +import pypsn + +# create a psn_info object with appropriate data +psn_info = pypsn.PsnInfoPacket( + info=pypsn.PsnInfo( + timestamp=1312, + version_high=2, + version_low=0, + frame_id=1, + packet_count=1, + ), + name="system_name_001", + trackers=( + [ + pypsn.PsnTrackerInfo( + tracker_id=i, + tracker_name="tracker_" + str(i), + ) + for i in range(0, 8)) + ] + ), +) + +# convert the object to a byte string +psn_info_packet_bytes = pypsn.prepare_psn_info_packet_bytes(psn_info) + +# send the PSN info via multicast +pypsn.send_psn_packet( + psn_packet=psn_info_packet_bytes, + mcast_ip="236.10.10.10", + ip_addr="192.168.1.42", + mcast_port=56565, +) + ``` See examples folder for some more examples. diff --git a/pypsn/__init__.py b/pypsn/__init__.py index 343d4a0..8b06005 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -661,7 +661,7 @@ def send_psn_packet( Args: psn_packet (_type_): _description_ - mcast_ip (str, optional): _description_. Default '236.10.10.10'. + mcast_ip (str, optional): _description_. Default "236.10.10.10". ip_addr (str, optional): _description_. Default "0.0.0.0". mcast_port (int, optional): _description_. Default to 56565. """ From ca1b5cbcebadb481920829c2482d2f63d3e3865a Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Sat, 30 Nov 2024 22:09:12 +0100 Subject: [PATCH 10/17] Added unicast PSN sending --- examples/simple_send_and_receive/send_psn.py | 12 +++++++-- pypsn/__init__.py | 26 +++++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index 2118a8d..abc058b 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -5,6 +5,14 @@ Change the IP address. The 2nd arg is the number of tracker to generate. +Variables mapping for GrandMa3 users: + +- pos.xyz -> Position X Y Z +- speed.xyz -> Speed X Y Z +- ori.xyz -> Rot X Y Z +- accel.xyz -> None +- trgtpos.xyz -> None + """ import sys @@ -134,7 +142,7 @@ psn_info.info.timestamp = elapsed_time_us psn_info_packet_bytes = pypsn.prepare_psn_info_packet_bytes(psn_info) - pypsn.send_psn_packet( + pypsn.send_psn_packet_mc( psn_packet=psn_info_packet_bytes, mcast_ip="236.10.10.10", ip_addr=sys.argv[1], @@ -158,7 +166,7 @@ psn_data_packet_bytes = pypsn.prepare_psn_data_packet_bytes(psn_data) - pypsn.send_psn_packet( + pypsn.send_psn_packet_mc( psn_packet=psn_data_packet_bytes, mcast_ip="236.10.10.10", ip_addr=sys.argv[1], diff --git a/pypsn/__init__.py b/pypsn/__init__.py index 8b06005..d02809e 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -654,18 +654,32 @@ def prepare_psn_data_packet_bytes(data_packet: PsnDataPacket): return data_packet_bytes -def send_psn_packet( +def send_psn_packet_mc( psn_packet, mcast_ip="236.10.10.10", ip_addr="0.0.0.0", mcast_port=56565 ): - """_summary_ + """Send psn packet via multicat with the help of multicast_expert Args: - psn_packet (_type_): _description_ - mcast_ip (str, optional): _description_. Default "236.10.10.10". - ip_addr (str, optional): _description_. Default "0.0.0.0". - mcast_port (int, optional): _description_. Default to 56565. + psn_packet (_type_): as bytes + mcast_ip (str, optional): Default "236.10.10.10". + ip_addr (str, optional): Default "0.0.0.0". + mcast_port (int, optional): Default to 56565. """ with multicast_expert.McastTxSocket( socket.AF_INET, mcast_ips=[mcast_ip], iface_ip=ip_addr ) as mcast_tx_sock: mcast_tx_sock.sendto(psn_packet, (mcast_ip, mcast_port)) + + +def send_psn_packet_uc( + psn_packet, ucast_ip="127.0.0.1", ucast_port=56565 +): + """Send psn packet via unicast + + Args: + psn_packet (_type_): as bytes + ucast_ip (str, optional): Default "127.0.0.1". + ucast_port (int, optional): Default to 56565. + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP + sock.sendto(psn_packet, (ucast_ip, ucast_port)) From 43cd430cfb9abbaef4fd4a5ec3e5824c132fd95c Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Sat, 30 Nov 2024 22:58:02 +0100 Subject: [PATCH 11/17] merged both uni- and multicast in one function --- examples/simple_send_and_receive/send_psn.py | 2 +- pypsn/__init__.py | 40 ++++++++------------ 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index abc058b..6d7f749 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -166,7 +166,7 @@ psn_data_packet_bytes = pypsn.prepare_psn_data_packet_bytes(psn_data) - pypsn.send_psn_packet_mc( + pypsn.send_psn_packet( psn_packet=psn_data_packet_bytes, mcast_ip="236.10.10.10", ip_addr=sys.argv[1], diff --git a/pypsn/__init__.py b/pypsn/__init__.py index d02809e..42c45b9 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -654,32 +654,24 @@ def prepare_psn_data_packet_bytes(data_packet: PsnDataPacket): return data_packet_bytes -def send_psn_packet_mc( - psn_packet, mcast_ip="236.10.10.10", ip_addr="0.0.0.0", mcast_port=56565 +def send_psn_packet( + psn_packet, mcast_ip="236.10.10.10", ip_addr="127.0.0.1", port=56565 ): - """Send psn packet via multicat with the help of multicast_expert + """Send psn packet. + If mcast_ip is specified, it does via multicast with the help + of multicast_expert. If not, it does via simple unicast. Args: psn_packet (_type_): as bytes mcast_ip (str, optional): Default "236.10.10.10". - ip_addr (str, optional): Default "0.0.0.0". - mcast_port (int, optional): Default to 56565. - """ - with multicast_expert.McastTxSocket( - socket.AF_INET, mcast_ips=[mcast_ip], iface_ip=ip_addr - ) as mcast_tx_sock: - mcast_tx_sock.sendto(psn_packet, (mcast_ip, mcast_port)) - - -def send_psn_packet_uc( - psn_packet, ucast_ip="127.0.0.1", ucast_port=56565 -): - """Send psn packet via unicast - - Args: - psn_packet (_type_): as bytes - ucast_ip (str, optional): Default "127.0.0.1". - ucast_port (int, optional): Default to 56565. - """ - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP - sock.sendto(psn_packet, (ucast_ip, ucast_port)) + ip_addr (str, optional): Default "127.0.0.1". + port (int, optional): Default to 56565. + """ + if mcast_ip: + with multicast_expert.McastTxSocket( + socket.AF_INET, mcast_ips=[mcast_ip], iface_ip=ip_addr + ) as mcast_tx_sock: + mcast_tx_sock.sendto(psn_packet, (mcast_ip, port)) + else: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP + sock.sendto(psn_packet, (ip_addr, port)) From d04f1ecfd3a3ef73849082f3fb1d7b225b823750 Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Sat, 30 Nov 2024 22:59:31 +0100 Subject: [PATCH 12/17] typo in example --- examples/simple_send_and_receive/send_psn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index 6d7f749..6b15b4f 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -170,5 +170,5 @@ psn_packet=psn_data_packet_bytes, mcast_ip="236.10.10.10", ip_addr=sys.argv[1], - mcast_port=56565, + port=56565, ) From 714b761d5202965a662e2000b530808dcb4e345a Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Sun, 1 Dec 2024 07:21:02 +0100 Subject: [PATCH 13/17] typo in example --- examples/simple_send_and_receive/send_psn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index 6b15b4f..f057a25 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -142,11 +142,11 @@ psn_info.info.timestamp = elapsed_time_us psn_info_packet_bytes = pypsn.prepare_psn_info_packet_bytes(psn_info) - pypsn.send_psn_packet_mc( + pypsn.send_psn_packet( psn_packet=psn_info_packet_bytes, mcast_ip="236.10.10.10", ip_addr=sys.argv[1], - mcast_port=56565, + port=56565, ) psn_data.info.timestamp = elapsed_time_us From 8d074382e7cf797e3c20ea0fd3ad72b5ee40b0ca Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Sun, 1 Dec 2024 07:44:02 +0100 Subject: [PATCH 14/17] better example for uni-multi-casting psn --- examples/simple_send_and_receive/send_psn.py | 33 +++++++++++++++----- pypsn/__init__.py | 8 ++--- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index f057a25..166dd8d 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -1,9 +1,14 @@ #! /bin/env python3 """ -Usage: python send_psn.py 192.168.1.4 1 +Usage: -Change the IP address. -The 2nd arg is the number of tracker to generate. +multicast: python send_psn.py 1 192.168.1.4 236.10.10.10 +unicast: python send_psn.py 1 192.168.1.4 + +Args: +- number of trackers to generate +- local IP adress +- multricast adress (opt) Variables mapping for GrandMa3 users: @@ -19,6 +24,18 @@ import time import pypsn +try: + tracker_num = int(sys.argv[1]) + ip_addr = sys.argv[2] + mcast_ip = None + + if sys.argv[3]: + mcast_ip = sys.argv[3] + +except Exception as e: + print('Args: tracker_num ip_addr mcast_ip (opt).') + print(e) + start_time_us = time.time_ns() // 1000 psn_info = pypsn.PsnInfoPacket( @@ -36,7 +53,7 @@ tracker_id=i, tracker_name="tracker_" + str(i), ) - for i in range(0, int(sys.argv[2])) + for i in range(0, tracker_num) ] ), ) @@ -144,8 +161,8 @@ pypsn.send_psn_packet( psn_packet=psn_info_packet_bytes, - mcast_ip="236.10.10.10", - ip_addr=sys.argv[1], + mcast_ip=mcast_ip, + ip_addr=ip_addr, port=56565, ) @@ -168,7 +185,7 @@ pypsn.send_psn_packet( psn_packet=psn_data_packet_bytes, - mcast_ip="236.10.10.10", - ip_addr=sys.argv[1], + mcast_ip=mcast_ip, + ip_addr=ip_addr, port=56565, ) diff --git a/pypsn/__init__.py b/pypsn/__init__.py index 42c45b9..7d6af1b 100755 --- a/pypsn/__init__.py +++ b/pypsn/__init__.py @@ -663,9 +663,9 @@ def send_psn_packet( Args: psn_packet (_type_): as bytes - mcast_ip (str, optional): Default "236.10.10.10". - ip_addr (str, optional): Default "127.0.0.1". - port (int, optional): Default to 56565. + mcast_ip (str, optional): multicastip. Default: "236.10.10.10" + ip_addr (str, optional): local ip. Default: 127.0.0.1 + port (int, optional): udp port. Default: 56565 """ if mcast_ip: with multicast_expert.McastTxSocket( @@ -673,5 +673,5 @@ def send_psn_packet( ) as mcast_tx_sock: mcast_tx_sock.sendto(psn_packet, (mcast_ip, port)) else: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(psn_packet, (ip_addr, port)) From de3fdc9ad0f55d063ac1936cec841985034780af Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Sun, 1 Dec 2024 07:59:17 +0100 Subject: [PATCH 15/17] more extensive pack unpack test --- tests/test_own_pack_unpack.py | 50 ++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 19 deletions(-) mode change 100644 => 100755 tests/test_own_pack_unpack.py diff --git a/tests/test_own_pack_unpack.py b/tests/test_own_pack_unpack.py old mode 100644 new mode 100755 index 9a4b12f..fdb57e2 --- a/tests/test_own_pack_unpack.py +++ b/tests/test_own_pack_unpack.py @@ -33,31 +33,31 @@ tracker_id=tracker.tracker_id, info=psn_info.info, pos=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, + x=1.0, + y=1.0, + z=1.0, ), speed=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, + x=1.0, + y=1.0, + z=1.0, ), ori=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, + x=1.0, + y=1.0, + z=1.0, ), accel=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, + x=1.0, + y=1.0, + z=1.0, ), trgtpos=pypsn.PsnVector3( - x=0.0, - y=0.0, - z=0.0, + x=1.0, + y=1.0, + z=1.0, ), - status=0, + status=0.5, timestamp=psn_info.info.timestamp, ) for tracker in psn_info.trackers @@ -90,9 +90,17 @@ def test_data_data(pypsn_module): """Test position""" hexdata = get_test_data() data = pypsn_module.parse_psn_packet(hexdata) - test_vector = pypsn_module.PsnVector3(0.0, 0.0, 0.0) + test_vector = pypsn_module.PsnVector3(1.0, 1.0, 1.0) if isinstance(data, pypsn_module.PsnDataPacket): - assert test_vector == data.trackers[0].pos + for tracker_id in range(0, 7): + assert test_vector == data.trackers[tracker_id].pos + assert test_vector == data.trackers[tracker_id].ori + assert test_vector == data.trackers[tracker_id].speed + assert test_vector == data.trackers[tracker_id].accel + assert test_vector == data.trackers[tracker_id].trgtpos + assert 0.5 == data.trackers[tracker_id].status + assert 1312 == data.trackers[tracker_id].timestamp + assert tracker_id == data.trackers[tracker_id].tracker_id def test_data_info(pypsn_module): @@ -128,4 +136,8 @@ def test_info_data(pypsn_module): hexdata = get_test_info() data = pypsn_module.parse_psn_packet(hexdata) if isinstance(data, pypsn_module.PsnInfoPacket): - assert data.trackers[0].tracker_name == b"tracker_0" + for tracker_id in range(0, 7): + assert tracker_id == data.trackers[tracker_id].tracker_id + assert ("tracker_" + str(tracker_id)).encode("UTF-8") == data.trackers[ + tracker_id + ].tracker_name From 2c439a76e9ad5b69c779465e03e607c29590432a Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Sun, 1 Dec 2024 08:10:44 +0100 Subject: [PATCH 16/17] updated README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1cb98d..d6bf84b 100755 --- a/README.md +++ b/README.md @@ -90,9 +90,10 @@ See examples folder for some more examples. ## Development, status - Supporting PSN V2 -- Parsing and sending (via [Multicast Expert](https://github.com/multiplemonomials/multicast_expert)) +- Parsing and sending (via [Multicast Expert](https://github.com/multiplemonomials/multicast_expert)) or via Unicast - Using threading module - Linux (Rpi OS incl.), Windows and macOS tested +- PSN messages recognized by GrandMa3 2.1 - Typed, no-strict - Initial pytest testing provided together with CI/CD setup From 00a439d707cd1f73c14717ef0c3a8d023ba925ea Mon Sep 17 00:00:00 2001 From: Matthew Franklin Date: Sun, 1 Dec 2024 08:13:08 +0100 Subject: [PATCH 17/17] ruff on example --- examples/simple_send_and_receive/send_psn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple_send_and_receive/send_psn.py b/examples/simple_send_and_receive/send_psn.py index 166dd8d..37aab36 100755 --- a/examples/simple_send_and_receive/send_psn.py +++ b/examples/simple_send_and_receive/send_psn.py @@ -33,7 +33,7 @@ mcast_ip = sys.argv[3] except Exception as e: - print('Args: tracker_num ip_addr mcast_ip (opt).') + print("Args: tracker_num ip_addr mcast_ip (opt).") print(e) start_time_us = time.time_ns() // 1000