photonlibpy 2025.0.0b5__py3-none-any.whl → 2025.0.0b7__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,10 +17,11 @@ class NTTopicSet:
17
17
  different for sim vs. real camera
18
18
  """
19
19
 
20
- def __init__(self, tableName: str, cameraName: str) -> None:
21
- instance = nt.NetworkTableInstance.getDefault()
22
- photonvision_root_table = instance.getTable(tableName)
23
- self.subTable = photonvision_root_table.getSubTable(cameraName)
20
+ def __init__(
21
+ self,
22
+ ntSubTable: nt.NetworkTable,
23
+ ) -> None:
24
+ self.subTable = ntSubTable
24
25
 
25
26
  def updateEntries(self) -> None:
26
27
  options = nt.PubSubOptions()
@@ -60,11 +60,10 @@ class PhotonCameraSim:
60
60
  self.videoSimRawEnabled: bool = False
61
61
  self.videoSimWireframeEnabled: bool = False
62
62
  self.videoSimWireframeResolution: float = 0.1
63
- self.videoSimProcEnabled: bool = (
64
- False # TODO switch this back to default True when the functionality is enabled
65
- )
63
+ # TODO switch this back to default True when the functionality is enabled
64
+ self.videoSimProcEnabled: bool = False
66
65
  self.heartbeatCounter: int = 0
67
- self.nextNtEntryTime = int(wpilib.Timer.getFPGATimestamp() * 1e6)
66
+ self.nextNtEntryTime = wpilib.Timer.getFPGATimestamp()
68
67
  self.tagLayout = AprilTagFieldLayout.loadField(AprilTagField.k2024Crescendo)
69
68
 
70
69
  self.cam = camera
@@ -95,7 +94,7 @@ class PhotonCameraSim:
95
94
  (self.prop.getResWidth(), self.prop.getResHeight())
96
95
  )
97
96
 
98
- self.ts = NTTopicSet("photonvision", self.cam.getName())
97
+ self.ts = NTTopicSet(self.cam._cameraTable)
99
98
  self.ts.updateEntries()
100
99
 
101
100
  # Handle this last explicitly for this function signature because the other constructor is called in the initialiser list
@@ -173,20 +172,20 @@ class PhotonCameraSim:
173
172
  def consumeNextEntryTime(self) -> float | None:
174
173
  """Determine if this camera should process a new frame based on performance metrics and the time
175
174
  since the last update. This returns an Optional which is either empty if no update should occur
176
- or a Long of the timestamp in microseconds of when the frame which should be received by NT. If
175
+ or a float of the timestamp in seconds of when the frame which should be received by NT. If
177
176
  a timestamp is returned, the last frame update time becomes that timestamp.
178
177
 
179
- :returns: Optional long which is empty while blocked or the NT entry timestamp in microseconds if
178
+ :returns: Optional float which is empty while blocked or the NT entry timestamp in seconds if
180
179
  ready
181
180
  """
182
181
  # check if this camera is ready for another frame update
183
- now = int(wpilib.Timer.getFPGATimestamp() * 1e6)
184
- timestamp = 0
182
+ now = wpilib.Timer.getFPGATimestamp()
183
+ timestamp = 0.0
185
184
  iter = 0
186
185
  # prepare next latest update
187
186
  while now >= self.nextNtEntryTime:
188
- timestamp = int(self.nextNtEntryTime)
189
- frameTime = int(self.prop.estSecUntilNextFrame() * 1e6)
187
+ timestamp = self.nextNtEntryTime
188
+ frameTime = self.prop.estSecUntilNextFrame()
190
189
  self.nextNtEntryTime += frameTime
191
190
 
192
191
  # if frame time is very small, avoid blocking
@@ -432,7 +431,9 @@ class PhotonCameraSim:
432
431
  )
433
432
 
434
433
  def submitProcessedFrame(
435
- self, result: PhotonPipelineResult, receiveTimestamp: float | None
434
+ self,
435
+ result: PhotonPipelineResult,
436
+ receiveTimestamp_us: float | None = None,
436
437
  ):
437
438
  """Simulate one processed frame of vision data, putting one result to NT. Image capture timestamp
