# `BB.Transmission`
[🔗](https://github.com/beam-bots/bb/blob/main/lib/bb/transmission.ex#L5)

Mechanical transmission math: convert quantities between joint-space and
motor-space.

A transmission captures three properties of the linkage between a joint
and its actuator:

- `reduction` — the gear ratio. A reduction of `n` means the actuator
  rotates `n` times for one rotation of the joint, so motor angular
  motion is `n` times the joint motion, and motor torque is `1 / n`
  times the joint torque.
- `offset` — the joint-space value (radians for rotational joints, metres
  for linear joints) corresponding to the actuator's zero position.
  Applies to positions only — velocities and accelerations have no
  offset since they are rates.
- `reversed?` — whether actuator motion is reversed relative to joint
  motion.

All values are floats in SI base units (radians, metres, rad/s, m/s,
N·m, N).

## Equations

Position (joint → motor):       `motor = sign × reduction × (joint − offset)`
Position (motor → joint):       `joint = sign × motor / reduction + offset`
Rate (velocity, acceleration):  `motor = sign × reduction × joint`
Effort (torque, force):         `motor = sign × joint / reduction`

Where `sign = -1` if `reversed?`, otherwise `+1`. Each pair of
apply/unapply is an exact inverse within float precision.

# `t`

```elixir
@type t() :: %{reduction: float(), offset: float(), reversed?: boolean()}
```

# `apply_effort`

```elixir
@spec apply_effort(float(), t()) :: float()
```

Convert a joint-space effort (torque for rotational, force for linear)
into motor-space.

A gear reduction multiplies position and velocity but divides effort:
a 50:1 reduction means the motor needs `1 / 50` of the joint torque.

# `apply_position`

```elixir
@spec apply_position(float(), t()) :: float()
```

Convert a joint-space position into a motor-space position.

# `apply_rate`

```elixir
@spec apply_rate(float(), t()) :: float()
```

Convert a joint-space rate (velocity or acceleration) into motor-space.

No offset is applied since the offset is a constant in position space.

# `apply_to_command`

```elixir
@spec apply_to_command(BB.Message.t(), t() | nil) :: BB.Message.t()
```

Apply a transmission to an actuator command message, returning a new
message with the payload values transformed into motor-space.

Position, velocity, and effort commands are transformed; hold and stop
commands have no values to transform and are returned unchanged.
Trajectory waypoints are transformed pointwise.

When `transmission` is `nil`, the message is returned unchanged.

# `unapply_effort`

```elixir
@spec unapply_effort(float(), t()) :: float()
```

Convert a motor-space effort into a joint-space effort.

# `unapply_position`

```elixir
@spec unapply_position(float(), t()) :: float()
```

Convert a motor-space position into a joint-space position.

# `unapply_rate`

```elixir
@spec unapply_rate(float(), t()) :: float()
```

Convert a motor-space rate into a joint-space rate.

# `unapply_to_payload`

```elixir
@spec unapply_to_payload(BB.Message.t(), t() | nil) :: BB.Message.t()
```

Unapply a transmission to a message published by an actuator, returning a
new message with motor-space values transformed back into joint-space.

`BeginMotion` and `JointState` payloads are supported. `JointState`
messages must be single-joint — the same transmission is applied to every
list element. Other payloads are returned unchanged.

`peak_velocity` and `acceleration` in `BeginMotion` are magnitudes, so
their joint-space values are taken as the absolute value of the unapplied
rate (a reversed transmission flips the sign but does not change the
magnitude).

When `transmission` is `nil`, the message is returned unchanged.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
