photonlibpy 2025.0.0a0__tar.gz → 2025.0.0b1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/PKG-INFO +2 -2
  2. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/generated/MultiTargetPNPResultSerde.py +12 -0
  3. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/generated/PhotonPipelineMetadataSerde.py +23 -4
  4. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/generated/PhotonPipelineResultSerde.py +19 -2
  5. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/generated/PhotonTrackedTargetSerde.py +40 -0
  6. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/generated/PnpResultSerde.py +19 -0
  7. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/generated/TargetCornerSerde.py +12 -0
  8. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/packet.py +108 -1
  9. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/photonCamera.py +1 -1
  10. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/photonPoseEstimator.py +2 -2
  11. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/targeting/multiTargetPNPResult.py +2 -2
  12. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/targeting/photonPipelineResult.py +5 -1
  13. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/targeting/photonTrackedTarget.py +6 -4
  14. photonlibpy-2025.0.0b1/photonlibpy/version.py +2 -0
  15. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy.egg-info/PKG-INFO +2 -2
  16. photonlibpy-2025.0.0a0/photonlibpy/version.py +0 -2
  17. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/__init__.py +0 -0
  18. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/estimatedRobotPose.py +0 -0
  19. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/generated/__init__.py +0 -0
  20. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/targeting/TargetCorner.py +0 -0
  21. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy/targeting/__init__.py +0 -0
  22. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy.egg-info/SOURCES.txt +0 -0
  23. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy.egg-info/dependency_links.txt +0 -0
  24. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy.egg-info/requires.txt +0 -0
  25. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/photonlibpy.egg-info/top_level.txt +0 -0
  26. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/setup.cfg +0 -0
  27. {photonlibpy-2025.0.0a0 → photonlibpy-2025.0.0b1}/setup.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: photonlibpy
3
- Version: 2025.0.0a0
4
- Summary: Pure-python implementation of PhotonLib for interfacing with PhotonVision on coprocessors. Implemented with PhotonVision version v2025.0.0-alpha-0 .
3
+ Version: 2025.0.0b1
4
+ Summary: Pure-python implementation of PhotonLib for interfacing with PhotonVision on coprocessors. Implemented with PhotonVision version v2025.0.0-beta-1 .
5
5
  Home-page: https://photonvision.org
6
6
  Author: Photonvision Development Team
7
7
  Description-Content-Type: text/markdown
@@ -21,6 +21,7 @@
21
21
  ###############################################################################
22
22
 
23
23
  from ..targeting import *
24
+ from ..packet import Packet
24
25
 
25
26
 
26
27
  class MultiTargetPNPResultSerde:
@@ -28,6 +29,17 @@ class MultiTargetPNPResultSerde:
28
29
  MESSAGE_VERSION = "541096947e9f3ca2d3f425ff7b04aa7b"
29
30
  MESSAGE_FORMAT = "PnpResult:ae4d655c0a3104d88df4f5db144c1e86 estimatedPose;int16 fiducialIDsUsed[?];"
30
31
 
32
+ @staticmethod
33
+ def pack(value: "MultiTargetPNPResult") -> "Packet":
34
+ ret = Packet()
35
+
36
+ # estimatedPose is of non-intrinsic type PnpResult
37
+ ret.encodeBytes(PnpResult.photonStruct.pack(value.estimatedPose).getData())
38
+
39
+ # fiducialIDsUsed is a custom VLA!
40
+ ret.encodeShortList(value.fiducialIDsUsed)
41
+ return ret
42
+
31
43
  @staticmethod
32
44
  def unpack(packet: "Packet") -> "MultiTargetPNPResult":
33
45
  ret = MultiTargetPNPResult()
@@ -21,14 +21,30 @@
21
21
  ###############################################################################
22
22
 
23
23
  from ..targeting import *
24
+ from ..packet import Packet
24
25
 
25
26
 
26
27
  class PhotonPipelineMetadataSerde:
27
28
  # Message definition md5sum. See photon_packet.adoc for details
