Files
vibing/CableCalc/index.php
thrhymes b049dded72 feat: Add WFW-Aushang web app with PWA support, offline caching, and dark mode
- Created index.html for the main application interface with responsive design and dark mode support.
- Added manifest.webmanifest for PWA configuration, including app icons and display settings.
- Implemented service worker (sw.js) for offline caching of assets and network-first strategy for versioning.
- Introduced version.json to manage app versioning.
2025-12-24 16:59:51 +01:00

482 lines
15 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// index.php - simple cable size calculator
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CableCalc Cable Size Calculator</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
--bg: #f4f4f4;
--card-bg: #ffffff;
--text: #222;
--text-muted: #555;
--border: #ccc;
--accent: #0078d7;
--result-bg: #f0f7ff;
--result-border: #c5ddff;
--error-bg: #ffecec;
--error-border: #ffb3b3;
}
/* DARK MODE VARIABLES */
@media (prefers-color-scheme: dark) {
:root {
--bg: #111;
--card-bg: #1b1b1b;
--text: #eee;
--text-muted: #bbb;
--border: #444;
--accent: #3ea0ff;
--result-bg: #102233;
--result-border: #1a3a57;
--error-bg: #331111;
--error-border: #883333;
}
}
/* USER OVERRIDE DARK */
body.dark {
--bg: #111;
--card-bg: #1b1b1b;
--text: #eee;
--text-muted: #bbb;
--border: #444;
--accent: #3ea0ff;
--result-bg: #102233;
--result-border: #1a3a57;
--error-bg: #331111;
--error-border: #883333;
}
/* USER OVERRIDE LIGHT */
body.light {
--bg: #f4f4f4;
--card-bg: #ffffff;
--text: #222;
--text-muted: #555;
--border: #ccc;
--accent: #0078d7;
--result-bg: #f0f7ff;
--result-border: #c5ddff;
--error-bg: #ffecec;
--error-border: #ffb3b3;
}
/* Base styles */
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
margin: 0;
padding: 0;
background: var(--bg);
color: var(--text);
transition: background 0.3s, color 0.3s;
}
.container {
max-width: 800px;
margin: 20px auto 40px;
background: var(--card-bg);
padding: 20px 24px 28px;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.20);
transition: background 0.3s;
}
h1 {
margin-top: 0;
font-size: 1.8rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
gap: 16px 24px;
}
.field label {
font-size: 0.9rem;
font-weight: 600;
display: block;
margin-bottom: 4px;
color: var(--text);
}
.field input,
.field select {
width: 100%;
box-sizing: border-box;
padding: 6px 8px;
border-radius: 6px;
border: 1px solid var(--border);
background: var(--card-bg);
color: var(--text);
transition: background 0.3s, color 0.3s, border 0.3s;
}
.inline-options label {
color: var(--text-muted);
}
button {
margin-top: 12px;
padding: 8px 16px;
border-radius: 999px;
border: none;
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
background: var(--accent);
color: white;
transition: opacity 0.2s;
}
button:hover {
opacity: 0.85;
}
.result {
margin-top: 20px;
padding: 12px 16px;
border-radius: 8px;
background: var(--result-bg);
border: 1px solid var(--result-border);
}
.error {
margin-top: 12px;
padding: 10px 14px;
border-radius: 8px;
background: var(--error-bg);
border: 1px solid var(--error-border);
color: #ff8080;
}
.notes {
margin-top: 14px;
font-size: 0.8rem;
color: var(--text-muted);
}
.badge {
display: inline-block;
padding: 2px 8px;
border-radius: 999px;
font-size: 0.75rem;
background: var(--border);
color: var(--text);
}
/* Toggle button */
#modeToggle {
float: right;
padding: 4px 12px;
border-radius: 20px;
background: var(--border);
color: var(--text);
font-size: 0.8rem;
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>CableCalc <span class="badge">beta</span></h1>
<p style="font-size:0.9rem; color:#555;">
Enter your data the calculator suggests the smallest copper cable size that keeps current
within a basic ampacity and voltage drop limit.
</p>
<div class="grid">
<!-- AC / DC -->
<div class="field">
<label for="acdc">AC / DC</label>
<select id="acdc">
<option value="AC">AC</option>
<option value="DC">DC</option>
</select>
<small>AC/DC is currently only shown for reference; calculation uses the same resistive model.</small>
</div>
<!-- Voltage -->
<div class="field">
<label for="voltage">Voltage [V]</label>
<input type="number" id="voltage" min="0" step="0.1" value="230">
</div>
<!-- Current / Power selector -->
<div class="field">
<label>What is known?</label>
<div class="inline-options">
<label>
<input type="radio" name="loadType" value="current" checked>
Current (A) known
</label>
<label>
<input type="radio" name="loadType" value="power">
Power (W) known
</label>
</div>
</div>
<!-- Current input -->
<div class="field" id="field-current">
<label for="current">Current [A]</label>
<input type="number" id="current" min="0" step="0.01">
</div>
<!-- Power input -->
<div class="field" id="field-power" style="display:none;">
<label for="power">Power [W]</label>
<input type="number" id="power" min="0" step="1">
<small>Assumes cos φ / power factor = 1.0 for simplicity.</small>
</div>
<!-- Length -->
<div class="field">
<label for="length">Cable length (one way)</label>
<input type="number" id="length" min="0" step="0.1" value="10">
</div>
<!-- Length unit -->
<div class="field">
<label for="lengthUnit">Length unit</label>
<select id="lengthUnit">
<option value="m">Meters</option>
<option value="ft">Feet</option>
</select>
<small>Voltage drop is calculated over the round-trip (out &amp; back).</small>
</div>
<!-- Max voltage drop -->
<div class="field">
<label for="maxDrop">Max voltage drop [%]</label>
<input type="number" id="maxDrop" min="0.5" step="0.1" value="3.0">
</div>
<!-- Output units -->
<div class="field">
<label for="unitSystem">Preferred output</label>
<select id="unitSystem">
<option value="mixed">AWG + mm²</option>
<option value="awg">AWG only</option>
<option value="mm2">mm² only</option>
</select>
</div>
</div>
<button type="button" onclick="calculateCable()">Calculate cable size</button>
<div id="error" class="error" style="display:none;"></div>
<div id="result" class="result" style="display:none;"></div>
<div id="modeToggle" onclick="toggleMode()">🌓</div>
<div class="notes">
<strong>Notes &amp; limitations:</strong>
<ul>
<li>Assumes copper conductors, single circuit, free air / light installation.</li>
<li>Uses generic ampacity values (conservative but not a substitute for local electrical codes).</li>
<li>For mains or critical installations always have a qualified electrician verify the result.</li>
</ul>
</div>
</div>
<script>
// Cable data: copper, approx. resistance at 20°C and conservative ampacity in A.
// Values are generic and simplified not a substitute for national standards.
const cableSizes = [
// AWG, mm², diameter(mm, bare conductor approx), R[ohm/km], ampacity[A]
{ awg: 18, mm2: 0.82, diameter: 1.02, r_km: 21.0, amp: 14 },
{ awg: 16, mm2: 1.31, diameter: 1.29, r_km: 13.3, amp: 18 },
{ awg: 14, mm2: 2.08, diameter: 1.63, r_km: 8.4, amp: 24 },
{ awg: 12, mm2: 3.31, diameter: 2.05, r_km: 5.3, amp: 32 },
{ awg: 10, mm2: 5.26, diameter: 2.59, r_km: 3.3, amp: 45 },
{ awg: 8, mm2: 8.37, diameter: 3.26, r_km: 2.1, amp: 60 },
{ awg: 6, mm2: 13.3, diameter: 4.11, r_km: 1.3, amp: 80 },
{ awg: 4, mm2: 21.1, diameter: 5.19, r_km: 0.8, amp: 105 },
{ awg: 2, mm2: 33.6, diameter: 6.54, r_km: 0.51, amp: 140 },
{ awg: 1, mm2: 42.4, diameter: 7.35, r_km: 0.40, amp: 165 },
{ awg: 0, mm2: 53.5, diameter: 8.25, r_km: 0.32, amp: 195 }
];
// Toggle current / power fields
document.querySelectorAll('input[name="loadType"]').forEach(r => {
r.addEventListener('change', function () {
const type = this.value;
document.getElementById('field-current').style.display = (type === 'current') ? 'block' : 'none';
document.getElementById('field-power').style.display = (type === 'power') ? 'block' : 'none';
});
});
function showError(msg) {
const err = document.getElementById('error');
err.textContent = msg;
err.style.display = 'block';
document.getElementById('result').style.display = 'none';
}
function clearError() {
const err = document.getElementById('error');
err.style.display = 'none';
err.textContent = '';
}
function toggleMode() {
if (document.body.classList.contains("dark")) {
document.body.classList.remove("dark");
document.body.classList.add("light");
localStorage.setItem("cablecalc-theme", "light");
} else if (document.body.classList.contains("light")) {
document.body.classList.remove("light");
document.body.classList.add("dark");
localStorage.setItem("cablecalc-theme", "dark");
} else {
// No override yet → force dark
document.body.classList.add("dark");
localStorage.setItem("cablecalc-theme", "dark");
}
}
// Load saved preference
(function() {
const saved = localStorage.getItem("cablecalc-theme");
if (saved === "dark") document.body.classList.add("dark");
if (saved === "light") document.body.classList.add("light");
})();
function calculateCable() {
clearError();
const voltage = parseFloat(document.getElementById('voltage').value);
const lengthOneWay = parseFloat(document.getElementById('length').value);
const lengthUnit = document.getElementById('lengthUnit').value;
const maxDropPct = parseFloat(document.getElementById('maxDrop').value);
const unitSystem = document.getElementById('unitSystem').value;
const loadType = document.querySelector('input[name="loadType"]:checked').value;
if (isNaN(voltage) || voltage <= 0) {
showError('Please enter a valid voltage.');
return;
}
if (isNaN(lengthOneWay) || lengthOneWay <= 0) {
showError('Please enter a valid cable length.');
return;
}
if (isNaN(maxDropPct) || maxDropPct <= 0) {
showError('Please enter a valid maximum voltage drop percentage.');
return;
}
let current;
if (loadType === 'current') {
current = parseFloat(document.getElementById('current').value);
if (isNaN(current) || current <= 0) {
showError('Please enter a valid current.');
return;
}
} else {
const power = parseFloat(document.getElementById('power').value);
if (isNaN(power) || power <= 0) {
showError('Please enter a valid power.');
return;
}
// Power factor assumed 1.0
current = power / voltage;
}
// Convert length to meters (one way)
let length_m = lengthOneWay;
if (lengthUnit === 'ft') {
length_m = lengthOneWay * 0.3048;
}
// Total circuit length = out + back
const totalLength_m = length_m * 2;
const maxDrop_V = voltage * (maxDropPct / 100.0);
let chosen = null;
let worstCase = null; // largest size if nothing meets both criteria
for (let i = 0; i < cableSizes.length; i++) {
const c = cableSizes[i];
const rPerMeter = c.r_km / 1000.0; // ohm/m
const rTotal = rPerMeter * totalLength_m; // ohm
const vDrop = current * rTotal; // V
const vDropPct = (vDrop / voltage) * 100;
const okAmpacity = current <= c.amp;
const okDrop = vDrop <= maxDrop_V;
// Remember largest size for fallback
worstCase = {
cable: c,
vDrop: vDrop,
vDropPct: vDropPct,
okAmpacity: okAmpacity,
okDrop: okDrop
};
if (okAmpacity && okDrop) {
chosen = {
cable: c,
vDrop: vDrop,
vDropPct: vDropPct
};
break; // smallest that satisfies
}
}
const res = document.getElementById('result');
res.style.display = 'block';
if (!chosen && worstCase) {
const c = worstCase.cable;
const vDrop = worstCase.vDrop;
const vDropPct = worstCase.vDropPct;
res.innerHTML = `
<strong>No cable size in the built-in table fully meets the limits.</strong><br><br>
Suggested <em>minimum</em> size (table maximum):<br>
AWG ${c.awg}, approx. ${c.mm2.toFixed(1)} mm², conductor diameter ≈ ${c.diameter.toFixed(2)} mm<br>
Ampacity (approx.): ${c.amp} A<br>
Estimated voltage drop: ${vDrop.toFixed(2)} V (${vDropPct.toFixed(2)} %)<br><br>
<span style="color:#a40000;">Warning:</span> Either the desired voltage drop or ampacity is exceeded.
Consider using a larger cable than listed here or shortening the run.
`;
return;
}
if (!chosen) {
showError('No valid cable size found. Please check inputs.');
return;
}
const c = chosen.cable;
const vDrop = chosen.vDrop;
const vDropPct = chosen.vDropPct;
let mainLine = '';
if (unitSystem === 'awg') {
mainLine = `Recommended cable size: <strong>AWG ${c.awg}</strong>`;
} else if (unitSystem === 'mm2') {
mainLine = `Recommended cable size: <strong>${c.mm2.toFixed(1)} mm²</strong>`;
} else {
mainLine = `Recommended cable size: <strong>AWG ${c.awg} (~${c.mm2.toFixed(1)} mm²)</strong>`;
}
res.innerHTML = `
${mainLine}<br>
Conductor diameter (approx.): <strong>${c.diameter.toFixed(2)} mm</strong><br>
Ampacity (approx.): <strong>${c.amp} A</strong><br>
Estimated voltage drop over ${length_m.toFixed(1)} m one-way (round-trip ${totalLength_m.toFixed(1)} m):<br>
<strong>${vDrop.toFixed(2)} V (${vDropPct.toFixed(2)} %)</strong>
`;
}
</script>
</body>
</html>