161 lines
6.2 KiB
Swift
161 lines
6.2 KiB
Swift
//
|
|
// CableCalculator.swift
|
|
// Cable
|
|
//
|
|
// Created by Stefan Lange-Hegermann on 11.09.25.
|
|
//
|
|
|
|
import Foundation
|
|
import SwiftData
|
|
|
|
class CableCalculator: ObservableObject {
|
|
@Published var voltage: Double = 12.0
|
|
@Published var current: Double = 5.0
|
|
@Published var power: Double = 60.0
|
|
@Published var length: Double = 10.0
|
|
@Published var loadName: String = "My Load"
|
|
|
|
var calculatedPower: Double {
|
|
voltage * current
|
|
}
|
|
|
|
var calculatedCurrent: Double {
|
|
voltage > 0 ? power / voltage : 0
|
|
}
|
|
|
|
func updateFromCurrent() {
|
|
power = voltage * current
|
|
}
|
|
|
|
func updateFromPower() {
|
|
current = voltage > 0 ? power / voltage : 0
|
|
}
|
|
|
|
func recommendedCrossSection(for unitSystem: UnitSystem) -> Double {
|
|
let lengthInMeters = unitSystem == .metric ? length : length * 0.3048 // ft to m
|
|
// Simplified calculation: minimum cross-section based on current and voltage drop
|
|
let maxVoltageDrop = voltage * 0.05 // 5% voltage drop limit
|
|
let resistivity = 0.017 // Copper resistivity at 20°C (Ω⋅mm²/m)
|
|
let calculatedMinCrossSection = (2 * current * lengthInMeters * resistivity) / maxVoltageDrop
|
|
|
|
if unitSystem == .imperial {
|
|
// Standard AWG wire sizes
|
|
let standardAWG = [20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, 0, 00, 000, 0000]
|
|
let awgCrossSections = [0.519, 0.823, 1.31, 2.08, 3.31, 5.26, 8.37, 13.3, 21.2, 33.6, 42.4, 53.5, 67.4, 85.0, 107.0]
|
|
|
|
// Find the smallest AWG that meets the requirement
|
|
for (index, crossSection) in awgCrossSections.enumerated() {
|
|
if crossSection >= calculatedMinCrossSection {
|
|
return Double(standardAWG[index])
|
|
}
|
|
}
|
|
return Double(standardAWG.last!) // Largest available
|
|
} else {
|
|
// Standard metric cable cross-sections in mm²
|
|
let standardSizes = [0.75, 1.0, 1.5, 2.5, 4.0, 6.0, 10.0, 16.0, 25.0, 35.0, 50.0, 70.0, 95.0, 120.0, 150.0, 185.0, 240.0, 300.0, 400.0, 500.0, 630.0]
|
|
|
|
// Find the smallest standard size that meets the requirement
|
|
return standardSizes.first { $0 >= max(0.75, calculatedMinCrossSection) } ?? standardSizes.last!
|
|
}
|
|
}
|
|
|
|
func crossSection(for unitSystem: UnitSystem) -> Double {
|
|
recommendedCrossSection(for: unitSystem)
|
|
}
|
|
|
|
func voltageDrop(for unitSystem: UnitSystem) -> Double {
|
|
let lengthInMeters = unitSystem == .metric ? length : length * 0.3048
|
|
let crossSectionMM2 = unitSystem == .metric ? crossSection(for: unitSystem) : crossSectionFromAWG(crossSection(for: unitSystem))
|
|
let resistivity = 0.017
|
|
let effectiveCurrent = current // Always use the current property which gets updated
|
|
return (2 * effectiveCurrent * lengthInMeters * resistivity) / crossSectionMM2
|
|
}
|
|
|
|
func voltageDropPercentage(for unitSystem: UnitSystem) -> Double {
|
|
(voltageDrop(for: unitSystem) / voltage) * 100
|
|
}
|
|
|
|
func powerLoss(for unitSystem: UnitSystem) -> Double {
|
|
let effectiveCurrent = current
|
|
return effectiveCurrent * voltageDrop(for: unitSystem)
|
|
}
|
|
|
|
var recommendedFuse: Int {
|
|
let targetFuse = current * 1.25 // 125% of load current for safety
|
|
|
|
// Common fuse values in amperes
|
|
let standardFuses = [1, 2, 3, 5, 7, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500, 600, 700, 800]
|
|
|
|
// Find the smallest standard fuse that's >= target
|
|
return standardFuses.first { $0 >= Int(targetFuse.rounded(.up)) } ?? standardFuses.last!
|
|
}
|
|
|
|
// AWG conversion helper for voltage drop calculations
|
|
private func crossSectionFromAWG(_ awg: Double) -> Double {
|
|
let awgSizes = [20: 0.519, 18: 0.823, 16: 1.31, 14: 2.08, 12: 3.31, 10: 5.26, 8: 8.37, 6: 13.3, 4: 21.2, 2: 33.6, 1: 42.4, 0: 53.5]
|
|
|
|
// Handle 00, 000, 0000 AWG (represented as negative values)
|
|
if awg == 00 { return 67.4 }
|
|
if awg == 000 { return 85.0 }
|
|
if awg == 0000 { return 107.0 }
|
|
|
|
return awgSizes[Int(awg)] ?? 0.75
|
|
}
|
|
}
|
|
|
|
@Model
|
|
class ElectricalSystem {
|
|
var name: String = ""
|
|
var location: String = ""
|
|
var timestamp: Date = Date()
|
|
var iconName: String = "building.2"
|
|
var colorName: String = "blue"
|
|
|
|
init(name: String, location: String = "", iconName: String = "building.2", colorName: String = "blue") {
|
|
self.name = name
|
|
self.location = location
|
|
self.timestamp = Date()
|
|
self.iconName = iconName
|
|
self.colorName = colorName
|
|
}
|
|
}
|
|
|
|
@Model
|
|
class SavedLoad {
|
|
var name: String = ""
|
|
var voltage: Double = 0.0
|
|
var current: Double = 0.0
|
|
var power: Double = 0.0
|
|
var length: Double = 0.0
|
|
var crossSection: Double = 0.0
|
|
var timestamp: Date = Date()
|
|
var iconName: String = "lightbulb"
|
|
var colorName: String = "blue"
|
|
var isWattMode: Bool = false
|
|
var system: ElectricalSystem?
|
|
var remoteIconURLString: String? = nil
|
|
var affiliateURLString: String? = nil
|
|
var affiliateCountryCode: String? = nil
|
|
var bomCompletedItemIDs: [String] = []
|
|
var identifier: String = UUID().uuidString
|
|
|
|
init(name: String, voltage: Double, current: Double, power: Double, length: Double, crossSection: Double, iconName: String = "lightbulb", colorName: String = "blue", isWattMode: Bool = false, system: ElectricalSystem? = nil, remoteIconURLString: String? = nil, affiliateURLString: String? = nil, affiliateCountryCode: String? = nil, bomCompletedItemIDs: [String] = [], identifier: String = UUID().uuidString) {
|
|
self.name = name
|
|
self.voltage = voltage
|
|
self.current = current
|
|
self.power = power
|
|
self.length = length
|
|
self.crossSection = crossSection
|
|
self.timestamp = Date()
|
|
self.iconName = iconName
|
|
self.colorName = colorName
|
|
self.isWattMode = isWattMode
|
|
self.system = system
|
|
self.remoteIconURLString = remoteIconURLString
|
|
self.affiliateURLString = affiliateURLString
|
|
self.affiliateCountryCode = affiliateCountryCode
|
|
self.bomCompletedItemIDs = bomCompletedItemIDs
|
|
self.identifier = identifier
|
|
}
|
|
}
|