termradar 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. termradar-0.3.0/.gitignore +34 -0
  2. termradar-0.3.0/CHANGELOG.md +79 -0
  3. termradar-0.3.0/LICENSE +21 -0
  4. termradar-0.3.0/PKG-INFO +144 -0
  5. termradar-0.3.0/README.md +110 -0
  6. termradar-0.3.0/docs/ARCHITECTURE.md +147 -0
  7. termradar-0.3.0/docs/DATA_PROVIDERS.md +188 -0
  8. termradar-0.3.0/docs/DEVELOPMENT.md +126 -0
  9. termradar-0.3.0/docs/ROADMAP.md +574 -0
  10. termradar-0.3.0/docs/assets/demo-quick.gif +0 -0
  11. termradar-0.3.0/docs/assets/demo-quick.tape +17 -0
  12. termradar-0.3.0/pyproject.toml +77 -0
  13. termradar-0.3.0/src/termradar/__init__.py +3 -0
  14. termradar-0.3.0/src/termradar/__main__.py +6 -0
  15. termradar-0.3.0/src/termradar/cli.py +285 -0
  16. termradar-0.3.0/src/termradar/config/__init__.py +23 -0
  17. termradar-0.3.0/src/termradar/config/storage.py +207 -0
  18. termradar-0.3.0/src/termradar/core/__init__.py +13 -0
  19. termradar-0.3.0/src/termradar/core/airline.py +36 -0
  20. termradar-0.3.0/src/termradar/core/bearing.py +25 -0
  21. termradar-0.3.0/src/termradar/core/distance.py +24 -0
  22. termradar-0.3.0/src/termradar/core/engine.py +154 -0
  23. termradar-0.3.0/src/termradar/core/limits.py +14 -0
  24. termradar-0.3.0/src/termradar/core/location.py +18 -0
  25. termradar-0.3.0/src/termradar/core/models.py +73 -0
  26. termradar-0.3.0/src/termradar/core/rate_limit.py +35 -0
  27. termradar-0.3.0/src/termradar/core/timezone.py +23 -0
  28. termradar-0.3.0/src/termradar/providers/__init__.py +15 -0
  29. termradar-0.3.0/src/termradar/providers/adsbdb.py +158 -0
  30. termradar-0.3.0/src/termradar/providers/aircraft.py +293 -0
  31. termradar-0.3.0/src/termradar/providers/base.py +36 -0
  32. termradar-0.3.0/src/termradar/providers/geocoding.py +137 -0
  33. termradar-0.3.0/src/termradar/providers/routes.py +225 -0
  34. termradar-0.3.0/src/termradar/renderers/__init__.py +7 -0
  35. termradar-0.3.0/src/termradar/renderers/bearing_display.py +36 -0
  36. termradar-0.3.0/src/termradar/renderers/formatting.py +73 -0
  37. termradar-0.3.0/src/termradar/renderers/location_display.py +68 -0
  38. termradar-0.3.0/src/termradar/renderers/radar_canvas.py +209 -0
  39. termradar-0.3.0/src/termradar/renderers/radar_coords.py +65 -0
  40. termradar-0.3.0/src/termradar/renderers/terminal.py +27 -0
  41. termradar-0.3.0/src/termradar/renderers/terminal_ui.py +232 -0
  42. termradar-0.3.0/src/termradar/renderers/terminal_view.py +35 -0
  43. termradar-0.3.0/src/termradar/renderers/time_display.py +33 -0
  44. termradar-0.3.0/src/termradar/session.py +132 -0
  45. termradar-0.3.0/tests/conftest.py +1 -0
  46. termradar-0.3.0/tests/fakes.py +54 -0
  47. termradar-0.3.0/tests/test_adsbdb.py +169 -0
  48. termradar-0.3.0/tests/test_aircraft.py +185 -0
  49. termradar-0.3.0/tests/test_airline.py +76 -0
  50. termradar-0.3.0/tests/test_bearing.py +37 -0
  51. termradar-0.3.0/tests/test_bearing_display.py +15 -0
  52. termradar-0.3.0/tests/test_cli.py +61 -0
  53. termradar-0.3.0/tests/test_config.py +126 -0
  54. termradar-0.3.0/tests/test_distance.py +34 -0
  55. termradar-0.3.0/tests/test_engine.py +157 -0
  56. termradar-0.3.0/tests/test_formatting.py +82 -0
  57. termradar-0.3.0/tests/test_geocoding.py +116 -0
  58. termradar-0.3.0/tests/test_location_display.py +19 -0
  59. termradar-0.3.0/tests/test_models.py +37 -0
  60. termradar-0.3.0/tests/test_radar_canvas.py +134 -0
  61. termradar-0.3.0/tests/test_radar_coords.py +65 -0
  62. termradar-0.3.0/tests/test_rate_limit.py +18 -0
  63. termradar-0.3.0/tests/test_renderer.py +39 -0
  64. termradar-0.3.0/tests/test_routes.py +155 -0
  65. termradar-0.3.0/tests/test_session.py +112 -0
  66. termradar-0.3.0/tests/test_terminal_ui.py +155 -0
  67. termradar-0.3.0/tests/test_time_display.py +22 -0
  68. termradar-0.3.0/tests/test_timezone.py +21 -0