438
439
  overrides :meth:`.PhotonPipelineResult.getTimestampSeconds` for more
@@ -441,44 +442,45 @@ class PhotonCameraSim:
441
442
  :param result: The pipeline result to submit
442
443
  :param receiveTimestamp: The (sim) timestamp when this result was read by NT in microseconds. If not passed image capture time is assumed be (current time - latency)
443
444
  """
444
- if receiveTimestamp is None:
445
- receiveTimestamp = wpilib.Timer.getFPGATimestamp() * 1e6
446
- receiveTimestamp = int(receiveTimestamp)
445
+ if receiveTimestamp_us is None:
446
+ receiveTimestamp_us = wpilib.Timer.getFPGATimestamp() * 1e6
447
+ receiveTimestamp_us = int(receiveTimestamp_us)
447
448
 
448
- self.ts.latencyMillisEntry.set(result.getLatencyMillis(), receiveTimestamp)
449
+ self.ts.latencyMillisEntry.set(result.getLatencyMillis(), receiveTimestamp_us)
449
450
 
450
451
  newPacket = PhotonPipelineResult.photonStruct.pack(result)
451
- self.ts.rawBytesEntry.set(newPacket.getData(), receiveTimestamp)
452
+ self.ts.rawBytesEntry.set(newPacket.getData(), receiveTimestamp_us)
452
453
 
453
454
  hasTargets = result.hasTargets()
454
- self.ts.hasTargetEntry.set(hasTargets, receiveTimestamp)
455
+ self.ts.hasTargetEntry.set(hasTargets, receiveTimestamp_us)
455
456
  if not hasTargets:
456
- self.ts.targetPitchEntry.set(0.0, receiveTimestamp)
457
- self.ts.targetYawEntry.set(0.0, receiveTimestamp)
458
- self.ts.targetAreaEntry.set(0.0, receiveTimestamp)
459
- self.ts.targetPoseEntry.set(Transform3d(), receiveTimestamp)
460
- self.ts.targetSkewEntry.set(0.0, receiveTimestamp)
457
+ self.ts.targetPitchEntry.set(0.0, receiveTimestamp_us)
458
+ self.ts.targetYawEntry.set(0.0, receiveTimestamp_us)
459
+ self.ts.targetAreaEntry.set(0.0, receiveTimestamp_us)
460
+ self.ts.targetPoseEntry.set(Transform3d(), receiveTimestamp_us)
461
+ self.ts.targetSkewEntry.set(0.0, receiveTimestamp_us)
461
462
  else:
462
463
  bestTarget = result.getBestTarget()
463
464
  assert bestTarget
464
465
 
465
- self.ts.targetPitchEntry.set(bestTarget.getPitch(), receiveTimestamp)
466
- self.ts.targetYawEntry.set(bestTarget.getYaw(), receiveTimestamp)
467
- self.ts.targetAreaEntry.set(bestTarget.getArea(), receiveTimestamp)
468
- self.ts.targetSkewEntry.set(bestTarget.getSkew(), receiveTimestamp)
466
+ self.ts.targetPitchEntry.set(bestTarget.getPitch(), receiveTimestamp_us)
467
+ self.ts.targetYawEntry.set(bestTarget.getYaw(), receiveTimestamp_us)
468
+ self.ts.targetAreaEntry.set(bestTarget.getArea(), receiveTimestamp_us)
469
+ self.ts.targetSkewEntry.set(bestTarget.getSkew(), receiveTimestamp_us)
469
470
 
470
471
  self.ts.targetPoseEntry.set(
471
- bestTarget.getBestCameraToTarget(), receiveTimestamp
472
+ bestTarget.getBestCameraToTarget(), receiveTimestamp_us
472
473
  )
473
474
 
474
- intrinsics = self.prop.getIntrinsics()
475
- intrinsicsView = intrinsics.flatten().tolist()
476
- self.ts.cameraIntrinsicsPublisher.set(intrinsicsView, receiveTimestamp)
475
+ intrinsics = self.prop.getIntrinsics()
476
+ intrinsicsView = intrinsics.flatten().tolist()
477
+ self.ts.cameraIntrinsicsPublisher.set(intrinsicsView, receiveTimestamp_us)
477
478
 
478
- distortion = self.prop.getDistCoeffs()
479
- distortionView = distortion.flatten().tolist()
480
- self.ts.cameraDistortionPublisher.set(distortionView, receiveTimestamp)
479
+ distortion = self.prop.getDistCoeffs()
480
+ distortionView = distortion.flatten().tolist()
481
+ self.ts.cameraDistortionPublisher.set(distortionView, receiveTimestamp_us)
481
482
 
482
- self.ts.heartbeatPublisher.set(self.heartbeatCounter, receiveTimestamp)
483
+ self.ts.heartbeatPublisher.set(self.heartbeatCounter, receiveTimestamp_us)
484
+ self.heartbeatCounter += 1
483
485
 
484
- self.ts.subTable.getInstance().flush()
486
+ self.ts.subTable.getInstance().flush()
@@ -4,6 +4,7 @@ import typing
4
4
 
5
5
  import cv2 as cv
6
6
  import numpy as np
7
+ import numpy.typing as npt
7
8
  from wpimath.geometry import Rotation2d, Rotation3d, Translation3d
8
9
  from wpimath.units import hertz, seconds
9
10
 
@@ -31,8 +32,8 @@ class SimCameraProperties:
31
32
  """Default constructor which is the same as {@link #PERFECT_90DEG}"""
32
33
  self.resWidth: int = -1
33
34
  self.resHeight: int = -1
34
- self.camIntrinsics: np.ndarray = np.zeros((3, 3)) # [3,3]
35
- self.distCoeffs: np.ndarray = np.zeros((8, 1)) # [8,1]
35
+ self.camIntrinsics: npt.NDArray[np.floating] = np.zeros((3, 3)) # [3,3]
36
+ self.distCoeffs: npt.NDArray[np.floating] = np.zeros((8, 1)) # [8,1]
36
37
  self.avgErrorPx: float = 0.0
37
38
  self.errorStdDevPx: float = 0.0
38
39
  self.frameSpeed: seconds = 0.0
@@ -80,7 +81,6 @@ class SimCameraProperties:
80
81
  newCamIntrinsics: np.ndarray,
81
82
  newDistCoeffs: np.ndarray,
82
83
  ) -> None:
