diff --git a/src/DockableApp.jsx b/src/DockableApp.jsx
index 411a422f..8d3e140a 100644
--- a/src/DockableApp.jsx
+++ b/src/DockableApp.jsx
@@ -24,7 +24,8 @@ import {
AnalogClockPanel,
RigControlPanel,
OnAirPanel,
- IDTimerPanel
+ IDTimerPanel,
+ HFJ350MPanel
} from './components';
import { loadLayout, saveLayout, DEFAULT_LAYOUT } from './store/layoutStore.js';
@@ -257,6 +258,7 @@ export const DockableApp = ({
'contests': { name: 'Contests', icon: '🏆' },
...(hasAmbient ? { 'ambient': { name: 'Ambient Weather', icon: '🌦️' } } : {}),
'rig-control': { name: 'Rig Control', icon: '📻' },
+ 'hfj350m-calc': { name: 'HFJ-350M Calc', icon: '📏' },
'on-air': { name: 'On Air', icon: '🔴' },
'id-timer': { name: 'ID Timer', icon: '📢' },
};
@@ -601,6 +603,10 @@ export const DockableApp = ({
content = ;
break;
+ case 'hfj350m-calc':
+ content = ;
+ break;
+
default:
content = (
diff --git a/src/components/HFJ350MPanel.jsx b/src/components/HFJ350MPanel.jsx
new file mode 100644
index 00000000..27e5e578
--- /dev/null
+++ b/src/components/HFJ350MPanel.jsx
@@ -0,0 +1,302 @@
+import React, { useState, useMemo, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+
+// Antenna data from the manual
+const ANTENNA_DATA = [
+ {
+ band: "160m",
+ freq_range: [1.8, 2.0],
+ std_freq: 1.8,
+ coil: "Basis + 3.5 Spule + 1.8 Spule",
+ jumper: "Kein Jumper",
+ length_mm: 1170,
+ radial: "> 20m (ideal 40m)",
+ change_per_cm: 7, // 7 kHz/cm
+ note: "Extrem schmalbandig! Tuner fast immer nötig."
+ },
+ {
+ band: "80m",
+ freq_range: [3.5, 3.8],
+ std_freq: 3.5,
+ coil: "Basis + 3.5 Spule",
+ jumper: "Kein Jumper",
+ length_mm: 910,
+ radial: "ca. 20m",
+ change_per_cm: 20, // 20 kHz/cm
+ note: ""
+ },
+ {
+ band: "40m",
+ freq_range: [7.0, 7.2],
+ std_freq: 7.0,
+ coil: "Basis (Keine Zusatzspule)",
+ jumper: "Kein Jumper",
+ length_mm: 960,
+ radial: "ca. 12m",
+ change_per_cm: 25, // 25 kHz/cm
+ note: "Standard-Band für Portable."
+ },
+ {
+ band: "30m",
+ freq_range: [10.1, 10.15],
+ std_freq: 10.1,
+ coil: "Basis",
+ jumper: "Terminal 1",
+ length_mm: 990,
+ radial: "ca. 7-8m",
+ change_per_cm: 40, // 40 kHz/cm
+ note: ""
+ },
+ {
+ band: "20m",
+ freq_range: [14.0, 14.35],
+ std_freq: 14.0,
+ coil: "Basis",
+ jumper: "Terminal 2",
+ length_mm: 800,
+ radial: "ca. 5m",
+ change_per_cm: 60, // 60 kHz/cm
+ note: ""
+ },
+ {
+ band: "17m",
+ freq_range: [18.068, 18.168],
+ std_freq: 18.0,
+ coil: "Basis",
+ jumper: "Terminal 3 (oder 2)",
+ length_mm: 1070,
+ radial: "ca. 4m",
+ change_per_cm: 50,
+ note: "Bei hohem SWR Terminal 2 testen."
+ },
+ {
+ band: "15m",
+ freq_range: [21.0, 21.45],
+ std_freq: 21.0,
+ coil: "Basis",
+ jumper: "Terminal 3",
+ length_mm: 750,
+ radial: "ca. 3.5m",
+ change_per_cm: 80, // 80 kHz/cm
+ note: ""
+ },
+ {
+ band: "12m",
+ freq_range: [24.89, 24.99],
+ std_freq: 24.9,
+ coil: "Basis",
+ jumper: "Terminal 3",
+ length_mm: 530,
+ radial: "ca. 3m",
+ change_per_cm: 100, // 100 kHz/cm
+ note: ""
+ },
+ {
+ band: "10m",
+ freq_range: [28.0, 29.7],
+ std_freq: 28.5,
+ coil: "Basis",
+ jumper: "Terminal 4",
+ length_mm: 1000,
+ radial: "ca. 2.5m",
+ change_per_cm: 120, // 120 kHz/cm
+ note: "Teleskop NICHT voll ausziehen! Reserve ~26cm."
+ },
+ {
+ band: "6m",
+ freq_range: [50.0, 52.0],
+ std_freq: 51.0,
+ coil: "Basis",
+ jumper: "Terminal 5",
+ length_mm: 950,
+ radial: "ca. 1.5m",
+ change_per_cm: 100, // 100 kHz/cm
+ note: "Achtung: Terminal 5 = Common + 5"
+ }
+];
+
+export const HFJ350MPanel = () => {
+ const { t } = useTranslation();
+ const [input, setInput] = useState("");
+ const [result, setResult] = useState(null);
+
+ const calculate = (query) => {
+ if (!query) {
+ setResult(null);
+ return;
+ }
+ const queryStr = String(query).toLowerCase().trim();
+ let targetFreq = null;
+ let data = null;
+
+ // Check if input is a band name
+ data = ANTENNA_DATA.find(d => {
+ const bandName = d.band.replace("m", "");
+ return queryStr === d.band.toLowerCase() || queryStr === bandName;
+ });
+
+ // Check if input is a frequency
+ if (!data) {
+ const freq = parseFloat(queryStr.replace(',', '.'));
+ if (!isNaN(freq)) {
+ targetFreq = freq;
+ data = ANTENNA_DATA.find(d => {
+ const [low, high] = d.freq_range;
+ return (low - 0.5) <= freq && freq <= (high + 1.0);
+ });
+ }
+ }
+
+ if (!data) {
+ setResult({ error: "Keine Konfiguration gefunden." });
+ return;
+ }
+
+ let calcLenMm = data.length_mm;
+ let diffMm = 0;
+ let warning = "";
+
+ if (targetFreq) {
+ const diffKhz = (targetFreq - data.std_freq) * 1000;
+ const changeCm = diffKhz / data.change_per_cm;
+ calcLenMm = Math.round(data.length_mm - (changeCm * 10));
+
+ if (calcLenMm > 1266) {
+ warning = "Max überschritten!";
+ calcLenMm = 1266;
+ } else if (calcLenMm < 100) {
+ warning = "Zu kurz!";
+ calcLenMm = 100;
+ }
+ diffMm = calcLenMm - data.length_mm;
+ }
+
+ setResult({
+ ...data,
+ targetFreq,
+ calcLenMm,
+ diffMm,
+ warning
+ });
+ };
+
+ // Load last input from localStorage on mount
+ useEffect(() => {
+ const savedInput = localStorage.getItem('hfj350m-last-input');
+ if (savedInput) {
+ setInput(savedInput);
+ calculate(savedInput);
+ }
+ }, []);
+
+ const handleInputChange = (e) => {
+ const value = e.target.value;
+ setInput(value);
+ calculate(value);
+ localStorage.setItem('hfj350m-last-input', value);
+ };
+
+ const renderBar = (len, maxLen = 1266, color = "var(--accent-blue)") => {
+ const percent = Math.min(100, Math.max(0, (len / maxLen) * 100));
+ return (
+
+ );
+ };
+
+ return (
+
+
+
+ 📡 HFJ-350M Calculator
+
+
+
+
+
+ {result && !result.error && (
+
+
+
+ Band:
+ {result.band}
+
+
+ Range:
+ {result.freq_range[0]} - {result.freq_range[1]} MHz
+
+
+
+
+
SETUP
+
+ Coil:
+ {result.coil}
+ Jumper:
+ {result.jumper}
+ Radial:
+ {result.radial}
+
+
+
+
+
TELESCOPE LENGTH
+
+
+
+ Standard ({result.std_freq} MHz):
+ {result.length_mm} mm
+
+ {renderBar(result.length_mm, 1266, 'var(--accent-amber)')}
+
+
+ {result.targetFreq && (
+
+
+ Calc ({result.targetFreq} MHz):
+
+ {result.calcLenMm} mm
+ {result.warning && ⚠ {result.warning}}
+
+
+ {renderBar(result.calcLenMm, 1266, 'var(--accent-purple)')}
+
0 ? 'var(--accent-green)' : 'var(--accent-red)' }}>
+ Diff: {result.diffMm > 0 ? '+' : ''}{result.diffMm} mm
+
+
+ )}
+
+
+
+
Sensitivity: {result.change_per_cm} kHz/cm
+ {result.note &&
⚠ {result.note}
}
+
+
+ )}
+
+ {result && result.error && (
+
+ {result.error}
+
+ )}
+
+ );
+};
+
+export default HFJ350MPanel;
diff --git a/src/components/index.js b/src/components/index.js
index 68317dbb..7bb91ea8 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -29,4 +29,5 @@ export { default as RigControlPanel } from './RigControlPanel.jsx';
export { default as OnAirPanel } from './OnAirPanel.jsx';
export { IDTimerPanel } from './IDTimerPanel.jsx';
export { default as RotatorPanel } from './RotatorPanel.jsx';
-export { default as RotatorMapOverlay } from './RotatorMapOverlay.jsx';
\ No newline at end of file
+export { default as RotatorMapOverlay } from './RotatorMapOverlay.jsx';
+export { default as HFJ350MPanel } from './HFJ350MPanel.jsx';
\ No newline at end of file
diff --git a/src/layouts/ModernLayout.jsx b/src/layouts/ModernLayout.jsx
index ea4e662f..d09296a0 100644
--- a/src/layouts/ModernLayout.jsx
+++ b/src/layouts/ModernLayout.jsx
@@ -12,7 +12,8 @@ import {
DXpeditionPanel,
PSKReporterPanel,
WeatherPanel,
- AnalogClockPanel
+ AnalogClockPanel,
+ HFJ350MPanel
} from '../components';
import { useRig } from '../contexts/RigContext.jsx';
@@ -268,6 +269,11 @@ export default function ModernLayout(props) {
propConfig={config.propagation}
/>
)}
+
+ {/* HFJ-350M Antenna Calculator */}
+
+
+
)}
diff --git a/src/store/layoutStore.js b/src/store/layoutStore.js
index 190c45b4..0b76d7ce 100644
--- a/src/store/layoutStore.js
+++ b/src/store/layoutStore.js
@@ -104,6 +104,7 @@ export const PANEL_DEFINITIONS = {
'id-timer': { name: 'ID Timer', icon: '📢', description: '10-minute station identification reminder' },
'world-map': { name: 'World Map', icon: '🗺️', description: 'Interactive world map' },
'rig-control': { name: 'Rig Control', icon: '📻', description: 'Transceiver control and feedback' },
+ 'hfj350m-calc': { name: 'HFJ-350M Calc', icon: '📏', description: 'Antenna calculator for Comet HFJ-350M' },
'on-air': { name: 'On Air', icon: '🔴', description: 'Large TX status indicator' },
};