实时波形与频谱分析———傅立叶变换

实时波形与频谱分析:一个交互式动画演示

在信号处理领域,时域波形频域频谱是理解信号特性的重要工具。通过时域波形,我们可以直观地观察信号随时间的变化,而频域频谱则揭示了信号中所包含的频率成分及其幅值。为了帮助大家更好地理解这两者之间的关系,我开发了一个交互式的动画演示系统,展示波形与频谱的实时分析。

本文将详细介绍该系统的功能、实现原理以及如何使用它来探索信号的特性。


功能概述

这个演示系统实现了以下功能:

  1. 实时波形显示

    • 左侧显示信号的时域波形。
    • 支持两个正弦波的叠加,并实时动态更新波形。
  2. 频谱分析

    • 右侧显示信号的频谱图。
    • 通过频谱图可以直观地看到信号中包含的频率分量及其幅值。
  3. 交互控制

    • 为每个正弦波提供独立的频率和振幅控制。
    • 可以通过复选框启用或禁用某个波形分量。
  4. 视觉效果

    • 网格背景便于观察波形和频谱。
    • 使用不同颜色区分不同波形的频率分量,使频谱图更加直观。

系统界面与交互

1. 时域波形

时域波形展示信号随时间的变化。在演示中,时域波形是一个复合信号,它由两个正弦波叠加而成。通过滑块调整正弦波的频率和振幅,波形会实时更新,帮助用户观察不同参数对信号的影响。

2. 频谱图

频谱图展示信号的频率成分及其幅值。在演示中,频谱图会显示两个正弦波的频率分量,分别用不同颜色标注正频率的幅值。通过调整正弦波的参数,可以观察频谱图的变化,例如频率峰值的位置和高度。

3. 交互控制

用户可以通过以下方式与系统交互:

  • 频率滑块:调节正弦波的频率(1-20 Hz)。
  • 振幅滑块:调节正弦波的振幅(0-100)。
  • 复选框:启用或禁用某个正弦波。
  • 系统会实时响应用户的调整,并动态更新波形与频谱图。

实现原理

1. 时域波形计算

时域波形是两个正弦波的叠加,表达式如下:

y(t) = A₁ * sin(2πf₁t + φ₁) + A₂ * sin(2πf₂t + φ₂)

其中:

  • A₁ 和 A₂ 是两个波形的振幅。
  • f₁ 和 f₂ 是两个波形的频率。
  • φ₁ 和 φ₂ 是相位,这里通过时间戳动态变化,实现波形的动画效果。
2. 频谱分析

对于单一正弦波,其频谱在频域中表现为两个对称的冲击峰,分别位于正频率和负频率处。通过分析复合波形的频率成分,我们可以在频谱图中绘制每个频率分量的幅值。

3. 动画与动态更新

通过 JavaScript 的 requestAnimationFrame 函数实现动画效果。系统每秒更新波形和频谱图,确保用户的调整能够实时反映在画布上。


使用方法

  1. 加载页面:将代码保存为 HTML 文件并在浏览器中打开。
  2. 调节波形参数
    • 使用滑块调整频率和振幅,观察时域波形的变化。
    • 启用或禁用某个波形,查看复合波形的变化。
  3. 观察频谱图
    • 调节频率,观察频谱峰值的位置变化。
    • 调节振幅,观察频谱峰值的高度变化。

实际应用

这个演示系统不仅可以帮助初学者理解时域与频域的关系,还可以用于以下场景:

  1. 信号分解

    • 通过调整参数,观察复合信号的组成成分。
    • 理解如何通过频谱图分析信号的频率特性。
  2. 滤波器设计

    • 通过观察频谱图,思考如何设计滤波器以保留或去除特定频率成分。
  3. 教育与教学

    • 作为教学工具,帮助学生直观理解傅立叶变换和频谱分析的基本概念。

示例代码