28
- MESSAGE_VERSION = "626e70461cbdb274fb43ead09c255f4e"
29
- MESSAGE_FORMAT = (
30
- "int64 sequenceID;int64 captureTimestampMicros;int64 publishTimestampMicros;"
31
- )
29
+ MESSAGE_VERSION = "ac0a45f686457856fb30af77699ea356"
30
+ MESSAGE_FORMAT = "int64 sequenceID;int64 captureTimestampMicros;int64 publishTimestampMicros;int64 timeSinceLastPong;"
31
+
32
+ @staticmethod
33
+ def pack(value: "PhotonPipelineMetadata") -> "Packet":
34
+ ret = Packet()
35
+
36
+ # sequenceID is of intrinsic type int64
37
+ ret.encodeLong(value.sequenceID)
38
+
39
+ # captureTimestampMicros is of intrinsic type int64
40
+ ret.encodeLong(value.captureTimestampMicros)
41
+
42
+ # publishTimestampMicros is of intrinsic type int64
43
+ ret.encodeLong(value.publishTimestampMicros)
44
+
45
+ # timeSinceLastPong is of intrinsic type int64
46
+ ret.encodeLong(value.timeSinceLastPong)
47
+ return ret
32
48
 
33
49
  @staticmethod
34
50
  def unpack(packet: "Packet") -> "PhotonPipelineMetadata":
@@ -43,6 +59,9 @@ class PhotonPipelineMetadataSerde:
43
59
  # publishTimestampMicros is of intrinsic type int64
44
60
  ret.publishTimestampMicros = packet.decodeLong()
45
61
 
62
+ # timeSinceLastPong is of intrinsic type int64
63
+ ret.timeSinceLastPong = packet.decodeLong()
64
+
46
65
  return ret
47
66
 
48
67
 
@@ -21,12 +21,29 @@
21
21
  ###############################################################################
22
22
 
23
23
  from ..targeting import *
24
+ from ..packet import Packet
24
25
 
25
26
 
26
27
  class PhotonPipelineResultSerde:
27
28
  # Message definition md5sum. See photon_packet.adoc for details
28
- MESSAGE_VERSION = "5eeaa293d0c69aea90eaddea786a2b3b"
29
- MESSAGE_FORMAT = "PhotonPipelineMetadata:626e70461cbdb274fb43ead09c255f4e metadata;PhotonTrackedTarget:cc6dbb5c5c1e0fa808108019b20863f1 targets[?];optional MultiTargetPNPResult:541096947e9f3ca2d3f425ff7b04aa7b multitagResult;"
29
+ MESSAGE_VERSION = "4b2ff16a964b5e2bf04be0c1454d91c4"
30
+ MESSAGE_FORMAT = "PhotonPipelineMetadata:ac0a45f686457856fb30af77699ea356 metadata;PhotonTrackedTarget:cc6dbb5c5c1e0fa808108019b20863f1 targets[?];optional MultiTargetPNPResult:541096947e9f3ca2d3f425ff7b04aa7b multitagResult;"
31
+
32
+ @staticmethod
33
+ def pack(value: "PhotonPipelineResult") -> "Packet":
34
+ ret = Packet()
35
+
36
+ # metadata is of non-intrinsic type PhotonPipelineMetadata
37
+ ret.encodeBytes(
38
+ PhotonPipelineMetadata.photonStruct.pack(value.metadata).getData()
39
+ )
40
+
41
+ # targets is a custom VLA!
42
+ ret.encodeList(value.targets, PhotonTrackedTarget.photonStruct)
43
+
44
+ # multitagResult is optional! it better not be a VLA too
45
+ ret.encodeOptional(value.multitagResult, MultiTargetPNPResult.photonStruct)
46
+ return ret
30
47
 
31
48
  @staticmethod
32
49
  def unpack(packet: "Packet") -> "PhotonPipelineResult":
@@ -21,6 +21,7 @@
21
21
  ###############################################################################
22
22
 
23
23
  from ..targeting import *
24
+ from ..packet import Packet
24
25
 
25
26
 
26
27
  class PhotonTrackedTargetSerde:
@@ -28,6 +29,45 @@ class PhotonTrackedTargetSerde:
28
29
  MESSAGE_VERSION = "cc6dbb5c5c1e0fa808108019b20863f1"
29
30
  MESSAGE_FORMAT = "float64 yaw;float64 pitch;float64 area;float64 skew;int32 fiducialId;int32 objDetectId;float32 objDetectConf;Transform3d bestCameraToTarget;Transform3d altCameraToTarget;float64 poseAmbiguity;TargetCorner:16f6ac0dedc8eaccb951f4895d9e18b6 minAreaRectCorners[?];TargetCorner:16f6ac0dedc8eaccb951f4895d9e18b6 detectedCorners[?];"
30
31
 
