added SkyCAT support

This commit is contained in:
2026-03-25 00:18:12 +01:00
parent 313d22be2e
commit 5b31a9a9d8
8 changed files with 432 additions and 22 deletions

View File

@@ -8,7 +8,7 @@
import PolarPlot from './components/PolarPlot.svelte'
import { Go, onWailsEvent } from './lib/wails.js'
import { get } from 'svelte/store'
import { satPositions, satList, trackedSat, trackedPosition, passes, tleAge, tleLoaded, flexConnected, rotorConnected, settings, watchlist, soundEnabled, passesAllCache } from './stores/satstore.js'
import { satPositions, satList, trackedSat, trackedPosition, passes, tleAge, tleLoaded, flexConnected, rotorConnected, skycatConnected, settings, watchlist, soundEnabled, passesAllCache } from './stores/satstore.js'
let activeTab = 'map'
let cleanups = []
@@ -43,6 +43,15 @@
await Go.SetRotorAzOnly(s.rotorAzOnly ?? true)
}
}
if (s.autoConnectSkycat && s.radioType === 'skycat') {
const res = await Go.ConnectSkyCAT(s.skycatHost, s.skycatPort)
if (res === 'OK') {
skycatConnected.set(true)
await Go.SetSkyCATMode(s.skycatSatMode || 'Duplex')
}
}
// Sync radio type to backend on startup
if (s.radioType) await Go.SetRadioType(s.radioType)
const names = await Go.GetSatelliteList()
if (names) satList.set(names)

View File

