对RF 的基础理论不太熟,所以想把最近遇到的一些问题和自己的理解记录下来,也不知对错,反正是自己的领悟。
其实这是一个很基础的东西,题目应该这样叫会比较好:how to generate multi-tone in IQ RF system.
现在的SDR基本都是采用IQ的方法来产生射频信号,就是下图这种结构,先不考虑什么调制解调之类的,仅仅是考虑如何产生两个频点的或多个频点的RF信号。
RF=ACos(2πft+φ) = A*Cos(2πft)Cos(φ) - A*Sin(2πft)Sin(φ)
I=A*Cos(φ)
Q=A*Sine(φ)
RF=I*Cos(2πft) - Q*ASin(2πft)
要产生一个单频点的RF信号,只要让I或者Q其中一个为常量,而另一个为0即可。
比如:I=1, Q=0
所以当I=1, Q=0时, RF= Cos(2πft), 这是一个纯正弦的单频RF载波信号,频率为f,就是本振LO的频率。
我们看一下真实的代码,以及其所产生的真实信号。由于篇幅有限,截取了部分代码,附件是完整的代码,硬件是LimeSDR,本振频率设置为500MHz, 采样率为20MS/s, 这个tx_buffer的数据是这样的:IQIQIQIQIQIQIQ.....
const double sampleRate = 20e6;
const int tx_size = 8196 * 256;
float tx_buffer[2 * tx_size];
。。。。
std::cout << "SetSampleRate\n";
if (LMS_SetSampleRate(device, sampleRate, 0) != 0) error();
std::cout << "SetLOFrequency\n";
if (LMS_SetLOFrequency(device, LMS_CH_TX, 0, 500e6) != 0) error();
。。。。
for (int i = 0; i
tx_buffer[2 * i] =1; // I data
tx_buffer[2 * i + 1] = 0 ; //Q data
}
如果限定LO=F, 产生一个频率为 f2 = F + f1 的单频信号, 如何做呢?
其实这时只要输入的IQ数据代表的信号的频率为 f1 即可,IQ调制器最基本的功能是做频谱搬移(先不要考虑他的调制功能),如果 f1 为正频率, 往F 频点的右边搬移,如果 f1 为负频率, 往F的左边搬移。
那怎么理解这个负频率呢? 数据中怎么表示这个负频率呢?这里就要用到复信号了。
由欧拉公式可以得到:
所以上面的公式表明一个复信号可以由两个实信号来表示,至于这符号“j”,可以不用理他,他仅仅是个符号而已。我们可以将cosθ通过I路输入,而sinθ通过Q路输入。
产生IQ数据的代码:
for (int i = 0; i
tx_buffer[2 * i] = cos(2 * 3.14 *i / 256.0) ;
tx_buffer[2 * i + 1] = sin(2 * 3.14 *i / 256.0) ;
}
产生IQ数据的代码:
for (int i = 0; i
tx_buffer[2 * i] = cos(2 * 3.14 *i / 256.0) ;
tx_buffer[2 * i + 1] = -sin(2 * 3.14 *i / 256.0) ;
}
可以看到负频率仅仅是在正频率的数据的基础上取负而已。
这个频率值是怎么算出来的呢?
采样率fs为:20MS/s, 采样间隔 是 Δt = 1/fs =
tx_buffer[2 * i] = cos(2 * 3.14 *i / 256.0) ;
这是一个正弦信号,一个周期是T, 周期的倒数就是他的频率。
f = 1/T
tx_buffer[2 * i] = cos(2 * 3.14 *i / 256.0)
当 i 从0 到 256 正好完成一个周期, 也就是i = 256时:
cos0 = cos(2 * 3.14 * 256 / 256.0) = cos( 2 * 3.14) = cos2π
所以256个采样点正好完成一个周期
T = 256 * Δt = 256 / fs =0.0000128
f = 1/T = fs / 256 = 78125 Hz = 78.125kHz
产生IQ数据的代码:
这里幅度数据为0.5, 不是1, 这是因为硬件幅度太大容易造成失真。
for (int i = 0; i
tx_buffer[2 * i] = 0.5*cos(2 * 3.14 *i / 256.0) + 0.5*cos(2 * 3.14 *i / 64.0);
tx_buffer[2 * i + 1] = 0.5*sin(2 * 3.14 *i / 256.0) + 0.5*sin(2 * 3.14 *i / 64.0);
}
产生IQ数据的代码:
大注意到了什么,注意Q路的数据。思考一下为什么会这样?
for (int i = 0; i
tx_buffer[2 * i] = 0.5*cos(2 * 3.14 *i / 256.0) + 0.5*cos(2 * 3.14 *i / 256.0);
tx_buffer[2 * i + 1] = 0.5*sin(2 * 3.14 *i / 256.0) - 0.5*sin(2 * 3.14 *i / 256.0); //全是零,Q 全是0
}
可以看到,其实Q路数据全是零, 只有I路的数据,结果就变成实信号了,而非复信号。
结合前面的数据可以得到这样的一个结论:
完整的代码如下:
// LimeSDR_TX.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include
#include
#include
#include
#include
using namespace std;
lms_device_t* device;
const double sampleRate = 20e6;
bool running;
const int tx_size = 8196 * 256;
float tx_buffer[2 * tx_size];
int error()
{
cout << "ERROR: " << LMS_GetLastErrorMessage() << endl;
LMS_Close(device);
std::cin.get();
exit(-1);
}
void StreamTest()
{
lms_stream_t tx_stream;
tx_stream.channel = 0;
tx_stream.fifoSize = 256 * 1024;
tx_stream.throughputVsLatency = 0.5;
tx_stream.dataFmt = lms_stream_t::LMS_FMT_F32;
tx_stream.isTx = true;
lms_stream_meta_t meta_tx;
meta_tx.waitForTimestamp = false;
meta_tx.flushPartialPacket = false;
meta_tx.timestamp = 0;
float t = 0;
for (int i = 0; i
tx_buffer[2 * i] = 0.5*cos(2 * 3.14 *i / 256.0) + 0.5*cos(2 * 3.14 *i / 256.0);
tx_buffer[2 * i + 1] = 0.5*sin(2 * 3.14 *i / 256.0) - 0.5*sin(2 * 3.14 *i / 256.0);
}
LMS_SetupStream(device, &tx_stream);
LMS_StartStream(&tx_stream);
while (running)
{
int ret = LMS_SendStream(&tx_stream, tx_buffer, tx_size, &meta_tx, 1000);
if (ret != tx_size)
cout << "error: samples sent: " << ret << "/" << tx_size << endl;;
}
LMS_StopStream(&tx_stream);
LMS_DestroyStream(device, &tx_stream);
}
int main(int argc, char** argv)
{
int n = LMS_GetDeviceList(nullptr);
//
std::cout << "Test" << std::endl;
if (n > 0)
{
std::cout << "Open device\n";
if (LMS_Open(&device, NULL, NULL) != 0) //open first device
error();
std::cout << "Init device\n";
if (LMS_Init(device) != 0)
error();
std::cout << "Enable Channel\n";
if (LMS_EnableChannel(device, LMS_CH_TX, 0, true) != 0)
error();
std::cout << "SetSampleRate\n";
if (LMS_SetSampleRate(device, sampleRate, 0) != 0)
error();
std::cout << "SetLOFrequency\n";
if (LMS_SetLOFrequency(device, LMS_CH_TX, 0, 500e6) != 0)
error();
std::cout << "SetAtenna\n";
if (LMS_SetAntenna(device, LMS_CH_TX, 0, LMS_PATH_TX1) != 0) //TX1_1
error();
running = true;
std::thread thread = std::thread(StreamTest);
this_thread::sleep_for(chrono::seconds(10));
running = false;
thread.join();
std::cout << "Disable Channel\n";
if (LMS_EnableChannel(device, LMS_CH_TX, 0, false) != 0)
error();
if (LMS_Close(device) == 0)
cout << "Closed" << endl;
}
std::cin.get();
return 0;
}