Class FakeGoBildaPinpoint
- All Implemented Interfaces:
com.qualcomm.robotcore.hardware.HardwareDevice,com.qualcomm.robotcore.hardware.usb.RobotArmingStateNotifier.Callback,FakeDevice
The public API is the real driver's, unchanged. This class IS a
com.qualcomm.hardware.gobilda.GoBildaPinpointDriver - the class FTC SDK 12.0 ships in its
Hardware artifact - and it overrides none of that driver's public methods. What it replaces is the
one thing a JVM does not have: the device on the other end of the I2C bus. The driver is built on
Firmware, an emulated Pinpoint that answers the driver's register reads and writes the way the
device's registers do. So every public method - the unit conversions, the heading normalisation, the
status decoding, the bulk-read parsing, the CRC check, the "is this reading corrupt" filter - runs the
SDK's own code, and behaves exactly as it does on a robot for the register values the device reports.
A reflection test pins that no public method is overridden.
This differs from FakeOctoQuad in the one place it has to. The OctoQuad's SDK type is an
interface, so its fake implements the interface; the Pinpoint's is a concrete driver, and re-implementing
its ~35 public methods would be re-deriving the SDK's code and hoping to match it. The rest follows
FakeOctoQuad: the same sim-side seam (attachChassisAt(double, double, double) and moveChassisTo(double, double, double)), the
same on-device integrator, the same fault injection, the same fail-loud rule for anything not modelled.
What the emulated device does
- The pods are mounted the way the sim says, and read the way the robot configured. Two
separate things, as on a real robot. The physical mount - where each pod really sits, which
way it really counts, how many ticks per millimetre it really makes - is sim-only, set with
setPhysicalPodMount(double, double, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, double)(default: goBILDA 4-bar pods on the tracking centre, counting up forward and left). The configured mount is what the robot code wrote withsetOffsets,setEncoderDirectionsandsetEncoderResolution.moveChassisTo(double, double, double)turns the chassis's motion into pod counts through the physical mount; the emulated firmware turns those counts back into a pose through the configured one. When the two agree the pose is the chassis's motion; when a robot sets a pod direction, offset or resolution wrong, its pose is wrong the way a real Pinpoint's would be. - Pose is an on-device integrator, as on the real board: each step it takes the change in the
two pod counts and the IMU's turn, and adds the motion they describe onto whatever pose it holds -
the pose last written with
setPosition/setPosX/setPosY/setHeading, or zero afterresetPosAndIMU(). Frame: X forward, Y left, heading CCW-positive; millimetres and radians in the registers, as the driver expects. The heading register is continuous (not wrapped), which is what lets the driver'sgetHeading(UnnormalizedAngleUnit)exist; the driver wraps it forgetHeading(AngleUnit)itself. Within one step the chassis is taken to turn at a steady rate, so the step is an arc, both when making pod counts and when integrating them. - The IMU is ideal except for the yaw scalar, which the device multiplies into every heading change, exactly as it corrects a real IMU - so a scalar other than 1 makes the fake's heading wrong by that factor, as it would a perfect IMU's. PLACEHOLDER: the pod counts are integrated exactly, before they are rounded to the whole numbers the encoder registers report, so the sim adds no count quantisation (a 4-bar pod's is 0.05 mm).
- Velocity is the pose's change over the last
update(double)tick, in the same frame as the pose, mm/s and rad/s. - Status is
READYunless calibrating or forced (forceFault(com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.DeviceStatus)).recalibrateIMU()andresetPosAndIMU()put it inCALIBRATINGforCALIBRATION_SECONDSof sim time. PLACEHOLDER: while calibrating, the pose ignores chassis motion (the device must be still to calibrate; what a real unit does if it is moved then is not documented), but the raw pod counts keep counting, as quadrature counters do.resetPosAndIMU()also zeroes the pose and the velocity. PLACEHOLDER: a new fake is READY at once. A real unit powers upNOT_READYand calibrates first, but a SimLoop scenario starts where robot code starts, long after the robot was switched on. - A written pose is not motion.
setPosition/setPosX/setPosY/setHeadingmove the pose without any velocity, as on the real board, whose velocity comes from the pods and the IMU. - Firmware version decides the bulk-read format, as in the driver: versions 1 and 2 answer the
fixed 40-byte bulk read, 3 and later the configurable one (
setBulkReadScope) and CRC-checked reads (setErrorDetectionType(CRC)). The driver itself refuses what a version cannot do. PLACEHOLDER: firmware 1 and 2 are taken to have no quaternion, pitch or roll registers, so the fake fails loud if one is read. The driver does read them on old firmware when it has not yet learnt the version; what a real v1 or v2 unit answers then is not known.
Getters are not always pure reads here, unlike the other fakes (conventions of
FakeDevice). The real driver reads a register over the bus, on every call and on every firmware,
whenever its getter's register is not in the driver's bulk-read scope. The default scope holds the ten
registers update() refreshes (status, loop time, both encoders, pose, velocity), so
getDeviceID, getDeviceVersion, getYawScalar, getXOffset,
getYOffset, getQuaternion, getPitch and getRoll always read the bus; on
firmware 3 and up, a narrower setBulkReadScope adds every register it leaves out. That is the
SDK's own behaviour, so the fake keeps it; busReadCount() shows it.
Determinism (domain R5): no wall clock, no randomness. The device changes only when something writes
a register, when moveChassisTo(double, double, double) reports motion, or when update(double) ticks it.
-
Nested Class Summary
Nested classes/interfaces inherited from class com.qualcomm.hardware.gobilda.GoBildaPinpointDriver
com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.DeviceStatus, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.ErrorDetectionType, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.GoBildaOdometryPods, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.ReadData, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.RegisterNested classes/interfaces inherited from interface com.qualcomm.robotcore.hardware.HardwareDevice
com.qualcomm.robotcore.hardware.HardwareDevice.Manufacturer -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final doubleHow long an IMU calibration keeps the device inCALIBRATING, seconds of sim time.static final intThe firmware version a new fake reports.static final floatThe goBILDA 4-bar odometry pod's resolution, ticks per millimetre.static final intWhat theDEVICE_IDregister reports.static final intThe device's own loop time, microseconds, as itsLOOP_TIMEregister reports it.Fields inherited from class com.qualcomm.robotcore.hardware.I2cDeviceSynchDevice
deviceClient, deviceClientIsOwned, isInitialized -
Constructor Summary
ConstructorsConstructorDescriptionA Pinpoint at the origin, READY, reportingDEFAULT_FIRMWARE_VERSION.FakeGoBildaPinpoint(int firmwareVersion) A Pinpoint at the origin, READY, reporting the given firmware version. -
Method Summary
Modifier and TypeMethodDescriptionvoidattachChassisAt(double plantXMm, double plantYMm, double plantHeadingRad) Tell the device where the sim's chassis is before any motion is reported, without moving the device's pose.intReturns how many I2C reads the driver has made.voidStop forcing a status; the device reports READY or CALIBRATING again.voidMake the next bulk read (the oneupdate()makes) arrive corrupt, as a bad I2C transfer does: every data byte 0xFF - a status and encoder counts of -1, and NaN for every pose and velocity float - and, on firmware 3, a CRC byte that does not match.protected booleanThe one driver hook overridden.voidforceFault(com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.DeviceStatus status) Make the device report this status, as if it had the fault.voidmoveChassisTo(double plantXMm, double plantYMm, double plantHeadingRad) The chassis is now here, in the drive plant's frame.voidsetPhysicalPodMount(double xPodOffsetMm, double yPodOffsetMm, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection xPodDirection, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection yPodDirection, double podTicksPerMm) Sim only: how the two odometry pods are REALLY mounted on the simulated robot - not what the robot code tells the device (setOffsets,setEncoderDirections,setEncoderResolution), but the truth those settings are supposed to describe.voidupdate(double deltaTime) Advance the device's own clock: finish a calibration that has run its time, and work out the velocity the device reports from the motion since the last tick.Methods inherited from class com.qualcomm.hardware.gobilda.GoBildaPinpointDriver
getDeviceID, getDeviceName, getDeviceStatus, getDeviceVersion, getEncoderX, getEncoderY, getFrequency, getHeading, getHeading, getHeadingVelocity, getLoopTime, getManufacturer, getPitch, getPosition, getPosX, getPosY, getQuaternion, getRoll, getVelX, getVelY, getXOffset, getYawScalar, getYOffset, recalibrateIMU, resetPosAndIMU, setBulkReadScope, setEncoderDirections, setEncoderResolution, setEncoderResolution, setErrorDetectionType, setHeading, setOffsets, setPosition, setPosX, setPosY, setYawScalar, update, updateMethods inherited from class com.qualcomm.robotcore.hardware.I2cDeviceSynchDevice
close, disengage, engage, getConnectionInfo, getDeviceClient, getVersion, initialize, initializeIfNecessary, onModuleStateChange, registerArmingStateCallback, resetDeviceConfigurationForOpMode
-
Field Details
-
DEFAULT_FIRMWARE_VERSION
public static final int DEFAULT_FIRMWARE_VERSIONThe firmware version a new fake reports. SET BY HAND: 3, the first version the SDK 12.0 driver reads with the configurable bulk read and CRC (every later version is read the same way).- See Also:
-
CALIBRATION_SECONDS
public static final double CALIBRATION_SECONDSHow long an IMU calibration keeps the device inCALIBRATING, seconds of sim time. PLACEHOLDER: a quarter second, from the SDK driver's javadoc forrecalibrateIMU()andresetPosAndIMU()("This takes approximately 0.25 seconds"); not measured on a unit.- See Also:
-
LOOP_TIME_MICROSECONDS
public static final int LOOP_TIME_MICROSECONDSThe device's own loop time, microseconds, as itsLOOP_TIMEregister reports it. PLACEHOLDER: 667 us, about 1.5 kHz, inside the range the SDK driver's javadoc forgetLoopTime()calls healthy (it says values commonly below 500 or above 1100 mean something is wrong with the device); not measured on a unit. It must be at least 2, because on firmware 1 and 2 the driver's fixed bulk read treats a loop time of 1 or less as a corrupt read (the configurable read of firmware 3 and up does not check it).- See Also:
-
DEVICE_ID
public static final int DEVICE_IDWhat theDEVICE_IDregister reports. PLACEHOLDER: 1; nothing in the driver checks it.- See Also:
-
DEFAULT_TICKS_PER_MM
public static final float DEFAULT_TICKS_PER_MMThe goBILDA 4-bar odometry pod's resolution, ticks per millimetre. SPEC: the SDK driver's owngoBILDA_4_BAR_PODconstant (19.89436789 ticks/mm). Used twice: as the resolution the device's register holds before anyone sets one (PLACEHOLDER: the driver does not say what a real unit starts with), and as the resolution of the sim's default physical pods (setPhysicalPodMount(double, double, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, double)).- See Also:
-
-
Constructor Details
-
FakeGoBildaPinpoint
public FakeGoBildaPinpoint()A Pinpoint at the origin, READY, reportingDEFAULT_FIRMWARE_VERSION. -
FakeGoBildaPinpoint
public FakeGoBildaPinpoint(int firmwareVersion) A Pinpoint at the origin, READY, reporting the given firmware version.- Parameters:
firmwareVersion- the version theDEVICE_VERSIONregister reports; 1 or 2 selects the fixed bulk read, 3 and up the configurable one; must be positive
-
-
Method Details
-
doInitialize
protected boolean doInitialize()The one driver hook overridden. The real one casts the bus to the REV hub'sLynxI2cDeviceSynchto set its speed to 400 kHz; this bus is not a REV hub and has no speed. Called only byinitialize(), which the SDK runs when the hardware is armed.- Overrides:
doInitializein classcom.qualcomm.hardware.gobilda.GoBildaPinpointDriver
-
attachChassisAt
public void attachChassisAt(double plantXMm, double plantYMm, double plantHeadingRad) Tell the device where the sim's chassis is before any motion is reported, without moving the device's pose. Call it once, when a drive plant is wired to this device, with that plant's current pose.Millimetres, not inches. SimLoop's drive plants report inches, so multiply by 25.4 first:
attachChassisAt(plant.getX() * 25.4, plant.getY() * 25.4, plant.getHeading()). Nothing can catch inches passed here; the robot would simply read a pose 25.4 times too small.Call it again after any teleport of the plant (
setPoseon a drive plant, a scenario placing the robot).moveChassisTo(double, double, double)treats every change as driven motion, so a teleport not announced here reaches the pods as one enormous step - a jump in the pose and a velocity spike that the real driver then refuses as a bad read.Wiring a robot that configures its Pinpoint also means calling
setPhysicalPodMount(double, double, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, double)with the same pod offsets, directions and resolution the robot writes. The default physical mount is pods on the centre at 4-bar resolution, counting forward; a robot that writes its real offsets or resolution against that default drifts while it turns, or reads every distance at the wrong scale, exactly as a real robot whose settings do not match its pods would.- Parameters:
plantXMm- the chassis X in the drive plant's frame (forward at heading 0), millimetresplantYMm- the chassis Y in the drive plant's frame (left at heading 0), millimetresplantHeadingRad- the chassis heading in the drive plant's frame, radians CCW
-
moveChassisTo
public void moveChassisTo(double plantXMm, double plantYMm, double plantHeadingRad) The chassis is now here, in the drive plant's frame. The pods roll through the motion since the last call and count it, through the physical mount (setPhysicalPodMount(double, double, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection, double)); the emulated firmware adds the motion those counts and the IMU describe onto its own pose, as the real board does - so a pose the robot wrote stays written, and the plant and the device do not need to share an origin. While calibrating, the pose ignores the motion; the raw pod counts do not. Millimetres, asattachChassisAt(double, double, double): convert a plant's inches first.The heading may be wrapped or not. The turn since the last call is taken the short way round, into (-pi, pi], so a plant whose heading jumps from +pi to -pi has turned a little, not most of a circle; that means one call must never be asked to report a turn of half a circle or more.
- Parameters:
plantXMm- the chassis X in the drive plant's frame (forward at heading 0), millimetresplantYMm- the chassis Y in the drive plant's frame (left at heading 0), millimetresplantHeadingRad- the chassis heading in the drive plant's frame, radians CCW, wrapped or not
-
setPhysicalPodMount
public void setPhysicalPodMount(double xPodOffsetMm, double yPodOffsetMm, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection xPodDirection, com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.EncoderDirection yPodDirection, double podTicksPerMm) Sim only: how the two odometry pods are REALLY mounted on the simulated robot - not what the robot code tells the device (setOffsets,setEncoderDirections,setEncoderResolution), but the truth those settings are supposed to describe. The pods count the chassis's motion through this mount; the device reads the counts through the configured one. Setting the two differently is how a test shows what a robot with a mis-set Pinpoint would read. It takes effect from the nextmoveChassisTo(double, double, double); counts already made stay.Default: goBILDA 4-bar pods (
DEFAULT_TICKS_PER_MM) on the tracking centre, both counting up in the device's positive direction. PLACEHOLDER: a robot's real pods are rarely on the centre; the default only makes a robot that configures nothing read the chassis exactly, because the device's own offset registers start at zero too.- Parameters:
xPodOffsetMm- how far LEFT of the tracking centre the X (forward-measuring) pod really is, millimetres, right negative - the same sign convention assetOffsetsyPodOffsetMm- how far FORWARD of the tracking centre the Y (strafe-measuring) pod really is, millimetres, backward negative - the same sign convention assetOffsetsxPodDirection-FORWARDif the X pod really counts up as the robot drives forward,REVERSEDif it counts down - that is, the setting the robot has to pass tosetEncoderDirectionsto read it correctlyyPodDirection-FORWARDif the Y pod really counts up as the robot drives left,REVERSEDif it counts downpodTicksPerMm- how many counts both pods really make per millimetre rolled; must be positive
-
update
public void update(double deltaTime) Advance the device's own clock: finish a calibration that has run its time, and work out the velocity the device reports from the motion since the last tick.Not the driver's
update(), which reads the device; this is the device itself running. A scenario calls this (orFakeHardwareMap.updateAll) once per tick, then the robot code callsupdate()as it would on the robot. The two share a name because every SimLoop fake's tick isFakeDevice.update(double); a scenario that calls only one of them sees stale readings, exactly as a robot that never callsupdate()does.- Specified by:
updatein interfaceFakeDevice- Parameters:
deltaTime- the tick, seconds
-
forceFault
public void forceFault(com.qualcomm.hardware.gobilda.GoBildaPinpointDriver.DeviceStatus status) Make the device report this status, as if it had the fault. It stays untilclearFault().Not
FAULT_BAD_READ: the device never reports that. It is the status the SDK driver sets itself when it catches a corrupt read (a failed CRC, a NaN, an impossible jump), so a test that wants it usescorruptNextBulkRead()and lets the real driver catch it.- Parameters:
status- whatgetDeviceStatus()should decode to after the next read- Throws:
IllegalArgumentException- forFAULT_BAD_READ
-
corruptNextBulkRead
public void corruptNextBulkRead()Make the next bulk read (the oneupdate()makes) arrive corrupt, as a bad I2C transfer does: every data byte 0xFF - a status and encoder counts of -1, and NaN for every pose and velocity float - and, on firmware 3, a CRC byte that does not match. The real driver's own checks then decide what happens: with CRC detection it discards the whole read and setsFAULT_BAD_READ. With its default local test and a scope that holds pose or velocity registers (the default scope does, and the fixed read of firmware 1 and 2 always does), it refuses the NaN floats, keeps their last good values, lets the integer registers it does not check through, and setsFAULT_BAD_READ. With the local test and a scope of integer registers only, the driver checks nothing and the garbage gets through with no fault; so it does with detectionNONE. PLACEHOLDER: all-0xFF is one kind of corrupt read, chosen because the driver's checks catch it in the default setup; a real bus fault can look like anything. -
clearFault
public void clearFault()Stop forcing a status; the device reports READY or CALIBRATING again. -
busReadCount
public int busReadCount()Returns how many I2C reads the driver has made. Real hardware has no such counter; it exists so a test can pin, say, one bulk read perupdate().- Returns:
- how many I2C reads the driver has made
-