以下是完整的 HTML 演示代码,可以直接复制并运行:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>波形与频谱分析</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 p-8">
    <div class="max-w-7xl mx-auto">
        <h1 class="text-3xl font-bold text-center mb-6">波形与频谱实时分析</h1>
        
        <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
            <!-- 时域波形 -->
            <div class="bg-white rounded-lg shadow-lg p-4">
                <h2 class="text-xl font-semibold mb-4">时域波形</h2>
                <canvas id="waveCanvas" width="600" height="300" class="border border-gray-300 rounded"></canvas>
            </div>
            
            <!-- 频谱图 -->
            <div class="bg-white rounded-lg shadow-lg p-4">
                <h2 class="text-xl font-semibold mb-4">频谱图</h2>
                <canvas id="spectrumCanvas" width="600" height="300" class="border border-gray-300 rounded"></canvas>
            </div>
        </div>

        <!-- 控制面板 -->
        <div class="mt-6 bg-white rounded-lg shadow-lg p-6">
            <h3 class="text-lg font-semibold mb-4">波形控制</h3>
            <div id="waveControls" class="space-y-4">
                <!-- 波形1 -->
                <div class="p-4 border rounded-lg">
                    <div class="flex items-center mb-2">
                        <span class="w-20">波形 1</span>
                        <input type="checkbox" id="wave1Enable" checked class="ml-2">
                    </div>
                    <div class="grid grid-cols-2 gap-4">
                        <div>
                            <label class="block text-sm">频率 (Hz)</label>
                            <input type="range" id="freq1" min="1" max="20" value="2" class="w-full">
                            <span id="freq1Value" class="text-sm">2 Hz</span>
                        </div>
                        <div>
                            <label class="block text-sm">振幅</label>
                            <input type="range" id="amp1" min="0" max="100" value="50" class="w-full">
                            <span id="amp1Value" class="text-sm">50</span>
                        </div>
                    </div>
                </div>
                
                <!-- 波形2 -->
                <div class="p-4 border rounded-lg">
                    <div class="flex items-center mb-2">
                        <span class="w-20">波形 2</span>
                        <input type="checkbox" id="wave2Enable" checked class="ml-2">
                    </div>
                    <div class="grid grid-cols-2 gap-4">
                        <div>
                            <label class="block text-sm">频率 (Hz)</label>
                            <input type="range" id="freq2" min="1" max="20" value="5" class="w-full">
                            <span id="freq2Value" class="text-sm">5 Hz</span>
                        </div>
                        <div>
                            <label class="block text-sm">振幅</label>
                            <input type="range" id="amp2" min="0" max="100" value="30" class="w-full">
                            <span id="amp2Value" class="text-sm">30</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
    const waveCanvas = document.getElementById('waveCanvas');
    const spectrumCanvas = document.getElementById('spectrumCanvas');
    const waveCtx = waveCanvas.getContext('2d');
    const spectrumCtx = spectrumCanvas.getContext('2d');

    // 控制参数
    const controls = {
        wave1: {
            enabled: document.getElementById('wave1Enable'),
            freq: document.getElementById('freq1'),
            amp: document.getElementById('amp1'),
            freqValue: document.getElementById('freq1Value'),
            ampValue: document.getElementById('amp1Value')
        },
        wave2: {
            enabled: document.getElementById('wave2Enable'),
            freq: document.getElementById('freq2'),
            amp: document.getElementById('amp2'),
            freqValue: document.getElementById('freq2Value'),
            ampValue: document.getElementById('amp2Value')
        }
    };

    // 更新显示值
    function updateValues() {
        controls.wave1.freqValue.textContent = controls.wave1.freq.value + ' Hz';
        controls.wave1.ampValue.textContent = controls.wave1.amp.value;
        controls.wave2.freqValue.textContent = controls.wave2.freq.value + ' Hz';
        controls.wave2.ampValue.textContent = controls.wave2.amp.value;
    }

    // 为所有控制添加事件监听
    Object.values(controls).forEach(control => {
        control.freq.addEventListener('input', updateValues);
        control.amp.addEventListener('input', updateValues);
    });

    // 绘制网格
    function drawGrid(ctx, width, height) {
        ctx.beginPath();
        ctx.strokeStyle = '#e5e5e5';
        ctx.lineWidth = 0.5;

        // 垂直线
        for (let x = 0; x <= width; x += 50) {
            ctx.moveTo(x, 0);
            ctx.lineTo(x, height);
        }
        // 水平线
        for (let y = 0; y <= height; y += 50) {
            ctx.moveTo(0, y);
            ctx.lineTo(width, y);
        }
        ctx.stroke();
    }

    // 绘制时域波形
    function drawWaveform(timestamp) {
        waveCtx.clearRect(0, 0, waveCanvas.width, waveCanvas.height);
        drawGrid(waveCtx, waveCanvas.width, waveCanvas.height);

        // 绘制中心线
        waveCtx.beginPath();
        waveCtx.strokeStyle = '#666';
        waveCtx.lineWidth = 1;
        waveCtx.moveTo(0, waveCanvas.height / 2);
        waveCtx.lineTo(waveCanvas.width, waveCanvas.height / 2);
        waveCtx.stroke();

        // 采样点数组
        const samples = new Float32Array(waveCanvas.width);
        const phase = timestamp / 1000;

        // 计算复合波形
        for (let x = 0; x < waveCanvas.width; x++) {
            let sample = 0;
            const t = x / 100;

            // 波形1
            if (controls.wave1.enabled.checked) {
                const freq1 = parseFloat(controls.wave1.freq.value);
                const amp1 = parseFloat(controls.wave1.amp.value);
                sample += (amp1 / 100) * Math.sin(2 * Math.PI * freq1 * t + phase);
            }

            // 波形2
            if (controls.wave2.enabled.checked) {
                const freq2 = parseFloat(controls.wave2.freq.value);
                const amp2 = parseFloat(controls.wave2.amp.value);
                sample += (amp2 / 100) * Math.sin(2 * Math.PI * freq2 * t + phase);
            }

            samples[x] = sample;
        }

        // 绘制波形
        waveCtx.beginPath();
        waveCtx.strokeStyle = '#3b82f6';
        waveCtx.lineWidth = 2;

        for (let x = 0; x < samples.length; x++) {
            const y = waveCanvas.height / 2 * (1 - samples[x]);
            if (x === 0) {
                waveCtx.moveTo(x, y);
            } else {
                waveCtx.lineTo(x, y);
            }
        }
        waveCtx.stroke();
    }

    // 绘制频谱
    function drawSpectrum() {
        spectrumCtx.clearRect(0, 0, spectrumCanvas.width, spectrumCanvas.height);
        drawGrid(spectrumCtx, spectrumCanvas.width, spectrumCanvas.height);

        // 绘制中心线
        spectrumCtx.beginPath();
        spectrumCtx.strokeStyle = '#666';
        spectrumCtx.lineWidth = 1;
        spectrumCtx.moveTo(0, spectrumCanvas.height);
        spectrumCtx.lineTo(spectrumCanvas.width, spectrumCanvas.height);
        spectrumCtx.stroke();

        // 频谱分析
        const maxFreq = 20; // 最大显示频率
        const freqScale = spectrumCanvas.width / maxFreq;

        // 绘制频谱线
        if (controls.wave1.enabled.checked) {
            const freq1 = parseFloat(controls.wave1.freq.value);
            const amp1 = parseFloat(controls.wave1.amp.value);
            drawFrequencyPeak(freq1, amp1, '#ef4444');
        }

        if (controls.wave2.enabled.checked) {
            const freq2 = parseFloat(controls.wave2.freq.value);
            const amp2 = parseFloat(controls.wave2.amp.value);
            drawFrequencyPeak(freq2, amp2, '#3b82f6');
        }

        function drawFrequencyPeak(freq, amp, color) {
            const x = freq * freqScale;
            const height = (amp / 100) * spectrumCanvas.height;

            spectrumCtx.beginPath();
            spectrumCtx.strokeStyle = color;
            spectrumCtx.lineWidth = 2;
            spectrumCtx.moveTo(x, spectrumCanvas.height);
            spectrumCtx.lineTo(x, spectrumCanvas.height - height);
            spectrumCtx.stroke();

            // 添加频率标签
            spectrumCtx.fillStyle = color;
            spectrumCtx.font = '12px Arial';
            spectrumCtx.textAlign = 'center';
            spectrumCtx.fillText(
                `${freq}Hz`,
                x,
                spectrumCanvas.height - height - 10
            );
        }
    }

    // 动画循环
    function animate(timestamp) {
        drawWaveform(timestamp);
        drawSpectrum();
        requestAnimationFrame(animate);
    }

    // 启动动画
    updateValues();
    requestAnimationFrame(animate);
    </script>
