diff --git a/public/index.html b/public/index.html index 8f1a647..9a8f14b 100644 --- a/public/index.html +++ b/public/index.html @@ -549,16 +549,92 @@ return { data, loading }; }; - const useBandConditions = () => { - const [data, setData] = useState([ - { band: '160m', condition: 'FAIR' }, { band: '80m', condition: 'GOOD' }, - { band: '40m', condition: 'GOOD' }, { band: '30m', condition: 'GOOD' }, - { band: '20m', condition: 'GOOD' }, { band: '17m', condition: 'GOOD' }, - { band: '15m', condition: 'FAIR' }, { band: '12m', condition: 'FAIR' }, - { band: '10m', condition: 'POOR' }, { band: '6m', condition: 'POOR' }, - { band: '2m', condition: 'GOOD' }, { band: '70cm', condition: 'GOOD' } - ]); - const [loading, setLoading] = useState(false); + const useBandConditions = (spaceWeather) => { + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (!spaceWeather?.solarFlux) { + setLoading(true); + return; + } + + const sfi = parseInt(spaceWeather.solarFlux) || 100; + const kIndex = parseInt(spaceWeather.kIndex) || 3; + const hour = new Date().getUTCHours(); + + // Determine if it's day or night (simplified - assumes mid-latitudes) + const isDaytime = hour >= 6 && hour <= 18; + const isGrayline = (hour >= 5 && hour <= 7) || (hour >= 17 && hour <= 19); + + // Calculate band conditions based on SFI, K-index, and time + const calculateCondition = (band) => { + let score = 50; // Base score + + // SFI impact (higher SFI = better high bands, less impact on low bands) + const sfiImpact = { + '160m': (sfi - 100) * 0.05, + '80m': (sfi - 100) * 0.1, + '60m': (sfi - 100) * 0.15, + '40m': (sfi - 100) * 0.2, + '30m': (sfi - 100) * 0.25, + '20m': (sfi - 100) * 0.35, + '17m': (sfi - 100) * 0.4, + '15m': (sfi - 100) * 0.45, + '12m': (sfi - 100) * 0.5, + '10m': (sfi - 100) * 0.55, + '6m': (sfi - 100) * 0.6, + '2m': 0, // VHF not affected by HF propagation + '70cm': 0 + }; + score += sfiImpact[band] || 0; + + // K-index impact (geomagnetic storms hurt propagation) + // K=0-1: bonus, K=2-3: neutral, K=4+: penalty + if (kIndex <= 1) score += 15; + else if (kIndex <= 2) score += 5; + else if (kIndex >= 5) score -= 40; + else if (kIndex >= 4) score -= 25; + else if (kIndex >= 3) score -= 10; + + // Time of day impact + const timeImpact = { + '160m': isDaytime ? -30 : 25, // Night band + '80m': isDaytime ? -20 : 20, // Night band + '60m': isDaytime ? -10 : 15, + '40m': isGrayline ? 20 : (isDaytime ? 5 : 15), // Good day & night + '30m': isDaytime ? 15 : 10, + '20m': isDaytime ? 25 : -15, // Day band + '17m': isDaytime ? 25 : -20, + '15m': isDaytime ? 20 : -25, // Day band + '12m': isDaytime ? 15 : -30, + '10m': isDaytime ? 15 : -35, // Day band, needs high SFI + '6m': isDaytime ? 10 : -40, // Sporadic E, mostly daytime + '2m': 10, // Local/tropo - always available + '70cm': 10 + }; + score += timeImpact[band] || 0; + + // High bands need minimum SFI to open + if (['10m', '12m', '6m'].includes(band) && sfi < 100) score -= 30; + if (['15m', '17m'].includes(band) && sfi < 80) score -= 15; + + // Convert score to condition + if (score >= 65) return 'GOOD'; + if (score >= 40) return 'FAIR'; + return 'POOR'; + }; + + const bands = ['160m', '80m', '60m', '40m', '30m', '20m', '17m', '15m', '12m', '10m', '6m', '2m']; + const conditions = bands.map(band => ({ + band, + condition: calculateCondition(band) + })); + + setData(conditions); + setLoading(false); + }, [spaceWeather?.solarFlux, spaceWeather?.kIndex]); + return { data, loading }; }; @@ -2217,7 +2293,7 @@ }; const spaceWeather = useSpaceWeather(); - const bandConditions = useBandConditions(); + const bandConditions = useBandConditions(spaceWeather.data); const potaSpots = usePOTASpots(); const dxCluster = useDXCluster(); const contests = useContests();