Indicador Mark / Space HTML

 Segue uma abordagem visual do Tuning Scope de tubo (conhecido como Cross-line Indicator)!



Estão sintonizados 2125Hz e 2295Hz

Site com áudio RTTY : http://internet-tty.net:8000/ITTY


Segue o código!

<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <title>RTTY Tuning Scope - CRT Style</title>
    <style>
        body { background: #1a1a1a; color: #00ff00; font-family: 'Courier New', monospace; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; }
        .container { border: 4px solid #333; border-radius: 50%; padding: 20px; background: #000; box-shadow: 0 0 50px #004400; position: relative; }
        canvas { background: #001100; border-radius: 50%; border: 2px solid #003300; width: 400px; height: 400px; }
        .controls { margin-top: 20px; text-align: center; display: flex; flex-direction: column; gap: 10px; align-items: center; }
        button { background: #004400; color: #00ff00; border: 1px solid #00ff00; padding: 10px 20px; cursor: pointer; font-size: 16px; transition: 0.3s; font-weight: bold; }
        button:hover { background: #00ff00; color: #000; }
        .info { font-size: 12px; color: #008800; }
        input[type=range] { width: 250px; accent-color: #00ff00; cursor: pointer; }
        label { font-size: 14px; }
    </style>
</head>
<body>

    <h2>RTTY X-Y SCOPE</h2>

    <div class="container">
        <canvas id="scope" width="400" height="400"></canvas>
    </div>

    <div class="controls">
        <button id="startBtn">ATIVAR SENSOR/ÁUDIO</button>
        
        <div style="display: flex; align-items: center; gap: 10px;">
            <label>SENSIVEL.</label>
            <input type="range" id="gainSlider" min="1" max="50" step="1" value="15">
        </div>

        <div class="info">Sintonize em: Mark 2125Hz (V) | Space 2295Hz (H)</div>
    </div>

    <script>
        let audioCtx, analyserMark, analyserSpace, source, gainNode;
        const canvas = document.getElementById('scope');
        const ctx = canvas.getContext('2d');
        const gainSlider = document.getElementById('gainSlider');

        async function initAudio() {
            audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            
            try {
                // DESATIVAR AGC e Noise Suppression para o sinal não sumir
                const stream = await navigator.mediaDevices.getUserMedia({ 
                    audio: {
                        echoCancellation: false,
                        noiseSuppression: false,
                        autoGainControl: false
                    } 
                });
                
                source = audioCtx.createMediaStreamSource(stream);

                // Criar o amplificador (Gain Node)
                gainNode = audioCtx.createGain();
                gainNode.gain.value = gainSlider.value;

                // Filtro para Mark (2125 Hz)
                const filterMark = audioCtx.createBiquadFilter();
                filterMark.type = "bandpass";
                filterMark.frequency.value = 2125;
                filterMark.Q.value = 25;

                // Filtro para Space (2295 Hz)
                const filterSpace = audioCtx.createBiquadFilter();
                filterSpace.type = "bandpass";
                filterSpace.frequency.value = 2295;
                filterSpace.Q.value = 25;

                analyserMark = audioCtx.createAnalyser();
                analyserSpace = audioCtx.createAnalyser();
                analyserMark.fftSize = 1024;
                analyserSpace.fftSize = 1024;

                // Fluxo: Microfone -> Ganho -> Filtros -> Analisadores
                source.connect(gainNode);
                gainNode.connect(filterMark);
                filterMark.connect(analyserMark);
                gainNode.connect(filterSpace);
                filterSpace.connect(analyserSpace);

                draw();
                document.getElementById('startBtn').innerText = "INDICADOR ATIVO";
            } catch (err) {
                alert("Erro ao acessar microfone: " + err);
            }
        }

        function draw() {
            requestAnimationFrame(draw);

            const dataMark = new Uint8Array(analyserMark.frequencyBinCount);
            const dataSpace = new Uint8Array(analyserSpace.frequencyBinCount);
            analyserMark.getByteTimeDomainData(dataMark);
            analyserSpace.getByteTimeDomainData(dataSpace);

            // Limpeza rápida para manter o traço nítido
            ctx.fillStyle = "rgba(0, 10, 0, 0.4)";
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            // Desenhar a grade (retículo)
            ctx.strokeStyle = "rgba(0, 50, 0, 0.5)";
            ctx.beginPath();
            ctx.moveTo(200, 0); ctx.lineTo(200, 400);
            ctx.moveTo(0, 200); ctx.lineTo(400, 200);
            ctx.stroke();

            // Desenhar o traço X-Y
            ctx.beginPath();
            ctx.strokeStyle = "#44ff88";
            ctx.lineWidth = 2.5;
            ctx.shadowBlur = 10;
            ctx.shadowColor = "#44ff88";

            // Multiplicador visual para expandir o desenho
            const zoom = 1.4;

            for (let i = 0; i < dataMark.length; i += 2) {
                // Normaliza de 0-255 para -1 a 1, e escala para o tamanho do canvas (200px de raio)
                let yNorm = (dataMark[i] - 128) / 128;
                let xNorm = (dataSpace[i] - 128) / 128;

                let y = 200 + (yNorm * 200 * zoom);
                let x = 200 + (xNorm * 200 * zoom);

                if (i === 0) ctx.moveTo(x, y);
                else ctx.lineTo(x, y);
            }
            ctx.stroke();
        }

        // Atualiza o ganho quando você move o slider
        gainSlider.oninput = () => {
            if (gainNode) gainNode.gain.value = gainSlider.value;
        };

        document.getElementById('startBtn').addEventListener('click', () => {
            if (!audioCtx) initAudio();
            else if (audioCtx.state === 'suspended') audioCtx.resume();
        });
    </script>
</body>
</html>


Comentários

Postagens mais visitadas deste blog

JVC Compulink, COMPU LINK, JVC, Tape Deck, Arduino, DCS Codes

Projetos elaborados, ferrovia