56 lines
1.5 KiB
Elixir
56 lines
1.5 KiB
Elixir
defmodule Aprsme.Passcode do
|
|
@moduledoc """
|
|
Module for generating APRS passcodes from callsigns.
|
|
The passcode is a hash of the callsign used for authentication with APRS-IS servers.
|
|
"""
|
|
|
|
@doc """
|
|
Generates an APRS passcode for a given callsign.
|
|
|
|
## Parameters
|
|
- callsign: The amateur radio callsign (e.g., "W5ISP")
|
|
|
|
## Returns
|
|
- The generated passcode as an integer
|
|
|
|
## Examples
|
|
iex> Aprsme.Passcode.generate("W5ISP")
|
|
15748
|
|
"""
|
|
@spec generate(String.t()) :: non_neg_integer()
|
|
def generate(callsign) when is_binary(callsign) do
|
|
# Split on '-' and take first part, uppercase, and limit to 10 chars
|
|
realcall =
|
|
callsign
|
|
|> String.split("-")
|
|
|> List.first()
|
|
|> String.upcase()
|
|
|> String.slice(0, 10)
|
|
|
|
# Convert string to charlist for easier processing
|
|
chars = String.to_charlist(realcall)
|
|
|
|
# Initial hash value
|
|
hash = 0x73E2
|
|
|
|
# Process characters in pairs, incrementing by 2 each time
|
|
hash =
|
|
Enum.reduce(0..div(length(chars) - 1, 2), hash, fn i, acc ->
|
|
current_char = Enum.at(chars, i * 2)
|
|
next_char = if i * 2 + 1 < length(chars), do: Enum.at(chars, i * 2 + 1)
|
|
step1 = Bitwise.bxor(acc, Bitwise.bsl(current_char, 8))
|
|
|
|
step2 =
|
|
if next_char do
|
|
Bitwise.bxor(step1, next_char)
|
|
else
|
|
step1
|
|
end
|
|
|
|
step2
|
|
end)
|
|
|
|
# Return final hash masked to 15 bits
|
|
Bitwise.band(hash, 0x7FFF)
|
|
end
|
|
end
|