@@ -1,5 +1,5 @@
<script>
import { settings, flexConnected, rotorConnected, soundEnabled } from '../stores/satstore.js'
import { settings, flexConnected, rotorConnected, soundEnabled, skycatConnected } from '../stores/satstore.js'
import { Go } from '../lib/wails.js'
import { maidenheadToLatLon, latLonToMaidenhead } from '../lib/maidenhead.js'
@@ -16,6 +16,8 @@
let availableVoices = []
let selectedVoiceName = ''
let skycatStatus = ''
// Load slice config on mount
import { onMount } from 'svelte'
onMount(async () => {
@@ -48,6 +50,28 @@
flexStatus = `Slices: RX=Slice ${'ABCDEFGH'[rxSlice]}, TX=Slice ${'ABCDEFGH'[txSlice]}`
}
async function connectSkycat() {
skycatStatus = 'Connecting…'
settings.update(s => ({ ...s, skycatHost: local.skycatHost, skycatPort: local.skycatPort, skycatSatMode: local.skycatSatMode }))
const res = await Go.ConnectSkyCAT(local.skycatHost, local.skycatPort)
if (res === 'OK') {
skycatConnected.set(true)
skycatStatus = 'Connected ✓'
await Go.SetSkyCATMode(local.skycatSatMode || 'Duplex')
} else { skycatStatus = res || 'Error' }
}
async function disconnectSkycat() {
await Go.DisconnectSkyCAT()
skycatConnected.set(false)
skycatStatus = 'Disconnected'
}
async function saveRadioType() {
settings.update(s => ({ ...s, radioType: local.radioType }))
await Go.SetRadioType(local.radioType)
}
function saveVoice() {
settings.update(s => ({ ...s, ttsVoiceName: selectedVoiceName }))
}
@@ -163,6 +187,58 @@
</button>
</section>
<!-- Radio Backend Selector -->
<section class="card">
<h3>📻 Radio Backend</h3>
<div class="fg">
<label>Active backend</label>
<select bind:value={local.radioType} on:change={saveRadioType}>
<option value="flex">FlexRadio 8600 (direct TCP)</option>
<option value="skycat">SkyCAT / rigctld (universal)</option>
</select>
</div>
<p class="hint">
{#if local.radioType === 'skycat'}
SkyCAT mode: connect skycatd.exe or rigctld.exe for your radio, then connect below.
{:else}
FlexRadio mode: direct TCP connection to SmartSDR API port 4992.
{/if}
</p>
</section>
<!-- SkyCAT -->
{#if local.radioType === 'skycat'}
<section class="card">
<div class="card-head">
<h3>🛰 SkyCAT / rigctld</h3>
<span class="badge {$skycatConnected ? 'ok' : 'off'}">{$skycatConnected ? 'CONNECTED' : 'OFFLINE'}</span>
</div>
<div class="fg">
<label>Host / IP</label>
<input type="text" bind:value={local.skycatHost} placeholder="127.0.0.1" />
<label>TCP Port</label>
<input type="number" bind:value={local.skycatPort} placeholder="4532" />
<label>Sat Mode</label>
<select bind:value={local.skycatSatMode}>
<option value="Duplex">Duplex (RX≠TX freq)</option>
<option value="Split">Split VFO</option>
<option value="Simplex">Simplex</option>
</select>
</div>
<div class="btn-row">
{#if $skycatConnected}
<button class="btn-danger" on:click={disconnectSkycat}>Disconnect</button>
{:else}
<button class="btn-primary" on:click={connectSkycat}>Connect</button>
{/if}
{#if skycatStatus}
<span class="status {skycatStatus.includes('✓') ? 'ok-txt' : 'err-txt'}">{skycatStatus}</span>
{/if}
</div>
<p class="hint">Start skycatd.exe first: <code>skycatd -m IC-9700 -r COM9 -t 4532</code></p>
</section>
{/if}
<!-- FlexRadio -->
<section class="card">
<div class="card-head">
@@ -308,7 +384,7 @@
<!-- About -->
<section class="card">
<h3> About</h3>
<p><strong>SatMaster v1.0</strong> — F4BPO</p>
<p><strong>SatMaster v0.2</strong> — F4BPO</p>
<p>Amateur satellite tracking · FlexRadio Doppler correction · PstRotator control</p>
<p class="hint">SGP4 via akhenakh/sgp4 · Go + Wails + Svelte</p>
</section>

View File

@@ -1,5 +1,5 @@
<script>
import { trackedSat, trackedPosition, flexConnected, rotorConnected, tleAge, settings, dopplerEnabled, rotorEnabled, soundEnabled } from '../stores/satstore.js'
import { trackedSat, trackedPosition, flexConnected, rotorConnected, skycatConnected, tleAge, settings, dopplerEnabled, rotorEnabled, soundEnabled } from '../stores/satstore.js'
import { formatFreq, formatShift, formatRange, computeDopplerShift } from '../lib/utils.js'
import { onMount, onDestroy } from 'svelte'
@@ -58,7 +58,7 @@
</div>
{/if}
{#if $flexConnected}
{#if ($settings.radioType === 'skycat' && $skycatConnected) || ($settings.radioType !== 'skycat' && $flexConnected)}
<button class="doppler-toggle {$dopplerEnabled ? 'don' : 'doff'}" on:click={() => dopplerEnabled.update(v => !v)} title={$dopplerEnabled ? 'Doppler ON — click to disable' : 'Doppler OFF — click to enable'}>
<span>⟳ DOPPLER {$dopplerEnabled ? 'ON' : 'OFF'}</span>
</button>
@@ -72,10 +72,17 @@
<span>🔊 SOUND {$soundEnabled ? 'ON' : 'OFF'}</span>
</button>
<div class="conn-group">
<div class="conn {$flexConnected ? 'on' : 'off'}">
<span class="conn-dot"></span>
<span>FLEX</span>
</div>
{#if $settings.radioType === 'skycat'}
<div class="conn {$skycatConnected ? 'on' : 'off'}">
<span class="conn-dot"></span>
<span>SKYCAT</span>
</div>
{:else}
<div class="conn {$flexConnected ? 'on' : 'off'}">
<span class="conn-dot"></span>
<span>FLEX</span>
</div>
{/if}
<div class="conn {$rotorConnected ? 'on' : 'off'}">
<span class="conn-dot"></span>
<span>ROTOR</span>

View File

@@ -51,6 +51,12 @@ export const Go = {
SetSatelliteMode: (mode) => wailsCall('main.App.SetSatelliteMode', mode),
SetTrackFreqMode: (v) => wailsCall('main.App.SetTrackFreqMode', v),
SetTrackAzimuth: (v) => wailsCall('main.App.SetTrackAzimuth', v),
// SkyCAT / rigctld
ConnectSkyCAT: (host, port) => wailsCall('main.App.ConnectSkyCAT', host, port),
DisconnectSkyCAT: () => wailsCall('main.App.DisconnectSkyCAT'),
GetSkyCATStatus: () => wailsCall('main.App.GetSkyCATStatus'),
SetRadioType: (type) => wailsCall('main.App.SetRadioType', type),
SetSkyCATMode: (mode) => wailsCall('main.App.SetSkyCATMode', mode),
}
// Wails event listener wrapper

View File

@@ -35,6 +35,7 @@ trackedSat.subscribe(name => {
// Connection states
export const flexConnected = writable(false)
export const skycatConnected = writable(false)
export const dopplerEnabled = writable(true)
// Passes panel state — persisted in memory so tab switches don't reset filters
@@ -55,22 +56,27 @@ const DEFAULT_SETTINGS = {
qthLat: 48.7,
qthLon: 2.55,
qthAlt: 100,
radioType: 'flex', // 'flex' | 'skycat'
flexHost: '192.168.1.100',
flexPort: 4992,
skycatHost: '127.0.0.1',
skycatPort: 4532,
skycatSatMode: 'Duplex', // 'Duplex' | 'Split' | 'Simplex'
autoConnectSkycat: false,
rotorHost: '127.0.0.1',
rotorPort: 12000,
rotorAzOnly: true, // true = azimuth only, false = az+el
rotorAzOnly: true,
downlinkHz: 145800000,
uplinkHz: 145200000,
minElFilter: 5,
autoConnectFlex: false,
autoConnectRotor: false,
rxSlice: 0, // Slice A = RX downlink
txSlice: 1, // Slice B = TX uplink
rxSlice: 0,
txSlice: 1,
}
// Settings version — bump this to force reset of specific fields
const SETTINGS_VERSION = 3
const SETTINGS_VERSION = 4
// Load persisted settings from localStorage
function loadSettings() {
@@ -85,6 +91,10 @@ function loadSettings() {
...parsed,
rxSlice: DEFAULT_SETTINGS.rxSlice,
txSlice: DEFAULT_SETTINGS.txSlice,
radioType: DEFAULT_SETTINGS.radioType,
skycatHost: DEFAULT_SETTINGS.skycatHost,
skycatPort: DEFAULT_SETTINGS.skycatPort,
skycatSatMode: DEFAULT_SETTINGS.skycatSatMode,
_version: SETTINGS_VERSION,
}
}