相关检测实验
凉凉不冷 Lv4

相关检测方法实验

  • 相关检测方法

    相关检测技术是信号检测领域里一种重要工具,常用于从噪声背景中检测出有用得确定性信号。它利用确定信号在不同时刻得取值一般都具有较强的相关性,而干扰噪声因为随机性较强,不同时刻取值相关性较差的特性,把确定性信号和干扰噪声区分开来。

  • 实验背景

    拥有12个基站信号(即12个data文件,文件按实部+虚部进行交替存储,共30000行),计算各个基站的信号强度,并得出信号最强的三个基站,将其信号作为确定信号,即前文提到的确定信号序列

    具有噪声背景的实际信号序列为从已知的三组数据中任选其一的PSS文件(共4096行);

    将data文件与PSS做滑动相关计算,需要注意的是此处为先复数计算再取模,

    最终再根据得到的序列比较,PSS与哪一个基站的信号最相关。

基本流图

信号强度计算

  • 序号为的点,,其信号强度:

  • 信号强度(N为序列个数):

复数相乘

已知两复数分别为,,

则计算有:

由复数的特性可知,乘积的模等于模的乘积,所以可以在读取文件时便提前取模再存入数组;

滑动相关检测计算

具体算法见前篇文章

此处将其封装并稍微调整便于调用,注意为复数计算;

因原为计算两数的乘积,前文提到,在复数运算中,两复数的乘积的模等于两复数模的乘积(并且可以推导到个复数相乘),所以可以先将数据处理为复数的模后相乘再求和;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//*****************************************************************************************
//函数实现:X[]与Y[]滑动相关计算,得出Z[]相关序列,得出确定信号的及其起始位置,返回其起始位置;
//函数入口:x[m] 基站确定信号;
// y[n] 具有噪声的确定信号;
// m 基站信号的有效数据个数,宏定义;
// n 携带噪声的确定信号的有效数据个数,宏定义;
// Z[] 相关序列,为全局变量;
// S 生成的序列个数 - 1;S < 3;
//函数出口:maxpos 确定信号的起始位置;
//*****************************************************************************************
int slide(double x[],double y[],int S)
{
int i = 0;
int j = 0;
int kz = 0;//滑动相关计算序列哨兵
double max[3] = { 0 };
int maxpos = 0;//确定信号的起始位置,也就是最大值的位置

for (kz = 0; kz < k; kz++)
{
for (i = 0; i < m ; i++)
{
Z[S][kz] += x[i] * y[i + j];
}
j++;
if (Z[S][kz] >= max[S])
{
max[S] = Z[S][kz];
maxpos = kz;
}
}

return maxpos;
}

读文件

使用 fopen() 函数来打开已有文件,

1
FILE *fopen( const char *filename, const char *mode );

filename 是字符串,用来命名文件,mode的值如下表:

模式 描述
r 打开一个已有的文本文件(只读模式)
w 打开一个文本文件,允许写入文件。若文件不存在,则会创建一个新文件。在这里程序会从文件的开头写入内容。如果文件存在,则会被截断为零长度,重新写入(只写模式)
a 打开一个文本文件,以追加模式写入文件。若文件不存在,则会创建一个新文件。在这里程序会在已有的文件内容中追加内容(追加写入模式)
r+ 打开一个文本文件,允许读写文件(读写模式)
w+ 打开一个文本文件,允许读写文件。若文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件
a+ 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式

如需处理二进制文件,在启动模式后添加 b

文件使用完毕后,务必关闭文件,使用如下函数:

1
int fclose( FILE *fp);

若成功关闭文件, fclose() 函数返回零,如果关闭文件时发生错误,函数返回 EOF ,此函数会清空缓冲区中的数据并关闭文件,释放用于该文件的所有内存。

此实验中使用 fread 函数来读文件,其原型如下:

1
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

ptr 指向带有最小尺寸 字节的内存块的指针;

size 是要读取的每个元素的大小,以字节为单位;

nmemb 是元素的个数,每个元素的大小为 字节;

stream 指向 对象的指针;

成功读取的元素总数会以 对象返回,该对象是一个整型数据类型,若总数与 参数不同,则可能发生了一个错误或者达到了文件末尾。

在VSCode中使用fopensprintf会产生报错,可以使用以下方式关闭此报错:

1
#pragma warning(disable:4996);

C4996

由于需要打开的data文件与PSS文件长度,返回值,需要存入的对象以及文件个数均不一致,为了简便(就是想偷懒不想写成一个函数…)为data文件与PSS文件分别写了一个函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//*****************************************************************************************
//函数实现:打开文件命名格式一致的基站信号文件,求模后存入数组
//函数入口:i 文件编号;
// *fp[] 文件指针;
//函数出口:成功返回此基站的信号强度;失败 -1;
//*****************************************************************************************
double OpData(int i, FILE *fp[12])
{
char sBuffer[100];//系列文件名
double Q = 0, R = 0;//临时存储数据
double Sum = 0;//信号强度为求模后的求模运算
sprintf(sBuffer, "E:\\datas\\data%d.txt", i);
if ((fp[i] = fopen(sBuffer, "r")) == NULL)
{
printf("Cannot open file data%d.txt\r\n",i);
return -1;
}
fseek(fp[i], 0, SEEK_SET);
for (int j = 0; j < n; j++)
{
fscanf(fp[i], "%lf %lf", &Q,&R);
Y[i][j] = sqrt(pow(Q, 2) + pow(R, 2));
Sum += Y[i][j];
}
fclose(fp[i]);
return Sum;
}

