SDR如何产生两个频点(或多个频点multi-tone)RF波形

对RF 的基础理论不太熟,所以想把最近遇到的一些问题和自己的理解记录下来,也不知对错,反正是自己的领悟。

其实这是一个很基础的东西,题目应该这样叫会比较好:how to generate multi-tone in IQ RF system.

 

现在的SDR基本都是采用IQ的方法来产生射频信号,就是下图这种结构,先不考虑什么调制解调之类的,仅仅是考虑如何产生两个频点的或多个频点的RF信号。

SDR如何产生两个频点(或多个频点multi-tone)RF波形_第1张图片

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信号如何产生

要产生一个单频点的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

    }

SDR如何产生两个频点(或多个频点multi-tone)RF波形_第2张图片

另外一种产生单频点的情况:

如果限定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) ;

    }

SDR如何产生两个频点(或多个频点multi-tone)RF波形_第3张图片

负频率的情况:

  产生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) ;

    }

SDR如何产生两个频点(或多个频点multi-tone)RF波形_第4张图片

 

可以看到负频率仅仅是在正频率的数据的基础上取负而已。

这个频率值是怎么算出来的呢?

采样率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

 

双频点的RF信号产生

双频点的信号仅仅是把两个频点的信号相加即可,就这么简单

 

非镜像

  产生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);

    }

SDR如何产生两个频点(或多个频点multi-tone)RF波形_第5张图片

 

镜像的情况

  产生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

    }

SDR如何产生两个频点(或多个频点multi-tone)RF波形_第6张图片

可以看到,其实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;
}

你可能感兴趣的:(SDR,IQ,镜像频率)