Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 87 additions & 37 deletions src/Circuit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -534,50 +534,99 @@ function ComplexComponent({ real, imaginary, index, setUserCircuit, slider_re, s
);
}

function TransformerComponent({ l1, unit_l1, l2, unit_l2, k, index, setUserCircuit }) {
function TransformerComponent({ l1, unit_l1, l2, unit_l2, k, model, index, setUserCircuit }) {
const modelValue = model ?? "coupledInductor";
return (
<>
<Typography variant="caption" align="center" sx={{ display: "block" }}>
~
</Typography>
<Box sx={{ display: "flex", m: 0, p: 0, mt: 1 }}>
<TextField
label="L1"
variant="outlined"
size="small"
sx={{ mx: 0.5, p: 0, padding: 0 }}
value={l1}
onChange={(e) => setValue(e.target.value, "l1", setUserCircuit, index)}
<RadioGroup
row
value={modelValue}
onChange={(e) => setUnit(e.target.value, "model", setUserCircuit, index)}
sx={{ m: 0, mb: 1, p: 0, alignItems: "center", minHeight: 60, "& .MuiFormControlLabel-root": { margin: 0 } }}
>
<FormControlLabel
value="coupledInductor"
control={<Radio size="small" sx={{ p: 0.25 }} />}
label="Coupled-Inductor Model"
sx={{ "& .MuiFormControlLabel-label": { fontSize: "0.7rem", lineHeight: 1 }, my: 0, mr: 1 }}
/>
<Select value={unit_l1} size="small" sx={{ marginRight: 0.5 }} onChange={(e) => setUnit(e.target.value, "unit_l1", setUserCircuit, index)}>
{Object.keys(inductorUnits).map((u) => (
<MenuItem value={u}>{u}</MenuItem>
))}
</Select>
</Box>
<Box sx={{ display: "flex", m: 0, p: 0, mt: 1 }}>
<TextField
label="L2"
variant="outlined"
size="small"
sx={{ mx: 0.5, p: 0, padding: 0 }}
value={l2}
onChange={(e) => setValue(e.target.value, "l2", setUserCircuit, index)}
<FormControlLabel
value="ideal"
control={<Radio size="small" sx={{ p: 0.25 }} />}
label="Ideal Transformer"
sx={{ "& .MuiFormControlLabel-label": { fontSize: "0.7rem", lineHeight: 1 }, my: 0 }}
/>
<Select value={unit_l2} size="small" sx={{ marginRight: 0.5 }} onChange={(e) => setUnit(e.target.value, "unit_l2", setUserCircuit, index)}>
{Object.keys(inductorUnits).map((u) => (
<MenuItem value={u}>{u}</MenuItem>
))}
</Select>
</Box>
<TextField
label="Coupling Factor"
variant="outlined"
size="small"
sx={{ mx: 0.5, p: 0, padding: 0, mt: 1 }}
value={k}
onChange={(e) => setValue(e.target.value, "k", setUserCircuit, index)}
/>
</RadioGroup>
{modelValue === "coupledInductor" && (
<>
<Box sx={{ display: "flex", m: 0, p: 0, mt: 1, mb: 1.2 }}>
<TextField
label="L1"
variant="outlined"
size="small"
sx={{ mx: 0.5, p: 0, padding: 0 }}
value={l1}
onChange={(e) => setValue(e.target.value, "l1", setUserCircuit, index)}
/>
<Select
value={unit_l1}
size="small"
sx={{ marginRight: 0.5 }}
onChange={(e) => setUnit(e.target.value, "unit_l1", setUserCircuit, index)}
>
{Object.keys(inductorUnits).map((u) => (
<MenuItem value={u}>{u}</MenuItem>
))}
</Select>
</Box>
<Box sx={{ display: "flex", m: 0, p: 0, mb: 1.2 }}>
<TextField
label="L2"
variant="outlined"
size="small"
sx={{ mx: 0.5, p: 0, padding: 0 }}
value={l2}
onChange={(e) => setValue(e.target.value, "l2", setUserCircuit, index)}
/>
<Select
value={unit_l2}
size="small"
sx={{ marginRight: 0.5 }}
onChange={(e) => setUnit(e.target.value, "unit_l2", setUserCircuit, index)}
>
{Object.keys(inductorUnits).map((u) => (
<MenuItem value={u}>{u}</MenuItem>
))}
</Select>
</Box>
<TextField
label="Coupling Factor"
variant="outlined"
size="small"
sx={{ mx: 0.5, p: 0, padding: 0 }}
value={k}
onChange={(e) => setValue(e.target.value, "k", setUserCircuit, index)}
/>
</>
)}
{modelValue === "ideal" && (
<Box sx={{ display: "flex", alignItems: "center", m: 0, p: 0, mt: 1, ml: 1, gap: 0.5 }}>
<Typography variant="body2" sx={{ fontSize: "0.875rem" }}>
1 :
</Typography>
<TextField
label="Turns Ratio"
variant="outlined"
size="small"
sx={{ mx: 0.5, p: 0, padding: 0, flex: 1 }}
value={k}
onChange={(e) => setValue(e.target.value, "k", setUserCircuit, index)}
/>
</Box>
)}
</>
);
}
Expand Down Expand Up @@ -920,6 +969,7 @@ function Circuit({ userCircuit, setUserCircuit, frequency, setPlotType, setSetti
l2={component.l2}
unit_l2={component.unit_l2}
k={component.k}
model={component.model}
index={index}
setUserCircuit={setUserCircuit}
key={type}
Expand Down
5 changes: 3 additions & 2 deletions src/circuitComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,8 @@ export const circuitComponents = {
name: "Transformer",
src: transformer,
circuitInputs: ["transformer"],
default: { l1: 1, unit_l1: "nH", l2: 1, unit_l2: "nH", k: 1 },
toURL: (c) => `transformer_${c.l1}_${c.unit_l1}_${c.l2}_${c.unit_l2}_${c.k}`,
default: { l1: 1, unit_l1: "nH", l2: 1, unit_l2: "nH", k: 1, model: "coupledInductor" },
toURL: (c) => `transformer_${c.l1}_${c.unit_l1}_${c.l2}_${c.unit_l2}_${c.k}_${c.model ?? "coupledInductor"}`,
fromURL: (u) => {
return {
name: u[0],
Expand All @@ -366,6 +366,7 @@ export const circuitComponents = {
l2: Number(u[3]),
unit_l2: u[4],
k: Number(u[5]),
model: u[6] === "ideal" ? "ideal" : "coupledInductor",
};
},
},
Expand Down
62 changes: 38 additions & 24 deletions src/impedanceFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,30 +151,44 @@ export function calculateImpedance(userCircuit, frequency, resolution, showIdeal
line_length = ((lengthLambda % 0.5) * speedOfLight) / frequency;
calculateTlineZ(resolution, component, line_length, beta, startImaginary, startReal, impedanceResolution, startAdmittance);
} else if (component.name == "transformer") {
//coupled inductor model. Do 3 separate equations
// --- L1 --- --- L2 --- <- look this way
// | |
// Zo Lm
// | |
var l1w = w * component.l1 * unitConverter[component.unit_l1];
var l2w = w * component.l2 * unitConverter[component.unit_l2];
var lmw = component.k * Math.sqrt(l1w * l2w);
var i1z, i2z, newStartAdmittance;

for (j = 0; j <= resolution; j++) {
//L1
i1z = {
real: startReal,
imaginary: startImaginary + ((l1w - lmw) * j) / resolution,
};
//Lm
newStartAdmittance = one_over_complex(i1z);
i2z = one_over_complex({ real: newStartAdmittance.real, imaginary: newStartAdmittance.imaginary - ((1 / lmw) * j) / resolution });
//L2
impedanceResolution.push({
real: i2z.real,
imaginary: i2z.imaginary + ((l2w - lmw) * j) / resolution,
});
if (component.model === "ideal") {
// Ideal transformer: Z_out = n² * Z_in (turns ratio n from primary to secondary)
var n = Number(component.k);
if (isNaN(n) || n <= 0) n = 1;
var n2 = n * n;
for (j = 0; j <= resolution; j++) {
const transformerScaler = 1 + ((n2 - 1) * j) / resolution;
impedanceResolution.push({
real: transformerScaler * startReal,
imaginary: transformerScaler * startImaginary,
});
}
} else {
// Coupled inductor model. Do 3 separate equations
// --- L1 --- --- L2 --- <- look this way
// | |
// Zo Lm
// | |
var l1w = w * component.l1 * unitConverter[component.unit_l1];
var l2w = w * component.l2 * unitConverter[component.unit_l2];
var lmw = component.k * Math.sqrt(l1w * l2w);
var i1z, i2z, newStartAdmittance;

for (j = 0; j <= resolution; j++) {
//L1
i1z = {
real: startReal,
imaginary: startImaginary + ((l1w - lmw) * j) / resolution,
};
//Lm
newStartAdmittance = one_over_complex(i1z);
i2z = one_over_complex({ real: newStartAdmittance.real, imaginary: newStartAdmittance.imaginary - ((1 / lmw) * j) / resolution });
//L2
impedanceResolution.push({
real: i2z.real,
imaginary: i2z.imaginary + ((l2w - lmw) * j) / resolution,
});
}
}
} else if (component.name == "custom") {
newImpedance = CustomZAtFrequency(component.value, frequency, component.interpolation);
Expand Down
99 changes: 99 additions & 0 deletions tests/impedance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,105 @@ test("Cap with ESL and ESR", () => {
});
});

test("Ideal Transformer", () => {
const circuit = [
{
name: "blackBox",
real: "20",
imaginary: "20",
tolerance: 0,
},
{
name: "transformer",
l1: 2,
unit_l1: "nH",
l2: 1,
unit_l2: "nH",
k: 2.04,
model: "ideal",
},
];
const settings = {
zo: 50,
frequency: 2440,
frequencyUnit: "MHz",
fSpan: 0,
fSpanUnit: "MHz",
fRes: 10,
zMarkers: [],
vswrCircles: [],
qCircles: [],
nfCircles: [],
gainInCircles: [],
gainOutCircles: [],
};
const [processedImpedanceResults, _spanResults, _multiZResults, _gainArray, _noiseArray, _numericalFrequency, _RefIn] = allImpedanceCalculations(
circuit,
settings,
);

expect(processedImpedanceResults).toEqual({
zStr: "83.23 + 83.23j",
zPolarStr: "117.71 ∠ 45.00°",
refStr: "0.460 + 0.337j",
refPolarStr: "0.570 ∠ 36.2°",
vswr: "3.66",
qFactor: "1.00",
refReal: 0.46012557939346155,
refImag: 0.33726753164347456,
admString: "0.00601 - 0.00601j",
});
});

test("Coupled Transformer", () => {
const circuit = [
{
name: "blackBox",
real: "20",
imaginary: "20",
tolerance: 0,
},
{
name: "transformer",
l1: 2,
unit_l1: "nH",
l2: 3,
unit_l2: "nH",
k: 4,
},
];
const settings = {
zo: 50,
frequency: 2440,
frequencyUnit: "MHz",
fSpan: 0,
fSpanUnit: "MHz",
fRes: 10,
zMarkers: [],
vswrCircles: [],
qCircles: [],
nfCircles: [],
gainInCircles: [],
gainOutCircles: [],
};
const [processedImpedanceResults, _spanResults, _multiZResults, _gainArray, _noiseArray, _numericalFrequency, _RefIn] = allImpedanceCalculations(
circuit,
settings,
);

expect(processedImpedanceResults).toEqual({
zStr: "152.12 - 339.33j",
zPolarStr: "371.87 ∠ -65.85°",
refStr: "0.870 - 0.218j",
refPolarStr: "0.897 ∠ -14.0°",
vswr: "18.5",
qFactor: "2.23",
refReal: 0.8704366392639906,
refImag: -0.2175237548329506,
admString: "0.00110 + 0.00245j",
});
});

test("Impedance s1p", () => {
const circuit = [
{
Expand Down
1 change: 1 addition & 0 deletions tests/url.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ test("URL clicked them all", () => {
l2: 3,
unit_l2: "nH",
k: 2,
model: "coupledInductor",
},
]);
});