用FPGA CORDIC IP核实现信号的相位检测,计算相位角

用FPGA CORDIC IP核实现信号的相位检测

1.matlab仿真

波形仿真代码:

代码功能:生成一个点频信号s,求出s的实部和虚部;并且结算相位角atan2。画出图形,并且将Q和I数据写入文件中。

%代码功能:生成一个点频信号s,求出s的实部和虚部;并且结算相位角atan2。画出图形,并且将Q和I数据写入
clc; clear;close all;

F1=1; %信号频率
Fs=65536; %采样频率					Fs=N,能采一个周期
P1=45; %信号初始相位(单位:°),90-cos函数
N=65536; %采样点数
t=[0:1/Fs:(N-1)/Fs]; %采样时刻
A=2^15-1; %信号幅度

%生成点频信号
s=A*exp(1i *(2*pi*F1*t + pi*P1/180));

% IQ分解,分别提取实部和虚部
I = real(s);
Q = imag(s);

% 计算相位角
phase = atan2(Q, I);

% 提取初始相位(t=0处的相位值)
initial_phase = phase(1);
%显示初始相位值,将其转换为π的倍数进行显示 
disp(['初始相位值:', num2str(initial_phase/pi),'pi']);


% 绘制结果图,画出signal信号的实部和虚部
figure;
subplot(3,1,1);
plot(t, real(s), 'b');
title('In-phase Component (I)');

subplot(3,1,2);
plot(t, imag(s), 'r');
title('Quadrature Component (Q)');
xlabel('Time (s)');

% 计算相位角
subplot(3,1,3);
plot(t, phase);
xlabel('时间');
ylabel('相位');
title('相位随时间变化');
%将纵坐标转化为Π的倍数
yticks([-1*pi, -0.5*pi, 0, 0.5*pi, pi]);
yticklabels({'-π', '-0.5π', '0', '0.5π', 'π'});


%创建 coe 文件  Idata
 fild = fopen('Idata_65536x15bit.coe','wt');
 %写入 coe 文件头
 %固定写法,表示写入的数据是 10 进制表示
 fprintf(fild, '%s\n','memory_initialization_radix=10;');
 %固定写法,下面开始写入数据
 fprintf(fild, '%s\n\n','memory_initialization_vector ='); 
 for i = 1:N
 s2(i) = round(I(i)); %对小数四舍五入以取整
 fprintf(fild, '%d',s2(i)); %数据写入
 if i==N
 fprintf(fild, '%s\n',';'); %最后一个数据用;
 else
 fprintf(fild,',\n'); % 其他数据用,
 end
 end
 fclose(fild); % 写完了,关闭文件

 %创建 coe 文件  Qdata
 fild = fopen('Qdata_65536x15bit.coe','wt');
 %写入 coe 文件头
 %固定写法,表示写入的数据是 10 进制表示
 fprintf(fild, '%s\n','memory_initialization_radix=10;');
 %固定写法,下面开始写入数据
 fprintf(fild, '%s\n\n','memory_initialization_vector ='); 
 for i = 1:N
 s3(i) = round(Q(i)); %对小数四舍五入以取整
 fprintf(fild, '%d',s3(i)); %数据写入
 if i==N
 fprintf(fild, '%s\n',';'); %最后一个数据用;
 else
 fprintf(fild,',\n'); % 其他数据用,
 end
 end
 fclose(fild); % 写完了,关闭文件

​ 上面的代码除了生成下面的波形结果外,还将数据写入文件“Idata_65536x15bit.coe” 和 “Qdata_65536x15bit.coe”

可以在电脑win图标旁边直接搜索这两个文件,默认是在MATLAB文件中的一个文件夹中。

波形结果

在这里插入图片描述

2.FPGA实现

生成的点频信号signal 以及I,Q的分解,可以用ROM来输入。

用FPGA实现的关键以及难点是计算相位角phase = atan2(Q, I);

ROM

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

创建两个ROM,分别将Idata_65536x15bit 和 Qdata_65536x15bit 写入。

数学知识补充:atan和atan2

参考文章:atan2函数和atan函数 - 知乎 (zhihu.com)

在MATLAB中,atan和atan2函数都用于计算角度,但它们之间有一些重要的区别。

  1. atan函数:
  • atan函数是计算反正切值的标准函数,其语法为y = atan(x)。
  • atan函数返回的角度范围是[-π/2, π/2],即从-90度到90度之间。
  • atan函数只能接受一个参数,即y = atan(x),其中x是输入的实数值。
  1. atan2函数:
  • atan2函数是计算反正切值的扩展函数,其语法为y = atan2(y, x)。
  • atan2函数返回的角度范围是[-π, π],即从-180度到180度之间。
  • atan2函数可以处理所有四个象限的情况,避免了由于分母为零而导致的错误。
  • atan2函数接受两个参数,即y = atan2(y, x),其中y是输入的虚部,x是输入的实部。

