defmodule Hamlib.Nif do @moduledoc """ Raw NIF surface over the Hamlib C API (via the `hlx_*` C shim and a Rustler crate at `native/hamlib_nif`). This is the low-level binding — prefer `Hamlib.Rig` for a managed, process-owned interface. Every function here corresponds 1:1 to a shim call. ## Return shapes * Operations that don't return data → `:ok | {:error, {code, reason}}` * `get_freq/1` → `{:ok, hz} | {:error, {code, reason}}` * `get_mode/1` → `{:ok, {mode_string, passband_hz}} | {:error, …}` * `get_ptt/1` → `{:ok, boolean} | {:error, …}` `code` is the integer Hamlib error (negative), `reason` its text. A handle is an opaque resource from `init/1`; the BEAM frees the underlying rig when the handle is garbage-collected (close + cleanup), even on owner crash. ## Lifecycle {:ok, h} = Hamlib.Nif.init(model) # allocate (no I/O) :ok = Hamlib.Nif.set_conf(h, "rig_pathname", path) :ok = Hamlib.Nif.set_conf(h, "serial_speed", "19200") :ok = Hamlib.Nif.open(h) # opens the port ... :ok = Hamlib.Nif.close(h) """ use Rustler, otp_app: :hamlib_ex, crate: "hamlib_nif" @typedoc "Opaque rig handle resource (from `init/1`)." @type handle :: reference() @typedoc "`{hamlib_error_code, reason_string}`." @type error :: {integer(), String.t()} @doc "Allocate + initialize a rig for a Hamlib model number. No port I/O." @spec init(integer()) :: {:ok, handle()} | {:error, atom()} def init(_model), do: :erlang.nif_error(:nif_not_loaded) @doc "Set a Hamlib config token by name (e.g. \"rig_pathname\", \"serial_speed\", \"ptt_type\")." @spec set_conf(handle(), String.t(), String.t()) :: :ok | {:error, error() | atom()} def set_conf(_handle, _token, _value), do: :erlang.nif_error(:nif_not_loaded) @doc "Open the configured rig port." @spec open(handle()) :: :ok | {:error, error()} def open(_handle), do: :erlang.nif_error(:nif_not_loaded) @doc "Close the rig port (rig stays initialized; reopenable)." @spec close(handle()) :: :ok | {:error, error()} def close(_handle), do: :erlang.nif_error(:nif_not_loaded) @doc "Set frequency in Hz on the current VFO." @spec set_freq(handle(), float()) :: :ok | {:error, error()} def set_freq(_handle, _freq_hz), do: :erlang.nif_error(:nif_not_loaded) @doc "Get frequency in Hz from the current VFO." @spec get_freq(handle()) :: {:ok, float()} | {:error, error()} def get_freq(_handle), do: :erlang.nif_error(:nif_not_loaded) @doc "Set mode by string + passband Hz (0 = rig's normal width)." @spec set_mode(handle(), String.t(), integer()) :: :ok | {:error, error() | atom()} def set_mode(_handle, _mode, _passband_hz), do: :erlang.nif_error(:nif_not_loaded) @doc "Get current `{mode_string, passband_hz}`." @spec get_mode(handle()) :: {:ok, {String.t(), integer()}} | {:error, error()} def get_mode(_handle), do: :erlang.nif_error(:nif_not_loaded) @doc "Set PTT: `true` = transmit, `false` = receive." @spec set_ptt(handle(), boolean()) :: :ok | {:error, error()} def set_ptt(_handle, _on), do: :erlang.nif_error(:nif_not_loaded) @doc "Get PTT state as a boolean." @spec get_ptt(handle()) :: {:ok, boolean()} | {:error, error()} def get_ptt(_handle), do: :erlang.nif_error(:nif_not_loaded) @doc "Set Hamlib's global debug verbosity (0 = silent … 5 = trace)." @spec set_debug(0..5) :: :ok def set_debug(_level), do: :erlang.nif_error(:nif_not_loaded) @doc "Hamlib library version string." @spec version() :: String.t() def version, do: :erlang.nif_error(:nif_not_loaded) end