From 22b7dfa2c2ec47bc5c5ba648585e2ae943711cab Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 14 Jan 2024 08:05:55 -0500 Subject: [PATCH] Other Smith chart "characterization" views --- .../CharacterizationView.swift | 52 ++++++++++++- .../Impedance Converter/Units.swift | 41 +++++++++- .../Impedance Converter/ViewModel.swift | 53 +++++++++++-- .../ViewModelTests.swift | 76 +++++++++++++++++-- 4 files changed, 205 insertions(+), 17 deletions(-) diff --git a/Impedance Converter/Impedance Converter/CharacterizationView.swift b/Impedance Converter/Impedance Converter/CharacterizationView.swift index f916bc3..09bcbcf 100644 --- a/Impedance Converter/Impedance Converter/CharacterizationView.swift +++ b/Impedance Converter/Impedance Converter/CharacterizationView.swift @@ -8,11 +8,27 @@ struct SWRView: View { } } -struct ReturnLossView: View { +struct SWRDBView: View { @ObservedObject var viewModel: ViewModel var body: some View { - UnitInputView(value: $viewModel.returnLoss, unit: ReturnLossUnit.dB, label: "RL", description: "return loss") + UnitInputView(value: $viewModel.swr_dB, unit: StandingWaveRatioDBUnit.dB, label: "SWR", description: "st. wave ratio") + } +} + +struct ReflectionCoefficientView: View { + @ObservedObject var viewModel: ViewModel + + var body: some View { + UnitInputView(value: $viewModel.reflectionCoefficientRho, unit: ReflectionCoefficientRhoUnit.ρ, label: "ρ", description: "refl. coeff.") + } +} + +struct ReflectionCoefficientPowerView: View { + @ObservedObject var viewModel: ViewModel + + var body: some View { + UnitInputView(value: $viewModel.reflectionCoefficientPower, unit: ReflectionCoefficientPowerUnit.ρ², label: "ρ²", description: "refl. coeff. power") } } @@ -20,7 +36,23 @@ struct TransmissionCoefficientView: View { @ObservedObject var viewModel: ViewModel var body: some View { - UnitInputView(value: $viewModel.transmissionCoefficient, unit: TransmissionCoefficientUnit.T, label: "T", description: "transmission coeff.") + UnitInputView(value: $viewModel.transmissionCoefficient, unit: TransmissionCoefficientUnit.τ, label: "τ", description: "trans. coeff.") + } +} + +struct TransmissionCoefficientPowerView: View { + @ObservedObject var viewModel: ViewModel + + var body: some View { + UnitInputView(value: $viewModel.transmissionCoefficientPower, unit: TransmissionCoefficientPowerUnit.τ², label: "τ²", description: "trans. coeff. power") + } +} + +struct ReturnLossView: View { + @ObservedObject var viewModel: ViewModel + + var body: some View { + UnitInputView(value: $viewModel.returnLoss, unit: ReturnLossUnit.dB, label: "RL", description: "return loss") } } @@ -43,12 +75,24 @@ struct CharacterizationView: View { DisplayView { HStack { SWRView(viewModel: viewModel) - ReturnLossView(viewModel: viewModel) + SWRDBView(viewModel: viewModel) } } DisplayView { HStack { + ReflectionCoefficientView(viewModel: viewModel) TransmissionCoefficientView(viewModel: viewModel) + } + } + DisplayView { + HStack { + ReflectionCoefficientPowerView(viewModel: viewModel) + TransmissionCoefficientPowerView(viewModel: viewModel) + } + } + DisplayView { + HStack { + ReturnLossView(viewModel: viewModel) TransmissionLossView(viewModel: viewModel) } } diff --git a/Impedance Converter/Impedance Converter/Units.swift b/Impedance Converter/Impedance Converter/Units.swift index bb9ad20..1c496a9 100644 --- a/Impedance Converter/Impedance Converter/Units.swift +++ b/Impedance Converter/Impedance Converter/Units.swift @@ -52,6 +52,33 @@ enum StandingWaveRatioUnit: String, UnitWithPowerOfTen { case SWR var id: Self { self } var basePower: Int { 0 } + var shouldRender: Bool { + return self != .SWR + } +} + +enum StandingWaveRatioDBUnit: String, UnitWithPowerOfTen { + case dB + var id: Self { self } + var basePower: Int { 0 } +} + +enum ReflectionCoefficientRhoUnit: String, UnitWithPowerOfTen { + case ρ + var id: Self { self } + var basePower: Int { 0 } + var shouldRender: Bool { + return self != .ρ + } +} + +enum ReflectionCoefficientPowerUnit: String, UnitWithPowerOfTen { + case ρ² + var id: Self { self } + var basePower: Int { 0 } + var shouldRender: Bool { + return self != .ρ² + } } enum ReturnLossUnit: String, UnitWithPowerOfTen { @@ -61,9 +88,21 @@ enum ReturnLossUnit: String, UnitWithPowerOfTen { } enum TransmissionCoefficientUnit: String, UnitWithPowerOfTen { - case T + case τ var id: Self { self } var basePower: Int { 0 } + var shouldRender: Bool { + return self != .τ + } +} + +enum TransmissionCoefficientPowerUnit: String, UnitWithPowerOfTen { + case τ² + var id: Self { self } + var basePower: Int { 0 } + var shouldRender: Bool { + return self != .τ² + } } enum TransmissionLossUnit: String, UnitWithPowerOfTen { diff --git a/Impedance Converter/Impedance Converter/ViewModel.swift b/Impedance Converter/Impedance Converter/ViewModel.swift index 23528c8..9042dcb 100644 --- a/Impedance Converter/Impedance Converter/ViewModel.swift +++ b/Impedance Converter/Impedance Converter/ViewModel.swift @@ -439,6 +439,35 @@ class ViewModel: ObservableObject, Codable { } } + var swr_dB: Double { + get { + return 20 * log10(swr) + } + set { + swr = pow(10, newValue / 20) + } + } + + var reflectionCoefficientRho: Double { + get { + return reflectionCoefficient.length + } + set { + guard !reflectionCoefficient.phase.isNaN else { return } + reflectionCoefficient = Complex.init(length: newValue, phase: reflectionCoefficient.phase) + } + } + + var reflectionCoefficientPower: Double { + get { + return reflectionCoefficient.lengthSquared + } + set { + guard !reflectionCoefficient.phase.isNaN else { return } + reflectionCoefficient = Complex.init(length: sqrt(newValue), phase: reflectionCoefficient.phase) + } + } + var returnLoss: Double { get { if (unityReflectionCoefficient()) { @@ -459,27 +488,39 @@ class ViewModel: ObservableObject, Codable { if (unityReflectionCoefficient()) { return 0 } else { - return 1 - pow(reflectionCoefficient.length, 2) + return 1 - reflectionCoefficient.length } } set { guard newValue >= 0 && newValue <= 1 else { return } - let reflectionCoefficientLength = sqrt(1 - newValue) - if (reflectionCoefficient.length > 0) { - reflectionCoefficient = Complex.init(length: reflectionCoefficientLength, phase: reflectionCoefficient.phase) + let reflectionCoefficientLength = 1 - newValue + reflectionCoefficient = Complex.init(length: reflectionCoefficientLength, phase: reflectionCoefficient.phase) + } + } + + var transmissionCoefficientPower: Double { + get { + if (unityReflectionCoefficient()) { + return 0 + } else { + return transmissionCoefficient * transmissionCoefficient } } + set { + guard newValue >= 0 && newValue <= 1 else { return } + transmissionCoefficient = sqrt(newValue) + } } var transmissionLoss: Double { get { - let transmissionCoefficientValue = transmissionCoefficient + let transmissionCoefficientValue = transmissionCoefficientPower return -10 * log10(transmissionCoefficientValue) } set { guard newValue >= 0 else { return } let transmissionCoefficientValue = pow(10, -newValue / 10) - transmissionCoefficient = transmissionCoefficientValue + transmissionCoefficientPower = transmissionCoefficientValue } } diff --git a/Impedance Converter/Impedance ConverterTests/ViewModelTests.swift b/Impedance Converter/Impedance ConverterTests/ViewModelTests.swift index 1a82bc4..81bea70 100644 --- a/Impedance Converter/Impedance ConverterTests/ViewModelTests.swift +++ b/Impedance Converter/Impedance ConverterTests/ViewModelTests.swift @@ -546,6 +546,70 @@ class TransmissionParametersTests: ViewModelTestBase { XCTAssertEqual(viewModel.swr, 1) } + // Testing Standing Wave Ratio (SWR) dB + func testSWR_db() { + viewModel.reactance = 40 + viewModel.swr = 10 + XCTAssertEqual(viewModel.swr_dB, 20, accuracy: 1e-6) + + viewModel.swr = 100 + XCTAssertEqual(viewModel.swr_dB, 40, accuracy: 1e-6) + + viewModel.swr_dB = 20 + XCTAssertEqual(viewModel.swr, 10, accuracy: 1e-6) + + viewModel.swr = 1 + XCTAssertEqual(viewModel.swr_dB, 0, accuracy: 1e-6) + + viewModel.reactance = 40 + viewModel.swr_dB = 0 + XCTAssertEqual(viewModel.swr, 1, accuracy: 1e-6) + } + + // Testing Reflection Coefficient Power + func testReflectionCoefficientPower() { + let reflectionCoefficient = Complex(0.5, 0) + viewModel.reflectionCoefficient = reflectionCoefficient + + let expectedReflectionCoefficientPower = reflectionCoefficient.lengthSquared + XCTAssertEqual(viewModel.reflectionCoefficientPower, expectedReflectionCoefficientPower) + + let newReflectionCoefficientPower = 0.3 + viewModel.reflectionCoefficientPower = newReflectionCoefficientPower + let expectedReflectionCoefficientMagnitude = sqrt(newReflectionCoefficientPower) + XCTAssertEqual(viewModel.reflectionCoefficient.magnitude, expectedReflectionCoefficientMagnitude, accuracy: 1e-6) + } + + // Testing Transmission Coefficient Power + func testTransmissionCoefficientPower() { + let reflectionCoefficient = Complex(0.5, 0) + viewModel.reflectionCoefficient = reflectionCoefficient + + let expectedTransmisionCoefficientPower = pow(1 - reflectionCoefficient.length, 2) + XCTAssertEqual(viewModel.transmissionCoefficientPower, expectedTransmisionCoefficientPower) + + let newTransmissionCoefficientPower = 0.3 + viewModel.transmissionCoefficientPower = newTransmissionCoefficientPower + let expectedReflectionCoefficientMagnitude = 1 - sqrt(newTransmissionCoefficientPower) + XCTAssertEqual(viewModel.reflectionCoefficient.magnitude, expectedReflectionCoefficientMagnitude, accuracy: 1e-6) + } + + func testReflectionCoefficientTransmissionCoefficient() { + viewModel.reactance = 40 + property("Reflection coefficient plus transmission coefficient must sum to 1") <- forAll( Gen.choose((0, 1)) ) { rho in + self.viewModel.reflectionCoefficientRho = rho + return abs(rho + self.viewModel.transmissionCoefficient - 1) < 1e-6 + } + } + + func testTransmissionCoefficientReflectionCoefficient() { + viewModel.reactance = 40 + property("Reflection coefficient plus transmission coefficient must sum to 1") <- forAll( Gen.choose((0, 1)) ) { tau in + self.viewModel.transmissionCoefficient = tau + return abs(self.viewModel.reflectionCoefficientRho + tau - 1) < 1e-6 + } + } + // Testing Return Loss func testReturnLoss() { let reflectionCoefficient = Complex(0.5, 0) @@ -575,13 +639,13 @@ class TransmissionParametersTests: ViewModelTestBase { viewModel.reflectionCoefficient = reflectionCoefficient // Check transmission coefficient calculation - let expectedTransmissionCoefficient = 1 - pow(reflectionCoefficient.magnitude, 2) + let expectedTransmissionCoefficient = 1 - reflectionCoefficient.length XCTAssertEqual(viewModel.transmissionCoefficient, expectedTransmissionCoefficient) // Set transmission coefficient and check reflection coefficient changes let newTransmissionCoefficient = 0.75 viewModel.transmissionCoefficient = newTransmissionCoefficient - let expectedReflectionCoefficientMagnitude = sqrt(1 - newTransmissionCoefficient) + let expectedReflectionCoefficientMagnitude = 1 - newTransmissionCoefficient XCTAssertEqual(viewModel.reflectionCoefficient.magnitude, expectedReflectionCoefficientMagnitude, accuracy: 1e-6) } @@ -598,13 +662,13 @@ class TransmissionParametersTests: ViewModelTestBase { viewModel.transmissionCoefficient = 0.75 // Check transmission loss calculation - let expectedTransmissionLoss = -10 * log10(viewModel.transmissionCoefficient) + let expectedTransmissionLoss = -20 * log10(viewModel.transmissionCoefficient) XCTAssertEqual(viewModel.transmissionLoss, expectedTransmissionLoss) // Set transmission loss and check transmission coefficient changes let newTransmissionLoss = 3.0 viewModel.transmissionLoss = newTransmissionLoss - let expectedTransmissionCoefficientValue = pow(10, -newTransmissionLoss / 10) + let expectedTransmissionCoefficientValue = pow(10, -newTransmissionLoss / 20) XCTAssertEqual(viewModel.transmissionCoefficient, expectedTransmissionCoefficientValue, accuracy: 1e-6) } } @@ -729,10 +793,10 @@ class FullTests: ViewModelTestBase { XCTAssertEqual(viewModel.reactance, -51, accuracy: 5e-1) XCTAssertEqual(viewModel.reflectionCoefficient.length, 0.54, accuracy: 5e-2) XCTAssertEqual(Angle(radians: viewModel.reflectionCoefficient.phase).degrees, -76.4, accuracy: 8e-1) - // TODO gamma power should be 0.3 + XCTAssertEqual(viewModel.reflectionCoefficientPower, 0.3, accuracy: 5e-2) XCTAssertEqual(viewModel.returnLoss, 5.25, accuracy: 1e-1) XCTAssertEqual(viewModel.swr, 3.4, accuracy: 1e-1) - // TODO SWR DB 10.6 + XCTAssertEqual(viewModel.swr_dB, 10.6, accuracy: 3e-1) XCTAssertEqual(viewModel.wavelength, 21.13, accuracy: 5e-2) viewModel.angleOrientation = .clockwise viewModel.zeroLength()