kopia lustrzana https://github.com/miguelvaca/vk3cpu
New versions - imperial and large metric
rodzic
c479f53e30
commit
2d42445ee8
29
inductor.css
29
inductor.css
|
@ -114,6 +114,35 @@ div input {
|
|||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
section.gridLayoutClass {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
grid-template-areas:
|
||||
"inductor-container" "slider-container"
|
||||
"inductor-container" "notes";
|
||||
justify-items: stretch;
|
||||
}
|
||||
|
||||
section div.inductor-container {
|
||||
height: 100vh;
|
||||
width: 50vw;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
section div.slider_container {
|
||||
width: 100%;
|
||||
height: 20%;
|
||||
}
|
||||
|
||||
section div.notes {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@media (orientation: landscape) {
|
||||
section.gridLayoutClass {
|
||||
|
|
239
inductor.html
239
inductor.html
|
@ -7,7 +7,7 @@
|
|||
<link rel="stylesheet" href="inductor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Miguel <a href="mailto:vk3cpu@gmail.com">VK3CPU</a> - RF Inductor Calculator v1.0</header>
|
||||
<header><a href="mailto:vk3cpu@gmail.com">VK3CPU</a> - RF Inductor Calculator v1.0<br><a href="inductor_imp.html">[Imperial]</a> <a href="inductor_lrg.html">[Large Metric]</a></header>
|
||||
<section class="gridLayoutClass">
|
||||
<div id="inductor-container" class="inductor-container" style="position: relative;">
|
||||
<canvas id="inductor2D" class="inductorClass" width="350" height="350">
|
||||
|
@ -16,7 +16,7 @@
|
|||
<div class="slider_container">
|
||||
<div class="sliders">
|
||||
<label for="conductor_diameter_slider">⌀a:</label>
|
||||
<input type="range" id="conductor_diameter_slider" min="0.1" max="5.0" value="1.0" step="0.05">
|
||||
<input type="range" id="conductor_diameter_slider" min="0.1" max="4.0" value="1.0" step="0.05">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="loop_diameter_slider">⌀b:</label>
|
||||
|
@ -35,6 +35,7 @@
|
|||
<input type="range" id="frequency_slider" min="1.0" max="30.0" value="7.0" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
<div id="notes" class="notes">
|
||||
<br>
|
||||
<b><u>Notes:</u></b><br>
|
||||
RF Inductor Calculator was developed to help users predict the RF characteristics of a single-layer solenoid-style air-core inductor. <br><br>
|
||||
|
@ -69,193 +70,73 @@
|
|||
<li>Rac : AC resistance is calculated using the skin effect and proximity resistance from empirical data collected by Medhurst using the spacing ratio, and length-to-diameter ratio.</li>
|
||||
<li>Q : Quality factor of device, based on reactance (X) ÷ resistance (Rac) at the given frequency.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-crosshair@1.1.2"></script>
|
||||
<script src="inductor.js"></script>
|
||||
<script>
|
||||
var loop_diameter_slider = document.getElementById("loop_diameter_slider");
|
||||
var loop_diameter_value = document.getElementById("loop_diameter_value");
|
||||
var val = loop_diameter_slider.value * 1.0;
|
||||
// Define global storage for calculated values, so we don't recalculate the same things multiple times:
|
||||
var inductor = {
|
||||
L : 0.0,
|
||||
C : 0.0,
|
||||
Rdc : 0.0,
|
||||
SRF : 0.0,
|
||||
|
||||
var conductor_diameter_slider = document.getElementById("conductor_diameter_slider");
|
||||
var conductor_diameter_value = document.getElementById("conductor_diameter_value");
|
||||
|
||||
var loop_turns_slider = document.getElementById("loop_turns_slider");
|
||||
var loop_turns_value = document.getElementById("loop_turns_value");
|
||||
X : 0.0,
|
||||
skin_depth : 0.0,
|
||||
Rac : 0.0,
|
||||
Q : 0.0
|
||||
};
|
||||
|
||||
var loop_spacing_slider = document.getElementById("loop_spacing_slider");
|
||||
var loop_spacing_value = document.getElementById("loop_spacing_value");
|
||||
val = loop_spacing_slider.value * 1.0;
|
||||
|
||||
// Global constants:
|
||||
const mu0 = Math.PI * 4e-7;
|
||||
const cu_sigma = 58e6; // Copper conductance value
|
||||
|
||||
function dcResistance() {
|
||||
const loop_diameter_meters = 1e-3 * loop_diameter_slider.value;
|
||||
const cond_diameter_meters = 1e-3 * conductor_diameter_slider.value;
|
||||
const cond_radius_meters = 0.5 * cond_diameter_meters;
|
||||
const c_spacing = loop_spacing_slider.value * cond_diameter_meters;
|
||||
const corr_factor = Math.sqrt(1.0 + (c_spacing**2 / loop_diameter_meters**2));
|
||||
const conductor_length = Math.PI * loop_diameter_meters * loop_turns_slider.value * corr_factor;
|
||||
const conductor_area = Math.PI * (cond_radius_meters**2.0);
|
||||
//console.log(conductor_length, conductor_area);
|
||||
return 1.68e-8 * conductor_length / conductor_area;
|
||||
}
|
||||
|
||||
function skinDepth() {
|
||||
return Math.sqrt(1.0 / (Math.PI * frequency_slider.value * 1e6 * mu0 * cu_sigma));
|
||||
}
|
||||
|
||||
// From Knight "Solenoid Impedance and Q"
|
||||
function dc2acFactor() {
|
||||
const diameter = 1e-3 * conductor_diameter_slider.value;
|
||||
const skin_depth = skinDepth();
|
||||
return diameter**2 / (4.0 * (diameter * skin_depth - skin_depth**2));
|
||||
}
|
||||
|
||||
function getInductance() {
|
||||
const a_coil_radius = loop_diameter_slider.value * 0.0005;
|
||||
const b_conductor_radius = conductor_diameter_slider.value * 0.0005;
|
||||
const n_turns = 1.0 * loop_turns_slider.value;
|
||||
const coil_length = b_conductor_radius * 2.0 * loop_spacing_slider.value * n_turns;
|
||||
var retval = (n_turns**2.0) * mu0 * Math.PI * (a_coil_radius**2.0) * nagaokaCoefficient() / coil_length;
|
||||
return retval; // In Henries
|
||||
}
|
||||
|
||||
function inductiveReactance(frequency) {
|
||||
return 2.0 * Math.PI * frequency * getInductance(); // In Ohms
|
||||
}
|
||||
|
||||
function nagaokaCoefficient() {
|
||||
// From Knight's 2016 paper on coil self-resonance, attributed to Wheeler's 1982 eqn as modified by Bob Weaver
|
||||
const loop_diameter_meters = 1e-3 * loop_diameter_slider.value;
|
||||
const cond_diameter_meters = 1e-3 * conductor_diameter_slider.value;
|
||||
const c_spacing = loop_spacing_slider.value * cond_diameter_meters;
|
||||
const x = loop_diameter_meters / (c_spacing * loop_turns_slider.value);
|
||||
const zk = 2.0 / (Math.PI * x);
|
||||
const k0 = 1.0 / (Math.log(8.0 / Math.PI) - 0.5);
|
||||
const k2 = 24.0 / (3.0 * Math.PI**2 - 16.0);
|
||||
const w = -0.47 / (0.755 + x)**1.44;
|
||||
const p = k0 + 3.437/x + k2/x**2 + w;
|
||||
var retval = zk * (Math.log(1 + 1/zk) + 1/p);
|
||||
return retval;
|
||||
}
|
||||
|
||||
function ctdw(ff, ei, ex) {
|
||||
// From Knight's 2016 paper
|
||||
const kL = nagaokaCoefficient();
|
||||
const kct = 1.0/kL - 1.0;
|
||||
return 11.27350207 * ex * ff * (1.0 + kct * (1.0 + ei/ex) / 2.0);
|
||||
}
|
||||
|
||||
function ciae(ff, ei, ex) {
|
||||
// From Knight's 2016 paper
|
||||
return 17.70837564 * (ei+ex) / Math.log(1.0 + Math.PI**2 * ff);
|
||||
}
|
||||
|
||||
function multiloopCapacitance() {
|
||||
const loop_diameter_meters = 1e-3 * loop_diameter_slider.value;
|
||||
const cond_diameter_meters = 1e-3 * conductor_diameter_slider.value;
|
||||
const e0 = 8.854187e-12;
|
||||
const h = 1.0 * loop_spacing_slider.value * cond_diameter_meters;
|
||||
const ei = 1.0; // Assume internal epsilon is air (or free-space)
|
||||
const ex = 1.0; // Assume external epsilon is air (or free-space)
|
||||
const solenoid_length = 1.0 * loop_turns_slider.value * h;
|
||||
const ff = solenoid_length / loop_diameter_meters;
|
||||
var multiloop_capacitance = 1e-12 * (ctdw(ff, ei, ex) / Math.sqrt(1 - h**2 / loop_diameter_meters**2) + ciae(ff, ei, ex)) * loop_diameter_meters;
|
||||
return multiloop_capacitance; // in Farads
|
||||
}
|
||||
|
||||
const proximityResistance = [
|
||||
// From R.G. Medhurst H.F. Resistance and Self-Capacitance of Single-Layer Solenoids (Feb, 1947)
|
||||
[ 0.0, 1.0, 1.111, 1.25, 1.429, 1.667, 2.0, 2.5, 3.333, 5.00, 10.0 ],
|
||||
[ 0.0, 5.31, 3.73, 2.74, 2.12, 1.74, 1.44, 1.20, 1.16, 1.07, 1.02 ],
|
||||
[ 0.2, 5.45, 3.84, 2.83, 2.20, 1.77, 1.48, 1.29, 1.19, 1.08, 1.02 ],
|
||||
[ 0.4, 5.65, 3.99, 2.97, 2.28, 1.83, 1.54, 1.33, 1.21, 1.08, 1.03 ],
|
||||
[ 0.6, 5.80, 4.11, 3.10, 2.38, 1.89, 1.60, 1.38, 1.22, 1.10, 1.03 ],
|
||||
[ 0.8, 5.80, 4.17, 3.20, 2.44, 1.92, 1.64, 1.42, 1.23, 1.10, 1.03 ],
|
||||
[ 1.0, 5.55, 4.10, 3.17, 2.47, 1.94, 1.67, 1.45, 1.24, 1.10, 1.03 ],
|
||||
[ 2.0, 4.10, 3.36, 2.74, 2.32, 1.98, 1.74, 1.50, 1.28, 1.13, 1.04 ],
|
||||
[ 4.0, 3.54, 3.05, 2.60, 2.27, 2.01, 1.78, 1.54, 1.32, 1.15, 1.04 ],
|
||||
[ 6.0, 3.31, 2.92, 2.60, 2.29, 2.03, 1.80, 1.56, 1.34, 1.16, 1.04 ],
|
||||
[ 8.0, 3.20, 2.90, 2.62, 2.34, 2.08, 1.81, 1.57, 1.34, 1.165, 1.04],
|
||||
[ 10.0, 3.23, 2.93, 2.65, 2.37, 2.10, 1.83, 1.58, 1.35, 1.17, 1.04 ],
|
||||
[999.0, 3.41, 3.11, 2.815, 2.51, 2.22, 1.93, 1.65, 1.395, 1.19, 1.05]
|
||||
];
|
||||
|
||||
function getProximityResFromSpacing() {
|
||||
// Use the proximityResistance look-up table and interpolate values depending on the spacing ratio and the number of turns.
|
||||
var retval = 0.0;
|
||||
const n_turns = 1.0 * loop_turns_slider.value;
|
||||
// Solve all the parameters, and re-draw the canvas:
|
||||
function recalculate() {
|
||||
// Input variables:
|
||||
const loop_diameter_meters = 0.001 * loop_diameter_slider.value;
|
||||
const cond_diameter_meters = 0.001 * conductor_diameter_slider.value;
|
||||
const spacing_ratio = 1.0 * loop_spacing_slider.value;
|
||||
const length_diameter_ratio = n_turns * spacing_ratio * conductor_diameter_slider.value / loop_diameter_slider.value;
|
||||
var i = 0; // Going to the right, which is the spacing ratio
|
||||
var j = 0; // Going down the page, which is the solenoid length to diameter ratio
|
||||
for (i = 1; i < (proximityResistance[0].length); i++) {
|
||||
if(spacing_ratio < proximityResistance[0][i]) {
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (j = 1; j < (proximityResistance.length); j++) {
|
||||
if(length_diameter_ratio < proximityResistance[j][0]) {
|
||||
j--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var t1 = ((spacing_ratio - proximityResistance[0][i]) / (proximityResistance[0][i+1] - proximityResistance[0][i])) * (proximityResistance[j][i+1] - proximityResistance[j][i]) + proximityResistance[j][i];
|
||||
var t2 = ((spacing_ratio - proximityResistance[0][i]) / (proximityResistance[0][i+1] - proximityResistance[0][i])) * (proximityResistance[j+1][i+1] - proximityResistance[j+1][i]) + proximityResistance[j+1][i];
|
||||
retval = ((length_diameter_ratio - proximityResistance[j][0])/(proximityResistance[j+1][0] - proximityResistance[j][0])) * (t2 - t1) + t1;
|
||||
return retval;
|
||||
}
|
||||
|
||||
function acResistance() {
|
||||
const Rdc = dcResistance();
|
||||
const dc2ac = dc2acFactor();
|
||||
const prox = getProximityResFromSpacing();
|
||||
// console.log(Rdc, dc2ac, prox);
|
||||
return Rdc * dc2ac * prox;
|
||||
}
|
||||
|
||||
function qualityFactor(frequency) {
|
||||
const Xl = inductiveReactance(frequency);
|
||||
const Rac = acResistance();
|
||||
const Q = Xl / Rac;
|
||||
return Q;
|
||||
}
|
||||
|
||||
function selfResonantFrequency() {
|
||||
var freq = 1.0 / (2.0 * Math.PI * Math.sqrt(getInductance() * multiloopCapacitance()));
|
||||
return freq;
|
||||
const loop_turns = 1.0 * loop_turns_slider.value;
|
||||
const frequency_hz = 1e6 * frequency_slider.value;
|
||||
// Frequency independent characteristics:
|
||||
inductor.L = getInductance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.C = multiloopCapacitance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.Rdc = dcResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.SRF = selfResonantFrequency(inductor.L, inductor.C);
|
||||
// Frequency dependent characteristics:
|
||||
inductor.X = inductiveReactance(frequency_hz, inductor.L);
|
||||
inductor.skin_depth = skinDepth(frequency_hz);
|
||||
inductor.Rac = acResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns, frequency_hz);
|
||||
inductor.Q = qualityFactor(inductor.X, inductor.Rac);
|
||||
// Redraw the canvas:
|
||||
drawDesign();
|
||||
}
|
||||
|
||||
loop_diameter_slider.oninput = function() {
|
||||
drawDesign();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
conductor_diameter_slider.oninput = function() {
|
||||
drawDesign();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
loop_turns_slider.oninput = function() {
|
||||
drawDesign();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
loop_spacing_slider.oninput = function() {
|
||||
drawDesign();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
frequency_slider.oninput = function() {
|
||||
drawDesign();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onresize = function() {
|
||||
drawDesign();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onorientationchange = function() {
|
||||
drawDesign();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onbeforeprint = function() {
|
||||
|
@ -273,7 +154,7 @@
|
|||
afront_canvas.height = win_height-12;
|
||||
|
||||
fctx.clearRect(0, 0, win_width, win_height);
|
||||
const loop_radius = 0.14 * win_height; // 100; // loop_diameter_slider.value * 80;
|
||||
const loop_radius = 0.11 * win_height; // 100; // loop_diameter_slider.value * 80;
|
||||
var cond_radius = loop_radius * conductor_diameter_slider.value / loop_diameter_slider.value;
|
||||
const loopx = win_width/2;
|
||||
const loopy = win_height/4;
|
||||
|
@ -321,8 +202,8 @@
|
|||
// Write loop diameter symbol:
|
||||
fctx.font = "12px arial";
|
||||
fctx.textAlign = "right";
|
||||
const dia = 1.0 * loop_diameter_slider.value;
|
||||
fctx.fillText("\u2300b = " + dia.toPrecision(3).toString() + "mm", loopx - loop_radius - 2.0*arrow_size, y_offset - 2);
|
||||
const loop_dia = 1.0 * loop_diameter_slider.value; // Convert from mm to inches
|
||||
fctx.fillText("\u2300b = " + loop_dia.toPrecision(3).toString() + "mm", loopx - loop_radius - 2.0*arrow_size, y_offset - 2);
|
||||
|
||||
// Draw inner-diameter arrows: (for using a winding former)
|
||||
const inner_dia_y = loopy + loop_radius + 40;
|
||||
|
@ -344,7 +225,7 @@
|
|||
fctx.lineTo(loopx + loop_radius + 3.0*arrow_size, inner_dia_y);
|
||||
fctx.stroke();
|
||||
fctx.textAlign = "left";
|
||||
fctx.fillText("\u2300i = " + (dia-0.5*conductor_diameter_slider.value).toPrecision(3).toString() + "mm", loopx + loop_radius + 2.0*arrow_size, inner_dia_y - 2);
|
||||
fctx.fillText("\u2300i = " + (loop_dia-0.5*conductor_diameter_slider.value).toPrecision(3).toString() + "mm", loopx + loop_radius + 2.0*arrow_size, inner_dia_y - 2);
|
||||
|
||||
// Draw outer-diameter arrows: (for using a winding former)
|
||||
const outer_dia_y = loopy + loop_radius + 0;
|
||||
|
@ -365,16 +246,16 @@
|
|||
fctx.lineTo(loopx + loop_radius + cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius + 3.0*arrow_size, outer_dia_y);
|
||||
fctx.stroke();
|
||||
fctx.fillText("\u2300o = " + (dia+0.5*conductor_diameter_slider.value).toPrecision(3).toString() + "mm", loopx + loop_radius + 2.0*arrow_size, outer_dia_y - 2);
|
||||
fctx.fillText("\u2300o = " + (loop_dia+0.5*conductor_diameter_slider.value).toPrecision(3).toString() + "mm", loopx + loop_radius + 2.0*arrow_size, outer_dia_y - 2);
|
||||
|
||||
// Write loop inductance:
|
||||
fctx.font = "12px arial";
|
||||
fctx.textAlign = "left";
|
||||
const L = getInductance() * 1.0e+6;
|
||||
const L = inductor.L * 1.0e+6;
|
||||
fctx.fillText("L = " + L.toPrecision(3).toString() + " \u03bcH", 8, 18);
|
||||
fctx.fillText("C = " + (multiloopCapacitance()*1e12).toFixed(1) + " pF", 8, 32);
|
||||
fctx.fillText("Rdc = " + dcResistance().toFixed(2) + " \u03A9", 8, 46);
|
||||
fctx.fillText("SRF = " + (selfResonantFrequency()*1e-6).toFixed(1) + " MHz", 8, 60);
|
||||
fctx.fillText("C = " + (inductor.C * 1e12).toFixed(1) + " pF", 8, 32);
|
||||
fctx.fillText("Rdc = " + inductor.Rdc.toFixed(2) + " \u03A9", 8, 46);
|
||||
fctx.fillText("SRF = " + (inductor.SRF * 1e-6).toFixed(1) + " MHz", 8, 60);
|
||||
|
||||
// Draw conductor diameter arrow:
|
||||
fctx.beginPath();
|
||||
|
@ -494,8 +375,8 @@
|
|||
cond_spacing = 2.0 * cond_radius * loop_spacing_slider.value;
|
||||
}
|
||||
var start_x = win_width/2.0 - loop_turns_slider.value * cond_spacing * 0.5;
|
||||
var top_y = win_height * 0.60;
|
||||
var bot_y = top_y + cond_radius * (loop_diameter_slider.value / conductor_diameter_slider.value);
|
||||
var top_y = win_height * 0.56;
|
||||
var bot_y = top_y + 2.0 * cond_radius * (loop_diameter_slider.value / conductor_diameter_slider.value);
|
||||
|
||||
for (let i = 0; i < loop_turns_slider.value; i++) {
|
||||
fctx.beginPath();
|
||||
|
@ -522,7 +403,7 @@
|
|||
fctx.fillStyle = "black";
|
||||
|
||||
// Draw left spacing arrow:
|
||||
const dim_y = win_height * 0.90;
|
||||
const dim_y = win_height * 0.92;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x - 20, dim_y);
|
||||
fctx.lineTo(start_x, dim_y);
|
||||
|
@ -571,13 +452,13 @@
|
|||
fctx.textAlign = "right";
|
||||
var freq = 1.0 * frequency_slider.value;
|
||||
fctx.fillText("f = " + freq.toFixed(1) + " MHz", win_width-18, 18);
|
||||
fctx.fillText("Xl = " + inductiveReactance(freq * 1e6).toFixed(1) + " \u03A9", win_width-18, 32);
|
||||
fctx.fillText("\u03B4 = " + (skinDepth() * 1e6).toFixed(1) + " \u03BCm", win_width-18, 46);
|
||||
fctx.fillText("Rac = " + acResistance(freq * 1e6).toFixed(2) + " \u03A9", win_width-18, 60);
|
||||
fctx.fillText("Q = " + qualityFactor(freq * 1e6).toFixed(1), win_width-18, 74);
|
||||
fctx.fillText("Xl = " + inductor.X.toFixed(1) + " \u03A9", win_width-18, 32);
|
||||
fctx.fillText("\u03B4 = " + (inductor.skin_depth * 1e6).toFixed(1) + " \u03BCm", win_width-18, 46);
|
||||
fctx.fillText("Rac = " + inductor.Rac.toFixed(2) + " \u03A9", win_width-18, 60);
|
||||
fctx.fillText("Q = " + inductor.Q.toFixed(1), win_width-18, 74);
|
||||
|
||||
fctx.textAlign = "center";
|
||||
fctx.fillText("N = " + loop_turns_slider.value.toString(), win_width/2, win_height * 0.56);
|
||||
fctx.fillText("N = " + loop_turns_slider.value.toString(), win_width/2, win_height * 0.52);
|
||||
|
||||
// Draw spacing text: (gap is to avoid collision of spacing and length texts)
|
||||
fctx.textAlign = "right";
|
||||
|
@ -589,7 +470,7 @@
|
|||
const sol_len = loop_turns_slider.value*spc;
|
||||
fctx.fillText("\u2113 = " + sol_len.toFixed(1).toString() + "mm", start_x + loop_turns_slider.value*cond_spacing + 20, dim_y + 20);
|
||||
}
|
||||
drawDesign();
|
||||
recalculate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,127 @@
|
|||
// Global constants:
|
||||
const mu0 = Math.PI * 4e-7;
|
||||
const cu_sigma = 58e6; // Copper conductance value
|
||||
|
||||
function dcResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns) {
|
||||
const cond_radius_meters = 0.5 * cond_diameter_meters;
|
||||
const c_spacing = spacing_ratio * cond_diameter_meters;
|
||||
const corr_factor = Math.sqrt(1.0 + (c_spacing**2 / loop_diameter_meters**2));
|
||||
const conductor_length = Math.PI * loop_diameter_meters * loop_turns * corr_factor;
|
||||
const conductor_area = Math.PI * (cond_radius_meters**2.0);
|
||||
return 1.68e-8 * conductor_length / conductor_area;
|
||||
}
|
||||
|
||||
function skinDepth(frequency_hz) {
|
||||
return Math.sqrt(1.0 / (Math.PI * frequency_hz * mu0 * cu_sigma));
|
||||
}
|
||||
|
||||
// From Knight "Solenoid Impedance and Q"
|
||||
function dc2acFactor(cond_diameter_meters, skin_depth) {
|
||||
return cond_diameter_meters**2 / (4.0 * (cond_diameter_meters * skin_depth - skin_depth**2));
|
||||
}
|
||||
|
||||
function getInductance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns) {
|
||||
const a_coil_radius = loop_diameter_meters * 0.5;
|
||||
//const b_conductor_radius = cond_diameter_meters * 0.5;
|
||||
const coil_length = cond_diameter_meters * spacing_ratio * loop_turns;
|
||||
var retval = (loop_turns**2.0) * mu0 * Math.PI * (a_coil_radius**2.0) * nagaokaCoefficient(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns) / coil_length;
|
||||
return retval; // In Henries
|
||||
}
|
||||
|
||||
function inductiveReactance(frequency, inductance) {
|
||||
return 2.0 * Math.PI * frequency * inductance; // In Ohms
|
||||
}
|
||||
|
||||
function nagaokaCoefficient(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns) {
|
||||
// From Knight's 2016 paper on coil self-resonance, attributed to Wheeler's 1982 eqn as modified by Bob Weaver
|
||||
const c_spacing = spacing_ratio * cond_diameter_meters;
|
||||
const x = loop_diameter_meters / (c_spacing * loop_turns);
|
||||
const zk = 2.0 / (Math.PI * x);
|
||||
const k0 = 1.0 / (Math.log(8.0 / Math.PI) - 0.5);
|
||||
const k2 = 24.0 / (3.0 * Math.PI**2 - 16.0);
|
||||
const w = -0.47 / (0.755 + x)**1.44;
|
||||
const p = k0 + 3.437/x + k2/x**2 + w;
|
||||
var retval = zk * (Math.log(1 + 1/zk) + 1/p);
|
||||
return retval;
|
||||
}
|
||||
|
||||
function ctdw(ff, ei, ex, kL) {
|
||||
// From Knight's 2016 paper
|
||||
//const kL = nagaokaCoefficient(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
const kct = 1.0/kL - 1.0;
|
||||
return 11.27350207 * ex * ff * (1.0 + kct * (1.0 + ei/ex) / 2.0);
|
||||
}
|
||||
|
||||
function ciae(ff, ei, ex) {
|
||||
// From Knight's 2016 paper
|
||||
return 17.70837564 * (ei+ex) / Math.log(1.0 + Math.PI**2 * ff);
|
||||
}
|
||||
|
||||
function multiloopCapacitance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns) {
|
||||
const e0 = 8.854187e-12;
|
||||
const h = spacing_ratio * cond_diameter_meters;
|
||||
const ei = 1.0; // Assume internal epsilon is air (or free-space)
|
||||
const ex = 1.0; // Assume external epsilon is air (or free-space)
|
||||
const solenoid_length = 1.0 * loop_turns * h;
|
||||
const ff = solenoid_length / loop_diameter_meters;
|
||||
const kL = nagaokaCoefficient(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
var multiloop_capacitance = 1e-12 * (ctdw(ff, ei, ex, kL) / Math.sqrt(1 - h**2 / loop_diameter_meters**2) + ciae(ff, ei, ex)) * loop_diameter_meters;
|
||||
return multiloop_capacitance; // in Farads
|
||||
}
|
||||
|
||||
const proximityResistance = [
|
||||
// From R.G. Medhurst H.F. Resistance and Self-Capacitance of Single-Layer Solenoids (Feb, 1947)
|
||||
[ 0.0, 1.0, 1.111, 1.25, 1.429, 1.667, 2.0, 2.5, 3.333, 5.00, 10.0 ],
|
||||
[ 0.0, 5.31, 3.73, 2.74, 2.12, 1.74, 1.44, 1.20, 1.16, 1.07, 1.02 ],
|
||||
[ 0.2, 5.45, 3.84, 2.83, 2.20, 1.77, 1.48, 1.29, 1.19, 1.08, 1.02 ],
|
||||
[ 0.4, 5.65, 3.99, 2.97, 2.28, 1.83, 1.54, 1.33, 1.21, 1.08, 1.03 ],
|
||||
[ 0.6, 5.80, 4.11, 3.10, 2.38, 1.89, 1.60, 1.38, 1.22, 1.10, 1.03 ],
|
||||
[ 0.8, 5.80, 4.17, 3.20, 2.44, 1.92, 1.64, 1.42, 1.23, 1.10, 1.03 ],
|
||||
[ 1.0, 5.55, 4.10, 3.17, 2.47, 1.94, 1.67, 1.45, 1.24, 1.10, 1.03 ],
|
||||
[ 2.0, 4.10, 3.36, 2.74, 2.32, 1.98, 1.74, 1.50, 1.28, 1.13, 1.04 ],
|
||||
[ 4.0, 3.54, 3.05, 2.60, 2.27, 2.01, 1.78, 1.54, 1.32, 1.15, 1.04 ],
|
||||
[ 6.0, 3.31, 2.92, 2.60, 2.29, 2.03, 1.80, 1.56, 1.34, 1.16, 1.04 ],
|
||||
[ 8.0, 3.20, 2.90, 2.62, 2.34, 2.08, 1.81, 1.57, 1.34, 1.165, 1.04],
|
||||
[ 10.0, 3.23, 2.93, 2.65, 2.37, 2.10, 1.83, 1.58, 1.35, 1.17, 1.04 ],
|
||||
[999.0, 3.41, 3.11, 2.815, 2.51, 2.22, 1.93, 1.65, 1.395, 1.19, 1.05]
|
||||
];
|
||||
|
||||
function getProximityResFromSpacing(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns) {
|
||||
// Use the proximityResistance look-up table and interpolate values depending on the spacing ratio and the number of turns.
|
||||
var retval = 0.0;
|
||||
const length_diameter_ratio = loop_turns * spacing_ratio * cond_diameter_meters / loop_diameter_meters;
|
||||
var i = 0; // Going to the right, which is the spacing ratio
|
||||
var j = 0; // Going down the page, which is the solenoid length to diameter ratio
|
||||
for (i = 1; i < (proximityResistance[0].length); i++) {
|
||||
if(spacing_ratio < proximityResistance[0][i]) {
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (j = 1; j < (proximityResistance.length); j++) {
|
||||
if(length_diameter_ratio < proximityResistance[j][0]) {
|
||||
j--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var t1 = ((spacing_ratio - proximityResistance[0][i]) / (proximityResistance[0][i+1] - proximityResistance[0][i])) * (proximityResistance[j][i+1] - proximityResistance[j][i]) + proximityResistance[j][i];
|
||||
var t2 = ((spacing_ratio - proximityResistance[0][i]) / (proximityResistance[0][i+1] - proximityResistance[0][i])) * (proximityResistance[j+1][i+1] - proximityResistance[j+1][i]) + proximityResistance[j+1][i];
|
||||
retval = ((length_diameter_ratio - proximityResistance[j][0])/(proximityResistance[j+1][0] - proximityResistance[j][0])) * (t2 - t1) + t1;
|
||||
return retval;
|
||||
}
|
||||
|
||||
function acResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns, frequency_hz) {
|
||||
const Rdc = dcResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
const dc2ac = dc2acFactor(cond_diameter_meters, skinDepth(frequency_hz));
|
||||
const prox = getProximityResFromSpacing(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
return Rdc * dc2ac * prox;
|
||||
}
|
||||
|
||||
function qualityFactor(reactance, resistance) {
|
||||
return reactance/resistance;
|
||||
}
|
||||
|
||||
function selfResonantFrequency(inductance, capacitance) {
|
||||
var freq = 1.0 / (2.0 * Math.PI * Math.sqrt(inductance * capacitance));
|
||||
return freq;
|
||||
}
|
|
@ -0,0 +1,437 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>VK3CPU RF Inductor Calculator</title>
|
||||
<link rel="stylesheet" href="inductor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header><a href="mailto:vk3cpu@gmail.com">VK3CPU</a> - RF Inductor Calculator v1.0<br><a href="inductor.html">[Metric]</a> <a href="inductor_lrg.html">[Large Metric]</a></header>
|
||||
<section class="gridLayoutClass">
|
||||
<div id="inductor-container" class="inductor-container" style="position: relative;">
|
||||
<canvas id="inductor2D" class="inductorClass" width="350" height="350">
|
||||
</canvas>
|
||||
</div>
|
||||
<div class="slider_container">
|
||||
<div class="sliders">
|
||||
<label for="conductor_diameter_slider">⌀a:</label>
|
||||
<input type="range" id="conductor_diameter_slider" min="0.010" max="0.250" value="0.032" step="0.001">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="loop_diameter_slider">⌀b:</label>
|
||||
<input type="range" id="loop_diameter_slider" min="0.25" max="2.0" value="0.50" step="0.01">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="loop_spacing_slider">c/a:</label>
|
||||
<input type="range" id="loop_spacing_slider" min="1.1" max="4.0" value="2.0" step="0.01">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="loop_turns_slider">N:</label>
|
||||
<input type="range" id="loop_turns_slider" min="2" max="100" value="4.0" step="1.0">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="frequency_slider">f:</label>
|
||||
<input type="range" id="frequency_slider" min="1.0" max="30.0" value="7.0" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
<div id="notes" class="notes">
|
||||
<br>
|
||||
<b><u>Notes:</u></b><br>
|
||||
RF Inductor Calculator was developed to help users predict the RF characteristics of a single-layer solenoid-style air-core inductor. <br><br>
|
||||
<u>Inputs via the slider widgets:</u>
|
||||
<ul>
|
||||
<li>⌀a : Conductor diameter in millimeters (mm). Estimated equivalent AWG wire size is also displayed where appropriate.</li>
|
||||
<li>⌀b : Loop diameter in millimeters (mm).</li>
|
||||
<li>c/a : 'c' is the inter-winding spacing, and 'a' is the conductor diameter, so 'c/a' is the spacing ratio. (Must be >= 1.1)
|
||||
A low-value will increase the resistance due to the proximity effect.</li>
|
||||
<li>N : Number of turns or windings.</li>
|
||||
<li>f : The frequency of interest (MHz) for some of the calculations.</li>
|
||||
</ul>
|
||||
<p>Characteristics on the left are independent of frequency, while the characteristics on the right are dependent on the selected frequency. <br><br>
|
||||
Each of the graphic representations attempt to keep the relative geometry correct, without exceeding the drawing boundary. The coil diameter
|
||||
relative to the conductor diameter are representative. </p>
|
||||
<u>Calculated dimensions:</u>
|
||||
<ul>
|
||||
<li>⌀o : Outer loop diameter (mm) </li>
|
||||
<li>⌀i : Inner loop diameter (mm) - corresponds to the diameter of the winding former.</li>
|
||||
<li>c : Distance between windings, measured from the conductor centers (mm).</li>
|
||||
<li>ℓ : Length of the coil (mm). Equal to c x N.</li>
|
||||
</ul>
|
||||
<u>Calculated parameters:</u>
|
||||
<ul>
|
||||
<li>L : Inductance is calculated using an equation incorporating the Nagaoka coefficient.</li>
|
||||
<li>C : Capacitance is calculated using Knight's 2016 paper on self-resonance and self-capacitance of solenoid coils.</li>
|
||||
<li>Rdc : DC resistance is calculated using conductor length divided by the conductor cross-sectional area, assuming a copper conductor.</li>
|
||||
<li>SRF : Self-resonant frequency (MHz) for the unloaded coil. Currently using a lumped reactances model. (Looking into modifying the model to
|
||||
use the conductor length and velocity factor as described by Knight (2016).</li>
|
||||
<li>Xl : Inductive reactance at the given frequency. (Ω)</li>
|
||||
<li>δ : Skin depth due to skin effect (μm)</li>
|
||||
<li>Rac : AC resistance is calculated using the skin effect and proximity resistance from empirical data collected by Medhurst using the spacing ratio, and length-to-diameter ratio.</li>
|
||||
<li>Q : Quality factor of device, based on reactance (X) ÷ resistance (Rac) at the given frequency.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-crosshair@1.1.2"></script>
|
||||
<script src="inductor.js"></script>
|
||||
<script>
|
||||
// Define global storage for calculated values, so we don't recalculate the same things multiple times:
|
||||
var inductor = {
|
||||
L : 0.0,
|
||||
C : 0.0,
|
||||
Rdc : 0.0,
|
||||
SRF : 0.0,
|
||||
|
||||
X : 0.0,
|
||||
skin_depth : 0.0,
|
||||
Rac : 0.0,
|
||||
Q : 0.0
|
||||
};
|
||||
|
||||
// Solve all the parameters, and re-draw the canvas:
|
||||
function recalculate() {
|
||||
// Input variables:
|
||||
const loop_diameter_meters = 0.001 * loop_diameter_slider.value * 25.4; // Inches to mm then to m
|
||||
const cond_diameter_meters = 0.001 * conductor_diameter_slider.value * 25.4; // Inches to mm then to m
|
||||
const spacing_ratio = 1.0 * loop_spacing_slider.value;
|
||||
const loop_turns = 1.0 * loop_turns_slider.value;
|
||||
const frequency_hz = 1e6 * frequency_slider.value;
|
||||
// Frequency independent characteristics:
|
||||
inductor.L = getInductance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.C = multiloopCapacitance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.Rdc = dcResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.SRF = selfResonantFrequency(inductor.L, inductor.C);
|
||||
// Frequency dependent characteristics:
|
||||
inductor.X = inductiveReactance(frequency_hz, inductor.L);
|
||||
inductor.skin_depth = skinDepth(frequency_hz);
|
||||
inductor.Rac = acResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns, frequency_hz);
|
||||
inductor.Q = qualityFactor(inductor.X, inductor.Rac);
|
||||
// Redraw the canvas:
|
||||
drawDesign();
|
||||
}
|
||||
|
||||
loop_diameter_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
conductor_diameter_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
loop_turns_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
loop_spacing_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
frequency_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onresize = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onorientationchange = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onbeforeprint = function() {
|
||||
console.log("onbeforeprint");
|
||||
drawDesign();
|
||||
}
|
||||
|
||||
const afront_canvas = document.getElementById("inductor2D");
|
||||
const fctx = afront_canvas.getContext('2d');
|
||||
|
||||
function drawDesign() {
|
||||
const win_width = document.getElementById("inductor-container").clientWidth;
|
||||
const win_height = document.getElementById("inductor-container").clientHeight;
|
||||
afront_canvas.width = win_width-12;
|
||||
afront_canvas.height = win_height-12;
|
||||
|
||||
fctx.clearRect(0, 0, win_width, win_height);
|
||||
const loop_radius = 0.11 * win_height; // 100; // loop_diameter_slider.value * 80;
|
||||
var cond_radius = loop_radius * conductor_diameter_slider.value / loop_diameter_slider.value;
|
||||
const loopx = win_width/2;
|
||||
const loopy = win_height/4;
|
||||
|
||||
// Draw loop ends first, then draw the loop after:
|
||||
fctx.strokeStyle = "grey";
|
||||
fctx.beginPath();
|
||||
fctx.arc(loopx - 0.5*loop_radius, loopy + 1.414*loop_radius, 0.5 * loop_radius, 0.0, -0.40 * Math.PI, true);
|
||||
fctx.lineWidth = cond_radius * 2.0;
|
||||
fctx.stroke();
|
||||
fctx.strokeStyle = "black";
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.arc(loopx + 0.5*loop_radius, loopy + 1.414*loop_radius, 0.5 * loop_radius, Math.PI, -0.60 * Math.PI, false);
|
||||
fctx.lineWidth = cond_radius * 2.0;
|
||||
fctx.stroke();
|
||||
|
||||
// Draw loop:
|
||||
fctx.beginPath();
|
||||
fctx.arc(loopx, loopy, loop_radius, 0.0, 2.0 * Math.PI, false);
|
||||
fctx.stroke();
|
||||
fctx.lineWidth = 1.0;
|
||||
|
||||
// Draw loop diameter arrow:
|
||||
const y_offset = loopy + loop_radius + 20;
|
||||
var arrow_size = 10.0;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx - loop_radius, loopy);
|
||||
fctx.lineTo(loopx - loop_radius, y_offset);
|
||||
fctx.lineTo(loopx - loop_radius - arrow_size, y_offset - arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius - arrow_size, y_offset + arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius, y_offset);
|
||||
fctx.lineTo(loopx - loop_radius - 3.0*arrow_size, y_offset);
|
||||
fctx.stroke();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius, y_offset);
|
||||
fctx.lineTo(loopx + loop_radius + arrow_size, y_offset - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + arrow_size, y_offset + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius, y_offset);
|
||||
fctx.lineTo(loopx + loop_radius + 3.0*arrow_size, y_offset);
|
||||
fctx.stroke();
|
||||
|
||||
// Write loop diameter symbol:
|
||||
fctx.font = "12px arial";
|
||||
fctx.textAlign = "right";
|
||||
const loop_dia = 1.0 * loop_diameter_slider.value; // Convert from mm to inches
|
||||
fctx.fillText("\u2300b = " + loop_dia.toPrecision(3).toString() + "\"", loopx - loop_radius - 2.0*arrow_size, y_offset - 2);
|
||||
|
||||
// Draw inner-diameter arrows: (for using a winding former)
|
||||
const inner_dia_y = loopy + loop_radius + 40;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx - loop_radius + cond_radius, loopy);
|
||||
fctx.lineTo(loopx - loop_radius + cond_radius, inner_dia_y);
|
||||
fctx.lineTo(loopx - loop_radius + cond_radius - arrow_size, inner_dia_y - arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius + cond_radius - arrow_size, inner_dia_y + arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius + cond_radius, inner_dia_y);
|
||||
fctx.lineTo(loopx - loop_radius - 3.0*arrow_size, inner_dia_y);
|
||||
fctx.stroke();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius - cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius, inner_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius + arrow_size, inner_dia_y - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius + arrow_size, inner_dia_y + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius, inner_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius + 3.0*arrow_size, inner_dia_y);
|
||||
fctx.stroke();
|
||||
fctx.textAlign = "left";
|
||||
fctx.fillText("\u2300i = " + (loop_dia-0.5*conductor_diameter_slider.value).toPrecision(3).toString() + "\"", loopx + loop_radius + 2.0*arrow_size, inner_dia_y - 2);
|
||||
|
||||
// Draw outer-diameter arrows: (for using a winding former)
|
||||
const outer_dia_y = loopy + loop_radius + 0;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx - loop_radius - cond_radius, loopy);
|
||||
fctx.lineTo(loopx - loop_radius - cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx - loop_radius - cond_radius - arrow_size, outer_dia_y - arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius - cond_radius - arrow_size, outer_dia_y + arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius - cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx - loop_radius - 3.0*arrow_size, outer_dia_y);
|
||||
fctx.stroke();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius + cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + arrow_size, outer_dia_y - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + arrow_size, outer_dia_y + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius + 3.0*arrow_size, outer_dia_y);
|
||||
fctx.stroke();
|
||||
fctx.fillText("\u2300o = " + (loop_dia+0.5*conductor_diameter_slider.value).toPrecision(3).toString() + "\"", loopx + loop_radius + 2.0*arrow_size, outer_dia_y - 2);
|
||||
|
||||
// Write loop inductance:
|
||||
fctx.font = "12px arial";
|
||||
fctx.textAlign = "left";
|
||||
const L = inductor.L * 1.0e+6;
|
||||
fctx.fillText("L = " + L.toPrecision(3).toString() + " \u03bcH", 8, 18);
|
||||
fctx.fillText("C = " + (inductor.C * 1e12).toFixed(1) + " pF", 8, 32);
|
||||
fctx.fillText("Rdc = " + inductor.Rdc.toFixed(2) + " \u03A9", 8, 46);
|
||||
fctx.fillText("SRF = " + (inductor.SRF * 1e-6).toFixed(1) + " MHz", 8, 60);
|
||||
|
||||
// Draw conductor diameter arrow:
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius - cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius - arrow_size, loopy - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius - arrow_size, loopy + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius, loopy);
|
||||
fctx.lineTo(loopx - 0.6*loop_radius, loopy);
|
||||
fctx.stroke();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius + cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + arrow_size, loopy - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + arrow_size, loopy + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + 2.0*arrow_size, loopy);
|
||||
fctx.stroke();
|
||||
//fctx.textAlign = "right";
|
||||
const cond_dia = 1.0 * conductor_diameter_slider.value;
|
||||
fctx.textAlign = "center";
|
||||
fctx.fillText("\u2300a = " + cond_dia.toPrecision(2).toString() + "\"", loopx, loopy - 6);
|
||||
var awg = "";
|
||||
switch(cond_dia) {
|
||||
case 0.004 :
|
||||
awg = "38 AWG";
|
||||
break;
|
||||
case 0.005 :
|
||||
awg = "36 AWG";
|
||||
break;
|
||||
case 0.008 :
|
||||
awg = "32 AWG";
|
||||
break;
|
||||
case 0.010 :
|
||||
awg = "30 AWG";
|
||||
break;
|
||||
case 0.016 :
|
||||
awg = "26 AWG";
|
||||
break;
|
||||
case 0.018 :
|
||||
awg = "25 AWG";
|
||||
break;
|
||||
case 0.020 :
|
||||
awg = "24 AWG";
|
||||
break;
|
||||
case 0.032 :
|
||||
awg = "20 AWG";
|
||||
break;
|
||||
case 0.040 :
|
||||
awg = "~18 AWG";
|
||||
break;
|
||||
case 0.051 :
|
||||
awg = "~16 AWG";
|
||||
break;
|
||||
case 0.081 :
|
||||
awg = "~12 AWG";
|
||||
break;
|
||||
case 0.102 :
|
||||
awg = "~10 AWG";
|
||||
break;
|
||||
case 0.128 :
|
||||
awg = "~8 AWG";
|
||||
break;
|
||||
case 0.114 :
|
||||
awg = "~9 AWG";
|
||||
break;
|
||||
case 0.162 :
|
||||
awg = "6 AWG";
|
||||
break;
|
||||
}
|
||||
fctx.textAlign = "left";
|
||||
fctx.fillText(awg, loopx + loop_radius + cond_radius + 2.0*arrow_size, loopy - 6);
|
||||
|
||||
|
||||
|
||||
var cond_spacing = 2.0 * cond_radius * loop_spacing_slider.value;
|
||||
if((cond_spacing * loop_turns_slider.value) > (0.8 * win_width)) {
|
||||
cond_radius = ((0.8 * win_width) / (loop_turns_slider.value * 2.0*loop_spacing_slider.value));
|
||||
cond_spacing = 2.0 * cond_radius * loop_spacing_slider.value;
|
||||
}
|
||||
var start_x = win_width/2.0 - loop_turns_slider.value * cond_spacing * 0.5;
|
||||
var top_y = win_height * 0.56;
|
||||
var bot_y = top_y + 2.0 * cond_radius * (loop_diameter_slider.value / conductor_diameter_slider.value);
|
||||
|
||||
for (let i = 0; i < loop_turns_slider.value; i++) {
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x + ((0.5 + i) * cond_spacing) + cond_radius, top_y);
|
||||
fctx.lineTo(start_x + (i+1) * cond_spacing + cond_radius, bot_y);
|
||||
fctx.arc(start_x + (i+1) * cond_spacing, bot_y, cond_radius, 0, Math.PI, false);
|
||||
fctx.lineTo(start_x + ((0.5 + i) * cond_spacing) - cond_radius, top_y);
|
||||
fctx.fillStyle = "grey";
|
||||
fctx.fill();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.arc(start_x + (i * cond_spacing), bot_y, cond_radius, 0, Math.PI);
|
||||
fctx.arc(start_x + (cond_spacing * 0.5) + i * cond_spacing, top_y, cond_radius, Math.PI, 0);
|
||||
fctx.lineTo(start_x + (i * cond_spacing) + cond_radius, bot_y);
|
||||
fctx.closePath();
|
||||
fctx.fillStyle = "black";
|
||||
fctx.fill();
|
||||
}
|
||||
|
||||
// Draw the wire ends:
|
||||
fctx.fillRect(start_x - cond_radius, bot_y, 2.0 * cond_radius, 30);
|
||||
fctx.fillStyle = "grey";
|
||||
fctx.fillRect(start_x + loop_turns_slider.value*cond_spacing - cond_radius, bot_y, 2.0 * cond_radius, 30);
|
||||
fctx.fillStyle = "black";
|
||||
|
||||
// Draw left spacing arrow:
|
||||
const dim_y = win_height * 0.90;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x - 20, dim_y);
|
||||
fctx.lineTo(start_x, dim_y);
|
||||
fctx.lineTo(start_x - 7, dim_y + 7)
|
||||
fctx.lineTo(start_x - 7, dim_y - 7)
|
||||
fctx.lineTo(start_x, dim_y);
|
||||
fctx.moveTo(start_x, dim_y - 7);
|
||||
fctx.lineTo(start_x, dim_y + 7);
|
||||
fctx.stroke();
|
||||
|
||||
// Draw right spacing arrow:
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x + cond_spacing + 20, dim_y);
|
||||
fctx.lineTo(start_x + cond_spacing, dim_y);
|
||||
fctx.lineTo(start_x + cond_spacing + 7, dim_y + 7)
|
||||
fctx.lineTo(start_x + cond_spacing + 7, dim_y - 7)
|
||||
fctx.lineTo(start_x + cond_spacing, dim_y);
|
||||
fctx.moveTo(start_x + cond_spacing, dim_y - 7);
|
||||
fctx.lineTo(start_x + cond_spacing, dim_y + 7);
|
||||
fctx.stroke();
|
||||
|
||||
// Draw right length arrow:
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x + loop_turns_slider.value*cond_spacing + 20, dim_y);
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing, dim_y);
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing + 7, dim_y + 7)
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing + 7, dim_y - 7)
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing, dim_y);
|
||||
fctx.moveTo(start_x + loop_turns_slider.value*cond_spacing, dim_y - 7);
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing, dim_y + 7);
|
||||
fctx.stroke();
|
||||
|
||||
// Extended lines:
|
||||
fctx.strokeStyle = "grey";
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x, bot_y + 35);
|
||||
fctx.lineTo(start_x, dim_y - 12);
|
||||
fctx.moveTo(start_x + cond_spacing, bot_y + 15);
|
||||
fctx.lineTo(start_x + cond_spacing, dim_y - 12);
|
||||
fctx.moveTo(start_x + loop_turns_slider.value*cond_spacing, bot_y + 35);
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing, dim_y - 12);
|
||||
fctx.stroke();
|
||||
fctx.strokeStyle = "black";
|
||||
|
||||
fctx.font = "12px arial";
|
||||
fctx.textAlign = "right";
|
||||
var freq = 1.0 * frequency_slider.value;
|
||||
fctx.fillText("f = " + freq.toFixed(1) + " MHz", win_width-18, 18);
|
||||
fctx.fillText("Xl = " + inductor.X.toFixed(1) + " \u03A9", win_width-18, 32);
|
||||
fctx.fillText("\u03B4 = " + (inductor.skin_depth * 1e6).toFixed(1) + " \u03BCm", win_width-18, 46);
|
||||
fctx.fillText("Rac = " + inductor.Rac.toFixed(2) + " \u03A9", win_width-18, 60);
|
||||
fctx.fillText("Q = " + inductor.Q.toFixed(1), win_width-18, 74);
|
||||
|
||||
fctx.textAlign = "center";
|
||||
fctx.fillText("N = " + loop_turns_slider.value.toString(), win_width/2, win_height * 0.52);
|
||||
|
||||
// Draw spacing text: (gap is to avoid collision of spacing and length texts)
|
||||
fctx.textAlign = "right";
|
||||
var gap = ((loop_turns_slider.value*cond_spacing - cond_spacing) < 60) ? (60 - (loop_turns_slider.value*cond_spacing - cond_spacing)) : 0;
|
||||
const spc = loop_spacing_slider.value * conductor_diameter_slider.value;
|
||||
fctx.fillText("c = " + spc.toFixed(3).toString() + "\"", start_x + cond_spacing + 20 - gap, dim_y + 20);
|
||||
|
||||
// Draw length text:
|
||||
const sol_len = loop_turns_slider.value*spc;
|
||||
fctx.fillText("\u2113 = " + sol_len.toFixed(3).toString() + "\"", start_x + loop_turns_slider.value*cond_spacing + 20, dim_y + 20);
|
||||
}
|
||||
recalculate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,476 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>VK3CPU RF Inductor Calculator</title>
|
||||
<link rel="stylesheet" href="inductor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header><a href="mailto:vk3cpu@gmail.com">VK3CPU</a> - RF Inductor Calculator v1.0<br><a href="inductor_imp.html">[Imperial]</a> <a href="inductor.html">[Small Metric]</a></header>
|
||||
<section class="gridLayoutClass">
|
||||
<div id="inductor-container" class="inductor-container" style="position: relative;">
|
||||
<canvas id="inductor2D" class="inductorClass" width="350" height="350">
|
||||
</canvas>
|
||||
</div>
|
||||
<div class="slider_container">
|
||||
<div class="sliders">
|
||||
<label for="conductor_diameter_slider">⌀a:</label>
|
||||
<input type="range" id="conductor_diameter_slider" min="2.5" max="10.0" value="4.95" step="0.05">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="loop_diameter_slider">⌀b:</label>
|
||||
<input type="range" id="loop_diameter_slider" min="20.0" max="200.0" value="50.0" step="1.0">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="loop_spacing_slider">c/a:</label>
|
||||
<input type="range" id="loop_spacing_slider" min="1.1" max="4.0" value="1.1" step="0.01">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="loop_turns_slider">N:</label>
|
||||
<input type="range" id="loop_turns_slider" min="2" max="100" value="10.0" step="1.0">
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label for="frequency_slider">f:</label>
|
||||
<input type="range" id="frequency_slider" min="1.0" max="30.0" value="7.0" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
<div id="notes" class="notes">
|
||||
<br>
|
||||
<b><u>Notes:</u></b><br>
|
||||
RF Inductor Calculator was developed to help users predict the RF characteristics of a single-layer solenoid-style air-core inductor. <br><br>
|
||||
<u>Inputs via the slider widgets:</u>
|
||||
<ul>
|
||||
<li>⌀a : Conductor diameter in millimeters (mm). Estimated equivalent AWG wire size is also displayed where appropriate.</li>
|
||||
<li>⌀b : Loop diameter in millimeters (mm).</li>
|
||||
<li>c/a : 'c' is the inter-winding spacing, and 'a' is the conductor diameter, so 'c/a' is the spacing ratio. (Must be >= 1.1)
|
||||
A low-value will increase the resistance due to the proximity effect.</li>
|
||||
<li>N : Number of turns or windings.</li>
|
||||
<li>f : The frequency of interest (MHz) for some of the calculations.</li>
|
||||
</ul>
|
||||
<p>Characteristics on the left are independent of frequency, while the characteristics on the right are dependent on the selected frequency. <br><br>
|
||||
Each of the graphic representations attempt to keep the relative geometry correct, without exceeding the drawing boundary. The coil diameter
|
||||
relative to the conductor diameter are representative. </p>
|
||||
<u>Calculated dimensions:</u>
|
||||
<ul>
|
||||
<li>⌀o : Outer loop diameter (mm) </li>
|
||||
<li>⌀i : Inner loop diameter (mm) - corresponds to the diameter of the winding former.</li>
|
||||
<li>c : Distance between windings, measured from the conductor centers (mm).</li>
|
||||
<li>ℓ : Length of the coil (mm). Equal to c x N.</li>
|
||||
</ul>
|
||||
<u>Calculated parameters:</u>
|
||||
<ul>
|
||||
<li>L : Inductance is calculated using an equation incorporating the Nagaoka coefficient.</li>
|
||||
<li>C : Capacitance is calculated using Knight's 2016 paper on self-resonance and self-capacitance of solenoid coils.</li>
|
||||
<li>Rdc : DC resistance is calculated using conductor length divided by the conductor cross-sectional area, assuming a copper conductor.</li>
|
||||
<li>SRF : Self-resonant frequency (MHz) for the unloaded coil. Currently using a lumped reactances model. (Looking into modifying the model to
|
||||
use the conductor length and velocity factor as described by Knight (2016).</li>
|
||||
<li>Xl : Inductive reactance at the given frequency. (Ω)</li>
|
||||
<li>δ : Skin depth due to skin effect (μm)</li>
|
||||
<li>Rac : AC resistance is calculated using the skin effect and proximity resistance from empirical data collected by Medhurst using the spacing ratio, and length-to-diameter ratio.</li>
|
||||
<li>Q : Quality factor of device, based on reactance (X) ÷ resistance (Rac) at the given frequency.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-crosshair@1.1.2"></script>
|
||||
<script src="inductor.js"></script>
|
||||
<script>
|
||||
// Define global storage for calculated values, so we don't recalculate the same things multiple times:
|
||||
var inductor = {
|
||||
L : 0.0,
|
||||
C : 0.0,
|
||||
Rdc : 0.0,
|
||||
SRF : 0.0,
|
||||
|
||||
X : 0.0,
|
||||
skin_depth : 0.0,
|
||||
Rac : 0.0,
|
||||
Q : 0.0
|
||||
};
|
||||
|
||||
// Solve all the parameters, and re-draw the canvas:
|
||||
function recalculate() {
|
||||
// Input variables:
|
||||
const loop_diameter_meters = 0.001 * loop_diameter_slider.value;
|
||||
const cond_diameter_meters = 0.001 * conductor_diameter_slider.value;
|
||||
const spacing_ratio = 1.0 * loop_spacing_slider.value;
|
||||
const loop_turns = 1.0 * loop_turns_slider.value;
|
||||
const frequency_hz = 1e6 * frequency_slider.value;
|
||||
// Frequency independent characteristics:
|
||||
inductor.L = getInductance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.C = multiloopCapacitance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.Rdc = dcResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns);
|
||||
inductor.SRF = selfResonantFrequency(inductor.L, inductor.C);
|
||||
// Frequency dependent characteristics:
|
||||
inductor.X = inductiveReactance(frequency_hz, inductor.L);
|
||||
inductor.skin_depth = skinDepth(frequency_hz);
|
||||
inductor.Rac = acResistance(loop_diameter_meters, cond_diameter_meters, spacing_ratio, loop_turns, frequency_hz);
|
||||
inductor.Q = qualityFactor(inductor.X, inductor.Rac);
|
||||
// Redraw the canvas:
|
||||
drawDesign();
|
||||
}
|
||||
|
||||
loop_diameter_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
conductor_diameter_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
loop_turns_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
loop_spacing_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
frequency_slider.oninput = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onresize = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onorientationchange = function() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
window.onbeforeprint = function() {
|
||||
console.log("onbeforeprint");
|
||||
drawDesign();
|
||||
}
|
||||
|
||||
const afront_canvas = document.getElementById("inductor2D");
|
||||
const fctx = afront_canvas.getContext('2d');
|
||||
|
||||
function drawDesign() {
|
||||
const win_width = document.getElementById("inductor-container").clientWidth;
|
||||
const win_height = document.getElementById("inductor-container").clientHeight;
|
||||
afront_canvas.width = win_width-12;
|
||||
afront_canvas.height = win_height-12;
|
||||
|
||||
fctx.clearRect(0, 0, win_width, win_height);
|
||||
const loop_radius = 0.11 * win_height;
|
||||
var cond_radius = loop_radius * conductor_diameter_slider.value / loop_diameter_slider.value;
|
||||
const loopx = win_width/2;
|
||||
const loopy = win_height/4;
|
||||
|
||||
// Draw loop ends first, then draw the loop after:
|
||||
fctx.strokeStyle = "grey";
|
||||
fctx.beginPath();
|
||||
fctx.arc(loopx - 0.5*loop_radius, loopy + 1.414*loop_radius, 0.5 * loop_radius, 0.0, -0.40 * Math.PI, true);
|
||||
fctx.lineWidth = cond_radius * 2.0;
|
||||
fctx.stroke();
|
||||
fctx.strokeStyle = "black";
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.arc(loopx + 0.5*loop_radius, loopy + 1.414*loop_radius, 0.5 * loop_radius, Math.PI, -0.60 * Math.PI, false);
|
||||
fctx.lineWidth = cond_radius * 2.0;
|
||||
fctx.stroke();
|
||||
|
||||
// Draw loop:
|
||||
fctx.beginPath();
|
||||
fctx.arc(loopx, loopy, loop_radius, 0.0, 2.0 * Math.PI, false);
|
||||
fctx.stroke();
|
||||
fctx.lineWidth = 1.0;
|
||||
|
||||
// Draw loop diameter arrow:
|
||||
const y_offset = loopy + loop_radius + 20;
|
||||
var arrow_size = 10.0;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx - loop_radius, loopy);
|
||||
fctx.lineTo(loopx - loop_radius, y_offset);
|
||||
fctx.lineTo(loopx - loop_radius - arrow_size, y_offset - arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius - arrow_size, y_offset + arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius, y_offset);
|
||||
fctx.lineTo(loopx - loop_radius - 3.0*arrow_size, y_offset);
|
||||
fctx.stroke();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius, y_offset);
|
||||
fctx.lineTo(loopx + loop_radius + arrow_size, y_offset - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + arrow_size, y_offset + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius, y_offset);
|
||||
fctx.lineTo(loopx + loop_radius + 3.0*arrow_size, y_offset);
|
||||
fctx.stroke();
|
||||
|
||||
// Write loop diameter symbol:
|
||||
fctx.font = "12px arial";
|
||||
fctx.textAlign = "right";
|
||||
const loop_dia = 1.0 * loop_diameter_slider.value; // Convert from mm to inches
|
||||
fctx.fillText("\u2300b = " + loop_dia.toPrecision(3).toString() + "mm", loopx - loop_radius - 2.0*arrow_size, y_offset - 2);
|
||||
|
||||
// Draw inner-diameter arrows: (for using a winding former)
|
||||
const inner_dia_y = loopy + loop_radius + 40;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx - loop_radius + cond_radius, loopy);
|
||||
fctx.lineTo(loopx - loop_radius + cond_radius, inner_dia_y);
|
||||
fctx.lineTo(loopx - loop_radius + cond_radius - arrow_size, inner_dia_y - arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius + cond_radius - arrow_size, inner_dia_y + arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius + cond_radius, inner_dia_y);
|
||||
fctx.lineTo(loopx - loop_radius - 3.0*arrow_size, inner_dia_y);
|
||||
fctx.stroke();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius - cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius, inner_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius + arrow_size, inner_dia_y - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius + arrow_size, inner_dia_y + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius, inner_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius + 3.0*arrow_size, inner_dia_y);
|
||||
fctx.stroke();
|
||||
fctx.textAlign = "left";
|
||||
fctx.fillText("\u2300i = " + (loop_dia-0.5*conductor_diameter_slider.value).toPrecision(3).toString() + "mm", loopx + loop_radius + 2.0*arrow_size, inner_dia_y - 2);
|
||||
|
||||
// Draw outer-diameter arrows: (for using a winding former)
|
||||
const outer_dia_y = loopy + loop_radius + 0;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx - loop_radius - cond_radius, loopy);
|
||||
fctx.lineTo(loopx - loop_radius - cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx - loop_radius - cond_radius - arrow_size, outer_dia_y - arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius - cond_radius - arrow_size, outer_dia_y + arrow_size);
|
||||
fctx.lineTo(loopx - loop_radius - cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx - loop_radius - 3.0*arrow_size, outer_dia_y);
|
||||
fctx.stroke();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius + cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + arrow_size, outer_dia_y - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + arrow_size, outer_dia_y + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius, outer_dia_y);
|
||||
fctx.lineTo(loopx + loop_radius + 3.0*arrow_size, outer_dia_y);
|
||||
fctx.stroke();
|
||||
fctx.fillText("\u2300o = " + (loop_dia+0.5*conductor_diameter_slider.value).toPrecision(3).toString() + "mm", loopx + loop_radius + 2.0*arrow_size, outer_dia_y - 2);
|
||||
|
||||
// Write loop inductance:
|
||||
fctx.font = "12px arial";
|
||||
fctx.textAlign = "left";
|
||||
const L = inductor.L * 1.0e+6;
|
||||
fctx.fillText("L = " + L.toPrecision(3).toString() + " \u03bcH", 8, 18);
|
||||
fctx.fillText("C = " + (inductor.C * 1e12).toFixed(1) + " pF", 8, 32);
|
||||
fctx.fillText("Rdc = " + inductor.Rdc.toFixed(2) + " \u03A9", 8, 46);
|
||||
fctx.fillText("SRF = " + (inductor.SRF * 1e-6).toFixed(1) + " MHz", 8, 60);
|
||||
|
||||
// Draw conductor diameter arrow:
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius - cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius - arrow_size, loopy - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius - arrow_size, loopy + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius - cond_radius, loopy);
|
||||
fctx.lineTo(loopx - 0.6*loop_radius, loopy);
|
||||
fctx.stroke();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(loopx + loop_radius + cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + arrow_size, loopy - arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + arrow_size, loopy + arrow_size);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius, loopy);
|
||||
fctx.lineTo(loopx + loop_radius + cond_radius + 2.0*arrow_size, loopy);
|
||||
fctx.stroke();
|
||||
//fctx.textAlign = "right";
|
||||
const cond_dia = 1.0 * conductor_diameter_slider.value;
|
||||
fctx.textAlign = "center";
|
||||
fctx.fillText("\u2300a = " + cond_dia.toPrecision(3).toString() + "mm", loopx, loopy - 6);
|
||||
var awg = "";
|
||||
switch(cond_dia) {
|
||||
case 0.100 :
|
||||
awg = "~38 AWG";
|
||||
break;
|
||||
case 0.150 :
|
||||
awg = "~35 AWG";
|
||||
break;
|
||||
case 0.200 :
|
||||
awg = "~32 AWG";
|
||||
break;
|
||||
case 0.250 :
|
||||
awg = "~30 AWG";
|
||||
break;
|
||||
case 0.300 :
|
||||
awg = "~29 AWG";
|
||||
break;
|
||||
case 0.350 :
|
||||
awg = "~27 AWG";
|
||||
break;
|
||||
case 0.400 :
|
||||
awg = "~26 AWG";
|
||||
break;
|
||||
case 0.450 :
|
||||
awg = "~25 AWG";
|
||||
break;
|
||||
case 0.500 :
|
||||
awg = "~24 AWG";
|
||||
break;
|
||||
case 0.550 :
|
||||
awg = "~23 AWG";
|
||||
break;
|
||||
case 0.650 :
|
||||
awg = "~22 AWG";
|
||||
break;
|
||||
case 0.700 :
|
||||
awg = "~21 AWG";
|
||||
break;
|
||||
case 0.800 :
|
||||
awg = "~20 AWG";
|
||||
break;
|
||||
case 0.900 :
|
||||
awg = "~19 AWG";
|
||||
break;
|
||||
case 1.00 :
|
||||
awg = "~18 AWG";
|
||||
break;
|
||||
case 1.15 :
|
||||
awg = "~17 AWG";
|
||||
break;
|
||||
case 1.30 :
|
||||
awg = "~16 AWG";
|
||||
break;
|
||||
case 1.45 :
|
||||
awg = "~15 AWG";
|
||||
break;
|
||||
case 1.55 :
|
||||
awg = "~14 AWG";
|
||||
break;
|
||||
case 1.80 :
|
||||
awg = "~13 AWG";
|
||||
break;
|
||||
case 2.00 :
|
||||
awg = "~12 AWG";
|
||||
break;
|
||||
case 2.30 :
|
||||
awg = "~11 AWG";
|
||||
break;
|
||||
case 2.60 :
|
||||
awg = "~10 AWG";
|
||||
break;
|
||||
case 2.90 :
|
||||
awg = "~9 AWG";
|
||||
break;
|
||||
case 3.25 :
|
||||
awg = "~8 AWG";
|
||||
break;
|
||||
case 3.65 :
|
||||
awg = "~7 AWG";
|
||||
break;
|
||||
case 4.10 :
|
||||
awg = "~6 AWG";
|
||||
break;
|
||||
case 4.60 :
|
||||
awg = "~5 AWG";
|
||||
break;
|
||||
}
|
||||
fctx.textAlign = "left";
|
||||
fctx.fillText(awg, loopx + loop_radius + cond_radius + 2.0*arrow_size, loopy - 6);
|
||||
|
||||
|
||||
|
||||
var cond_spacing = 2.0 * cond_radius * loop_spacing_slider.value;
|
||||
if((cond_spacing * loop_turns_slider.value) > (0.8 * win_width)) {
|
||||
cond_radius = ((0.8 * win_width) / (loop_turns_slider.value * 2.0*loop_spacing_slider.value));
|
||||
cond_spacing = 2.0 * cond_radius * loop_spacing_slider.value;
|
||||
}
|
||||
var start_x = win_width/2.0 - loop_turns_slider.value * cond_spacing * 0.5;
|
||||
var top_y = win_height * 0.56;
|
||||
var bot_y = top_y + 2.0 * cond_radius * (loop_diameter_slider.value / conductor_diameter_slider.value);
|
||||
|
||||
for (let i = 0; i < loop_turns_slider.value; i++) {
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x + ((0.5 + i) * cond_spacing) + cond_radius, top_y);
|
||||
fctx.lineTo(start_x + (i+1) * cond_spacing + cond_radius, bot_y);
|
||||
fctx.arc(start_x + (i+1) * cond_spacing, bot_y, cond_radius, 0, Math.PI, false);
|
||||
fctx.lineTo(start_x + ((0.5 + i) * cond_spacing) - cond_radius, top_y);
|
||||
fctx.fillStyle = "grey";
|
||||
fctx.fill();
|
||||
|
||||
fctx.beginPath();
|
||||
fctx.arc(start_x + (i * cond_spacing), bot_y, cond_radius, 0, Math.PI);
|
||||
fctx.arc(start_x + (cond_spacing * 0.5) + i * cond_spacing, top_y, cond_radius, Math.PI, 0);
|
||||
fctx.lineTo(start_x + (i * cond_spacing) + cond_radius, bot_y);
|
||||
fctx.closePath();
|
||||
fctx.fillStyle = "black";
|
||||
fctx.fill();
|
||||
}
|
||||
|
||||
// Draw the wire ends:
|
||||
fctx.fillRect(start_x - cond_radius, bot_y, 2.0 * cond_radius, 30);
|
||||
fctx.fillStyle = "grey";
|
||||
fctx.fillRect(start_x + loop_turns_slider.value*cond_spacing - cond_radius, bot_y, 2.0 * cond_radius, 30);
|
||||
fctx.fillStyle = "black";
|
||||
|
||||
// Draw left spacing arrow:
|
||||
const dim_y = win_height * 0.92;
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x - 20, dim_y);
|
||||
fctx.lineTo(start_x, dim_y);
|
||||
fctx.lineTo(start_x - 7, dim_y + 7)
|
||||
fctx.lineTo(start_x - 7, dim_y - 7)
|
||||
fctx.lineTo(start_x, dim_y);
|
||||
fctx.moveTo(start_x, dim_y - 7);
|
||||
fctx.lineTo(start_x, dim_y + 7);
|
||||
fctx.stroke();
|
||||
|
||||
// Draw right spacing arrow:
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x + cond_spacing + 20, dim_y);
|
||||
fctx.lineTo(start_x + cond_spacing, dim_y);
|
||||
fctx.lineTo(start_x + cond_spacing + 7, dim_y + 7)
|
||||
fctx.lineTo(start_x + cond_spacing + 7, dim_y - 7)
|
||||
fctx.lineTo(start_x + cond_spacing, dim_y);
|
||||
fctx.moveTo(start_x + cond_spacing, dim_y - 7);
|
||||
fctx.lineTo(start_x + cond_spacing, dim_y + 7);
|
||||
fctx.stroke();
|
||||
|
||||
// Draw right length arrow:
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x + loop_turns_slider.value*cond_spacing + 20, dim_y);
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing, dim_y);
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing + 7, dim_y + 7)
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing + 7, dim_y - 7)
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing, dim_y);
|
||||
fctx.moveTo(start_x + loop_turns_slider.value*cond_spacing, dim_y - 7);
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing, dim_y + 7);
|
||||
fctx.stroke();
|
||||
|
||||
// Extended lines:
|
||||
fctx.strokeStyle = "grey";
|
||||
fctx.beginPath();
|
||||
fctx.moveTo(start_x, bot_y + 35);
|
||||
fctx.lineTo(start_x, dim_y - 12);
|
||||
fctx.moveTo(start_x + cond_spacing, bot_y + 15);
|
||||
fctx.lineTo(start_x + cond_spacing, dim_y - 12);
|
||||
fctx.moveTo(start_x + loop_turns_slider.value*cond_spacing, bot_y + 35);
|
||||
fctx.lineTo(start_x + loop_turns_slider.value*cond_spacing, dim_y - 12);
|
||||
fctx.stroke();
|
||||
fctx.strokeStyle = "black";
|
||||
|
||||
fctx.font = "12px arial";
|
||||
fctx.textAlign = "right";
|
||||
var freq = 1.0 * frequency_slider.value;
|
||||
fctx.fillText("f = " + freq.toFixed(1) + " MHz", win_width-18, 18);
|
||||
fctx.fillText("Xl = " + inductor.X.toFixed(1) + " \u03A9", win_width-18, 32);
|
||||
fctx.fillText("\u03B4 = " + (inductor.skin_depth * 1e6).toFixed(1) + " \u03BCm", win_width-18, 46);
|
||||
fctx.fillText("Rac = " + inductor.Rac.toFixed(2) + " \u03A9", win_width-18, 60);
|
||||
fctx.fillText("Q = " + inductor.Q.toFixed(1), win_width-18, 74);
|
||||
|
||||
fctx.textAlign = "center";
|
||||
fctx.fillText("N = " + loop_turns_slider.value.toString(), win_width/2, win_height * 0.52);
|
||||
|
||||
// Draw spacing text: (gap is to avoid collision of spacing and length texts)
|
||||
fctx.textAlign = "right";
|
||||
var gap = ((loop_turns_slider.value*cond_spacing - cond_spacing) < 60) ? (60 - (loop_turns_slider.value*cond_spacing - cond_spacing)) : 0;
|
||||
const spc = loop_spacing_slider.value * conductor_diameter_slider.value;
|
||||
fctx.fillText("c = " + spc.toFixed(1).toString() + "mm", start_x + cond_spacing + 20 - gap, dim_y + 20);
|
||||
|
||||
// Draw length text:
|
||||
const sol_len = loop_turns_slider.value*spc;
|
||||
fctx.fillText("\u2113 = " + sol_len.toFixed(1).toString() + "mm", start_x + loop_turns_slider.value*cond_spacing + 20, dim_y + 20);
|
||||
}
|
||||
recalculate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Ładowanie…
Reference in New Issue