natnet 0.1.0

OptiTrack NatNet protocol
Documentation
//! OptiTrack NatNet protocol
//!
//! ```
//! # fn main() -> natnet::Result<()> {
//! use natnet::{Decoder, Message, PlannedBody, SyntheticServer, Version};
//!
//! let server = SyntheticServer::new(Version::V4_1, 1_000_000);
//! let body = PlannedBody::tracked(7, [1.0, 2.0, 3.0], [0.0, 0.0, 0.0, 1.0]);
//! let wire = server.datagram(42, 0.5, &[body])?;
//! let decoded = Decoder::new(Version::V4_1)?.parse(&wire)?;
//!
//! let Message::FrameOfData(frame) = decoded.message() else {
//!     panic!("expected a frame");
//! };
//! assert_eq!(frame.frame_number, 42);
//! assert_eq!(frame.rigid_bodies[0].id, 7);
//! assert_eq!(decoded.to_datagram()?, wire);
//! # Ok(())
//! # }
//! ```
//!
//! # Where to start
//!
//! 1. With `net`, call `net::CommandClient::connect_handshake` to get
//!    [`ServerInfo::protocol_version`]. The `examples/probe.rs` example shows
//!    the full exchange.
//! 2. Build a [`Decoder`] for that version and [`parse`](Decoder::parse) each
//!    datagram; inspect [`Message`] for [`ModelDef`] or [`FrameOfData`].
//! 3. Read the [`command`] API and the `net` module docs for requests and socket
//!    clients.
//!
//! See [`Decoder`] and [`ParsePolicy`] for parsing policies; encoder limits are
//! documented on each method's `# Errors` section.
//!
//! See [frame round-trip and float equality](crate::frame#round-trip-and-float-equality)
//! for preservation limits, and [`Version`] for supported layouts. Poses are
//! plain arrays in the server's frame.
//!
//! # Features
//!
//! - `net`: std UDP clients. Peer filtering is not authentication.
//! - `imagery`: the AUGI/1 codec (not part of NatNet); its TCP
//!   `imagery::net::ImageClient` requires both `imagery` and `net`.
//! - `serde`: derives on value types.
//! - `nalgebra`: one-way conversions from plain arrays.

#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code)]
#![deny(missing_docs)]

mod bytes;
pub mod command;
mod decoder;
mod error;
pub mod frame;
pub mod generate;
mod message;
pub mod modeldef;
pub mod serverinfo;
mod version;

#[cfg(feature = "imagery")]
pub mod imagery;

#[cfg(feature = "nalgebra")]
mod nalgebra_conv;

#[cfg(feature = "net")]
pub mod net;

pub use decoder::{DecodedMessage, Decoder};
pub use error::{Error, Result};
pub use frame::{
    Asset, Device, ForcePlate, FrameOfData, FrameTiming, LabeledMarker, MarkerSet, RawSection,
    RigidBody, Skeleton,
};
pub use generate::{PlannedBody, PlannedSections, SyntheticServer};
pub use message::{
    HEADER_LEN, MessageHeader, NAT_CONNECT, NAT_DISCONNECT, NAT_DISCONNECTBYTIMEOUT, NAT_DISCOVERY,
    NAT_ECHOREQUEST, NAT_ECHORESPONSE, NAT_FRAMEOFDATA, NAT_KEEPALIVE, NAT_MESSAGESTRING,
    NAT_MODELDEF, NAT_REQUEST, NAT_REQUEST_FRAMEOFDATA, NAT_REQUEST_MODELDEF, NAT_RESPONSE,
    NAT_SERVERINFO, NAT_UNRECOGNIZED_REQUEST, parse_message_string,
};
pub use modeldef::{
    AssetDef, CameraDef, DataSet, DeviceDef, ForcePlateDef, MarkerDef, MarkerSetDef, ModelDef,
    RigidBodyDef, SkeletonDef,
};
pub use serverinfo::{ConnectionInfo, ServerInfo};
pub use version::{ParsePolicy, Version};

#[cfg(feature = "nalgebra")]
pub use nalgebra_conv::marker_vector;

/// A decoded NatNet message — the ones this crate understands, plus a
/// verbatim fallback for any other message id.
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Message {
    /// SERVERINFO (id 1).
    ServerInfo(ServerInfo),
    /// MODELDEF (id 5).
    ModelDef(ModelDef),
    /// FrameOfData (id 7). Boxed: a frame is far larger than the other
    /// variants, so boxing keeps [`Message`] small to move around.
    FrameOfData(Box<FrameOfData>),
    /// Any other message id, payload preserved verbatim.
    Other {
        /// The message id from the header.
        id: u16,
        /// The payload bytes (after the 4-byte header).
        payload: Vec<u8>,
    },
}

#[cfg(doctest)]
#[doc = include_str!("../README.md")]
struct ReadmeDoctests;