​ 主要区别在于atan函数只能处理一个参数且返回值范围是[-π/2, π/2],而atan2函数可以处理两个参数且返回值范围是[-π, π],并且能够处理所有四个象限的情况。在处理复数的相位角度时,通常会使用atan2函数来确保得到正确的结果

这两个函数的转换关系

atan函数转换为atan2函数:(图片来自知乎)

在这里插入图片描述

  • x > 0, 是一、四象限的点,等价atan;
  • x < 0,是二、三象限的点,根据y的范围来确定:
    • y > 0,是第二象限的点,先用atan得到第四象限的弧度制,再加上Π就转换到了第二象限;
    • y < 0.是第三象限的点,先用atan得到第一象限的弧度制,再减去Π就转换到了第二象限;
  • x = 0,根据y的正负来确定
    • y > 0, 值为Π/2
    • y < 0,值为-Π/2
  • 注意,当x=0,y=0时,atan2(0,0)=0 (matlab中这样规定)

FPGA代码思路

VIVADO的CORDIC IP核中的Arc Tan ,可以直接计算atan2(自动将atan转换为atan2)。

CORDIC IP核使用

参考视频:FPGA IP之CORDIC使用与仿真_哔哩哔哩_bilibili

参考文章:FPGA数字信号处理(十四)Vivado Cordic IP核计算arctan_fpga arctan-CSDN博客

FPGA 代码

DDS_IQ模块,是DDS波形产生模块,读取ROM中存入的数据,生成两个波形 I_data 和 Q_data

module DDS_IQ
(
    input 				clk,    	//系统时钟
    input 				rst_n,
	
	output signed [15:0] I_data,
	output signed [15:0] Q_data

);

reg [15:0]r_Fword;		//频率控制字寄存器
reg [1:0]r_Pword;		//相位控制字寄存器
reg [31:0] Fcnt;		//累加寄存器
wire [15:0] I_rom_addr; 	//ROM地址,宽度:16位
wire [15:0] Q_rom_addr; 	//ROM地址,宽度:16位

//将值存入寄存器
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		r_Fword <= 16'd0;
		r_Pword <= 2'd0;
	end
	else begin
		r_Fword <= 16'd1000;	
		r_Pword <= 2'd0;	
	end
end

//累加
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		Fcnt <= 32'd0;
	else
		Fcnt <= Fcnt + r_Fword;
end

//相位调制器
assign I_rom_addr = Fcnt[31:15] + r_Pword;	//截取高位,并加上相位累加器的值
assign Q_rom_addr = Fcnt[31:15] + r_Pword;

