units icon indicating copy to clipboard operation
units copied to clipboard

Go library that makes it easy to work with physical types, e.g. distances, velocities and angles.

Build Status GoDoc

Units

Use at your own risk

Note that this library is NOT production ready. If you want to use it anyway, contributions and bug reports are welcome!

Breaking changes!

This branch of the units library is introduces breaking changes to the API. To use the old API, please checkout tag v0.0.1.

To instead use the new API, see this short migration guide for more information.

TODO

  • [x] Code generation for sub-packages
  • [x] Sub-package for angle, angular velocity and acceleration
  • [x] Sub-package for linear distance, velocity and acceleration
  • [x] Sub-package for planar coordinates based on distance and angle
  • [ ] Support for volume, volumetric flow rate (velocity) and acceleration
  • [ ] Support for mass, mass flow rate (velocity) and acceleration
  • [ ] Support for force, work and torque
  • [x] Metric units
  • [x] Modern Imperial units
  • [ ] Appropriate test coverage per package.

About

Units is a small Go package that utilizes Go's strict type system to achieve better type safety when storing and doing simple mathematical operations on physical units. The concept is very similar to that of the time.Duration type in the Go standard library.

It currently has the following sub-packages:

  • angular defines the types:
    • Angle
    • Velocity
    • Acceleration
  • linear define the types:
    • Distance
    • Velocity
    • Acceleration
  • planar defines the types:
    • Point
    • Area

Each type has defined constants for relevant units. In general, you don't need to worry about which unit the underlying values is stored as, as long as you always multiply with a relevant uint when you first set your value. E.g.:

var a angular.Angle = 90 * angular.Degrees

Why would I want to use the units package?

The Mars Climate Orbiter crashed into Mars when software producing the non-SI units of pound-seconds was used to generate input to a program expecting newton-seconds (N s). While the units package would not have prevented that particular issue of communication between programs, the goal is that it will help you to avoid unit related mistakes within your own program, and to make working with various units as straight-forward and painless as possible.

Our experience

We found an earlier version of this code surprisingly useful back in 2012, when we where developing an autonomous robot using Go for the high-level programming.

While we still used float64 values in the matrices that calculated the next system states, we found using the units package for the input and output values of various blocks extremely useful. This package helped us verify that we never did something stupid like passing a distance to a function that wanted the velocity. The simplicity of using the return value from .Normalize()on an angle, also made it very easy for us to ensure normalized angles for our navigation system.

Of course, we used metric values for both internal and displaying purposes, but the aim is also that this package will help those who wish to provide non-metric units as input or display values for their programs.

Simple example

This example shows that you can divide linear.Distance with a time.Duration (standard library) to get a velocity, as well as use arbitrary units for input and display purposes.

package main

import(
	"fmt"
	"time"
	"github.com/smyrman/units/linear"
)

func main() {
	d := 66 * linear.Feet
	t := 2 * time.Second
	v := d.DivDuration(t)
	fmt.Printf("v = %.2f m/s\n", v.MetersPerSecond())
	fmt.Printf("v = %.2f kmph\n", v.KilometersPerHour())
	fmt.Printf("v = %.2f mph\n", v.MilesPerHour())
}

See the godoc reference docs for a full list of methods per type.

Origin

The units package was originally developed for use in the robot Loke (eng:Loki), that participated in the Eurobot competition in France in 2012.

Migrating from v0.0.1

Since v0.0.1, the package layout has been completely changed. Type and constant definitions have been moved into sub-packages to allow for shorter names, and a more restricted scope from which package symmetry can arise.

To migrate, search and replace the following:

  • units.Distance -> linear.Distance
  • units.Velocity -> linear.Velocity
  • units.Angle -> angular.Angle
  • units.AngularVelocity -> angular.Velocity
  • units.Coordinate -> planar.Point

Then, assure the proper imports, most conveniently through the use of goimports, or manually by replacing the "github.com/smyrman/units" import with the appropriate import(s):

  • "github.com/smyrman/units/linear"
  • "github.com/smyrman/units/angular"
  • "github.com/smyrman/units/planar"