CoolFace
Apppublic

azdindrissi/bode-buddy-visual-control-wizard

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
script.js199 linesDownload Raw Back to root
1document.addEventListener('DOMContentLoaded', function() {2    // Initialize charts3    const magnitudeCtx = document.getElementById('magnitudePlot').getContext('2d');4    const phaseCtx = document.getElementById('phasePlot').getContext('2d');5    6    let magnitudeChart = new Chart(magnitudeCtx, {7        type: 'line',8        data: {9            labels: [],10            datasets: [{11                label: 'Magnitude (dB)',12                data: [],13                borderColor: '#4f46e5',14                backgroundColor: 'rgba(79, 70, 229, 0.1)',15                borderWidth: 2,16                fill: true,17                tension: 0.418            }]19        },20        options: getChartOptions('Magnitude (dB)')21    });22    23    let phaseChart = new Chart(phaseCtx, {24        type: 'line',25        data: {26            labels: [],27            datasets: [{28                label: 'Phase (degrees)',29                data: [],30                borderColor: '#10b981',31                backgroundColor: 'rgba(16, 185, 129, 0.1)',32                borderWidth: 2,33                fill: true,34                tension: 0.435            }]36        },37        options: getChartOptions('Phase (degrees)')38    });39    40    // System type change handler41    document.getElementById('systemType').addEventListener('change', function() {42        const systemType = this.value;43        document.getElementById('dampingControl').classList.toggle('hidden', systemType === '1st');44        document.getElementById('naturalFreqControl').classList.toggle('hidden', systemType === '1st');45        updateSystemInfo();46    });47    48    // Slider value displays49    document.getElementById('gain').addEventListener('input', function() {50        document.getElementById('gainValue').textContent = this.value;51    });52    53    document.getElementById('timeConstant').addEventListener('input', function() {54        document.getElementById('timeConstantValue').textContent = this.value + ' s';55    });56    57    document.getElementById('dampingRatio').addEventListener('input', function() {58        document.getElementById('dampingRatioValue').textContent = this.value;59    });60    61    document.getElementById('naturalFreq').addEventListener('input', function() {62        document.getElementById('naturalFreqValue').textContent = this.value + ' rad/s';63    });64    65    // Update button click handler66    document.getElementById('updateBtn').addEventListener('click', updatePlots);67    68    // Initial update69    updatePlots();70    71    function updatePlots() {72        const systemType = document.getElementById('systemType').value;73        const K = parseFloat(document.getElementById('gain').value);74        const tau = parseFloat(document.getElementById('timeConstant').value);75        76        let omega = generateLogSpace(0.01, 100, 100);77        let magnitude, phase;78        79        if (systemType === '1st') {80            // 1st order system calculations81            magnitude = omega.map(w => 20 * Math.log10(K / Math.sqrt(1 + Math.pow(w * tau, 2))));82            phase = omega.map(w => -Math.atan(w * tau) * 180 / Math.PI);83        } else {84            // 2nd order system calculations85            const zeta = parseFloat(document.getElementById('dampingRatio').value);86            const wn = parseFloat(document.getElementById('naturalFreq').value);87            88            magnitude = omega.map(w => {89                const ratio = w / wn;90                return 20 * Math.log10(K / Math.sqrt(91                    Math.pow(1 - Math.pow(ratio, 2), 2) + 92                    Math.pow(2 * zeta * ratio, 2)93                ));94            });95            96            phase = omega.map(w => {97                const ratio = w / wn;98                return -Math.atan2(2 * zeta * ratio, 1 - Math.pow(ratio, 2)) * 180 / Math.PI;99            });100        }101        102        // Update charts103        magnitudeChart.data.labels = omega;104        magnitudeChart.data.datasets[0].data = magnitude;105        magnitudeChart.update();106        107        phaseChart.data.labels = omega;108        phaseChart.data.datasets[0].data = phase;109        phaseChart.update();110        111        updateSystemInfo();112    }113    114    function updateSystemInfo() {115        const systemType = document.getElementById('systemType').value;116        const K = parseFloat(document.getElementById('gain').value);117        const tau = parseFloat(document.getElementById('timeConstant').value);118        119        if (systemType === '1st') {120            document.getElementById('transferFunction').textContent = 121                `H(s) = ${K} / (1 + ${tau}s)`;122            document.getElementById('cutoffFreq').textContent = 123                `${(1/tau).toFixed(2)} rad/s`;124            document.getElementById('phaseAtCutoff').textContent = 125                `-45°`;126        } else {127            const zeta = parseFloat(document.getElementById('dampingRatio').value);128            const wn = parseFloat(document.getElementById('naturalFreq').value);129            130            document.getElementById('transferFunction').textContent = 131                `H(s) = ${K} / (s² + ${(2*zeta*wn).toFixed(2)}s + ${Math.pow(wn, 2).toFixed(2)})`;132            document.getElementById('cutoffFreq').textContent = 133                `${wn.toFixed(2)} rad/s`;134            document.getElementById('phaseAtCutoff').textContent = 135                `-${(zeta < 0.707 ? 180 : 90)}°`;136        }137    }138    139    function generateLogSpace(start, end, count) {140        let result = [];141        const logStart = Math.log10(start);142        const logEnd = Math.log10(end);143        const delta = (logEnd - logStart) / (count - 1);144        145        for (let i = 0; i < count; i++) {146            result.push(Math.pow(10, logStart + i * delta));147        }148        149        return result;150    }151    152    function getChartOptions(title) {153        return {154            responsive: true,155            maintainAspectRatio: false,156            scales: {157                x: {158                    type: 'logarithmic',159                    title: {160                        display: true,161                        text: 'Frequency (rad/s)',162                        font: {163                            weight: 'bold'164                        }165                    },166                    ticks: {167                        callback: function(value) {168                            return value.toExponential(1);169                        }170                    }171                },172                y: {173                    title: {174                        display: true,175                        text: title,176                        font: {177                            weight: 'bold'178                        }179                    }180                }181            },182            plugins: {183                legend: {184                    display: false185                },186                tooltip: {187                    callbacks: {188                        label: function(context) {189                            return `${title}: ${context.parsed.y.toFixed(2)}`;190                        },191                        title: function(context) {192                            return `ω = ${context[0].parsed.x.toExponential(2)} rad/s`;193                        }194                    }195                }196            }197        };198    }199});