//*****************************************************************************************
//函数实现:打开文件命名格式一致的含噪声的确定信号文件,求模后存入数组
//函数入口:文件编号
//函数出口:成功 0;失败 -1;
//*****************************************************************************************
int OpPSS(int i, FILE* fp)
{
char sBuffer[100];//系列文件名
double Q = 0, R = 0;//临时存储数据
sprintf(sBuffer, "E:\\datas\\PSS%d.txt", i);
if ((fp = fopen(sBuffer, "r")) == NULL)
{
printf("Cannot open file data%d.txt\r\n", i);
return -1;
}
fseek(fp, 0, SEEK_SET);
for (int j = 0; j < 2 * m; j += 2)
{
fscanf(fp, "%lf %lf", &Q, &R);
X[j / 2] = sqrt(pow(Q, 2) + pow(R, 2));
}
fclose(fp);
return 0;
}

其中的作用就是临时存储读出的实部与虚部,求模后再存入数组;

获取信号最强的三个基站

由于直接进行排序无法同时记录三个基站的位序,如果使用结构体直接进行排序应该会更加简单,并提升运算速度;

但在此处我们用一个长度为3的数组几下三次的最大值,便可轻易地得出三个最大值及其下标,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//*****************************************************************************************
//函数实现:依次打开12个基站的数据文件,并进行信号强度计算按从大到小排序得出信号最大的三个基站
//函数入口:无
//函数出口:成功 0;
// 三个基站的标号用全局变量存储 Station[3]
//*****************************************************************************************
int GetStations()
{
double Intensity[12] = { 0 };//存储各个基站的信号强度
int i = 0;//基站标号
int j = 0;
FILE* fp[12] = { NULL };//文件指针
double max[3] = { 0 };//记录三次各自的最大值
for (i = 0; i < 12; i++)
{
Intensity[i] = OpData(i, fp);
}
for (j = 0; j < 3; j++)
{
for (i = 0; i < 12; i++)
{
switch (j)
{
case 0:
{
if (Intensity[i] >= max[j])
{
max[j] = Intensity[i];
Station[j] = i;
}
break;
}
default://除开第一次获取的整个序列的最大值,其他每次一循坏的“最大值”需小于上一次循环的“最大值”
{
if(Intensity[i] >= max[j] && Intensity[i] < max[j - 1])
{
max[j] = Intensity[i];
Station[j] = i;
}
break;
}
}
}
}
return 0;
}

得出相关性最强的基站

数组拓展为二维数组以存储三个基站分别进行相关检测计算后的结果,再根据每一次相关检测的结果,比较他们最大确定信号(也就是序列中模值的最大值),得出这一个基站对应的标号(预先已经存储在了Station这一个全局变量内),具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//*****************************************************************************************
//函数实现:将信号最强的三组基站用来与PSS进行相关检测,得出信号最强的基站标号
//函数入口:无
//函数出口:返回信号最强基站的标号
//*****************************************************************************************
int MaxIntensity()
{
int maxpos[3];//分别记录三个基站的最大确定信号起始值
double max = 0;//暂时存储最大值
int pos = 0;//信号最强基站的标号
int j = 0;//确定信号的起始位置的角标
for (int i = 0; i < 3; i++)//i的作用是记录当前是第几次滑动相关检测,同时可以作为Station数组的下标
{
maxpos[i] = slide(X, Y[Station[i]], i);
if (Z[i][maxpos[i]] >= max)
{
max = Z[i][maxpos[i]];
pos = Station[i];
j = i;
}
}
printf("相关性最强的基站确定信号的起始位置为%d\r\n", maxpos[j]);
return pos;
}

主函数及宏定义等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
已知12个基站信号数据 data0.txt —— data 11.txt
以及3个含噪声的确定信号数据 PSS0 —— PSS2.txt
比较得出三个信号最强的基站用来与任选一组的PSS信号进行相关检测
得出与PSS信号最相关的基站
*/
#include <stdio.h>
#include <math.h>
#include <string.h>

#define n 30000/2
#define m 4096/2
#define k n-m+1

#pragma warning(disable:4996)
#pragma warning(disable:6031)

int slide(double x[], double y[]);
double OpData(int i, FILE* fp[12]);
int OpPSS(int i, FILE* fp);
int GetStations();
int MaxIntensity();

double Z[3][k] = { 0 };
double X[m] = { 0 };// 具有噪声的确定信号的模组成的数组 PSS
double Y[12][n] = { 0 }; // 基站确定信号的模组成的数组 DATA
int Station[3] = { 0 };//用于存储信号最强的三个基站标号

int main(void)
{
int pos = 0;//最强信号基站标号
FILE* fp1[12] = { NULL };
FILE* fp = NULL;
OpPSS(0, fp);
GetStations();
pos = MaxIntensity();
printf("相关性最强的基站为%d号基站\r\n", pos);
return 0;
}

参考网站

C 文件读写 | 菜鸟教程 (runoob.com)

附.实验数据

Lucy

 Comments