mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-26 01:51:40 +00:00
Auto-generate packet dataclasses with Jinja (#1374)
This commit is contained in:
9
photon-lib/py/photonlibpy/targeting/TargetCorner.py
Normal file
9
photon-lib/py/photonlibpy/targeting/TargetCorner.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class TargetCorner:
|
||||
x: float = 0
|
||||
y: float = 9
|
||||
|
||||
photonStruct: "TargetCornerSerde" = None
|
||||
6
photon-lib/py/photonlibpy/targeting/__init__.py
Normal file
6
photon-lib/py/photonlibpy/targeting/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# no one but us chickens
|
||||
|
||||
from .TargetCorner import TargetCorner # noqa
|
||||
from .multiTargetPNPResult import MultiTargetPNPResult, PnpResult # noqa
|
||||
from .photonPipelineResult import PhotonPipelineMetadata, PhotonPipelineResult # noqa
|
||||
from .photonTrackedTarget import PhotonTrackedTarget # noqa
|
||||
34
photon-lib/py/photonlibpy/targeting/multiTargetPNPResult.py
Normal file
34
photon-lib/py/photonlibpy/targeting/multiTargetPNPResult.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from dataclasses import dataclass, field
|
||||
from wpimath.geometry import Transform3d
|
||||
from ..packet import Packet
|
||||
|
||||
|
||||
@dataclass
|
||||
class PnpResult:
|
||||
best: Transform3d = field(default_factory=Transform3d)
|
||||
alt: Transform3d = field(default_factory=Transform3d)
|
||||
ambiguity: float = 0.0
|
||||
bestReprojError: float = 0.0
|
||||
altReprojError: float = 0.0
|
||||
|
||||
photonStruct: "PNPResultSerde" = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class MultiTargetPNPResult:
|
||||
_MAX_IDS = 32
|
||||
|
||||
estimatedPose: PnpResult = field(default_factory=PnpResult)
|
||||
fiducialIDsUsed: list[int] = field(default_factory=list)
|
||||
|
||||
def createFromPacket(self, packet: Packet) -> Packet:
|
||||
self.estimatedPose = PnpResult()
|
||||
self.estimatedPose.createFromPacket(packet)
|
||||
self.fiducialIDsUsed = []
|
||||
for _ in range(MultiTargetPNPResult._MAX_IDS):
|
||||
fidId = packet.decode16()
|
||||
if fidId >= 0:
|
||||
self.fiducialIDsUsed.append(fidId)
|
||||
return packet
|
||||
|
||||
photonStruct: "MultiTargetPNPResultSerde" = None
|
||||
65
photon-lib/py/photonlibpy/targeting/photonPipelineResult.py
Normal file
65
photon-lib/py/photonlibpy/targeting/photonPipelineResult.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
from .multiTargetPNPResult import MultiTargetPNPResult
|
||||
from .photonTrackedTarget import PhotonTrackedTarget
|
||||
|
||||
|
||||
@dataclass
|
||||
class PhotonPipelineMetadata:
|
||||
# Image capture and NT publish timestamp, in microseconds and in the coprocessor timebase. As
|
||||
# reported by WPIUtilJNI::now.
|
||||
captureTimestampMicros: int = -1
|
||||
publishTimestampMicros: int = -1
|
||||
|
||||
# Mirror of the heartbeat entry -- monotonically increasing
|
||||
sequenceID: int = -1
|
||||
|
||||
photonStruct: "PhotonPipelineMetadataSerde" = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class PhotonPipelineResult:
|
||||
# Since we don't trust NT time sync, keep track of when we got this packet into robot code
|
||||
ntReceiveTimestampMicros: int = -1
|
||||
|
||||
targets: list[PhotonTrackedTarget] = field(default_factory=list)
|
||||
metadata: PhotonPipelineMetadata = field(default_factory=PhotonPipelineMetadata)
|
||||
multiTagResult: Optional[MultiTargetPNPResult] = None
|
||||
|
||||
def getLatencyMillis(self) -> float:
|
||||
return (
|
||||
self.metadata.publishTimestampMicros - self.metadata.captureTimestampMicros
|
||||
) / 1e3
|
||||
|
||||
def getTimestampSeconds(self) -> float:
|
||||
"""
|
||||
Returns the estimated time the frame was taken, in the Received system's time base. This is
|
||||
calculated as (NT Receive time (robot base) - (publish timestamp, coproc timebase - capture
|
||||
timestamp, coproc timebase))
|
||||
"""
|
||||
# TODO - we don't trust NT4 to correctly latency-compensate ntReceiveTimestampMicros
|
||||
return (
|
||||
self.ntReceiveTimestampMicros
|
||||
- (
|
||||
self.metadata.publishTimestampMicros
|
||||
- self.metadata.captureTimestampMicros
|
||||
)
|
||||
) / 1e6
|
||||
|
||||
def getTargets(self) -> list[PhotonTrackedTarget]:
|
||||
return self.targets
|
||||
|
||||
def hasTargets(self) -> bool:
|
||||
return len(self.targets) > 0
|
||||
|
||||
def getBestTarget(self) -> PhotonTrackedTarget:
|
||||
"""
|
||||
Returns the best target in this pipeline result. If there are no targets, this method will
|
||||
return null. The best target is determined by the target sort mode in the PhotonVision UI.
|
||||
"""
|
||||
if not self.hasTargets():
|
||||
return None
|
||||
return self.getTargets()[0]
|
||||
|
||||
photonStruct: "PhotonPipelineResultSerde" = None
|
||||
58
photon-lib/py/photonlibpy/targeting/photonTrackedTarget.py
Normal file
58
photon-lib/py/photonlibpy/targeting/photonTrackedTarget.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from dataclasses import dataclass, field
|
||||
from wpimath.geometry import Transform3d
|
||||
from ..packet import Packet
|
||||
from .TargetCorner import TargetCorner
|
||||
|
||||
|
||||
@dataclass
|
||||
class PhotonTrackedTarget:
|
||||
yaw: float = 0.0
|
||||
pitch: float = 0.0
|
||||
area: float = 0.0
|
||||
skew: float = 0.0
|
||||
fiducialId: int = -1
|
||||
bestCameraToTarget: Transform3d = field(default_factory=Transform3d)
|
||||
altCameraToTarget: Transform3d = field(default_factory=Transform3d)
|
||||
minAreaRectCorners: list[TargetCorner] | None = None
|
||||
detectedCorners: list[TargetCorner] | None = None
|
||||
poseAmbiguity: float = 0.0
|
||||
|
||||
def getYaw(self) -> float:
|
||||
return self.yaw
|
||||
|
||||
def getPitch(self) -> float:
|
||||
return self.pitch
|
||||
|
||||
def getArea(self) -> float:
|
||||
return self.area
|
||||
|
||||
def getSkew(self) -> float:
|
||||
return self.skew
|
||||
|
||||
def getFiducialId(self) -> int:
|
||||
return self.fiducialId
|
||||
|
||||
def getPoseAmbiguity(self) -> float:
|
||||
return self.poseAmbiguity
|
||||
|
||||
def getMinAreaRectCorners(self) -> list[TargetCorner] | None:
|
||||
return self.minAreaRectCorners
|
||||
|
||||
def getDetectedCorners(self) -> list[TargetCorner] | None:
|
||||
return self.detectedCorners
|
||||
|
||||
def getBestCameraToTarget(self) -> Transform3d:
|
||||
return self.bestCameraToTarget
|
||||
|
||||
def getAlternateCameraToTarget(self) -> Transform3d:
|
||||
return self.altCameraToTarget
|
||||
|
||||
def _decodeTargetList(self, packet: Packet, numTargets: int) -> list[TargetCorner]:
|
||||
retList = []
|
||||
for _ in range(numTargets):
|
||||
cx = packet.decodeDouble()
|
||||
cy = packet.decodeDouble()
|
||||
retList.append(TargetCorner(cx, cy))
|
||||
return retList
|
||||
|
||||
photonStruct: "PhotonTrackedTargetSerde" = None
|
||||
Reference in New Issue
Block a user