update km/h
This commit is contained in:
@@ -95,10 +95,10 @@
|
|||||||
|
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="weather-info">
|
<div class="weather-info">
|
||||||
<span title="Wind">🌬️ {weatherData.wind_speed.toFixed(1)}m/s</span>
|
<span title="Wind">🌬️ {weatherData.wind_speed.toFixed(1)} km/h</span>
|
||||||
<span title="Gust">💨 {weatherData.wind_gust.toFixed(1)}m/s</span>
|
<span title="Gust">💨 {weatherData.wind_gust.toFixed(1)} km/h</span>
|
||||||
<span title="Temperature">🌡️ {weatherData.temp.toFixed(1)}°C</span>
|
<span title="Temperature">🌡️ {weatherData.temp.toFixed(1)} °C</span>
|
||||||
<span title="Feels like">→ {weatherData.feels_like.toFixed(1)}°C</span>
|
<span title="Feels like">→ {weatherData.feels_like.toFixed(1)} °C</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="clock">
|
<div class="clock">
|
||||||
<span class="time">{formatTime(currentTime)}</span>
|
<span class="time">{formatTime(currentTime)}</span>
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
// Update heading with detailed logging to debug
|
// Update heading with detailed logging to debug
|
||||||
$: if (status?.heading !== undefined && status?.heading !== null) {
|
$: if (status?.heading !== undefined && status?.heading !== null) {
|
||||||
const newHeading = status.heading;
|
const newHeading = status.heading;
|
||||||
const oldHeading = heading;
|
|
||||||
|
|
||||||
if (heading === null) {
|
if (heading === null) {
|
||||||
// First time: accept any value
|
// First time: accept any value
|
||||||
@@ -25,7 +24,6 @@
|
|||||||
} else {
|
} else {
|
||||||
// Normal update
|
// Normal update
|
||||||
heading = newHeading;
|
heading = newHeading;
|
||||||
console.log(` ✓ Updated to ${heading}°`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,31 +32,38 @@
|
|||||||
|
|
||||||
$: connected = status?.connected || false;
|
$: connected = status?.connected || false;
|
||||||
|
|
||||||
let targetHeading = 0;
|
// ✅ Target heading from rotator status (when controlled by PST Rotator or other software)
|
||||||
let hasTarget = false;
|
$: statusTargetHeading = status?.target_heading ?? null;
|
||||||
|
|
||||||
// Clear target when we reach it (within 5 degrees)
|
// Local target (when clicking on map in ShackMaster)
|
||||||
$: if (hasTarget && heading !== null) {
|
let localTargetHeading = null;
|
||||||
const diff = Math.abs(heading - targetHeading);
|
|
||||||
|
// ✅ Determine if antenna is moving to a target from status
|
||||||
|
// (target differs from current heading by more than 2 degrees)
|
||||||
|
$: isMovingFromStatus = statusTargetHeading !== null &&
|
||||||
|
heading !== null &&
|
||||||
|
(() => {
|
||||||
|
const diff = Math.abs(statusTargetHeading - heading);
|
||||||
|
const wrappedDiff = Math.min(diff, 360 - diff);
|
||||||
|
return wrappedDiff > 2;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// ✅ Active target: prefer status target when moving, otherwise use local target
|
||||||
|
$: activeTargetHeading = localTargetHeading ?? (isMovingFromStatus ? statusTargetHeading : null);
|
||||||
|
|
||||||
|
// ✅ Has target if there's an active target that differs from current heading
|
||||||
|
$: hasTarget = activeTargetHeading !== null && heading !== null && (() => {
|
||||||
|
const diff = Math.abs(activeTargetHeading - heading);
|
||||||
const wrappedDiff = Math.min(diff, 360 - diff);
|
const wrappedDiff = Math.min(diff, 360 - diff);
|
||||||
if (wrappedDiff < 5) {
|
return wrappedDiff > 2;
|
||||||
hasTarget = false;
|
})();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function goToHeading() {
|
// Clear local target when we reach it (within 3 degrees)
|
||||||
if (targetHeading < 0 || targetHeading > 359) {
|
$: if (localTargetHeading !== null && heading !== null) {
|
||||||
// Removed alert popup - check console for errors
|
const diff = Math.abs(heading - localTargetHeading);
|
||||||
return;
|
const wrappedDiff = Math.min(diff, 360 - diff);
|
||||||
}
|
if (wrappedDiff < 3) {
|
||||||
try {
|
localTargetHeading = null;
|
||||||
hasTarget = true; // Mark that we have a target
|
|
||||||
const adjustedHeading = (targetHeading + 360) % 360;
|
|
||||||
await api.rotator.setHeading(adjustedHeading);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to set heading:', err);
|
|
||||||
hasTarget = false;
|
|
||||||
// Removed alert popup - check console for errors
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +85,7 @@
|
|||||||
|
|
||||||
async function stop() {
|
async function stop() {
|
||||||
try {
|
try {
|
||||||
|
localTargetHeading = null; // Clear local target on stop
|
||||||
await api.rotator.stop();
|
await api.rotator.stop();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to stop:', err);
|
console.error('Failed to stop:', err);
|
||||||
@@ -87,7 +93,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle click on compass to set heading
|
// Handle click on compass to set heading
|
||||||
function handleCompassClick(event) {
|
async function handleCompassClick(event) {
|
||||||
const svg = event.currentTarget;
|
const svg = event.currentTarget;
|
||||||
const rect = svg.getBoundingClientRect();
|
const rect = svg.getBoundingClientRect();
|
||||||
const centerX = rect.width / 2;
|
const centerX = rect.width / 2;
|
||||||
@@ -103,10 +109,16 @@
|
|||||||
|
|
||||||
// Round to nearest 5 degrees
|
// Round to nearest 5 degrees
|
||||||
const roundedHeading = Math.round(angle / 5) * 5;
|
const roundedHeading = Math.round(angle / 5) * 5;
|
||||||
|
const adjustedHeading = (roundedHeading + 360) % 360;
|
||||||
|
|
||||||
// Set target and go
|
// ✅ CORRIGÉ : Send command first, then set localTargetHeading only on success
|
||||||
targetHeading = roundedHeading;
|
try {
|
||||||
goToHeading();
|
await api.rotator.setHeading(adjustedHeading);
|
||||||
|
// Only set local target AFTER successful API call
|
||||||
|
localTargetHeading = adjustedHeading;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to set heading:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -123,8 +135,8 @@
|
|||||||
<div class="heading-label">CURRENT HEADING</div>
|
<div class="heading-label">CURRENT HEADING</div>
|
||||||
<div class="heading-value">
|
<div class="heading-value">
|
||||||
{displayHeading}°
|
{displayHeading}°
|
||||||
{#if hasTarget}
|
{#if hasTarget && activeTargetHeading !== null}
|
||||||
<span class="target-indicator">→ {targetHeading}°</span>
|
<span class="target-indicator">→ {activeTargetHeading}°</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -281,19 +293,21 @@
|
|||||||
|
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<!-- Target arrow (if we have a target) -->
|
<!-- ✅ Target arrow (yellow) - shown when antenna is moving to target -->
|
||||||
{#if hasTarget}
|
{#if hasTarget && activeTargetHeading !== null}
|
||||||
<g transform="rotate({targetHeading})">
|
<g transform="rotate({activeTargetHeading})">
|
||||||
<line x1="0" y1="0" x2="0" y2="-120"
|
<!-- Target direction line (dashed yellow) -->
|
||||||
|
<line x1="0" y1="0" x2="0" y2="-135"
|
||||||
stroke="#ffc107"
|
stroke="#ffc107"
|
||||||
stroke-width="3"
|
stroke-width="3"
|
||||||
stroke-dasharray="8,4"
|
stroke-dasharray="8,4"
|
||||||
opacity="0.9"/>
|
opacity="0.9"/>
|
||||||
<g transform="translate(0, -120)">
|
<!-- Target arrow head with pulse animation -->
|
||||||
<polygon points="0,-15 -10,10 0,5 10,10"
|
<g transform="translate(0, -135)">
|
||||||
|
<polygon points="0,-12 -8,6 0,2 8,6"
|
||||||
fill="#ffc107"
|
fill="#ffc107"
|
||||||
stroke="#ff9800"
|
stroke="#ff9800"
|
||||||
stroke-width="2"
|
stroke-width="1.5"
|
||||||
style="filter: drop-shadow(0 0 10px rgba(255, 193, 7, 0.8))">
|
style="filter: drop-shadow(0 0 10px rgba(255, 193, 7, 0.8))">
|
||||||
<animate attributeName="opacity" values="0.8;1;0.8" dur="1s" repeatCount="indefinite"/>
|
<animate attributeName="opacity" values="0.8;1;0.8" dur="1s" repeatCount="indefinite"/>
|
||||||
</polygon>
|
</polygon>
|
||||||
@@ -301,7 +315,7 @@
|
|||||||
</g>
|
</g>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Center dot (your QTH - JN36dg) -->
|
<!-- Center dot (your QTH) -->
|
||||||
<circle cx="0" cy="0" r="5" fill="#f44336" stroke="#fff" stroke-width="2">
|
<circle cx="0" cy="0" r="5" fill="#f44336" stroke="#fff" stroke-width="2">
|
||||||
<animate attributeName="r" values="5;7;5" dur="2s" repeatCount="indefinite"/>
|
<animate attributeName="r" values="5;7;5" dur="2s" repeatCount="indefinite"/>
|
||||||
</circle>
|
</circle>
|
||||||
@@ -343,8 +357,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Go To Heading -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -395,8 +407,6 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Heading Display */
|
|
||||||
|
|
||||||
.heading-controls-row {
|
.heading-controls-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -485,7 +495,6 @@
|
|||||||
50% { opacity: 1; }
|
50% { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Map */
|
|
||||||
.map-container {
|
.map-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -521,7 +530,7 @@
|
|||||||
.clickable-compass {
|
.clickable-compass {
|
||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
outline: none; /* Remove focus outline */
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clickable-compass:hover {
|
.clickable-compass:hover {
|
||||||
@@ -540,5 +549,4 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
</style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user