</body>
</html>

总结

通过这个交互式的动画演示,我们可以直观地理解时域波形与频域频谱之间的关系。无论是学习信号处理的初学者,还是从事相关领域的专业人士,这个工具都可以作为一个有趣且实用的学习和研究工具。

如果你对这个演示有任何改进建议,或者希望了解更多相关内容,欢迎留言讨论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/964703.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

03链表+栈+队列(D1_链表(D1_基础学习))

目录 一、什么是链表 二、基本操作 三、为什么要使用链表 四、为什么能够在常数时间访问数组元素 数组优点 数组缺点 五、动态数组诞生 链表优点 链表缺点 六、链表、数组和动态数组的对比 七、 链表种类 1. 单向链表 2. 双向链表 3. 循环链表 八、链表衍生 ...…

企业微信开发012_使用WxJava企业微信开发框架_封装第三方应用企业微信开发005_多企业授权实现---企业微信开发014

这里主要说一下如何授权的思路,如何来做,其实非常简单, 如果你有很多企业微信需要授权以后才能使用自己开发的,第三方企业微信功能,那么 首先,在企业列表中,你可以给某个企业去配置,这个企业,他对应的企业微信的,比如, 这个企业的企业id,cropID,当然还可以有,比如企业名称,用…

“AI智能分析综合管理系统:企业管理的智慧中枢

