Skip to main content

lib/air_play/ntp.ex

defmodule AirPlay.Ntp do
  @moduledoc """
  NTP 64-bit timestamps for the AirPlay/RAOP timing channel.

  An NTP timestamp is `seconds-since-1900 (32 bits) . fraction (32 bits)`. The
  RAOP timing packets (`0xd2`/`0xd3`) carry these so the receiver can align its
  clock to ours.
  """

  import Bitwise

  # Seconds between the NTP epoch (1900-01-01) and the Unix epoch (1970-01-01).
  @ntp_unix_offset 2_208_988_800

  @doc "Current time as a 64-bit NTP timestamp (integer)."
  @spec now() :: non_neg_integer()
  def now, do: from_unix_ns(System.os_time(:nanosecond))

  @doc "Convert Unix nanoseconds to a 64-bit NTP timestamp."
  @spec from_unix_ns(integer()) :: non_neg_integer()
  def from_unix_ns(ns) do
    secs = div(ns, 1_000_000_000) + @ntp_unix_offset
    frac = (rem(ns, 1_000_000_000) * (1 <<< 32)) |> div(1_000_000_000)
    secs * (1 <<< 32) + frac
  end
end