83
-
84
84
  self.resWidth = width
85
85
  self.resHeight = height
86
86
  self.camIntrinsics = newCamIntrinsics
@@ -173,10 +173,10 @@ class SimCameraProperties:
173
173
  def getAspectRatio(self) -> float:
174
174
  return 1.0 * self.resWidth / self.resHeight
175
175
 
176
- def getIntrinsics(self) -> np.ndarray:
176
+ def getIntrinsics(self) -> npt.NDArray[np.floating]:
177
177
  return self.camIntrinsics
178
178
 
179
- def getDistCoeffs(self) -> np.ndarray:
179
+ def getDistCoeffs(self) -> npt.NDArray[np.floating]:
180
180
  return self.distCoeffs
181
181
 
182
182
  def getFPS(self) -> hertz:
@@ -355,7 +355,6 @@ class SimCameraProperties:
355
355
 
356
356
  # find intersections
357
357
  for i, normal in enumerate(self.viewplanes):
358
-
359
358
  # // we want to know the value of t when the line intercepts this plane
360
359
  # // parametrized: v = t * ab + a, where v lies on the plane
361
360
  # // we can find the projection of a onto the plane normal
@@ -467,7 +466,7 @@ class SimCameraProperties:
467
466
 
468
467
  def estSecUntilNextFrame(self) -> seconds:
469
468
  """
470
- :returns: Estimate how long until the next frame should be processed in milliseconds
469
+ :returns: Estimate how long until the next frame should be processed in seconds
471
470
  """
472
471
  # // exceptional processing latency blocks the next frame
473
472
  return self.frameSpeed + max(0.0, self.estLatency() - self.frameSpeed)
@@ -305,7 +305,7 @@ class VisionSystemSim:
305
305
  timestampNt = optTimestamp
306
306
  latency = camSim.prop.estLatency()
