Packages

Elixir ephemeris library backed by NAIF CSPICE and JPL kernels.

Current section

Files

Jump to
angelus lib mix tasks ephemeride.ex
Raw

lib/mix/tasks/ephemeride.ex

defmodule Mix.Tasks.Angelus.Ephemeride do
@moduledoc "Prints an Angelus ephemeris for one ISO 8601 instant."
use Mix.Task
@names %{
sun: "Sun",
moon: "Moon",
mercury: "Mercury",
venus: "Venus",
mars: "Mars",
jupiter: "Jupiter",
saturn: "Saturn",
uranus: "Uranus",
neptune: "Neptune",
pluto: "Pluto",
north_node: "North Node",
south_node: "South Node",
lilith: "Lilith",
chiron: "Chiron"
}
@impl true
@spec run([String.t()]) :: :ok | no_return()
def run([datetime]) do
Logger.configure(level: :warning)
Mix.Task.run("app.start")
with {:ok, datetime, _offset} <- DateTime.from_iso8601(datetime),
{:ok, ephemeride} <- Angelus.get_ephemeride(datetime) do
print(ephemeride)
else
{:error, _reason} -> Mix.raise("datetime must be an ISO 8601 instant with an offset")
end
end
def run(_args), do: Mix.raise("usage: mix angelus.ephemeride DATETIME")
defp print(ephemeride) do
Mix.shell().info("UTC: #{DateTime.to_iso8601(ephemeride.datetime)}")
sidereal_time = ephemeride.sidereal_time
Mix.shell().info(
"Sidereal time: #{pad(sidereal_time.hour)}:#{pad(sidereal_time.minute)}:#{pad(sidereal_time.second)}"
)
Mix.shell().info("")
ephemeride.positions
|> rows()
|> render_table(["Body", "Ecliptic lat (deg)", "Declination (deg)", "Motion"])
|> Mix.shell().info()
end
defp rows(positions) do
Enum.map(positions, fn position ->
[
Map.fetch!(@names, position.body),
format_angle(position.lat),
format_angle(position.decl),
Atom.to_string(position.motion)
]
end)
end
defp render_table(rows, header) do
widths =
[header | rows]
|> Enum.zip()
|> Enum.map(fn column ->
column |> Tuple.to_list() |> Enum.map(&String.length/1) |> Enum.max()
end)
separator = "+" <> Enum.map_join(widths, "+", &String.duplicate("-", &1 + 2)) <> "+"
[separator, render_row(header, widths), separator]
|> Kernel.++(Enum.map(rows, &render_row(&1, widths)))
|> Kernel.++([separator])
|> Enum.join("\n")
end
defp render_row(row, widths) do
"|" <>
(row
|> Enum.zip(widths)
|> Enum.map_join("|", fn {value, width} ->
" " <> String.pad_trailing(value, width) <> " "
end)) <> "|"
end
defp format_angle(angle), do: :erlang.float_to_binary(angle, decimals: 4) <> "deg"
defp pad(value), do: value |> Integer.to_string() |> String.pad_leading(2, "0")
@shortdoc "Prints an Angelus ephemeris"
end