在如今这个快节奏的商业世界里&#xff0c;企业面临的挑战越来越多&#xff0c;数据像潮水一样涌来&#xff0c;管理工作变得愈发复杂。为了应对这些难题&#xff0c;AI智能分析综合管理系统闪亮登场&#xff0c;它就像是企业的智慧中枢&#xff0c;让管理变得轻松又高效。 过去…

蓝桥杯思维训练营(三)

文章目录 题目详解680.验证回文串 II30.魔塔游戏徒步旅行中的补给问题观光景点组合得分问题 题目详解 680.验证回文串 II 680.验证回文串 II 思路分析&#xff1a;这个题目的关键就是&#xff0c;按照正常来判断对应位置是否相等&#xff0c;如果不相等&#xff0c;那么就判…

[LeetCode] 二叉树 I — 深度优先遍历(前中后序遍历) | 广度优先遍历(层序遍历):递归法迭代法

二叉树 基础知识深度优先遍历递归法迭代法&#xff08;栈&#xff09;144# 二叉树的前序遍历94# 二叉树的中序遍历145# 二叉树的后序遍历 广度优先遍历递归法迭代法&#xff08;队列&#xff09;102# 二叉树的层序遍历107# 二叉树的层序遍历 II199# 二叉树的右视图637# 二叉树的…

Hugging Face GGUF 模型可视化

Hugging Face GGUF 模型可视化 1. Finding GGUF files (检索 GGUF 模型)2. Viewer for metadata & tensors info (可视化 GGUF 模型)References 无知小儿&#xff0c;仙家雄霸天下&#xff0c;依附强者才是唯一的出路。否则天地虽大&#xff0c;也让你们无路可走&#xff0…

基于Coze平台实现抖音链接提取文案转小红书文案的智能体开发全流程解析

文章目录 引言:跨平台内容运营的AI解法实例最终效果1. 平台特性对比与转化需求分析1.1 用户画像与内容风格对比1.2 文案转化核心需求2. Coze平台技术架构解析2.1 Coze核心能力矩阵2.2 关键技术组件选型3. 智能体工作流设计3.1 完整处理流程3.2 关键节点说明4. 核心模块实现详解…

【低功耗 Power 学习专栏 -- Power domian 和 power rail】

文章目录 power rail(followpin) 和 Power domain1. Power Domain2. Power Rail3. Followpin4. Power Stripe5. IR Drop芯片中电源管理设计 举例 power rail(followpin) 和 Power domain followpin 指两部分&#xff0c;一个就是 STD cell 上下的 VDD, VSS。同时&#xff0c;f…

PopupMenuButton组件的功能和用法

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了Sliver综合示例相关的内容&#xff0c;本章回中将介绍PopupMenuButton组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的PopupMenuButton组件位于AppBar右侧&#xff0c;…

TiDB 分布式数据库多业务资源隔离应用实践