307
307
  # the image capture timestamp in seconds of this result
308
- timestampCapture = timestampNt * 1.0e-6 - latency
308
+ timestampCapture = timestampNt - latency
309
309
 
310
310
  # use camera pose from the image capture timestamp
311
311
  lateRobotPose = self.getRobotPose(timestampCapture)
@@ -318,7 +318,8 @@ class VisionSystemSim:
318
318
  # process a PhotonPipelineResult with visible targets
319
319
  camResult = camSim.process(latency, lateCameraPose, allTargets)
320
320
  # publish this info to NT at estimated timestamp of receive
321
- camSim.submitProcessedFrame(camResult, timestampNt)
321
+ # needs a timestamp in microseconds
322
+ camSim.submitProcessedFrame(camResult, timestampNt * 1.0e6)
322
323
  # display debug results
323
324
  for tgt in camResult.getTargets():
324
325
  trf = tgt.getBestCameraToTarget()
photonlibpy/version.py CHANGED
@@ -1,2 +1,2 @@
1
- PHOTONLIB_VERSION="v2025.0.0.beta.5"
2
- PHOTONVISION_VERSION="v2025.0.0-beta-5"
1
+ PHOTONLIB_VERSION="v2025.0.0.beta.7"
2
+ PHOTONVISION_VERSION="v2025.0.0-beta-7"
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: photonlibpy
3
- Version: 2025.0.0b5
4
- Summary: Pure-python implementation of PhotonLib for interfacing with PhotonVision on coprocessors. Implemented with PhotonVision version v2025.0.0-beta-5 .
3
+ Version: 2025.0.0b7
4
+ Summary: Pure-python implementation of PhotonLib for interfacing with PhotonVision on coprocessors. Implemented with PhotonVision version v2025.0.0-beta-7 .
5
5
  Home-page: https://photonvision.org
6
6
  Author: Photonvision Development Team
7
7
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ photonlibpy/packet.py,sha256=5YomViVFwOljL2FGOetWM9FbPc_yCQ15ylzkYlgLIs8,9724
4
4
  photonlibpy/photonCamera.py,sha256=ENBHp959it4aLnFtV66rHoAIYCvK4bTflZMrGSCwnZ8,12905
5
5
  photonlibpy/photonPoseEstimator.py,sha256=2iMqxPFsQHTsq95yv-WCSv1a6wXNcHPqOyMc4Bu6IG0,12584
6
6
  photonlibpy/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
- photonlibpy/version.py,sha256=6IuHSryBtyyUy01tGUnTVp4WTudFZoF1qjXkZI10_s8,77
7
+ photonlibpy/version.py,sha256=BYilKcVVr0-QNCDokqtB4odE1QfHYAG5R7_A_PA2O-I,77
8
8
  photonlibpy/estimation/__init__.py,sha256=pZ-d6fN1DJvT-lRl4FfIos5HAvlzmetIOrGIinrdv7k,223
9
9
  photonlibpy/estimation/cameraTargetRelation.py,sha256=i7DPBXtkZve4ToXQscEIe-5F1oGQ1Qmf5QBaE__EeMQ,1158
10
10
  photonlibpy/estimation/openCVHelp.py,sha256=O1dV7v7RHSyw7l5L0QXbal6t9K7iyvEG76tG-t4AFVg,12388
@@ -18,20 +18,20 @@ photonlibpy/generated/PhotonTrackedTargetSerde.py,sha256=-6vKir_ABDVBGbg8ktM48IK
18
18
  photonlibpy/generated/PnpResultSerde.py,sha256=YoTKdQ51oSdxC-7Poy6hunL0-zkMKvP5uedqaHWPudY,2693
19
19
  photonlibpy/generated/TargetCornerSerde.py,sha256=kziD_rQIwyhzPfgOaDgn-3d87tvtXiAYbBjzu76biYU,2190
20
20
  photonlibpy/generated/__init__.py,sha256=mElM8M88---wxTWO-SRqIJ4EfxN0fdIUwZBZ-UIGuRw,428
