Add FT-991A Remote Control App for macOS
Complete Phase 1 implementation of the Yaesu FT-991A remote control application with CAT protocol support over USB serial (CP210x). Features implemented: - SerialPortManager with auto-detection of CP210x ports - Full CAT protocol parser and command builder - RadioState model with all transceiver parameters - Modern SwiftUI interface with frequency/mode/level controls - Skeuomorphic front panel view (switchable) - Debug panel with CAT command console - QSO log panel with CSV export/import - Audio routing panel with BlackHole integration - Settings with connection, UI, keyboard configuration - Menu bar extra for background operation - German/English localization - Logging system for debugging Supports: Frequency control, VFO A/B, all modes (LSB/USB/CW/FM/AM/ DATA/RTTY/C4FM), level controls, NB/NR/DNF/ATU/Split functions, S-meter/Power/SWR metering, PTT control via Shift key. Target: macOS 15.0+ (Sequoia/Tahoe)
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// Settings.swift
|
||||
// FT991A-Remote
|
||||
//
|
||||
// Application settings model
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - App Settings
|
||||
|
||||
struct AppSettings: Codable {
|
||||
// Connection
|
||||
var serialPort: String = ""
|
||||
var baudRate: Int = 38400
|
||||
var autoReconnect: Bool = true
|
||||
var reconnectInterval: TimeInterval = 5.0
|
||||
|
||||
// UI
|
||||
var uiStyle: UIStyle = .modern
|
||||
var language: AppLanguage = .german
|
||||
var showDebugPanel: Bool = false
|
||||
var showLogPanel: Bool = false
|
||||
var compactMode: Bool = true
|
||||
|
||||
// Frequency
|
||||
var frequencyStep: FrequencyStep = .khz1
|
||||
|
||||
// Logging
|
||||
var logDirectory: String = "~/Documents/FT991A-Logs/"
|
||||
var autoSaveLog: Bool = true
|
||||
|
||||
// Audio
|
||||
var audioInputDevice: String = ""
|
||||
var audioOutputDevice: String = ""
|
||||
var useBlackHole: Bool = false
|
||||
|
||||
// Keyboard
|
||||
var pttShortcutEnabled: Bool = true
|
||||
var arrowFrequencyEnabled: Bool = true
|
||||
var tunerShortcutEnabled: Bool = true
|
||||
|
||||
// MARK: - Persistence
|
||||
|
||||
static let defaults = AppSettings()
|
||||
|
||||
static var settingsURL: URL {
|
||||
let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
||||
let appFolder = appSupport.appendingPathComponent("FT991A-Remote", isDirectory: true)
|
||||
|
||||
try? FileManager.default.createDirectory(at: appFolder, withIntermediateDirectories: true)
|
||||
|
||||
return appFolder.appendingPathComponent("settings.json")
|
||||
}
|
||||
|
||||
static func load() -> AppSettings {
|
||||
guard FileManager.default.fileExists(atPath: settingsURL.path) else {
|
||||
return defaults
|
||||
}
|
||||
|
||||
do {
|
||||
let data = try Data(contentsOf: settingsURL)
|
||||
return try JSONDecoder().decode(AppSettings.self, from: data)
|
||||
} catch {
|
||||
print("Failed to load settings: \(error)")
|
||||
return defaults
|
||||
}
|
||||
}
|
||||
|
||||
func save() {
|
||||
do {
|
||||
let data = try JSONEncoder().encode(self)
|
||||
try data.write(to: AppSettings.settingsURL)
|
||||
} catch {
|
||||
print("Failed to save settings: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Log Directory
|
||||
|
||||
var expandedLogDirectory: String {
|
||||
(logDirectory as NSString).expandingTildeInPath
|
||||
}
|
||||
|
||||
mutating func ensureLogDirectoryExists() {
|
||||
let path = expandedLogDirectory
|
||||
if !FileManager.default.fileExists(atPath: path) {
|
||||
try? FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Serial Port Configuration
|
||||
|
||||
struct SerialConfig: Codable {
|
||||
var baudRate: Int = 38400
|
||||
var dataBits: Int = 8
|
||||
var stopBits: Int = 1
|
||||
var parity: Parity = .none
|
||||
var flowControl: FlowControl = .none
|
||||
|
||||
enum Parity: String, Codable, CaseIterable {
|
||||
case none = "None"
|
||||
case odd = "Odd"
|
||||
case even = "Even"
|
||||
}
|
||||
|
||||
enum FlowControl: String, Codable, CaseIterable {
|
||||
case none = "None"
|
||||
case hardware = "RTS/CTS"
|
||||
case software = "XON/XOFF"
|
||||
}
|
||||
|
||||
static let ft991aDefault = SerialConfig(
|
||||
baudRate: 38400,
|
||||
dataBits: 8,
|
||||
stopBits: 1,
|
||||
parity: .none,
|
||||
flowControl: .none
|
||||
)
|
||||
|
||||
static let availableBaudRates = [4800, 9600, 19200, 38400, 57600, 115200]
|
||||
}
|
||||
Reference in New Issue
Block a user