@@ -0,0 +1,34 @@
1
+ # Byte-compiled / cache
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Virtual environments
8
+ .venv/
9
+ venv/
10
+ env/
11
+
12
+ # Distribution / packaging
13
+ dist/
14
+ build/
15
+ *.egg-info/
16
+ .eggs/
17
+
18
+ # Testing
19
+ .pytest_cache/
20
+ .coverage
21
+ htmlcov/
22
+
23
+ # Tooling
24
+ .ruff_cache/
25
+ .mypy_cache/
26
+
27
+ # IDE
28
+ .idea/
29
+ .vscode/
30
+ *.swp
31
+ *.swo
32
+
33
+ # User configuration (never commit)
34
+ config.toml
@@ -0,0 +1,79 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.3.0] - 2026-07-05
11
+
12
+ ### Added
13
+
14
+ - adsb.lol v2 as default live aircraft provider (`--aircraft-provider opensky` for OpenSky)
15
+ - ADSBDB enrichment for routes and airlines (replaces adsb.lol routeset in CLI)
16
+ - ICAO callsign prefix airline inference when enrichment has no airline
17
+ - Top-five nearby aircraft list with numbered radar markers (`1`–`5`)
18
+ - Rate limits: 5 s min / 5 s default refresh, 30 ADSBDB req/min, enrichment TTL cache
19
+ - Nominatim 1 req/s throttle; legacy `refresh_seconds < 5` auto-upgraded on load
20
+ - Local time display via `timezonefinder` and optional `timezone` in config
21
+ - README demo GIF and VHS tape (`docs/assets/demo-quick.*`)
22
+
23
+ ### Changed
24
+
25
+ - Right panel: **CLOSEST** detail + **NEARBY** compact list (title: NEARBY AIRCRAFT)
26
+ - Radar marker collision handling (offsets when on ring dots or overlapping)
27
+ - Airline display shortens trailing ` Airlines` / ` Air Lines`
28
+ - Documentation overhaul for providers, internal limits, and usage
29
+ - README quick start includes `pip install termradar`
30
+ - Provider User-Agent strings track package `__version__`
31
+
32
+ ### Fixed
33
+
34
+ - OpenSky HTTP 429 at aggressive refresh (adsb.lol default + minimum 5 s refresh)
35
+ - ADSBDB HTTP 404 logged as debug, not terminal warning
36
+ - ADSBDB route lookup falls back to callsign endpoint when aircraft hex lookup misses
37
+ - Aircraft markers skipped on ring dots
38
+
39
+ ### Dependencies
40
+
41
+ - Added `timezonefinder` for coordinate → timezone lookup
42
+
43
+ ## [0.2.0] - 2026-07-05
44
+
45
+ ### Added
46
+
47
+ - Live terminal UI with Rich panels, radar visualization, and nearest-aircraft panel
48
+ - `RadarSession` live refresh loop with configurable interval
49
+ - CLI overrides: `--location`, `--radius` / `--radius-km`, `--refresh` (current run only)
50
+ - Testable `radar_to_grid()` coordinate mapping and ASCII radar canvas
51
+ - Graceful handling of missing aircraft metadata in the UI
52
+ - Stale snapshot display when aircraft provider temporarily fails
53
+ - PyPI-ready project metadata and local wheel build validation
54
+
55
+ ### Changed
56
+
57
+ - `termradar` runs a live refresh loop instead of a single scan
58
+ - Route provider accepts HTTP 201 responses from adsb.lol
59
+ - Improved first-run onboarding with refresh interval prompt
60
+
61
+ ### Fixed
62
+
63
+ - Empty adsb.lol route responses (HTTP 201, no body) no longer log false "malformed" warnings
64
+ - Terminal display clears screen before Live render to avoid duplicate headers
65
+
66
+ ### Dependencies
67
+
68
+ - Added `rich` for terminal layout and live display
69
+
70
+ ## [0.1.0] - 2026-07-05
71
+
72
+ ### Added
73
+
74
+ - Core radar engine, providers, config storage, CLI, and tests
75
+
76
+ [Unreleased]: https://github.com/rusty3699/termradar/compare/v0.3.0...HEAD
77
+ [0.3.0]: https://github.com/rusty3699/termradar/compare/v0.2.0...v0.3.0
78
+ [0.2.0]: https://github.com/rusty3699/termradar/compare/v0.1.0...v0.2.0
79
+ [0.1.0]: https://github.com/rusty3699/termradar/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TermRadar Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.4
2
+ Name: termradar
3
+ Version: 0.3.0
4
+ Summary: See what's flying above you - live aircraft radar for your terminal
5
+ Project-URL: Homepage, https://github.com/rusty3699/termradar
6
+ Project-URL: Repository, https://github.com/rusty3699/termradar
7
+ Project-URL: Documentation, https://github.com/rusty3699/termradar/tree/main/docs
8
+ Project-URL: Issues, https://github.com/rusty3699/termradar/issues
9
+ Author: Anish Tipnis
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ads-b,aircraft,aviation,cli,flight-tracking,radar,terminal
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: GIS
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: platformdirs>=4.0
25
+ Requires-Dist: rich>=13.7
26
+ Requires-Dist: timezonefinder>=6.5
27
+ Requires-Dist: tomli-w>=1.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.0; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.8; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # TermRadar
36
+
37
+ **See what's flying above you - without leaving your terminal.**
38
+
39
+ ![TermRadar live demo](docs/assets/demo-quick.gif)
40
+
41
+ TermRadar is a lightweight live aircraft radar for developers, programmers, and aviation enthusiasts.
42
+
43
+ You're coding, you hear an aircraft overhead, or spot something from the window. Instead of opening a browser or reaching for a flight-tracking app, open a terminal and run:
44
+
45
+ ```bash
46
+ termradar
47
+ ```
48
+
49
+ You get a live radar centered on your location: nearby callsigns, distance, bearing, speed, altitude, and route info when available. Use it for the quick answer, then jump to another tracker when you want deeper details.
50
+
51
+ > **One radar engine. Multiple displays.**
52
+
53
+ The core is display-agnostic. Today: a polished terminal UI. Planned: Raspberry Pi fullscreen and small displays.
54
+
55
+ ## Quick start
56
+
57
+ ```bash
58
+ pip install termradar
59
+ termradar
60
+ ```
61
+
62
+ From source:
63
+
64
+ ```bash
65
+ git clone https://github.com/rusty3699/termradar.git
66
+ cd termradar
67
+ python3 -m venv .venv
68
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
69
+ pip install -e .
70
+ termradar
71
+ ```
72
+
73
+ First run: enter a location, pick a geocoding result, set radius and refresh. Later runs reuse saved settings. Press **Ctrl+C** to exit.
74
+
75
+ Config: `~/.config/termradar/config.toml` on Linux (platformdirs on macOS and Windows — see [ARCHITECTURE.md](docs/ARCHITECTURE.md)).
76
+
77
+ ## What you get
78
+
79
+ | Available now | Planned |
80
+ |---------------|---------|
81
+ | Live terminal radar (default 5 s refresh) | Raspberry Pi fullscreen display |
82
+ | Numbered radar markers + top-five nearby list | OLED / e-paper displays |
83
+ | Closest-aircraft detail panel | Local ADS-B receivers |
84
+ | adsb.lol live aircraft (default) | Alerts and notifications |
85
+ | ADSBDB route/airline enrichment | Web UI |
86
+ | Nominatim geocoding, local timezone | |
87
+ | OpenSky via `--aircraft-provider opensky` | |
88
+
89
+ **Requirements:** Python 3.11+, network access. Works on Linux, macOS, Windows, and Raspberry Pi.
90
+
91
+ ## Usage
92
+
93
+ ```bash
94
+ termradar --location "Andheri, Mumbai" # temporary location (this run only)
95
+ termradar --radius 25 # search radius in km
96
+ termradar --refresh 10 # refresh interval (min 5 s)
97
+ termradar --aircraft-provider opensky # OpenSky instead of adsb.lol
98
+ termradar --enrichment-limit 10 # max aircraft to enrich per scan
99
+ termradar --reset-location # re-run setup
100
+ termradar --version
101
+ termradar --help
102
+ ```
103
+
104
+ | Setting | Default | Allowed |
105
+ |---------|---------|---------|
106
+ | Refresh | 5 s | 5-300 s |
107
+ | Radius | 15 km | 1-250 km |
108
+ | Aircraft source | adsb.lol | `adsblol` or `opensky` |
109
+ | Enrichment | 10 nearest | `--enrichment-limit` |
110
+
111
+ Provider details and internal limits: [docs/DATA_PROVIDERS.md](docs/DATA_PROVIDERS.md)
112
+
113
+ ## How it works
114
+
115
+ ```text
116
+ each refresh (every 5 s by default):
117
+ 1. Fetch aircraft near you → adsb.lol
118
+ 2. Distance and bearing from you
119
+ 3. Enrich nearest (cache miss only) → ADSBDB
120
+ 4. Draw radar + aircraft panels
121
+
122
+ setup only (not each refresh):
123
+ Geocoding → Nominatim
124
+ ```
125
+
126
+ **Data sources:** [adsb.lol](https://adsb.lol) (aircraft), [ADSBDB](https://adsbdb.com) (routes/airlines), [Nominatim](https://www.openstreetmap.org) (geocoding)
127
+
128
+ ## Documentation
129
+
130
+ | Doc | What it covers |
131
+ |-----|----------------|
132
+ | [CHANGELOG.md](CHANGELOG.md) | Release history |
133
+ | [ARCHITECTURE.md](docs/ARCHITECTURE.md) | Design, UI layout, code boundaries |
134
+ | [DATA_PROVIDERS.md](docs/DATA_PROVIDERS.md) | APIs, internal limits, caching |
135
+ | [ROADMAP.md](docs/ROADMAP.md) | What's next |
136
+ | [DEVELOPMENT.md](docs/DEVELOPMENT.md) | Tests, lint, packaging |
137
+
138
+ ## License
139
+
140
+ MIT - see [LICENSE](LICENSE).
141
+
142
+ ---
143
+
144
+ Made with <3 Anish
@@ -0,0 +1,110 @@
1
+ # TermRadar
2
+
3
+ **See what's flying above you - without leaving your terminal.**
4
+
5
+ ![TermRadar live demo](docs/assets/demo-quick.gif)
6
+
7
+ TermRadar is a lightweight live aircraft radar for developers, programmers, and aviation enthusiasts.
8
+
9
+ You're coding, you hear an aircraft overhead, or spot something from the window. Instead of opening a browser or reaching for a flight-tracking app, open a terminal and run:
10
+
11
+ ```bash
12
+ termradar
13
+ ```
14
+
15
+ You get a live radar centered on your location: nearby callsigns, distance, bearing, speed, altitude, and route info when available. Use it for the quick answer, then jump to another tracker when you want deeper details.
16
+
17
+ > **One radar engine. Multiple displays.**
18
+
19
+ The core is display-agnostic. Today: a polished terminal UI. Planned: Raspberry Pi fullscreen and small displays.
20
+
21
+ ## Quick start
22
+
23
+ ```bash
24
+ pip install termradar
25
+ termradar
26
+ ```
27
+
28
+ From source:
29
+
30
+ ```bash
31
+ git clone https://github.com/rusty3699/termradar.git
32
+ cd termradar
33
+ python3 -m venv .venv
34
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
35
+ pip install -e .
36
+ termradar
37
+ ```
38
+
39
+ First run: enter a location, pick a geocoding result, set radius and refresh. Later runs reuse saved settings. Press **Ctrl+C** to exit.
40
+
41
+ Config: `~/.config/termradar/config.toml` on Linux (platformdirs on macOS and Windows — see [ARCHITECTURE.md](docs/ARCHITECTURE.md)).
42
+
43
+ ## What you get
44
+
45
+ | Available now | Planned |
46
+ |---------------|---------|
47
+ | Live terminal radar (default 5 s refresh) | Raspberry Pi fullscreen display |
48
+ | Numbered radar markers + top-five nearby list | OLED / e-paper displays |
49
+ | Closest-aircraft detail panel | Local ADS-B receivers |
50
+ | adsb.lol live aircraft (default) | Alerts and notifications |
51
+ | ADSBDB route/airline enrichment | Web UI |
52
+ | Nominatim geocoding, local timezone | |
53
+ | OpenSky via `--aircraft-provider opensky` | |
54
+
55
+ **Requirements:** Python 3.11+, network access. Works on Linux, macOS, Windows, and Raspberry Pi.
56
+
57
+ ## Usage
58
+
59
+ ```bash
60
+ termradar --location "Andheri, Mumbai" # temporary location (this run only)
61
+ termradar --radius 25 # search radius in km
62
+ termradar --refresh 10 # refresh interval (min 5 s)
63
+ termradar --aircraft-provider opensky # OpenSky instead of adsb.lol
64
+ termradar --enrichment-limit 10 # max aircraft to enrich per scan
65
+ termradar --reset-location # re-run setup
66
+ termradar --version
67
+ termradar --help
68
+ ```
69
+
70
+ | Setting | Default | Allowed |
71
+ |---------|---------|---------|
72
+ | Refresh | 5 s | 5-300 s |
73
+ | Radius | 15 km | 1-250 km |
74
+ | Aircraft source | adsb.lol | `adsblol` or `opensky` |
75
+ | Enrichment | 10 nearest | `--enrichment-limit` |
76
+
77
+ Provider details and internal limits: [docs/DATA_PROVIDERS.md](docs/DATA_PROVIDERS.md)
78
+
79
+ ## How it works
80
+
81
+ ```text
82
+ each refresh (every 5 s by default):
83
+ 1. Fetch aircraft near you → adsb.lol
84
+ 2. Distance and bearing from you
85
+ 3. Enrich nearest (cache miss only) → ADSBDB
86
+ 4. Draw radar + aircraft panels
87
+
88
+ setup only (not each refresh):
89
+ Geocoding → Nominatim
90
+ ```
91
+
92
+ **Data sources:** [adsb.lol](https://adsb.lol) (aircraft), [ADSBDB](https://adsbdb.com) (routes/airlines), [Nominatim](https://www.openstreetmap.org) (geocoding)
93
+
94
+ ## Documentation
95
+
96
+ | Doc | What it covers |
97
+ |-----|----------------|
98
+ | [CHANGELOG.md](CHANGELOG.md) | Release history |
99
+ | [ARCHITECTURE.md](docs/ARCHITECTURE.md) | Design, UI layout, code boundaries |
100
+ | [DATA_PROVIDERS.md](docs/DATA_PROVIDERS.md) | APIs, internal limits, caching |
101
+ | [ROADMAP.md](docs/ROADMAP.md) | What's next |
102
+ | [DEVELOPMENT.md](docs/DEVELOPMENT.md) | Tests, lint, packaging |
103
+
104
+ ## License
105
+
106
+ MIT - see [LICENSE](LICENSE).
107
+
108
+ ---
109
+
110
+ Made with <3 Anish
@@ -0,0 +1,147 @@
1
+ # Architecture
2
+
3
+ > **One radar engine. Multiple displays.**
4
+
5
+ The core produces a `RadarSnapshot` on each scan. Renderers draw it; they never fetch data or call external APIs.
6
+
7
+ ## Data flow
8
+
9
+ ```text
10
+ GeocodingProvider (setup / location override only)
11
+
12
+ Location (+ timezone)
13
+
14
+ AircraftProvider (every scan - default: adsb.lol)
15
+
16
+ RadarEngine ◄──── CachedRouteProvider ──► AdsbDbRouteProvider
17
+
18
+ RadarSnapshot
19
+
20
+ RadarSession → TerminalView → TerminalRenderer
21
+
22
+ Terminal output (Rich live UI)
23
+ ```
24
+
25
+ A future `FullscreenRenderer` (Phase 3, Raspberry Pi) will consume the same `RadarSnapshot`.
26
+
27
+ ## Scan pipeline
28
+
29
+ ```text
30
+ RadarEngine.scan()
31
+ → fetch aircraft (AdsblolAircraftProvider by default)
32
+ → distance_km() + bearing_deg() from radar center
33
+ → filter by radius, sort nearest-first
34
+ → enrich nearest N aircraft (ADSBDB with callsign fallback, cached, rate-limited)
35
+ → infer airline from ICAO callsign prefix when still unknown
36
+ → RadarSnapshot
37
+ ```
38
+
39
+ Geocoding and timezone resolution run only during onboarding, `--reset-location`, or `--location` - **not** on refresh.
40
+
41
+ ## Live display loop
42
+
43
+ ```text
44
+ RadarSession
45
+ → engine.scan()
46
+ → build TerminalView (location, snapshot, errors, terminal size)
47
+ → TerminalRenderer.render()
48
+ → sleep(refresh_seconds) # default 5 s, minimum 5 s
49
+ ```
50
+
51
+ `RadarSession` owns the loop and keeps the last good snapshot when a scan fails (stale mode). `TerminalRenderer` is stateless per frame.
52
+
53
+ ## Terminal UI layout
54
+
55
+ Wide terminals (≥ 72 columns): radar panel left, aircraft panel right.
56
+
57
+ **Radar panel**
58
+
59
+ | Symbol | Meaning |
60
+ |--------|---------|
61
+ | `+` | Your location (radar center) |
62
+ | Outer dotted ring | Search radius |
63
+ | Inner dotted ring | Half of search radius |
64
+ | `1`–`5` | Top five nearest aircraft (matches nearby list) |
65
+ | `✈` | Other aircraft in range |
66
+ | `N` `E` `S` `W` | Compass |
67
+
68
+ Markers use collision offsets when the ideal grid cell is occupied (ring dot, center, or another marker).
69
+
70
+ **Nearby aircraft panel**
71
+
72
+ - **CLOSEST** - full detail for nearest aircraft (callsign, airline, route, distance, bearing, speed, altitude)
73
+ - **NEARBY** - compact top-five list: rank, callsign, distance, compass direction
74
+
75
+ Narrow terminals fall back to a compact aircraft table.
76
+
77
+ ## Package layout
78
+
79
+ ```text
80
+ src/termradar/
81
+ ├── cli.py # Entry point, onboarding, provider wiring
82
+ ├── session.py # Live refresh loop
83
+ ├── core/
84
+ │ ├── engine.py # Scan orchestration
85
+ │ ├── models.py # Location, Aircraft, RadarSnapshot, …
86
+ │ ├── limits.py # Rate-limit constants
87
+ │ ├── rate_limit.py # Rolling minute limiter
88
+ │ ├── airline.py # ICAO prefix → airline inference
89
+ │ ├── distance.py / bearing.py
90
+ │ ├── location.py / timezone.py
91
+ │ └── …
92
+ ├── providers/
93
+ │ ├── geocoding.py # Nominatim
94
+ │ ├── aircraft.py # adsb.lol + OpenSky
95
+ │ ├── adsbdb.py # ADSBDB enrichment
96
+ │ └── routes.py # CachedRouteProvider, adsb.lol routeset
97
+ ├── config/storage.py # TOML load/save, validation
98
+ └── renderers/
99
+ ├── formatting.py # Display strings
100
+ ├── bearing_display.py # Compass labels
101
+ ├── location_display.py # Short location header
102
+ ├── radar_coords.py # Polar → grid
103
+ ├── radar_canvas.py # ASCII radar + markers
104
+ ├── terminal_ui.py # Rich layout
105
+ ├── terminal_view.py # Per-frame view model
106
+ └── time_display.py # Local time in location timezone
107
+ ```
108
+
109
+ ## Boundaries
110
+
111
+ | Layer | Does | Does not |
112
+ |-------|------|----------|
113
+ | Providers | HTTP, parsing, caching, rate limiting | Display, geometry |
114
+ | `RadarEngine` | Fetch, filter, sort, enrich | Terminal drawing |
115
+ | `CachedRouteProvider` | TTL cache + 30/min cap | Aircraft positions |
116
+ | `TerminalRenderer` | Layout, radar, formatting | API calls |
117
+ | `radar_to_grid()` | Bearing/distance → grid cell | Fetch or enrich |
118
+
119
+ ## Error handling
120
+
121
+ | Failure | Behaviour |
122
+ |---------|-----------|
123
+ | Aircraft provider down | Error state; last snapshot shown as stale if available |
124
+ | ADSBDB miss / 404 | Aircraft shown; airline may come from callsign prefix |
125
+ | Enrichment rate limit | Skip remaining lookups this scan; retry later |
126
+ | Missing altitude / route | Graceful fallbacks (`-`, `Unknown airline`, `Route unavailable`) |
127
+ | Ctrl+C | Clean exit |
128
+
129
+ ## Configuration
130
+
131
+ Stored at `~/.config/termradar/config.toml` (platformdirs).
132
+
133
+ | Key | Default | Notes |
134
+ |-----|---------|-------|
135
+ | `location.*` | - | Set during onboarding |
136
+ | `radar.radius_km` | 15 | 1–250 km |
137
+ | `radar.refresh_seconds` | 5 | 5–300; values &lt; 5 upgraded on load |
138
+
139
+ CLI overrides (`--location`, `--radius`, `--refresh`, `--aircraft-provider`, `--enrichment-limit`) apply to the current run only unless `--reset-location` re-saves config.
140
+
141
+ ## Internal limits and caching
142
+
143
+ See [DATA_PROVIDERS.md](DATA_PROVIDERS.md) for full detail. Summary:
144
+
145
+ - **Refresh:** 5 s default and minimum → one adsb.lol request per cycle
146
+ - **Enrichment:** 30 ADSBDB requests/minute max, 10 nearest aircraft per scan, 12 h / 30 min cache
147
+ - **Geocoding:** 1 Nominatim request/second, setup only