Struct GpsSensor

Source
pub struct GpsSensor { /* private fields */ }
Expand description

A GPS sensor plugged into a Smart Port.

Implementations§

Source§

impl GpsSensor

Source

pub const MAX_HEADING: f64 = 360f64

The maximum value that can be returned by Self::heading.

Source

pub fn new( port: SmartPort, offset: impl Into<Point2<f64>>, initial_position: impl Into<Point2<f64>>, initial_heading: f64, ) -> Self

Creates a new GPS sensor from a SmartPort.

§Sensor Configuration

The sensor requires three measurements to be made at the start of a match, passed as arguments to this function:

§Sensor Offset

offset is the physical offset of the sensor’s mounting location from a reference point on the robot.

Offset defines the exact point on the robot that is considered a “source of truth” for the robot’s position. For example, if you considered the center of your robot to be the reference point for coordinates, then this value would be the signed 4-quadrant x and y offset from that point on your robot in meters. Similarly, if you considered the sensor itself to be the robot’s origin of tracking, then this value would simply be Point2 { x: 0.0, y: 0.0 }.

§Initial Robot Position

initial_position is an estimate of the robot’s initial cartesian coordinates on the field in meters. This value helpful for cases when the robot’s starting point is near a field wall.

When the GPS Sensor is too close to a field wall to properly read the GPS strips, the sensor will be unable to localize the robot’s position due the wall’s proximity limiting the view of the camera. This can cause the sensor inaccurate results at the start of a match, where robots often start directly near a wall.

By providing an estimate of the robot’s initial position on the field, this problem is partially mitigated by giving the sensor an initial frame of reference to use.

§Initial Robot Heading

initial_heading is a value between 0 and 360 degrees that informs the GPS of its heading at the start of the match. Similar to initial_position, this is useful for improving accuracy when the sensor is in close proximity to a field wall, as the sensor’s rotation values are continiously checked against the GPS field strips to prevent drift over time. If the sensor starts too close to a field wall, providing an initial_haeding can help prevent this drift at the start of the match.

§Examples
use vexide::prelude::*;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Create a GPS sensor mounted 2 inches forward and 1 inch right of center
    // Starting at position (0, 0) with 90 degree heading
    let gps = GpsSensor::new(
        // Port 1
        peripherals.port_1,

        // Sensor is mounted 0.225 meters to the left and 0.225 meters above the robot's tracking origin.
        Point2 { x: -0.225, y: 0.225 },

        // Robot's starting point is at the center of the field.
        Point2 { x: 0.0, y: 0.0 },

        // Robot is facing to the right initially.
        90.0,
    );
}
Source

pub fn offset(&self) -> Result<Point2<f64>, PortError>

Returns the user-configured offset from a reference point on the robot.

This offset value is passed to GpsSensor::new and can be changed using GpsSensor::set_offset.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    let mut gps = GpsSensor::new(
        peripherals.port_1,

        // Initial offset value is configured here!
        //
        // Let's assume that the sensor is mounted 0.225 meters to the left and 0.225 meters above
        // our desired tracking origin.
        Point2 { x: -0.225, y: 0.225 }, // Configure offset value
        Point2 { x: 0.0, y: 0.0 },
        90.0,
    );

    // Get the configured offset of the sensor
    if let Ok(offset) = gps.offset() {
        println!("GPS sensor is mounted at x={}, y={}", offset.x, offset.y); // "Sensor is mounted at x=-0.225, y=0.225"
    }

    // Change the offset to something new
    _ = gps.set_offset(Point2 { x: 0.0, y: 0.0 });

    // Get the configured offset of the sensor again
    if let Ok(offset) = gps.offset() {
        println!("GPS sensor is mounted at x={}, y={}", offset.x, offset.y); // "Sensor is mounted at x=0.0, y=0.0"
    }
}
Source

pub fn set_offset(&mut self, offset: Point2<f64>) -> Result<(), PortError>

Adjusts the sensor’s physical offset from the robot’s tracking origin.

This value is also configured initially through GpsSensor::new.