blk_mem_gen_I I_value(
  .clka(clk),    // input wire clka
  .ena(1'b1),      // input wire ena
  .addra(I_rom_addr),  // input wire [15 : 0] addra
  .douta(I_data)  // output wire [15 : 0] douta
);
blk_mem_gen_Q Q_value (
  .clka(clk),    // input wire clka
  .ena(1'b1),      // input wire ena
  .addra(Q_rom_addr),  // input wire [15 : 0] addra
  .douta(Q_data)  // output wire [15 : 0] douta
);

endmodule

顶层 atan_top 模块,计算atan(Q/I)

module atan_top(
	input 				clk,    	//系统时钟
	input 				rst_n,
	input 				[15:0] I_data,
	input 				[15:0] Q_data,

    output signed 		out_valid,   //输出有效信号
    output signed [15:0]theta 		//arctan计算结果
	
    );

//例化DDS_IQ模块,将两个信号引入	
DDS_IQ u_DDS_IQ(
	.clk		(clk),    	
	.rst_n		(rst_n),
	.I_data		(I_data),
	.Q_data		(Q_data)
);

//输入I和Q,out=arctan(Q/I);
//tdata端口,虚部Q在前,实部I在后
cordic_1 u_cordic_1 (
  .aclk(clk),                                        // input wire aclk
  .aresetn(rst_n),                                  // input wire aresetn
  .s_axis_cartesian_tvalid(1'b1),  // input wire s_axis_cartesian_tvalid
  .s_axis_cartesian_tdata({Q_data[15],Q_data[15:1],I_data[15],I_data[15:1]}),    // input wire [31 : 0] s_axis_cartesian_tdata
  .m_axis_dout_tvalid(out_valid),            // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(theta)              // output wire [15 : 0] m_axis_dout_tdata
);
//输入的32位中,[15:0]为实部I,[16:31]为虚部Q
	
endmodule
TB文件
module cordic_tb_top();

//接口声明
  reg clk;
  reg rst_n;
  
  wire signed out_valid;
  wire signed [15:0]theta;
  wire signed[15:0]I_data;
  wire signed[15:0]Q_data;

//initial handle = $fopen("F:/MY_WORK/3U_phase_discrimination/cordic.txt");//打开文件

/*
//对被测的设计进行例化
DDS_IQ u_DDS_IQ(
	.clk		(clk),    	
	.rst_n		(rst_n),
	.I_data		(I_data),
	.Q_data		(Q_data)
);
*/
DDS_IQ u_DDS_IQ(
	.clk		(clk),    	
	.rst_n		(rst_n),
	.I_data		(I_data),
	.Q_data		(Q_data)
);

atan_top u_atan_top(
	.clk		(clk),    	
	.rst_n		(rst_n),
	.I_data		(I_data),
	.Q_data		(Q_data),
	.out_valid	(out_valid),  
	.theta 		(theta)
);


//产生时钟 50MHZ
initial clk = 1;
always #10 clk = ~clk;

//测试激励产生
initial begin
	rst_n = 0;

	#200;
	rst_n = 1;

end

/*
always@(posedge clk) begin
  if(out_valid)
      $fdisplay(handle,"%b",theta);//写数据
end
*/

endmodule
仿真结果

注意这三个波形为模拟波形,有符号数

在这里插入图片描述

结果分析

如何判断自己的仿真结果是正确的?

就以上面的仿真图片和下面的结果来验证一下

在这里插入图片描述

数据1:Q = 0 ; I = -32767 ; atan2 = 25736

数据2:Q = -25279; I = -20848; atan2 = -18517

这些数据如何转化为二进制,可以查看CORDIC IP核中的这个界面:

在这里插入图片描述

Q格式数据可以用Fix格式数据表示。

对于有符号数,表示为Fix(1+X+N)_N,X表示整数位数,N表示小数位数。

但是在这里的结果验证中,不需要转换数据格式就可以


  • 数据1:属于《数学知识补充:atan和atan2》 的第二种情况,x<0, y>=0

​ atan2(Q,I) = atan(Q/I) + Π = 0 + Π = Π

​ 而根据输出波形,atan2 = 25736,将它转换:25736/(2^13) = 3.1416

  • 数据2:属于《数学知识补充:atan和atan2》 的第三种情况,x<0, y<0

​ atan2(Q,I) = atan(Q/I) - Π

​ 其中,atan(Q/I) = atan(-25279/-20848) = atan(1.2125) = 0.88115 Radians

​ ∴ atan2(Q,I) = 0.88115 - Π = -2.26

而根据输出波形,atan2 = -18517,将它转换:-18517/(2^13) = -2.26


仿真结果符合事实

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

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

相关文章

Linux 之一:Linux 简介、客户端、安装

Linux简介 Linux 内核最初只是由芬兰人林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;在赫尔辛基大学上学时出于个人爱好而编写的。 Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹于…

Java 反射详解:动态创建实例、调用方法和访问字段

“一般情况下&#xff0c;我们在使用某个类之前已经确定它到底是个什么类了&#xff0c;拿到手就直接可以使用 new 关键字来调用构造方法进行初始化&#xff0c;之后使用这个类的对象来进行操作。” Writer writer new Writer(); writer.setName("少年");像上面这个…

HTTP常用状态码详解

目录 1xx - 信息性状态码 2xx - 成功状态码 3xx - 重定向状态码 4xx - 客户端错误状态码 5xx - 服务器错误状态码 总结 HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是一种用于传输超文本的应用层协议。在HTTP通信中&#xff0c;服务器和客户端之间会通过状态…

使用华为云云函数functiongraph

之前使用腾讯云serverless&#xff0c;但是突然开始收费了。所以改用functiongraph 首先登陆华为云。 目录 1.登录华为云 2.在控制台找到functiongraph并开通 3.添加依赖包&#xff1a; 3.1 制作依赖包 3.2引入依赖包 4.发送请求 4.1直接发送 4.1.1uri 4.1.2 请求头…

使用word写论文或项目书时,插入图片显示不完整或随文字移动解决办法

一、背景 虽然我们写科技论文时可以用Latex进行排版&#xff0c;但在写项目书或硕士论文时&#xff0c;一般给的模板都是word类型&#xff0c;所以我们还是不能放弃word的使用。但在word中插入图片时&#xff0c;会出现图片显示不完整情况&#xff0c;如下图所示&#xff0c;具…

单调队列(347. 前 K 个高频元素239. 滑动窗口最大值)

单调队列和单调栈其实差不多,就是维护一个区间单调的队列或者是栈,单调队列就是我们所说的大顶堆小顶堆, //升序队列 小顶堆 great 小到大 priority_queue <int,vector<int>,greater<int> > pri_que; //降序队列 大顶堆 less 大到小 默认 priority_qu…

xss.haozi.me:0x01

<textarea></textarea> 标签是不可以写入javascript代码的 所以我们要把textarea标签给闭合掉 这样就成功了 </textarea><script>alert(1)</script>

【Java设计模式】四、原型设计模式

文章目录 1、原型设计模式2、深克隆和浅克隆 1、原型设计模式 说明&#xff1a; 用一个原型对象&#xff0c;创建和原型对象相同的对象&#xff0c;以能够保证创建对象的性能是创建大量相同对象的最佳方式 使用场景&#xff1a; 对象的创建非常复杂&#xff0c;可以使用原型…

神经网络结构——CNN、RNN、LSTM、Transformer !!

文章目录 前言 一、什么是CNN 网络结构 解决问题 工作原理 实际应用 二、什么是RNN 网络结构 解决问题 工作原理 应用场景 三、什么是LSTM 网络结构 解决问题 工作原理 应用场景 四、什么是Transformer 网络结构 解决问题 工作原理 BERT GPT 前言 本文将从什么是CNN&#xff1…

使用 Footprint Analytics 提升 Web3 项目的空投活动

作者&#xff1a;stellafootprint.network 空投已成为 Web3 项目提升知名度、激励早期贡献者并吸引新用户的核心策略。尽管空投的概念一目了然——即向各类钱包免费发放代币&#xff0c;但要成功实施却需要周密的执行和战略规划。单纯的代币分发并非万全之策。在许多的空投后&a…

爬虫案例一

首先我举一个案例比如豆瓣电影排行榜 (douban.com)这个电影&#xff0c;首先我们进去检查源代码 说明源代码有&#xff0c;说明是服务器渲染&#xff0c;可以直接那html 但是返回的结果是空&#xff0c;所以我们需要在头里面加上User-Agent 然后可以看到有返回的结果&#xff0…

VBA字典与数组第十二讲:行列数相同的数组间运算

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

RabbitMQ的整体架构是怎么样的?

RabbitMQ是一个开源的消息中间件&#xff0c;用于在应用程序之间传递消息。它实现了AMQP(高级消息队列协议)并支持其他消息传递协议&#xff0c;例如STOMP(简单文本定向消息协议)和MQTT&#xff08;物联网协议&#xff09; 他的整体架构大致如下&#xff1a; Producer&#xf…

ABAP - SALV教程12 显示图标和提示信息

ALV要求字段的值为图标的需求并不多见&#xff0c;一般都用于红黄绿灯&#xff0c;来表示单据的执行状态&#xff0c;添加图标的方式也可以实现红黄绿灯的功能&#xff0c;也可以参考SALV实现红黄绿灯这篇文章&#xff1a;http://t.csdnimg.cn/Dzx7x效果图SAVL列设置为图标图标…

【Flutter 面试题】在flutter里streams是什么?有几种streams?有什么场景用到它?

【Flutter 面试题】在flutter里streams是什么&#xff1f;有几种streams&#xff1f;有什么场景用到它&#xff1f; 文章目录 写在前面解答补充说明**Single subscription streams** 读取文件广播流 Broadcast streams 通知多个监听器关于状态的变化 写在前面 关于我 &#xf…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:组件标识)

id为组件的唯一标识&#xff0c;在整个应用内唯一。本模块提供组件标识相关接口&#xff0c;可以获取指定id组件的属性&#xff0c;也提供向指定id组件发送事件的功能。 说明&#xff1a; 从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容…

最短路径Floyd算法

第一题&#xff1a;[USACO08OPEN] Clear And Present Danger S #include<bits/stdc.h> using namespace std; int n,m; int g[105][105]; int arr[100005]; long long sum; int main() {scanf("%d%d",&n,&m);for(int i1;i<m;i){scanf("%d"…

Pytorch实现卷积、Depthwise Convolution、分组卷积、动态卷积和转置卷积、反卷积、全卷积、空洞卷积、可变形卷积、深度可分离卷积等操作

底层是用img2col实现的&#xff0c;但是如果想用pytorch来实现&#xff0c;可以试试torch.unfold这个函数&#xff0c; torch.unfold torch.unfold可以按照指定维度&#xff0c;以一定的间隔将原始张量进行分片&#xff08;slicing&#xff09;&#xff0c;然后返回重整后的张…

开发知识点-前端-layUI

layui layertabletable render <script type"text/html" id"buttonTpl">{{# if(d.check true){ }}<button class"layui-btn layui-btn-xs">已审核</button>{{# } else { }}<button class"layui-btn layui-btn-prim…

Docker镜像导出/导入

Docker镜像导出/导入 一、前言 在实际操作中&#xff0c;为了便于docker镜像环境和服务配置的迁移&#xff0c;我们有时需要将已在测试环境主机上完成一系列配置的docker镜像或运行中的容器镜像导出&#xff0c;并传输到生产或其他目标环境主机上运行。为此&#xff0c;本文主…
最新文章