A web application that displays the current positions of celestial bodies (Sun, Moon, planets, asteroids, comets) in ASCII art.
- Real-time tracking of celestial bodies (altitude and azimuth)
- Display of objects above and below the horizon
- Current moon phase visualization
- Rise, set, and transit times for all celestial objects
- Interactive object selection with detailed information dialog
- Distance and magnitude information for all objects
- Bright asteroids (minor planets) with apparent magnitude filtering and rise/set/transit times
- Comets using real MPC data with M1/k1 magnitude model, positions, and rise/set/transit times
- User-adjustable brightness filters for asteroids and comets (magnitude 10-20)
- Accessible via ⚙️ button under "Visible Objects"
- Filters saved per user in
user_settings.json - No cache invalidation when filters change; filtering is applied in API routes and cached positions are unfiltered and reused
- Constellation visualization with:
- Interactive toggle to show/hide constellations
- Constellation lines connecting major stars (using data from constellationship.fab)
- Constellation names in the selected language
- Smooth panning and zooming
- Messier objects overlay (catalog from RASC list via SEDS dataRASC.txt), real-time Alt/Az with rise/set/transit, toggleable like constellations
- Auto-updates every 60 seconds
- Internationalization (i18n) with German as default language
- Simulated time controls (optional): view the sky at a chosen UTC time
- Extended navigation controls: day back, hour back, reset to current time, hour forward, day forward
- Clickable time display for entering any custom date and time
- Frontend appends
?time=<ISO8601>to API calls automatically when enabled - Automatic background precomputation for smooth time navigation
- Yearly sunpath overlay for the current location
- Toggleable SVG overlay with sunrise/sunset curves for the whole year
- Hover tooltips with localized date, sunrise, sunset, and day length in hours and minutes
- Visualization of astronomical, nautical, and civil twilight as colored bands
- Monthly markers (vertical lines) on the 1st of each month with short month labels at the bottom
- Planisphere view (circular sky map)
- Toggle between horizon and planisphere view
- Physically consistent projection of objects above and below the horizon
- SVG horizon overlay aligned with the ASCII sky grid
- Minimalist UI design with optimized space usage
- Horizontal navigation with arrow controls and mouse drag panning
- Labels for bright asteroids, comets, and constellations
- Responsive design with mobile/tablet support
- Desktop zoom functionality (1×, 2×, 4×) with vertical pan/scroll (desktop only, disabled on mobile devices)
- PostgreSQL database backend for efficient data storage and retrieval
- RabbitMQ message queue with distributed compute workers (precompute and on-demand), scalable across multiple hosts; see API Request Flow
- Hybrid Deduplication (Phase 3): Deterministic RabbitMQ message IDs + PostgreSQL Advisory Locks
- Prevents duplicate computations across all workers
- Scales horizontally across unlimited worker hosts
- Automatic cleanup and monitoring
- Performance: -80% memory usage, +35% throughput
- Vectorized Performance Optimization: NumPy-based magnitude calculations
- 100-200x faster magnitude computations for asteroids and comets
- NumPy pre-filtering reduces expensive Skyfield observe() calls by 40-60%
- 3-stage pipeline: H-filter → NumPy pre-filter → precise calculation
- Overall performance: 2-4x faster (7-8x with many objects)
- Automatic nightly updates of asteroid and comet orbital data (configurable; default 4:00 AM)
- DataFrame-first loading from filesystem cache (pickled MPC DataFrames) instead of reparsing raw MPC text files
- Docker and Docker Compose
- Clone this repository
- Navigate to the project directory
- Run the Hybrid Deduplication setup:
./scripts/hybrid-setup.sh local - Open your browser and navigate to
http://localhost:8000
The Hybrid setup automatically:
- Configures PostgreSQL Advisory Locks for deduplication
- Starts all core services (FastAPI, PostgreSQL, RabbitMQ, workers)
- Builds Docker images with the latest code
- Initializes the database and downloads asteroid/comet data
- Launches unified workers (with per-message dedup IDs + Advisory Locks)
- Runs the hybrid deduplication smoke tests
Data Safety: By default, all data (database, cache, etc.) is preserved when restarting. Only use ./scripts/hybrid-setup.sh local --clean if you want to delete everything.
Access Points:
- Web API: http://localhost:8000
- API Docs: http://localhost:8000/docs
- RabbitMQ UI: http://localhost:15672 (admin/changeme)
Testing Hybrid Deduplication:
# Run all tests and verification
./scripts/hybrid-setup.sh test
# Show implementation summary
./scripts/hybrid-setup.sh summary
# Test API with deduplication
curl -s "http://localhost:8000/api/bright_asteroids?lat=46.7632&lon=14.8417&elevation=405"For production deployment across multiple servers:
- Configure
.envfile (see.env.example) - Optional: Create
.env.band.env.cfor worker-specific settings (see.env.b.example,.env.c.example) - Run the production setup script:
./scripts/setup-production.sh
This deploys with PostgreSQL Advisory Locks (Phase 3):
- Main Server ($RABBITMQ_MAIN): Web UI, PostgreSQL, RabbitMQ, Data Updater
- Worker Server B ($RABBITMQ_B): Unified Workers with PostgreSQL Advisory Locks
- Worker Server C ($RABBITMQ_C): Unified Workers with PostgreSQL Advisory Locks
PostgreSQL Deduplication Features:
- PostgreSQL Advisory Locks prevent duplicate tasks (100% protection)
- Standard RabbitMQ queues for task distribution
- Automatic scaling across unlimited worker hosts
- Performance: -80% memory, +35% throughput
See doc/PRODUCTION_DEPLOYMENT.md for detailed deployment instructions and docs/hybrid-deduplication.md for deduplication details.
Compose files for production:
docker-compose.production.yml— main server with PostgreSQL Advisory Locksdocker-compose.workers.yml— worker hosts with Unified Workers and Advisory Locks
Production Updates:
# Update with PostgreSQL Advisory Locks verification
./scripts/hybrid-setup.sh update
# Check PostgreSQL Advisory Locks status
./scripts/hybrid-setup.sh testThe application runs multiple services. In local development these are defined in docker-compose.yml, in production on the main server in docker-compose.production.yml, and on worker hosts in docker-compose.workers.yml.
Core services (main server / local):
web– FastAPI web server (port 8000)postgres– PostgreSQL database with Advisory Locks support (port 5432)rabbitmq– RabbitMQ 4.1 message broker for task distribution (ports 5672, 15672)data_updater– Nightly data update service (runs vianightly_data_updater.py)precompute_coordinator– Coordinates creation of precompute tasks and publishes them to RabbitMQprecompute_worker– Dedicated precompute workers that consumeprecompute.tasksand write asteroid/comet positions to PostgreSQL (production main server)
Unified Workers and monitoring (local + worker hosts):
unified_worker– Unified Worker(s) with hybrid deduplication- Handles all task types: precompute, on-demand asteroids, on-demand comets
- Uses RabbitMQ Message Deduplication + PostgreSQL Advisory Locks
- Runs as a single container in local
docker-compose.ymland as scalable workers indocker-compose.workers.yml
worker_monitor– Real-time performance dashboard for workers (port configurable viaMONITOR_PORT)
Performance Benefits (Unified Worker Architecture):
- -80% Memory Usage – Unified Workers share Skyfield resources
- +35% Throughput – Hybrid deduplication eliminates duplicate work
- Unlimited Scaling – Horizontal scaling across multiple worker hosts
- Hybrid Deduplication – RabbitMQ + PostgreSQL Advisory Locks for duplicate protection
- RabbitMQ 4.1 – Modern message broker with management UI and advanced features
All services restart automatically unless stopped.
Worker Scaling (via .env):
# Main server (precompute_worker in docker-compose.production.yml)
PRECOMPUTE_WORKERS=4 # Dedicated precompute workers on the main server
# Worker hosts (unified_worker in docker-compose.workers.yml)
UNIFIED_WORKERS=8 # Number of unified workers per worker host (handles all task types)
WORKER_MONITOR=1 # Worker monitoring dashboardOn worker hosts, UNIFIED_WORKERS controls unified_worker scaling. If it is not set,
the deployment scripts fall back to PRECOMPUTE_WORKERS.
Hybrid Deduplication Configuration:
ENABLE_HYBRID_DEDUPLICATION=true # Enable Hybrid Deduplication
ASCII_SKY_DEDUPLICATION_TTL=300 # RabbitMQ message TTL (5 minutes)
ASCII_SKY_ADVISORY_LOCK_TTL=300 # PostgreSQL lock TTL (5 minutes)- First Startup: The app automatically downloads and stores MPC orbital data in PostgreSQL database
- Daily Updates: The
data_updaterservice automatically downloads fresh data at the configured hour (default 4:00 AM local time) - Performance: After initial setup, all data loads from database (10x faster than file parsing)
-
PostgreSQL Database
- All MPC orbital data (currently >1.1 million minor planets, ~4,000 comets)
- Pre-computed positions cached per location and time bucket
- Automatic nightly updates via
data_updaterservice - Multi-host capable: All workers connect to central PostgreSQL instance
- See
doc/ARCHITECTURE_DATABASE.mdanddoc/ARCHITECTURE_CACHE.mdfor details
-
RabbitMQ Task Queue
- Async computation of asteroid and comet positions
- Dedicated worker processes for parallel processing
- Cache-first strategy: returns cached data immediately, computes missing data in background
- Automatic retry and error handling
-
Magnitude Filters
- User-adjustable filters via UI (⚙️ button under "Visible Objects")
- Range: magnitude 10.0 to 20.0 (adjustable in 0.5 steps)
- Saved per user in
user_settings.json - Cache automatically recalculated when filters change (may take several minutes)
- Default values from environment variables:
- Asteroids:
ASCII_SKY_ASTEROID_MAX_APPARENT_MAG(default: 10.0) - Comets:
ASCII_SKY_COMET_MAX_APPARENT_MAG(default: 14.0)
- Asteroids:
- Cache strategy: All objects up to magnitude 20.0 are cached, filtering happens at API level
- Current filter values exposed via
/api/filtersendpoint
- User-adjustable filters via UI (⚙️ button under "Visible Objects")
- Ensure you have Python 3.14+ installed
- Install the required packages:
pip install -r requirements.txt
- Run the application:
uvicorn main:app --reload
- Open your browser and navigate to
http://localhost:8000
main.py- FastAPI application with celestial object calculation logicbright_asteroids.py- Bright asteroid pipeline (IAU H–G), Sun+orbit observation, event timescomets.py- Comet pipeline using MPC data with M1/k1 magnitude modeldb_utils.py- PostgreSQL database utilities for efficient data storage and retrievalnightly_data_updater.py- Automatic daily updates of asteroid and comet data (2:00 AM)settings.py- User/location settings; persists touser_settings.jsonde421.bsp- JPL ephemeris used by Skyfield
workers/unified_worker.py- Unified Worker with Hybrid Deduplication (replaces separate asteroid/comet workers)- Handles all task types: precompute, asteroids, comets
- Uses deterministic RabbitMQ message IDs + PostgreSQL Advisory Locks
- Vectorized magnitude calculations for performance
workers/precompute_worker.py- Dedicated precompute worker consumingprecompute.tasksand writing positions to PostgreSQLworkers/precompute_coordinator.py- Coordinates precomputation across workers (schedules tasks to RabbitMQ)
scripts/hybrid-setup.sh- All-in-One Hybrid Deduplication Setup (local, production, tests, monitoring)scripts/setup-production.sh- Multi-host production deploymentscripts/setup-firewall.sh- Firewall configuration for production
templates/- HTML templatesstatic/js/- JavaScript modulesconstants.js- Configuration parameters and centralized API endpointsskyRenderer.js- ASCII sky rendering, dialogs, zoom/pan functionalityskyManager.js- Sky rendering initialization and update managementi18n.js- Internationalization module with translationslocationDialog.js- User location dialog logicsettings.js- Frontend settings utilitieszodiacRenderer.js- Zodiac rendering utilities
static/css/- CSS styles
doc/asteroids.md- Asteroid position and magnitude pipeline (H–G model)doc/comets.md- Comet position and magnitude pipeline (M1/k1 model)doc/planets.md- Planet/Sun/Moon positions, magnitudes, and event timesdoc/ARCHITECTURE_INDEX.md- Architecture entry pointdoc/ARCHITECTURE_FLOW.md- System & precompute flowdoc/ARCHITECTURE_FLOW_API.md- API request flowdoc/ARCHITECTURE_CACHE.md- Cache strategydoc/ARCHITECTURE_DATABASE.md- Database schema and data flowdocs/hybrid-deduplication.md- Hybrid Deduplication Implementation (Phase 3)
Dockerfile- Docker configurationdocker-compose.yml- Development Docker Compose configurationdocker-compose.production.yml- Production configuration (main server)docker-compose.workers.yml- Worker servers configuration.env.example- Environment variables template (main server).env.b.example- Environment variables template (worker server B).env.c.example- Environment variables template (worker server C)requirements.txt- Python dependencies
scripts/hybrid-setup.sh- All-in-one Hybrid Deduplication setup (local, production, tests, monitoring)scripts/setup-production.sh- Production deployment with Hybrid Deduplicationscripts/update-production.sh- Production updates with Hybrid verification
scripts/setup-firewall.sh- Production firewall configurationscripts/init-postgres.sql- PostgreSQL schema initializationtest_hybrid_deduplication.py- Comprehensive Hybrid Deduplication tests
All frontend calls use centralized API endpoint constants in static/js/constants.js.
GET /api/celestial— real-time snapshot for Sun, Moon, and planets (no cache)GET /api/celestial/{body_id}— real-time data for a single celestial bodyGET /api/celestial/sunpath— yearly sunrise/sunset curve for the current or given locationGET /api/bright_asteroids— bright asteroids with H–G magnitudes, distances and rise/set/transit timesGET /api/asteroids— backward-compatible alias for/api/bright_asteroidsGET /api/comets— comets using MPC data with M1/k1 magnitude model and rise/set/transit times; optionalmax_cometsquery parameter; seedoc/comets.mdGET /api/zodiac— zodiac and selected constellations for a location and timeGET /api/session/location— get current session location (if set)POST /api/session/location— set session location; triggers background sunpath precomputeGET /api/config— exposure of magnitude limits and constellation defaults to the frontendGET /api/filters— get current user magnitude filters (applied at API layer, caches stay unfiltered)POST /api/filters— update user magnitude filtersGET /api/user/settings— get per-user settings (location, display, filters, theme, language, options)PUT /api/user/settings— upsert per-user settingsPOST /api/auth/register— register a new user (first user becomes admin)POST /api/auth/login— log in with username or email + passwordPOST /api/auth/logout— clear current sessionGET /api/auth/me— return authentication status and basic user infoGET /api/admin/users— list users (admin-only)PATCH /api/admin/users/{user_id}— update user (admin-only)DELETE /api/admin/users/{user_id}— delete user (admin-only)
The sky-data endpoints (/api/celestial, /api/bright_asteroids, /api/comets, /api/zodiac) accept an optional time query parameter to simulate calculations at a specific UTC instant. The value must be ISO 8601 and may end with Z or include a timezone offset. The backend normalizes it to UTC.
- Examples:
/api/celestial?lat=48.2082&lon=16.3738&elevation=171&time=2025-01-15T21:30:00Z/api/bright_asteroids?lat=48.2082&lon=16.3738&elevation=171&time=2025-01-15T21:30:00Z/api/comets?lat=48.2082&lon=16.3738&elevation=171&time=2025-01-15T21:30:00Z
Notes:
- Event windows (rise/set/transit) are anchored to UTC midnight of the simulated day.
- The response echoes
timein UTC ISO format. - The frontend simulated time controls persist an offset in minutes and, when enabled, automatically append
time=<ISO8601>to requests.
Times returned by the backend are plain local HH:MM. The frontend appends the localized hour label.
The application provides zoom and pan functionality for desktop users:
- Zoom Levels: Toggle between 1×, 2×, and 4× magnification using the zoom button in the top-right corner
- Pan Control: When zoomed (2× or 4×), click and drag to pan vertically through the sky view
- Visual Indicators:
- Cursor changes to "grab" when hovering over zoomable content
- Cursor changes to "grabbing" during active panning
- Mobile Behavior: Zoom and pan are automatically disabled on mobile devices (screen width ≤ 768px)
- Reset: Zoom level resets vertical offset when toggled
The application can be configured using the following environment variables in docker-compose.yml:
RABBITMQ_MAIN— Main server hostname used by workers (default:asciisky.example.org)RABBITMQ_B— Worker server B hostname (default:rabbit-b.example.org)RABBITMQ_C— Worker server C hostname (default:rabbit-c.example.org)RABBITMQ_MAIN_IP— Main server static IP used byextra_hostsindocker-compose.workers.yml(default:203.0.113.10)
Notes:
- These values are read from
.envbydocker-compose.workers.ymlto resolvePOSTGRES_HOST,RABBITMQ_URL, andextra_hosts. - Replace the example hostnames/IP with your production domain names or static IPs.
USE_RABBITMQ- Enable RabbitMQ for async processing (default: true)RABBITMQ_URL- RabbitMQ connection URL (default: amqp://admin:password@rabbitmq:5672/)RABBITMQ_TIMEOUT- RabbitMQ task timeout in seconds (default: 120)RABBITMQ_PREFETCH_COUNT- Prefetch count per worker (default: 1)
UNIFIED_WORKERS- Number of unified workers (handles all task types) (default: 8)WORKER_MONITOR- Worker monitoring dashboard instances (default: 1)PRECOMPUTE_WORKERS- Number of dedicatedprecompute_workerinstances on the main server (default: 4). On worker hosts it is used as a fallback ifUNIFIED_WORKERSis not set.
ASCII_SKY_PRECOMPUTE_HOURS– Number of hours into the future that the precompute coordinator generates tasks for (default: 720 = 30 days)ASCII_SKY_PRECOMPUTE_LOCATIONS– Optional JSON array of locations (latitude,longitude,elevation,name) used by the precompute coordinator in addition to the last global location, staticprecompute_locations.json, and all user locations from the database.
ENABLE_HYBRID_DEDUPLICATION- Enable Hybrid Deduplication (default: true)ASCII_SKY_DEDUPLICATION_TTL- RabbitMQ message TTL in seconds (default: 300)ASCII_SKY_ADVISORY_LOCK_TTL- PostgreSQL advisory lock TTL in seconds (default: 300)
Note: The old separate worker variables (ASTEROID_WORKERS, COMET_WORKERS) have been replaced by UNIFIED_WORKERS for better resource efficiency.
ASCII_SKY_UPDATE_HOUR- Hour of day for automatic data updates (default: 4, meaning 4:00 AM)
ASCII_SKY_ASTEROID_MAX_ABSOLUTE_MAG– Maximum absolute magnitude for asteroid prefiltering (Docker default: 14.0, code fallback: 12.0)ASCII_SKY_ASTEROID_MAX_APPARENT_MAG– Maximum apparent magnitude for asteroid processing/display (default: 10.0)ASCII_SKY_COMET_MAX_ABSOLUTE_MAG– Maximum absolute magnitude for comet prefiltering (Docker default: 20.0, code fallback: 18.0)ASCII_SKY_COMET_MAX_APPARENT_MAG– Maximum apparent magnitude for comets (default: 14.0)ASCII_SKY_ASTEROIDS_EVENTS_MAX– Max rise/set/transit computations per asteroid (default: 50)ASCII_SKY_COMET_EVENTS_MAX– Max rise/set/transit computations per comet (Docker default: 50, code fallback: 300)
PYTHONUNBUFFERED- Python output buffering (default: 1)TZ- Timezone for the application (default: Europe/Berlin)ASCII_SKY_SESSION_SECRET- Secret key for session encryption (default: change-in-production)
- Asteroids - Implementation details for asteroid tracking
- Comets - Comet tracking and M1/k1 magnitude model
- Planets - Planetary positions and calculations
- Architecture Index - Entry point for architecture docs
- System & Precompute Flow
- API Request Flow
- Cache Strategy
- Database Schema
- Production Deployment
- Firewall Setup
- Backend: FastAPI, Skyfield, PostgreSQL with Advisory Locks
- Performance: NumPy vectorization for high-speed magnitude calculations
- Message Queue: RabbitMQ 4.1 with async workers (deterministic IDs + PostgreSQL locks for dedup)
- Frontend: HTML, CSS, JavaScript
- Containerization: Docker, Docker Compose
All-in-One Setup:
./scripts/hybrid-setup.sh local # Start local development (keeps data)
./scripts/hybrid-setup.sh local --clean # Fresh start with empty database
./scripts/hybrid-setup.sh production # Deploy to production
./scripts/hybrid-setup.sh update # Update production
./scripts/hybrid-setup.sh test # Run tests
./scripts/hybrid-setup.sh summary # Show overviewKey Benefits:
- 100% Deduplication - No duplicate computations
- Unlimited Scaling - Horizontal across multiple hosts
- -80% Memory - Unified Workers share resources
- +35% Throughput - Hybrid eliminates duplicate work
Monitoring:
- RabbitMQ UI: http://localhost:15672
- Quick status:
./scripts/hybrid-setup.sh test - Complete overview:
./scripts/hybrid-setup.sh summary
Documentation: See docs/hybrid-deduplication.md for complete implementation details.
ASCII Sky uses NumPy vectorization for maximum performance:
Magnitude Calculations:
- 100-200x faster than traditional loops
- Vectorized asteroid apparent magnitude (H-G model)
- Vectorized comet apparent magnitude (M1/k1 model)
- Batch processing of multiple objects
Smart Pre-Filtering:
- NumPy rough magnitude estimation
- Filters impossible objects before expensive Skyfield calls
- Reduces observe() calls by 40-60%
- 3-stage pipeline for optimal efficiency
Performance Results:
- Overall: 2-4x faster (realistic)
- Many objects: 7-8x faster
- Magnitude: 100-200x faster
- Phase angle: 50-100x faster
- Rise/set: 10-50x faster (top 30-50 objects)
- DataFrame-first loading from filesystem cache (pickled MPC DataFrames) instead of reparsing raw MPC text files
- Intelligent caching with TTL and precompute windows
- PostgreSQL advisory locks for database consistency
This project uses Skyfield for astronomical calculations.
Skyfield: High precision research-grade positions for planets and Earth satellites generator Rhodes, Brandon Skyfield computes positions for the stars, planets, and satellites in orbit around the Earth. Its results should agree with the positions generated by the United States Naval Observatory and their Astronomical Almanac to within 0.0005 arcseconds (which equals half a "mas" or milliarcsecond). It computes geocentric coordinates or topocentric coordinates specific to your location on the Earth's surface. Skyfield accepts AstroPy (ascl:1304.002) time objects as input and can return results in native AstroPy units but is not dependend on AstroPy nor its compiled libraries. Code site: https://github.com/skyfielders/python-skyfield https://pypi.org/project/skyfield/ Used in: https://ui.adsabs.harvard.edu/abs/2019AdSpR..63.3795S Bibcode: 2019ascl.soft07024R Preferred citation method: https://ui.adsabs.harvard.edu/abs/2019ascl.soft07024R
This project was built with assistance from Windsurf (agentic AI coding assistant), GPT 5, 5.1, Claude 3.7, 4.5 Sonnet, SWE-1 and google-labs-jules[bot]. Babysitting by a human in a virtual environment.
This research has made use of data and/or services provided by the International Astronomical Union's Minor Planet Center.
You can try ASCII Sky online at: https://asciisky.eibrain.org/ When switching to a new location for the first time, calculations may take longer because asteroid and comet sky data for that location must be computed and cached first.
Registration & Accounts The login and registration feature is a convenience option for this demo. The app can be used fully without logging in.
When you register, the following data is stored: username, password hash (no plain-text password) and optional user settings such as location, display settings (e.g. horizontal shift), simulated time, brightness filters, color theme and language. Without logging in, these settings are only stored locally in your browser (localStorage), not on the server.
Important: There is no password reset and no support for account recovery. If you forget your password, the account cannot be restored – simply create a new account instead.
This repository is released under the MIT License.