Offset defines the exact point on the robot that is considered a “source of truth” for the robot’s position. For example, if you considered the center of your robot to be the reference point for coordinates, then this value would be the signed 4-quadrant x and y offset from that point on your robot in meters. Similarly, if you considered the sensor itself to be the robot’s origin of tracking, then this value would simply be Point2 { x: 0.0, y: 0.0 }.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    let mut gps = GpsSensor::new(
        peripherals.port_1,

        // Initial offset value is configured here!
        //
        // Let's assume that the sensor is mounted 0.225 meters to the left and 0.225 meters above
        // our desired tracking origin.
        Point2 { x: -0.225, y: 0.225 }, // Configure offset value
        Point2 { x: 0.0, y: 0.0 },
        90.0,
    );

    // Get the configured offset of the sensor
    if let Ok(offset) = gps.offset() {
        println!("GPS sensor is mounted at x={}, y={}", offset.x, offset.y); // "Sensor is mounted at x=-0.225, y=0.225"
    }

    // Change the offset to something new
    _ = gps.set_offset(Point2 { x: 0.0, y: 0.0 });

    // Get the configured offset of the sensor again
    if let Ok(offset) = gps.offset() {
        println!("GPS sensor is mounted at x={}, y={}", offset.x, offset.y); // "Sensor is mounted at x=0.0, y=0.0"
    }
}
Source

pub fn position(&self) -> Result<Point2<f64>, PortError>

Returns an estimate of the robot’s location on the field as cartesian coordinates measured in meters.

The reference point for a robot’s position is determined by the sensor’s configured offset value.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Get current position and heading
    if let Ok(position) = gps.position() {
        println!(
            "Robot is at x={}, y={}",
            position.x,
            position.y,
        );
    }
}
Source

pub fn error(&self) -> Result<f64, PortError>

Returns the RMS (Root Mean Squared) error for the sensor’s position reading in meters.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Check position accuracy
    if gps.error().is_ok_and(|err| err > 0.3) {
        println!("Warning: GPS position accuracy is low ({}m error)", error);
    }
}
Source

pub fn status(&self) -> Result<u32, PortError>

Returns the internal status code of the sensor.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    if let Ok(status) = gps.status() {
        println!("Status: {:b}", status);
    }
}
Source

pub fn heading(&self) -> Result<f64, PortError>

Returns the sensor’s yaw angle bounded by [0.0, 360.0) degrees.

Clockwise rotations are represented with positive degree values, while counterclockwise rotations are represented with negative ones. If a heading offset has not been set using GpsSensor::set_heading, then 90 degrees will located to the right of the field.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    if let Ok(heading) = gps.heading() {
        println!("Heading is {} degrees.", rotation);
    }
}
Source

pub fn rotation(&self) -> Result<f64, PortError>

Returns the total number of degrees the GPS has spun about the z-axis.

This value is theoretically unbounded. Clockwise rotations are represented with positive degree values, while counterclockwise rotations are represented with negative ones. If a heading offset has not been set using GpsSensor::set_rotation, then 90 degrees will located to the right of the field.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    if let Ok(rotation) = gps.rotation() {
        println!("Robot has rotated {} degrees since calibration.", rotation);
    }
}
Source

pub fn euler(&self) -> Result<EulerAngles<f64, f64>, PortError>

Returns the Euler angles (pitch, yaw, roll) representing the GPS’s orientation.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    if let Ok(angles) = gps.euler() {
        println!(
            "yaw: {}°, pitch: {}°, roll: {}°",
            angles.a.to_degrees(),
            angles.b.to_degrees(),
            angles.c.to_degrees(),
        );
    }
}
Source

pub fn quaternion(&self) -> Result<Quaternion<f64>, PortError>

Returns a quaternion representing the sensor’s orientation.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    if let Ok(quaternion) = gps.quaternion() {
        println!(
            "x: {}, y: {}, z: {}, scalar: {}",
            quaternion.v.x,
            quaternion.v.y,
            quaternion.v.z,
            quaternion.s,
        );
    }
}
Source

pub fn acceleration(&self) -> Result<Vector3<f64>, PortError>

Returns raw accelerometer values of the sensor’s internal IMU.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Read out accleration values every 10mS
    loop {
        if let Ok(acceleration) = gps.acceleration() {
            println!(
                "x: {}G, y: {}G, z: {}G",
                acceleration.x,
                acceleration.y,
                acceleration.z,
            );
        }

        sleep(Duration::from_millis(10)).await;
    }
}
Source

pub fn gyro_rate(&self) -> Result<Vector3<f64>, PortError>

