diff --git a/src/Circuit.jsx b/src/Circuit.jsx
index 34b0592..816780a 100644
--- a/src/Circuit.jsx
+++ b/src/Circuit.jsx
@@ -940,8 +940,6 @@ function Circuit({ userCircuit, setUserCircuit, frequency, setSettings, showIdea
const w = 2 * Math.PI * frequency;
const [modalOpen, setModalOpen] = useState(false);
const [modalSparam, setModalSparam] = useState(false);
- const [shortedStubDialogOpen, setShortedStubDialogOpen] = useState(false);
- const [hasSeenShortedStubPopup, setHasSeenShortedStubPopup] = useState(false);
const sParamIndex = userCircuit.findIndex((c) => c.name === "sparam");
const s1pIndex = userCircuit.findIndex((c) => c.type === "s1p");
@@ -1128,9 +1126,6 @@ function Circuit({ userCircuit, setUserCircuit, frequency, setSettings, showIdea
setModalOpen(true);
} else if (k == "sparam") {
setModalSparam(true);
- } else if (k === "shortedStub" && !hasSeenShortedStubPopup) {
- setHasSeenShortedStubPopup(true);
- setShortedStubDialogOpen(true);
}
}}
color="bland"
@@ -1155,14 +1150,6 @@ function Circuit({ userCircuit, setUserCircuit, frequency, setSettings, showIdea
);
})}
-
{t("circuit.hint", {
diff --git a/src/ReleaseNotes.jsx b/src/ReleaseNotes.jsx
index 334cc35..dd93ec9 100644
--- a/src/ReleaseNotes.jsx
+++ b/src/ReleaseNotes.jsx
@@ -14,6 +14,7 @@ import TableRow from "@mui/material/TableRow";
function ReleaseNotes() {
const { t } = useTranslation();
+ const v23 = t("release.v23", { returnObjects: true });
const v22 = t("release.v22", { returnObjects: true });
const v21 = t("release.v21", { returnObjects: true });
const v20features = t("release.v20features", { returnObjects: true });
@@ -41,6 +42,17 @@ function ReleaseNotes() {
+
+
+ v2.3
+
+
+ September 2026
+
+
+ {Array.isArray(v23) && v23.map((item, i) => - {item}
)}
+
+
v2.2
diff --git a/src/impedanceFunctions.js b/src/impedanceFunctions.js
index 5effb78..18edaa7 100644
--- a/src/impedanceFunctions.js
+++ b/src/impedanceFunctions.js
@@ -14,10 +14,10 @@ import { sParamFrequencyRange } from "./sparam.js"; // Import the sParamFrequenc
const detailedResolution = 50;
-export function calculateTlineZ(resolution, component, line_length, beta, startImaginary, startReal, impedanceResolution, startAdmittance) {
+export function calculateTlineZ(resolution, component, line_length, beta, startImaginary, startReal, impedanceResolution, startAdmittance, flipArc) {
var tan_beta, zBottom_inv, zTop;
for (var j = 0; j <= resolution; j++) {
- if (component.name == "shortedStub") tan_beta = Math.tan((beta * j * line_length) / resolution + Math.PI / 2);
+ if (flipArc) tan_beta = Math.tan((beta * j * -line_length) / resolution);
else tan_beta = Math.tan((beta * j * line_length) / resolution);
if (component.name == "transmissionLine") {
@@ -147,8 +147,18 @@ export function calculateImpedance(userCircuit, frequency, resolution, showIdeal
startAdmittance,
);
- line_length = ((lengthLambda % 0.5) * speedOfLight) / frequency;
- calculateTlineZ(resolution, component, line_length, beta, startImaginary, startReal, impedanceResolution, startAdmittance);
+ var segmentLambda = lengthLambda % 0.5;
+ var flipArc = false;
+ if (component.name === "shortedStub") {
+ if (segmentLambda > 0 && segmentLambda < 0.25) {
+ flipArc = true;
+ segmentLambda = 0.25 - segmentLambda;
+ } else if (segmentLambda >= 0.25) {
+ segmentLambda = segmentLambda - 0.25;
+ }
+ }
+ line_length = (segmentLambda * speedOfLight) / frequency;
+ calculateTlineZ(resolution, component, line_length, beta, startImaginary, startReal, impedanceResolution, startAdmittance, flipArc);
} else if (component.name == "transformer") {
if (component.model === "ideal") {
// Ideal transformer: Z_out = n² * Z_in (turns ratio n from primary to secondary)
diff --git a/src/locales/en.json b/src/locales/en.json
index f8d66d0..2b09f08 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -45,7 +45,7 @@
},
"footer": {
"license": "© {{year}} Will Kelsey. This work is licensed under a Creative Commons Attribution 4.0 International License. You may not resell this tool",
- "version": "v2.0"
+ "version": "v2.3"
},
"language": {
"label": "Language"
@@ -212,7 +212,6 @@
"tolerance": "Tolerance",
"qFactorEquals": "Q Factor = {{v}}"
},
- "shortedStubDialog": "Shorted-stub arc starts at 0-ohms (length = 0), therefore will not be connected to the previous point. This may seem unintuitive but makes more sense once considering frequency-span and tolerance",
"hint": "Click components above to add them to the circuit below. Impedance is looking {{direction}}",
"towardsBlackBox": "towards the BLACK BOX",
"towardsTermination": "from the termination",
@@ -237,6 +236,7 @@
"version": "Version",
"date": "Date",
"notes": "Notes",
+ "v23": ["Shorted stub arcs now connect from the current impedance point to the final impedance (opposed to from 0ohms)"],
"v22": [
"Reworked .s1p matching: S-parameter block on the left, matching components to the right, load termination at the end",
"Legacy .s1p share links migrate automatically",
diff --git a/src/locales/es.json b/src/locales/es.json
index 0e35bd9..c513ec9 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -45,7 +45,7 @@
},
"footer": {
"license": "© {{year}} Will Kelsey. Esta obra tiene licencia Creative Commons Atribución 4.0 Internacional. No puede revender esta herramienta",
- "version": "v2.0"
+ "version": "v2.3"
},
"language": {
"label": "Idioma"
@@ -212,7 +212,6 @@
"tolerance": "Tolerancia",
"qFactorEquals": "Factor Q = {{v}}"
},
- "shortedStubDialog": "El arco del stub en corto comienza en 0 Ω (longitud = 0), por lo que no se conectará al punto anterior. Puede parecer poco intuitivo pero encaja con el rango de frecuencia y la tolerancia",
"hint": "Pulse los componentes de arriba para añadirlos al circuito. La impedancia se ve {{direction}}",
"towardsBlackBox": "hacia la CAJA NEGRA",
"towardsTermination": "hacia la TERMINACIÓN",
@@ -237,6 +236,7 @@
"version": "Versión",
"date": "Fecha",
"notes": "Notas",
+ "v23": ["Los arcos del stub en corto conectan desde el punto de impedancia actual hasta la impedancia final (en lugar de desde 0 Ω)"],
"v22": [
"Flujo .s1p mejorado: bloque S a la izquierda, red de adaptación a la derecha, terminación al final",
"Los enlaces .s1p antiguos se migran automáticamente",
diff --git a/src/locales/fr.json b/src/locales/fr.json
index f4a3753..4545c39 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -45,7 +45,7 @@
},
"footer": {
"license": "© {{year}} Will Kelsey. Cette œuvre est sous licence Creative Commons Attribution 4.0 International. Vous ne pouvez pas revendre cet outil",
- "version": "v2.0"
+ "version": "v2.3"
},
"language": {
"label": "Langue"
@@ -212,7 +212,6 @@
"tolerance": "Tolérance",
"qFactorEquals": "Facteur Q = {{v}}"
},
- "shortedStubDialog": "L’arc du stub court-circuité part de 0 Ω (longueur = 0) et ne se raccorde donc pas au point précédent. Cela peut surprendre mais est cohérent avec la plage de fréquence et la tolérance",
"hint": "Cliquez sur les composants ci-dessus pour les ajouter au circuit. L’impédance est vue {{direction}}",
"towardsBlackBox": "vers la BOÎTE NOIRE",
"towardsTermination": "vers la TERMINAISON",
@@ -237,6 +236,7 @@
"version": "Version",
"date": "Date",
"notes": "Notes",
+ "v23": ["Les arcs du stub court-circuité partent du point d’impédance actuel jusqu’à l’impédance finale (au lieu de partir de 0 Ω)"],
"v22": [
"Adaptation .s1p refaite : bloc S à gauche, réseau d’adaptation à droite, terminaison en fin de chaîne",
"Les liens .s1p existants sont migrés automatiquement",
diff --git a/src/locales/hi.json b/src/locales/hi.json
index c183777..e07eee8 100644
--- a/src/locales/hi.json
+++ b/src/locales/hi.json
@@ -45,7 +45,7 @@
},
"footer": {
"license": "© {{year}} Will Kelsey। यह कार्य Creative Commons Attribution 4.0 International लाइसेंस के अंतर्गत है। आप इस टूल को पुनः बेच नहीं सकते",
- "version": "v2.0"
+ "version": "v2.3"
},
"language": {
"label": "भाषा"
@@ -212,7 +212,6 @@
"tolerance": "टॉलरेंस",
"qFactorEquals": "Q फैक्टर = {{v}}"
},
- "shortedStubDialog": "शॉर्टेड-स्टब चाप 0 ओम (लंबाई = 0) से शुरू होता है, इसलिए पिछले बिंदु से जुड़ा नहीं होगा। यह अजीब लग सकता है लेकिन आवृत्ति-स्पैन और टॉलरेंस के साथ अधिक समझ में आता है",
"hint": "ऊपर के घटकों पर क्लिक करके उन्हें नीचे सर्किट में जोड़ें। इम्पीडेंस {{direction}} देख रहा है",
"towardsBlackBox": "ब्लैक बॉक्स की ओर",
"towardsTermination": "टर्मिनेशन की ओर",
@@ -237,6 +236,7 @@
"version": "संस्करण",
"date": "तारीख",
"notes": "नोट्स",
+ "v23": ["शॉर्टेड स्टब चाप अब वर्तमान इम्पीडेंस बिंदु से अंतिम इम्पीडेंस तक जुड़ते हैं (0 Ω से नहीं)"],
"v22": [
".s1p मिलान अपडेट: बाएँ S-पैरामीटर, दाएँ मिलान नेटवर्क, अंत में टर्मिनेशन",
"पुराने .s1p लिंक स्वचालित रूप से माइग्रेट होते हैं",
diff --git a/src/locales/ja.json b/src/locales/ja.json
index a9b448c..dde2b39 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -45,7 +45,7 @@
},
"footer": {
"license": "© {{year}} Will Kelsey. 本作品はクリエイティブ・コモンズ表示4.0国際ライセンスの下で提供されています。本ツールの再販は禁止です",
- "version": "v2.0"
+ "version": "v2.3"
},
"language": {
"label": "言語"
@@ -212,7 +212,6 @@
"tolerance": "許容差",
"qFactorEquals": "Q = {{v}}"
},
- "shortedStubDialog": "短絡スタブの円弧は0Ω(長さ=0)から始まるため、前の点には接続されません。周波数スパンと許容差を考えると納得できます",
"hint": "上の部品をクリックして下の回路に追加します。インピーダンスは{{direction}}を見ています",
"towardsBlackBox": "ブラックボックス方向",
"towardsTermination": "終端方向",
@@ -237,6 +236,7 @@
"version": "バージョン",
"date": "日付",
"notes": "内容",
+ "v23": ["短絡スタブの円弧が現在のインピーダンス点から最終インピーダンスまでつながるように改善(0Ωからではなく)"],
"v22": [
".s1pマッチングを改善:左にSパラメータ、右にマッチングネットワーク、末尾に終端",
"旧.s1p共有URLは自動的に移行",
diff --git a/src/locales/tr.json b/src/locales/tr.json
index fb37d21..5ca5e46 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -45,7 +45,7 @@
},
"footer": {
"license": "© {{year}} Will Kelsey. Bu çalışma Creative Commons Atıf 4.0 Uluslararası Lisansı altındadır. Bu aracı yeniden satamazsınız",
- "version": "sürüm 2.0"
+ "version": "sürüm 2.3"
},
"language": {
"label": "Dil"
@@ -212,7 +212,6 @@
"tolerance": "Tolerans",
"qFactorEquals": "Q faktörü = {{v}}"
},
- "shortedStubDialog": "Kısa devre stub yayı 0 Ω’da (uzunluk = 0) başlar, bu yüzden önceki noktaya bağlı görünmez. İlk bakışta karşı sezgine gelebilir; frekans aralığı ve tolerans düşünüldüğünde daha anlaşılır olur",
"hint": "Aşağıdaki devreye eklemek için yukarıdaki bileşenlere tıklayın. Empedans yönü: {{direction}}",
"towardsBlackBox": "KARA KUTU'ya doğru",
"towardsTermination": "SONLANDIRMA'ya doğru",
@@ -237,6 +236,7 @@
"version": "Sürüm",
"date": "Tarih",
"notes": "Notlar",
+ "v23": ["Kısa devre stub yayları artık geçerli empedans noktasından son empedansa bağlanır (0 Ω yerine)"],
"v22": [
".s1p eşleme yenilendi: solda S-parametre, sağda eşleme ağı, sonda sonlandırma",
"Eski .s1p paylaşım bağlantıları otomatik taşınır",
diff --git a/src/locales/zh.json b/src/locales/zh.json
index cd18de6..3ef82fa 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -45,7 +45,7 @@
},
"footer": {
"license": "© {{year}} Will Kelsey。本作品采用知识共享署名 4.0 国际许可协议。不得转售本工具",
- "version": "v2.0"
+ "version": "v2.3"
},
"language": {
"label": "语言"
@@ -212,7 +212,6 @@
"tolerance": "容差",
"qFactorEquals": "Q 值 = {{v}}"
},
- "shortedStubDialog": "短路短截线弧从 0 欧(长度 = 0)开始,因此不会连接到前一点。看似不直观,但结合频率范围与容差后更易理解",
"hint": "点击上方元件将其加入下方电路。阻抗为看向 {{direction}}",
"towardsBlackBox": "黑盒方向",
"towardsTermination": "终端方向",
@@ -237,6 +236,7 @@
"version": "版本",
"date": "日期",
"notes": "说明",
+ "v23": ["短路短截线弧现从当前阻抗点连接到最终阻抗(而非从 0 Ω 起)"],
"v22": ["改进 .s1p 匹配流程:左侧 S 参数、右侧匹配网络、末端负载", "旧 .s1p 分享链接自动迁移", "反射系数与原始 S 参数数据同时绘制"],
"v21": ["新增 S 参数元件", "新增用于 s2p 增益的增益圆", "新增若干 S 参数教程"],
"v20a": "网站从 will-kelsey.com/smith_chart/ 迁移至 onlinesmithchart.com",
diff --git a/tests/impedance.test.js b/tests/impedance.test.js
index babf9e4..09f1e11 100644
--- a/tests/impedance.test.js
+++ b/tests/impedance.test.js
@@ -4,6 +4,26 @@ import { readFileSync /*, writeFileSync*/ } from "fs";
import { join } from "path";
import { allImpedanceCalculations, calculateImpedance } from "../src/impedanceFunctions.js";
+const FLOAT_PRECISION = 10;
+
+function expectProcessedImpedanceResults(actual, expected) {
+ const { refReal, refImag, ...expectedRest } = expected;
+ expect(actual).toMatchObject(expectedRest);
+ expect(actual.refReal).toBeCloseTo(refReal, FLOAT_PRECISION);
+ expect(actual.refImag).toBeCloseTo(refImag, FLOAT_PRECISION);
+}
+
+function expectImpedanceArcs(actual, expected, precision = FLOAT_PRECISION) {
+ expect(actual.length).toBe(expected.length);
+ for (let i = 0; i < actual.length; i++) {
+ expect(actual[i].length).toBe(expected[i].length);
+ for (let j = 0; j < actual[i].length; j++) {
+ expect(actual[i][j].real).toBeCloseTo(expected[i][j].real, precision);
+ expect(actual[i][j].imaginary).toBeCloseTo(expected[i][j].imaginary, precision);
+ }
+ }
+}
+
test("Transmission line impedance test", () => {
const circuit = [
{ name: "blackBox", real: 25, imaginary: -25 },
@@ -20,7 +40,7 @@ test("Transmission line impedance test", () => {
{ real: 46.53103192423643, imaginary: 48.109436254243874 },
],
];
- expect(impedance).toEqual(expected);
+ expectImpedanceArcs(impedance, expected);
});
test("Impedance L-R-Shorted-Stub", () => {
@@ -67,7 +87,7 @@ test("Impedance L-R-Shorted-Stub", () => {
settings,
);
- expect(processedImpedanceResults).toEqual({
+ expectProcessedImpedanceResults(processedImpedanceResults, {
zStr: "27.88 - 11.2j",
zPolarStr: "30.05 ∠ -21.88°",
refStr: "-0.258 - 0.181j",
@@ -116,7 +136,7 @@ test("Cap with ESL and ESR", () => {
settings,
);
- expect(processedImpedanceResults).toEqual({
+ expectProcessedImpedanceResults(processedImpedanceResults, {
zStr: "73.00 - 34.57j",
zPolarStr: "80.77 ∠ -25.34°",
refStr: "0.246 - 0.212j",
@@ -166,7 +186,7 @@ test("Ideal Transformer", () => {
settings,
);
- expect(processedImpedanceResults).toEqual({
+ expectProcessedImpedanceResults(processedImpedanceResults, {
zStr: "83.23 + 83.23j",
zPolarStr: "117.71 ∠ 45.00°",
refStr: "0.460 + 0.337j",
@@ -215,7 +235,7 @@ test("Coupled Transformer", () => {
settings,
);
- expect(processedImpedanceResults).toEqual({
+ expectProcessedImpedanceResults(processedImpedanceResults, {
zStr: "152.12 - 339.33j",
zPolarStr: "371.87 ∠ -65.85°",
refStr: "0.870 - 0.218j",