vk3cpu/magloop.html

1893 wiersze
96 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VK3CPU Magnetic Loop Antenna Calculator</title>
<link rel="stylesheet" href="magloop.css">
</head>
<body>
<header>Miguel <a href="mailto:vk3cpu@gmail.com">VK3CPU</a> - Magloop Antenna Calculator V10.4</header>
<section class="gridLayoutClass">
<div class="chart-container" style="position: relative;">
<canvas id="chartCanvas" class="chartCanvasClass">
2D Chart Canvas
</canvas>
</div>
<div class="slider_container">
<div class="sliders">
<label for="conductor_diameter_slider">&#8960a:</label>
<input type="range" id="conductor_diameter_slider" min="5" max="80" value="19" step="0.2">
</div>
<div class="sliders">
<label for="loop_diameter_slider">&#8960b:</label>
<input type="range" id="loop_diameter_slider" min="0.04" max="5.0" value="1.0" step="0.01">
</div>
<div class="sliders">
<label for="loop_turns_slider">N:</label>
<input type="range" id="loop_turns_slider" min="0" max="8" value="1" step="1">
</div>
<div class="sliders">
<label for="loop_spacing_slider">c/a:</label>
<input type="range" id="loop_spacing_slider" min="1.1" max="30.0" value="2.0" step="0.01">
</div>
<div class="sliders">
<label for="transmit_power_slider">Tx:</label>
<input type="range" id="transmit_power_slider" min="5" max="1500" value="100" step="5">
</div>
<div class="sliders">
<label for="external_losses_slider">Re:</label>
<input type="range" id="external_losses_slider" min="0" max="1000" value="0" step="1">
</div>
<div class="radios">
<label>Met</label>
<input type="radio" name="unit_radio" id="metric_radio" value="metric" checked/>
<label>Imp</label>
<input type="radio" name="unit_radio" id="imperial_radio" value="imperial"/>
<label>Cu</label>
<input type="radio" name="metal_radio" id="copper_radio" value="Cu" checked/>
<label>Al</label>
<input type="radio" name="metal_radio" id="aluminium_radio" value="Al"/>
<label>Circ</label>
<input type="radio" name="shape_radio" id="circle_radio" value="circle" checked/>
<label>Oct</label>
<input type="radio" name="shape_radio" id="oct_radio" value="octagon"/>
<label>Hex</label>
<input type="radio" name="shape_radio" id="hex_radio" value="hexagon"/>
<label>Sqr</label>
<input type="radio" name="shape_radio" id="square_radio" value="square"/>
</div>
</div>
<div id="antenna-front-container" class="antennaFront-container" style="position: relative;">
<canvas id="antennaFront2D" class="antennaFrontClass" width="150" height="150">
</canvas>
</div>
<div id="antenna-side-container" class="antennaSide-container" style="position: relative;">
<canvas id="antennaSide2D" class="antennaSideClass" width="150" height="150">
</canvas>
</div>
<div class="notes">
<p style="text-align:center"><b><u><a href="./magloop_equations.html"> EQUATIONS USED </a></u></b></p>
<b><u>Notes:</u></b><br>
The Magloop Antenna Calculator was developed to predict the characteristics of a small-transmitting-loop (aka "STL", "magnetic loop" or "magloop")
antenna, given physical dimensions entered via slider widgets. <br>
It supports:
<ul>
<li>circular, octagonal, hexagonal and square-shaped loops</li>
<li>main loops made from either hollow round anodised-copper or aluminium conductors</li>
<li>metric and imperial units</li>
<li>magloops with 1-to-8 turns</li>
</ul>
I developed this multi-turn capable magloop calculator to take advantage of the
touch-screens and high-speed of modern mobile phones, to allow users to get realtime feedback of the predicted
behaviour of a magloop antenna. <br>-- 73 de VK3CPU<br><br>
<u><b>Inputs via the slider and radio widgets:</b></u>
<ul>
<li>&#8960a : Conductor diameter in millimeters (mm) or inches ("). (Measured between opposing conductor outer surfaces.)</li>
<li>&#8960b : Loop diameter in meters (m) or feet ('). (Measured between the conductor centers.)</li>
<li>N : Number of turns or loops. Sweeping left-to-right for parallel multiloop, then single loop, then series multiloop configurations. A "(P)" indicates a multiloop antenna configured with parallel main loops.</li>
<li>c/a : is the spacing ratio; based on 'c' being the inter-winding spacing for multi-turn loops measured between conductor centers, and 'a' is the conductor diameter. (Must be >= 1.1)
A low-value will increase the resistance due to the proximity effect. (Ignore for single-turn loops.)</li>
<li>Tx : The transmit power in Watts. This affects the predicted voltage across the capacitor (Vcap), and the RMS loop current (Ia).</li>
<li>Re : Additional resistance due to external losses, due mainly from capacitor contact resistance and proximity-to-ground effects.
Use Re=0.0 to assume the loop is in free-space with no capacitor losses (i.e. ideal conditions, with loop-related losses only).
Adding Re will reduce antenna efficiency, Q, Vcap and Ia, while increasing antenna BW.
According to [1] and [2], a 1 m diameter loop of 22 mm copper tubing at a height of 1.5 m above the ground operating at 7 MHz had a calculated capacitor contact resistance of ~190 m&#937;
and an additional ground proximity loss resistance of ~30 m&#937;. Note that true ground losses are dependent on both frequency and height-above-ground.</li>
<li>Metric or Imperial : selects the measuring system.</li>
<li>Cu or Al : selects the type of metal conductor (annealed copper or aluminum).</li>
<li>Circ, Oct, Hex or Sqr : selects the shape of the magloop. </li>
</ul>
<u><b>Calculated parameters:</b></u>
<ul>
<li>L : Inductance in microhenries. Equations from [3].</li>
<li>A : Loop area in square meters or square feet.</li>
<li>C : Effective capacitance of the loop in picofarads.</li>
<li>peri : Perimeter of the main loop in meters or feet.</li>
<li>c : Distance between windings, measured from the conductor centers in mm or inches.</li>
<li>cond : Total required conductor length in meters or feet.</li>
<li>Tuning Cap (pF): The capacitance required to bring the loop into resonance at the given frequency. Value in picofarads.</li>
<li>Vcap (kV): The predicted voltage across the capacitance given the desired transmit power.</li>
<li>BW (kHz): The predicted 3dB bandwidth of the magloop antenna. </li>
<li>Efficiency (%): The percentage of input energy that is actually radiated and not lost as heat. This efficiency figure is based on conductor losses of the main loop only.
Missing, are losses due to the capacitor, feedline and the near-field environment. These losses can be added via the Re slider.</li>
<li>R-radiation (&#937): The calculated radiation resistance of the loop in ohms.</li>
<li>R-loop (&#937): The calculated resistance of the loop in ohms, due to the combination of material conductance, conductor length, skin-effect and proximity effects.</li>
<li>Reactance (j&#937): The inductive reactance of the loop in ohms.</li>
<li>Q : The antenna Q (quality) factor.</li>
<li>Ia (A): The RMS loop current in amps.</li>
<li>Perimeter (&#955): Antenna perimeter size relative to the wavelength.</li>
<li>Skin depth (&#956m): Conductor skin depth in micrometers.</li>
</ul>
<u><b>Usage hints:</b></u>
<li>Tap on legend items to disable or enable an output parameter. This can be used to declutter the chart.</li>
<li>Tap on a chart 'dot' to display a tooltip containing calculated output parameters for that frequency or band.</li>
<br>
<u><b>Other VK3CPU calculators:</b>
<ul>
<li><a href="https://miguelvaca.github.io/vk3cpu/toroid.html">RF Toroid Calculator</a></li>
<li><a href="https://miguelvaca.github.io/vk3cpu/inductor_imp.html">RF Inductor Calculator</a></li>
<li><a href="https://miguelvaca.github.io/vk3cpu/short_dipole.html">Coil-loaded Dipole Antenna Calculator</a></li>
</ul>
</u>
<br>
<b><u>References:</u></b><br>
[1]: B. Austin, A. Boswell and M. Perks, <b>"Loss Mechanisms in the Electrically Small Loop Antenna"</b> <i>, IEEE Antennas and Propagation Magazine, 56, 4, August 2014, pp. 143.</i> <br>
[2]: A. Boswell, A. J. Tyler and A. White, <b>"Performance of a Small Loop Antenna in the 3 - 10 MHz Band"</b> <i>, IEEE Antennas and Propagation Magazine, 47, 2, April 2005, pp. 5 1 -56.</i> <br>
[3]: F W Grover, <b>"Formulas and Tables for the Calculation of the Inductance of Coils of Polygonal Form"</b> <i>Scientific Papers of the Bureau of Standards, Vol 18, p753</i> <br>
<br>
<b><u>Change history:</u></b><br>
<b>[21-Dec-23] - V10.4</b> <br>
* Added CB frequencies (11m, 26.965 - 27.855 MHz). <br>
<b>[26-Aug-23] - V10.3</b> <br>
* Added support for URL parameters. Some code-base clean-up. <br>
<b>[13-May-23] - V10.2</b> <br>
* Changed radiation and loss resistance to logarithmic. Capacitance limited to 5-5000pF. <br>
<b>[8-May-23] - V10.1</b> <br>
* Stopped autoscaling on all axes. Changed Vcap, Icap to logarithmic. This makes it easier when comparing different configurations.<br>
<b>[8-May-23] - V10</b> <br>
* Added conductor skin depth as a new calculated parameter.<br>
<b>[23-Apr-23] - V9</b> <br>
* Changed the frequency list to include VHF (2m) and UHF (70cm) bands.<br>
* Reduced minimum loop diameter to 4 cm.<br>
<b>[12-Feb-22] - V8</b> <br>
* Changed the frequency list to include top and bottom frequencies of each band. This is to highlight the range of capacitance required between the top and bottom of each band.<br>
* Increased max c/a ratio from 10 to 30.<br>
<b>[13-Jan-22] - V8</b> <br>
* Added support for parallel conductor magloop antennas.<br>
<b>[21-Nov-21] - V7</b> <br>
* Upgrade Chart.js to the latest version, v3.5.1.<br>
* Tooltips are now justified, (using monospace fonts) and support changing metric prefix.<br>
<b>[27-Sep-21]</b> <br>
* Added band wavelength to tooltip display. Changed 60 m band from 5.0 to 5.3 MHz..<br>
<b>[26-Sep-21]</b> <br>
* Added metal type to schematic display.<br>
* Reduced display precision for Cap and Q in tooltip.<br>
* Changed Tuning Cap scale max from 2000 to 1000 pF.<br>
<b>[23-Sep-21]</b> <br>
* Changed Q equation back to the original Xl/Rtot. Changed max Q to 4000.<br>
* Introduced a new slider "Re" to inject external losses to account for the combined losses due to capacitor contact resistance and ground losses.<br>
* Renamed R-loss to R-loop to avoid confusion, as loop resistance is no longer the only resistance that contributes to losses. The other being Re.<br>
* Changed to V6 to capture the significant changes.<br>
<b>[22-Sep-21]</b> <br>
* Added antenna perimeter size in wavelength to the chart display as a new item.<br>
* Changed maximum spacing ratio c/a from 4.0 to 10.0. Values higher than 4 have no further effect on proximity resistance, but does reduce coil inductance which drives up the SRF.<br>
<b>[21-Sep-21]</b> <br>
* Added distributed capacitance calculation and display for the single turn loop.<br>
<b>[19-Sep-21]</b> <br>
* Increased supported conductor diameter to 80 mm. (3.15 inches)<br>
<b>[18-Sep-21]</b> <br>
* Updated to V5; Added support for octagon, hexagon and square shaped loops. Moved and hyperlinked equations-used to a separate page for clarity.<br>
<b>[16-Sep-21]</b> <br>
* Updated to V4; Updated equation used for Q to match the one use in the ARRL Antenna Book. This will affect predictions for V_cap, I_loop and BW. (Based on Q equation D.1 used in
<i>"Impedance, Bandwidth, and Q of Antennas"</i> by A D Yaghjian, IEEE Transactions on Antennas and Propagation, April 2005.)<br>
* Added equation graphics for V_cap, I_loop and BW formulas.<br>
* Flipped the main-loop graphic to have the capacitor above the coupling loop.<br>
<b>[12-Sep-21]</b> <br>
* Set maximum values to Q, Vcap and I axes to stop autoscaling. Max Q set to 2000, Vcap to 20 kV and I to 100 A.<br>
* Added formula/equation graphics in Notes section. A few more complex ones, such as effective capacitance and SRF, are still needed.<br>
* Fixed minor error in calculation of resistive loss due to proximity effect.<br>
<b>[11-Sep-21]</b> <br>
* Added visual cues for all slider-controlled parameters to highlight which parameter is being modified in the graphic representation.<br>
<b>[10-Sep-21]</b> <br>
* Added c/a display to graphic representation. Moved N from center to left.<br>
<b>[30-Aug-21]</b> <br>
* Added SRF calculation and display for multi-loop antennas.<br>
<b>[28-Aug-21]</b> <br>
* Added support for imperial units and for aluminum metal.<br>
<b>[27-Jul-21]</b> <br>
* Added total conductor length display.<br>
<b>[24-Jul-21]</b> <br>
* Added loop circumference display.<br>
<br>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
<script>
// GUI control widgets:
var loop_diameter_slider = document.getElementById("loop_diameter_slider");
var conductor_diameter_slider = document.getElementById("conductor_diameter_slider");
var loop_turns_slider = document.getElementById("loop_turns_slider");
var loop_spacing_slider = document.getElementById("loop_spacing_slider");
var transmit_power_slider = document.getElementById("transmit_power_slider");
var metric_radio = document.getElementById("metric_radio");
var imperial_radio = document.getElementById("imperial_radio");
var shape_radio = document.getElementById("shape_radio");
var external_losses_slider = document.getElementById("external_losses_slider");
const params_to_sliders = {
loop_diameter: loop_diameter_slider,
conductor_diameter: conductor_diameter_slider,
loop_turns: loop_turns_slider,
loop_spacing: loop_spacing_slider,
transmit_power: transmit_power_slider,
external_losses: external_losses_slider,
}
const sliders_to_params = Object.entries(params_to_sliders).map(([k, v]) => [v, k]);
const params_to_radio_names = {
unit: "unit_radio",
metal: "metal_radio",
shape: "shape_radio",
}
const radio_names_to_params = Object.entries(params_to_radio_names).map(([k, v]) => [v, k]);
// If there's a query param in the URL, set each slider's value to the respective params.
for (const [key, value] of new URLSearchParams(window.location.search)) {
var slider = params_to_sliders[key];
if (slider) {
slider.value = value;
}
var radio_name = params_to_radio_names[key];
if (radio_name) {
// Find all radio elements with this name, set each checked status to the current value.
var radios = document.getElementsByName(radio_name);
for (var i = 0; i < radios.length; i++) {
radios[i].checked = (radios[i].value == value);
}
}
}
// Function to call after a successful recalculation
const updateURL = function() {
const usp = new URLSearchParams();
for (const [slider, param] of sliders_to_params) {
if (slider == null) continue;
var value = slider.value;
usp.set(param, value);
}
for (const [radio_name, param] of radio_names_to_params) {
var radio = document.querySelector(`input[type=radio][name=${radio_name}]:checked`);
usp.set(param, radio.value);
}
var new_url = new URL(window.location.href);
new_url.search = usp;
window.history.replaceState(null, null, new_url);
}
function getUnits() {
var unit_radio = document.getElementsByName("unit_radio");
for(var i = 0; i < unit_radio.length; i++) {
if(unit_radio[i].checked == true) {
return unit_radio[i].value;
}
}
return "null";
}
function getMetal() {
var metal_radio = document.getElementsByName("metal_radio");
for(var i = 0; i < metal_radio.length; i++) {
if(metal_radio[i].checked == true) {
return metal_radio[i].value;
}
}
return "null";
}
function getShape() {
var shape_radio = document.getElementsByName("shape_radio");
for(var i = 0; i < shape_radio.length; i++) {
if(shape_radio[i].checked == true) {
return shape_radio[i].value;
}
}
return "null";
}
// Global variables:
var units = getUnits();
var metal = getMetal(); // Default metal is copper
var conductivity = 58e6; // Default is annealed copper
var shape = getShape();
var loop_turns = 1;
var loop_mode = "series"; // Series or parallel. When loop_turns_slider.value == 0, loop_turns is 2 and loop_mode is "parallel"
const mu0 = Math.PI * 4e-7;
var inductance = 0.0;
var area = 0.0; // Loop area in square meters.
var perimeter = 0.0; // Perimeter of a single turn of the main loop
var loop_capacitance = 0.0; // Effective capacitance of a single or multi-turn loop
var srf = 0.0; // Self-resonant frequency SRF
var conductor_length = 0.0; // Total conductor length
var R_ext = 0.0; // External losses due to capacitor resistance and ground effects, in ohms
const proximityResistance = {
// From G. S. Smith, "Radiation Efficiency of Electrically Small Multiturn Loop Antennas", IEEE Trans Antennas Propagation, September 1972
// 0 - this is the corresponding x-axis value. 1 - single loop adds zero to proximity resistance. Others measured empirically.
0:[ 1.1, 1.15, 1.20, 1.25, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.20, 2.40, 2.50, 2.60, 2.80, 3.00, 3.50, 4.00],
1:[0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],
2:[0.299, 0.284, 0.268, 0.254, 0.240, 0.214, 0.191, 0.173, 0.155, 0.141, 0.128, 0.116, 0.098, 0.032, 0.077, 0.071, 0.061, 0.054, 0.040, 0.031],
3:[0.643, 0.580, 0.531, 0.491, 0.455, 0.395, 0.346, 0.305, 0.270, 0.241, 0.216, 0.195, 0.161, 0.135, 0.124, 0.114, 0.098, 0.085, 0.062, 0.048],
4:[0.996, 0.868, 0.777, 0.704, 0.644, 0.564, 0.470, 0.408, 0.353, 0.316, 0.281, 0.252, 0.205, 0.170, 0.156, 0.144, 0.123, 0.106, 0.077, 0.058],
5:[1.347, 1.142, 1.002, 0.896, 0.809, 0.674, 0.572, 0.492, 0.428, 0.375, 0.332, 0.295, 0.239, 0.197, 0.180, 0.165, 0.141, 0.121, 0.087, 0.066],
6:[1.689, 1.400, 1.210, 1.068, 0.956, 0.784, 0.658, 0.561, 0.485, 0.423, 0.372, 0.330, 0.265, 0.217, 0.198, 0.182, 0.154, 0.133, 0.095, 0.072],
7:[2.020, 1.693, 1.401, 1.224, 1.086, 0.880, 0.732, 0.620, 0.532, 0.462, 0.405, 0.358, 0.286, 0.234, 0.213, 0.195, 0.165, 0.142, 0.101, 0.076],
8:[2.340, 1.872, 1.577, 1.365, 1.203, 0.965, 0.796, 0.670, 0.573, 0.495, 0.433, 0.392, 0.304, 0.247, 0.225, 0.206, 0.174, 0.150, 0.106, 0.080]
};
var frequencies = [];
function updateFrequencies() {
const hamFrequencies = [
0.1357, 0.1378, 0.472, 0.479, 1.8, 1.875, 3.5, 3.8, 5.3, 5.4, 7.0, 7.3, 10.1, 10.15, 14.0, 14.35, 18.068, 18.168, 21.0, 21.45, 24.89, 24.99, 26.965, 27.855, 28.0, 29.7, 35.0, 40.0, 45.0, 50.0, 52.0, 54.0, 55.0, 60.0, 65.0, 69.9, 70.5, 80.0, 90.0,
100.0, 110.0, 120.0, 130.0, 140.0, 144.0, 146.0, 148.0, 150.0, 160.0, 170.0, 180.0, 190.0, 200.0, 210.0, 219.0, 222.0, 225.0, 230.0, 240.0, 250.0, 275.0, 300.0, 325.0, 350.0, 375.0, 400.0, 420.0, 430.0, 440.0, 450.0
];
frequencies = [];
hamFrequencies.forEach(freq => {
const wavelength = 3e8 / (freq * 1e6);
const l = (Math.PI * loop_diameter_slider.value) / wavelength;
if ((l <= 0.30) && ((freq * 1e6) < srf)) {
frequencies.push(freq);
}
});
}
function setGlobals() {
loop_turns = loop_turns_slider.value >= 1 ? parseInt(loop_turns_slider.value) : 2;
loop_mode = loop_turns_slider.value >= 1 ? "series" : "parallel";
units = getUnits();
shape = getShape();
metal = getMetal();
if(metal == "Cu") {
conductivity = 58e6; // Default is annealed copper
} else if(metal == "Al") {
conductivity = 35e6;
}
area = getArea();
perimeter = getPerimeter();
inductance = getInductance();
loop_capacitance = ((loop_turns > 1) && (loop_mode == "series")) ? multiloopCapacitance() : (2.69e-12 * perimeter);
srf = calculateSRF();
conductor_length = (loop_turns_slider.value > 1)
? ((((perimeter* loop_turns) ** 2.0) + ((loop_spacing_slider.value * conductor_diameter_slider.value * 1e-3 * loop_turns) ** 2.0)) ** 0.5)
: (perimeter* loop_turns);
R_ext = external_losses_slider.value * 0.001;
}
// Returns the loop area in square meters:
function getArea() {
var l_area = 0.0;
const width = loop_diameter_slider.value;
if(shape == "circle") {
l_area = 3.141592 * (0.5 * width)**2;
} else
if(shape == "octagon") {
const r = 0.4142135 * width;
l_area = 4.8284 * r**2.0;
} else
if(shape == "hexagon") {
const r = 0.5 * width;
l_area = 3.4641 * r**2.0;
} else
if(shape == "square") {
l_area = width **2.0;
}
return l_area;
}
// Returns the perimeter length in meters:
function getPerimeter() {
var l_perimeter = 0.0;
const width = loop_diameter_slider.value;
if(shape == "circle") {
l_perimeter = 3.141592 * width;
} else
if(shape == "octagon") {
const r = 0.4142135 * width;
l_perimeter = 8 * r;
} else
if(shape == "hexagon") {
const r = 0.5 * width;
const t = 2.0 * r * 0.57735;
l_perimeter = 6 * t;
} else
if(shape == "square") {
l_perimeter = 4 * width;
}
return l_perimeter;
}
// Calculate the inductance of the coil. For single turn loops, use standard inductance equation. For multi-turn, use Nagaoka correction.
function getInductance() {
const loop_diameter_meters = loop_diameter_slider.value;
const cond_diameter_meters = conductor_diameter_slider.value * 1e-3;
const spacing_ratio = loop_spacing_slider.value;
const a_coil_radius = loop_diameter_meters * 0.5;
const coil_length = cond_diameter_meters * spacing_ratio * loop_turns;
const N = loop_turns;
const l = (N>1) ? (coil_length * 100.0) : (cond_diameter_meters * 100.0); // coil length in cm
var retval = 0.0;
if(shape == "circle") {
if(loop_turns > 1) {
retval = (loop_turns**2.0) * mu0 * Math.PI * (a_coil_radius**2.0) * nagaokaCoefficient() / coil_length;
} else {
const b_conductor_radius = cond_diameter_meters * 0.5;
retval = mu0 * a_coil_radius * (Math.log(8.0 * a_coil_radius / b_conductor_radius) - 2.0);
}
} else
if(shape == "octagon") {
const s = (100.0 * loop_diameter_meters) * 0.414213; // side length in cm
const bOn2r = l / (1.306563*s);
//retval = 1e-6 * 0.016 * (N**2) * s * ( Math.log((2.613*s*N)/((N+1)*l)) + 0.75143 + ((0.07153*(N+1)*l) / (s*N))); // ARRL Antennas Book 17th Ed
retval = 1e-6 * 0.016 * (N**2) * s * ( Math.log(1.0/bOn2r) + 0.75143 + 0.18693*bOn2r + 0.11969*bOn2r**2 - 0.08234*bOn2r**4); // F W Grover p753
//retval = 1e-6 * 0.016 * (N**2) * s * ( Math.log(s/l) + 1.711976 + 0.075143*(l/s) + 0.017528*(l/s)**2 - 0.001766*(l/s)**4);
} else
if(shape == "hexagon") {
const s = (100.0 * loop_diameter_meters) * 0.57735; // side length in cm
const bOn2r = l / (loop_diameter_meters * 115.470);
//retval = 1e-6 * 0.012 * (N**2) * s * ( Math.log((2.0*s*N)/((N+1)*l)) + 0.65533 + ((0.1348*(N+1)*l) / (s*N))); // ARRL Antennas Book 17th Ed
retval = 1e-6 * 0.012 * (N**2) * s * ( Math.log(1.0/bOn2r) + 0.65533 + 0.26960*bOn2r + 0.07736*bOn2r**2 - 0.05504*bOn2r**4); // F W Grover p753
} else
if(shape == "square") {
const s = (100.0 * loop_diameter_meters); // side length in cm
const bOn2r = l / (loop_diameter_meters * 141.4214);
//retval = 1e-6 * 0.008 * (N**2) * s * ( Math.log((1.4142*s*N)/((N+1)*l)) + 0.37942 + ((0.3333*(N+1)*l)/(s*N))); // ARRL Antennas Book 17th Ed
retval = 1e-6 * 0.008 * (N**2) * s * ( Math.log(1.0/bOn2r) + 0.37942 + 0.47140*bOn2r - 0.014298*bOn2r**2 - 0.02904*bOn2r**4); // F W Grover p753
}
if(loop_mode == "parallel") {
// then N==2, so divide by 4 to go from serial to parallel inductance:
retval *= 0.25;
}
return retval; // In Henries
}
function radiationResistance(frequency) {
const wavelength = 299792458.0 / frequency;
var retval = 0.0;
if(shape == "circle") {
const k = 20.0 * (Math.PI ** 2.0);
const l = (Math.PI * loop_diameter_slider.value) / wavelength;
retval = (loop_turns ** 2.0) * k * (l ** 4.0);
} else {
retval = (31171.0 * loop_turns**2.0 * area**2.0) / (wavelength**4.0);
}
if(loop_mode == "parallel") {
retval *= 0.25;
}
return retval;
}
function calculateRadiationResistance() {
var retval = [];
frequencies.forEach(freq => {
const rr = radiationResistance(freq * 1e6);
retval.push({x:freq, y:rr});
});
return retval;
}
function inductiveReactance(frequency) {
//const inductance = getInductance();
const reactance = 2.0 * Math.PI * frequency * inductance;
return reactance;
}
function calculateInductiveReactance() {
var retval = [];
frequencies.forEach(freq => {
const reactance = inductiveReactance(freq * 1e6);
retval.push({x:freq, y:reactance});
});
return retval;
}
function nagaokaCoefficient() {
// From Knight's 2016 paper on coil self-resonance, attributed to Wheeler's 1982 eqn as modified by Bob Weaver
const c_spacing = 1e-3 * loop_spacing_slider.value * conductor_diameter_slider.value;
const x = loop_diameter_slider.value / (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;
return zk * (Math.log(1 + 1/zk) + 1/p);
}
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);
}
// Calculates the effective capacitance of a multi-turn loop based on the work of Knight from his 2016 paper:
function multiloopCapacitance() {
const e0 = 8.854187e-12;
const h = 1e-3 * loop_spacing_slider.value * conductor_diameter_slider.value;
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 = loop_turns * h;
const ff = solenoid_length / loop_diameter_slider.value;
// How much longer is the perimeter compared to the circumference if it were circular:
const shape_factor = perimeter / (Math.PI * loop_diameter_slider.value);
var l_multiloop_capacitance = 1e-12 * shape_factor * (ctdw(ff, ei, ex) / Math.sqrt(1 - h**2 / loop_diameter_slider.value**2) + ciae(ff, ei, ex)) * loop_diameter_slider.value;
return l_multiloop_capacitance; // in Farads
}
/*
function singleloopCapacitance() {
var retval = 2.69 * perimeter;
return (retval*1e-12); // in Farads
}
*/
function tuningCapacitance(frequency) {
// frequency is in Hertz
const reactance = inductiveReactance(frequency);
const capacitance = 1e12 * ((1.0 / (2.0 * Math.PI * frequency * reactance)) - loop_capacitance);
return capacitance; // in picofarads
}
function calculateTuningCapacitor() {
var retval = [];
frequencies.forEach(freq => {
const capacitor = tuningCapacitance(freq * 1e6);
retval.push({x:freq, y:capacitor});
});
return retval;
}
function getProximityResFromSpacing(spacing_ratio) {
// Use the proximityResistance look-up table and interpolate values depending on the spacing ratio and the number of turns.
var retval = 0.0;
var i = 0;
for (i = 0; i < (proximityResistance[0].length-1); i++) {
if(spacing_ratio <= proximityResistance[0][i+1]) {
// Linear interpolation between empirical proximity resistance values:
retval = (((spacing_ratio - proximityResistance[0][i]) / (proximityResistance[0][i+1] - proximityResistance[0][i])
* (proximityResistance[loop_turns][i+1] - proximityResistance[loop_turns][i])) + proximityResistance[loop_turns][i]);
break;
}
}
return retval;
}
function lossResistance(frequency) {
// Frequency in Hertz
const a_coil_radius = loop_diameter_slider.value * 0.5;
const b_conductor_radius = conductor_diameter_slider.value * 0.0005;
const loop_spacing_ratio = loop_spacing_slider.value;
// How much longer is the perimeter compared to the circumference if it were circular:
const shape_factor = perimeter / (Math.PI * loop_diameter_slider.value);
const k = (loop_turns * a_coil_radius / b_conductor_radius);
const Rp = getProximityResFromSpacing(loop_spacing_ratio);
const Rs = Math.sqrt(Math.PI * frequency * mu0 / conductivity);
//const R0 = (loop_turns * Rs) / (2.0 * Math.PI * b_conductor_radius);
var R_ohmic = shape_factor * k * Rs * (Rp + 1.0);
if(loop_mode == "parallel") {
R_ohmic *= 0.25;
}
//const R_ohmic = k * Rs * (Rp / R0 + 1.0);
return R_ohmic;
}
function calculateLossResistance() {
var retval = [];
frequencies.forEach(freq => {
const R_ohmic = lossResistance(freq * 1e6);
retval.push({x:freq, y:R_ohmic});
});
return retval;
}
// Returns skin-depth in micrometers:
function calculateSkinDepth() {
var retval = [];
frequencies.forEach(freq => {
const skin_depth = 1e6 * Math.sqrt(1e-6/(conductivity * Math.PI * freq * mu0));
retval.push({x:freq, y:skin_depth});
});
return retval;
}
function calculateEfficiencyFactor() {
var retval = [];
frequencies.forEach(freq => {
const R_ohmic = lossResistance(freq * 1e6);
const R_rad = radiationResistance(freq * 1e6);
const efficiency = 100.0 * R_rad / (R_rad + R_ohmic + R_ext);
retval.push({x:freq, y:efficiency});
});
return retval;
}
function qualityFactor(frequency) {
const Xl = inductiveReactance(frequency);
const Rl = lossResistance(frequency);
const Rr = radiationResistance(frequency);
const Q = Xl / (Rl + Rr + R_ext);
return Q;
}
function calculateQualityFactor() {
var retval = [];
frequencies.forEach(freq => {
const Q = qualityFactor(freq * 1e6);
retval.push({x:freq, y:Q});
});
return retval;
}
function bandwidth(frequency) {
const Q = qualityFactor(frequency);
const bw = frequency * 1e-3 / Q; // in kiloHertz, remember that frequency comes in as Hz. Conversion between Hz and kHz is why the 1e-3 exists.
return bw;
}
function calculateBandwidth() {
var retval = [];
frequencies.forEach(freq => {
const bw = bandwidth(freq * 1e6);
retval.push({x:freq, y:bw});
});
return retval;
}
function capacitorVoltage(frequency) {
const Vcap = Math.sqrt(transmit_power_slider.value * inductiveReactance(frequency) * qualityFactor(frequency));
return Vcap;
}
function calculateCapacitorVoltage() {
var retval = [];
frequencies.forEach(freq => {
const Vcap = 0.001 * capacitorVoltage(freq * 1e6);
retval.push({x:freq, y:Vcap});
});
return retval;
}
function circulatingCurrent(frequency) {
const cc = Math.sqrt(transmit_power_slider.value * qualityFactor(frequency) / inductiveReactance(frequency));
return cc;
}
function calculateCirculatingCurrent() {
var retval = [];
frequencies.forEach(freq => {
const cc = circulatingCurrent(freq * 1e6);
retval.push({x:freq, y:cc});
});
return retval;
}
function calculateAntennaSize() {
var retval = [];
frequencies.forEach(freq => {
const lambda = 3e8 / (freq*1e6);
const size = perimeter / lambda; // size along the perimeter
// const size = conductor_length / lambda;
retval.push({x:freq, y:size});
});
return retval;
}
function calculateSRF() {
// According to Knight (2016), SRF for a single coil is equivalent to the circumference being equivalent to a half-wave dipole.
const inductance = getInductance();
return (1.0 / (2.0 * Math.PI * ((inductance * loop_capacitance) ** 0.5)));
}
function updateUnits() {
setGlobals();
drawFrontDesign();
drawSideDesign();
updateURL();
}
function updateAll() {
setGlobals();
drawFrontDesign();
drawSideDesign();
updateFrequencies();
myChart.data.datasets[0].data = calculateTuningCapacitor();
myChart.data.datasets[1].data = calculateCapacitorVoltage();
myChart.data.datasets[2].data = calculateBandwidth();
myChart.data.datasets[3].data = calculateEfficiencyFactor();
myChart.data.datasets[4].data = calculateRadiationResistance();
myChart.data.datasets[5].data = calculateLossResistance();
myChart.data.datasets[6].data = calculateInductiveReactance();
myChart.data.datasets[7].data = calculateQualityFactor();
myChart.data.datasets[8].data = calculateCirculatingCurrent();
myChart.data.datasets[9].data = calculateAntennaSize();
myChart.data.datasets[10].data = calculateSkinDepth();
myChart.update();
updateURL();
}
metric_radio.oninput = function() {
updateUnits();
}
imperial_radio.oninput = function() {
updateUnits();
}
copper_radio.oninput = function() {
updateAll();
}
aluminium_radio.oninput = function() {
updateAll();
}
circle_radio.oninput = function() {
updateAll();
}
oct_radio.oninput = function() {
updateAll();
}
hex_radio.oninput = function() {
updateAll();
}
square_radio.oninput = function() {
updateAll();
}
// Specify fonts for changing parameters controlled by the sliders:
const normal_font = "12px arial";
const emphasis_font = "bold 14px arial";
const emphasis_delay = 1200;
const normal_width = 1;
const emphasis_width = 3;
var loop_dia_timer_handler = 0;
var loop_dia_font = normal_font;
var loop_dia_thickness = normal_width;
loop_diameter_slider.oninput = function() {
if(loop_dia_timer_handler == 0) {
loop_dia_font = emphasis_font;
loop_dia_thickness = emphasis_width;
loop_dia_timer_handler = setTimeout(function(){
loop_dia_font = normal_font;
drawFrontDesign();
loop_dia_timer_handler = 0;
}, emphasis_delay);
} else {
clearTimeout(loop_dia_timer_handler);
loop_dia_timer_handler = setTimeout(function(){
loop_dia_font = normal_font;
loop_dia_thickness = normal_width;
drawFrontDesign();
loop_dia_timer_handler = 0;
}, emphasis_delay);
}
updateAll();
}
var cond_dia_timer_handler = 0;
var cond_dia_font = normal_font;
var cond_dia_thickness = normal_width;
conductor_diameter_slider.oninput = function() {
if(cond_dia_timer_handler == 0) {
cond_dia_font = emphasis_font;
cond_dia_thickness = emphasis_width;
cond_dia_timer_handler = setTimeout(function(){
cond_dia_font = normal_font;
drawFrontDesign();
cond_dia_timer_handler = 0;
}, emphasis_delay);
} else {
clearTimeout(cond_dia_timer_handler);
cond_dia_timer_handler = setTimeout(function(){
cond_dia_font = normal_font;
cond_dia_thickness = normal_width;
drawFrontDesign();
cond_dia_timer_handler = 0;
}, emphasis_delay);
}
updateAll();
}
var turns_timer_handler = 0;
var turns_font = normal_font;
loop_turns_slider.oninput = function() {
if(turns_timer_handler == 0) {
turns_font = emphasis_font;
turns_timer_handler = setTimeout(function(){
turns_font = normal_font;
drawSideDesign();
turns_timer_handler = 0;
}, emphasis_delay);
} else {
clearTimeout(turns_timer_handler);
turns_timer_handler = setTimeout(function(){
turns_font = normal_font;
drawSideDesign();
turns_timer_handler = 0;
}, emphasis_delay);
}
updateAll();
}
var spacing_timer_handler = 0;
var spacing_font = normal_font;
loop_spacing_slider.oninput = function() {
if(spacing_timer_handler == 0) {
spacing_font = emphasis_font;
spacing_timer_handler = setTimeout(function(){
spacing_font = normal_font;
drawSideDesign();
spacing_timer_handler = 0;
}, emphasis_delay);
} else {
clearTimeout(spacing_timer_handler);
spacing_timer_handler = setTimeout(function(){
spacing_font = normal_font;
drawSideDesign();
spacing_timer_handler = 0;
}, emphasis_delay);
}
updateAll();
}
var tx_timer_handler = 0;
var tx_font = normal_font;
transmit_power_slider.oninput = function() {
if(tx_timer_handler == 0) {
tx_font = emphasis_font;
tx_timer_handler = setTimeout(function(){
tx_font = normal_font;
drawFrontDesign();
tx_timer_handler = 0;
}, emphasis_delay);
} else {
clearTimeout(tx_timer_handler);
tx_timer_handler = setTimeout(function(){
tx_font = normal_font;
drawFrontDesign();
tx_timer_handler = 0;
}, emphasis_delay);
}
setGlobals();
drawFrontDesign();
myChart.data.datasets[1].data = calculateCapacitorVoltage();
myChart.data.datasets[8].data = calculateCirculatingCurrent();
myChart.update();
updateURL();
}
var external_losses_handler = 0;
var external_losses_font = normal_font;
external_losses_slider.oninput = function() {
if(external_losses_handler == 0) {
external_losses_font = emphasis_font;
external_losses_handler = setTimeout(function(){
external_losses_font = normal_font;
drawFrontDesign();
external_losses_handler = 0;
}, emphasis_delay);
} else {
clearTimeout(external_losses_handler);
external_losses_handler = setTimeout(function(){
external_losses_font = normal_font;
drawFrontDesign();
external_losses_handler = 0;
}, emphasis_delay);
}
setGlobals();
drawFrontDesign();
myChart.data.datasets[1].data = calculateCapacitorVoltage();
myChart.data.datasets[2].data = calculateBandwidth();
myChart.data.datasets[3].data = calculateEfficiencyFactor();
myChart.data.datasets[7].data = calculateQualityFactor();
myChart.data.datasets[8].data = calculateCirculatingCurrent();
myChart.update();
updateURL();
}
window.onresize = function() {
myChart.resize();
//myChart.update();
drawFrontDesign();
drawSideDesign();
// console.log("resize!");
}
window.onorientationchange = function() {
//myChart.resize();
//myChart.update();
drawFrontDesign();
drawSideDesign();
}
window.onbeforeprint = function() {
console.log("onbeforeprint");
//myChart.resize();
drawFrontDesign();
drawSideDesign();
}
const afront_canvas = document.getElementById("antennaFront2D");
const fctx = afront_canvas.getContext('2d');
function drawFrontDesign() {
const win_width = document.getElementById("antenna-front-container").offsetWidth;
const win_height = document.getElementById("antenna-front-container").offsetHeight;
afront_canvas.width = win_width-2;
afront_canvas.height = win_height-2;
fctx.clearRect(0, 0, win_width, win_height);
const loop_radius = win_width < win_height ? 0.32 * win_width : 0.32 * win_height;
const cond_radius = conductor_diameter_slider.value / 12;
const loopx = win_width/2;
const loopy = win_height/2;
if(shape == "circle") {
// Draw loop:
fctx.beginPath();
fctx.arc(loopx, loopy, loop_radius + cond_radius, -0.5 * Math.PI + 0.025, -0.5 * Math.PI - 0.025, false);
fctx.arc(loopx, loopy, loop_radius - cond_radius, -0.5 * Math.PI - 0.025, -0.5 * Math.PI + 0.025, true);
fctx.closePath();
fctx.fill();
// Draw variable capacitor:
fctx.lineWidth = 3;
fctx.beginPath();
fctx.moveTo(loopx - 3, loopy - loop_radius - 3*cond_radius);
fctx.lineTo(loopx - 3, loopy - loop_radius + 3*cond_radius);
fctx.moveTo(loopx + 3, loopy - loop_radius - 3*cond_radius);
fctx.lineTo(loopx + 3, loopy - loop_radius + 3*cond_radius);
fctx.moveTo(loopx - 12, loopy - loop_radius + 3*cond_radius);
fctx.lineTo(loopx + 12, loopy - loop_radius - 3*cond_radius);
/*
fctx.moveTo(loopx + 8, loopy - loop_radius - 3*cond_radius);
fctx.lineTo(loopx + 14, loopy - loop_radius - 3*cond_radius);
fctx.lineTo(loopx + 14, loopy - loop_radius - 3*cond_radius + 6);
*/
fctx.stroke();
// Draw coupling loop:
fctx.lineWidth = 2;
fctx.beginPath();
fctx.arc(loopx, loopy + (loop_radius - loop_radius/5) - cond_radius , loop_radius/5, 0, 2*Math.PI, true);
fctx.stroke();
// Draw conductor diameter arrow:
fctx.lineWidth = cond_dia_thickness;
fctx.beginPath();
var p1x = loopx + 0.45 * (loop_radius - cond_radius);
var p1y = loopy + 0.45 * (loop_radius - cond_radius);
var p2x = loopx + 0.707 * (loop_radius - cond_radius);
var p2y = loopy + 0.707 * (loop_radius - cond_radius);
var p3x = loopx + 0.707 * (loop_radius - cond_radius) - 3*cond_radius;
var p3y = loopy + 0.707 * (loop_radius - cond_radius);
var p4x = loopx + 0.707 * (loop_radius - cond_radius);
var p4y = loopy + 0.707 * (loop_radius - cond_radius) - 3*cond_radius;
fctx.moveTo(win_width-p1x, p1y);
fctx.lineTo(p1x, p1y);
fctx.lineTo(p2x, p2y);
fctx.lineTo(p3x, p3y);
fctx.lineTo(p4x, p4y);
fctx.lineTo(p2x, p2y);
p1x = loopx + 0.9 * (loop_radius + cond_radius);
p1y = loopy + 0.9 * (loop_radius + cond_radius);
p2x = loopx + 0.707 * (loop_radius + cond_radius);
p2y = loopy + 0.707 * (loop_radius + cond_radius);
p3x = loopx + 0.707 * (loop_radius + cond_radius) + 3*cond_radius;
p3y = loopy + 0.707 * (loop_radius + cond_radius);
p4x = loopx + 0.707 * (loop_radius + cond_radius);
p4y = loopy + 0.707 * (loop_radius + cond_radius) + 3*cond_radius;
fctx.moveTo(p1x, p1y);
fctx.lineTo(p2x, p2y);
fctx.lineTo(p3x, p3y);
fctx.lineTo(p4x, p4y);
fctx.lineTo(p2x, p2y);
fctx.stroke();
fctx.lineWidth = normal_width;
fctx.font = normal_font;
fctx.textAlign = "left";
fctx.fillText(metal, loopx + 0.9 * (loop_radius + cond_radius) + 3, loopy + 0.9 * (loop_radius + cond_radius));
} else
if(shape == "octagon") {
// Draw octagon:
//const width = loop_diameter_slider.value;
const a = 0.4142135 * loop_radius * 2.0;
const circumradius = 1.30656296 * a;
fctx.lineWidth = 2 * cond_radius;
fctx.beginPath();
fctx.moveTo(loopx - 3, loopy - loop_radius);
for(var i=0; i<8; i++) {
fctx.lineTo(loopx - circumradius * Math.sin(Math.PI * (i/4.0 + 1.0/8)), loopy - circumradius * Math.cos(Math.PI * (i/4.0 + 1.0/8)));
}
fctx.lineTo(loopx + 3, loopy - loop_radius);
fctx.stroke();
// Draw variable capacitor:
fctx.lineWidth = 3;
fctx.beginPath();
fctx.moveTo(loopx - 3, loopy - loop_radius - 3*cond_radius);
fctx.lineTo(loopx - 3, loopy - loop_radius + 3*cond_radius);
fctx.moveTo(loopx + 3, loopy - loop_radius - 3*cond_radius);
fctx.lineTo(loopx + 3, loopy - loop_radius + 3*cond_radius);
fctx.moveTo(loopx - 12, loopy - loop_radius + 3*cond_radius);
fctx.lineTo(loopx + 12, loopy - loop_radius - 3*cond_radius);
fctx.stroke();
// Draw coupling loop:
fctx.lineWidth = 2;
fctx.beginPath();
fctx.arc(loopx, loopy + (loop_radius - loop_radius/5) - cond_radius , loop_radius/5, 0, 2*Math.PI, true);
fctx.stroke();
fctx.font = normal_font;
fctx.save();
fctx.translate(loopx - loop_radius - cond_radius - 8, loopy);
fctx.rotate(-Math.PI * 0.5);
fctx.textAlign = "center";
const s = (100.0 * loop_diameter_slider.value) * 0.414213; // side length in cm
if(units == "metric") {
fctx.fillText(s.toPrecision(3).toString() + " cm", 0, 0);
} else {
fctx.fillText((s*0.03281).toPrecision(3).toString() + "\'", 0, 0);
}
fctx.restore();
// Draw conductor diameter arrow:
fctx.lineWidth = cond_dia_thickness;
fctx.beginPath();
var p1x = loopx + 0.45 * (loop_radius - cond_radius);
var p1y = loopy + 0.45 * (loop_radius - cond_radius);
var p2x = loopx + 0.707 * (loop_radius - cond_radius);
var p2y = loopy + 0.707 * (loop_radius - cond_radius);
var p3x = loopx + 0.707 * (loop_radius - cond_radius) - 3*cond_radius;
var p3y = loopy + 0.707 * (loop_radius - cond_radius);
var p4x = loopx + 0.707 * (loop_radius - cond_radius);
var p4y = loopy + 0.707 * (loop_radius - cond_radius) - 3*cond_radius;
fctx.moveTo(win_width-p1x, p1y);
fctx.lineTo(p1x, p1y);
fctx.lineTo(p2x, p2y);
fctx.lineTo(p3x, p3y);
fctx.lineTo(p4x, p4y);
fctx.lineTo(p2x, p2y);
p1x = loopx + 0.9 * (loop_radius + cond_radius);
p1y = loopy + 0.9 * (loop_radius + cond_radius);
p2x = loopx + 0.707 * (loop_radius + cond_radius);
p2y = loopy + 0.707 * (loop_radius + cond_radius);
p3x = loopx + 0.707 * (loop_radius + cond_radius) + 3*cond_radius;
p3y = loopy + 0.707 * (loop_radius + cond_radius);
p4x = loopx + 0.707 * (loop_radius + cond_radius);
p4y = loopy + 0.707 * (loop_radius + cond_radius) + 3*cond_radius;
fctx.moveTo(p1x, p1y);
fctx.lineTo(p2x, p2y);
fctx.lineTo(p3x, p3y);
fctx.lineTo(p4x, p4y);
fctx.lineTo(p2x, p2y);
fctx.stroke();
fctx.lineWidth = normal_width;
fctx.font = normal_font;
fctx.textAlign = "left";
fctx.fillText(metal, p1x + 3, p1y);
} else
if(shape == "hexagon") {
// Draw hexagon:
const radius = 2.0 * loop_radius * 0.57735;
fctx.lineWidth = 2 * cond_radius;
fctx.beginPath();
fctx.moveTo(loopx - 3, loopy - radius);
for(var i=1; i<6; i++) {
fctx.lineTo(loopx - radius * Math.sin(Math.PI * (i/3.0)), loopy - radius * Math.cos(Math.PI * (i/3.0)));
}
fctx.lineTo(loopx + 3, loopy - radius);
fctx.stroke();
// Draw variable capacitor:
fctx.lineWidth = 3;
fctx.beginPath();
fctx.moveTo(loopx - 3, loopy - radius - 3*cond_radius);
fctx.lineTo(loopx - 3, loopy - radius + 3*cond_radius);
fctx.moveTo(loopx + 3, loopy - radius - 3*cond_radius);
fctx.lineTo(loopx + 3, loopy - radius + 3*cond_radius);
fctx.moveTo(loopx - 12, loopy - radius + 3*cond_radius);
fctx.lineTo(loopx + 12, loopy - radius - 3*cond_radius);
fctx.stroke();
// Draw coupling loop:
fctx.lineWidth = 2;
fctx.beginPath();
fctx.arc(loopx, loopy + (radius - radius/4) - cond_radius , radius/5, 0, 2*Math.PI, true);
fctx.stroke();
fctx.font = normal_font;
fctx.save();
fctx.translate(loopx - loop_radius - cond_radius - 8, loopy);
fctx.rotate(-Math.PI * 0.5);
fctx.textAlign = "center";
const s = (100.0 * loop_diameter_slider.value) * 0.57735; // side length in cm
if(units == "metric") {
fctx.fillText(s.toPrecision(3).toString() + " cm", 0, 0);
} else {
fctx.fillText((s*0.03281).toPrecision(3).toString() + "\'", 0, 0);
}
fctx.restore();
// Draw conductor diameter arrow:
fctx.lineWidth = cond_dia_thickness;
fctx.beginPath();
var p0x = loopx + 0.45 * loop_radius;
var p0y = loopy + 0.45 * loop_radius;
var p1x = loopx + loop_radius - cond_radius - 4*cond_radius;
var p1y = loopy + 0.45 * loop_radius;
var p2x = loopx + loop_radius - cond_radius;
var p2y = loopy + 0.45 * loop_radius;
var p3x = loopx + loop_radius - cond_radius - 2*cond_radius;
var p3y = loopy + 0.45 * loop_radius - 2*cond_radius;
var p4x = loopx + loop_radius - cond_radius - 2*cond_radius;
var p4y = loopy + 0.45 * loop_radius + 2*cond_radius;
fctx.moveTo(win_width-p0x, p0y);
fctx.lineTo(p0x, p0y);
//fctx.lineTo(p1x, p1y);
fctx.lineTo(p2x, p2y);
fctx.lineTo(p3x, p3y);
fctx.lineTo(p4x, p4y);
fctx.lineTo(p2x, p2y);
p1x = loopx + loop_radius + cond_radius + 4*cond_radius;
//p1y = loopy + 1.0 * (loop_radius + cond_radius);
p2x = loopx + loop_radius + cond_radius;
//p2y = loopy + 0.707 * (loop_radius + cond_radius);
p3x = loopx + loop_radius + cond_radius + 2*cond_radius;
//p3y = loopy + 0.707 * (loop_radius + cond_radius);
p4x = loopx + loop_radius + cond_radius + 2*cond_radius;
//p4y = loopy + 0.707 * (loop_radius + cond_radius) + 3*cond_radius;
fctx.moveTo(p1x, p1y);
fctx.lineTo(p2x, p2y);
fctx.lineTo(p3x, p3y);
fctx.lineTo(p4x, p4y);
fctx.lineTo(p2x, p2y);
fctx.stroke();
fctx.lineWidth = normal_width;
fctx.font = normal_font;
fctx.textAlign = "left";
fctx.fillText(metal, p1x + 3, p1y);
} else {
// Draw square:
const radius = 1.414 * loop_radius;
fctx.lineWidth = 2 * cond_radius;
fctx.beginPath();
fctx.moveTo(loopx - 3, loopy - loop_radius);
for(var i=0; i<4; i++) {
fctx.lineTo(loopx - radius * Math.sin(Math.PI * (i/2.0 + 0.25)), loopy - radius * Math.cos(Math.PI * (i/2.0 + 0.25)));
}
fctx.lineTo(loopx + 3, loopy - loop_radius);
fctx.stroke();
// Draw variable capacitor:
fctx.lineWidth = 3;
fctx.beginPath();
fctx.moveTo(loopx - 3, loopy - loop_radius - 3*cond_radius);
fctx.lineTo(loopx - 3, loopy - loop_radius + 3*cond_radius);
fctx.moveTo(loopx + 3, loopy - loop_radius - 3*cond_radius);
fctx.lineTo(loopx + 3, loopy - loop_radius + 3*cond_radius);
fctx.moveTo(loopx - 12, loopy - loop_radius + 3*cond_radius);
fctx.lineTo(loopx + 12, loopy - loop_radius - 3*cond_radius);
fctx.stroke();
// Draw coupling loop:
fctx.lineWidth = 2;
fctx.beginPath();
fctx.arc(loopx, loopy + (loop_radius - loop_radius/5) - cond_radius , loop_radius/5, 0, 2*Math.PI, true);
fctx.stroke();
fctx.font = normal_font;
fctx.save();
fctx.translate(loopx - loop_radius - cond_radius - 8, loopy);
fctx.rotate(-Math.PI * 0.5);
fctx.textAlign = "center";
const s = (100.0 * loop_diameter_slider.value); // side length in cm
if(units == "metric") {
fctx.fillText(s.toPrecision(3).toString() + " cm", 0, 0);
} else {
fctx.fillText((s*0.03281).toPrecision(3).toString() + "\'", 0, 0);
}
fctx.restore();
// Draw conductor diameter arrow:
fctx.lineWidth = cond_dia_thickness;
fctx.beginPath();
var p0x = loopx + 0.45 * (loop_radius - cond_radius);
var p0y = loopy + 0.45 * (loop_radius - cond_radius);
var p1x = loopx + loop_radius - cond_radius - 4*cond_radius;
var p1y = loopy + 0.7 * loop_radius;
var p2x = loopx + loop_radius - cond_radius;
var p2y = loopy + 0.7 * loop_radius;
var p3x = loopx + loop_radius - cond_radius - 2*cond_radius;
var p3y = loopy + 0.7 * loop_radius - 2*cond_radius;
var p4x = loopx + loop_radius - cond_radius - 2*cond_radius;
var p4y = loopy + 0.7 * loop_radius + 2*cond_radius;
fctx.moveTo(win_width-p0x, p0y);
fctx.lineTo(p0x, p0y);
fctx.lineTo(p1x, p1y);
fctx.lineTo(p2x, p2y);
fctx.lineTo(p3x, p3y);
fctx.lineTo(p4x, p4y);
fctx.lineTo(p2x, p2y);
p1x = loopx + loop_radius + cond_radius + 4*cond_radius;
//p1y = loopy + 1.0 * (loop_radius + cond_radius);
p2x = loopx + loop_radius + cond_radius;
//p2y = loopy + 0.707 * (loop_radius + cond_radius);
p3x = loopx + loop_radius + cond_radius + 2*cond_radius;
//p3y = loopy + 0.707 * (loop_radius + cond_radius);
p4x = loopx + loop_radius + cond_radius + 2*cond_radius;
//p4y = loopy + 0.707 * (loop_radius + cond_radius) + 3*cond_radius;
fctx.moveTo(p1x, p1y);
fctx.lineTo(p2x, p2y);
fctx.lineTo(p3x, p3y);
fctx.lineTo(p4x, p4y);
fctx.lineTo(p2x, p2y);
fctx.stroke();
fctx.lineWidth = normal_width;
fctx.font = normal_font;
fctx.textAlign = "left";
fctx.fillText(metal, p1x + 3, p1y);
}
// Draw loop diameter arrow:
fctx.lineWidth = loop_dia_thickness;
fctx.beginPath();
fctx.moveTo(loopx - loop_radius, loopy);
fctx.lineTo(loopx - loop_radius + 2*cond_radius, loopy - 2*cond_radius);
fctx.lineTo(loopx - loop_radius + 2*cond_radius, loopy + 2*cond_radius);
fctx.lineTo(loopx - loop_radius, loopy);
fctx.lineTo(loopx + loop_radius, loopy);
fctx.lineTo(loopx + loop_radius - 2*cond_radius, loopy + 2*cond_radius);
fctx.lineTo(loopx + loop_radius - 2*cond_radius, loopy - 2*cond_radius);
fctx.lineTo(loopx + loop_radius, loopy);
fctx.stroke();
fctx.lineWidth = normal_width;
// Write loop inductance:
fctx.font = normal_font;
const L = inductance * 1.0e+6;
//const L = getInductance() * 1.0e+6;
fctx.fillText("L = " + L.toPrecision(3).toString() + " \u03bcH", 8, 18);
// Write Tx power text:
fctx.font = tx_font;
fctx.fillText("Tx = " + transmit_power_slider.value + " W", 8, win_height * 0.8 + 10);
fctx.font = external_losses_font;
fctx.fillText("Re = " + R_ext.toFixed(3).toString() + " \u03A9", 8, win_height * 0.8 + 24);
// Write loop diameter symbol:
fctx.font = normal_font;
fctx.textAlign = "center";
const dia = 1.0 * loop_diameter_slider.value;
fctx.font = loop_dia_font;
if(units == "metric") {
fctx.fillText("\u2300b = " + dia.toPrecision(3).toString() + " m", loopx, loopy - 6);
} else {
fctx.fillText("\u2300b = " + (dia*3.28084).toPrecision(3).toString() + "\'", loopx, loopy - 6);
}
fctx.font = normal_font;
p1x = loopx + 0.45 * (loop_radius - cond_radius);
p1y = loopy + 0.45 * (loop_radius - cond_radius) - 5;
//fctx.textAlign = "right";
const cond_dia = 1.0 * conductor_diameter_slider.value;
fctx.textAlign = "center";
if(units == "metric") {
fctx.font = cond_dia_font;
fctx.fillText("\u2300a = " + cond_dia.toPrecision(3).toString() + " mm", loopx, p1y+1);
fctx.font = normal_font;
// Write loop area:
fctx.textAlign = "right";
fctx.fillText("A = " + area.toPrecision(3).toString() + " m\u00B2", win_width-8, 18);
// Write Tx power text:
fctx.fillText("peri = " + perimeter.toPrecision(3).toString() + " m", win_width-8, win_height * 0.8 + 24);
} else {
fctx.font = cond_dia_font;
fctx.fillText("\u2300a = " + (cond_dia/25.4).toPrecision(3).toString() + "\"", loopx, p1y+1);
fctx.font = normal_font;
// Write loop area:
fctx.textAlign = "right";
fctx.fillText("A = " + (area * 10.76391).toPrecision(3).toString() + " ft\u00B2", win_width-8, 18);
// Write Tx power text:
fctx.fillText("peri = " + (perimeter*3.28084).toPrecision(3).toString() + " ft", win_width-8, win_height * 0.8 + 24);
}
}
const aside_canvas = document.getElementById("antennaSide2D");
const sctx = aside_canvas.getContext('2d');
function drawSideDesign() {
const win_width = document.getElementById("antenna-side-container").offsetWidth;
const win_height = document.getElementById("antenna-side-container").offsetHeight;
aside_canvas.width = win_width-2;
aside_canvas.height = win_height-2;
sctx.clearRect(0, 0, win_width, win_height);
const cond_radius = conductor_diameter_slider.value / 12;
const cond_spacing = 2 * cond_radius * loop_spacing_slider.value;
const start_x = (loop_turns > 0) ? win_width/2 - loop_turns * cond_spacing * 0.5 : win_width/2;
const top_y = win_height * 0.2;
const bot_y = win_height * 0.7;
if(loop_mode == "series") {
if(loop_turns > 1) {
for (let i = 0; i < loop_turns; i++) {
sctx.beginPath();
sctx.arc(start_x + i * cond_spacing, top_y, cond_radius, Math.PI, 0);
sctx.arc(start_x + cond_spacing * 0.5 + i * cond_spacing, bot_y, cond_radius, 0, Math.PI);
//sctx.lineTo(start_x + i * cond_spacing + cond_radius, top_y);
sctx.fill();
sctx.beginPath();
sctx.moveTo(start_x + cond_spacing * 0.5 + i * cond_spacing + cond_radius, bot_y);
sctx.lineTo(start_x + (i+1) * cond_spacing + cond_radius, top_y);
sctx.arc(start_x + (i+1) * cond_spacing, top_y, cond_radius, 0, Math.PI, true);
sctx.lineTo(start_x + cond_spacing * 0.5 + i * cond_spacing - cond_radius, bot_y);
sctx.stroke();
}
} else {
sctx.beginPath();
sctx.arc(win_width/2, bot_y, cond_radius, 0, Math.PI);
sctx.arc(win_width/2, top_y, cond_radius, Math.PI, 0);
sctx.lineTo(win_width/2 + cond_radius, bot_y);
sctx.fill();
}
} else {
// "parallel" - means draw a two-arm parallel magloop:
sctx.beginPath();
sctx.arc(start_x + 0.5 * cond_spacing, bot_y, cond_radius, 0, Math.PI);
sctx.arc(start_x + 0.5 * cond_spacing, top_y, cond_radius, Math.PI, 0);
sctx.lineTo(start_x + 0.5 * cond_spacing + cond_radius, bot_y);
sctx.fill();
sctx.beginPath();
sctx.arc(start_x + 1.5 * cond_spacing, bot_y, cond_radius, 0, Math.PI);
sctx.arc(start_x + 1.5 * cond_spacing, top_y, cond_radius, Math.PI, 0);
sctx.lineTo(start_x + 1.5 * cond_spacing + cond_radius, bot_y);
sctx.fill();
}
// Draw left spacing arrow:
const dim_y = win_height * 0.8;
if(loop_turns > 1) {
sctx.beginPath();
//sctx.moveTo(0.5 * win_width - 0.5 * cond_spacing + shift - 20, dim_y);
sctx.moveTo(start_x + 0.5 * cond_spacing - 20, dim_y);
sctx.lineTo(start_x + 0.5 * cond_spacing , dim_y);
sctx.lineTo(start_x + 0.5 * cond_spacing - 7, dim_y + 7)
sctx.lineTo(start_x + 0.5 * cond_spacing - 7, dim_y - 7)
sctx.lineTo(start_x + 0.5 * cond_spacing , dim_y);
sctx.moveTo(start_x + 0.5 * cond_spacing , dim_y - 7);
sctx.lineTo(start_x + 0.5 * cond_spacing , dim_y + 7);
sctx.stroke();
// Draw right spacing arrow:
sctx.beginPath();
sctx.moveTo(start_x + 1.5 * cond_spacing + 20, dim_y);
sctx.lineTo(start_x + 1.5 * cond_spacing , dim_y);
sctx.lineTo(start_x + 1.5 * cond_spacing + 7, dim_y + 7)
sctx.lineTo(start_x + 1.5 * cond_spacing + 7, dim_y - 7)
sctx.lineTo(start_x + 1.5 * cond_spacing , dim_y);
sctx.moveTo(start_x + 1.5 * cond_spacing , dim_y - 7);
sctx.lineTo(start_x + 1.5 * cond_spacing , dim_y + 7);
sctx.stroke();
}
sctx.textAlign = "left";
sctx.font = turns_font;
sctx.fillText("N = " + loop_turns.toString() + ((loop_mode == "series") ? "" : " (P)"), 8, win_height * 0.1 + 3);
sctx.font = spacing_font;
sctx.fillText("c/a = ", 8, win_height * 0.1 + 18);
sctx.fillText((loop_spacing_slider.value*1.0).toPrecision(3).toString(), 8, win_height * 0.1 + 33);
sctx.font = normal_font;
// Multi-turn loop, so calculate C and SRF:
const L = loop_capacitance * 1e+12;
sctx.textAlign = "right";
sctx.fillText("C = " + L.toFixed(0).toString() + " pF", win_width-8, 18);
sctx.fillText("SRF = ", win_width-8, win_height * 0.1 + 18);
sctx.fillText((srf*1e-6).toPrecision(3).toString() + " MHz", win_width-8, win_height * 0.1 + 33);
sctx.textAlign = "right";
sctx.fillText("cond = " , win_width-8, dim_y + 10);
if(units == "metric") {
sctx.fillText(conductor_length.toPrecision(4).toString() + " m", win_width-8, dim_y + 24);
} else {
sctx.fillText((conductor_length * 3.28084).toPrecision(4).toString() + " ft", win_width-8, dim_y + 24);
}
// Draw spacing text:
if(loop_turns > 1) {
sctx.textAlign = "center";
const spc = (loop_turns > 1) ? loop_spacing_slider.value * conductor_diameter_slider.value : 0.0;
if(units == "metric") {
sctx.fillText("c = " + spc.toPrecision(3).toString() + " mm", start_x + cond_spacing, dim_y + 20);
} else {
sctx.fillText("c = " + (spc/25.4).toPrecision(3).toString() + " in", start_x + cond_spacing, dim_y + 20);
}
}
}
// Set the global variables, which are all determined by physical dimensions, and are thus frequency-independent:
setGlobals();
// Update the frequencies, now that we have the sliders available:
updateFrequencies();
drawFrontDesign();
drawSideDesign();
const chartCanvas = document.getElementById("chartCanvas");
const chartCanvasContext = chartCanvas.getContext('2d');
function getMetricPrefix(num) {
if(num >= 1e9) return {val: num*1e-9, pfx:'G'};
if(num >= 1e6) return {val: num*1e-6, pfx:'M'};
if(num >= 1e3) return {val: num*1e-3, pfx:'k'};
if(num < 1e-12) return {val: num*1e15, pfx:'f'};
if(num < 1e-9) return {val: num*1e12, pfx:'p'};
if(num < 1e-6) return {val: num*1e9, pfx:'n'};
if(num < 1e-3) return {val: num*1e6, pfx:'\u03bc'};
if(num < 1.0) return {val: num*1e3, pfx:'m'};
return {val:num, pfx:' '};
}
function justifyText(pre, post) {
var whitespace = 22 - pre.length - post.length;
return pre + ' '.repeat(whitespace) + post;
}
var myChart = new Chart(chartCanvasContext, {
type: 'line',
data: {
datasets: [
{
label: 'Tuning Cap (pF)',
fill: false,
borderColor: 'green',
backgroundColor: 'green',
data: calculateTuningCapacitor(),
borderWidth: 1,
yAxisID: 'pfID',
tension: 0.3,
},
{
label: 'Vcap (kV)',
fill: false,
borderColor: 'rgb(150, 150, 0)',
backgroundColor: 'rgb(200, 200, 0)',
data: calculateCapacitorVoltage(),
borderWidth: 1,
yAxisID: 'vID',
tension: 0.3,
},
{
label: 'BW (kHz)',
fill: false,
borderColor: 'brown',
backgroundColor: 'brown',
data: calculateBandwidth(),
borderWidth: 1,
yAxisID: 'bwID',
tension: 0.3,
},
{
label: 'Efficiency (%)',
fill: false,
borderColor: 'black',
backgroundColor: 'black',
data: calculateEfficiencyFactor(),
borderWidth: 1,
yAxisID: 'effID',
tension: 0.3,
},
{
label: 'R-radiation (\u03A9)',
fill: false,
borderColor: 'red',
backgroundColor: 'red',
data: calculateRadiationResistance(),
borderWidth: 1,
yAxisID: 'mohmsID',
tension: 0.3,
},
{
label: 'R-loop (\u03A9)',
fill: false,
borderColor: 'orange',
backgroundColor: 'orange',
data: calculateLossResistance(),
borderWidth: 1,
yAxisID: 'mohmsID',
tension: 0.3,
},
{
label: 'Reactance (j\u03A9)',
fill: false,
borderColor: 'blue',
backgroundColor: 'blue',
data: calculateInductiveReactance(),
borderWidth: 1,
yAxisID: 'ohmsID',
tension: 0.3,
},
{
label: 'Q',
fill: false,
borderColor: 'purple',
backgroundColor: 'purple',
data: calculateQualityFactor(),
borderWidth: 1,
yAxisID: 'qID',
tension: 0.3,
},
{
label: 'I\u2092 (A)',
fill: false,
borderColor: 'rgb(0,255,255)',
backgroundColor: 'rgb(0,128,128)',
data: calculateCirculatingCurrent(),
borderWidth: 1,
yAxisID: 'ccID',
tension: 0.3,
},
{
label: 'Perimeter (\u03BB)',
fill: false,
borderColor: 'rgb(130,130,130)',
backgroundColor: 'rgb(130,130,130)',
data: calculateAntennaSize(),
borderWidth: 1,
yAxisID: 'sizeID',
tension: 0.3,
},
{
label: 'Skin depth (\u03BCm)',
fill: false,
borderColor: 'rgb(75,75,75)',
backgroundColor: 'rgb(75,75,75)',
data: calculateSkinDepth(),
borderWidth: 1,
yAxisID: 'skinID',
tension: 0.3,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'linear',
position: 'bottom',
display: 'auto',
title: {
display: true,
text: 'Frequency (MHz)',
color: 'black',
font: {
weight: 'bold'
}
},
ticks: {
autoSkip: false,
}
},
'mohmsID' : {
type: 'logarithmic',
display: 'auto',
title: {
display: true,
text: '\u03A9',
color: 'red',
font: {
weight : 'bold'
}
},
min: 0.001,
max: 1.0,
position: 'left',
},
'effID' : {
type: 'linear',
display: 'auto',
title: {
display: true,
text: 'Efficiency %',
color: 'black',
font: {
weight : 'bold'
}
},
ticks: {
},
min: 0.0,
max: 100.0,
position: 'left',
},
'bwID' : {
type: 'linear',
display: 'auto',
title: {
display: true,
text: 'BW kHz',
color: 'brown',
font: {
weight : 'bold'
}
},
ticks: {
beginAtZero: true,
},
max: 50.0,
min: 0.0,
position: 'left',
},
'vID' : {
type: 'logarithmic',
display: 'auto',
title: {
display: true,
text: 'kV',
color: 'rgb(150, 150, 0)',
font: {
weight : 'bold'
}
},
ticks: {
beginAtZero: true,
},
max: 50.0,
min: 0.05,
position: 'left',
},
'pfID' : {
type: 'logarithmic',
display: 'auto',
title: {
display: true,
text: 'pF',
color: 'green',
font: {
weight : 'bold'
}
},
max: 5000.0,
min: 5.0,
ticks: {
},
position: 'left',
},
'ohmsID' : {
type: 'linear',
display: 'auto',
title: {
display: true,
text: 'j\u03A9',
color: 'blue',
font: {
weight : 'bold'
}
},
ticks: {
beginAtZero: true,
},
min: 0.0,
max: 500.0,
position: 'right',
},
'qID' : {
type: 'linear',
display: 'auto',
title: {
display: true,
text: 'Q',
color: 'purple',
font: {
weight : 'bold'
}
},
ticks: {
beginAtZero: true,
},
min: 0.0,
max: 5000.0,
position: 'right',
},
'ccID' : {
type: 'logarithmic',
display: 'auto',
title: {
display: true,
text: 'A',
color: 'rgb(0,128,128)',
font: {
weight : 'bold'
}
},
ticks: {
beginAtZero: true,
},
min: 1.0,
max: 500.0,
position: 'right',
},
'sizeID' : {
type: 'linear',
display: 'auto',
title: {
display: true,
text: '\u03BB',
color: 'rgb(90,90,90)',
font: {
weight : 'bold'
}
},
ticks: {
beginAtZero: true,
},
max: 0.3,
min: 0.0,
position: 'right',
},
'skinID' : {
type: 'linear',
display: 'auto',
title: {
display: true,
text: '\u03B4 \u03BCm',
color: 'rgb(90,90,90)',
font: {
weight : 'bold'
}
},
ticks: {
beginAtZero: true,
//max: 0.3,
},
min: 0.0,
position: 'right',
}
},
plugins: {
//showLines: true,
mode: 'nearest',
tooltip: {
enabled: true,
mode: 'index',
intersect: false,
position: 'nearest',
bodyFont: {
family: 'monospace',
},
callbacks: {
title: function(context) {
var value = context[0].parsed.x;
var lut = {0.1357:'2200', 0.1378:'2200', 0.472:'600', 0.479:'600', 1.8:'160', 1.875:'160', 3.5:'80', 3.8:'80', 5.3:'60', 5.4:'60', 7.0:'40', 7.3:'40', 10.1:'30', 10.15:'30', 14.0:'20', 14.35:'20', 18.068:'17', 18.168:'17', 21.0:'15', 21.45:'15',
24.89:'12', 24.99:'12', 26.965:'11', 27.855:'11', 28.0:'10', 29.7:'10', 35.0:'', 40.0:'', 45.0:'', 50.0:'6', 52.0:'6', 54.0:'6', 69.9:'4', 70.5:'4', 144.0:'2', 146.0:'2', 148.0:'2', 420.0:'0.7', 430.0:'0.7', 440.0:'0.7', 450.0:'0.7'};
var label = '' + value.toPrecision(4).toString() + ' MHz';
if(lut[value]) {
label += ' (';
label += lut[value] + ' m)';
}
return label;
},
label: function(context) {
var value = context.element.parsed.y;
var label = context.dataset.label || '';
if (label) {
label += ': ';
}
if(context.dataset.label == "Tuning Cap (pF)") {
var num = getMetricPrefix(value * 1e-12);
label = justifyText("Tuning Cap: ", num.val.toPrecision(3).toString() + ' ' + num.pfx + 'F');
} else
if(label[0] == "Q"){
label = justifyText("Q: ", Math.round(value).toString() + " ");
} else
if(label[0] == 'V'){
var num = getMetricPrefix(value * 1e3);
label = justifyText("Vcap: ", num.val.toPrecision(3).toString() + ' ' + num.pfx + 'V');
} else
if(label[0] == 'B'){
var num = getMetricPrefix(value * 1e3);
label = justifyText("Bandwidth: ", num.val.toPrecision(3).toString() + ' ' + num.pfx + 'Hz');
} else
if(label[0] == 'E'){
label = justifyText("Efficiency: ", value.toFixed(2).toString() + ' ' + ' %');
} else
if((label[0] == 'R') && (label[2] == 'r')){
var num = getMetricPrefix(value);
label = justifyText("R-rad: ", num.val.toPrecision(3).toString() + ' ' + num.pfx + '\u03A9');
} else
if((label[0] == 'R') && (label[2] == 'l')){
var num = getMetricPrefix(value);
label = justifyText("R-loop: ", num.val.toPrecision(3).toString() + ' ' + num.pfx + '\u03A9');
} else
if((label[0] == 'R') && (label[1] == 'e')){
var num = getMetricPrefix(value);
label = justifyText("Reactance: ", 'j' + num.val.toPrecision(3) + ' ' + num.pfx + '\u03A9');
} else
if(label[0] == 'I'){
var num = getMetricPrefix(value);
label = justifyText("I\u2092: ", num.val.toPrecision(3).toString() + ' ' + num.pfx + 'A');
} else
if(label[0] == 'P'){
label = justifyText("Perimeter: ", value.toFixed(3).toString() + ' ' + '\u03BB');
} else
if(label[0] == 'S'){
var num = getMetricPrefix(value * 1e-6);
label = justifyText("Skin depth: ", num.val.toPrecision(3).toString() + ' ' + num.pfx + 'm');
} else {
label += value.toFixed(3).toString();
}
return label;
}
}
},
}
}
});
</script>
</body>
</html>