Returns the raw gyroscope values of the sensor’s internal IMU.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Read out angular velocity values every 10mS
    loop {
        if let Ok(rates) = gps.gyro_rate() {
            println!(
                "x: {}°/s, y: {}°/s, z: {}°/s",
                rates.x,
                rates.y,
                rates.z,
            );
        }

        sleep(Duration::from_millis(10)).await;
    }
}
Source

pub fn reset_heading(&mut self) -> Result<(), PortError>

Offsets the reading of GpsSensor::heading to zero.

This method has no effect on the values returned by GpsSensor::position.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let mut gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Sleep for two seconds to allow the robot to be moved.
    sleep(Duration::from_secs(2)).await;

    // Store heading before reset.
    let heading = gps.heading().unwrap_or_default();

    // Reset heading back to zero.
    _ = gps.reset_heading();
}
Source

pub fn reset_rotation(&mut self) -> Result<(), PortError>

Offsets the reading of GpsSensor::rotation to zero.

This method has no effect on the values returned by GpsSensor::position.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let mut gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Sleep for two seconds to allow the robot to be moved.
    sleep(Duration::from_secs(2)).await;

    // Store rotation before reset.
    let rotation = gps.rotation().unwrap_or_default();

    // Reset rotation back to zero.
    _ = gps.reset_rotation();
}
Source

pub fn set_rotation(&mut self, rotation: f64) -> Result<(), PortError>

Offsets the reading of GpsSensor::rotation to a specified angle value.

This method has no effect on the values returned by GpsSensor::position.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let mut gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Set rotation to 90 degrees clockwise.
    _ = gps.set_rotation(90.0);

    println!("Rotation: {:?}", gps.rotation());
}
Source

pub fn set_heading(&mut self, heading: f64) -> Result<(), PortError>

Offsets the reading of GpsSensor::heading to a specified angle value.

Target will default to 360.0 if above 360.0 and default to 0.0 if below 0.0.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    // Assume we're starting in the middle of the field facing upwards, with the
    // sensor's mounting point being our reference for position.
    let mut gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Set heading to 90 degrees clockwise.
    _ = gps.set_heading(90.0);

    println!("Heading: {:?}", gps.heading());
}
Source

pub fn set_data_interval(&mut self, interval: Duration) -> Result<(), PortError>

Sets the internal computation speed of the sensor’s internal IMU.

This method does NOT change the rate at which user code can read data off the GPS, as the brain will only talk to the device every 10mS regardless of how fast data is being sent or computed. This also has no effect on the speed of methods such as GpsSensor::position, as it only changes the internal computation speed of the sensor’s internal IMU.

§Errors

An error is returned if a GPS sensor is not currently connected to the Smart Port.

§Examples
use vexide::prelude::*;
use core::time::Duration;

#[vexide::main]
async fn main(peripherals: Peripherals) {
    let mut gps = GpsSensor::new(
        peripherals.port_1,
        Point2 { x: 0.0, y: 0.0 },
        Point2 { x: 0.0, y: 0.0 },
        0.0,
    );

    // Set to minimum interval.
    _ = gps.set_data_interval(Duration::from_millis(5));
}

Trait Implementations§

Source§

impl Debug for GpsSensor

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<GpsSensor> for SmartPort

Source§

fn from(device: GpsSensor) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for GpsSensor

Source§

fn eq(&self, other: &GpsSensor) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

const fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl SmartDevice for GpsSensor

Source§

fn port_number(&self) -> u8

Returns the port number of the SmartPort this device is registered on. Read more
Source§

fn device_type(&self) -> SmartDeviceType

Returns the variant of SmartDeviceType that this device is associated with. Read more
Source§

const UPDATE_INTERVAL: Duration = _

The interval at which the V5 brain reads packets from Smart devices.
Source§

fn is_connected(&self) -> bool

Determine if this device type is currently connected to the SmartPort that it’s registered to. Read more
Source§

fn timestamp(&self) -> Result<SmartDeviceTimestamp, PortError>

Returns the timestamp recorded by this device’s internal clock. Read more
Source§

fn validate_port(&self) -> Result<(), PortError>

Verify that the device type is currently plugged into this port, returning an appropriate PortError if not available. Read more
Source§

impl Send for GpsSensor

Source§

impl StructuralPartialEq for GpsSensor

Source§

impl Sync for GpsSensor

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.