ASCII Sky is a test and demonstration application for the combined use of RabbitMQ and PostgreSQL. Its astronomy UI displays the current positions of the Sun, Moon, planets, asteroids, and comets as ASCII art, while providing a realistic workload for queues, workers, caching, persistence, and deduplication.
The supported default setup is a local, single-workstation system: the web application, PostgreSQL, RabbitMQ, and the workers all run on one computer with Docker Compose and are used from a browser on that same computer. It is intended for development and experimentation, not as a production-ready public service. The repository also contains historical and experimental multi-host deployment material; that is outside the primary usage described here.
- 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
- Distributed duplicate-work protection: deterministic RabbitMQ message IDs + PostgreSQL advisory locks
- Message IDs make equivalent tasks identifiable in logs and tooling; standard RabbitMQ queues do not deduplicate on this property
- Advisory locks serialize equivalent computations across workers that use the same PostgreSQL instance
- Locks are released explicitly after processing and automatically when their database connection closes
- Vectorized computation path: NumPy-based orbit propagation, geometry, and magnitude calculations
- Asteroids use an H filter, a rough apparent-magnitude filter, then batched propagation and H–G magnitude evaluation
- Comets use an M1 filter followed by vectorized geometry and M1/k1 magnitude evaluation
- Rise/set/transit calculations remain limited to the brightest result subset because they are comparatively expensive
- 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
The local Docker Compose stack runs these components on one machine:
- FastAPI web application and astronomy UI
- PostgreSQL for orbital data, user data, cached results, and advisory locks
- RabbitMQ for asynchronous task distribution
- Unified workers for precomputation and on-demand calculations
- A nightly data updater and a precompute coordinator
This setup demonstrates the complete RabbitMQ/PostgreSQL processing path without requiring additional hosts.
- Docker with Docker Compose v2
- A current browser
- Internet access during the initial data download
- Clone this repository
- Navigate to the project directory
- Start the local stack:
./scripts/hybrid-setup.sh local - Open
http://localhost:8000in a browser on the same computer.
The setup script creates .env from .env.example if necessary, builds the images, starts the services, initializes PostgreSQL, downloads the astronomy data, launches the RabbitMQ workers, and runs smoke tests. The first start can take several minutes because images and orbital data must be downloaded.
Local access points:
- Application: http://localhost:8000
- OpenAPI documentation: http://localhost:8000/docs
- RabbitMQ management UI: http://localhost:15672
The RabbitMQ login is taken from RABBITMQ_USER and RABBITMQ_PASSWORD in .env (development defaults: admin / changeme). These defaults and the published database/message-broker ports are suitable only for an isolated development machine. Do not expose this stack directly to an untrusted network.
# Show container status
docker compose ps
# Follow logs from all services
docker compose logs -f
# Stop the application and retain PostgreSQL/RabbitMQ data
docker compose down
# Start it again with the retained data
docker compose up -dPostgreSQL and RabbitMQ state is stored in named Docker volumes. Normal restarts retain it. To deliberately remove containers and all local database and queue data, run:
./scripts/hybrid-setup.sh local --clean- API requests use PostgreSQL as a cache and persistent data store.
- Cache misses create asynchronous tasks in RabbitMQ.
- Workers consume those tasks, calculate asteroid/comet positions, and write results to PostgreSQL.
- Deterministic message IDs and PostgreSQL advisory locks prevent duplicate work.
- The browser receives cached results while missing data can be calculated in the background.
See API Request Flow for the detailed processing path.
# Run the RabbitMQ/PostgreSQL integration checks
./scripts/hybrid-setup.sh test
# Exercise an API endpoint
curl -s "http://localhost:8000/api/bright_asteroids?lat=46.7632&lon=14.8417&elevation=405"The files below document an experimental multi-host topology. They are not required for, and are not the supported default of, the local single-workstation test application.
For 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:
- 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 workers from computing the same cache key concurrently
- Standard RabbitMQ queues for task distribution
- Horizontal worker scaling against the shared RabbitMQ/PostgreSQL services
See doc/PRODUCTION_DEPLOYMENT.md for detailed deployment instructions and doc/hybrid-deduplication.md for duplicate-work protection 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 only)
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
- Publishes deterministic RabbitMQ message IDs and uses PostgreSQL advisory locks while computing
- Runs as a locally replicated service in
docker-compose.ymland as scalable workers indocker-compose.workers.yml
worker_monitor– Real-time performance dashboard for workers (port configurable viaMONITOR_PORT)
Unified Worker Architecture:
- Loads shared Skyfield resources once per worker process and handles precompute, on-demand, and RPC tasks
- Supports horizontal scaling across worker hosts connected to the same broker and database
- Uses PostgreSQL advisory locks to avoid concurrent work for the same computation key
- Adds deterministic message IDs for traceability; this is not broker-side deduplication
- 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.
Duplicate-work protection configuration:
ENABLE_HYBRID_DEDUPLICATION=true # Legacy compatibility value; currently not read by Python
ASCII_SKY_DEDUPLICATION_TTL=300 # Legacy/configuration value; publishing code currently uses a fixed 5-minute message expiration
ASCII_SKY_ADVISORY_LOCK_TTL=300 # Passed to lock instrumentation; it does not expire a PostgreSQL advisory lock- 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) - Loading: Parsed MPC data is cached as pickled DataFrames on the filesystem; PostgreSQL stores orbital records and computed positions
-
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 (default: 4:00 AM)settings.py- User/location settings; persists touser_settings.jsonde421.bsp- JPL ephemeris used by Skyfield
workers/unified_worker.py- Unified Worker (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 PostgreSQLprecompute_coordinator.py- Coordinates precomputation across workers (schedules tasks to RabbitMQ)
scripts/hybrid-setup.sh- All-in-one local/production setup and diagnosticsscripts/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_API.md- API request flowdoc/ARCHITECTURE_CACHE.md- Cache strategydoc/ARCHITECTURE_DATABASE.md- Database schema and data flowdoc/hybrid-deduplication.md- Distributed duplicate-work protection
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 local/production setup and diagnosticsscripts/setup-production.sh- Experimental multi-host deploymentscripts/update-production.sh- Experimental multi-host updates
scripts/setup-firewall.sh- Production firewall configurationscripts/init-postgres.sql- PostgreSQL schema initializationtest_hybrid_deduplication.py- Message-ID and advisory-lock smoke 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; 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.PRECOMPUTE_TASK_CLAIM_TTL– Expiry for persistent duplicate-publication claims (default: 86400 seconds)
ENABLE_HYBRID_DEDUPLICATION,ASCII_SKY_DEDUPLICATION_TTL, andASCII_SKY_ADVISORY_LOCK_TTLremain in Compose and environment examples but are currently not read by Python. PostgreSQL advisory locks have no TTL; seedoc/hybrid-deduplication.md.
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)ASCII_SKY_EVENT_GRID_MINUTES– Event sampling interval; rise/set crossings are interpolated (default: 10)
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
- 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 overviewHow it works:
- Publishers attach a deterministic
message_idand a message expiration; the standard queues still accept duplicate messages. - Before computing, workers acquire a PostgreSQL advisory lock derived from the normalized location, time bucket, and object type.
- A second equivalent task waits for the lock and then rechecks the cache, so it normally skips work completed by the first worker.
- The lock is connection-scoped, has no time-based expiry, and is released in
finallyor when the database connection closes.
Monitoring:
- RabbitMQ UI: http://localhost:15672
- Quick status:
./scripts/hybrid-setup.sh test - Complete overview:
./scripts/hybrid-setup.sh summary
Documentation: See doc/hybrid-deduplication.md for implementation details and limitations.
Measured with the repository data (1,552,856 asteroids and 958 comets), Vienna, 2026-06-28 12:00 UTC, Python 3.14.6 / NumPy 2.5.0. DataFrames were already loaded, matching a long-running worker:
| Pipeline | Candidates/results | Wall time |
|---|---|---|
| Asteroids, magnitude ≤ 10 | 5,399 / 7 | 1.66 s |
| Comets, top results | 939 / 20 | 2.48 s |
These are current-path measurements, not a controlled before/after claim. Exact times vary with data, event limits, cache state, and hardware.
Asteroid pipeline (bright_asteroids.py):
- Replaced bulk
observe(target)loops with vectorised NumPy position arithmetic for heliocentric distancer, geocentric distanceδ, and phase angleα. observer.at(t)is now called only for the small final output set (≤ 50 objects) to produce RA / Dec / Alt / Az — not for every candidate.- Tightened the rough-magnitude pre-filter margin from +3.0 → +1.5 mag (26 % fewer candidates; profiling confirmed 0 bright objects missed at margin 1.0).
- Accuracy impact: < 0.001 AU in distance, < 0.001° in phase angle vs. the full light-time-corrected
observe()path — negligible for H–G magnitude computation.
Comet pipeline (comets.py):
- Fixed
AttributeError: eccentricitycrash:mpc.comet_orbit()requireseccentricity,perihelion_distance_au,inclination_degrees, etc. as row attributes; the_RowProxydata dictionary was missing these MPC field aliases → all aliases now generated consistently. - Fixed fractional perihelion-day handling (
perihelion_day = 20.3223→ float preserved ints.tt(y, m, d); previously truncated to integer). - Same
target.at(t)vectorisation as asteroids:observe()eliminated from the first pass, deferred to the final output loop only. - Perihelion epochs are prepared during ingestion; full Skyfield targets are built only for final results.
ASCII Sky uses NumPy vectorization to reduce Python-loop and per-object astronomy overhead:
Batched calculations:
- Asteroid orbit propagation, geometry, phase angle, and H–G apparent magnitude
- Comet distance geometry and M1/k1 apparent magnitude
- Batch processing of candidate arrays
Smart Pre-Filtering:
- NumPy rough magnitude estimation
- Filters impossible objects before expensive Skyfield calls
- Asteroid pipeline: absolute-magnitude filter → rough apparent-magnitude filter (+1.5 mag margin) → vectorized precise calculation
- Comet pipeline: absolute-magnitude filter → vectorized calculation → apparent-magnitude selection
- Event-time calculations are capped via
ASCII_SKY_ASTEROIDS_EVENTS_MAXandASCII_SKY_COMET_EVENTS_MAX - Event-grid resolution is controlled by
ASCII_SKY_EVENT_GRID_MINUTES(default 10 minutes; crossings are interpolated)
- 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
- Persistent precompute claims reduce duplicate queue publications across coordinator restarts
- Current, previous two, and next two hourly buckets receive the highest precompute priorities
- MPC updates invalidate only the changed asteroid/comet cache type, and unchanged datasets retain cached positions
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, Claude, Kimi, GLM, SWE 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.