21
- photonlibpy/networktables/NTTopicSet.py,sha256=TXJyVg6S8gONeuRqX_NI0FdxYw51bNLlFIAQ4Y2wyVg,3104
21
+ photonlibpy/networktables/NTTopicSet.py,sha256=29wPgXcuqT-u72-YXwSjRHWhECNzU8eDsexcqlA8KQ0,2967
22
22
  photonlibpy/networktables/__init__.py,sha256=o_LxTdyIylAszMy_zhUtTkXHyu6jqxccccj78d44OrI,35
23
23
  photonlibpy/simulation/__init__.py,sha256=HKJV02of5d8bOnuI7syLzSYtOYge7XUrHSaLvawh99M,227
24
- photonlibpy/simulation/photonCameraSim.py,sha256=5GTPdU7SRAahhtXYHcbm0GJEzARWA7lyBG3v279pqGk,19891
25
- photonlibpy/simulation/simCameraProperties.py,sha256=zh6dZ5SeVvW6qxLDhlMm09o-uScQf-20b_r4K3lutjs,27119
24
+ photonlibpy/simulation/photonCameraSim.py,sha256=8ELLcOXUtDftV_OYzGG03wigANimJs2SehV-fluWy3k,19907
25
+ photonlibpy/simulation/simCameraProperties.py,sha256=ODVxnylF8zw9HZSbw0PzG_OEtUo9ChRo-G_iEgADOCg,27195
26
26
  photonlibpy/simulation/videoSimUtil.py,sha256=xMuTvJ2Jx9IoQqmAJi_zUm06MdEwhVpIz9OyzYQp0k4,29
27
- photonlibpy/simulation/visionSystemSim.py,sha256=ln1TYVMXUreg6nxrVZJ7lw8puvgLbBBde_ciM2CH5G4,13788
27
+ photonlibpy/simulation/visionSystemSim.py,sha256=GmKs0d32WE8B020YEWnj-0dQuCnVv1ScGdcFl1fOsKo,13835
28
28
  photonlibpy/simulation/visionTargetSim.py,sha256=FH85fKE4NntowUvssfgZ1KlE-I_3Z-QuAgb2bFqvfdY,2219
29
29
  photonlibpy/targeting/TargetCorner.py,sha256=ouKj3E5uD76OZSNHHuSDzKOY65a8HqtcOsuejH-MVsU,276
30
30
  photonlibpy/targeting/__init__.py,sha256=YzINSpq6A0cjr-yAQcFqHoiYdLGKPFXThlVYlMjY11w,295
31
31
  photonlibpy/targeting/multiTargetPNPResult.py,sha256=Y9rweHtMzoCZ6mv6F8CutQi2Thq5pHN0ydBWvTCsOwY,806
32
32
  photonlibpy/targeting/photonPipelineResult.py,sha256=MbaSyHZTJpoKTtLOZztpSGSt9xWWFqhzgwj8medObVA,2732
33
33
  photonlibpy/targeting/photonTrackedTarget.py,sha256=zCoFp32hX-3GmBYEmsYBQieBoMzXtP2F_55_q0zPOXA,1956
34
- photonlibpy-2025.0.0b5.dist-info/METADATA,sha256=4ZsDeto_2wGBLbC11F4UxpFEQu2OE6F1DRhfssRZK9w,689
35
- photonlibpy-2025.0.0b5.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
36
- photonlibpy-2025.0.0b5.dist-info/top_level.txt,sha256=T8Xc6U6he2VjKUAca6zawSkHdUZuLanxYIc4nxw2ctc,12
37
- photonlibpy-2025.0.0b5.dist-info/RECORD,,
34
+ photonlibpy-2025.0.0b7.dist-info/METADATA,sha256=1_kdzX3qorgdxPyPEFtZa2ZbKddOjmQbCC4A91OKNes,689
35
+ photonlibpy-2025.0.0b7.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
36
+ photonlibpy-2025.0.0b7.dist-info/top_level.txt,sha256=T8Xc6U6he2VjKUAca6zawSkHdUZuLanxYIc4nxw2ctc,12
37
+ photonlibpy-2025.0.0b7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.0)
2
+ Generator: bdist_wheel (0.45.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5