导读 随着 TiDB 在各行业客户中的广泛应用 &#xff0c;特别是在多个业务融合到一套 TiDB 集群中的场景&#xff0c;各企业对集群内多业务隔离的需求日益增加。与此同时&#xff0c;TiDB 在多业务融合场景下的资源隔离方案日趋完善&#xff0c;详情可参考文章 《你需要什么样的…

CommonAPI学习笔记-2

一. 概述 ​ 这篇文章主要是想整理并且分析CommonAPI代码生成工具根据fidl和fdepl配置文件生成出来的代码的结构和作用。 二. fidl ​ 用户根据业务需求在fidl文件中定义业务服务接口的结构以及自定义数据类型&#xff0c;然后使用core生成工具传入fidl文件生成该fidl的核心…

ELK模块封装starter

文章目录 1.combinations-elk-starter1.目录结构2.log4j2-spring.xml 从环境变量读取host和port3.ELKProperties.java 两个属性4.ELKAutoConfiguration.java 启用配置类5.ELKEnvironmentPreparedListener.java 监听器从application.yml中获取属性值6.spring.factories 注册监听…

KNN算法:从思想到实现(附代码)

引言 K最近邻算法&#xff08;K Nearest Neighbors, KNN&#xff09;是一种简单而有效的机器学习算法&#xff0c;用于分类和回归问题。其核心思想基于“近朱者赤&#xff0c;近墨者黑”&#xff0c;即通过测量不同特征值之间的距离来进行分类或预测数值。本文将详细介绍KNN的…

学前端框架之前,你需要先理解 MVC

MVC 软件架构设计模式鼎鼎大名&#xff0c;相信你已经听说过了&#xff0c;但你确定自己已经完全理解到 MVC 的精髓了吗&#xff1f; 如果你是新同学&#xff0c;没听过 MVC&#xff0c;那可以到网上搜一些文章来看看&#xff0c;不过你要有心理准备&#xff0c;那些文章大多都…

第十八章 视图

目录 一、概述 二、语法 2.1. 创建视图 2.2. 查询视图 2.3. 修改视图 2.4. 删除视图 2.5. 示例 三、检查选项 3.1. CASCADED&#xff08;级联&#xff09; 3.2. LOCAL&#xff08;本地&#xff09; 四、视图的更新 五、视图作用 5.1. 简单 5.2. 安全 5.3. 数据独…

【MySQL】第一弹---MySQL 在 Centos 7环境安装

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】 目录 1. 卸载不要的环境 2. 检查系统安装包 3. 卸载这些默认安装包 4. 获取mysql官方yum源 5. 安装mysql yum 源&am…

实验2 词法分析(一)

实验2 词法分析(一) [实验目的]&#xff1a; 1 . 熟悉给定的词法分析程序&#xff1b; 2 . 改进词法分析程序。 [实验内容]&#xff1a; 1.运行TESE编译演示.exe&#xff0c;观看词法分析程序的分析过程&#xff0c;理解词法分析的原理。并尝试在“TEST源程序输入框”输入一段…

【PyQt】PyQt工具栏

PyQt工具栏 在 PyQt 中创建工具栏主要涉及 QMainWindow、QToolBar 和 QAction 类 界面展示 基本示例 import sys from PyQt5.QtWidgets import QMainWindow, QApplication, QAction from PyQt5.QtGui import QIcon from PyQt5.QtCore import Qtclass MainWindow(QMainWindow…

STM32 串口收发数据包

接线图 HEX数据包接收 文本数据包接收 代码配置 发送HEX数据包 //存储发送或接收的载荷数据 uint8_t TX_Packet[4]; uint8_t RX_Packet[4];void Serial_SendPacket(void) {Serial_SendByte(0xFF);//发送包头Serial_SendArray(TX_Packet, 4);//发送4个载荷数据Serial_SendByte…

zabbix5.0.46版本源码安装

zabbix5.0.46版本源码安装 1.安装环境说明 本例中安装zabbix开源软件和zabbix运行所需的中间件和数据库apache、php和flyingdb&#xff0c;软件版本信息如下&#xff1a; 软件版本zabbix5.0.46apachehttpd-2.4.61aprapr-1.7.5apr-util1.6.3php7.3.24PostgreSQL16.6 主机操作…