photonlibpy 2025.0.0b3__py3-none-any.whl → 2025.0.0b5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- photonlibpy/estimation/openCVHelp.py +110 -1
- photonlibpy/estimation/rotTrlTransform3d.py +19 -0
- photonlibpy/estimation/targetModel.py +63 -0
- photonlibpy/estimation/visionEstimation.py +22 -0
- photonlibpy/generated/PhotonPipelineResultSerde.py +1 -3
- photonlibpy/networktables/NTTopicSet.py +7 -0
- photonlibpy/photonCamera.py +102 -36
- photonlibpy/py.typed +1 -0
- photonlibpy/simulation/photonCameraSim.py +109 -3
- photonlibpy/simulation/simCameraProperties.py +142 -1
- photonlibpy/simulation/visionSystemSim.py +92 -0
- photonlibpy/simulation/visionTargetSim.py +10 -0
- photonlibpy/version.py +2 -2
- {photonlibpy-2025.0.0b3.dist-info → photonlibpy-2025.0.0b5.dist-info}/METADATA +2 -3
- {photonlibpy-2025.0.0b3.dist-info → photonlibpy-2025.0.0b5.dist-info}/RECORD +17 -16
- {photonlibpy-2025.0.0b3.dist-info → photonlibpy-2025.0.0b5.dist-info}/WHEEL +0 -0
- {photonlibpy-2025.0.0b3.dist-info → photonlibpy-2025.0.0b5.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,22 @@ from .visionTargetSim import VisionTargetSim
|
|
15
15
|
|
16
16
|
|
17
17
|
class VisionSystemSim:
|
18
|
+
"""A simulated vision system involving a camera(s) and coprocessor(s) mounted on a mobile robot
|
19
|
+
running PhotonVision, detecting targets placed on the field. :class:`.VisionTargetSim`s added to
|
20
|
+
this class will be detected by the :class:`.PhotonCameraSim`s added to this class. This class
|
21
|
+
should be updated periodically with the robot's current pose in order to publish the simulated
|
22
|
+
camera target info.
|
23
|
+
"""
|
24
|
+
|
18
25
|
def __init__(self, visionSystemName: str):
|
26
|
+
"""A simulated vision system involving a camera(s) and coprocessor(s) mounted on a mobile robot
|
27
|
+
running PhotonVision, detecting targets placed on the field. :class:`.VisionTargetSim`s added to
|
28
|
+
this class will be detected by the :class:`.PhotonCameraSim`s added to this class. This class
|
29
|
+
should be updated periodically with the robot's current pose in order to publish the simulated
|
30
|
+
camera target info.
|
31
|
+
|
32
|
+
:param visionSystemName: The specific identifier for this vision system in NetworkTables.
|
33
|
+
"""
|
19
34
|
self.dbgField: Field2d = Field2d()
|
20
35
|
self.bufferLength: seconds = 1.5
|
21
36
|
|
@@ -32,12 +47,21 @@ class VisionSystemSim:
|
|
32
47
|
wpilib.SmartDashboard.putData(self.tableName + "/Sim Field", self.dbgField)
|
33
48
|
|
34
49
|
def getCameraSim(self, name: str) -> PhotonCameraSim | None:
|
50
|
+
"""Get one of the simulated cameras."""
|
35
51
|
return self.camSimMap.get(name, None)
|
36
52
|
|
37
53
|
def getCameraSims(self) -> list[PhotonCameraSim]:
|
54
|
+
"""Get all the simulated cameras."""
|
38
55
|
return [*self.camSimMap.values()]
|
39
56
|
|
40
57
|
def addCamera(self, cameraSim: PhotonCameraSim, robotToCamera: Transform3d) -> None:
|
58
|
+
"""Adds a simulated camera to this vision system with a specified robot-to-camera transformation.
|
59
|
+
The vision targets registered with this vision system simulation will be observed by the
|
60
|
+
simulated :class:`.PhotonCamera`.
|
61
|
+
|
62
|
+
:param cameraSim: The camera simulation
|
63
|
+
:param robotToCamera: The transform from the robot pose to the camera pose
|
64
|
+
"""
|
41
65
|
name = cameraSim.getCamera().getName()
|
42
66
|
if name not in self.camSimMap:
|
43
67
|
self.camSimMap[name] = cameraSim
|
@@ -49,10 +73,15 @@ class VisionSystemSim:
|
|
49
73
|
)
|
50
74
|
|
51
75
|
def clearCameras(self) -> None:
|
76
|
+
"""Remove all simulated cameras from this vision system."""
|
52
77
|
self.camSimMap.clear()
|
53
78
|
self.camTrfMap.clear()
|
54
79
|
|
55
80
|
def removeCamera(self, cameraSim: PhotonCameraSim) -> bool:
|
81
|
+
"""Remove a simulated camera from this vision system.
|
82
|
+
|
83
|
+
:returns: If the camera was present and removed
|
84
|
+
"""
|
56
85
|
name = cameraSim.getCamera().getName()
|
57
86
|
if name in self.camSimMap:
|
58
87
|
del self.camSimMap[name]
|
@@ -65,6 +94,14 @@ class VisionSystemSim:
|
|
65
94
|
cameraSim: PhotonCameraSim,
|
66
95
|
time: seconds = wpilib.Timer.getFPGATimestamp(),
|
67
96
|
) -> Transform3d | None:
|
97
|
+
"""Get a simulated camera's position relative to the robot. If the requested camera is invalid, an
|
98
|
+
empty optional is returned.
|
99
|
+
|
100
|
+
:param cameraSim: The specific camera to get the robot-to-camera transform of
|
101
|
+
:param timeSeconds: Timestamp in seconds of when the transform should be observed
|
102
|
+
|
103
|
+
:returns: The transform of this camera, or an empty optional if it is invalid
|
104
|
+
"""
|
68
105
|
if cameraSim in self.camTrfMap:
|
69
106
|
trfBuffer = self.camTrfMap[cameraSim]
|
70
107
|
sample = trfBuffer.sample(time)
|
@@ -80,6 +117,13 @@ class VisionSystemSim:
|
|
80
117
|
cameraSim: PhotonCameraSim,
|
81
118
|
time: seconds = wpilib.Timer.getFPGATimestamp(),
|
82
119
|
) -> Pose3d | None:
|
120
|
+
"""Get a simulated camera's position on the field. If the requested camera is invalid, an empty
|
121
|
+
optional is returned.
|
122
|
+
|
123
|
+
:param cameraSim: The specific camera to get the field pose of
|
124
|
+
|
125
|
+
:returns: The pose of this camera, or an empty optional if it is invalid
|
126
|
+
"""
|
83
127
|
robotToCamera = self.getRobotToCamera(cameraSim, time)
|
84
128
|
if robotToCamera is None:
|
85
129
|
return None
|
@@ -93,6 +137,14 @@ class VisionSystemSim:
|
|
93
137
|
def adjustCamera(
|
94
138
|
self, cameraSim: PhotonCameraSim, robotToCamera: Transform3d
|
95
139
|
) -> bool:
|
140
|
+
"""Adjust a camera's position relative to the robot. Use this if your camera is on a gimbal or
|
141
|
+
turret or some other mobile platform.
|
142
|
+
|
143
|
+
:param cameraSim: The simulated camera to change the relative position of
|
144
|
+
:param robotToCamera: New transform from the robot to the camera
|
145
|
+
|
146
|
+
:returns: If the cameraSim was valid and transform was adjusted
|
147
|
+
"""
|
96
148
|
if cameraSim in self.camTrfMap:
|
97
149
|
self.camTrfMap[cameraSim].addSample(
|
98
150
|
wpilib.Timer.getFPGATimestamp(), Pose3d() + robotToCamera
|
@@ -102,6 +154,7 @@ class VisionSystemSim:
|
|
102
154
|
return False
|
103
155
|
|
104
156
|
def resetCameraTransforms(self, cameraSim: PhotonCameraSim | None = None) -> None:
|
157
|
+
"""Reset the transform history for this camera to just the current transform."""
|
105
158
|
now = wpilib.Timer.getFPGATimestamp()
|
106
159
|
|
107
160
|
def resetSingleCamera(self, cameraSim: PhotonCameraSim) -> bool:
|
@@ -133,12 +186,30 @@ class VisionSystemSim:
|
|
133
186
|
def addVisionTargets(
|
134
187
|
self, targets: list[VisionTargetSim], targetType: str = "targets"
|
135
188
|
) -> None:
|
189
|
+
"""Adds targets on the field which your vision system is designed to detect. The {@link
|
190
|
+
PhotonCamera}s simulated from this system will report the location of the camera relative to
|
191
|
+
the subset of these targets which are visible from the given camera position.
|
192
|
+
|
193
|
+
:param targets: Targets to add to the simulated field
|
194
|
+
:param type: Type of target (e.g. "cargo").
|
195
|
+
"""
|
196
|
+
|
136
197
|
if targetType not in self.targetSets:
|
137
198
|
self.targetSets[targetType] = targets
|
138
199
|
else:
|
139
200
|
self.targetSets[targetType] += targets
|
140
201
|
|
141
202
|
def addAprilTags(self, layout: AprilTagFieldLayout) -> None:
|
203
|
+
"""Adds targets on the field which your vision system is designed to detect. The {@link
|
204
|
+
PhotonCamera}s simulated from this system will report the location of the camera relative to
|
205
|
+
the subset of these targets which are visible from the given camera position.
|
206
|
+
|
207
|
+
The AprilTags from this layout will be added as vision targets under the type "apriltag".
|
208
|
+
The poses added preserve the tag layout's current alliance origin. If the tag layout's alliance
|
209
|
+
origin is changed, these added tags will have to be cleared and re-added.
|
210
|
+
|
211
|
+
:param tagLayout: The field tag layout to get Apriltag poses and IDs from
|
212
|
+
"""
|
142
213
|
targets: list[VisionTargetSim] = []
|
143
214
|
for tag in layout.getTags():
|
144
215
|
tag_pose = layout.getTagPose(tag.ID)
|
@@ -172,9 +243,15 @@ class VisionSystemSim:
|
|
172
243
|
def getRobotPose(
|
173
244
|
self, timestamp: seconds = wpilib.Timer.getFPGATimestamp()
|
174
245
|
) -> Pose3d | None:
|
246
|
+
"""Get the robot pose in meters saved by the vision system at this timestamp.
|
247
|
+
|
248
|
+
:param timestamp: Timestamp of the desired robot pose
|
249
|
+
"""
|
250
|
+
|
175
251
|
return self.robotPoseBuffer.sample(timestamp)
|
176
252
|
|
177
253
|
def resetRobotPose(self, robotPose: Pose2d | Pose3d) -> None:
|
254
|
+
"""Clears all previous robot poses and sets robotPose at current time."""
|
178
255
|
if type(robotPose) is Pose2d:
|
179
256
|
robotPose = Pose3d(robotPose)
|
180
257
|
assert type(robotPose) is Pose3d
|
@@ -186,16 +263,23 @@ class VisionSystemSim:
|
|
186
263
|
return self.dbgField
|
187
264
|
|
188
265
|
def update(self, robotPose: Pose2d | Pose3d) -> None:
|
266
|
+
"""Periodic update. Ensure this is called repeatedly-- camera performance is used to automatically
|
267
|
+
determine if a new frame should be submitted.
|
268
|
+
|
269
|
+
:param robotPoseMeters: The simulated robot pose in meters
|
270
|
+
"""
|
189
271
|
if type(robotPose) is Pose2d:
|
190
272
|
robotPose = Pose3d(robotPose)
|
191
273
|
assert type(robotPose) is Pose3d
|
192
274
|
|
275
|
+
# update vision targets on field
|
193
276
|
for targetType, targets in self.targetSets.items():
|
194
277
|
posesToAdd: list[Pose2d] = []
|
195
278
|
for target in targets:
|
196
279
|
posesToAdd.append(target.getPose().toPose2d())
|
197
280
|
self.dbgField.getObject(targetType).setPoses(posesToAdd)
|
198
281
|
|
282
|
+
# save "real" robot poses over time
|
199
283
|
now = wpilib.Timer.getFPGATimestamp()
|
200
284
|
self.robotPoseBuffer.addSample(now, robotPose)
|
201
285
|
self.dbgField.setRobotPose(robotPose.toPose2d())
|
@@ -208,17 +292,22 @@ class VisionSystemSim:
|
|
208
292
|
visTgtPoses2d: list[Pose2d] = []
|
209
293
|
cameraPoses2d: list[Pose2d] = []
|
210
294
|
processed = False
|
295
|
+
# process each camera
|
211
296
|
for camSim in self.camSimMap.values():
|
297
|
+
# check if this camera is ready to process and get latency
|
212
298
|
optTimestamp = camSim.consumeNextEntryTime()
|
213
299
|
if optTimestamp is None:
|
214
300
|
continue
|
215
301
|
else:
|
216
302
|
processed = True
|
217
303
|
|
304
|
+
# when this result "was" read by NT
|
218
305
|
timestampNt = optTimestamp
|
219
306
|
latency = camSim.prop.estLatency()
|
307
|
+
# the image capture timestamp in seconds of this result
|
220
308
|
timestampCapture = timestampNt * 1.0e-6 - latency
|
221
309
|
|
310
|
+
# use camera pose from the image capture timestamp
|
222
311
|
lateRobotPose = self.getRobotPose(timestampCapture)
|
223
312
|
robotToCamera = self.getRobotToCamera(camSim, timestampCapture)
|
224
313
|
if lateRobotPose is None or robotToCamera is None:
|
@@ -226,8 +315,11 @@ class VisionSystemSim:
|
|
226
315
|
lateCameraPose = lateRobotPose + robotToCamera
|
227
316
|
cameraPoses2d.append(lateCameraPose.toPose2d())
|
228
317
|
|
318
|
+
# process a PhotonPipelineResult with visible targets
|
229
319
|
camResult = camSim.process(latency, lateCameraPose, allTargets)
|
320
|
+
# publish this info to NT at estimated timestamp of receive
|
230
321
|
camSim.submitProcessedFrame(camResult, timestampNt)
|
322
|
+
# display debug results
|
231
323
|
for tgt in camResult.getTargets():
|
232
324
|
trf = tgt.getBestCameraToTarget()
|
233
325
|
if trf == Transform3d():
|
@@ -6,7 +6,16 @@ from ..estimation.targetModel import TargetModel
|
|
6
6
|
|
7
7
|
|
8
8
|
class VisionTargetSim:
|
9
|
+
"""Describes a vision target located somewhere on the field that your vision system can detect."""
|
10
|
+
|
9
11
|
def __init__(self, pose: Pose3d, model: TargetModel, id: int = -1):
|
12
|
+
"""Describes a fiducial tag located somewhere on the field that your vision system can detect.
|
13
|
+
|
14
|
+
:param pose: Pose3d of the tag in field-relative coordinates
|
15
|
+
:param model: TargetModel which describes the shape of the target(tag)
|
16
|
+
:param id: The ID of this fiducial tag
|
17
|
+
"""
|
18
|
+
|
10
19
|
self.pose: Pose3d = pose
|
11
20
|
self.model: TargetModel = model
|
12
21
|
self.fiducialId: int = id
|
@@ -47,4 +56,5 @@ class VisionTargetSim:
|
|
47
56
|
return self.model
|
48
57
|
|
49
58
|
def getFieldVertices(self) -> list[Translation3d]:
|
59
|
+
"""This target's vertices offset from its field pose."""
|
50
60
|
return self.model.getFieldVertices(self.pose)
|
photonlibpy/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
PHOTONLIB_VERSION="v2025.0.0.beta.
|
2
|
-
PHOTONVISION_VERSION="v2025.0.0-beta-
|
1
|
+
PHOTONLIB_VERSION="v2025.0.0.beta.5"
|
2
|
+
PHOTONVISION_VERSION="v2025.0.0-beta-5"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: photonlibpy
|
3
|
-
Version: 2025.0.
|
4
|
-
Summary: Pure-python implementation of PhotonLib for interfacing with PhotonVision on coprocessors. Implemented with PhotonVision version v2025.0.0-beta-
|
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 .
|
5
5
|
Home-page: https://photonvision.org
|
6
6
|
Author: Photonvision Development Team
|
7
7
|
Description-Content-Type: text/markdown
|
@@ -12,6 +12,5 @@ Requires-Dist: robotpy-apriltag<2026,>=2025.0.0b1
|
|
12
12
|
Requires-Dist: robotpy-cscore<2026,>=2025.0.0b1
|
13
13
|
Requires-Dist: pyntcore<2026,>=2025.0.0b1
|
14
14
|
Requires-Dist: opencv-python; platform_machine != "roborio"
|
15
|
-
Requires-Dist: robotpy-opencv; platform_machine == "roborio"
|
16
15
|
|
17
16
|
A Pure-python implementation of PhotonLib
|
@@ -1,36 +1,37 @@
|
|
1
1
|
photonlibpy/__init__.py,sha256=WW1OGrrcNXwwxaHSZlkxmhH2GYiQIHHxSxGVTJZhZbY,1136
|
2
2
|
photonlibpy/estimatedRobotPose.py,sha256=X7wF9xdPXGKSVy0MY0qrWZJOEbuZPd721lYp0KXKlP0,1603
|
3
3
|
photonlibpy/packet.py,sha256=5YomViVFwOljL2FGOetWM9FbPc_yCQ15ylzkYlgLIs8,9724
|
4
|
-
photonlibpy/photonCamera.py,sha256=
|
4
|
+
photonlibpy/photonCamera.py,sha256=ENBHp959it4aLnFtV66rHoAIYCvK4bTflZMrGSCwnZ8,12905
|
5
5
|
photonlibpy/photonPoseEstimator.py,sha256=2iMqxPFsQHTsq95yv-WCSv1a6wXNcHPqOyMc4Bu6IG0,12584
|
6
|
-
photonlibpy/
|
6
|
+
photonlibpy/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
7
|
+
photonlibpy/version.py,sha256=6IuHSryBtyyUy01tGUnTVp4WTudFZoF1qjXkZI10_s8,77
|
7
8
|
photonlibpy/estimation/__init__.py,sha256=pZ-d6fN1DJvT-lRl4FfIos5HAvlzmetIOrGIinrdv7k,223
|
8
9
|
photonlibpy/estimation/cameraTargetRelation.py,sha256=i7DPBXtkZve4ToXQscEIe-5F1oGQ1Qmf5QBaE__EeMQ,1158
|
9
|
-
photonlibpy/estimation/openCVHelp.py,sha256=
|
10
|
-
photonlibpy/estimation/rotTrlTransform3d.py,sha256=
|
11
|
-
photonlibpy/estimation/targetModel.py,sha256=
|
12
|
-
photonlibpy/estimation/visionEstimation.py,sha256=
|
10
|
+
photonlibpy/estimation/openCVHelp.py,sha256=O1dV7v7RHSyw7l5L0QXbal6t9K7iyvEG76tG-t4AFVg,12388
|
11
|
+
photonlibpy/estimation/rotTrlTransform3d.py,sha256=oRYk4V1XF7guNxefJsCPcBWPIkKZl0HYD_Gs86wwToE,2671
|
12
|
+
photonlibpy/estimation/targetModel.py,sha256=fIhkf0-_Sfv-KhYEBnhFWMF4Xu84FfF6B-KUEsuuGjQ,6459
|
13
|
+
photonlibpy/estimation/visionEstimation.py,sha256=Q4KWdLCV1H0wJUJCzG7OYNhVmk2jR1iPUmX_PFylLdA,4174
|
13
14
|
photonlibpy/generated/MultiTargetPNPResultSerde.py,sha256=CzsCosHUnzxAoSC_sSAqpsXsDp54KW-BClnIXdzfMPc,2506
|
14
15
|
photonlibpy/generated/PhotonPipelineMetadataSerde.py,sha256=9pq5XUEDEoRowCd7sRnuGV7xtugu5HZCRrnwqZG2xXc,2887
|
15
|
-
photonlibpy/generated/PhotonPipelineResultSerde.py,sha256=
|
16
|
+
photonlibpy/generated/PhotonPipelineResultSerde.py,sha256=8_YhW1icdhPOx5eAqwKadvnR29NABmhHr6ERAWK8wEo,3115
|
16
17
|
photonlibpy/generated/PhotonTrackedTargetSerde.py,sha256=-6vKir_ABDVBGbg8ktM48IKm_nBMFBbiyuZLiO_fP9U,4437
|
17
18
|
photonlibpy/generated/PnpResultSerde.py,sha256=YoTKdQ51oSdxC-7Poy6hunL0-zkMKvP5uedqaHWPudY,2693
|
18
19
|
photonlibpy/generated/TargetCornerSerde.py,sha256=kziD_rQIwyhzPfgOaDgn-3d87tvtXiAYbBjzu76biYU,2190
|
19
20
|
photonlibpy/generated/__init__.py,sha256=mElM8M88---wxTWO-SRqIJ4EfxN0fdIUwZBZ-UIGuRw,428
|
20
|
-
photonlibpy/networktables/NTTopicSet.py,sha256=
|
21
|
+
photonlibpy/networktables/NTTopicSet.py,sha256=TXJyVg6S8gONeuRqX_NI0FdxYw51bNLlFIAQ4Y2wyVg,3104
|
21
22
|
photonlibpy/networktables/__init__.py,sha256=o_LxTdyIylAszMy_zhUtTkXHyu6jqxccccj78d44OrI,35
|
22
23
|
photonlibpy/simulation/__init__.py,sha256=HKJV02of5d8bOnuI7syLzSYtOYge7XUrHSaLvawh99M,227
|
23
|
-
photonlibpy/simulation/photonCameraSim.py,sha256=
|
24
|
-
photonlibpy/simulation/simCameraProperties.py,sha256=
|
24
|
+
photonlibpy/simulation/photonCameraSim.py,sha256=5GTPdU7SRAahhtXYHcbm0GJEzARWA7lyBG3v279pqGk,19891
|
25
|
+
photonlibpy/simulation/simCameraProperties.py,sha256=zh6dZ5SeVvW6qxLDhlMm09o-uScQf-20b_r4K3lutjs,27119
|
25
26
|
photonlibpy/simulation/videoSimUtil.py,sha256=xMuTvJ2Jx9IoQqmAJi_zUm06MdEwhVpIz9OyzYQp0k4,29
|
26
|
-
photonlibpy/simulation/visionSystemSim.py,sha256=
|
27
|
-
photonlibpy/simulation/visionTargetSim.py,sha256=
|
27
|
+
photonlibpy/simulation/visionSystemSim.py,sha256=ln1TYVMXUreg6nxrVZJ7lw8puvgLbBBde_ciM2CH5G4,13788
|
28
|
+
photonlibpy/simulation/visionTargetSim.py,sha256=FH85fKE4NntowUvssfgZ1KlE-I_3Z-QuAgb2bFqvfdY,2219
|
28
29
|
photonlibpy/targeting/TargetCorner.py,sha256=ouKj3E5uD76OZSNHHuSDzKOY65a8HqtcOsuejH-MVsU,276
|
29
30
|
photonlibpy/targeting/__init__.py,sha256=YzINSpq6A0cjr-yAQcFqHoiYdLGKPFXThlVYlMjY11w,295
|
30
31
|
photonlibpy/targeting/multiTargetPNPResult.py,sha256=Y9rweHtMzoCZ6mv6F8CutQi2Thq5pHN0ydBWvTCsOwY,806
|
31
32
|
photonlibpy/targeting/photonPipelineResult.py,sha256=MbaSyHZTJpoKTtLOZztpSGSt9xWWFqhzgwj8medObVA,2732
|
32
33
|
photonlibpy/targeting/photonTrackedTarget.py,sha256=zCoFp32hX-3GmBYEmsYBQieBoMzXtP2F_55_q0zPOXA,1956
|
33
|
-
photonlibpy-2025.0.
|
34
|
-
photonlibpy-2025.0.
|
35
|
-
photonlibpy-2025.0.
|
36
|
-
photonlibpy-2025.0.
|
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,,
|
File without changes
|
File without changes
|