32
+ @staticmethod
33
+ def pack(value: "PhotonTrackedTarget") -> "Packet":
34
+ ret = Packet()
35
+
36
+ # yaw is of intrinsic type float64
37
+ ret.encodeDouble(value.yaw)
38
+
39
+ # pitch is of intrinsic type float64
40
+ ret.encodeDouble(value.pitch)
41
+
42
+ # area is of intrinsic type float64
43
+ ret.encodeDouble(value.area)
44
+
45
+ # skew is of intrinsic type float64
46
+ ret.encodeDouble(value.skew)
47
+
48
+ # fiducialId is of intrinsic type int32
49
+ ret.encodeInt(value.fiducialId)
50
+
51
+ # objDetectId is of intrinsic type int32
52
+ ret.encodeInt(value.objDetectId)
53
+
54
+ # objDetectConf is of intrinsic type float32
55
+ ret.encodeFloat(value.objDetectConf)
56
+
57
+ ret.encodeTransform(value.bestCameraToTarget)
58
+
59
+ ret.encodeTransform(value.altCameraToTarget)
60
+
61
+ # poseAmbiguity is of intrinsic type float64
62
+ ret.encodeDouble(value.poseAmbiguity)
63
+
64
+ # minAreaRectCorners is a custom VLA!
65
+ ret.encodeList(value.minAreaRectCorners, TargetCorner.photonStruct)
66
+
67
+ # detectedCorners is a custom VLA!
68
+ ret.encodeList(value.detectedCorners, TargetCorner.photonStruct)
69
+ return ret
70
+
31
71
  @staticmethod
32
72
  def unpack(packet: "Packet") -> "PhotonTrackedTarget":
33
73
  ret = PhotonTrackedTarget()
@@ -21,6 +21,7 @@
21
21
  ###############################################################################
22
22
 
23
23
  from ..targeting import *
24
+ from ..packet import Packet
24
25
 
25
26
 
26
27
  class PnpResultSerde:
@@ -28,6 +29,24 @@ class PnpResultSerde:
28
29
  MESSAGE_VERSION = "ae4d655c0a3104d88df4f5db144c1e86"
29
30
  MESSAGE_FORMAT = "Transform3d best;Transform3d alt;float64 bestReprojErr;float64 altReprojErr;float64 ambiguity;"
30
31
 
32
+ @staticmethod
33
+ def pack(value: "PnpResult") -> "Packet":
34
+ ret = Packet()
35
+
36
+ ret.encodeTransform(value.best)
37
+
38
+ ret.encodeTransform(value.alt)
39
+
40
+ # bestReprojErr is of intrinsic type float64
41
+ ret.encodeDouble(value.bestReprojErr)
42
+
43
+ # altReprojErr is of intrinsic type float64
44
+ ret.encodeDouble(value.altReprojErr)
45
+
46
+ # ambiguity is of intrinsic type float64
47
+ ret.encodeDouble(value.ambiguity)
48
+ return ret
49
+
31
50
  @staticmethod
32
51
  def unpack(packet: "Packet") -> "PnpResult":
33
52
  ret = PnpResult()
@@ -21,6 +21,7 @@
21
21
  ###############################################################################
22
22
 
23
23
  from ..targeting import *
24
+ from ..packet import Packet
24
25
 
25
26
 
26
27
  class TargetCornerSerde:
@@ -28,6 +29,17 @@ class TargetCornerSerde:
28
29
  MESSAGE_VERSION = "16f6ac0dedc8eaccb951f4895d9e18b6"
29
30
  MESSAGE_FORMAT = "float64 x;float64 y;"
30
31
 
32
+ @staticmethod
33
+ def pack(value: "TargetCorner") -> "Packet":
34
+ ret = Packet()
35
+
36
+ # x is of intrinsic type float64
37
+ ret.encodeDouble(value.x)
38
+
39
+ # y is of intrinsic type float64
40
+ ret.encodeDouble(value.y)
41
+ return ret
42
+
31
43
  @staticmethod
32
44
  def unpack(packet: "Packet") -> "TargetCorner":
33
45
  ret = TargetCorner()
@@ -22,7 +22,7 @@ import wpilib
22
22
 
23
23
 
24
24
  class Packet:
