| | <!DOCTYPE html> |
| | <html lang="en"> |
| |
|
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Fourier Transform Visualizer</title> |
| | <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script> |
| | <link rel="stylesheet" href="src/css/style.css"> |
| | <link rel="stylesheet" href="src/css/fourier_transform.css"> |
| | </head> |
| |
|
| | <body> |
| | <div class="container"> |
| | <h1>Fourier Transform Visualizer</h1> |
| | <p class="subtitle">Compose signals from sine waves and visualize their frequency spectrum</p> |
| |
|
| | <div class="floating-controls" id="floatingControls"> |
| | <div class="controls-title" id="controlsTitle">Signal Controls</div> |
| | <div class="controls-grid"> |
| | |
| | <div class="wave-section"> |
| | <div class="wave-header"> |
| | <label class="control-label"> |
| | <input type="checkbox" id="enableWave1" class="checkbox-input" checked> |
| | Wave 1 |
| | </label> |
| | <span class="wave-color" style="background-color: #1976d2;"></span> |
| | </div> |
| | <div class="control-group"> |
| | <label for="freq1" class="control-label">Frequency (Hz)</label> |
| | <input type="range" min="0" max="50" value="4" step="1" class="slider" id="freq1"> |
| | <div class="slider-value" id="freq1Value">4 Hz</div> |
| | </div> |
| | <div class="control-group"> |
| | <label for="amp1" class="control-label">Amplitude</label> |
| | <input type="range" min="0" max="10" value="5" step="0.1" class="slider" id="amp1"> |
| | <div class="slider-value" id="amp1Value">5.0</div> |
| | </div> |
| | <div class="control-group"> |
| | <label for="phase1" class="control-label">Phase (°)</label> |
| | <input type="range" min="0" max="360" value="0" step="15" class="slider" id="phase1"> |
| | <div class="slider-value" id="phase1Value">0°</div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="wave-section"> |
| | <div class="wave-header"> |
| | <label class="control-label"> |
| | <input type="checkbox" id="enableWave2" class="checkbox-input" checked> |
| | Wave 2 |
| | </label> |
| | <span class="wave-color" style="background-color: #d32f2f;"></span> |
| | </div> |
| | <div class="control-group"> |
| | <label for="freq2" class="control-label">Frequency (Hz)</label> |
| | <input type="range" min="0" max="50" value="8" step="1" class="slider" id="freq2"> |
| | <div class="slider-value" id="freq2Value">8 Hz</div> |
| | </div> |
| | <div class="control-group"> |
| | <label for="amp2" class="control-label">Amplitude</label> |
| | <input type="range" min="0" max="10" value="3" step="0.1" class="slider" id="amp2"> |
| | <div class="slider-value" id="amp2Value">3.0</div> |
| | </div> |
| | <div class="control-group"> |
| | <label for="phase2" class="control-label">Phase (°)</label> |
| | <input type="range" min="0" max="360" value="90" step="15" class="slider" id="phase2"> |
| | <div class="slider-value" id="phase2Value">90°</div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="wave-section"> |
| | <div class="wave-header"> |
| | <label class="control-label"> |
| | <input type="checkbox" id="enableWave3" class="checkbox-input"> |
| | Wave 3 |
| | </label> |
| | <span class="wave-color" style="background-color: #388e3c;"></span> |
| | </div> |
| | <div class="control-group"> |
| | <label for="freq3" class="control-label">Frequency (Hz)</label> |
| | <input type="range" min="0" max="50" value="12" step="1" class="slider" id="freq3"> |
| | <div class="slider-value" id="freq3Value">12 Hz</div> |
| | </div> |
| | <div class="control-group"> |
| | <label for="amp3" class="control-label">Amplitude</label> |
| | <input type="range" min="0" max="10" value="0" step="0.1" class="slider" id="amp3"> |
| | <div class="slider-value" id="amp3Value">0.0</div> |
| | </div> |
| | <div class="control-group"> |
| | <label for="phase3" class="control-label">Phase (°)</label> |
| | <input type="range" min="0" max="360" value="0" step="15" class="slider" id="phase3"> |
| | <div class="slider-value" id="phase3Value">0°</div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="wave-section"> |
| | <div class="wave-header"> |
| | <label class="control-label"> |
| | <input type="checkbox" id="enableWave4" class="checkbox-input"> |
| | Wave 4 |
| | </label> |
| | <span class="wave-color" style="background-color: #f57c00;"></span> |
| | </div> |
| | <div class="control-group"> |
| | <label for="freq4" class="control-label">Frequency (Hz)</label> |
| | <input type="range" min="0" max="50" value="16" step="1" class="slider" id="freq4"> |
| | <div class="slider-value" id="freq4Value">16 Hz</div> |
| | </div> |
| | <div class="control-group"> |
| | <label for="amp4" class="control-label">Amplitude</label> |
| | <input type="range" min="0" max="10" value="0" step="0.1" class="slider" id="amp4"> |
| | <div class="slider-value" id="amp4Value">0.0</div> |
| | </div> |
| | <div class="control-group"> |
| | <label for="phase4" class="control-label">Phase (°)</label> |
| | <input type="range" min="0" max="360" value="0" step="15" class="slider" id="phase4"> |
| | <div class="slider-value" id="phase4Value">0°</div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="wave-section" style="border-top: 2px solid var(--color-border); padding-top: var(--space-md); margin-top: var(--space-md);"> |
| | <div class="control-group"> |
| | <label for="numSamples" class="control-label"> |
| | Samples (N) |
| | <span class="help-tooltip" title="Limiter N à des puissances de 2 (64, 128, 256, 512) garantit une meilleure résolution spectrale et évite le 'spectral leakage' qui crée des pics parasites dans le spectre de phase.">ℹ️</span> |
| | </label> |
| | <input type="range" min="64" max="512" value="256" step="64" class="slider" id="numSamples" list="powersOf2"> |
| | <datalist id="powersOf2"> |
| | <option value="64"></option> |
| | <option value="128"></option> |
| | <option value="256"></option> |
| | <option value="512"></option> |
| | </datalist> |
| | <div class="slider-value" id="numSamplesValue">256</div> |
| | </div> |
| | <div class="control-group"> |
| | <label class="control-label"> |
| | <input type="checkbox" id="addNoise" class="checkbox-input"> |
| | Add Gaussian Noise |
| | </label> |
| | </div> |
| | <div class="control-group" id="noiseLevelGroup" style="opacity: 0.4;"> |
| | <label for="noiseLevel" class="control-label">Noise Level (σ)</label> |
| | <input type="range" min="0.1" max="3" value="0.5" step="0.1" class="slider" id="noiseLevel" disabled> |
| | <div class="slider-value" id="noiseLevelValue">0.5</div> |
| | </div> |
| | </div> |
| |
|
| | <div class="chart-hint" style="margin-top: 8px; font-size: 0.7rem; color: var(--color-text-muted);"> |
| | 💡 Combine up to 4 sine waves to create complex signals |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div class="charts-container"> |
| | |
| | <div class="chart-card" style="grid-column: span 2;"> |
| | <div class="chart-title">Time Domain Signal</div> |
| | <div class="chart-container" style="height: 300px;"> |
| | <canvas id="timeDomainChart"></canvas> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="chart-card"> |
| | <div class="chart-title">Magnitude Spectrum |X(f)|</div> |
| | <div class="chart-container" style="height: 280px;"> |
| | <canvas id="magnitudeChart"></canvas> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="chart-card" id="phaseCard"> |
| | <div class="chart-title">Phase Spectrum ∠X(f)</div> |
| | <div class="chart-container" style="height: 280px;"> |
| | <canvas id="phaseChart"></canvas> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="chart-card" style="grid-column: span 2;"> |
| | <div class="chart-title">Signal Information</div> |
| | <div class="metrics-display" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-md);"> |
| | <div class="metric-item"> |
| | <span class="metric-label">Sampling Rate (Fs)</span> |
| | <span class="metric-value" id="samplingRate">-</span> |
| | </div> |
| | <div class="metric-item"> |
| | <span class="metric-label">Nyquist Frequency</span> |
| | <span class="metric-value" id="nyquistFreq">-</span> |
| | </div> |
| | <div class="metric-item"> |
| | <span class="metric-label">Frequency Resolution (Δf)</span> |
| | <span class="metric-value" id="freqResolution">-</span> |
| | </div> |
| | <div class="metric-item"> |
| | <span class="metric-label">Signal Duration</span> |
| | <span class="metric-value" id="signalDuration">-</span> |
| | </div> |
| | <div class="metric-item"> |
| | <span class="metric-label">Total Power</span> |
| | <span class="metric-value" id="totalPower">-</span> |
| | </div> |
| | <div class="metric-item"> |
| | <span class="metric-label">RMS Amplitude</span> |
| | <span class="metric-value" id="rmsAmplitude">-</span> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <footer> |
| | <a href="https://github.com/berangerthomas/SchoolOfStatistics" target="_blank" |
| | rel="noopener noreferrer">Project's GitHub</a> |
| | </footer> |
| |
|
| | <script src="src/js/common.js"></script> |
| | <script src="src/js/fourier_transform.js"></script> |
| | </body> |
| |
|
| | </html> |