initial commit

pull/1/head
accius 6 days ago
commit 8030ef70ff

@ -0,0 +1,242 @@
# OpenHamClock
**A modern, open-source amateur radio dashboard - spiritual successor to HamClock**
*In memory of Elwood Downey, WB0OEW, creator of the original HamClock*
---
## Overview
OpenHamClock is a web-based kiosk-style application that provides real-time space weather, radio propagation information, and other data useful to amateur radio operators. It's designed to run on any platform with a web browser, with special consideration for Raspberry Pi deployments.
## Features
### Current Features (v1.0.0)
- **World Map with Day/Night Terminator**
- Real-time gray line display
- Sun position tracking
- DE and DX location markers with path visualization
- **Time Displays**
- UTC time (large, prominent display)
- Local time with date
- Uptime counter
- **Location Information**
- DE (your location) with Maidenhead grid square
- DX (target location) with grid square
- Short path and long path bearing
- Distance calculation
- Sunrise/sunset times for both locations
- **Space Weather Panel**
- Solar Flux Index (SFI)
- Sunspot Number
- K-Index and A-Index
- X-Ray flux
- Overall conditions assessment
- **Band Conditions**
- Visual display for all HF bands
- Color-coded conditions (Good/Fair/Poor)
- VHF band status
- **DX Cluster Feed**
- Live spot display (placeholder for API integration)
- Frequency, callsign, comment, and time
- **POTA Activity**
- Parks on the Air activator tracking
- Reference, frequency, and mode display
### Planned Features (Roadmap)
- [ ] Live API integration for space weather (NOAA, hamqsl.com)
- [ ] Real DX cluster connectivity (Telnet/WebSocket)
- [ ] Live POTA/SOTA API integration
- [ ] Satellite tracking
- [ ] VOACAP propagation predictions
- [ ] Contest calendar integration
- [ ] Hamlib/flrig radio control
- [ ] Rotator control
- [ ] Customizable panel layout
- [ ] Multiple map projections
- [ ] ADIF log file integration
- [ ] RESTful API for external control
- [ ] Touch screen support
- [ ] Alarm/alert system
## Installation
### Option 1: Direct Browser Use
Simply open `index.html` in any modern web browser. No server required!
```bash
# Clone or download the files
firefox index.html
# or
chromium-browser index.html
```
### Option 2: Raspberry Pi Kiosk Mode
1. **Install Raspberry Pi OS** (Desktop version recommended)
2. **Copy OpenHamClock files**
```bash
mkdir ~/openhamclock
cp index.html ~/openhamclock/
```
3. **Run the setup script**
```bash
chmod +x setup-pi.sh
./setup-pi.sh
```
4. **Manual Kiosk Setup** (alternative)
```bash
# Install unclutter to hide mouse cursor
sudo apt-get install unclutter
# Create autostart entry
mkdir -p ~/.config/autostart
cat > ~/.config/autostart/openhamclock.desktop << EOF
[Desktop Entry]
Type=Application
Name=OpenHamClock
Exec=chromium-browser --kiosk --noerrdialogs --disable-infobars --incognito file:///home/pi/openhamclock/index.html
EOF
```
### Option 3: Local Web Server
For advanced features (future API integrations), run with a local server:
```bash
# Python 3
cd openhamclock
python3 -m http.server 8080
# Then open http://localhost:8080
```
### Option 4: Electron Desktop App (Future)
Coming soon: Packaged desktop applications for Windows, macOS, and Linux.
## Configuration
Edit the following values in `index.html` to customize:
```javascript
// Your callsign
const [callsign, setCallsign] = useState('YOUR_CALL');
// Your location (lat, lon)
const [deLocation, setDeLocation] = useState({ lat: 39.7392, lon: -104.9903 });
// Default DX location
const [dxLocation, setDxLocation] = useState({ lat: 35.6762, lon: 139.6503 });
```
### Future Configuration File
A separate `config.js` will be provided for easier configuration:
```javascript
// config.js (coming soon)
export default {
callsign: 'K0CJH',
location: {
lat: 39.7392,
lon: -104.9903
},
theme: 'dark',
panels: ['clock', 'map', 'weather', 'dx', 'bands'],
// ... more options
};
```
## Display Resolutions
OpenHamClock is responsive and works at various resolutions:
| Resolution | Recommended Use |
|------------|-----------------|
| 800x480 | Small Pi displays, Inovato Quadra |
| 1024x600 | 7" Pi touchscreens |
| 1280x720 | HD ready monitors |
| 1600x960 | Recommended for full features |
| 1920x1080 | Full HD monitors |
| 2560x1440 | Large displays, high detail |
## API Data Sources (Planned)
| Data | Source | Status |
|------|--------|--------|
| Space Weather | NOAA SWPC | Planned |
| Band Conditions | hamqsl.com | Planned |
| DX Cluster | Various Telnet nodes | Planned |
| POTA | pota.app API | Planned |
| SOTA | sotawatch.org | Planned |
| Satellites | N2YO, CelesTrak | Planned |
## Technical Details
### Architecture
- **Frontend**: React 18 (single-file, no build required)
- **Styling**: CSS-in-JS with CSS variables for theming
- **Maps**: SVG-based rendering (no external tiles)
- **Data**: Currently static, API integration planned
### Browser Support
- Chrome/Chromium 80+
- Firefox 75+
- Safari 13+
- Edge 80+
### Dependencies
None! OpenHamClock loads React and Babel from CDN for simplicity.
For offline/airgapped deployments, download these files:
- react.production.min.js
- react-dom.production.min.js
- babel.min.js
## Contributing
Contributions are welcome! Areas where help is needed:
1. **API Integrations** - Connect to live data sources
2. **Satellite Tracking** - SGP4 propagator implementation
3. **Map Improvements** - Better landmass rendering, additional projections
4. **Testing** - Various Pi models and display sizes
5. **Documentation** - User guides, translations
6. **Design** - UI/UX improvements, accessibility
## License
MIT License - Free for personal and commercial use.
## Acknowledgments
- **Elwood Downey, WB0OEW** - Creator of the original HamClock. His work inspired thousands of amateur radio operators worldwide. Rest in peace, OM.
- **Amateur Radio Community** - For continued innovation and the spirit of experimentation.
## Contact
- **GitHub**: [github.com/your-repo/openhamclock](https://github.com)
- **Email**: your-email@example.com
---
**73 de K0CJH**
*"The original HamClock will cease to function in June 2026. OpenHamClock aims to carry on Elwood's legacy with a modern, open-source implementation that the community can maintain and improve together."*

@ -0,0 +1,194 @@
/**
* OpenHamClock Configuration
*
* Edit this file to customize your OpenHamClock instance.
* After making changes, refresh the browser to apply.
*
* For Raspberry Pi: Edit this file at ~/openhamclock/config.js
*/
const OpenHamClockConfig = {
// ========================================
// STATION INFORMATION
// ========================================
// Your callsign (displayed in header)
callsign: "K0CJH",
// Your location (DE - "This End")
// Find coordinates: https://www.latlong.net/
location: {
lat: 39.7392, // Latitude (positive = North, negative = South)
lon: -104.9903 // Longitude (positive = East, negative = West)
},
// Default DX location (far end for path calculations)
// Set to a frequently worked location, or leave as default
defaultDX: {
lat: 35.6762, // Tokyo, Japan
lon: 139.6503
},
// ========================================
// DISPLAY OPTIONS
// ========================================
// Theme: 'dark' (default), 'light' (coming soon)
theme: "dark",
// Time format: '24h' or '12h'
timeFormat: "24h",
// Date format: 'iso' (YYYY-MM-DD), 'us' (MM/DD/YYYY), 'eu' (DD/MM/YYYY)
dateFormat: "iso",
// Show seconds in time display
showSeconds: true,
// ========================================
// PANELS TO DISPLAY
// ========================================
// Enable/disable individual panels
panels: {
utcClock: true,
localClock: true,
worldMap: true,
deInfo: true,
dxInfo: true,
spaceWeather: true,
bandConditions: true,
dxCluster: true,
potaActivity: true,
sotaActivity: false, // Coming soon
satellites: false, // Coming soon
contests: false // Coming soon
},
// ========================================
// MAP OPTIONS
// ========================================
map: {
// Map style: 'standard', 'terrain', 'minimal'
style: "standard",
// Show day/night terminator (gray line)
showTerminator: true,
// Show grid lines
showGrid: true,
// Show path between DE and DX
showPath: true,
// Path style: 'greatCircle' or 'straight'
pathStyle: "greatCircle"
},
// ========================================
// DX CLUSTER SETTINGS
// ========================================
dxCluster: {
// Enable live DX cluster connection
enabled: false, // Set to true when API is implemented
// Cluster node (Telnet)
node: "dxc.nc7j.com",
port: 7373,
// Login callsign (usually your call)
login: "K0CJH",
// Filter options
filters: {
// Only show spots for these bands (empty = all bands)
bands: [], // e.g., ["20m", "40m", "15m"]
// Only show these modes (empty = all modes)
modes: [], // e.g., ["FT8", "CW", "SSB"]
// Minimum spot age to display (minutes)
maxAge: 30
}
},
// ========================================
// POTA/SOTA SETTINGS
// ========================================
pota: {
enabled: true,
// Filter by state/region (empty = all)
regions: [], // e.g., ["K-CO", "K-WY"]
// Maximum number of spots to show
maxSpots: 10
},
sota: {
enabled: false,
// Filter by association (empty = all)
associations: [], // e.g., ["W7C", "W0C"]
maxSpots: 10
},
// ========================================
// SPACE WEATHER DATA SOURCES
// ========================================
dataRefresh: {
// Refresh interval in seconds
spaceWeather: 300, // 5 minutes
bandConditions: 300, // 5 minutes
dxCluster: 5, // 5 seconds (live)
pota: 60, // 1 minute
sota: 60 // 1 minute
},
// ========================================
// SOUND/ALERTS (Coming Soon)
// ========================================
alerts: {
enabled: false,
// Sound alerts for new DX spots
dxClusterSound: false,
// Alert for specific DXCC entities
watchedEntities: [], // e.g., ["VP8", "3Y", "P5"]
// Alert for space weather events
spaceWeatherAlert: false
},
// ========================================
// ADVANCED
// ========================================
advanced: {
// Enable debug logging
debug: false,
// Custom CSS (appended to page)
customCSS: "",
// API endpoints (for self-hosted data servers)
apiEndpoints: {
spaceWeather: null, // null = use default
dxCluster: null,
pota: null,
sota: null
}
}
};
// Export for use in main application
if (typeof module !== 'undefined' && module.exports) {
module.exports = OpenHamClockConfig;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""
OpenHamClock Development Server
A simple HTTP server for OpenHamClock with API proxy capabilities.
This allows the application to fetch live data from external sources
without CORS issues.
Usage:
python3 server.py [port]
Default port: 8080
Open http://localhost:8080 in your browser
Requirements:
Python 3.7+
requests library (optional, for API proxy)
"""
import http.server
import socketserver
import json
import urllib.request
import urllib.error
import sys
import os
from datetime import datetime
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8080
# API endpoints for live data
API_ENDPOINTS = {
'solarflux': 'https://services.swpc.noaa.gov/json/solar-cycle/observed-solar-flux.json',
'kindex': 'https://services.swpc.noaa.gov/json/planetary_k_index_1m.json',
'xray': 'https://services.swpc.noaa.gov/json/goes/primary/xrays-7-day.json',
'sunspots': 'https://services.swpc.noaa.gov/json/solar-cycle/sunspots.json',
'pota': 'https://api.pota.app/spot/activator',
'bands': 'https://www.hamqsl.com/solarxml.php', # HamQSL solar data
}
class OpenHamClockHandler(http.server.SimpleHTTPRequestHandler):
"""Custom HTTP handler with API proxy support."""
def do_GET(self):
# Handle API proxy requests
if self.path.startswith('/api/'):
self.handle_api()
else:
# Serve static files
super().do_GET()
def handle_api(self):
"""Proxy API requests to avoid CORS issues."""
endpoint = self.path.replace('/api/', '').split('?')[0]
if endpoint not in API_ENDPOINTS:
self.send_error(404, f"Unknown API endpoint: {endpoint}")
return
try:
url = API_ENDPOINTS[endpoint]
print(f"[{datetime.now().strftime('%H:%M:%S')}] Fetching: {url}")
# Make the request
req = urllib.request.Request(
url,
headers={'User-Agent': 'OpenHamClock/1.0'}
)
with urllib.request.urlopen(req, timeout=10) as response:
data = response.read()
content_type = response.headers.get('Content-Type', 'application/json')
# Send response
self.send_response(200)
self.send_header('Content-Type', content_type)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Cache-Control', 'max-age=60')
self.end_headers()
self.wfile.write(data)
except urllib.error.URLError as e:
print(f"[ERROR] Failed to fetch {endpoint}: {e}")
self.send_error(502, f"Failed to fetch data: {e}")
except Exception as e:
print(f"[ERROR] {e}")
self.send_error(500, str(e))
def log_message(self, format, *args):
"""Custom logging format."""
if args[0].startswith('GET /api/'):
return # Already logged in handle_api
print(f"[{datetime.now().strftime('%H:%M:%S')}] {args[0]}")
def main():
# Change to the directory containing this script
script_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(script_dir)
print("=" * 50)
print(" OpenHamClock Development Server")
print("=" * 50)
print()
print(f" Serving from: {script_dir}")
print(f" URL: http://localhost:{PORT}")
print(f" Press Ctrl+C to stop")
print()
print(" Available API endpoints:")
for name, url in API_ENDPOINTS.items():
print(f" /api/{name}")
print()
print("=" * 50)
print()
with socketserver.TCPServer(("", PORT), OpenHamClockHandler) as httpd:
httpd.allow_reuse_address = True
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nServer stopped.")
if __name__ == "__main__":
main()

@ -0,0 +1,194 @@
#!/bin/bash
#
# OpenHamClock Raspberry Pi Setup Script
# Configures Pi for kiosk mode operation
#
# Usage: chmod +x setup-pi.sh && ./setup-pi.sh
#
set -e
echo "========================================"
echo " OpenHamClock Raspberry Pi Setup"
echo "========================================"
echo ""
# Check if running on Raspberry Pi
if [ ! -f /proc/device-tree/model ]; then
echo "Warning: This doesn't appear to be a Raspberry Pi."
echo "Continuing anyway..."
fi
# Get the current user
CURRENT_USER=$(whoami)
HOME_DIR=$(eval echo ~$CURRENT_USER)
OPENHAMCLOCK_DIR="$HOME_DIR/openhamclock"
echo "Installing for user: $CURRENT_USER"
echo "Install directory: $OPENHAMCLOCK_DIR"
echo ""
# Update system
echo ">>> Updating system packages..."
sudo apt-get update -qq
# Install required packages
echo ">>> Installing required packages..."
sudo apt-get install -y -qq \
chromium-browser \
unclutter \
xdotool \
x11-xserver-utils
# Create OpenHamClock directory if it doesn't exist
echo ">>> Setting up OpenHamClock directory..."
mkdir -p "$OPENHAMCLOCK_DIR"
# Copy index.html if it exists in the current directory
if [ -f "index.html" ]; then
cp index.html "$OPENHAMCLOCK_DIR/"
echo ">>> Copied index.html to $OPENHAMCLOCK_DIR"
fi
# Create the autostart directory
echo ">>> Configuring autostart..."
mkdir -p "$HOME_DIR/.config/autostart"
# Create autostart entry for OpenHamClock
cat > "$HOME_DIR/.config/autostart/openhamclock.desktop" << EOF
[Desktop Entry]
Type=Application
Name=OpenHamClock
Comment=Amateur Radio Dashboard
Exec=/bin/bash $OPENHAMCLOCK_DIR/start-kiosk.sh
Terminal=false
Hidden=false
X-GNOME-Autostart-enabled=true
EOF
# Create kiosk start script
echo ">>> Creating kiosk start script..."
cat > "$OPENHAMCLOCK_DIR/start-kiosk.sh" << 'EOF'
#!/bin/bash
#
# OpenHamClock Kiosk Mode Launcher
#
# Wait for desktop to be ready
sleep 5
# Disable screen blanking and power management
xset s off
xset -dpms
xset s noblank
# Hide the mouse cursor after 3 seconds of inactivity
unclutter -idle 3 -root &
# Kill any existing Chromium processes
pkill -f chromium-browser || true
sleep 2
# Start Chromium in kiosk mode
chromium-browser \
--kiosk \
--noerrdialogs \
--disable-infobars \
--disable-session-crashed-bubble \
--disable-restore-session-state \
--disable-features=TranslateUI \
--check-for-update-interval=31536000 \
--disable-component-update \
--overscroll-history-navigation=0 \
--incognito \
"file://$HOME/openhamclock/index.html"
EOF
chmod +x "$OPENHAMCLOCK_DIR/start-kiosk.sh"
# Create a stop script
cat > "$OPENHAMCLOCK_DIR/stop-kiosk.sh" << 'EOF'
#!/bin/bash
# Stop OpenHamClock kiosk mode
pkill -f chromium-browser
pkill -f unclutter
echo "OpenHamClock stopped."
EOF
chmod +x "$OPENHAMCLOCK_DIR/stop-kiosk.sh"
# Create a restart script
cat > "$OPENHAMCLOCK_DIR/restart-kiosk.sh" << 'EOF'
#!/bin/bash
# Restart OpenHamClock
$HOME/openhamclock/stop-kiosk.sh
sleep 2
$HOME/openhamclock/start-kiosk.sh &
EOF
chmod +x "$OPENHAMCLOCK_DIR/restart-kiosk.sh"
# Create systemd service for headless operation (optional)
echo ">>> Creating systemd service (for headless operation)..."
sudo tee /etc/systemd/system/openhamclock.service > /dev/null << EOF
[Unit]
Description=OpenHamClock Kiosk
After=graphical-session.target
[Service]
Type=simple
User=$CURRENT_USER
Environment=DISPLAY=:0
ExecStart=/bin/bash $OPENHAMCLOCK_DIR/start-kiosk.sh
Restart=on-failure
RestartSec=5
[Install]
WantedBy=graphical-session.target
EOF
# Disable screen blanking in config.txt
echo ">>> Configuring boot options..."
if ! grep -q "consoleblank=0" /boot/cmdline.txt 2>/dev/null; then
sudo sed -i '$ s/$/ consoleblank=0/' /boot/cmdline.txt 2>/dev/null || true
fi
# Configure GPU memory for better graphics (optional)
if ! grep -q "gpu_mem=" /boot/config.txt 2>/dev/null; then
echo "gpu_mem=128" | sudo tee -a /boot/config.txt > /dev/null 2>/dev/null || true
fi
echo ""
echo "========================================"
echo " Setup Complete!"
echo "========================================"
echo ""
echo "OpenHamClock has been installed to: $OPENHAMCLOCK_DIR"
echo ""
echo "Files created:"
echo " - $OPENHAMCLOCK_DIR/index.html (main application)"
echo " - $OPENHAMCLOCK_DIR/start-kiosk.sh (start in kiosk mode)"
echo " - $OPENHAMCLOCK_DIR/stop-kiosk.sh (stop kiosk)"
echo " - $OPENHAMCLOCK_DIR/restart-kiosk.sh (restart kiosk)"
echo ""
echo "Auto-start:"
echo " OpenHamClock will automatically start on next boot."
echo ""
echo "Manual commands:"
echo " Start: ~/openhamclock/start-kiosk.sh"
echo " Stop: ~/openhamclock/stop-kiosk.sh"
echo " Restart: ~/openhamclock/restart-kiosk.sh"
echo ""
echo "To disable auto-start:"
echo " rm ~/.config/autostart/openhamclock.desktop"
echo ""
echo "Reboot recommended to apply all changes."
echo ""
echo "73 de OpenHamClock!"
echo ""
read -p "Would you like to reboot now? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
sudo reboot
fi
Loading…
Cancel
Save

Powered by TurnKey Linux.