25
- def __init__(self, data: bytes):
25
+ def __init__(self, data: bytes = b""):
26
26
  """
27
27
  * Constructs an empty packet.
28
28
  *
@@ -198,3 +198,110 @@ class Packet:
198
198
  return serde.unpack(self)
199
199
  else:
200
200
  return None
201
+
202
+ def _encodeGeneric(self, packFormat, value):
203
+ """
204
+ Append bytes to the packet data buffer.
205
+ """
206
+ self.packetData = self.packetData + struct.pack(packFormat, value)
207
+ self.size = len(self.packetData)
208
+
209
+ def encode8(self, value: int):
210
+ """
211
+ Encodes a single byte and appends it to the packet.
212
+ """
213
+ self._encodeGeneric("<b", value)
214
+
215
+ def encode16(self, value: int):
216
+ """
217
+ Encodes a short (2 bytes) and appends it to the packet.
218
+ """
219
+ self._encodeGeneric("<h", value)
220
+
221
+ def encodeInt(self, value: int):
222
+ """
223
+ Encodes an int (4 bytes) and appends it to the packet.
224
+ """
225
+ self._encodeGeneric("<l", value)
226
+
227
+ def encodeFloat(self, value: float):
228
+ """
229
+ Encodes a float (4 bytes) and appends it to the packet.
230
+ """
231
+ self._encodeGeneric("<f", value)
232
+
233
+ def encodeLong(self, value: int):
234
+ """
235
+ Encodes a long (8 bytes) and appends it to the packet.
236
+ """
237
+ self._encodeGeneric("<q", value)
238
+
239
+ def encodeDouble(self, value: float):
240
+ """
241
+ Encodes a double (8 bytes) and appends it to the packet.
242
+ """
243
+ self._encodeGeneric("<d", value)
244
+
245
+ def encodeBoolean(self, value: bool):
246
+ """
247
+ Encodes a boolean as a single byte and appends it to the packet.
248
+ """
249
+ self.encode8(1 if value else 0)
250
+
251
+ def encodeDoubleArray(self, values: list[float]):
252
+ """
253
+ Encodes an array of doubles and appends it to the packet.
254
+ """
255
+ self.encode8(len(values))
256
+ for value in values:
257
+ self.encodeDouble(value)
258
+
259
+ def encodeShortList(self, values: list[int]):
260
+ """
261
+ Encodes a list of shorts, with length prefixed as a single byte.
262
+ """
263
+ self.encode8(len(values))
264
+ for value in values:
265
+ self.encode16(value)
266
+
267
+ def encodeTransform(self, transform: Transform3d):
268
+ """
269
+ Encodes a Transform3d (translation and rotation) and appends it to the packet.
270
+ """
271
+ # Encode Translation3d part (x, y, z)
272
+ self.encodeDouble(transform.translation().x)
273
+ self.encodeDouble(transform.translation().y)
274
+ self.encodeDouble(transform.translation().z)
275
+
276
+ # Encode Rotation3d as Quaternion (w, x, y, z)
277
+ quaternion = transform.rotation().getQuaternion()
278
+ self.encodeDouble(quaternion.W())
279
+ self.encodeDouble(quaternion.X())
280
+ self.encodeDouble(quaternion.Y())
281
+ self.encodeDouble(quaternion.Z())
282
+
283
+ def encodeList(self, values: list[Any], serde: Type):
284
+ """
285
+ Encodes a list of items using a specific serializer and appends it to the packet.
286
+ """
287
+ self.encode8(len(values))
288
+ for item in values:
289
+ packed = serde.pack(item)
290
+ self.packetData = self.packetData + packed.getData()
291
+ self.size = len(self.packetData)
292
+
293
+ def encodeOptional(self, value: Optional[Any], serde: Type):
294
+ """
295
+ Encodes an optional value using a specific serializer.
296
+ """
297
+ if value is None:
298
+ self.encodeBoolean(False)
299
+ else:
300
+ self.encodeBoolean(True)
301
+ packed = serde.pack(value)
302
+ self.packetData = self.packetData + packed.getData()
303
+ self.size = len(self.packetData)
304
+
305
+ def encodeBytes(self, value: bytes):
306
+ self.packetData = self.packetData + value
307
+ self.size = len(self.packetData)
@@ -124,7 +124,7 @@ class PhotonCamera:
124
124
  pkt = Packet(byteList)
125
125
  newResult = PhotonPipelineResult.photonStruct.unpack(pkt)
126
126
  # NT4 allows us to correct the timestamp based on when the message was sent
127
- newResult.ntReceiveTimestampMicros = timestamp / 1e6
127
+ newResult.ntReceiveTimestampMicros = timestamp
128
128
  ret.append(newResult)
129
129
 
130
130
  return ret
@@ -269,8 +269,8 @@ class PhotonPoseEstimator:
269
269
  def _multiTagOnCoprocStrategy(
270
270
  self, result: PhotonPipelineResult
271
271
  ) -> Optional[EstimatedRobotPose]:
272
- if result.multiTagResult.estimatedPose.isPresent:
273
- best_tf = result.multiTagResult.estimatedPose.best
272
+ if result.multitagResult is not None:
273
+ best_tf = result.multitagResult.estimatedPose.best
274
274
  best = (
275
275
  Pose3d()
276
276
  .transformBy(best_tf) # field-to-camera
@@ -8,8 +8,8 @@ class PnpResult:
8
8
  best: Transform3d = field(default_factory=Transform3d)
9
9
  alt: Transform3d = field(default_factory=Transform3d)
10
10
  ambiguity: float = 0.0
11
- bestReprojError: float = 0.0
12
- altReprojError: float = 0.0
11
+ bestReprojErr: float = 0.0
12
+ altReprojErr: float = 0.0
13
13
 
14
14
  photonStruct: "PNPResultSerde" = None
15
15
 
@@ -15,6 +15,8 @@ class PhotonPipelineMetadata:
15
15
  # Mirror of the heartbeat entry -- monotonically increasing
16
16
  sequenceID: int = -1
17
17
 
18
+ timeSinceLastPong: int = -1
19
+
18
20
  photonStruct: "PhotonPipelineMetadataSerde" = None
19
21
 
20
22
 
@@ -24,8 +26,10 @@ class PhotonPipelineResult:
24
26
  ntReceiveTimestampMicros: int = -1
25
27
 
26
28
  targets: list[PhotonTrackedTarget] = field(default_factory=list)
29
+ # Python users beware! We don't currently run a Time Sync Server, so these timestamps are in
30
+ # an arbitrary timebase. This is not true in C++ or Java.
27
31
  metadata: PhotonPipelineMetadata = field(default_factory=PhotonPipelineMetadata)
28
- multiTagResult: Optional[MultiTargetPNPResult] = None
32
+ multitagResult: Optional[MultiTargetPNPResult] = None
29
33
 
30
34
  def getLatencyMillis(self) -> float:
31
35
  return (
@@ -13,9 +13,11 @@ class PhotonTrackedTarget:
13
13
  fiducialId: int = -1
14
14
  bestCameraToTarget: Transform3d = field(default_factory=Transform3d)
15
15
  altCameraToTarget: Transform3d = field(default_factory=Transform3d)
16
- minAreaRectCorners: list[TargetCorner] | None = None
17
- detectedCorners: list[TargetCorner] | None = None
16
+ minAreaRectCorners: list[TargetCorner] = field(default_factory=list[TargetCorner])
17
+ detectedCorners: list[TargetCorner] = field(default_factory=list[TargetCorner])
18
18
  poseAmbiguity: float = 0.0
19
+ objDetectId: int = -1
20
+ objDetectConf: float = 0.0
19
21
 
20
22
  def getYaw(self) -> float:
21
23
  return self.yaw
@@ -35,10 +37,10 @@ class PhotonTrackedTarget:
35
37
  def getPoseAmbiguity(self) -> float:
36
38
  return self.poseAmbiguity
37
39
 
38
- def getMinAreaRectCorners(self) -> list[TargetCorner] | None:
40
+ def getMinAreaRectCorners(self) -> list[TargetCorner]:
39
41
  return self.minAreaRectCorners
40
42
 
41
- def getDetectedCorners(self) -> list[TargetCorner] | None:
43
+ def getDetectedCorners(self) -> list[TargetCorner]:
42
44
  return self.detectedCorners
43
45
 
44
46
  def getBestCameraToTarget(self) -> Transform3d:
@@ -0,0 +1,2 @@
1
+ PHOTONLIB_VERSION="v2025.0.0.beta.1"
2
+ PHOTONVISION_VERSION="v2025.0.0-beta-1"
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: photonlibpy
3
- Version: 2025.0.0a0
4
- Summary: Pure-python implementation of PhotonLib for interfacing with PhotonVision on coprocessors. Implemented with PhotonVision version v2025.0.0-alpha-0 .
3
+ Version: 2025.0.0b1
4
+ Summary: Pure-python implementation of PhotonLib for interfacing with PhotonVision on coprocessors. Implemented with PhotonVision version v2025.0.0-beta-1 .
5
5
  Home-page: https://photonvision.org
6
6
  Author: Photonvision Development Team
7
7
  Description-Content-Type: text/markdown
@@ -1,2 +0,0 @@
1
- PHOTONLIB_VERSION="v2025.0.0.alpha.0"
2
- PHOTONVISION_VERSION="v2025.0.0-alpha-0"