Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
b195e5b
Merge pull request #32 from accius/Staging
MichaelWheeley Apr 2, 2026
2092e2c
add station altitude, minimum elevation settings
MichaelWheeley Apr 3, 2026
f84b472
consume station altitude
MichaelWheeley Apr 3, 2026
c23de1a
Merge pull request #33 from accius/Staging
MichaelWheeley Apr 3, 2026
39a4091
Merge branch 'feature/thai-language-dev' into feature/sat_predict_dev
MichaelWheeley Apr 3, 2026
7aa6c90
Merge pull request #34 from accius/Staging
MichaelWheeley Apr 3, 2026
080f59e
is visible only when exceeds min elevation
MichaelWheeley Apr 3, 2026
00a192e
satellite footprint disable green when elevation not exceeding minimum
MichaelWheeley Apr 3, 2026
9957a58
satellite details reorder and add subtle colored boxing
MichaelWheeley Apr 3, 2026
61c1478
minor correction to input variables
MichaelWheeley Apr 3, 2026
07d00c5
Merge pull request #35 from accius/Staging
MichaelWheeley Apr 4, 2026
1db9a8b
Merge branch 'feature/thai-language-dev' into feature/sat_predict_dev
MichaelWheeley Apr 4, 2026
2df9780
style highliting green background when satellite is visible
MichaelWheeley Apr 4, 2026
0ecd4b4
orbit predict
MichaelWheeley Apr 4, 2026
e1c967b
adjust table columns
MichaelWheeley Apr 4, 2026
6603c7b
make satellite details with scrollable by blocking propagation of mou…
MichaelWheeley Apr 5, 2026
67b3029
add time from now column
MichaelWheeley Apr 5, 2026
ed9cb3f
updates table every minute
MichaelWheeley Apr 5, 2026
52a5a0c
wire global variables to satellite prediction
MichaelWheeley Apr 5, 2026
a5fd0ac
fix table within a table html
MichaelWheeley Apr 5, 2026
840c60e
remove dead code in useSatelliteLayer.js, this appears to be unutiliz…
MichaelWheeley Apr 5, 2026
b13b635
satellite prediction modal updated every 1s however satellite passes …
MichaelWheeley Apr 5, 2026
5897abc
move ./src/plugins/layers/satelliteOrbit.js to ./src/utils/orbit.js
MichaelWheeley Apr 5, 2026
b075109
enhance table countdown, add ACTIVE, and drop passes ended
MichaelWheeley Apr 6, 2026
ffaa64f
remove # hardcoded colors, replace with rgba(), modify opacity on sel…
MichaelWheeley Apr 6, 2026
4ec9e6b
must fix per https://github.com/accius/openhamclock/pull/877
MichaelWheeley Apr 7, 2026
74f8d02
settingsPanel.jsx ?? vs ||
MichaelWheeley Apr 7, 2026
3e7c446
Merge branch 'feature/sat_predict_dev' into sat_predict_temp
MichaelWheeley Apr 7, 2026
7c83c34
clear interval on load
MichaelWheeley Apr 7, 2026
803476c
introduced handlers
MichaelWheeley Apr 7, 2026
25f8180
handle mouse dragging
MichaelWheeley Apr 7, 2026
f2e1c5d
intermedite color dev
MichaelWheeley Apr 8, 2026
ba4e6df
intermdiate color dev
MichaelWheeley Apr 8, 2026
dbb4f2a
satellite color updates
MichaelWheeley Apr 8, 2026
466283e
plus symbol in time from now
MichaelWheeley Apr 9, 2026
e6d11a4
Merge branch 'feature/sat_predict_dev2' into sat_predict_temp
MichaelWheeley Apr 9, 2026
7658dab
forward copy satellite color style changes
MichaelWheeley Apr 14, 2026
2c215e3
limit station altitude [-500, 9000]m, defaults from || to ??, remove …
MichaelWheeley Apr 14, 2026
dbb8446
Merge branch 'feature/sat_predict_dev' into feature/sat_predict_dev2
MichaelWheeley Apr 15, 2026
a96e711
Merge branch 'sat_predict_temp' into feature/sat_predict_dev2
MichaelWheeley Apr 15, 2026
ed5903f
remove unused computePassesSwath() from orbit.js
MichaelWheeley Apr 15, 2026
57ca9dd
remove enter key as closure key for modal
MichaelWheeley Apr 15, 2026
73d6a3f
set min/max height/width
MichaelWheeley Apr 15, 2026
2811f7f
eliminate dayjs as dependency
MichaelWheeley Apr 15, 2026
76d8d9d
orbit.js add optional logging, fix prediction algorithm
MichaelWheeley Apr 16, 2026
dfb3aba
fix click propagation problem preventing PREDICT button working when …
MichaelWheeley Apr 16, 2026
d71a530
fix bug where zero minimum elevation is reverting to 5
MichaelWheeley Apr 16, 2026
41abbae
Merge branch 'feature/sat_predict_dev' into feature/sat_predict_dev2
MichaelWheeley Apr 16, 2026
e1a1b92
format with local time and not UTC
MichaelWheeley Apr 16, 2026
b7c2be2
clean-up event listeners on modal close
MichaelWheeley Apr 17, 2026
2862fd4
fix modal 1sec refresh accumulation of event listeners
MichaelWheeley Apr 17, 2026
e9fda12
fix other event listener clean-up issues
MichaelWheeley Apr 17, 2026
6363f98
cleanup added to global window.openSatellitePredict
MichaelWheeley Apr 17, 2026
a70958b
remove reference to useAppConfig
MichaelWheeley Apr 17, 2026
92d8eed
ensure config and satellite propagate in all views
MichaelWheeley Apr 17, 2026
9937d7c
staging merge fix conflict fix, add deLat, deLon to WorldMap.jsx
MichaelWheeley Apr 17, 2026
5499dd2
Merge branch 'Staging' into feature/sat_predict_dev2
MichaelWheeley Apr 17, 2026
90f2d3e
consistency, default station altitude 100m, default minimum elevation…
MichaelWheeley Apr 17, 2026
b562a47
Fix: change all four sites from || to ??
MichaelWheeley Apr 20, 2026
1976a24
border-bottom to var(--border-color)
MichaelWheeley Apr 20, 2026
bc6cbca
--accent-cyan --accent-green --accent-amber via lookup
MichaelWheeley Apr 20, 2026
d8c8cd2
accentCyan via lookup
MichaelWheeley Apr 20, 2026
1fcc3a4
undo previous accentCyan change to useSatellites.js, it makes no sens…
MichaelWheeley Apr 20, 2026
2016182
Merge branch 'feature/sat_predict_dev' into feature/sat_predict_dev2
MichaelWheeley Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ const App = () => {

const propagation = usePropagation(config.location, dxLocation, config.propagation);
const mySpots = useMySpots(config.callsign);
const satellites = useSatellites(config.location);
const satellites = useSatellites(config.location, config.satellite);
const localWeather = useWeather(config.location, config.allUnits);
const dxWeather = useWeather(dxLocation, config.allUnits);
const localAlerts = useWeatherAlerts(config.location);
Expand Down
1 change: 1 addition & 0 deletions src/DockableApp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ export const DockableApp = ({
const renderWorldMap = () => (
<div style={{ height: '100%', width: '100%', position: 'relative' }}>
<WorldMap
config={config}
deLocation={config.location}
dxLocation={dxLocation}
onDXChange={handleDXChange}
Expand Down
80 changes: 79 additions & 1 deletion src/components/SettingsPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export const SettingsPanel = ({
const [gridSquare, setGridSquare] = useState(config?.locator || '');
const [lat, setLat] = useState(config?.location?.lat ?? 0);
const [lon, setLon] = useState(config?.location?.lon ?? 0);
const [stationAlt, setStationAlt] = useState(config?.location?.stationAlt ?? 100);
const [minElev, setMinElev] = useState(config?.satellite?.minElev ?? 5.0);
const [layout, setLayout] = useState(config?.layout || 'modern');
const [mouseZoom, setMouseZoom] = useState(config?.mouseZoom || 50);
const [timezone, setTimezone] = useState(config?.timezone || '');
Expand Down Expand Up @@ -180,6 +182,8 @@ export const SettingsPanel = ({
setheaderSize(config.headerSize || 1.0);
setLat(config.location?.lat ?? 0);
setLon(config.location?.lon ?? 0);
setStationAlt(config.location?.stationAlt ?? 100);
setMinElev(config.satellite?.minElev ?? 5.0);
setLayout(config.layout || 'modern');
setMouseZoom(config.mouseZoom || 50);
setTimezone(config.timezone || '');
Expand Down Expand Up @@ -420,7 +424,8 @@ export const SettingsPanel = ({
headerSize: headerSize,
swapHeaderClocks,
showMutualReception,
location: { lat: parseFloat(lat), lon: parseFloat(lon) },
location: { lat: parseFloat(lat) || 0, lon: parseFloat(lon) || 0, stationAlt: parseInt(stationAlt) || 100 },
satellite: { minElev: isNaN(parseFloat(minElev)) ? 5.0 : parseFloat(minElev) },
theme,
customTheme,
layout,
Expand Down Expand Up @@ -3390,6 +3395,79 @@ export const SettingsPanel = ({
Footprints
</label>
</div>

{/* station altitude and minimum elevation inputs */}
<div
style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px', marginBottom: '12px' }}
>
<div>
<label
style={{
display: 'block',
marginBottom: '6px',
color: 'var(--text-muted)',
fontSize: '11px',
textTransform: 'uppercase',
}}
>
Station Altitude [m]
</label>
<input
type="number"
step="1"
min="-500"
max="9000"
value={isNaN(stationAlt) ? '' : stationAlt}
onChange={(e) =>
setStationAlt(isNaN(e.target.valueAsNumber) ? 100 : e.target.valueAsNumber)
}
style={{
width: '100%',
padding: '10px',
background: 'var(--bg-tertiary)',
border: '1px solid var(--border-color)',
borderRadius: '6px',
color: 'var(--text-primary)',
fontSize: '14px',
fontFamily: 'JetBrains Mono, monospace',
boxSizing: 'border-box',
}}
/>
</div>
<div>
<label
style={{
display: 'block',
marginBottom: '6px',
color: 'var(--text-muted)',
fontSize: '11px',
textTransform: 'uppercase',
}}
>
Minimum Elevation [°]
</label>
<input
type="number"
step="0.1"
min="-5.0"
max="89.0"
value={isNaN(minElev) ? '' : minElev}
onChange={(e) => setMinElev(isNaN(e.target.valueAsNumber) ? 5.0 : e.target.valueAsNumber)}
style={{
width: '100%',
padding: '10px',
background: 'var(--bg-tertiary)',
border: '1px solid var(--border-color)',
borderRadius: '6px',
color: 'var(--text-primary)',
fontSize: '14px',
fontFamily: 'JetBrains Mono, monospace',
boxSizing: 'border-box',
}}
/>
</div>
</div>

{/* Lead Time Slider WIP
<div style={{ marginTop: '8px' }}>
<label style={{
Expand Down
58 changes: 39 additions & 19 deletions src/components/WorldMap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import { mapDefs as WWFFDefs } from './WWFFPanel.jsx';
const POPUP_AUTO_CLOSE_MS = 20_000;

export const WorldMap = ({
config,
deLocation,
dxLocation,
onDXChange,
Expand Down Expand Up @@ -2211,25 +2212,44 @@ export const WorldMap = ({
{/* Key includes projection so hooks fully remount when map instance changes.
This resets internal refs (layerGroupRef, controlRef) that are bound to a
specific Leaflet map — without this, layers stay on the hidden old map. */}
{getAllLayers().map((layerDef) => (
<PluginLayer
key={`${layerDef.id}-${isAzimuthal ? 'az' : 'merc'}`}
plugin={layerDef}
enabled={pluginLayerStates[layerDef.id]?.enabled ?? layerDef.defaultEnabled}
opacity={pluginLayerStates[layerDef.id]?.opacity ?? layerDef.defaultOpacity}
onDXChange={onDXChange}
mapBandFilter={mapBandFilter}
config={pluginLayerStates[layerDef.id]?.config ?? layerDef.config}
map={isAzimuthal ? azimuthalMapRef.current : mapInstanceRef.current}
satellites={satellites}
allUnits={allUnits}
callsign={callsign}
locator={deLocator}
deLat={deLocation?.lat ?? null}
deLon={deLocation?.lon ?? null}
lowMemoryMode={lowMemoryMode}
/>
))}
{getAllLayers().map((layerDef) => {
// Merge location config into satellite layer to keep config access consistent
const layerConfig = pluginLayerStates[layerDef.id]?.config ?? layerDef.config;
const finalConfig =
layerDef.id === 'satellites' && deLocation
? {
...layerConfig,
location: {
lat: deLocation.lat,
lon: deLocation.lon,
stationAlt: deLocation.stationAlt || 100,
},
satellite: {
minElev: config?.satellite?.minElev ?? layerConfig?.satellite?.minElev ?? 5,
},
}
: layerConfig;

return (
<PluginLayer
key={`${layerDef.id}-${isAzimuthal ? 'az' : 'merc'}`}
plugin={layerDef}
enabled={pluginLayerStates[layerDef.id]?.enabled ?? layerDef.defaultEnabled}
opacity={pluginLayerStates[layerDef.id]?.opacity ?? layerDef.defaultOpacity}
onDXChange={onDXChange}
mapBandFilter={mapBandFilter}
config={finalConfig}
map={isAzimuthal ? azimuthalMapRef.current : mapInstanceRef.current}
satellites={satellites}
allUnits={allUnits}
callsign={callsign}
locator={deLocator}
deLat={deLocation?.lat ?? null}
deLon={deLocation?.lon ?? null}
lowMemoryMode={lowMemoryMode}
/>
);
})}

{/* Unified map control dock */}
{!isAzimuthal && (
Expand Down
15 changes: 10 additions & 5 deletions src/hooks/useSatellites.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function round(value, decimals) {
return Math.round(value * factor) / factor;
}

export const useSatellites = (observerLocation) => {
export const useSatellites = (observerLocation, satelliteConfig) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [tleData, setTleData] = useState({});
Expand Down Expand Up @@ -51,7 +51,7 @@ export const useSatellites = (observerLocation) => {
const observerGd = {
longitude: satellite.degreesToRadians(observerLocation.lon),
latitude: satellite.degreesToRadians(observerLocation.lat),
height: 0.1, // km above sea level
height: (observerLocation.stationAlt ?? 100) / 1000, // above sea level [km], stationAlt is [m]), defaults to 100m
};

Object.entries(tleData).forEach(([name, tle]) => {
Expand Down Expand Up @@ -82,10 +82,13 @@ export const useSatellites = (observerLocation) => {
const elevation = satellite.radiansToDegrees(lookAngles.elevation);
const rangeSat = lookAngles.rangeSat;

// Calculate range-rate and doppler factor, only if satellite is above horizon
const isVisible = elevation >= (satelliteConfig?.minElev ?? 5.0); // visible only if above minimum elevation

// Calculate range-rate and doppler factor, only if satellite is visible
let dopplerFactor = 1;
let rangeRate = 0;
if (elevation > 0) {

if (isVisible) {
const observerEcf = satellite.geodeticToEcf(observerGd);
const velocityEcf = satellite.eciToEcf(velocityEci, gmst);
dopplerFactor = satellite.dopplerFactor(observerEcf, positionEcf, velocityEcf);
Expand Down Expand Up @@ -125,6 +128,8 @@ export const useSatellites = (observerLocation) => {

positions.push({
name: tle.name || name,
tle1: line1,
tle2: line2,
lat,
lon,
alt: round(alt, 1),
Expand All @@ -134,7 +139,7 @@ export const useSatellites = (observerLocation) => {
range: round(rangeSat, 1),
rangeRate: round(rangeRate, 3),
dopplerFactor: round(dopplerFactor, 9),
isVisible: elevation > 0,
isVisible, // visible if above minimum elevation
isPopular: tle.priority <= 2,
track,
footprintRadius: Math.round(footprintRadius),
Expand Down
3 changes: 3 additions & 0 deletions src/layouts/ClassicLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,7 @@ export default function ClassicLayout(props) {
{/* CENTER: World Map */}
<div style={{ position: 'relative', overflow: 'hidden' }}>
<WorldMap
config={config}
deLocation={config.location}
dxLocation={dxLocation}
onDXChange={handleDXChange}
Expand Down Expand Up @@ -1415,6 +1416,7 @@ export default function ClassicLayout(props) {
{/* MAP */}
<div style={{ flex: 1, position: 'relative' }}>
<WorldMap
config={config}
deLocation={config.location}
dxLocation={dxLocation}
onDXChange={handleDXChange}
Expand Down Expand Up @@ -2088,6 +2090,7 @@ export default function ClassicLayout(props) {
{/* Map */}
<div style={{ flex: 1, position: 'relative' }}>
<WorldMap
config={config}
deLocation={config.location}
dxLocation={dxLocation}
onDXChange={handleDXChange}
Expand Down
1 change: 1 addition & 0 deletions src/layouts/EmcommLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ export default function EmcommLayout(props) {
{/* MAP */}
<div style={{ position: 'relative', overflow: 'hidden' }}>
<WorldMap
config={config}
deLocation={config.location}
dxLocation={dxLocation}
onDXChange={handleDXChange}
Expand Down
1 change: 1 addition & 0 deletions src/layouts/ModernLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export default function ModernLayout(props) {
const mapComponent = (style) => (
<div style={{ position: 'relative', borderRadius: '6px', overflow: 'hidden', ...style }}>
<WorldMap
config={config}
deLocation={config.location}
dxLocation={dxLocation}
onDXChange={handleDXChange}
Expand Down
Loading
Loading