class

Kozai::Config

Inherits Reference < Object

Configuration: a small key = value format with sections, plus environment overrides.

Why not YAML

Because YAML in the standard library links libyaml, and this program has no C dependencies beyond the language runtime. That constraint is not an affectation — it is what makes the binary a single self-contained file — but it is not the only reason.

This file gets edited over ssh, on a station in a shed, possibly at night, possibly on a phone. It wants to be the kind of format where a missing character produces an error naming a line, not a silently different meaning. Indentation-sensitive formats are a poor fit for that. So: a section header in brackets, key = value beneath it, # starts a comment.

# Where the antenna is.
[station]
name      = Shed
latitude  = 52.3676     # degrees north
longitude = 4.9041      # degrees east
altitude  = 5           # metres above the ellipsoid

[prediction]
minimum_elevation = 10
horizon_hours     = 48

Environment overrides

Any setting can be overridden by an environment variable named KOZAI_<SECTION>_<KEY> in upper case — KOZAI_STATION_LATITUDE, KOZAI_SERVER_PORT. This is what makes the container image useful without a config file at all, and it is how docker-compose.yml is written.

Constants

ENVIRONMENT_PREFIX = "KOZAI_"

Prefix for environment variable overrides.

ROOT_SECTION = ""

Section used for keys that appear before any section header.

Constructors

load(path : String) : Config

Reads and parses a configuration file.

Raises Config::ParseError if the file cannot be read.

Source
load_or_default(path : String | Nil) : Config

Reads a configuration file if it exists, otherwise returns the defaults.

Source
new

Creates an empty configuration.

Source
parse(text : String, path : String | Nil = nil) : Config

Parses configuration from text.

path is used only to make error messages point somewhere useful.

Raises Config::ParseError on a malformed line.

Source

Instance methods

[]?(section : String, key : String) : String | Nil

The raw string value, or nil if unset.

Source
boolean(section : String, key : String, default : Bool) : Bool

A boolean setting, or default.

Accepts true/false, yes/no, on/off and 1/0, in any case, because all of them turn up in configuration files written by humans.

Source
cache_directory

Directory for cached element sets.

Source
cache_ttl

How long a cached element set stays fresh.

NOTE: the default is 24 hours, and it is deliberately not shorter. CelesTrak asks that clients cache and not poll, element sets are typically regenerated once or twice a day, and a station that re-downloads every few minutes gains nothing and costs someone else bandwidth.

Source
dup

An independent copy.

NOTE: needed because the settings page can clear a setting, and clearing cannot be expressed by writing over a live configuration — there is nothing to write. Rebuilding from a copy of everything the page did not set is the only way a cleared value goes back to whatever the file and the environment say, without a restart.

Source
float(section : String, key : String, default : Float64, range : Range(Float64, Float64) | Nil = nil) : Float64

A floating-point setting, or default.

Raises Config::ValueError if present but not a number, or outside range. Reporting a bad value beats silently substituting the default: a station configured with a typo'd latitude should refuse to start, not quietly point at the equator.

Source
has?(section : String, key : String) : Bool

Whether a setting is present.

Source
horizon

How far ahead to predict.

Source
integer(section : String, key : String, default : Int32, range : Range(Int32, Int32) | Nil = nil) : Int32

An integer setting, or default.

Source
keys

Every {section, key} present, sorted.

Source
list(section : String, key : String, default : Array(String) = [] of String) : Array(String)

A comma-separated list setting.

Empty entries are dropped, so a trailing comma is harmless.

Source
minimum_elevation

Minimum elevation for a pass to count, in degrees.

Source
observer

The observing station.

NOTE: altitude is given in metres in the file, because that is what a map or a GPS reports, and converted to kilometres here, because that is what the geometry works in. Getting this backwards would put the station a thousand times too high and is exactly the sort of thing that produces plausible, wrong answers.

Source
offline?

Whether to work purely from what is already on disk.

Source
origin(section : String, key : String) : String | Nil

Where a setting came from — a file and line, or an environment variable.

Reported when a value fails validation, so that the operator is told which of the two won.

Source
radio_enabled?

Whether radio control is enabled.

Source
radio_host

Radio daemon address.

Source
radio_port

Radio daemon port. 4532 is the rigctld default.

Source
rotator_enabled?

Whether rotator control is enabled.

Source
rotator_host

Rotator daemon address.

Source
rotator_port

Rotator daemon port. 4533 is the rotctld default.

Source
rotator_protocol

Which rotator protocol to speak.

Source
schedule_enabled?

Whether the station works passes on its own.

Source
schedule_lookahead

How far ahead the plan is built.

Source
schedule_minimum_duration

Shortest pass worth committing the antenna to.

Source
schedule_minimum_elevation

Elevation a pass must reach to be worth scheduling.

Separate from #minimum_elevation, which decides what is worth showing. A station will happily list a nine-degree pass and want nothing to do with swinging an antenna for it.

Source
schedule_park_at_los?

Whether to park the antenna when a scheduled pass ends.

Source
server_host

Address the HTTP server binds to.

Source
server_port

Port the HTTP server binds to.

Source
server_token

Shared secret required on requests that move hardware, if set.

NOTE: empty by default, and that is safe only because the server binds to loopback by default. The moment [server] host is changed to reach the interface from a phone, every device on the network can point the antenna; Cli.serve says so on the way up when this is unset.

Source
set(section : String, key : String, value : String, origin : String = "<runtime>") : Nil

Sets a value, replacing any existing one.

Source
settings_file

Where settings written from the interface are kept.

Next to the element cache, because that is the one directory a station is already required to have writable — the container image is read-only everywhere else by design.

Source
station_configured?

Whether a station position was configured at all.

Without one every answer is about the Gulf of Guinea, which is a confusing way to be wrong.

Source
string(section : String, key : String, default : String) : String

A string setting, or default.

Source
string!(section : String, key : String) : String

A required string setting.

Raises Config::ValueError if absent.

Source
tle_files

Local element set files to load.

Source
tle_urls

Remote element set sources.

Ignored by a -Dno_network build, which has no way to fetch them.

Source
transponder_file

Path to a transponder file, if one is configured.

Its entries are laid over the ones compiled into the binary, section by section, so a station corrects a single frequency by writing a section of the same name rather than by restating the whole table.

Source
twilight

Sun elevation below which the sky counts as dark, in degrees.

A satellite is only worth going outside for when it is in sunlight and the observer is not, so this is half of what makes a pass visual — see Illumination.

NOTE: −6 degrees, civil twilight, and that is a convention rather than a measurement. It is where visual pass predictions are usually quoted, so it is the value that makes this software's answers comparable with everybody else's. It is also generous: at −6 the brightest stars are out and the ISS is unmistakable, but a fainter satellite will not be. Nautical twilight (−12) or astronomical (−18) are the stricter readings, and a station under a bright sky may want them.

Zero would mean "the Sun has set", which is not the same thing at all: the sky stays too bright to see anything for another half hour.

Source
watchlist

The satellites this station works, as written.

Empty means the whole catalogue. Entries may carry a priority after a colon — 25544:10 — which only the scheduler reads.

Source
with_environment(environment = ENV) : Config

Returns a copy with environment variables applied on top.

environment defaults to the process environment; passing one explicitly is what makes this testable.

Source

Nested types