├── .gitignore
├── LICENSE
├── README.en.md
├── README.md
├── doc
└── img
│ ├── Channel.svg
│ ├── Document.svg
│ ├── Example.svg
│ ├── Fundamental.svg
│ ├── Library.svg
│ ├── Probe.svg
│ ├── Receiver.svg
│ ├── SystemStructure.svg
│ ├── Tool.svg
│ └── Transmitter.svg
├── example
├── Example1.m
├── Example2.m
├── FreeEnvironment.m
├── LoadEnvironment.m
├── OAMFocusingExample.m
└── RFSoCSinglePathExample.m
├── lib
├── BaseSymbolSample.m
├── CarrierGen.m
├── Channel.m
├── ChannelConvolution.m
├── ChannelShaping.m
├── Demodulation.m
├── DigitalSignalGen.m
├── FrameDecapsulate.m
├── FrameEncapsulate.m
├── IQDemixing.m
├── IQMixing.m
├── InitAnalogSignal.m
├── InitChannelImpulseResponse.m
├── InitDigitalSignal.m
├── Modulation.m
├── OFDMLoad.m
├── OFDMUnload.m
├── OrthogonalMatrixLoad.m
├── OrthogonalMatrixUnload.m
└── PreambleGen.m
├── probe
├── ProbeBitError.m
├── ProbeChannelImpulse.m
├── ProbeConstellation.m
├── ProbeDelay.m
├── ProbeSignalPower.m
├── ProbeSpectrum.m
├── ProbeWave.m
└── UnitConvert.m
└── tool
├── DemixingResampleFilter.m
├── IdealOAMChannel.m
├── IdealOAMVisualizer.m
├── OAMFocusing.m
├── OFDMVisualizer.m
├── ShapingFilterDesign.m
├── SignalResample.m
└── ZadoffChuGen.m
/.gitignore:
--------------------------------------------------------------------------------
1 | doc/docsrc
2 | *.mat
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Tifer King
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.en.md:
--------------------------------------------------------------------------------
1 | # MIMO Simulation Toolbox
2 |
3 | [中文](README.md)
4 | [English](README.en.md)
5 |
6 | The documentation and example programs are under development, please refer to the comments inside the source code first, thank you~
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 多通道通信仿真工具箱
2 |
3 | [中文](README.md)
4 | [English](README.en.md)
5 |
6 | - [多通道通信仿真工具箱](#多通道通信仿真工具箱)
7 | - [简介](#简介)
8 | - [仿真工具结构](#仿真工具结构)
9 | - [模块目录及简介](#模块目录及简介)
10 | - [探针目录及简介](#探针目录及简介)
11 | - [工具目录及简介](#工具目录及简介)
12 | - [样例目录及简介](#样例目录及简介)
13 |
14 |
15 | ## 简介
16 |
17 | 本项目主要目标是构建一个基于MATLAB的便捷多通道仿真工具箱,工具会包含一些我在实际工程中做通信系统时对于通信的理解。当然它也会包含一些好用和易用的小工具,方便你来构筑自己的多通道通信仿真程序。同时我也会提供一些样例和说明,这些样例会包含一些经典的通信场景,并且我也会将我自己的一些研究内容做成样例放在里边。总之希望这个工程能在你了解通信工程的道路上起到作用。如果你有任何问题或者改进意见及建议都可以通过项目议题与我讨论,欢迎~
18 |
19 | ## 仿真工具结构
20 |
21 | 本项目主要旨在对多通道通信系统的各个部分实际工作状态进行仿真,其中各个模块的分布也尽量按照实际当中的通信系统进行包装。当然根据不同的研究内容,你可以自行增加或裁剪部分模块,更聚焦于其中一部分的功能仿真。不过我认为对整体进行仿真会加深对于通信系统的理解。如下图所示,一般来说对于通信系统来说有三大部分需要进行仿真:发射机、信道、接收机。而这三个部分又分别对应许多模块。在不同的场景下利用不同的手段和技术来解决相应的问题,确保接收端能够接收到有用的信息是通信的终极目的。而本项目也会从这三个部分入手尽量去构建一个完整的通信系统仿真。
22 |
23 | 
24 |
25 | 当然,在本项目中的文件目录并不是按照系统结构来的,而是根据文件的不同功能属性进行组合的。本项目中的文件主要分为:
26 |
27 | -  doc: 项目各模块的介绍文档以及使用手册
28 | -  lib: 组成通信系统的模块文件
29 | -  probe: 用于分析系统状态和观察系统运行的探针
30 | -  tool: 一些可能会在构建通信系统时用到的辅助工具
31 | -  example: 一些基于该项目的样例工程
32 |
33 | 本项目也会不断的增加新的模块,完善现有模块,希望能提供一个强大且易用的仿真工具集。
34 |
35 | ## 模块目录及简介
36 |
37 | -  ChannelShaping: 信道脉冲成型模块,用来做基带信号的脉冲成型,一般用于解决码间串扰。
38 | -  DigitalSignalGen: 数字序列生成器,可以生成特定测试序列或者随机的二进制序列。
39 | -  FrameEncapsulate: 组帧模块,能够将特定的帧头与数据拼接形成一个数据帧。
40 | -  IQMixing: 正交上混频器,利用正交信号对基带进行上混频。
41 | -  Modulation: 调制器,将二进制序列调制成复平面上的星座符号。
42 | -  OrthogonalMatrixLoad: 正交矩阵加载器,用于对发射的多通道信号进行正交矩阵的加载,使其发射的信号具有正交性。这个模块通常用在涡旋电磁波通信等特殊场景中。
43 | -  PreambleGen: 帧头生成模块,利用该模块可以生成包含特定自相关或互相关序列的帧头。
44 | -   CarrierGen: 载波发生器,利用该模块生成载波信号并送入混频器,可以同时用在发射端和接收端。
45 | -  Channel: 信道仿真模块,根据信道的冲激响应和噪声水平实现对信道传输的仿真。
46 | -  BaseSymbolSample: 基带符号采样器,根据特定采样率对基带的符号进行采样并输出其对应的复数值。
47 | -  Demodulation: 解调器,将复平面上的星座信号进行判决,将信号还原成为数字信号。
48 | -  FrameDecapsulate: 解帧模块,对收到的数据进行均衡和解帧头,回复字符数据。
49 | -  IQDemixing: 正交下混频器,利用正交信号对射频信号进行下混频。
50 | -  OrthogonalMatrixUnload: 正交矩阵卸载器,将多通道中的正交矩阵进行卸载,恢复成独立的数据通道。这个模块通常用在涡旋电磁波通信等特殊场景中。
51 | -  ChannelConvolution: 信道卷积核心,对输入信道的信号与信道进行冲击响应的核心计算函数。可以通过优化该函数提升信道仿真性能。
52 | -  InitAnalogSignal: 模拟信号结构体初始化核心,该函数定义了一种包含模拟信号基础特征等信息的结构体。具体结构和定义请参阅其说明文档。
53 | -  InitChannelImpulseResponse: 信道冲击响应结构体初始化核心,该函数定义了一种包含信道冲击响应基础特征等信息的结构体。具体结构和定义请参阅其说明文档。
54 | -  InitDigitalSignal: 数字信号结构体初始化核心,该函数定义了一种包含数字信号基础特征等信息的结构体。具体结构和定义请参阅其说明文档。
55 |
56 |
57 | ## 探针目录及简介
58 |
59 | -  ProbeBitError: 误码率探针,该探针用于比对发射信号与接收信号的二进制比特流是否一致,判断其产生的误码数量和误码率。
60 | -  ProbeChannelImpulse: 信道响应探针,图像化显示信道冲击响应。
61 | -  ProbeConstellation: 星座图探针,图像化显示信号的星座图,并在图中标出预设星座点的位置。
62 | -  ProbeDelay: 延迟探针,可以比较两个信号之间的时间差,也可以对比同一组信号经过传输后产生的延迟时间。
63 | -  ProbeSignalPower: 功率探针,检测目标信号的功率。
64 | -  ProbeSpectrum: 频谱探针,图像化显示信号的频谱,可自行调节频率显示范围及频谱的分辨率带宽(RBW)。
65 | -  ProbeWave: 波形探针,图像化显示信号的波形。
66 | -  UnitConvert: 自动单位转换,对输入数据进行分析,找出最适合其显示范围的缩放倍率和单位,使图像显示的坐标轴更美观。
67 |
68 | ## 工具目录及简介
69 |
70 | -  DemixingResampleFilter: 下混频滤波器设计工具,通过输入原始信号采样率和目标采样率,来设计下混频时的抗混叠滤波器。
71 | -  IdealOAMChannel: 理想涡旋电磁波(OAM)信道脉冲响应生成器,通过输入收发天线的参数和位置,生成理想涡旋电磁波的信道脉冲响应,天线是均匀圆阵列天线(UCA)。
72 | -  ShapingFilterDesign: 脉冲成型滤波器设计工具,针对基带信号的符号速率和目标采样率设计对应的脉冲成型滤波器。
73 | -  SignalResample: 信号重采样工具,对信号进行采样率变换以方便运算。
74 | -  ZadoffChuGen: Zadoff-Chu序列生成工具,生成一组不同根的Zadoff-Chu序列。
75 |
76 | ## 样例目录及简介
77 |
78 | -  Example1: 一个简单的基于涡旋电磁波(OAM)的通信系统仿真。
79 | -  LoadEnvironment: 加载样例运行环境,需要加在样例运行最开始的地方。
80 | -  FreeEnvironment: 释放样例运行环境,在样例运行结束后清理环境,一般加在样例运行末尾。
--------------------------------------------------------------------------------
/doc/img/Channel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/Document.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/Example.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/Fundamental.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/Library.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/Probe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/Receiver.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/SystemStructure.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/Tool.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/img/Transmitter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/Example1.m:
--------------------------------------------------------------------------------
1 | %Example1 OAM communications in ideal channel.
2 | %Author:
3 | % Tifer King
4 | %License:
5 | % Please refer to the 'LICENSE' file included in the root directory
6 | % of the project.
7 |
8 | LoadEnvironment;
9 |
10 | %% Simulation config
11 | DataLegth = 800;
12 | ChannelNum = 8;
13 | ChannelNoise = -150;
14 | FramePreambleLength = 67;
15 | SequenceMode = "prbs31";
16 | AnalogSignalPreset = "50Ohm-2.5V";
17 | DigitalSignalPreset = "LVCMOS3V3";
18 | BaseAnalogSampleRate = 1e9;
19 | BaseBitRate = 1e8;
20 | BaseModulationMode = "QPSK";
21 | BaseShapingBeta = 0.25;
22 | BaseShapingSpan = 8;
23 | BaseOrthogonalPreset = "OAM";
24 | CarrierFrequency = 5.8e9;
25 | CarrierSampleRate = 1000e9;
26 | MaxSimulationFrequency = CarrierFrequency * 5;
27 | %MaxChannelImpulseEndurance = 12 / (pi * MaxSimulationFrequency);
28 | AntennaRadiusTx = 0.5;
29 | AntennaRadiusRx = 0.5;
30 | AntennaPositionRx = [0; 0; 10];
31 | AntennaEulerianAngleRx = [0; 0; 0];
32 | BeamDisplayRange = 'Auto';
33 |
34 | %% Simulation Flow
35 | close all;
36 | BaseData = DigitalSignalGen(ChannelNum, DataLegth, BaseBitRate, SequenceMode, DigitalSignalPreset);
37 | IQBase = Modulation(BaseData, BaseModulationMode, AnalogSignalPreset);
38 | ProbeConstellation(IQBase, false, BaseModulationMode,'Transmitted IQ Base');
39 | OrthogonalBase = OrthogonalMatrixLoad(IQBase, BaseOrthogonalPreset);
40 | FramePreamble = PreambleGen(ChannelNum, FramePreambleLength, IQBase.SampleRate, @ZadoffChuGen, AnalogSignalPreset);
41 | FrameSignal = FrameEncapsulate(OrthogonalBase, FramePreamble);
42 | ShapingFilter = ShapingFilterDesign(BaseShapingBeta, BaseShapingSpan, BaseAnalogSampleRate, IQBase.SampleRate);
43 | ShapedSignal = ChannelShaping(FrameSignal, ShapingFilter, BaseAnalogSampleRate);
44 | ProbeSpectrum(ShapedSignal, false, 'Shaped Signal Spectrum');
45 | CarrierSignal = CarrierGen(CarrierFrequency, ChannelNum, CarrierSampleRate, ShapedSignal.TimeStart, ShapedSignal.TimeEndurance, AnalogSignalPreset);
46 | ProbeSpectrum(CarrierSignal, false, 'Carrier Signal Spectrum', 'Span', [5.75e9 5.85e9]);
47 | TransmiteSignal = IQMixing(ShapedSignal, CarrierSignal);
48 | ProbeWave(TransmiteSignal, true, 'Transmitted Signal');
49 | ProbeSpectrum(TransmiteSignal, false, 'Transmitted Spectrum');
50 | TxPower = ProbeSignalPower(TransmiteSignal, 'TxPower');
51 |
52 | %ChannelImpulse = InitChannelImpulseResponse(ChannelNum, ChannelNum, 0, MaxChannelImpulseEndurance, CarrierSampleRate, 0, MaxSimulationFrequency, 'Gaussian');
53 | ChannelImpulse = IdealOAMChannel(ChannelNum, ChannelNum, AntennaRadiusTx, AntennaRadiusRx, AntennaPositionRx, AntennaEulerianAngleRx, MaxSimulationFrequency, CarrierFrequency, CarrierSampleRate, true);
54 | IdealOAMVisualizer(ChannelNum, ChannelNum, AntennaRadiusTx, AntennaRadiusRx, AntennaPositionRx, AntennaEulerianAngleRx, CarrierFrequency, 1, true, BeamDisplayRange);
55 | ProbeChannelImpulse(ChannelImpulse);
56 | RecivedSignal = Channel(TransmiteSignal, ChannelImpulse, ChannelNoise);
57 |
58 | ProbeWave(RecivedSignal, true, 'Recived Signal');
59 | ProbeSpectrum(RecivedSignal, false, 'Recived Spectrum', 'Span', [5.6e9 6.0e9]);
60 | RxPower = ProbeSignalPower(RecivedSignal, 'RxPower');
61 | CarrierSignal = CarrierGen(CarrierFrequency, ChannelNum, CarrierSampleRate, RecivedSignal.TimeStart, RecivedSignal.TimeEndurance, AnalogSignalPreset);
62 | BaseSignal = IQDemixing(RecivedSignal, CarrierSignal, BaseAnalogSampleRate, @DemixingResampleFilter);
63 | [PayloadSiganl, ChannelEstimation] = FrameDecapsulate(BaseSignal, FramePreamble, BaseData.TimeEndurance, ShapingFilter);
64 | DeorthogonalBase = OrthogonalMatrixUnload(PayloadSiganl, BaseOrthogonalPreset);
65 | ProbeConstellation(DeorthogonalBase, false, BaseModulationMode, 'Recived IQ Base');
66 | ProbeSpectrum(DeorthogonalBase, false, 'Recived IQ Base Spectrum');
67 | SymbolSignal = BaseSymbolSample(DeorthogonalBase, IQBase.SampleRate);
68 | ProbeConstellation(SymbolSignal, false, BaseModulationMode, 'Decoded IQ Base');
69 | RecivedData = Demodulation(SymbolSignal, BaseModulationMode, DigitalSignalPreset);
70 | ErrorRate = ProbeBitError(BaseData, RecivedData, 'Recv');
71 |
72 | FreeEnvironment;
73 |
--------------------------------------------------------------------------------
/example/Example2.m:
--------------------------------------------------------------------------------
1 | %Example2 OFDM-OAM communications in ideal channel.
2 | %Author:
3 | % Tifer King
4 | %License:
5 | % Please refer to the 'LICENSE' file included in the root directory
6 | % of the project.
7 |
8 | LoadEnvironment;
9 |
10 | %% Simulation config
11 | DataLegth = 8000;
12 | ChannelNum = 8;
13 | ChannelNoise = -200;
14 | FramePreambleLength = 400;
15 | SequenceMode = "prbs31";
16 | AnalogSignalPreset = "50Ohm-2.5V";
17 | DigitalSignalPreset = "LVCMOS3V3";
18 | BaseAnalogSampleRate = 10e9;
19 | BaseBitRate = 10e8;
20 | BaseModulationMode = "QPSK";
21 | BaseShapingBeta = 0.25;
22 | BaseShapingSpan = 8;
23 | BaseOrthogonalPreset = "OAM";
24 | CarrierFrequency = 5.8e9;
25 | CarrierSampleRate = 1000e9;
26 | MaxSimulationFrequency = CarrierFrequency * 5;
27 | OFDMSubCarrierNum = 400;
28 | OFDMCyclicPrefixNum = 4;
29 | AntennaRadiusTx = 0.5;
30 | AntennaRadiusRx = 0.5;
31 | AntennaPositionRx = [0; 0; 10];
32 | AntennaEulerianAngleRx = [0; 0; 0];
33 | BeamDisplayRange = 'Auto';
34 |
35 | %% Simulation Flow
36 | close all;
37 | BaseData = DigitalSignalGen(ChannelNum, DataLegth, BaseBitRate, SequenceMode, DigitalSignalPreset);
38 | IQBase = Modulation(BaseData, BaseModulationMode, AnalogSignalPreset);
39 | ProbeConstellation(IQBase, false, BaseModulationMode,'Transmitted IQ Base');
40 | OFDMBase = OFDMLoad(IQBase, OFDMSubCarrierNum, OFDMCyclicPrefixNum);
41 | OrthogonalBase = OrthogonalMatrixLoad(OFDMBase, BaseOrthogonalPreset);
42 | FramePreamble = PreambleGen(ChannelNum, FramePreambleLength, IQBase.SampleRate, @ZadoffChuGen, AnalogSignalPreset);
43 | FrameSignal = FrameEncapsulate(OrthogonalBase, FramePreamble);
44 | ShapingFilter = ShapingFilterDesign(BaseShapingBeta, BaseShapingSpan, BaseAnalogSampleRate, IQBase.SampleRate);
45 | ShapedSignal = ChannelShaping(FrameSignal, ShapingFilter, BaseAnalogSampleRate);
46 | OFDMVisualizer(IQBase, OFDMSubCarrierNum, OFDMCyclicPrefixNum, ShapingFilter, BaseAnalogSampleRate);
47 | ProbeSpectrum(ShapedSignal, false, 'Shaped Signal Spectrum');
48 | CarrierSignal = CarrierGen(CarrierFrequency, ChannelNum, CarrierSampleRate, ShapedSignal.TimeStart, ShapedSignal.TimeEndurance, AnalogSignalPreset);
49 | ProbeSpectrum(CarrierSignal, false, 'Carrier Signal Spectrum', 'Span', [(CarrierFrequency - 0.5e9) (CarrierFrequency + 0.5e9)]);
50 | TransmiteSignal = IQMixing(ShapedSignal, CarrierSignal);
51 | ProbeWave(TransmiteSignal, true, 'Transmitted Signal');
52 | ProbeSpectrum(TransmiteSignal, false, 'Transmitted Spectrum');
53 | TxPower = ProbeSignalPower(TransmiteSignal, 'TxPower');
54 |
55 | ChannelImpulse = IdealOAMChannel(ChannelNum, ChannelNum, AntennaRadiusTx, AntennaRadiusRx, AntennaPositionRx, AntennaEulerianAngleRx, MaxSimulationFrequency, CarrierFrequency, CarrierSampleRate, true);
56 | for index = 0 : ChannelNum / 2
57 | IdealOAMVisualizer(ChannelNum, ChannelNum, AntennaRadiusTx, AntennaRadiusRx, AntennaPositionRx, AntennaEulerianAngleRx, CarrierFrequency, index, true, BeamDisplayRange);
58 | end
59 | ProbeChannelImpulse(ChannelImpulse);
60 | RecivedSignal = Channel(TransmiteSignal, ChannelImpulse, ChannelNoise);
61 |
62 | ProbeWave(RecivedSignal, true, 'Recived Signal');
63 | ProbeSpectrum(RecivedSignal, false, 'Recived Spectrum');
64 | RxPower = ProbeSignalPower(RecivedSignal, 'RxPower');
65 | CarrierSignal = CarrierGen(CarrierFrequency, ChannelNum, CarrierSampleRate, RecivedSignal.TimeStart, RecivedSignal.TimeEndurance, AnalogSignalPreset);
66 | BaseSignal = IQDemixing(RecivedSignal, CarrierSignal, BaseAnalogSampleRate, @DemixingResampleFilter);
67 | [PayloadSiganl, ChannelEstimation] = FrameDecapsulate(BaseSignal, FramePreamble, BaseData.TimeEndurance, ShapingFilter);
68 | DeorthogonalBase = OrthogonalMatrixUnload(PayloadSiganl, BaseOrthogonalPreset);
69 | ProbeConstellation(DeorthogonalBase, false, BaseModulationMode, 'Recived IQ Base');
70 | ProbeSpectrum(DeorthogonalBase, false, 'Recived IQ Base Spectrum');
71 | SymbolSignal = BaseSymbolSample(DeorthogonalBase, IQBase.SampleRate);
72 | RecoveredSignal = OFDMUnload(SymbolSignal, OFDMSubCarrierNum, OFDMCyclicPrefixNum);
73 | ProbeConstellation(RecoveredSignal, false, BaseModulationMode, 'Decoded IQ Base');
74 | RecivedData = Demodulation(RecoveredSignal, BaseModulationMode, DigitalSignalPreset);
75 | ErrorRate = ProbeBitError(BaseData, RecivedData, 'Recv');
76 |
77 | FreeEnvironment;
78 |
--------------------------------------------------------------------------------
/example/FreeEnvironment.m:
--------------------------------------------------------------------------------
1 | %FreeEnvironment
2 | %Author:
3 | % Tifer King
4 | %License:
5 | % Please refer to the 'LICENSE' file included in the root directory
6 | % of the project.
7 |
8 | if(isunix())
9 | rmpath('./../lib');
10 | rmpath('./../probe');
11 | rmpath('./../tool');
12 | else
13 | rmpath('.\..\lib');
14 | rmpath('.\..\probe');
15 | rmpath('.\..\tool');
16 | end
--------------------------------------------------------------------------------
/example/LoadEnvironment.m:
--------------------------------------------------------------------------------
1 | %LoadEnvironment
2 | %Author:
3 | % Tifer King
4 | %License:
5 | % Please refer to the 'LICENSE' file included in the root directory
6 | % of the project.
7 |
8 | if(isunix())
9 | addpath('./../lib');
10 | addpath('./../probe');
11 | addpath('./../tool');
12 | else
13 | addpath('.\..\lib');
14 | addpath('.\..\probe');
15 | addpath('.\..\tool');
16 | end
17 |
--------------------------------------------------------------------------------
/example/OAMFocusingExample.m:
--------------------------------------------------------------------------------
1 | %OAMFocusingExample
2 | %Author:
3 | % Tifer King
4 | %License:
5 | % Please refer to the 'LICENSE' file included in the root directory
6 | % of the project.
7 |
8 | LoadEnvironment;
9 |
10 | %% Config
11 | TxChannelNum = 16;
12 | TxAntennaRadius = 1;
13 | TxBFElementNum = 19;
14 | TxBFDistance = 0.1;
15 | TxFocusingAngle = atan(0.1);
16 | Frequency = 5.8e9;
17 | RxAntennaRadius = 0.36;
18 | MaxMode = 4;
19 |
20 | %% Display range initialization
21 | TopRange = [(TxAntennaRadius * 2); (TxAntennaRadius * 2); round(TxAntennaRadius ./ tan(TxFocusingAngle) .* 1.4); 0];
22 | BottomRange = [0; 0; round(TxAntennaRadius ./ tan(TxFocusingAngle) .* 0.6); 0];
23 | DisplayRange = [TopRange BottomRange (TopRange - BottomRange) ./ [500; 500; 1; 1]];
24 | DisplayRange(3,3) = 1;
25 |
26 | % XZ Plane calculate
27 | XRange = [DisplayRange(1,2) : DisplayRange(1,3) : DisplayRange(1,1)];
28 | ZRange = [DisplayRange(3,2) : DisplayRange(3,3) : DisplayRange(3,1)];
29 |
30 | [XZReceiveATotal, IndexMaxRecvA] = OAMFocusing(TxChannelNum, TxAntennaRadius, TxBFElementNum, TxBFDistance, TxFocusingAngle, Frequency, 'A-OAM', MaxMode, DisplayRange);
31 | [XZReceiveDTotal, IndexMaxRecvD] = OAMFocusing(TxChannelNum, TxAntennaRadius, 1, TxBFDistance, TxFocusingAngle, Frequency, 'D-OAM', MaxMode, DisplayRange);
32 | [XZReceiveHTotal, IndexMaxRecvH] = OAMFocusing(TxChannelNum, TxAntennaRadius, TxBFElementNum, TxBFDistance, TxFocusingAngle, Frequency, 'H-OAM', MaxMode, DisplayRange);
33 |
34 | RxIndex = round(RxAntennaRadius/DisplayRange(1,3));
35 |
36 | Config = ["o" "x" "^" "square"; "#77428D" "#51A8DD" "#EBB471" "#F596AA"; "#77428D" "#51A8DD" "#EBB471" "#F596AA"];
37 |
38 | %% FIG 1
39 | figure;
40 | for i = 1 : MaxMode
41 | plot(ZRange, XRange(IndexMaxRecvA(:,:,:,i)),'-' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
42 | hold on;
43 |
44 | plot(ZRange, XRange(IndexMaxRecvD(:,:,:,i)),'--' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
45 | hold on;
46 | end
47 |
48 | lgd = legend('A-OAM$(l=1)$','D-OAM$(l=1)$','A-OAM$(l=2)$','D-OAM$(l=2)$','A-OAM$(l=3)$','D-OAM$(l=3)$','A-OAM$(l=4)$','D-OAM$(l=4)$', ...
49 | 'Orientation','horizontal','NumColumns',2,'Location','NorthWest','Interpreter','latex');
50 | lgd.ItemTokenSize = [25 15];
51 | ylim([0 0.65]);
52 | ylabel('Best-receiving Radius $r_{l}$ (m)','Interpreter','latex');
53 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex');
54 | grid on;
55 | drawnow;
56 |
57 | %% FIG 2
58 | figure;
59 | for i = 1 : MaxMode
60 | plot(ZRange, pow2db(diag(abs(XZReceiveATotal(IndexMaxRecvA(:,:,:,i), :, :, i)))) + 30,'-' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
61 | hold on;
62 |
63 | plot(ZRange, pow2db(diag(abs(XZReceiveDTotal(IndexMaxRecvD(:,:,:,i), :, :, i)))) + 30,'--' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
64 | hold on;
65 | end
66 |
67 | lgd = legend('A-OAM$(l=1)$','D-OAM$(l=1)$','A-OAM$(l=2)$','D-OAM$(l=2)$','A-OAM$(l=3)$','D-OAM$(l=3)$','A-OAM$(l=4)$','D-OAM$(l=4)$', ...
68 | 'Orientation','horizontal','NumColumns',2,'Location','SouthWest','Interpreter','latex');
69 | lgd.ItemTokenSize = [25 15];
70 | ylim([-40 -20]);
71 | ylabel('Received Power (dBm)','Interpreter','latex');
72 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex');
73 | grid on;
74 | drawnow;
75 |
76 | %% FIG 3
77 | figure;
78 | for i = 1 : MaxMode
79 | plot(ZRange, pow2db(abs(XZReceiveATotal(RxIndex, :, :, i))) + 30,'-' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
80 | hold on;
81 |
82 | plot(ZRange, pow2db(abs(XZReceiveDTotal(RxIndex, :, :, i))) + 30,'--' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
83 | hold on;
84 | end
85 |
86 | lgd = legend('A-OAM$(l=1)$','D-OAM$(l=1)$','A-OAM$(l=2)$','D-OAM$(l=2)$','A-OAM$(l=3)$','D-OAM$(l=3)$','A-OAM$(l=4)$','D-OAM$(l=4)$', ...
87 | 'Orientation','horizontal','NumColumns',2,'Location','SouthWest','Interpreter','latex');
88 | lgd.ItemTokenSize = [25 15];
89 | ylim([-55 -25]);
90 | ylabel('Received Power (dBm)','Interpreter','latex');
91 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex');
92 | grid on;
93 | drawnow;
94 |
95 | %% FIG 4
96 | figure;
97 | for i = 1 : MaxMode
98 | plot(ZRange, XRange(IndexMaxRecvH(:,:,:,i)),'-' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
99 | hold on;
100 |
101 | plot(ZRange, XRange(IndexMaxRecvD(:,:,:,i)),'--' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
102 | hold on;
103 | end
104 |
105 | lgd = legend('H-OAM$(l=1)$','D-OAM$(l=1)$','H-OAM$(l=2)$','D-OAM$(l=2)$','H-OAM$(l=3)$','D-OAM$(l=3)$','H-OAM$(l=4)$','D-OAM$(l=4)$', ...
106 | 'Orientation','horizontal','NumColumns',2,'Location','NorthWest','Interpreter','latex');
107 | lgd.ItemTokenSize = [25 15];
108 | ylim([0 0.65]);
109 | ylabel('Best-receiving Radius $r_{l}$ (m)','Interpreter','latex');
110 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex');
111 | grid on;
112 | drawnow;
113 |
114 | %% FIG 5
115 | figure;
116 | for i = 1 : MaxMode
117 | plot(ZRange, pow2db(diag(abs(XZReceiveHTotal(IndexMaxRecvH(:,:,:,i), :, :, i)))) + 30,'-' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
118 | hold on;
119 |
120 | plot(ZRange, pow2db(diag(abs(XZReceiveDTotal(IndexMaxRecvD(:,:,:,i), :, :, i)))) + 30,'--' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
121 | hold on;
122 | end
123 |
124 | lgd = legend('H-OAM$(l=1)$','D-OAM$(l=1)$','H-OAM$(l=2)$','D-OAM$(l=2)$','H-OAM$(l=3)$','D-OAM$(l=3)$','H-OAM$(l=4)$','D-OAM$(l=4)$', ...
125 | 'Orientation','horizontal','NumColumns',2,'Location','SouthWest','Interpreter','latex');
126 | lgd.ItemTokenSize = [25 15];
127 | ylim([-40 -20]);
128 | ylabel('Received Power (dBm)','Interpreter','latex');
129 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex');
130 | grid on;
131 | drawnow;
132 |
133 | %% FIG 6
134 | figure;
135 | for i = 1 : MaxMode
136 | plot(ZRange, pow2db(abs(XZReceiveHTotal(RxIndex, :, :, i))) + 30,'-' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
137 | hold on;
138 |
139 | plot(ZRange, pow2db(abs(XZReceiveDTotal(RxIndex, :, :, i))) + 30,'--' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
140 | hold on;
141 | end
142 |
143 | lgd = legend('H-OAM$(l=1)$','D-OAM$(l=1)$','H-OAM$(l=2)$','D-OAM$(l=2)$','H-OAM$(l=3)$','D-OAM$(l=3)$','H-OAM$(l=4)$','D-OAM$(l=4)$', ...
144 | 'Orientation','horizontal','NumColumns',2,'Location','SouthWest','Interpreter','latex');
145 | lgd.ItemTokenSize = [25 15];
146 | ylim([-55 -25]);
147 | ylabel('Received Power (dBm)','Interpreter','latex');
148 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex');
149 | grid on;
150 | drawnow;
151 |
152 | %% FIG 7
153 | figure;
154 | for i = 1 : MaxMode
155 | plot(ZRange, atan(XRange(IndexMaxRecvA(:,:,:,i)) ./ ZRange),'-' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
156 | hold on;
157 |
158 | plot(ZRange, atan(XRange(IndexMaxRecvD(:,:,:,i)) ./ ZRange),'--' + Config(1,i),'LineWidth',2,'MarkerSize',10,'Color',Config(2,i),'MarkerEdgeColor',Config(3,i));
159 | hold on;
160 | end
161 |
162 | lgd = legend('A-OAM$(l=1)$','D-OAM$(l=1)$','A-OAM$(l=2)$','D-OAM$(l=2)$','A-OAM$(l=3)$','D-OAM$(l=3)$','A-OAM$(l=4)$','D-OAM$(l=4)$', ...
163 | 'Orientation','horizontal','NumColumns',2,'Location','NorthEast','Interpreter','latex');
164 | lgd.ItemTokenSize = [25 15];
165 | ylim([0 0.07]);
166 | ylabel('Best-receiving Angle (rad)','Interpreter','latex');
167 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex');
168 | grid on;
169 | drawnow;
170 |
171 | %% Display range initialization
172 | TopRange = [(TxAntennaRadius * 2); (TxAntennaRadius * 2); round(TxAntennaRadius ./ tan(TxFocusingAngle) .* 1.4); 0];
173 | BottomRange = [(-TxAntennaRadius * 2); (-TxAntennaRadius * 2); round(TxAntennaRadius ./ tan(TxFocusingAngle) .* 0.6); 0];
174 | DisplayRange = [TopRange BottomRange (TopRange - BottomRange) ./ [500; 500; 1; 1]];
175 | DisplayRange(3,3) = (DisplayRange(3,1) - DisplayRange(3,2)) / 500;
176 |
177 | % XZ Plane calculate
178 | XRange = [DisplayRange(1,2) : DisplayRange(1,3) : DisplayRange(1,1)];
179 | ZRange = [DisplayRange(3,2) : DisplayRange(3,3) : DisplayRange(3,1)];
180 |
181 | [XZReceiveATotal, IndexMaxRecvA] = OAMFocusing(TxChannelNum, TxAntennaRadius, TxBFElementNum, TxBFDistance, TxFocusingAngle, Frequency, 'A-OAM', MaxMode, DisplayRange);
182 | [XZReceiveHTotal, IndexMaxRecvH] = OAMFocusing(TxChannelNum, TxAntennaRadius, TxBFElementNum, TxBFDistance, TxFocusingAngle, Frequency, 'H-OAM', MaxMode, DisplayRange);
183 |
184 | %% FIG 8
185 | for i = 1 : MaxMode
186 | figure;
187 | surf(ZRange, XRange, pow2db(abs(XZReceiveATotal(:, :, :, i))) + 30);
188 | shading interp;
189 | ylabel('Vertical Distance (m)','Interpreter','latex','fontsize',18);
190 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex','fontsize',18);
191 | c = colorbar;
192 | c.Label.String = 'dBm';
193 | c.Label.Interpreter = 'latex';
194 | c.Label.FontSize = 18;
195 | view(0,90);
196 | clim([-80 -20]);
197 | daspect([1 1 1]);
198 | drawnow;
199 | end
200 |
201 | %% FIG 9
202 | for i = 1 : MaxMode
203 | figure;
204 | surf(ZRange, XRange, pow2db(abs(XZReceiveHTotal(:, :, :, i))) + 30);
205 | shading interp;
206 | ylabel('Vertical Distance (m)','Interpreter','latex','fontsize',18);
207 | xlabel('Receiving Distance $D$ (m)','Interpreter','latex','fontsize',18);
208 | c = colorbar;
209 | c.Label.String = 'dBm';
210 | c.Label.Interpreter = 'latex';
211 | c.Label.FontSize = 18;
212 | view(0,90);
213 | clim([-80 -20]);
214 | daspect([1 1 1]);
215 | drawnow;
216 | end
217 |
218 |
219 | FreeEnvironment;
--------------------------------------------------------------------------------
/example/RFSoCSinglePathExample.m:
--------------------------------------------------------------------------------
1 | %RFSoC Single Path Example ideal channel.
2 | %Author:
3 | % Tifer King
4 | %License:
5 | % Please refer to the 'LICENSE' file included in the root directory
6 | % of the project.
7 |
8 | LoadEnvironment;
9 |
10 | %% Simulation config
11 | DataLegth = 10000 * 8;
12 | ChannelNum = 1;
13 | ChannelNoise = -200;
14 | FramePreambleLength = 167;
15 | SequenceMode = "prbs31";
16 | AnalogSignalPreset = "50Ohm-2.5V";
17 | DigitalSignalPreset = "LVCMOS3V3";
18 | BaseAnalogSampleRate = 6.5536e9;
19 | BaseAnalogSampleRateRx = 4.9152e9;
20 | BaseBitRate = 163.84e6 * 8;
21 | BaseModulationMode = "256QAM";
22 | BaseShapingBeta = 0.5;
23 | BaseShapingSpan = 16;
24 | CarrierFrequency = 2.2e9;
25 | CarrierSampleRate = 6.5536e9;
26 | CarrierSampleRateRx = 4.9152e9;
27 | MaxSimulationFrequency = CarrierFrequency * 5;
28 | MaxChannelImpulseEndurance = 12 / (pi * MaxSimulationFrequency);
29 |
30 | %% Simulation Flow
31 | close all;
32 | BaseData = DigitalSignalGen(ChannelNum, DataLegth, BaseBitRate, SequenceMode, DigitalSignalPreset);
33 | IQBase = Modulation(BaseData, BaseModulationMode, AnalogSignalPreset);
34 | ProbeConstellation(IQBase, false, BaseModulationMode,'Transmitted IQ Base');
35 | FramePreamble = PreambleGen(ChannelNum, FramePreambleLength, IQBase.SampleRate, @ZadoffChuGen, AnalogSignalPreset);
36 | FrameSignal = FrameEncapsulate(IQBase, FramePreamble);
37 | ShapingFilterTx = ShapingFilterDesign(BaseShapingBeta, BaseShapingSpan, BaseAnalogSampleRate, IQBase.SampleRate);
38 | ShapedSignal = ChannelShaping(FrameSignal, ShapingFilterTx, BaseAnalogSampleRate);
39 | ProbeSpectrum(ShapedSignal, false, 'Shaped Signal Spectrum');
40 | CarrierSignal = CarrierGen(CarrierFrequency, ChannelNum, CarrierSampleRate, ShapedSignal.TimeStart, ShapedSignal.TimeEndurance, AnalogSignalPreset);
41 | ProbeSpectrum(CarrierSignal, false, 'Carrier Signal Spectrum', 'Span', [(CarrierFrequency - 0.1e9) (CarrierFrequency + 0.1e9)]);
42 | TransmiteSignal = IQMixing(ShapedSignal, CarrierSignal);
43 | ProbeWave(TransmiteSignal, true, 'Transmitted Signal');
44 | ProbeSpectrum(TransmiteSignal, false, 'Transmitted Spectrum');
45 | TxPower = ProbeSignalPower(TransmiteSignal, 'TxPower');
46 |
47 | ChannelImpulse = InitChannelImpulseResponse(ChannelNum, ChannelNum, 0, MaxChannelImpulseEndurance, CarrierSampleRate, 0, MaxSimulationFrequency, 'Gaussian');
48 | ProbeChannelImpulse(ChannelImpulse);
49 | RecivedSignal = Channel(TransmiteSignal, ChannelImpulse, ChannelNoise);
50 |
51 | ProbeWave(RecivedSignal, true, 'Recived Signal');
52 | ProbeSpectrum(RecivedSignal, false, 'Recived Spectrum', 'Span', [(CarrierFrequency - 400e6) (CarrierFrequency + 400e6)]);
53 | RxPower = ProbeSignalPower(RecivedSignal, 'RxPower');
54 | RecivedSignal.Signal = circshift(RecivedSignal.Signal, 0, 4);
55 | RecivedSignal = SignalResample(RecivedSignal, CarrierSampleRateRx);
56 | CarrierSignal = CarrierGen(CarrierFrequency, ChannelNum, CarrierSampleRateRx, RecivedSignal.TimeStart, RecivedSignal.TimeEndurance, AnalogSignalPreset);
57 | BaseSignal = IQDemixing(RecivedSignal, CarrierSignal, BaseAnalogSampleRateRx, @DemixingResampleFilter);
58 | ShapingFilterRx = ShapingFilterDesign(BaseShapingBeta, BaseShapingSpan, BaseAnalogSampleRateRx, IQBase.SampleRate);
59 | [PayloadSiganl, ChannelEstimation] = FrameDecapsulate(BaseSignal, FramePreamble, BaseData.TimeEndurance, ShapingFilterRx);
60 | ProbeConstellation(PayloadSiganl, false, BaseModulationMode, 'Recived IQ Base');
61 | ProbeSpectrum(PayloadSiganl, false, 'Recived IQ Base Spectrum');
62 | SymbolSignal = BaseSymbolSample(PayloadSiganl, IQBase.SampleRate);
63 | ProbeConstellation(SymbolSignal, false, BaseModulationMode, 'Decoded IQ Base');
64 | RecivedData = Demodulation(SymbolSignal, BaseModulationMode, DigitalSignalPreset);
65 | ErrorRate = ProbeBitError(BaseData, RecivedData, 'Recv');
66 |
67 | FreeEnvironment;
68 |
--------------------------------------------------------------------------------
/lib/BaseSymbolSample.m:
--------------------------------------------------------------------------------
1 | function [SymbolSignal] = BaseSymbolSample(BaseSignal, SymbolSampleRate)
2 | %BaseSymbolSample Sample the symbol from the baseband.
3 | %Introduction:
4 | % Sample the analog signal at its optimal sample point and convert it
5 | % into symbols.
6 | %Syntax:
7 | % SymbolSignal = BaseSymbolSample(BaseSignal, SymbolSampleRate)
8 | %Description:
9 | % SymbolSignal = BaseSymbolSample(BaseSignal, SymbolSampleRate)
10 | % returns the symbol sampled from baseband in symbol rate.
11 | %Input Arguments:
12 | % BaseSignal: (AnalogSignal)
13 | % The signal that contains the baseband content.
14 | % SymbolSampleRate: (double)
15 | % The sample rate (in Sa/s) of the baseband symbols.
16 | %Output Arguments:
17 | % SymbolSignal: (AnalogSignal)
18 | % The signal that contains the symbols.
19 | %Author:
20 | % Tifer King
21 | %License:
22 | % Please refer to the 'LICENSE' file included in the root directory
23 | % of the project.
24 |
25 | ChannelNum = BaseSignal.ChannelNum;
26 | TimeStart = BaseSignal.TimeStart;
27 | TimeEndurance = floor(BaseSignal.TimeEndurance * SymbolSampleRate) / SymbolSampleRate;
28 | DownSampleRate = BaseSignal.SampleRate / SymbolSampleRate;
29 | SymbolSignal = InitAnalogSignal(BaseSignal.ChannelNum, TimeStart, TimeEndurance, SymbolSampleRate, 'Template', 'As', BaseSignal);
30 | if(DownSampleRate == 1)
31 | SymbolSignal.Signal = BaseSignal.Signal;
32 | else
33 | for index = 1 : ChannelNum
34 | SymbolSignal.Signal(index, :) = downsample(BaseSignal.Signal(index,:), DownSampleRate, round(DownSampleRate / 2));
35 | end
36 | end
37 | end
38 |
39 |
--------------------------------------------------------------------------------
/lib/CarrierGen.m:
--------------------------------------------------------------------------------
1 | function [CarrierSignal] = CarrierGen(Frequency, Channel, SampleRate, StartTime, Endurance, SignalPreset)
2 | %CarrierGen Generate carrier signals.
3 | %Introduction:
4 | % Generate carrier signals for each channel with the same phase and
5 | % amplitude. Users can specify the start time and endurance of the
6 | % carrier signal.
7 | %Syntax:
8 | % CarrierSignal = CarrierGen(Frequency, Channel, SampleRate, StartTime, Endurance, SignalPreset)
9 | %Description:
10 | % CarrierSignal = CarrierGen(Frequency, Channel, SampleRate, StartTime, Endurance, SignalPreset)
11 | % returns carrier for each channel.
12 | %Input Arguments:
13 | % Frequency: (double)
14 | % Carrier frequency in Hz.
15 | % Channel: (positive integer scalar)
16 | % Number of channels.
17 | % SampleRate: (double)
18 | % Carrier sample rate in Sa/s.
19 | % StartTime: (double)
20 | % Carrier start time in second.
21 | % Endurance: (double)
22 | % Carrier endurance in second.
23 | % SignalPreset: (string)
24 | % The preset reference voltage and impedance of signal, please refer
25 | % to 'InitAnalogSignal' for more detail.
26 | %Output Arguments:
27 | % CarrierSignal: (AnalogSignal)
28 | % The signal that contains the carrier.
29 | %Author:
30 | % Tifer King
31 | %License:
32 | % Please refer to the 'LICENSE' file included in the root directory
33 | % of the project.
34 |
35 | CarrierSignal = InitAnalogSignal(Channel, StartTime, Endurance, SampleRate, SignalPreset);
36 | Carrierindex = [1 : round(Endurance * SampleRate)] / SampleRate;
37 | Carrier = exp(1i * (2 * pi * Frequency * Carrierindex));
38 | for index = 1 : Channel
39 | CarrierSignal.Signal(index,:) = Carrier;
40 | end
41 | end
42 |
43 |
--------------------------------------------------------------------------------
/lib/Channel.m:
--------------------------------------------------------------------------------
1 | function [OutputSignal] = Channel(InputSignal, ChannelImpulse, NoisedBW)
2 | %Channel Channel simulation.
3 | %Introduction:
4 | % Simulate the signal transmission through the channel with a specific
5 | % impulse response and a specific noise level.
6 | %Syntax:
7 | % OutputSignal = Channel(InputSignal, ChannelImpulse, NoisedBW)
8 | %Description:
9 | % OutputSignal = Channel(InputSignal, ChannelImpulse, NoisedBW)
10 | % returns the signal recived.
11 | %Input Arguments:
12 | % InputSignal: (AnalogSignal)
13 | % Transmitted signals.
14 | % ChannelImpulse: (ChannelImpulseResponse)
15 | % Channel impulse response for each channel.
16 | % NoisedBW: (double)
17 | % AWGN nois power in dBW.
18 | %Output Arguments:
19 | % OutputSignal: (AnalogSignal)
20 | % Recived signals.
21 | %Author:
22 | % Tifer King
23 | %License:
24 | % Please refer to the 'LICENSE' file included in the root directory
25 | % of the project.
26 |
27 | SampleRate = InputSignal.SampleRate;
28 | InputLength = InputSignal.TimeEndurance * InputSignal.SampleRate;
29 | [UpRate,DownRate] = rat(InputSignal.SampleRate / ChannelImpulse.SampleRate);
30 | ImpulseLength = round(ChannelImpulse.TimeEndurance * SampleRate);
31 | ChannelImpulseInput = zeros(ChannelImpulse.ChannelOutputNum, ChannelImpulse.ChannelInputNum, ImpulseLength);
32 | if (UpRate ~= DownRate)
33 | % In case the input signals have a different sample rate from the
34 | % channel impulse response, the channel impulse response will be
35 | % resampled.
36 | for indexrow = 1 : ChannelImpulse.ChannelOutputNum
37 | for indexcolumn = 1 : ChannelImpulse.ChannelInputNum
38 | ChannelImpulseInput(indexrow, indexcolumn,:) = resample(reshape(ChannelImpulse.Signal(indexrow, indexcolumn,:), 1, []), UpRate, DownRate);
39 | if (sum(ChannelImpulseInput(indexrow, indexcolumn,:)) ~= 0)
40 | AmplificationFactor = sum(ChannelImpulse.Signal(indexrow, indexcolumn,:)) / sum(ChannelImpulseInput(indexrow, indexcolumn,:));
41 | else
42 | AmplificationFactor = 1;
43 | end
44 | ChannelImpulseInput(indexrow, indexcolumn,:) = AmplificationFactor * ChannelImpulseInput(indexrow, indexcolumn,:);
45 | % Resampling the channel impulse response may change its
46 | % amplitude, so it should be rescaled.
47 | end
48 | end
49 | else
50 | ChannelImpulseInput = ChannelImpulse.Signal;
51 | end
52 | TimeStart = InputSignal.TimeStart + ChannelImpulse.TimeStart;
53 | TimeEndurance = (ImpulseLength + InputLength - 1) / SampleRate;
54 | OutputSignal = InitAnalogSignal(ChannelImpulse.ChannelOutputNum, TimeStart, TimeEndurance, SampleRate, 'Template','As',InputSignal);
55 | OutputSignal.Signal = ChannelConvolution(InputSignal.Signal, ChannelImpulseInput) / db2mag(ChannelImpulse.AttenuationdB);
56 | % Keeping the amplitude (power) of the signal correct is important.
57 | % Rescaling can adjust the signal to the correct amplitude.
58 | ReferencePower = pow2db(OutputSignal.ReferenceVoltage ^ 2 / OutputSignal.ReferenceImpedance);
59 | % ReferencePower calculates the reference power of the signal based on
60 | % a specific reference voltage and reference impedance. In other words,
61 | % it represents the power when the signal is 1.
62 | StandardizedPower = ReferencePower - mag2db(OutputSignal.ReferenceVoltage / sqrt(OutputSignal.ReferenceImpedance));
63 | % MATLAB always assumes that the reference impedance is 1 Ohm and the
64 | % reference voltage is 1 V when calculating power, so it needs to be
65 | % standardized in this case.
66 | OutputSignal.Signal = awgn(OutputSignal.Signal, ReferencePower - NoisedBW - pow2db(ChannelImpulse.ChannelInputNum), StandardizedPower);
67 | end
68 |
69 |
--------------------------------------------------------------------------------
/lib/ChannelConvolution.m:
--------------------------------------------------------------------------------
1 | function [OutputSignal] = ChannelConvolution(InputSignal, InputChannel)
2 | %ChannelConvolution Channel convolution core.
3 | %Introduction:
4 | % This is the convolution core of the channel simulation. Note: This
5 | % function is poorly optimized. To improve the channel simulation
6 | % performance, this function should be optimized first. It will bring a
7 | % huge performance boost to the channel simulation.
8 | %Syntax:
9 | % OutputSignal = ChannelConvolution(InputSignal, InputChannel)
10 | %Description:
11 | % OutputSignal = ChannelConvolution(InputSignal, InputChannel)
12 | % returns the signal and channel impulse response convolution.
13 | %Input Arguments:
14 | % InputSignal: (matrix)
15 | % Channel input signals.
16 | % InputChannel: (matrix)
17 | % Channel impulse response for each channel.
18 | %Output Arguments:
19 | % OutputSignal: (matrix)
20 | % Signal convolutions.
21 | %Author:
22 | % Tifer King
23 | %License:
24 | % Please refer to the 'LICENSE' file included in the root directory
25 | % of the project.
26 |
27 | [OutputNum, InputNum, ChannelLength] = size(InputChannel);
28 | InputLength = length(InputSignal);
29 | OutputSignal = zeros(OutputNum, ChannelLength + InputLength - 1);
30 | InputSignal = [InputSignal zeros(InputNum, ChannelLength - 1)];
31 | for indextime = 1 : ChannelLength
32 | OutputSignal = OutputSignal + InputChannel(:, :, indextime) * InputSignal;
33 | InputSignal = circshift(InputSignal,1,2);
34 | end
35 |
36 | %OutputSignal = pagemtimes(InputChannel, InputSignal);
37 | %for indextime = 1 : ChannelLength
38 | %OutputSignal(:, :, indextime) = circshift(OutputSignal(:, :, indextime), indextime - 1,2);
39 | %end
40 | %OutputSignal = sum(OutputSignal, 3);
41 | end
42 |
43 |
--------------------------------------------------------------------------------
/lib/ChannelShaping.m:
--------------------------------------------------------------------------------
1 | function [ShapedSignal] = ChannelShaping(IQSignal, Filter, SampleRate)
2 | %ChannelShaping Upconvert baseband signal and filter it with shaping filter.
3 | %Introduction:
4 | % The baseband signal that is transmitted into the channel should be
5 | % oversampled first, because the original signal may have a wide
6 | % spectrum, and the channel can only pass a narrower signal. In this
7 | % case, the signal may be affected by inter-symbol interference. To avoid
8 | % this effect, the signal should be pre-filtered.
9 | %Syntax:
10 | % ShapedSignal = ChannelShaping(IQSignal, Filter, SampleRate)
11 | %Description:
12 | % ShapedSignal = ChannelShaping(IQSignal, Filter, SampleRate)
13 | % returns the shaping filtered signal.
14 | %Input Arguments:
15 | % IQSignal: (AnalogSignal)
16 | % Baseband IQ signals.
17 | % Filter: (vector)
18 | % Symbol shaping filter coefficient.
19 | % SampleRate: (double)
20 | % Upconvert sample rate in Sa/s.
21 | %Output Arguments:
22 | % ShapedSignal: (matrix)
23 | % Signal filtered.
24 | %Author:
25 | % Tifer King
26 | %License:
27 | % Please refer to the 'LICENSE' file included in the root directory
28 | % of the project.
29 |
30 | FilterLength = length(Filter);
31 | [Channel,IQLength] = size(IQSignal.Signal);
32 | SignalLength = floor(IQLength * SampleRate / IQSignal.SampleRate + FilterLength);
33 | ShapedSignal = InitAnalogSignal(Channel, IQSignal.TimeStart, SignalLength / SampleRate, SampleRate, 'Template','As',IQSignal);
34 | ShapedSignal.Signal = upfirdn([IQSignal.Signal zeros(IQSignal.ChannelNum, 1)]', Filter', SampleRate / IQSignal.SampleRate)';
35 | % Upfirdn may cause the last sample point to be not upsampled, so it
36 | % should add an additional zero at the end of the sample to make sure
37 | % the signal is correct.
38 | ShapedSignal.TimeEndurance = (SignalLength) / SampleRate;
39 | end
40 |
41 |
--------------------------------------------------------------------------------
/lib/Demodulation.m:
--------------------------------------------------------------------------------
1 | function [DigitalSignal] = Demodulation(IQSignal, Mode, SignalPreset)
2 | %Demodulation Convert IQ signal to binary data by demodulation.
3 | %Introduction:
4 | % This function converts signal to binary by demodulation and paired with
5 | % 'Modulation'.
6 | %Syntax:
7 | % DigitalSignal = Demodulation(IQSignal, Mode, SignalPreset)
8 | %Description:
9 | % DigitalSignal = Demodulation(IQSignal, Mode, SignalPreset)
10 | % returns the binary data stream.
11 | %Input Arguments:
12 | % IQSignal: (AnalogSignal)
13 | % Baseband IQ signals.
14 | % Mode: (string)
15 | % Method of modulation, it is one of blow:
16 | % 'ASK': Amplitude Shift Keying.
17 | % 'BPSK': Binary Phase-Shift Keying.
18 | % 'QPSK': Quadrature Phase Shift Keying.
19 | % '16QAM': 16-points Quadrature Amplitude Modulation.
20 | % '64QAM': 64-points Quadrature Amplitude Modulation.
21 | % '256QAM': 256-points Quadrature Amplitude Modulation.
22 | % '1024QAM': 1024-points Quadrature Amplitude Modulation.
23 | % SignalPreset: (string)
24 | % The preset reference voltage, impedance and different pair of
25 | % signal, please refer to 'InitDigitalSignal' for more detail.
26 | %Output Arguments:
27 | % DigitalSignal: (DigitalSignal)
28 | % Recovered binary stream.
29 | %Author:
30 | % Tifer King
31 | %License:
32 | % Please refer to the 'LICENSE' file included in the root directory
33 | % of the project.
34 |
35 | ChannelNum = IQSignal.ChannelNum;
36 | TimeStart = IQSignal.TimeStart;
37 | TimeEndurance = IQSignal.TimeEndurance;
38 | SampleRate = IQSignal.SampleRate;
39 | SignalLength = size(IQSignal.Signal, 2);
40 | if(strcmp(Mode, "ASK"))
41 | DigitalSignal = InitDigitalSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate, SignalPreset);
42 | DigitalSignal.Signal = double(real(IQSignal.Signal) > 0.5);
43 | elseif(strcmp(Mode, "BPSK"))
44 | DigitalSignal = InitDigitalSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate, SignalPreset);
45 | DigitalSignal.Signal = double(real(IQSignal.Signal) > 0);
46 | elseif (strcmp(Mode, "QPSK"))
47 | BitStream = zeros(2, SignalLength, ChannelNum);
48 | IQStream = reshape(IQSignal.Signal.', 1, SignalLength, []);
49 | BitStream(1,:,:) = double(real(IQStream) > 0);
50 | BitStream(2,:,:) = double(imag(IQStream) > 0);
51 | DigitalSignal = InitDigitalSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate * 2, SignalPreset);
52 | BitStream = reshape(BitStream, 1, [], ChannelNum);
53 | DigitalSignal.Signal = reshape(BitStream, [], ChannelNum)';
54 | elseif (strcmp(Mode, "16QAM"))
55 | BitStream = zeros(4, SignalLength, ChannelNum);
56 | IQStream = reshape(IQSignal.Signal.', 1, SignalLength, []) * (3 / 2);
57 | BitStream(1,:,:) = double(real(IQStream) > 0);
58 | BitStream(3,:,:) = double(imag(IQStream) > 0);
59 | IQStream = IQStream - ((BitStream(1,:,:) * 2 - 1) + (BitStream(3,:,:) * 2 - 1) * 1i);
60 | BitStream(2,:,:) = double(real(IQStream) > 0);
61 | BitStream(4,:,:) = double(imag(IQStream) > 0);
62 | DigitalSignal = InitDigitalSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate * 4, SignalPreset);
63 | BitStream = reshape(BitStream, 1, [], ChannelNum);
64 | DigitalSignal.Signal = reshape(BitStream, [], ChannelNum)';
65 | elseif (strcmp(Mode, "64QAM"))
66 | BitStream = zeros(6, SignalLength, ChannelNum);
67 | IQStream = reshape(IQSignal.Signal.', 1, SignalLength, []) * (7 / 2);
68 | BitStream(1,:,:) = double(real(IQStream) > 0);
69 | BitStream(4,:,:) = double(imag(IQStream) > 0);
70 | IQStream = IQStream - ((BitStream(1,:,:) * 4 - 2) + (BitStream(4,:,:) * 4 - 2) * 1i);
71 | BitStream(2,:,:) = double(real(IQStream) > 0);
72 | BitStream(5,:,:) = double(imag(IQStream) > 0);
73 | IQStream = IQStream - ((BitStream(2,:,:) * 2 - 1) + (BitStream(5,:,:) * 2 - 1) * 1i);
74 | BitStream(3,:,:) = double(real(IQStream) > 0);
75 | BitStream(6,:,:) = double(imag(IQStream) > 0);
76 | DigitalSignal = InitDigitalSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate * 6, SignalPreset);
77 | BitStream = reshape(BitStream, 1, [], ChannelNum);
78 | DigitalSignal.Signal = reshape(BitStream, [], ChannelNum)';
79 | elseif (strcmp(Mode, "256QAM"))
80 | BitStream = zeros(8, SignalLength, ChannelNum);
81 | IQStream = reshape(IQSignal.Signal.', 1, SignalLength, []) * (15 / 2);
82 | BitStream(1,:,:) = double(real(IQStream) > 0);
83 | BitStream(5,:,:) = double(imag(IQStream) > 0);
84 | IQStream = IQStream - ((BitStream(1,:,:) * 8 - 4) + (BitStream(5,:,:) * 8 - 4) * 1i);
85 | BitStream(2,:,:) = double(real(IQStream) > 0);
86 | BitStream(6,:,:) = double(imag(IQStream) > 0);
87 | IQStream = IQStream - ((BitStream(2,:,:) * 4 - 2) + (BitStream(6,:,:) * 4 - 2) * 1i);
88 | BitStream(3,:,:) = double(real(IQStream) > 0);
89 | BitStream(7,:,:) = double(imag(IQStream) > 0);
90 | IQStream = IQStream - ((BitStream(3,:,:) * 2 - 1) + (BitStream(7,:,:) * 2 - 1) * 1i);
91 | BitStream(4,:,:) = double(real(IQStream) > 0);
92 | BitStream(8,:,:) = double(imag(IQStream) > 0);
93 | DigitalSignal = InitDigitalSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate * 8, SignalPreset);
94 | BitStream = reshape(BitStream, 1, [], ChannelNum);
95 | DigitalSignal.Signal = reshape(BitStream, [], ChannelNum)';
96 | elseif (strcmp(Mode, "1024QAM"))
97 | BitStream = zeros(10, SignalLength, ChannelNum);
98 | IQStream = reshape(IQSignal.Signal.', 1, SignalLength, []) * (31 / 2);
99 | BitStream(1,:,:) = double(real(IQStream) > 0);
100 | BitStream(6,:,:) = double(imag(IQStream) > 0);
101 | IQStream = IQStream - ((BitStream(1,:,:) * 16 - 8) + (BitStream(6,:,:) * 16 - 8) * 1i);
102 | BitStream(2,:,:) = double(real(IQStream) > 0);
103 | BitStream(7,:,:) = double(imag(IQStream) > 0);
104 | IQStream = IQStream - ((BitStream(2,:,:) * 8 - 4) + (BitStream(7,:,:) * 8 - 4) * 1i);
105 | BitStream(3,:,:) = double(real(IQStream) > 0);
106 | BitStream(8,:,:) = double(imag(IQStream) > 0);
107 | IQStream = IQStream - ((BitStream(3,:,:) * 4 - 2) + (BitStream(8,:,:) * 4 - 2) * 1i);
108 | BitStream(4,:,:) = double(real(IQStream) > 0);
109 | BitStream(9,:,:) = double(imag(IQStream) > 0);
110 | IQStream = IQStream - ((BitStream(4,:,:) * 2 - 1) + (BitStream(9,:,:) * 2 - 1) * 1i);
111 | BitStream(5,:,:) = double(real(IQStream) > 0);
112 | BitStream(10,:,:) = double(imag(IQStream) > 0);
113 | DigitalSignal = InitDigitalSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate * 10, SignalPreset);
114 | BitStream = reshape(BitStream, 1, [], ChannelNum);
115 | DigitalSignal.Signal = reshape(BitStream, [], ChannelNum)';
116 | end
117 | end
118 |
119 |
--------------------------------------------------------------------------------
/lib/DigitalSignalGen.m:
--------------------------------------------------------------------------------
1 | function [DigitalSignal] = DigitalSignalGen(Channel, Length, BitRate, Mode, SignalPreset)
2 | %DigitalSignalGen Generate binary data stream.
3 | %Introduction:
4 | % Generate binary data using the specified method to evaluate the
5 | % communication system.
6 | %Syntax:
7 | % DigitalSignal = DigitalSignalGen(Channel, Length, BitRate, Mode, SignalPreset)
8 | %Description:
9 | % DigitalSignal = DigitalSignalGen(Channel, Length, BitRate, Mode, SignalPreset)
10 | % returns the generated binary stream.
11 | %Input Arguments:
12 | % Channel: (positive integer scalar)
13 | % Number of channels.
14 | % Length: (positive integer scalar)
15 | % Binary stream bit length.
16 | % BitRate: (double)
17 | % Data stream bit rate in bit/s.
18 | % Mode: (string)
19 | % Generating method, it is one of blow:
20 | % 'random': Random data with equal probability of 0 or 1.
21 | % 'ones': All 1.
22 | % 'zeros': All 0.
23 | % 'onoff': A repeating pattern of 101010…
24 | % 'prbs7': Pseudo-Random Binary Sequence in order 7.
25 | % 'prbs9': Pseudo-Random Binary Sequence in order 9.
26 | % 'prbs13': Pseudo-Random Binary Sequence in order 13.
27 | % 'prbs15': Pseudo-Random Binary Sequence in order 15.
28 | % 'prbs20': Pseudo-Random Binary Sequence in order 20.
29 | % 'prbs23': Pseudo-Random Binary Sequence in order 23.
30 | % 'prbs31': Pseudo-Random Binary Sequence in order 31.
31 | % SignalPreset: (string)
32 | % The preset reference voltage, impedance and different pair of
33 | % signal, please refer to 'InitDigitalSignal' for more detail.
34 | %Output Arguments:
35 | % DigitalSignal: (DigitalSignal)
36 | % Generated binary stream.
37 | %Author:
38 | % Tifer King
39 | %License:
40 | % Please refer to the 'LICENSE' file included in the root directory
41 | % of the project.
42 |
43 | DigitalSignal = InitDigitalSignal(Channel, 0, Length / BitRate, BitRate, SignalPreset);
44 | if(strcmp(Mode, "random"))
45 | DigitalSignal.Signal = randi([0 1],Channel, Length);
46 | elseif (strcmp(Mode, "ones"))
47 | DigitalSignal.Signal = ones(Channel, Length);
48 | elseif (strcmp(Mode, "zeros"))
49 | DigitalSignal.Signal = zeros(Channel, Length);
50 | elseif (strcmp(Mode, "onoff"))
51 | Signal = repmat([1 0], Channel, ceil(Length / 2));
52 | DigitalSignal.Signal = Signal(:, 1:Length);
53 | elseif (strcmp(Mode, "prbs7"))
54 | for index = 1:Channel
55 | Seed = randi([0 1], 1, 7);
56 | % PRBS need a start up sequence.
57 | Seed(1) = 1;
58 | % This is to prevent the initial sequence from being all 0.
59 | DigitalSignal.Signal(index,:) = prbs(7, Length, Seed);
60 | end
61 | elseif (strcmp(Mode, "prbs9"))
62 | for index = 1:Channel
63 | Seed = randi([0 1], 1, 9);
64 | Seed(1) = 1;
65 | DigitalSignal.Signal(index,:) = prbs(9, Length, Seed);
66 | end
67 | elseif (strcmp(Mode, "prbs13"))
68 | for index = 1:Channel
69 | Seed = randi([0 1], 1, 13);
70 | Seed(1) = 1;
71 | DigitalSignal.Signal(index,:) = prbs(13, Length, Seed);
72 | end
73 | elseif (strcmp(Mode, "prbs15"))
74 | for index = 1:Channel
75 | Seed = randi([0 1], 1, 15);
76 | Seed(1) = 1;
77 | DigitalSignal.Signal(index,:) = prbs(15, Length, Seed);
78 | end
79 | elseif (strcmp(Mode, "prbs20"))
80 | for index = 1:Channel
81 | Seed = randi([0 1], 1, 20);
82 | Seed(1) = 1;
83 | DigitalSignal.Signal(index,:) = prbs(20, Length, Seed);
84 | end
85 | elseif (strcmp(Mode, "prbs23"))
86 | for index = 1:Channel
87 | Seed = randi([0 1], 1, 23);
88 | Seed(1) = 1;
89 | DigitalSignal.Signal(index,:) = prbs(23, Length, Seed);
90 | end
91 | elseif (strcmp(Mode, "prbs31"))
92 | for index = 1:Channel
93 | Seed = randi([0 1], 1, 31);
94 | Seed(1) = 1;
95 | DigitalSignal.Signal(index,:) = prbs(31, Length, Seed);
96 | end
97 | else
98 | %Default is random
99 | warning("Cannot recognize generate mode, default using random mode.");
100 | DigitalSignal.Signal = randi([0 1],Channel, Length);
101 | end
102 |
103 | end
104 |
105 |
--------------------------------------------------------------------------------
/lib/FrameDecapsulate.m:
--------------------------------------------------------------------------------
1 | function [BaseSignal, ChannelEstimation] = FrameDecapsulate(FrameSignal, PreambleSignal, PayloadTimeEndurance, Filter)
2 | %FrameDecapsulate Decapsulate the frame using channel estimation and unload the preamble.
3 | %Introduction:
4 | % Use the generalized inverse matrix of channel estimation to unload the
5 | % preamble, and then remove the preamble. It is a paired function of
6 | % 'FrameEncapsulate'.
7 | %Syntax:
8 | % BaseSignal = FrameDecapsulate(FrameSignal, PreambleSignal, Filter)
9 | % [BaseSignal, ChannelEstimation] = FrameDecapsulate(FrameSignal, PreambleSignal, Filter)
10 | %Description:
11 | % BaseSignal = FrameDecapsulate(FrameSignal, PreambleSignal, Filter)
12 | % returns the base signal that unloaded from frame.
13 | % [BaseSignal, ChannelEstimation] = FrameDecapsulate(FrameSignal, PreambleSignal, Filter)
14 | % returns the base signal and channel estimation matrix that unloaded
15 | % from frame.
16 | %Input Arguments:
17 | % FrameSignal: (AnalogSignal)
18 | % Baseband signal with preamble.
19 | % PreambleSignal: (AnalogSignal)
20 | % The preamble signal.
21 | % PayloadTimeEndurance: (double)
22 | % The payload time endurance.
23 | % Filter: (matrix)
24 | % If the signal should be filtered, add a filter here. This argument
25 | % may be used for channel shaping filter. If not used, please input
26 | % [1].
27 | %Output Arguments:
28 | % BaseSignal: (AnalogSignal)
29 | % Payload baseband signal.
30 | % ChannelEstimation: (matrix)
31 | % Channel estimation matrix.
32 | %Author:
33 | % Tifer King
34 | %License:
35 | % Please refer to the 'LICENSE' file included in the root directory
36 | % of the project.
37 |
38 | ChannelNum = FrameSignal.ChannelNum;
39 | ChannelNumTx = PreambleSignal.ChannelNum;
40 | if (PreambleSignal.SampleRate < FrameSignal.SampleRate)
41 | % If the sample rate of the preamble signal is slower than the
42 | % baseband signal, the preamble should be first upsampled.
43 | Preamble = SignalResample(PreambleSignal, FrameSignal.SampleRate, 'zero');
44 | elseif (PreambleSignal.SampleRate > FrameSignal.SampleRate)
45 | Preamble = PreambleSignal;
46 | warning("Preamble cannot sample faster than base, ignore the sample rate.");
47 | else
48 | Preamble = PreambleSignal;
49 | end
50 | ShapedSignal = filter(Filter,1,FrameSignal.Signal,[],2);
51 | % First filter the baseband signal with shaping filter.
52 | PreambleCorrelation = zeros(ChannelNum, ChannelNumTx, size(FrameSignal.Signal, 2) + size(Preamble.Signal, 2) - 1);
53 | for index = 1 : ChannelNum
54 | for indextx = 1: ChannelNumTx
55 | % Calculate the correlation of the preamble signals.
56 | PreambleCorrelation(index, indextx, :) = conv(ShapedSignal(index, :), conj(flip(Preamble.Signal(indextx, :))));
57 | end
58 | end
59 | [CorrelationMax, CorrelationMaxIndex] = max(PreambleCorrelation, [], 3);
60 | CorrelationRange = max(abs(CorrelationMax), [],'all');
61 | % Find the highest level of correlation, and then judge the
62 | % availability of every other correlation based on it.
63 | DownSampleRate = FrameSignal.SampleRate / PreambleSignal.SampleRate;
64 | PayloadStart = round(mode(CorrelationMaxIndex(abs(CorrelationMax) > (CorrelationRange * 0.5)), 'all')) - round(DownSampleRate / 2) + 1;
65 | % The payload start time can be found using the correlation.
66 | EstimationSeq = zeros(ChannelNum, size(PreambleSignal.Signal, 2));
67 | for index = 1 : ChannelNum
68 | EstimationStart = PayloadStart - size(Preamble.Signal, 2) - 1;
69 | EstimationStop = PayloadStart - 1;
70 | if(DownSampleRate == 1)
71 | EstimationSeqRow = ShapedSignal(index, EstimationStart : EstimationStop);
72 | else
73 | EstimationSeqRow = downsample(ShapedSignal(index, EstimationStart : EstimationStop), DownSampleRate, round(DownSampleRate / 2));
74 | end
75 | % To estimate the channel, the received preamble should be first
76 | % downsampled to the same sample rate as the sent preamble.
77 | EstimationSeq(index, :) = EstimationSeqRow(1 : size(PreambleSignal.Signal, 2));
78 | % Remove the meaningless symbol before the preamble.
79 | end
80 | ChannelEstimation = EstimationSeq * pinv(PreambleSignal.Signal);
81 | % H = P * H * P ^ (-1)
82 | TimeStart = FrameSignal.TimeStart + PayloadStart / FrameSignal.SampleRate;
83 | TimeEndurance = PayloadTimeEndurance;
84 | BaseSignal = InitAnalogSignal(ChannelNum, TimeStart, TimeEndurance, FrameSignal.SampleRate, 'Template', 'As', FrameSignal);
85 | BaseSignal.Signal = pinv(ChannelEstimation) * ShapedSignal(:,PayloadStart : end);
86 | BaseSignal.Signal = BaseSignal.Signal(:, 1 : TimeEndurance * FrameSignal.SampleRate);
87 | end
88 |
89 |
--------------------------------------------------------------------------------
/lib/FrameEncapsulate.m:
--------------------------------------------------------------------------------
1 | function [FrameSignal] = FrameEncapsulate(BaseSignal, PreambleSignal)
2 | %FrameEncapsulate Encapsulate the frame using the specified preamble.
3 | %Introduction:
4 | % Combine the preamble signal and the baseband data into a single signal
5 | % stream.
6 | %Syntax:
7 | % FrameSignal = FrameEncapsulate(BaseSignal, PreambleSignal)
8 | %Description:
9 | % FrameSignal = FrameEncapsulate(BaseSignal, PreambleSignal)
10 | % returns the base signal of frame.
11 | %Input Arguments:
12 | % BaseSignal: (AnalogSignal)
13 | % Baseband data payload.
14 | % PreambleSignal: (AnalogSignal)
15 | % The preamble signal.
16 | %Output Arguments:
17 | % FrameSignal: (AnalogSignal)
18 | % Signal of frame.
19 | %Author:
20 | % Tifer King
21 | %License:
22 | % Please refer to the 'LICENSE' file included in the root directory
23 | % of the project.
24 |
25 | ChannelNum = BaseSignal.ChannelNum;
26 | TimeStart = BaseSignal.TimeStart + PreambleSignal.TimeStart;
27 | TimeEndurance = BaseSignal.TimeEndurance;
28 | if (PreambleSignal.SampleRate < BaseSignal.SampleRate)
29 | % If the sample rate of the preamble signal is slower than the
30 | % baseband signal, the preamble should be first upsampled.
31 | Preamble = SignalResample(PreambleSignal, BaseSignal.SampleRate, 'previous');
32 | TimeEndurance = BaseSignal.TimeEndurance + Preamble.TimeEndurance;
33 | elseif (PreambleSignal.SampleRate > BaseSignal.SampleRate)
34 | Preamble = PreambleSignal;
35 | TimeEndurance = BaseSignal.TimeEndurance + PreambleSignal.TimeEndurance;
36 | warning("Preamble cannot sample faster than base, ignore the sample rate.");
37 | else
38 | Preamble = PreambleSignal;
39 | end
40 | FrameSignal = InitAnalogSignal(ChannelNum, TimeStart, TimeEndurance, BaseSignal.SampleRate, 'Template', 'As', BaseSignal);
41 | FrameSignal.Signal = [Preamble.Signal BaseSignal.Signal];
42 | end
43 |
44 |
--------------------------------------------------------------------------------
/lib/IQDemixing.m:
--------------------------------------------------------------------------------
1 | function [BaseSignal] = IQDemixing(AnalogSignal, CarrierSignal, SampleRate, FilterDesignFx)
2 | %IQDemixing Mixing radio band signal to baseband.
3 | %Introduction:
4 | % IQDemixer can demodulate the in-phase and quadrature components of a
5 | % signal independently.
6 | %Syntax:
7 | % BaseSignal = IQDemixing(AnalogSignal, CarrierSignal, SampleRate, FilterDesignFx)
8 | %Description:
9 | % BaseSignal = IQDemixing(AnalogSignal, CarrierSignal, SampleRate, FilterDesignFx)
10 | % returns the baseband signal.
11 | %Input Arguments:
12 | % AnalogSignal: (AnalogSignal)
13 | % Radio frequency signal.
14 | % CarrierSignal: (AnalogSignal)
15 | % Local-oscillator signal.
16 | % SampleRate: (double)
17 | % Baseband sample rate.
18 | % FilterDesignFx: (function)
19 | % Design downconvert filter. The function will called in:
20 | % FilterDesignFx(LOSampleRate, BaseSampleRate);
21 | %Output Arguments:
22 | % BaseSignal: (AnalogSignal)
23 | % Baseband signal.
24 | %Author:
25 | % Tifer King
26 | %License:
27 | % Please refer to the 'LICENSE' file included in the root directory
28 | % of the project.
29 |
30 | [UpRate,DownRate] = rat(SampleRate / CarrierSignal.SampleRate);
31 |
32 | if AnalogSignal.TimeStart > CarrierSignal.TimeStart
33 | TimeStart = CarrierSignal.TimeStart;
34 | else
35 | TimeStart = AnalogSignal.TimeStart;
36 | end
37 | if (AnalogSignal.TimeStart + AnalogSignal.TimeEndurance) > (CarrierSignal.TimeStart + CarrierSignal.TimeEndurance)
38 | TimeEndurance = (AnalogSignal.TimeStart + AnalogSignal.TimeEndurance) - TimeStart;
39 | else
40 | TimeEndurance = (CarrierSignal.TimeStart + CarrierSignal.TimeEndurance) - TimeStart;
41 | end
42 | if AnalogSignal.ChannelNum > CarrierSignal.ChannelNum
43 | ChannelNum = AnalogSignal.ChannelNum;
44 | else
45 | ChannelNum = CarrierSignal.ChannelNum;
46 | end
47 | RFSampleRate = AnalogSignal.SampleRate;
48 | RFSignal = zeros(ChannelNum, round(TimeEndurance * RFSampleRate));
49 | LOSignal = zeros(ChannelNum, round(TimeEndurance * RFSampleRate));
50 | RFStartIndex = round((AnalogSignal.TimeStart - TimeStart) * RFSampleRate + 1);
51 | RFStopIndex = round((AnalogSignal.TimeEndurance) * RFSampleRate + RFStartIndex - 1);
52 | LOStartIndex = round((CarrierSignal.TimeStart - TimeStart) * RFSampleRate + 1);
53 | LOStopIndex = round((CarrierSignal.TimeEndurance) * RFSampleRate + LOStartIndex - 1);
54 | RFSignal(:,RFStartIndex : RFStopIndex) = AnalogSignal.Signal;
55 | LOSignal(:,LOStartIndex : LOStopIndex) = CarrierSignal.Signal;
56 |
57 | MixSignal = RFSignal .* LOSignal;
58 | if(CarrierSignal.SampleRate > 2 * SampleRate)
59 | Filter = FilterDesignFx(CarrierSignal.SampleRate, SampleRate);
60 | MixSignal = filter(Filter,1,MixSignal,[],2);
61 | end
62 | TimeEndurance = ceil(TimeEndurance * SampleRate) / SampleRate;
63 | BaseSignal = InitAnalogSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate, 'Template', 'As', AnalogSignal);
64 | for index = 1 : ChannelNum
65 | BaseSignal.Signal(index,:) = resample(MixSignal(index,:), UpRate, DownRate);
66 | end
67 | end
68 |
69 |
--------------------------------------------------------------------------------
/lib/IQMixing.m:
--------------------------------------------------------------------------------
1 | function [AnalogSignal] = IQMixing(BaseSignal, CarrierSignal)
2 | %IQMixing Mixing baseband signal to radio band.
3 | %Introduction:
4 | % IQMixer can modulate the in-phase and quadrature components of a signal
5 | % independently.
6 | %Syntax:
7 | % AnalogSignal = IQMixing(BaseSignal, CarrierSignal)
8 | %Description:
9 | % AnalogSignal = IQMixing(BaseSignal, CarrierSignal)
10 | % returns the radio signal.
11 | %Input Arguments:
12 | % BaseSignal: (AnalogSignal)
13 | % Baseband signal.
14 | % CarrierSignal: (AnalogSignal)
15 | % Local-oscillator signal.
16 | %Output Arguments:
17 | % AnalogSignal: (AnalogSignal)
18 | % Radio frequecy signal.
19 | %Author:
20 | % Tifer King
21 | %License:
22 | % Please refer to the 'LICENSE' file included in the root directory
23 | % of the project.
24 |
25 | SampleRate = CarrierSignal.SampleRate;
26 | IFSignalInput = SignalResample(BaseSignal, CarrierSignal.SampleRate, 'previous');
27 | if BaseSignal.TimeStart > CarrierSignal.TimeStart
28 | TimeStart = CarrierSignal.TimeStart;
29 | else
30 | TimeStart = BaseSignal.TimeStart;
31 | end
32 | if (BaseSignal.TimeStart + BaseSignal.TimeEndurance) > (CarrierSignal.TimeStart + CarrierSignal.TimeEndurance)
33 | TimeEndurance = (BaseSignal.TimeStart + BaseSignal.TimeEndurance) - TimeStart;
34 | else
35 | TimeEndurance = (CarrierSignal.TimeStart + CarrierSignal.TimeEndurance) - TimeStart;
36 | end
37 | if BaseSignal.ChannelNum > CarrierSignal.ChannelNum
38 | ChannelNum = BaseSignal.ChannelNum;
39 | else
40 | ChannelNum = CarrierSignal.ChannelNum;
41 | end
42 | IFSignal = zeros(ChannelNum, round(TimeEndurance * SampleRate));
43 | LOSignal = zeros(ChannelNum, round(TimeEndurance * SampleRate));
44 | IFStartIndex = round((BaseSignal.TimeStart - TimeStart) * SampleRate + 1);
45 | IFStopIndex = round((BaseSignal.TimeEndurance) * SampleRate + IFStartIndex - 1);
46 | LOStartIndex = round((CarrierSignal.TimeStart - TimeStart) * SampleRate + 1);
47 | LOStopIndex = round((CarrierSignal.TimeEndurance) * SampleRate + LOStartIndex - 1);
48 | IFSignal(:,IFStartIndex : IFStopIndex) = IFSignalInput.Signal;
49 | LOSignal(:,LOStartIndex : LOStopIndex) = conj(CarrierSignal.Signal);
50 | % IQ mixing means mixing the I and Q signals independently and then
51 | % adding them together. However, due to the feature of complex numbers,
52 | % the LO signal should be conjugated first. For example, if ‘a + bi’ is
53 | % the IF signal and ‘c + di’ is the LO signal, the output signal should
54 | % be ‘ac + bdi’. But simply multiplying ‘(a + bi)(c + di)’ will result
55 | % in ‘ac - bd + (ad + bc)i’ (the real part of it become 'ac - bd'), so
56 | % the LO signal should be conjugated first.
57 |
58 | AnalogSignal = InitAnalogSignal(ChannelNum, TimeStart, TimeEndurance, SampleRate, 'Template','As',BaseSignal);
59 | AnalogSignal.Signal = real(IFSignal .* LOSignal);
60 | end
61 |
--------------------------------------------------------------------------------
/lib/InitAnalogSignal.m:
--------------------------------------------------------------------------------
1 | function [AnalogSignal] = InitAnalogSignal(varargin)
2 | %InitAnalogSignal Initialize a structure containing multi-channel analog signals.
3 | %Introduction:
4 | % Initialize the struct to contain multi-channel analog signals, along
5 | % with the reference voltage, impedance, etc. It will provide a standard
6 | % struct to log analog signal and make the analysis or tracing easy.
7 | % Using the standard analog signal struct will bring much more
8 | % convenience.
9 | %Syntax:
10 | % AnalogSignal = InitAnalogSignal(Channel, TimeStart, TimeEndurance, SampleRate, ReferencePreset)
11 | % AnalogSignal = InitAnalogSignal(Channel, TimeStart, TimeEndurance, SampleRate, 'Custom', ReferenceVoltage, ReferenceImpedance)
12 | % AnalogSignal = InitAnalogSignal(Channel, TimeStart, TimeEndurance, SampleRate, 'Template', 'As', TemplateSignal)
13 | %Description:
14 | % AnalogSignal = InitAnalogSignal(Channel, TimeStart, TimeEndurance, SampleRate, ReferencePreset)
15 | % returns a blank analog signal.
16 | % AnalogSignal = InitAnalogSignal(Channel, TimeStart, TimeEndurance, SampleRate, 'Custom', ReferenceVoltage, ReferenceImpedance)
17 | % returns a blank analog signal with customized arguments.
18 | % AnalogSignal = InitAnalogSignal(Channel, TimeStart, TimeEndurance, SampleRate, 'Template', 'As', TemplateSignal)
19 | % returns a blank analog signal with the arguments the same as the 'TemplateSignal'.
20 | %Input Arguments:
21 | % Channel: (positive integer scalar)
22 | % Number of channels.
23 | % TimeStart: (double)
24 | % The signal start time in second.
25 | % TimeEndurance: (double)
26 | % The signal endurance time in second.
27 | % SampleRate: (double)
28 | % The signal sample rate in Sa/s.
29 | % ReferencePreset: (string)
30 | % Signal preset reference arguments, it is one of blow:
31 | % '50Ohm-2.5V': Signal with 2.5V reference voltage and 50Ohm impedance.
32 | % '50Ohm-3.3V': Signal with 3.3V reference voltage and 50Ohm impedance.
33 | % '50Ohm-1V': Signal with 1V reference voltage and 50Ohm impedance.
34 | % ReferenceVoltage: (double)
35 | % Reference voltage of the analog signal in V. For example, the
36 | % reference voltage is 2.5 means that 1 in the signal represents 2.5V
37 | % in reality.
38 | % ReferenceImpedance: (double)
39 | % Reference impedance of the analog system in Ohm. It means the
40 | % characteristic impedance of the transmission line used in the
41 | % simulation system. It is 50Ohm most of the time in modern radio
42 | % system. Because 50Ohm is the balance of the power capacity and
43 | % power loss of the transmission line. But other impedances will also
44 | % be chosen for other reasons (75Ohm for lowest power loss).
45 | %Output Arguments:
46 | % AnalogSignal: (AnalogSignal)
47 | % Blank analog signal.
48 | %Author:
49 | % Tifer King
50 | %License:
51 | % Please refer to the 'LICENSE' file included in the root directory
52 | % of the project.
53 |
54 | DefaultChannel = 1;
55 | DefaultTimeStart = 0;
56 | DefaultTimeEndurance = 0;
57 | DefaultSampleRate = 1;
58 | DefaultReferencePreset = '50Ohm-2.5V';
59 | DefaultReferenceVoltage = 2.5;
60 | DefaultReferenceImpedance = 50;
61 | DefaultTemplate = struct;
62 | ExpectedReferencePreset = {'50Ohm-2.5V','50Ohm-3.3V','50Ohm-1V','Custom','Template'};
63 | InPar = inputParser;
64 | ValidScalarPosNum = @(x) isnumeric(x) && isscalar(x) && (x > 0);
65 | ValidScalarNoNegNum = @(x) isnumeric(x) && isscalar(x) && (x >= 0);
66 | addOptional(InPar,'Channel', DefaultChannel,ValidScalarPosNum);
67 | addOptional(InPar,'TimeStart',DefaultTimeStart,@isnumeric);
68 | addOptional(InPar,'TimeEndurance',DefaultTimeEndurance,ValidScalarNoNegNum);
69 | addOptional(InPar,'SampleRate',DefaultSampleRate,ValidScalarPosNum);
70 | addOptional(InPar,'ReferencePreset',DefaultReferencePreset,@(x) any(validatestring(x,ExpectedReferencePreset)));
71 | addOptional(InPar,'ReferenceVoltage',DefaultReferenceVoltage,ValidScalarPosNum);
72 | addOptional(InPar,'ReferenceImpedance',DefaultReferenceImpedance,ValidScalarPosNum);
73 | addParameter(InPar,'As',DefaultTemplate,@isstruct);
74 | parse(InPar,varargin{:});
75 |
76 | AnalogSignal = struct;
77 | SignalLength = round(InPar.Results.TimeEndurance * InPar.Results.SampleRate);
78 | AnalogSignal.ChannelNum = InPar.Results.Channel;
79 | AnalogSignal.TimeStart = InPar.Results.TimeStart;
80 | AnalogSignal.TimeEndurance = SignalLength * (1 / InPar.Results.SampleRate);
81 | AnalogSignal.SampleRate = InPar.Results.SampleRate;
82 | AnalogSignal.Signal = zeros(InPar.Results.Channel, SignalLength);
83 |
84 | if(strcmp(InPar.Results.ReferencePreset, "50Ohm-2.5V"))
85 | AnalogSignal.ReferenceVoltage = 2.5;
86 | AnalogSignal.ReferenceImpedance = 50;
87 | elseif(strcmp(InPar.Results.ReferencePreset, "50Ohm-3.3V"))
88 | AnalogSignal.ReferenceVoltage = 3.3;
89 | AnalogSignal.ReferenceImpedance = 50;
90 | elseif(strcmp(InPar.Results.ReferencePreset, "50Ohm-1V"))
91 | AnalogSignal.ReferenceVoltage = 1;
92 | AnalogSignal.ReferenceImpedance = 50;
93 | elseif(strcmp(InPar.Results.ReferencePreset, "Custom"))
94 | AnalogSignal.ReferenceVoltage = InPar.Results.ReferenceVoltage;
95 | AnalogSignal.ReferenceImpedance = InPar.Results.ReferenceImpedance;
96 | elseif(strcmp(InPar.Results.ReferencePreset, "Template"))
97 | AnalogSignal.ReferenceVoltage = InPar.Results.As.ReferenceVoltage;
98 | AnalogSignal.ReferenceImpedance = InPar.Results.As.ReferenceImpedance;
99 | end
100 | end
101 |
102 |
--------------------------------------------------------------------------------
/lib/InitChannelImpulseResponse.m:
--------------------------------------------------------------------------------
1 | function [ChannelImpulseResponse] = InitChannelImpulseResponse(varargin)
2 | %InitChannelImpulseResponse Initialize a structure containing channel impulse response.
3 | %Introduction:
4 | % Initialize the struct to contain multi-channel to multi-channel impulse
5 | % response. It will provide a standard struct to log channel impulse
6 | % response. Using the standard channel impulse response struct will bring
7 | % much more convenience.
8 | %Syntax:
9 | % ChannelImpulseResponse = InitChannelImpulseResponse(InputChannel, OutputChannel, TimeStart, TimeEndurance, SampleRate,...
10 | % AttenuationdB, MaximumFrequency, ChannelPreset)
11 | %Description:
12 | % ChannelImpulseResponse = InitChannelImpulseResponse(InputChannel, OutputChannel, TimeStart, TimeEndurance, SampleRate,...
13 | % AttenuationdB, MaximumFrequency, ChannelPreset)
14 | % returns a preset channel impulse response.
15 | %Input Arguments:
16 | % InputChannel: (positive integer scalar)
17 | % Number of transmitte channels.
18 | % OutputChannel: (positive integer scalar)
19 | % Number of receive channels.
20 | % TimeStart: (double)
21 | % The impulse start time in second.
22 | % TimeEndurance: (double)
23 | % The impluse endurance time in second.
24 | % SampleRate: (double)
25 | % The impluse sample rate in Sa/s.
26 | % AttenuationdB: (double)
27 | % Signal attenuation in dB. This enables the impulse response to be
28 | % rescaled into an appropriate range.
29 | % MaximumFrequency: (double)
30 | % The maximum frequency that channel impluse respose will simulate.
31 | % ChannelPreset: (string)
32 | % Channel preset, it is one of blow:
33 | % 'Ideal': Ideal channel, the input signal will have no change.
34 | % 'Gaussian': The channel is represented by a Gaussian impulse.
35 | % Moreover, each transmitted channel is only connected to its
36 | % corresponding received channel.
37 | % 'Custom': Channel will be blank.
38 | %Output Arguments:
39 | % ChannelImpulseResponse: (ChannelImpulseResponse)
40 | % A channel impulse response.
41 | %Author:
42 | % Tifer King
43 | %License:
44 | % Please refer to the 'LICENSE' file included in the root directory
45 | % of the project.
46 |
47 | DefaultInputChannel = 1;
48 | DefaultOutputChannel = 1;
49 | DefaultTimeStart = 0;
50 | DefaultTimeEndurance = 0;
51 | DefaultSampleRate = 1;
52 | DefaultAttenuationdB = 0;
53 | DefaultChannelPreset = 'Ideal';
54 | DefaultMaximumFrequency = 0;
55 | ExpectedChannelPreset = {'Ideal','Gaussian','Custom'};
56 | InPar = inputParser;
57 | ValidScalarPosNum = @(x) isnumeric(x) && isscalar(x) && (x > 0);
58 | ValidScalarNoNegNum = @(x) isnumeric(x) && isscalar(x) && (x >= 0);
59 | addOptional(InPar,'InputChannel', DefaultInputChannel,ValidScalarPosNum);
60 | addOptional(InPar,'OutputChannel', DefaultOutputChannel,ValidScalarPosNum);
61 | addOptional(InPar,'TimeStart',DefaultTimeStart,@(x) isnumeric(x));
62 | addOptional(InPar,'TimeEndurance',DefaultTimeEndurance,ValidScalarNoNegNum);
63 | addOptional(InPar,'SampleRate',DefaultSampleRate,ValidScalarPosNum);
64 | addOptional(InPar,'AttenuationdB',DefaultAttenuationdB,@(x) isnumeric(x));
65 | addOptional(InPar,'MaximumFrequency',DefaultMaximumFrequency,ValidScalarPosNum);
66 | addOptional(InPar,'ChannelPreset',DefaultChannelPreset,@(x) any(validatestring(x,ExpectedChannelPreset)));
67 | parse(InPar,varargin{:});
68 |
69 | ChannelImpulseResponse = struct;
70 | ChannelLength = floor(InPar.Results.TimeEndurance * InPar.Results.SampleRate);
71 | ChannelImpulseResponse.ChannelInputNum = InPar.Results.InputChannel;
72 | ChannelImpulseResponse.ChannelOutputNum = InPar.Results.OutputChannel;
73 | ChannelImpulseResponse.TimeStart = InPar.Results.TimeStart;
74 | ChannelImpulseResponse.TimeEndurance = ChannelLength * (1 / InPar.Results.SampleRate);
75 | ChannelImpulseResponse.SampleRate = InPar.Results.SampleRate;
76 | ChannelImpulseResponse.AttenuationdB = InPar.Results.AttenuationdB;
77 |
78 | if(ChannelLength == 0)
79 | ChannelLength = 1;
80 | end
81 | ChannelImpulseResponse.Signal = zeros(InPar.Results.OutputChannel, InPar.Results.InputChannel, ChannelLength);
82 | if(InPar.Results.InputChannel > InPar.Results.OutputChannel)
83 | Channel = InPar.Results.OutputChannel;
84 | else
85 | Channel = InPar.Results.InputChannel;
86 | end
87 |
88 | if(strcmp(InPar.Results.ChannelPreset, "Ideal"))
89 | ChannelImpulseResponse.Signal(:,:,1) = eye(InPar.Results.OutputChannel, InPar.Results.InputChannel);
90 | elseif(strcmp(InPar.Results.ChannelPreset, "Gaussian"))
91 | GaussianTau = 1 / (pi * InPar.Results.MaximumFrequency);
92 | if (GaussianTau < 10 * (1 / InPar.Results.SampleRate))
93 | warning("The Gaussian impulse sample may not have the precision needed. Please increase the channel sample rate.");
94 | end
95 | if (InPar.Results.TimeEndurance > 12 * GaussianTau)
96 | GaussianImpulse = exp(-(([1 : ChannelLength] .* (1 / InPar.Results.SampleRate) - (6 * GaussianTau)) ...
97 | .^ 2) ./ (GaussianTau .^ 2));
98 | GaussianImpulse = GaussianImpulse ./ sum(GaussianImpulse);
99 | ChannelImpulseResponse.TimeStart = InPar.Results.TimeStart - 6 * GaussianTau;
100 | else
101 | GaussianImpulse = exp(-(([1 : ChannelLength] .* (1 / InPar.Results.SampleRate) - (InPar.Results.TimeEndurance / 2)) ...
102 | .^ 2) ./ (GaussianTau .^ 2));
103 | GaussianImpulse = GaussianImpulse ./ sum(GaussianImpulse);
104 | ChannelImpulseResponse.TimeStart = InPar.Results.TimeStart - InPar.Results.TimeEndurance / 2;
105 | end
106 | for index = 1 : Channel
107 | ChannelImpulseResponse.Signal(index, index, :) = GaussianImpulse;
108 | end
109 | elseif(strcmp(InPar.Results.ChannelPreset, "Custom"))
110 | % Return a blank channel impluse respose.
111 | end
112 | end
113 |
114 |
--------------------------------------------------------------------------------
/lib/InitDigitalSignal.m:
--------------------------------------------------------------------------------
1 | function [DigitalSignal] = InitDigitalSignal(varargin)
2 | %InitDigitalSignal Initialize a structure containing multi-channel digital signals.
3 | %Introduction:
4 | % Initialize the struct to contain multi-channel digital signals, along
5 | % with the different pair, reference voltage etc. It will provide a
6 | % standard struct to log digital signal and make the analysis or tracing
7 | % easy. Using the standard digital signal struct will bring much more
8 | % convenience.
9 | %Syntax:
10 | % DigitalSignal = InitDigitalSignal(Channel, TimeStart, TimeEndurance, ClockFrequency, ReferencePreset)
11 | % DigitalSignal = InitDigitalSignal(Channel, TimeStart, TimeEndurance, ClockFrequency, 'Custom', ReferenceVoltage, ReferenceImpedance,
12 | % IsDiffDifferential, Threshold, IdealHigh, IdealLow, IdealHighNeg, IdealLowNeg)
13 | % DigitalSignal = InitDigitalSignal(Channel, TimeStart, TimeEndurance, ClockFrequency, 'Template', 'As', TemplateSignal)
14 | %Description:
15 | % DigitalSignal = InitDigitalSignal(Channel, TimeStart, TimeEndurance, ClockFrequency, ReferencePreset)
16 | % returns a blank digital signal with preset arguments.
17 | % DigitalSignal = InitDigitalSignal(Channel, TimeStart, TimeEndurance, ClockFrequency, 'Custom', ReferenceVoltage, ReferenceImpedance,
18 | % IsDiffDifferential, Threshold, IdealHigh, IdealLow, IdealHighNeg, IdealLowNeg)
19 | % returns a blank digital signal with customized arguments.
20 | % DigitalSignal = InitDigitalSignal(Channel, TimeStart, TimeEndurance, ClockFrequency, 'Template', 'As', TemplateSignal)
21 | % returns a blank digital signal with the arguments the same as the 'TemplateSignal'.
22 | %Input Arguments:
23 | % Channel: (positive integer scalar)
24 | % Number of channels.
25 | % TimeStart: (double)
26 | % The signal start time in second.
27 | % TimeEndurance: (double)
28 | % The signal endurance time in second.
29 | % ClockFrequency: (double)
30 | % The signal clock in Hz.
31 | % ReferencePreset: (string)
32 | % Signal preset reference arguments, it is one of blow:
33 | % 'CMOS': Complementary Metal-Oxide Semiconductor common logic voltage level.
34 | % 'LVCMOS3V3': Low-Voltage CMOS common logic voltage working in 3.3V.
35 | % 'LVCMOS2V5': LVCMOS common logic voltage working in 2.5V.
36 | % 'TTL': Transistor Transistor Logic common logic voltage level.
37 | % 'LVTTL3V3': Low-Voltage TTL common logic voltage working in 3.3V.
38 | % ReferenceVoltage: (double)
39 | % Reference voltage of the digital signal in V. For example, the
40 | % reference voltage is 2.5 means that 1 in the signal represents 2.5V
41 | % in reality.
42 | % ReferenceImpedance: (double)
43 | % Reference impedance of the digital system in Ohm. It means the
44 | % characteristic impedance of the transmission line used in the
45 | % simulation system. It is 50Ohm most of the time in modern radio
46 | % system. Because 50Ohm is the balance of the power capacity and
47 | % power loss of the transmission line. But other impedances will also
48 | % be chosen for other reasons (100Ohm for different pair).
49 | % IsDiffDifferential: (logic)
50 | % TRUE represents that the digital signal has a different paired
51 | % signal.
52 | % Threshold: (double)
53 | % Logic threshold for 1 or 0 in 'V'. Note: all the voltage inputs
54 | % below should under the unit 'V', and the transformation for
55 | % reference will be calculated automatically.
56 | % IdealHigh: (double)
57 | % Ideal voltage level for 1;
58 | % IdealLow: (double)
59 | % Ideal voltage level for 0;
60 | % IdealHighNeg: (double)
61 | % Ideal voltage level for 1 in different pair;
62 | % IdealLowNeg: (double)
63 | % Ideal voltage level for 0 in different pair;
64 | %Output Arguments:
65 | % DigitalSignal: (DigitalSignal)
66 | % Blank digital signal.
67 | %Author:
68 | % Tifer King
69 | %License:
70 | % Please refer to the 'LICENSE' file included in the root directory
71 | % of the project.
72 |
73 | DefaultChannel = 1;
74 | DefaultTimeStart = 0;
75 | DefaultTimeEndurance = 0;
76 | DefaultClockFrequency = 1;
77 | DefaultReferencePreset = 'LVCMOS3V3';
78 | DefaultReferenceVoltage = 3.3;
79 | DefaultReferenceImpedance = 50;
80 | DefaultIsDiffDifferential = false;
81 | DefaultThreshold = 1.65;
82 | DefaultIdealHigh = 3.3;
83 | DefaultIdealLow = 0;
84 | DefaultIdealHighNeg = 0;
85 | DefaultIdealLowNeg = 0;
86 | DefaultTemplate = struct;
87 | ExpectedReferencePreset = {'CMOS','LVCMOS3V3','LVCMOS2V5','TTL','LVTTL3V3','Custom', 'Template'};
88 | InPar = inputParser;
89 | ValidScalarPosNum = @(x) isnumeric(x) && isscalar(x) && (x > 0);
90 | ValidScalarNoNegNum = @(x) isnumeric(x) && isscalar(x) && (x >= 0);
91 | addOptional(InPar,'Channel', DefaultChannel,ValidScalarPosNum);
92 | addOptional(InPar,'TimeStart',DefaultTimeStart,@isnumeric);
93 | addOptional(InPar,'TimeEndurance',DefaultTimeEndurance,ValidScalarNoNegNum);
94 | addOptional(InPar,'ClockFrequency',DefaultClockFrequency,ValidScalarPosNum);
95 | addOptional(InPar,'ReferencePreset',DefaultReferencePreset,@(x) any(validatestring(x,ExpectedReferencePreset)));
96 | addOptional(InPar,'ReferenceVoltage',DefaultReferenceVoltage,ValidScalarPosNum);
97 | addOptional(InPar,'ReferenceImpedance',DefaultReferenceImpedance,ValidScalarPosNum);
98 | addOptional(InPar,'IsDiffDifferential',DefaultIsDiffDifferential,@(x) islogical(x));
99 | addOptional(InPar,'Threshold',DefaultThreshold,@(x) isnumeric(x));
100 | addOptional(InPar,'IdealHigh',DefaultIdealHigh,@(x) isnumeric(x));
101 | addOptional(InPar,'IdealLow',DefaultIdealLow,@(x) isnumeric(x));
102 | addOptional(InPar,'IdealHighNeg',DefaultIdealHighNeg,@(x) isnumeric(x));
103 | addOptional(InPar,'IdealLowNeg',DefaultIdealLowNeg,@(x) isnumeric(x));
104 | addParameter(InPar,'As',DefaultTemplate,@isstruct);
105 | parse(InPar,varargin{:});
106 |
107 | DigitalSignal = struct;
108 | SignalLength = round(InPar.Results.TimeEndurance * InPar.Results.ClockFrequency);
109 | DigitalSignal.ChannelNum = InPar.Results.Channel;
110 | DigitalSignal.TimeStart = InPar.Results.TimeStart;
111 | DigitalSignal.TimeEndurance = SignalLength * (1 / InPar.Results.ClockFrequency);
112 | DigitalSignal.ClockFrequency = InPar.Results.ClockFrequency;
113 | DigitalSignal.Signal = zeros(InPar.Results.Channel, SignalLength);
114 |
115 | if(strcmp(InPar.Results.ReferencePreset, "CMOS"))
116 | DigitalSignal.SignalNeg = [];
117 | DigitalSignal.IsDiffDifferential = false;
118 | DigitalSignal.ReferenceVoltage = 5;
119 | DigitalSignal.ReferenceImpedance = 50;
120 | DigitalSignal.Threshold = 2.5 / 5;
121 | DigitalSignal.IdealHigh = 5 / 5;
122 | DigitalSignal.IdealLow = 0 / 5;
123 | DigitalSignal.IdealHighNeg = 0 / 5;
124 | DigitalSignal.IdealLowNeg = 0 / 5;
125 | elseif(strcmp(InPar.Results.ReferencePreset, "LVCMOS3V3"))
126 | DigitalSignal.SignalNeg = [];
127 | DigitalSignal.IsDiffDifferential = false;
128 | DigitalSignal.ReferenceVoltage = 3.3;
129 | DigitalSignal.ReferenceImpedance = 50;
130 | DigitalSignal.Threshold = 1.65 / 3.3;
131 | DigitalSignal.IdealHigh = 3.3 / 3.3;
132 | DigitalSignal.IdealLow = 0 / 3.3;
133 | DigitalSignal.IdealHighNeg = 0 / 3.3;
134 | DigitalSignal.IdealLowNeg = 0 / 3.3;
135 | elseif(strcmp(InPar.Results.ReferencePreset, "LVCMOS2V5"))
136 | DigitalSignal.SignalNeg = [];
137 | DigitalSignal.IsDiffDifferential = false;
138 | DigitalSignal.ReferenceVoltage = 2.5;
139 | DigitalSignal.ReferenceImpedance = 50;
140 | DigitalSignal.Threshold = 1.25 / 2.5;
141 | DigitalSignal.IdealHigh = 2.5 / 2.5;
142 | DigitalSignal.IdealLow = 0 / 2.5;
143 | DigitalSignal.IdealHighNeg = 0 / 2.5;
144 | DigitalSignal.IdealLowNeg = 0 / 2.5;
145 | elseif(strcmp(InPar.Results.ReferencePreset, "TTL"))
146 | DigitalSignal.SignalNeg = [];
147 | DigitalSignal.IsDiffDifferential = false;
148 | DigitalSignal.ReferenceVoltage = 5;
149 | DigitalSignal.ReferenceImpedance = 50;
150 | DigitalSignal.Threshold = 1.5 / 5;
151 | DigitalSignal.IdealHigh = 5 / 5;
152 | DigitalSignal.IdealLow = 0 / 5;
153 | DigitalSignal.IdealHighNeg = 0 / 5;
154 | DigitalSignal.IdealLowNeg = 0 / 5;
155 | elseif(strcmp(InPar.Results.ReferencePreset, "LVTTL3V3"))
156 | DigitalSignal.SignalNeg = [];
157 | DigitalSignal.IsDiffDifferential = false;
158 | DigitalSignal.ReferenceVoltage = 3.3;
159 | DigitalSignal.ReferenceImpedance = 50;
160 | DigitalSignal.Threshold = 1.5 / 3.3;
161 | DigitalSignal.IdealHigh = 3.3 / 3.3;
162 | DigitalSignal.IdealLow = 0 / 3.3;
163 | DigitalSignal.IdealHighNeg = 0 / 3.3;
164 | DigitalSignal.IdealLowNeg = 0 / 3.3;
165 | elseif(strcmp(InPar.Results.ReferencePreset, "Custom"))
166 | DigitalSignal.SignalNeg = [];
167 | DigitalSignal.IsDiffDifferential = InPar.Results.IsDiffDifferential;
168 | DigitalSignal.ReferenceVoltage = InPar.Results.ReferenceVoltage;
169 | DigitalSignal.ReferenceImpedance = InPar.Results.ReferenceImpedance;
170 | DigitalSignal.Threshold = InPar.Results.Threshold / InPar.Results.ReferenceVoltage;
171 | DigitalSignal.IdealHigh = InPar.Results.IdealHigh / InPar.Results.ReferenceVoltage;
172 | DigitalSignal.IdealLow = InPar.Results.IdealLow / InPar.Results.ReferenceVoltage;
173 | DigitalSignal.IdealHighNeg = InPar.Results.IdealHighNeg / InPar.Results.ReferenceVoltage;
174 | DigitalSignal.IdealLowNeg = InPar.Results.IdealLowNeg / InPar.Results.ReferenceVoltage;
175 | elseif(strcmp(InPar.Results.ReferencePreset, "Template"))
176 | DigitalSignal.SignalNeg = [];
177 | DigitalSignal.IsDiffDifferential = InPar.Results.As.IsDiffDifferential;
178 | DigitalSignal.ReferenceVoltage = InPar.Results.As.ReferenceVoltage;
179 | DigitalSignal.ReferenceImpedance = InPar.Results.As.ReferenceImpedance;
180 | DigitalSignal.Threshold = InPar.Results.As.Threshold;
181 | DigitalSignal.IdealHigh = InPar.Results.As.IdealHigh;
182 | DigitalSignal.IdealLow = InPar.Results.As.IdealLow;
183 | DigitalSignal.IdealHighNeg = InPar.Results.As.IdealHighNeg;
184 | DigitalSignal.IdealLowNeg = InPar.Results.As.IdealLowNeg;
185 | end
186 | end
187 |
--------------------------------------------------------------------------------
/lib/Modulation.m:
--------------------------------------------------------------------------------
1 | function [IQSignal] = Modulation(DigitalSignal, Mode, SignalPreset)
2 | %Modulation Convert binary data to IQ signal by modulation.
3 | %Introduction:
4 | % This function converts signal to IQ symbol by modulation and paired with
5 | % 'Demodulation'.
6 | %Syntax:
7 | % IQSignal = Modulation(DigitalSignal, Mode, SignalPreset)
8 | %Description:
9 | % IQSignal = Modulation(DigitalSignal, Mode, SignalPreset)
10 | % returns the IQ symbol.
11 | %Input Arguments:
12 | % DigitalSignal: (DigitalSignal)
13 | % Binary stream signals.
14 | % Mode: (string)
15 | % Method of modulation, it is one of blow:
16 | % 'ASK': Amplitude Shift Keying.
17 | % 'BPSK': Binary Phase-Shift Keying.
18 | % 'QPSK': Quadrature Phase Shift Keying.
19 | % '16QAM': 16-points Quadrature Amplitude Modulation.
20 | % '64QAM': 64-points Quadrature Amplitude Modulation.
21 | % '256QAM': 256-points Quadrature Amplitude Modulation.
22 | % '1024QAM': 1024-points Quadrature Amplitude Modulation.
23 | % SignalPreset: (string)
24 | % The preset reference voltage, impedance and different pair of
25 | % signal, please refer to 'InitAnalogSignal' for more detail.
26 | %Output Arguments:
27 | % IQSignal: (AnalogSignal)
28 | % IQ symbol signal.
29 | %Author:
30 | % Tifer King
31 | %License:
32 | % Please refer to the 'LICENSE' file included in the root directory
33 | % of the project.
34 |
35 | IQSignal = InitAnalogSignal(DigitalSignal.ChannelNum, DigitalSignal.TimeStart, DigitalSignal.TimeEndurance, DigitalSignal.ClockFrequency, SignalPreset);
36 | if(strcmp(Mode, "ASK"))
37 | IQSignal.Signal = DigitalSignal.Signal;
38 | elseif(strcmp(Mode, "BPSK"))
39 | IQSignal.Signal = DigitalSignal.Signal * 2 - 1;
40 | elseif (strcmp(Mode, "QPSK"))
41 | IQSignal.Signal = DigitalSignal.Signal(:,1:2:end) * 2 - 1 + (DigitalSignal.Signal(:,2:2:end) * 2 - 1) * 1i;
42 | IQSignal.SampleRate = DigitalSignal.ClockFrequency / 2;
43 | elseif (strcmp(Mode, "16QAM"))
44 | IQSignal.Signal = ((DigitalSignal.Signal(:,1:4:end) * 2 + DigitalSignal.Signal(:,2:4:end)) * 2 / 3 - 1) + ...
45 | ((DigitalSignal.Signal(:,3:4:end) * 2 + DigitalSignal.Signal(:,4:4:end)) * 2 / 3 - 1) * 1i;
46 | IQSignal.SampleRate = DigitalSignal.ClockFrequency / 4;
47 | elseif (strcmp(Mode, "64QAM"))
48 | IQSignal.Signal = ((DigitalSignal.Signal(:,1:6:end) * 4 + DigitalSignal.Signal(:,2:6:end) * 2 + DigitalSignal.Signal(:,3:6:end)) * 2 / 7 - 1) + ...
49 | ((DigitalSignal.Signal(:,4:6:end) * 4 + DigitalSignal.Signal(:,5:6:end) * 2 + DigitalSignal.Signal(:,6:6:end)) * 2 / 7 - 1) * 1i;
50 | IQSignal.SampleRate = DigitalSignal.ClockFrequency / 6;
51 | elseif (strcmp(Mode, "256QAM"))
52 | IQSignal.Signal = ((DigitalSignal.Signal(:,1:8:end) * 8 + DigitalSignal.Signal(:,2:8:end) * 4 + DigitalSignal.Signal(:,3:8:end) * 2 + DigitalSignal.Signal(:,4:8:end)) * 2 / 15 - 1) + ...
53 | ((DigitalSignal.Signal(:,5:8:end) * 8 + DigitalSignal.Signal(:,6:8:end) * 4 + DigitalSignal.Signal(:,7:8:end) * 2 + DigitalSignal.Signal(:,8:8:end)) * 2 / 15 - 1) * 1i;
54 | IQSignal.SampleRate = DigitalSignal.ClockFrequency / 8;
55 | elseif (strcmp(Mode, "1024QAM"))
56 | IQSignal.Signal = ((DigitalSignal.Signal(:,1:10:end) * 16 + DigitalSignal.Signal(:,2:10:end) * 8 + DigitalSignal.Signal(:,3:10:end) * 4 + ...
57 | DigitalSignal.Signal(:,4:10:end) * 2 + DigitalSignal.Signal(:,5:10:end)) * 2 / 31 - 1) + ...
58 | ((DigitalSignal.Signal(:,6:10:end) * 16 + DigitalSignal.Signal(:,7:10:end) * 8 + DigitalSignal.Signal(:,8:10:end) * 4 + ...
59 | DigitalSignal.Signal(:,9:10:end) * 2 + DigitalSignal.Signal(:,10:10:end)) * 2 / 31 - 1) * 1i;
60 | IQSignal.SampleRate = DigitalSignal.ClockFrequency / 10;
61 | end
62 | end
63 |
64 |
--------------------------------------------------------------------------------
/lib/OFDMLoad.m:
--------------------------------------------------------------------------------
1 | function [OFDMSignal] = OFDMLoad(IQSignal, SubCarrierNum, CyclicPrefixNum)
2 |
3 | StartTime = IQSignal.TimeStart + SubCarrierNum * (1 / IQSignal.SampleRate);
4 | EnduranceTime = (SubCarrierNum + CyclicPrefixNum) / SubCarrierNum * IQSignal.TimeEndurance;
5 | OFDMSignal = InitAnalogSignal(IQSignal.ChannelNum, StartTime, EnduranceTime, IQSignal.SampleRate, 'Template', 'As', IQSignal);
6 | [ChannelNum, IQLength] = size(IQSignal.Signal);
7 | RawLength = IQLength / SubCarrierNum;
8 | if(round(RawLength) ~= RawLength)
9 | warning("The input signal length is not an integer multiple of 'SubCarrierNum'.");
10 | end
11 | RawSignal = reshape(IQSignal.Signal', SubCarrierNum, RawLength, ChannelNum);
12 | BaseSignal = ifft(RawSignal) * sqrt(SubCarrierNum);
13 | OrthogonalSignal = reshape([BaseSignal((SubCarrierNum - CyclicPrefixNum + 1) : SubCarrierNum, :, :); BaseSignal], RawLength * (SubCarrierNum + CyclicPrefixNum), ChannelNum)';
14 | OFDMSignal.Signal = OrthogonalSignal;
15 | end
16 |
17 |
--------------------------------------------------------------------------------
/lib/OFDMUnload.m:
--------------------------------------------------------------------------------
1 | function [IQSignal] = OFDMUnload(OFDMSignal, SubCarrierNum, CyclicPrefixNum)
2 |
3 | StartTime = OFDMSignal.TimeStart + (SubCarrierNum + CyclicPrefixNum) * (1 / OFDMSignal.SampleRate);
4 | EnduranceTime = SubCarrierNum / (SubCarrierNum + CyclicPrefixNum) * OFDMSignal.TimeEndurance;
5 | StartTimeMin = OFDMSignal.TimeStart + OFDMSignal.TimeEndurance - EnduranceTime;
6 | if (StartTime < StartTimeMin)
7 | StartTime = StartTimeMin;
8 | end
9 | IQSignal = InitAnalogSignal(OFDMSignal.ChannelNum, StartTime, EnduranceTime, OFDMSignal.SampleRate, 'Template', 'As', OFDMSignal);
10 | [ChannelNum, OFDMLength] = size(OFDMSignal.Signal);
11 | RawLength = OFDMLength / (SubCarrierNum + CyclicPrefixNum);
12 | if(round(RawLength) ~= RawLength)
13 | warning("The input signal length is not an integer multiple of 'SubCarrierNum'.");
14 | end
15 | RawSignal = reshape(OFDMSignal.Signal', (SubCarrierNum + CyclicPrefixNum), RawLength, ChannelNum);
16 | BaseSignal = fft(RawSignal((CyclicPrefixNum + 1) : end, :, :)) / sqrt(SubCarrierNum);
17 | RecoveredSignal = reshape(BaseSignal, RawLength * SubCarrierNum, ChannelNum)';
18 | IQSignal.Signal = RecoveredSignal;
19 | end
20 |
21 |
--------------------------------------------------------------------------------
/lib/OrthogonalMatrixLoad.m:
--------------------------------------------------------------------------------
1 | function [OrthogonalSignal] = OrthogonalMatrixLoad(IQSignal, varargin)
2 | %OrthogonalMatrixLoad Load orthogonal matrix to multi-channel.
3 | %Introduction:
4 | % In some special scenarios, the channel does not have enough
5 | % orthogonality. The orthogonal matrix can add more orthogonality to the
6 | % signal in order to increase the communication capacity.
7 | %Syntax:
8 | % OrthogonalSignal = OrthogonalMatrixLoad(IQSignal, MatrixPreset)
9 | % OrthogonalSignal = OrthogonalMatrixLoad(IQSignal, 'Custom', OrthogonalMatrix)
10 | %Description:
11 | % OrthogonalSignal = OrthogonalMatrixLoad(IQSignal, MatrixPreset)
12 | % returns the base signal with preset orthogonal matrix.
13 | % OrthogonalSignal = OrthogonalMatrixLoad(IQSignal, 'Custom', OrthogonalMatrix)
14 | % returns the base signal with custom orthogonal matrix.
15 | %Input Arguments:
16 | % IQSignal: (AnalogSignal)
17 | % Baseband signals.
18 | % MatrixPreset: (string)
19 | % Type of matrix, it is one of blow:
20 | % 'OAM': IDFT matrix.
21 | % 'ZC': Zadoff-Chu matrix.
22 | % OrthogonalMatrix: (matrix)
23 | % User defined orthogonal matrix.
24 | %Output Arguments:
25 | % OrthogonalSignal: (AnalogSignal)
26 | % Baseband signals loaded orthogonal matrix.
27 | %Author:
28 | % Tifer King
29 | %License:
30 | % Please refer to the 'LICENSE' file included in the root directory
31 | % of the project.
32 |
33 | OrthogonalSignal = IQSignal;
34 | DefaultOrthogonalMatrix = eye(IQSignal.ChannelNum);
35 | ExpectedMatrixPreset = {'OAM','ZC','Custom'};
36 | InPar = inputParser;
37 | addRequired(InPar,'MatrixPreset',@(x) any(validatestring(x,ExpectedMatrixPreset)));
38 | addOptional(InPar,'OrthogonalMatrix', DefaultOrthogonalMatrix,@ismatrix);
39 | parse(InPar,varargin{:});
40 |
41 | if(strcmp(InPar.Results.MatrixPreset, "OAM"))
42 | OrthogonalMatrix = dftmtx(IQSignal.ChannelNum)' / IQSignal.ChannelNum * sqrt(IQSignal.ChannelNum);
43 | elseif(strcmp(InPar.Results.MatrixPreset, "ZC"))
44 | N = IQSignal.ChannelNum;
45 | u = 1;
46 | q = 1;
47 | cf = mod(IQSignal.ChannelNum, 2);
48 | ZadoffChuSequence = exp(-1i * (pi * 1 * u * [0 : N - 1] .* ([0 : N - 1] + cf + 2 * q)) / N);
49 | OrthogonalMatrix = dftmtx(N)' * diag(ZadoffChuSequence * dftmtx(N)') * dftmtx(N) / N;
50 | elseif(strcmp(InPar.Results.MatrixPreset, "Custom"))
51 | OrthogonalMatrix = InPar.Results.OrthogonalMatrix;
52 | end
53 | OrthogonalSignal.Signal = OrthogonalMatrix * IQSignal.Signal;
54 | end
55 |
56 |
--------------------------------------------------------------------------------
/lib/OrthogonalMatrixUnload.m:
--------------------------------------------------------------------------------
1 | function [OrthogonalSignal] = OrthogonalMatrixUnload(IQSignal, varargin)
2 | %OrthogonalMatrixUnload Unload orthogonal matrix for multi-channel.
3 | %Introduction:
4 | % In some special scenarios, the channel does not have enough
5 | % orthogonality. The orthogonal matrix can add more orthogonality to the
6 | % signal in order to increase the communication capacity.
7 | %Syntax:
8 | % OrthogonalSignal = OrthogonalMatrixUnload(IQSignal, MatrixPreset)
9 | % OrthogonalSignal = OrthogonalMatrixUnload(IQSignal, 'Custom', OrthogonalMatrix)
10 | %Description:
11 | % OrthogonalSignal = OrthogonalMatrixUnload(IQSignal, MatrixPreset)
12 | % returns the base signal decoded by preset orthogonal matrix.
13 | % OrthogonalSignal = OrthogonalMatrixUnload(IQSignal, 'Custom', OrthogonalMatrix)
14 | % returns the base signal decoded by custom orthogonal matrix.
15 | %Input Arguments:
16 | % IQSignal: (AnalogSignal)
17 | % Orthogonal signals.
18 | % MatrixPreset: (string)
19 | % Type of matrix, it is one of blow:
20 | % 'OAM': DFT matrix.
21 | % OrthogonalMatrix: (matrix)
22 | % User defined orthogonal matrix.
23 | %Output Arguments:
24 | % OrthogonalSignal: (AnalogSignal)
25 | % Baseband signals.
26 | %Author:
27 | % Tifer King
28 | %License:
29 | % Please refer to the 'LICENSE' file included in the root directory
30 | % of the project.
31 |
32 | OrthogonalSignal = IQSignal;
33 | DefaultOrthogonalMatrix = eye(IQSignal.ChannelNum);
34 | ExpectedMatrixPreset = {'OAM','Custom'};
35 | InPar = inputParser;
36 | addRequired(InPar,'MatrixPreset',@(x) any(validatestring(x,ExpectedMatrixPreset)));
37 | addOptional(InPar,'OrthogonalMatrix', DefaultOrthogonalMatrix,@ismatrix);
38 | parse(InPar,varargin{:});
39 |
40 | if(strcmp(InPar.Results.MatrixPreset, "OAM"))
41 | OrthogonalMatrix = dftmtx(IQSignal.ChannelNum) / sqrt(IQSignal.ChannelNum);
42 | elseif(strcmp(InPar.Results.MatrixPreset, "Custom"))
43 | OrthogonalMatrix = InPar.Results.OrthogonalMatrix;
44 | end
45 | OrthogonalSignal.Signal = OrthogonalMatrix * IQSignal.Signal;
46 | end
--------------------------------------------------------------------------------
/lib/PreambleGen.m:
--------------------------------------------------------------------------------
1 | function [PreambleSignal] = PreambleGen(ChannelNum, Length, SampleRate, PreambleGenerateFx, SignalPreset)
2 | %PreambleGen Generate the frame preamble.
3 | %Introduction:
4 | % Generate a frame preamble using the specified function.
5 | %Syntax:
6 | % PreambleSignal = PreambleGen(ChannelNum, Length, SampleRate, PreambleGenerateFx, SignalPreset)
7 | %Description:
8 | % PreambleSignal = PreambleGen(ChannelNum, Length, SampleRate, PreambleGenerateFx, SignalPreset)
9 | % returns the frame preamble.
10 | %Input Arguments:
11 | % ChannelNum: (positive integer scalar)
12 | % Number of channels.
13 | % Length: (positive integer scalar)
14 | % Preamble length.
15 | % SampleRate: (double)
16 | % Preamble sample rate in Sa/s.
17 | % PreambleGenerateFx: (function)
18 | % Design preamble sequence. The function will called in:
19 | % PreambleGenerateFx(ChannelNum, Length);
20 | % SignalPreset: (string)
21 | % The preset reference voltage, impedance and different pair of
22 | % signal, please refer to 'InitAnalogSignal' for more detail.
23 | %Output Arguments:
24 | % PreambleSignal: (AnalogSignal)
25 | % Generated preamble signal.
26 | %Author:
27 | % Tifer King
28 | %License:
29 | % Please refer to the 'LICENSE' file included in the root directory
30 | % of the project.
31 |
32 | TimeEndurance = Length / SampleRate;
33 | PreambleSignal = InitAnalogSignal(ChannelNum, 0, TimeEndurance, SampleRate, SignalPreset);
34 | PreambleSignal.Signal = PreambleGenerateFx(ChannelNum, Length);
35 | end
36 |
37 |
--------------------------------------------------------------------------------
/probe/ProbeBitError.m:
--------------------------------------------------------------------------------
1 | function [ErrorRate, ErrorNum, TotalNum, ErrorPos] = ProbeBitError(DataTx, DataRx, Title)
2 | %ProbeBitError A probe that detects bit errors in the binary stream.
3 | %Introduction:
4 | % Compare two binary streams and get the error bits.
5 | %Syntax:
6 | % ErrorRate = ProbeBitError(DataTx, DataRx)
7 | % [ErrorRate, ErrorNum, TotalNum, ErrorPos] = ProbeBitError(DataTx, DataRx)
8 | % [ ___ ] = ProbeBitError(DataTx, DataRx, Title)
9 | %Description:
10 | % ErrorRate = ProbeBitError(DataTx, DataRx)
11 | % returns the bit error rate.
12 | % [ErrorRate, ErrorNum, TotalNum, ErrorPos] = ProbeBitError(DataTx, DataRx)
13 | % returns a vector that contains the bit error rate, the total number
14 | % of errors, the total number of bits and the error positions.
15 | % [ ___ ] = ProbeBitError(DataTx, DataRx, Title)
16 | % display all these results after 'Title' on console.
17 | %Input Arguments:
18 | % DataTx: (DigitalSignal)
19 | % Data stream transmitted.
20 | % DataRx: (DigitalSignal)
21 | % Data stream received.
22 | % Title: (string)
23 | % Probe display title.
24 | %Output Arguments:
25 | % ErrorRate: (double)
26 | % The error rate of the binary stream.
27 | % ErrorNum: (integer)
28 | % The number of error bits.
29 | % TotalNum: (integer)
30 | % The number of total transmitted bits.
31 | % ErrorPos: (vector)
32 | % The index of every error bit.
33 | %Author:
34 | % Tifer King
35 | %License:
36 | % Please refer to the 'LICENSE' file included in the root directory
37 | % of the project.
38 |
39 | if(size(DataTx.Signal) == size(DataRx.Signal))
40 | ErrorNum = sum(DataTx.Signal ~= DataRx.Signal, "all");
41 | [BitChannel, BitLength] = size(DataTx.Signal);
42 | TotalNum = BitChannel * BitLength;
43 | ErrorRate = ErrorNum / TotalNum;
44 | ErrorPos = find(DataTx.Signal ~= DataRx.Signal);
45 | else
46 | % Bit stream with different size
47 | [BitChannel, BitLength] = size(DataTx.Signal);
48 | TotalNum = BitChannel * BitLength;
49 | ErrorNum = TotalNum;
50 | ErrorRate = TotalNum / TotalNum;
51 | ErrorPos = [1 : TotalNum];
52 | end
53 | if(exist('Title','var'))
54 | DisplayString = [Title '_TotalBitNum = ' num2str(TotalNum) ' bits.'];
55 | disp(DisplayString);
56 | DisplayString = [Title '_ErrorNum = ' num2str(ErrorNum) ' bits.'];
57 | disp(DisplayString);
58 | DisplayString = [Title '_ErrorRate = ' num2str(ErrorRate * 100) ' %.'];
59 | disp(DisplayString);
60 | end
61 | end
62 |
63 |
--------------------------------------------------------------------------------
/probe/ProbeChannelImpulse.m:
--------------------------------------------------------------------------------
1 | function [] = ProbeChannelImpulse(ChannelImpulseResponse, SeparateDisplay, Title)
2 | figure;
3 | TiledFigure = tiledlayout("flow");
4 | if (~exist('SeparateDisplay', 'var'))
5 | SeparateDisplay = false;
6 | end
7 | Horizon = [0 : 1 / ChannelImpulseResponse.SampleRate : ChannelImpulseResponse.TimeEndurance - 1 / ChannelImpulseResponse.SampleRate];
8 | [HorizonUnit, HorizonFactor] = UnitConvert(ChannelImpulseResponse.TimeEndurance, 's');
9 | [StartTimeUnit, StartTimeFactor] = UnitConvert(ChannelImpulseResponse.TimeStart, 's');
10 | Horizon = HorizonFactor * Horizon;
11 | if (~SeparateDisplay)
12 | nexttile;
13 | end
14 | for indexRx = 1 : ChannelImpulseResponse.ChannelOutputNum;
15 | if (SeparateDisplay)
16 | nexttile;
17 | end
18 | for indexTx = 1 : ChannelImpulseResponse.ChannelInputNum;
19 | ChannelImpulseFig = reshape(ChannelImpulseResponse.Signal(indexRx,indexTx,:),1,[]);
20 | plot(Horizon, ChannelImpulseFig);
21 | xlim([0 (ChannelImpulseResponse.TimeEndurance * HorizonFactor)]);
22 | hold on;
23 | end
24 | end
25 | xlabel(TiledFigure, [HorizonUnit ' (Start@' num2str(ChannelImpulseResponse.TimeStart * StartTimeFactor) ' ' StartTimeUnit ')']);
26 | if (exist('Title', 'var'))
27 | title(TiledFigure, Title);
28 | end
29 | hold off;
30 | drawnow;
31 | end
32 |
33 |
--------------------------------------------------------------------------------
/probe/ProbeConstellation.m:
--------------------------------------------------------------------------------
1 | function [] = ProbeConstellation(Signal, SeparateDisplay, ConstellationMap, Title)
2 | figure;
3 | TiledFigure = tiledlayout("flow");
4 | if (~exist('SeparateDisplay', 'var'))
5 | SeparateDisplay = false;
6 | end
7 | if (exist('ConstellationMap', 'var'))
8 | if (isstring(ConstellationMap) || ischar(ConstellationMap))
9 | % Some preset configuration
10 | if (strcmp(ConstellationMap, 'ASK'))
11 | ConstellationMap = [(0 + 0i) (1 + 0i)];
12 | elseif (strcmp(ConstellationMap, 'BPSK'))
13 | ConstellationMap = [(-1 + 0i) (1 + 0i)];
14 | elseif (strcmp(ConstellationMap, 'QPSK'))
15 | ConstellationMap = [(1 + 1i) (1 + -1i) (-1 + 1i) (-1 + -1i)];
16 | elseif (strcmp(ConstellationMap, '16QAM'))
17 | ConstellationMap = [];
18 | for indexI = 0 : 3
19 | for indexQ = 0 : 3
20 | ConstellationMap = [ConstellationMap ((indexI / 1.5 - 1) + (indexQ / 1.5 - 1) * 1i)];
21 | end
22 | end
23 | elseif (strcmp(ConstellationMap, '64QAM'))
24 | ConstellationMap = [];
25 | for indexI = 0 : 7
26 | for indexQ = 0 : 7
27 | ConstellationMap = [ConstellationMap ((indexI / 3.5 - 1) + (indexQ / 3.5 - 1) * 1i)];
28 | end
29 | end
30 | elseif (strcmp(ConstellationMap, '256QAM'))
31 | ConstellationMap = [];
32 | for indexI = 0 : 15
33 | for indexQ = 0 : 15
34 | ConstellationMap = [ConstellationMap ((indexI / 7.5 - 1) + (indexQ / 7.5 - 1) * 1i)];
35 | end
36 | end
37 | elseif (strcmp(ConstellationMap, '1024QAM'))
38 | ConstellationMap = [];
39 | for indexI = 0 : 31
40 | for indexQ = 0 : 31
41 | ConstellationMap = [ConstellationMap ((indexI / 15.5 - 1) + (indexQ / 15.5 - 1) * 1i)];
42 | end
43 | end
44 | else
45 | ConstellationMap = [];
46 | end
47 | ConstellationMap = ConstellationMap * Signal.ReferenceVoltage;
48 | end
49 | else
50 | ConstellationMap = [];
51 | end
52 | MaxReal = max(abs(real(Signal.Signal)),[],'All');
53 | MaxImaginary = max(abs(imag(Signal.Signal)),[],'All');
54 | MaxScale = max([MaxReal MaxImaginary]) * Signal.ReferenceVoltage;
55 | [VoltageUnit, VoltageFactor] = UnitConvert(MaxScale, 'V');
56 | if (~SeparateDisplay)
57 | nexttile;
58 | % Force convert to complex number to avoid the unpredictable plot
59 | % error.
60 | plot(complex(ConstellationMap),'+','Markersize',10,'color','red','LineWidth',1);
61 | end
62 | for index = 1 : Signal.ChannelNum
63 | if (SeparateDisplay)
64 | nexttile;
65 | plot(complex(ConstellationMap),'+','Markersize',10,'color','red','LineWidth',1);
66 | hold on;
67 | else
68 | hold on;
69 | end
70 | plot(complex(Signal.Signal(index, :) * Signal.ReferenceVoltage),'.');
71 | xlim([-(MaxScale * 1.1 * VoltageFactor) (MaxScale * 1.1 * VoltageFactor)]);
72 | ylim([-(MaxScale * 1.1 * VoltageFactor) (MaxScale * 1.1 * VoltageFactor)]);
73 | end
74 | hold off;
75 | xlabel(TiledFigure, VoltageUnit);
76 | ylabel(TiledFigure, VoltageUnit);
77 | if (exist('Title', 'var'))
78 | title(TiledFigure, Title);
79 | end
80 |
81 | drawnow;
82 | end
83 |
--------------------------------------------------------------------------------
/probe/ProbeDelay.m:
--------------------------------------------------------------------------------
1 | function [Delay] = ProbeDelay(SignalA, SignalB, Title)
2 | %ProbeBitError A probe that detects delay between two signals.
3 | %Introduction:
4 | % Get the delay between two signals.
5 | %Syntax:
6 | % Delay = ProbeDelay(SignalA, SignalB)
7 | % Delay = ProbeDelay(SignalA, SignalB, Title)
8 | %Description:
9 | % Delay = ProbeDelay(SignalA, SignalB)
10 | % returns the signal delay.
11 | % Delay = ProbeDelay(SignalA, SignalB, Title)
12 | % display 'Delay' after 'Title' on console.
13 | %Input Arguments:
14 | % SignalA: (Signal)
15 | % One signal.
16 | % SignalB: (Signal)
17 | % Another signal.
18 | % Title: (string)
19 | % Probe display title.
20 | %Output Arguments:
21 | % Delay: (double)
22 | % The delay time in second.
23 | %Author:
24 | % Tifer King
25 | %License:
26 | % Please refer to the 'LICENSE' file included in the root directory
27 | % of the project.
28 |
29 | Delay = SignalB.TimeStart - SignalA.TimeStart;
30 | if(exist('Title','var'))
31 | DisplayString = [Title ' = ' num2str(Delay) ' s.'];
32 | disp(DisplayString);
33 | end
34 | end
35 |
36 |
--------------------------------------------------------------------------------
/probe/ProbeSignalPower.m:
--------------------------------------------------------------------------------
1 | function [TotalPowerRMS, ChannelPowerRMS, TotalPowerPeak, ChannelPowerPeak] = ProbeSignalPower(Signal, Title)
2 | %ProbeBitError A probe that detects signal power.
3 | %Introduction:
4 | % Calculate signal power.
5 | %Syntax:
6 | % TotalPowerRMS = ProbeSignalPower(Signal)
7 | % [TotalPowerRMS, ChannelPowerRMS, TotalPowerPeak, ChannelPowerPeak] = ProbeSignalPower(Signal)
8 | % [ ___ ] = ProbeSignalPower(Signal, Title)
9 | %Description:
10 | % TotalPowerRMS = ProbeSignalPower(Signal)
11 | % returns the rms power of signal.
12 | % [TotalPowerRMS, ChannelPowerRMS, TotalPowerPeak, ChannelPowerPeak] = ProbeSignalPower(Signal)
13 | % returns the rms power of signals, the rms powers in each channel,the
14 | % peak power of signals, the peak powers in each channel.
15 | % [ ___ ] = ProbeSignalPower(Signal, Title)
16 | % display all total power after 'Title' on console.
17 | %Input Arguments:
18 | % Signal: (Signal)
19 | % Input signals.
20 | % Title: (string)
21 | % Probe display title.
22 | %Output Arguments:
23 | % TotalPowerRMS: (double)
24 | % The rms power of signals for all channels.
25 | % ChannelPowerRMS: (vector)
26 | % The rms power of signals in each channel.
27 | % TotalPowerPeak: (double)
28 | % The peak power of signals for all channels.
29 | % ChannelPowerPeak: (vector)
30 | % The peak power of signals in each channel.
31 | %Author:
32 | % Tifer King
33 | %License:
34 | % Please refer to the 'LICENSE' file included in the root directory
35 | % of the project.
36 |
37 | ChannelPowerRMS = sum((abs(Signal.Signal) * Signal.ReferenceVoltage) .^ 2 / Signal.ReferenceImpedance, 2) / size(Signal.Signal, 2);
38 | TotalPowerRMS = sum(ChannelPowerRMS);
39 | ChannelPowerPeak = sum((max(abs(Signal.Signal), [], 2) * Signal.ReferenceVoltage) .^ 2 / Signal.ReferenceImpedance, 2);
40 | TotalPowerPeak = sum(ChannelPowerPeak);
41 | if(exist('Title','var'))
42 | DisplayString = [Title '_PowerRMS = ' num2str(pow2db(TotalPowerRMS)) 'dBW'];
43 | disp(DisplayString);
44 | DisplayString = [Title '_PowerPeak = ' num2str(pow2db(TotalPowerPeak)) 'dBW'];
45 | disp(DisplayString);
46 | end
47 | end
48 |
49 |
--------------------------------------------------------------------------------
/probe/ProbeSpectrum.m:
--------------------------------------------------------------------------------
1 | function [] = ProbeSpectrum(Signal, SeparateDisplay, Title, varargin)
2 | DefaultSpan = 'Auto';
3 | DefaultRBW = 'Auto';
4 | DefaultWindow = 'FlatTop';
5 | ExpectedWindowPreset = {'FlatTop'};
6 | InPar = inputParser;
7 | addOptional(InPar,'Span', DefaultSpan);
8 | addOptional(InPar,'RBW', DefaultRBW);
9 | addOptional(InPar,'Window',DefaultWindow,@(x) any(validatestring(x,ExpectedWindowPreset)));
10 | parse(InPar,varargin{:});
11 |
12 | figure;
13 | TiledFigure = tiledlayout("flow");
14 | if (~exist('SeparateDisplay', 'var'))
15 | SeparateDisplay = false;
16 | end
17 | SignalSize = size(Signal.Signal,2);
18 | %if(strcmp(InPar.Results.Window, "FlatTop"))
19 | %SignalWithWindow = Signal.Signal .* repmat(flattopwin(SignalSize)', Signal.ChannelNum, 1);
20 | %end
21 | FFTConvert = (abs(fft(Signal.Signal .* Signal.ReferenceVoltage, [], 2) / SignalSize) .^ 2) / Signal.ReferenceImpedance;
22 | if (mod(SignalSize, 2) == 0)
23 | FFTSize = SignalSize / 2;
24 | FFTBand = Signal.SampleRate / 2;
25 | FFTConvert = FFTConvert(:, 1 : SignalSize / 2);
26 | else
27 | % Handle the signal in even points, and the specturm of fft should
28 | % also been calculated correctly.
29 | FFTSize = (SignalSize - 1) / 2;
30 | FFTBand = Signal.SampleRate * FFTSize / SignalSize;
31 | FFTConvert = FFTConvert(:, 1 : FFTSize);
32 | end
33 | FFTAutoThreshold = -10;
34 |
35 | if (SeparateDisplay)
36 | for index = 1 : Signal.ChannelNum
37 | if(strcmp(InPar.Results.Span, "Auto"))
38 | % Auto calculate span
39 | FFTPeak = max(FFTConvert(index, :), [], "all");
40 | FFTThreshold = FFTPeak * db2pow(FFTAutoThreshold);
41 | FFTBase = FFTConvert(index, :) > FFTThreshold;
42 | FFTSpanStart = find(FFTBase, 1, "first");
43 | FFTSpanStop = find(FFTBase, 1, "last");
44 | FFTSpan = FFTSpanStop - FFTSpanStart;
45 | % Expand the monitor span for a little bit.
46 | if ((FFTSpanStart - round(FFTSpan / 2)) > 1)
47 | FFTSpanStart = (FFTSpanStart - round(FFTSpan / 2));
48 | else
49 | FFTSpanStart = 1;
50 | end
51 | if ((FFTSpanStop + round(FFTSpan / 2)) < FFTSize)
52 | FFTSpanStop = (FFTSpanStop + round(FFTSpan / 2));
53 | else
54 | FFTSpanStop = FFTSize;
55 | end
56 | FFTSpan = FFTSpanStop - FFTSpanStart;
57 | if(FFTSpan < 100)
58 | if ((FFTSpanStart - round(FFTSize / 20)) > 1)
59 | FFTSpanStart = (FFTSpanStart - round(FFTSize / 20));
60 | else
61 | FFTSpanStart = 1;
62 | end
63 | if ((FFTSpanStop + round(FFTSize / 20)) < FFTSize)
64 | FFTSpanStop = (FFTSpanStop + round(FFTSize / 20));
65 | else
66 | FFTSpanStop = FFTSize;
67 | end
68 | end
69 | FFTSpan = FFTSpanStop - FFTSpanStart;
70 | FFTStartFrequency = (FFTSpanStart - 1) / FFTSize * FFTBand;
71 | FFTStopFrequency = (FFTSpanStop - 1) / FFTSize * FFTBand;
72 | FFTSpanFrequency = FFTSpan / FFTSize * FFTBand;
73 | else
74 | FFTSpanStart = round(InPar.Results.Span(1) / Signal.SampleRate * SignalSize);
75 | FFTSpanStop = round(InPar.Results.Span(2) / Signal.SampleRate * SignalSize);
76 | FFTSpan = FFTSpanStop - FFTSpanStart;
77 | FFTStartFrequency = (FFTSpanStart - 1) / FFTSize * FFTBand;
78 | FFTStopFrequency = (FFTSpanStop - 1) / FFTSize * FFTBand;
79 | FFTSpanFrequency = FFTSpan / FFTSize * FFTBand;
80 | end
81 | if(strcmp(InPar.Results.RBW, "Auto"))
82 | FFTRBW = round(0.02 * FFTSpan);
83 | FFTRBWFrequency = FFTRBW / FFTSize * FFTBand;
84 | FFTRBWRelatively = FFTRBWFrequency / Signal.SampleRate;
85 | else
86 | FFTRBWFrequency = InPar.Results.RBW;
87 | FFTRBW = round(FFTRBWFrequency / Signal.SampleRate * FFTSize);
88 | FFTRBWRelatively = FFTRBWFrequency / Signal.SampleRate;
89 | end
90 |
91 | %FFTConvertFiltered = conv(FFTConvert(index, : ), gausswin(FFTRBW * 12, 10)', "same");
92 | FFTSigma = FFTRBWRelatively / 2 / sqrt(log(2));
93 | FFTFilterDefinitionDomain = [flip(-[(1 / SignalSize) : (1 / SignalSize) : (7 * FFTSigma)]) [0 : (1 / SignalSize) : (7 * FFTSigma)]];
94 | FFTWindow = exp(-(FFTFilterDefinitionDomain .^ 2) / (2 .* FFTSigma .^ 2));
95 | FFTConvertSpan = FFTConvert(index, FFTSpanStart : FFTSpanStop);
96 | FFTConvertFiltered = convn(FFTConvertSpan, FFTWindow, "same");
97 |
98 | [FrequencyUnit, FrequencyFactor] = UnitConvert(FFTStopFrequency, 'Hz');
99 |
100 | Horizon = [(FFTStartFrequency * FrequencyFactor) : (FFTSpanFrequency / FFTSpan * FrequencyFactor) : (FFTStopFrequency * FrequencyFactor)];
101 | nexttile;
102 | plot(Horizon, pow2db(FFTConvertFiltered));
103 | xlim([(FFTStartFrequency * FrequencyFactor) (FFTStopFrequency * FrequencyFactor)]);
104 | xlabel(TiledFigure, [FrequencyUnit ' (RBW = ' num2str(FFTRBWFrequency * FrequencyFactor) FrequencyUnit ')']);
105 | ylabel(TiledFigure, 'dBW');
106 | end
107 | else
108 | nexttile;
109 | if(strcmp(InPar.Results.Span, "Auto"))
110 | FFTPeak = max(FFTConvert, [], "all");
111 | FFTThreshold = FFTPeak * db2pow(FFTAutoThreshold);
112 | FFTBase = FFTConvert > FFTThreshold;
113 | FFTSpanStartMin = FFTSize;
114 | FFTSpanStopMax = 0;
115 | for index = 1 : Signal.ChannelNum
116 | FFTSpanStart = find(FFTBase(index, :), 1, "first");
117 | FFTSpanStop = find(FFTBase(index, :), 1, "last");
118 | if (FFTSpanStart < FFTSpanStartMin)
119 | FFTSpanStartMin = FFTSpanStart;
120 | end
121 | if (FFTSpanStop > FFTSpanStopMax)
122 | FFTSpanStopMax = FFTSpanStop;
123 | end
124 | end
125 | FFTSpan = FFTSpanStopMax - FFTSpanStartMin;
126 |
127 | % Expand the monitor span for a little bit.
128 | if ((FFTSpanStartMin - round(FFTSpan / 2)) > 1)
129 | FFTSpanStart = (FFTSpanStartMin - round(FFTSpan / 2));
130 | else
131 | FFTSpanStart = 1;
132 | end
133 | if ((FFTSpanStopMax + round(FFTSpan / 2)) < FFTSize)
134 | FFTSpanStop = (FFTSpanStopMax + round(FFTSpan / 2));
135 | else
136 | FFTSpanStop = FFTSize;
137 | end
138 | FFTSpan = FFTSpanStop - FFTSpanStart;
139 | if(FFTSpan < 100)
140 | if ((FFTSpanStart - round(FFTSize / 20)) > 1)
141 | FFTSpanStart = (FFTSpanStart - round(FFTSize / 20));
142 | else
143 | FFTSpanStart = 1;
144 | end
145 | if ((FFTSpanStop + round(FFTSize / 20)) < FFTSize)
146 | FFTSpanStop = (FFTSpanStop + round(FFTSize / 20));
147 | else
148 | FFTSpanStop = FFTSize;
149 | end
150 | end
151 | FFTSpan = FFTSpanStop - FFTSpanStart;
152 | FFTStartFrequency = FFTSpanStart / FFTSize * FFTBand;
153 | FFTStopFrequency = FFTSpanStop / FFTSize * FFTBand;
154 | FFTSpanFrequency = FFTSpan / FFTSize * FFTBand;
155 | else
156 | FFTSpanStart = round(InPar.Results.Span(1) / Signal.SampleRate * SignalSize);
157 | FFTSpanStop = round(InPar.Results.Span(2) / Signal.SampleRate * SignalSize);
158 | FFTSpan = FFTSpanStop - FFTSpanStart;
159 | FFTStartFrequency = (FFTSpanStart - 1) / FFTSize * FFTBand;
160 | FFTStopFrequency = (FFTSpanStop - 1) / FFTSize * FFTBand;
161 | FFTSpanFrequency = FFTSpan / FFTSize * FFTBand;
162 | end
163 | if(strcmp(InPar.Results.RBW, "Auto"))
164 | FFTRBW = round(0.01 * FFTSpan);
165 | if(FFTRBW < 1)
166 | FFTRBW = 1;
167 | end
168 | FFTRBWFrequency = FFTRBW / FFTSize * FFTBand;
169 | FFTRBWRelatively = FFTRBWFrequency / Signal.SampleRate;
170 | else
171 | FFTRBWFrequency = InPar.Results.RBW;
172 | FFTRBW = round(FFTRBWFrequency / Signal.SampleRate * FFTSize);
173 | FFTRBWRelatively = FFTRBWFrequency / Signal.SampleRate;
174 | end
175 |
176 | %FFTConvertFiltered = convn(FFTConvert, gausswin(FFTRBW * 12, 10)', "same") ;
177 | FFTSigma = FFTRBWRelatively / 2 / sqrt(log(2));
178 | FFTFilterDefinitionDomain = [flip(-[(1 / SignalSize) : (1 / SignalSize) : (7 * FFTSigma)]) [0 : (1 / SignalSize) : (7 * FFTSigma)]];
179 | FFTWindow = exp(-(FFTFilterDefinitionDomain .^ 2) / (2 .* FFTSigma .^ 2));
180 | FFTConvertSpan = FFTConvert(:, FFTSpanStart : FFTSpanStop);
181 | FFTConvertFiltered = convn(FFTConvertSpan, FFTWindow, "same");
182 |
183 | %FFTTimeWindow = exp(-(FFTTimeSigma .^ 2 * [[0 + (1 / Signal.SampleRate / 2) : (1 / Signal.SampleRate) : Signal.TimeEndurance / 2] [-Signal.TimeEndurance / 2 : (1 / Signal.SampleRate) : 0 - (1 / Signal.SampleRate / 2)]] .^ 2) / 2) * FFTTimeSigma;
184 | %FFTConvertFiltered = (abs(fft(Signal.Signal .* Signal.ReferenceVoltage .* FFTTimeWindow, [], 2) / SignalSize / 2 / pi) .^ 2) / Signal.ReferenceImpedance;
185 | %FFTConvertFiltered = FFTConvert;
186 |
187 | [FrequencyUnit, FrequencyFactor] = UnitConvert(FFTStopFrequency, 'Hz');
188 |
189 | Horizon = [(FFTStartFrequency * FrequencyFactor) : (FFTSpanFrequency / FFTSpan * FrequencyFactor) : (FFTStopFrequency * FrequencyFactor)];
190 | for index = 1 : Signal.ChannelNum
191 | hold on;
192 | plot(Horizon, pow2db(FFTConvertFiltered(index,:)));
193 | end
194 | xlim([(FFTStartFrequency * FrequencyFactor) (FFTStopFrequency * FrequencyFactor)]);
195 | xlabel(TiledFigure, [FrequencyUnit ' (RBW = ' num2str(FFTRBWFrequency * FrequencyFactor) FrequencyUnit ')']);
196 | ylabel(TiledFigure, 'dBW');
197 | end
198 | if (exist('Title', 'var'))
199 | title(TiledFigure, Title);
200 | end
201 | hold off;
202 | drawnow;
203 | end
204 |
205 |
--------------------------------------------------------------------------------
/probe/ProbeWave.m:
--------------------------------------------------------------------------------
1 | function [] = ProbeWave(Signal, SeparateDisplay, Title)
2 | figure('units','normalized','outerposition',[0 0 1 1]);
3 | TiledFigure = tiledlayout("vertical");
4 | if (~exist('SeparateDisplay', 'var'))
5 | SeparateDisplay = false;
6 | end
7 | %Horizon = linspace(Signal.TimeStart,Signal.TimeStart + Signal.TimeEndurance, Signal.TimeEndurance / 10);
8 | Horizon = [0 : 1 / Signal.SampleRate : Signal.TimeEndurance - 1 / Signal.SampleRate];
9 | [HorizonUnit, HorizonFactor] = UnitConvert(Signal.TimeEndurance, 's');
10 | Horizon = HorizonFactor * Horizon;
11 | MaxVoltage = max(abs(Signal.Signal),[],"all") * Signal.ReferenceVoltage;
12 | [VoltageUnit, VoltageFactor] = UnitConvert(MaxVoltage, 'V');
13 | [StartTimeUnit, StartTimeFactor] = UnitConvert(Signal.TimeStart, 's');
14 | if (~SeparateDisplay)
15 | nexttile;
16 | end
17 | for index = 1 : Signal.ChannelNum;
18 | if (SeparateDisplay)
19 | nexttile;
20 | else
21 | hold on;
22 | end
23 | plot(Horizon, Signal.Signal(index, :) * Signal.ReferenceVoltage * VoltageFactor);
24 | xlim([0 (Signal.TimeEndurance * HorizonFactor)]);
25 | ylim([-(MaxVoltage * VoltageFactor) (MaxVoltage * VoltageFactor)]);
26 | %xticks([Signal.TimeStart : Signal.TimeEndurance / 10 : Signal.TimeEndurance - 1 / Signal.SampleRate])
27 | end
28 | xlabel(TiledFigure, [HorizonUnit ' (Start@' num2str(Signal.TimeStart * StartTimeFactor) ' ' StartTimeUnit ')']);
29 | ylabel(TiledFigure, VoltageUnit);
30 | if (exist('Title', 'var'))
31 | title(TiledFigure, Title);
32 | end
33 | hold off;
34 | drawnow;
35 | end
36 |
37 |
--------------------------------------------------------------------------------
/probe/UnitConvert.m:
--------------------------------------------------------------------------------
1 | function [Unit, Factor] = UnitConvert(MaximumValue,UnitSuffix)
2 | if(MaximumValue == 0)
3 | UnitMagnitude = 0;
4 | else
5 | UnitMagnitude = log10(abs(MaximumValue));
6 | end
7 | if(UnitMagnitude >= 24)
8 | Unit = ['Y' UnitSuffix];
9 | Factor = 1e-24;
10 | elseif(UnitMagnitude >= 21)
11 | Unit = ['Z' UnitSuffix];
12 | Factor = 1e-21;
13 | elseif(UnitMagnitude >= 18)
14 | Unit = ['E' UnitSuffix];
15 | Factor = 1e-18;
16 | elseif(UnitMagnitude >= 15)
17 | Unit = ['P' UnitSuffix];
18 | Factor = 1e-15;
19 | elseif(UnitMagnitude >= 12)
20 | Unit = ['T' UnitSuffix];
21 | Factor = 1e-12;
22 | elseif(UnitMagnitude >= 9)
23 | Unit = ['G' UnitSuffix];
24 | Factor = 1e-9;
25 | elseif(UnitMagnitude >= 6)
26 | Unit = ['M' UnitSuffix];
27 | Factor = 1e-6;
28 | elseif(UnitMagnitude >= 3)
29 | Unit = ['k' UnitSuffix];
30 | Factor = 1e-3;
31 | elseif(UnitMagnitude >= 0)
32 | Unit = [UnitSuffix];
33 | Factor = 1;
34 | elseif(UnitMagnitude >= -3)
35 | Unit = ['m' UnitSuffix];
36 | Factor = 1e3;
37 | elseif(UnitMagnitude >= -6)
38 | Unit = ['µ' UnitSuffix];
39 | Factor = 1e6;
40 | elseif(UnitMagnitude >= -9)
41 | Unit = ['n' UnitSuffix];
42 | Factor = 1e9;
43 | elseif(UnitMagnitude >= -12)
44 | Unit = ['p' UnitSuffix];
45 | Factor = 1e12;
46 | elseif(UnitMagnitude >= -15)
47 | Unit = ['f' UnitSuffix];
48 | Factor = 1e15;
49 | elseif(UnitMagnitude >= -18)
50 | Unit = ['a' UnitSuffix];
51 | Factor = 1e18;
52 | elseif(UnitMagnitude >= -21)
53 | Unit = ['z' UnitSuffix];
54 | Factor = 1e21;
55 | elseif(UnitMagnitude >= -24)
56 | Unit = ['y' UnitSuffix];
57 | Factor = 1e24;
58 | end
59 | end
60 |
61 |
--------------------------------------------------------------------------------
/tool/DemixingResampleFilter.m:
--------------------------------------------------------------------------------
1 | function [Filter] = DemixingResampleFilter(RFSampleRate, BaseSampleRate)
2 | %DemixingResampleFilter Design demixing downsample filter.
3 | %Introduction:
4 | % This function designs the downsample filter utilized in demixing the
5 | % signals to avoid spectral aliasing.
6 | %Syntax:
7 | % Filter = DemixingResampleFilter(RFSampleRate, BaseSampleRate)
8 | %Description:
9 | % Filter = DemixingResampleFilter(RFSampleRate, BaseSampleRate)
10 | % returns the designed filter.
11 | %Input Arguments:
12 | % RFSampleRate: (double)
13 | % RF signal sample rate in Sa/s.
14 | % BaseSampleRate: (double)
15 | % Baseband signal sample rate in Sa/s.
16 | %Output Arguments:
17 | % Filter: (matrix)
18 | % Filter coefficient.
19 | %Author:
20 | % Tifer King
21 | %License:
22 | % Please refer to the 'LICENSE' file included in the root directory
23 | % of the project.
24 |
25 | persistent RFSa;
26 | persistent BaseSa;
27 | persistent DesignedFilter;
28 | if (isempty(RFSa) || isempty(BaseSa) || isempty(DesignedFilter) || (RFSa ~= RFSampleRate) || (BaseSa ~= BaseSampleRate))
29 | RFSa = RFSampleRate;
30 | BaseSa = BaseSampleRate;
31 | FilterFactor = BaseSampleRate / RFSampleRate;
32 | if(FilterFactor < 0.001)
33 | % When the baseband sample rate is significantly below the RF
34 | % sample rate, designing a high-performance filter becomes
35 | % challenging. Therefore, it is advisable to establish a limit
36 | % for the filter factor to mitigate potential degradation in
37 | % performance under these circumstances.
38 | FilterFactor = 0.001;
39 | end
40 | [N, Fo, Ao, W] = firpmord([FilterFactor, FilterFactor * 2], [1 0], [0.05, 1e-05]);
41 | DesignedFilter = firpm(N, Fo, Ao, W, {20});
42 | end
43 | Filter = DesignedFilter;
44 | end
45 |
46 |
--------------------------------------------------------------------------------
/tool/IdealOAMChannel.m:
--------------------------------------------------------------------------------
1 | function [ChannelImpulseResponse] = IdealOAMChannel(TxChannelNum, RxChannelNum, TxAntennaRadius, RxAntennaRadius, RxPosition, RxEulerianAngle, MaximumFrequency, CenterFrequency, SampleRate, AntennaDisplay)
2 | TxAntennaPos = zeros(4, TxChannelNum);
3 | TxAntennaPos(1,:) = cos([0 : 1 / TxChannelNum : 1 - (1 / TxChannelNum)] * 2 * pi) * TxAntennaRadius;
4 | TxAntennaPos(2,:) = sin([0 : 1 / TxChannelNum : 1 - (1 / TxChannelNum)] * 2 * pi) * TxAntennaRadius;
5 | TxAntennaPos(4,:) = ones(1, TxChannelNum);
6 |
7 | RxAntennaPos = zeros(4, RxChannelNum);
8 | RxAntennaPos(1,:) = cos([0 : 1 / RxChannelNum : 1 - (1 / RxChannelNum)] * 2 * pi) * RxAntennaRadius;
9 | RxAntennaPos(2,:) = sin([0 : 1 / RxChannelNum : 1 - (1 / RxChannelNum)] * 2 * pi) * RxAntennaRadius;
10 | RxAntennaPos(4,:) = ones(1, RxChannelNum);
11 | Roll = [1 0 0; 0 cos(RxEulerianAngle(1)) -sin(RxEulerianAngle(1)); 0 sin(RxEulerianAngle(1)) cos(RxEulerianAngle(1))];
12 | Pitch = [cos(RxEulerianAngle(2)) 0 sin(RxEulerianAngle(2)); 0 1 0; -sin(RxEulerianAngle(2)) 0 cos(RxEulerianAngle(2))];
13 | Yaw = [cos(RxEulerianAngle(3)) -sin(RxEulerianAngle(3)) 0; sin(RxEulerianAngle(3)) cos(RxEulerianAngle(3)) 0; 0 0 1];
14 | TransformEulerian = Roll * Pitch * Yaw;
15 | TransformMatrixAngle = [TransformEulerian RxPosition; 0 0 0 1];
16 | RxAntennaPos = TransformMatrixAngle * RxAntennaPos;
17 |
18 | if(exist('AntennaDisplay','var') && AntennaDisplay)
19 | figure;
20 | plot3([TxAntennaPos(1,:) TxAntennaPos(1,1)],[TxAntennaPos(2,:) TxAntennaPos(2,1)],[TxAntennaPos(3,:) TxAntennaPos(3,1)],'-o');
21 | hold on;
22 | plot3([RxAntennaPos(1,:) RxAntennaPos(1,1)],[RxAntennaPos(2,:) RxAntennaPos(2,1)],[RxAntennaPos(3,:) RxAntennaPos(3,1)],'-o');
23 | hold off;
24 | drawnow;
25 | end
26 |
27 | Distance = zeros(RxChannelNum, TxChannelNum);
28 | for index = 1 : RxChannelNum
29 | Distance(index,:) = sqrt(sum((TxAntennaPos - RxAntennaPos(:, index)) .^ 2));
30 | end
31 |
32 | MinDistance = min(Distance, [], 'all');
33 | MaxDistance = max(Distance, [], 'all');
34 | GaussianTau = 1 / (pi * MaximumFrequency);
35 | if (GaussianTau < 10 * (1 / SampleRate))
36 | warning("The Gaussian impulse sample may not have the precision needed. Please increase the channel sample rate.");
37 | end
38 | Delay = Distance / physconst('LightSpeed');
39 | MinDelay = MinDistance / physconst('LightSpeed');
40 | MaxDelay = MaxDistance / physconst('LightSpeed');
41 |
42 | TimeStart = MinDelay - 6 * GaussianTau;
43 | TimeEndurance = MaxDelay - MinDelay + 12 * GaussianTau;
44 | MinimumLambda = physconst('LightSpeed') * (1 / CenterFrequency);
45 | if (MinDistance < MinimumLambda)
46 | warning("The minimum distance between two antennas is shorter than the minimum wavelength (lambda), which may lead to incorrect channel characteristics.");
47 | end
48 | AttenuationdB = -pow2db((MinimumLambda / (4 * pi * MinDistance)) ^ 2);
49 |
50 |
51 | ChannelImpulseResponse = InitChannelImpulseResponse(TxChannelNum, RxChannelNum, TimeStart, TimeEndurance, SampleRate, AttenuationdB, MaximumFrequency, 'Custom');
52 | ChannelLength = size(ChannelImpulseResponse.Signal,3);
53 |
54 | for indexRx = 1 : RxChannelNum
55 | for indexTx = 1 : TxChannelNum
56 | GaussianImpulse = exp(-(([1 : ChannelLength] .* (1 / SampleRate) - (6 * GaussianTau) - (Delay(indexRx, indexTx) - MinDelay)) .^ 2) ./ (GaussianTau .^ 2));
57 | GaussianImpulse = (MinDistance / Distance(indexRx, indexTx)) * (GaussianImpulse ./ sum(GaussianImpulse));
58 | ChannelImpulseResponse.Signal(indexRx, indexTx, :) = GaussianImpulse;
59 | end
60 | end
61 |
62 | end
63 |
64 |
--------------------------------------------------------------------------------
/tool/IdealOAMVisualizer.m:
--------------------------------------------------------------------------------
1 | function [] = IdealOAMVisualizer(TxChannelNum, RxChannelNum, TxAntennaRadius, RxAntennaRadius, RxPosition, RxEulerianAngle, Frequency, OAMMode, AntennaDisplay, DisplayRange)
2 | TxAntennaPos = zeros(4, TxChannelNum);
3 | TxAntennaPos(1,:) = cos([0 : 1 / TxChannelNum : 1 - (1 / TxChannelNum)] * 2 * pi) * TxAntennaRadius;
4 | TxAntennaPos(2,:) = sin([0 : 1 / TxChannelNum : 1 - (1 / TxChannelNum)] * 2 * pi) * TxAntennaRadius;
5 | TxAntennaPos(4,:) = ones(1, TxChannelNum);
6 |
7 | RxAntennaPos = zeros(4, RxChannelNum);
8 | RxAntennaPos(1,:) = cos([0 : 1 / RxChannelNum : 1 - (1 / RxChannelNum)] * 2 * pi) * RxAntennaRadius;
9 | RxAntennaPos(2,:) = sin([0 : 1 / RxChannelNum : 1 - (1 / RxChannelNum)] * 2 * pi) * RxAntennaRadius;
10 | RxAntennaPos(4,:) = ones(1, RxChannelNum);
11 | Roll = [1 0 0; 0 cos(RxEulerianAngle(1)) -sin(RxEulerianAngle(1)); 0 sin(RxEulerianAngle(1)) cos(RxEulerianAngle(1))];
12 | Pitch = [cos(RxEulerianAngle(2)) 0 sin(RxEulerianAngle(2)); 0 1 0; -sin(RxEulerianAngle(2)) 0 cos(RxEulerianAngle(2))];
13 | Yaw = [cos(RxEulerianAngle(3)) -sin(RxEulerianAngle(3)) 0; sin(RxEulerianAngle(3)) cos(RxEulerianAngle(3)) 0; 0 0 1];
14 | TransformEulerian = Roll * Pitch * Yaw;
15 | TransformMatrixAngle = [TransformEulerian RxPosition; 0 0 0 1];
16 | RxAntennaPos = TransformMatrixAngle * RxAntennaPos;
17 |
18 | Lambda = physconst('LightSpeed') * (1 / Frequency);
19 | %SpinMode = OAMMode;
20 | SpinMode = 0;
21 |
22 | if (~exist('DisplayRange', 'var'))
23 | DisplayRange = 'Auto';
24 | end
25 | if (isstring(DisplayRange) || ischar(DisplayRange))
26 | if (strcmp(DisplayRange, 'Auto'))
27 | MaxRange = max([abs(TxAntennaPos) abs(RxAntennaPos)], [], 2);
28 | MinScale = min(MaxRange, [], "all");
29 | if (MinScale < Lambda)
30 | %MinScale = Lambda;
31 | MaxRange = ((MaxRange >= Lambda) .* MaxRange) + ((MaxRange < Lambda) .* Lambda);
32 | end
33 | %DisplayCell = (MinScale / 100);
34 | DisplayRange = [(MaxRange .* [2; 2; 1; 0]) (MaxRange / 50)];
35 | end
36 | end
37 |
38 | % XZ Plane calculate
39 | XSize = round(DisplayRange(1,1) / DisplayRange(1,2) * 2) + 1;
40 | YSize = round(DisplayRange(2,1) / DisplayRange(2,2) * 2) + 1;
41 | ZSize = round(DisplayRange(3,1) / DisplayRange(3,2)) + 1;
42 | XRange = [-DisplayRange(1,1) : DisplayRange(1,2) : DisplayRange(1,1)];
43 | YRange = [-DisplayRange(2,1) : DisplayRange(2,2) : DisplayRange(2,1)];
44 | ZRange = [0 : DisplayRange(3,2) : DisplayRange(3,1)];
45 | XZPlaneDistance = zeros(XSize, ZSize, TxChannelNum);
46 | XZPlanePoint = ones(4, XSize, ZSize);
47 | XZPlanePoint(1,:,:) = reshape(repmat(XRange, ZSize, 1)', [1, XSize, ZSize]);
48 | XZPlanePoint(2,:,:) = XZPlanePoint(2,:,:) .* 0;
49 | XZPlanePoint(3,:,:) = reshape(repmat(ZRange', 1, XSize)', [1, XSize, ZSize]);
50 |
51 | for index = 1 : TxChannelNum
52 | XZPlaneDistance(:,:,index) = reshape(sqrt(sum((TxAntennaPos(:, index) - XZPlanePoint) .^ 2, 1)), [XSize, ZSize]);
53 | end
54 |
55 | XZPlaneDistance = ((XZPlaneDistance < Lambda) .* Lambda) + ((XZPlaneDistance >= Lambda) .* XZPlaneDistance);
56 | XZAttenuation = Lambda ./ (4 .* pi .* XZPlaneDistance);
57 | XZPhase = exp(XZPlaneDistance ./ Lambda .* 2 .* pi .* 1i);
58 | XZReceiveV = zeros(XSize, ZSize, TxChannelNum);
59 | XZReceiveH = zeros(XSize, ZSize, TxChannelNum);
60 | for index = 1 : TxChannelNum
61 | XZReceiveV(:, :, index) = XZAttenuation(:, :, index) .* XZPhase(:, :, index) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode) .* real(exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* SpinMode));
62 | XZReceiveH(:, :, index) = XZAttenuation(:, :, index) .* XZPhase(:, :, index) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode) .* imag(exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* SpinMode));
63 | end
64 |
65 | % YZ Plane calculate
66 | YZPlaneDistance = zeros(YSize, ZSize, TxChannelNum);
67 | YZPlanePoint = ones(4, YSize, ZSize);
68 | YZPlanePoint(1,:,:) = YZPlanePoint(1,:,:) .* 0;
69 | YZPlanePoint(2,:,:) = reshape(repmat(YRange, ZSize, 1)', [1, YSize, ZSize]);
70 | YZPlanePoint(3,:,:) = reshape(repmat(ZRange', 1, YSize)', [1, YSize, ZSize]);
71 |
72 | for index = 1 : TxChannelNum
73 | YZPlaneDistance(:,:,index) = reshape(sqrt(sum((TxAntennaPos(:, index) - YZPlanePoint) .^ 2, 1)), [YSize, ZSize]);
74 | end
75 |
76 | YZPlaneDistance = ((YZPlaneDistance < Lambda) .* Lambda) + ((YZPlaneDistance >= Lambda) .* YZPlaneDistance);
77 | YZAttenuation = Lambda ./ (4 .* pi .* YZPlaneDistance);
78 | YZPhase = exp(YZPlaneDistance ./ Lambda .* 2 .* pi .* 1i);
79 | YZReceiveV = zeros(YSize, ZSize, TxChannelNum);
80 | YZReceiveH = zeros(YSize, ZSize, TxChannelNum);
81 | for index = 1 : TxChannelNum
82 | YZReceiveV(:, :, index) = YZAttenuation(:, :, index) .* YZPhase(:, :, index) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode) .* real(exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* SpinMode));
83 | YZReceiveH(:, :, index) = YZAttenuation(:, :, index) .* YZPhase(:, :, index) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode) .* imag(exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* SpinMode));
84 | end
85 |
86 | % XY Plane calculate
87 | XYPlaneDistance = zeros(XSize, YSize, TxChannelNum);
88 | XYPlanePoint = ones(4, XSize, YSize);
89 | XYPlanePoint(1,:,:) = reshape(repmat(XRange, YSize, 1)', [1, XSize, YSize]);
90 | XYPlanePoint(2,:,:) = reshape(repmat(YRange', 1, XSize)', [1, XSize, YSize]);
91 | XYPlanePoint(3,:,:) = XYPlanePoint(3,:,:) .* DisplayRange(3,1);
92 |
93 | for index = 1 : TxChannelNum
94 | XYPlaneDistance(:,:,index) = reshape(sqrt(sum((TxAntennaPos(:, index) - XYPlanePoint) .^ 2, 1)), [XSize, YSize]);
95 | end
96 |
97 | XYPlaneDistance = ((XYPlaneDistance < Lambda) .* Lambda) + ((XYPlaneDistance >= Lambda) .* XYPlaneDistance);
98 | XYAttenuation = Lambda ./ (4 .* pi .* XYPlaneDistance);
99 | XYPhase = exp(XYPlaneDistance ./ Lambda .* 2 .* pi .* 1i);
100 | XYReceiveV = zeros(XSize, YSize, TxChannelNum);
101 | XYReceiveH = zeros(XSize, YSize, TxChannelNum);
102 | for index = 1 : TxChannelNum
103 | XYReceiveV(:, :, index) = XYAttenuation(:, :, index) .* XYPhase(:, :, index) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode) .* real(exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* SpinMode));
104 | XYReceiveH(:, :, index) = XYAttenuation(:, :, index) .* XYPhase(:, :, index) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode) .* imag(exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* SpinMode));
105 | end
106 |
107 | % Recv Plane calculate
108 | RecvPlaneDistance = zeros(XSize, YSize, TxChannelNum);
109 | RecvPlanePoint = ones(4, XSize, YSize);
110 | RecvPlanePoint(1,:,:) = reshape(repmat(XRange, YSize, 1)', [1, XSize, YSize]);
111 | RecvPlanePoint(2,:,:) = reshape(repmat(YRange', 1, XSize)', [1, XSize, YSize]);
112 | RecvPlanePoint(3,:,:) = XYPlanePoint(3,:,:) .* 0;
113 |
114 | RecvPlanePoint = pagemtimes(TransformMatrixAngle, RecvPlanePoint);
115 |
116 | for index = 1 : TxChannelNum
117 | RecvPlaneDistance(:,:,index) = reshape(sqrt(sum((TxAntennaPos(:, index) - RecvPlanePoint) .^ 2, 1)), [XSize, YSize]);
118 | end
119 |
120 | RecvPlaneDistance = ((RecvPlaneDistance < Lambda) .* Lambda) + ((RecvPlaneDistance >= Lambda) .* RecvPlaneDistance);
121 | RecvAttenuation = Lambda ./ (4 .* pi .* RecvPlaneDistance);
122 | RecvPhase = exp(RecvPlaneDistance ./ Lambda .* 2 .* pi .* 1i);
123 | RecvReceiveV = zeros(XSize, YSize, TxChannelNum);
124 | RecvReceiveH = zeros(XSize, YSize, TxChannelNum);
125 | for index = 1 : TxChannelNum
126 | RecvReceiveV(:, :, index) = RecvAttenuation(:, :, index) .* RecvPhase(:, :, index) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode) .* real(exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* SpinMode));
127 | RecvReceiveH(:, :, index) = RecvAttenuation(:, :, index) .* RecvPhase(:, :, index) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode) .* imag(exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* SpinMode));
128 | end
129 |
130 | Distance = zeros(RxChannelNum, TxChannelNum);
131 | for index = 1 : RxChannelNum
132 | Distance(index,:) = sqrt(sum((TxAntennaPos - RxAntennaPos(:, index)) .^ 2));
133 | end
134 |
135 | MaxDistance = max(Distance, [], 'all');
136 | DisplayAttenuationRange = pow2db((Lambda / (4 * pi * MaxDistance)) ^ 2) - 10;
137 |
138 | figure('units','normalized','outerposition',[0 0 1 1]);
139 | TiledFigure = tiledlayout("flow");
140 | % XZ Plane Display
141 | nexttile;
142 | surf(XRange, ZRange, pow2db(abs(sum(XZReceiveV,3) .^ 2))');
143 | shading interp;
144 | title('XZ Plane');
145 | view(0,90);
146 | colorbar;
147 | clim([DisplayAttenuationRange -20]);
148 | daspect([1 1 1]);
149 |
150 | % YZ Plane Display
151 | nexttile;
152 | surf(YRange, ZRange, pow2db(abs(sum(YZReceiveV,3) .^ 2))');
153 | shading interp;
154 | title('YZ Plane');
155 | view(0,90);
156 | colorbar;
157 | clim([DisplayAttenuationRange -20]);
158 | daspect([1 1 1]);
159 |
160 | % XY Plane Display
161 | nexttile;
162 | surf(XRange, YRange, pow2db(abs(sum(XYReceiveV,3) .^ 2))');
163 | shading interp;
164 | title('XY Plane');
165 | view(0,90);
166 | colorbar;
167 | clim([DisplayAttenuationRange -20]);
168 | if(exist('AntennaDisplay','var') && AntennaDisplay)
169 | hold on;
170 | plot3([RxAntennaPos(1,:) RxAntennaPos(1,1)],[RxAntennaPos(2,:) RxAntennaPos(2,1)],[RxAntennaPos(3,:) RxAntennaPos(3,1)],'-o','Color','r','MarkerFaceColor','#D90000');
171 | hold off;
172 | end
173 | daspect([1 1 1]);
174 |
175 | nexttile;
176 | surf(XRange, YRange, angle(sum(XYReceiveV,3))');
177 | shading interp;
178 | title('XY Phase');
179 | view(0,90);
180 | colorbar;
181 | if(exist('AntennaDisplay','var') && AntennaDisplay)
182 | hold on;
183 | plot3([RxAntennaPos(1,:) RxAntennaPos(1,1)],[RxAntennaPos(2,:) RxAntennaPos(2,1)],[RxAntennaPos(3,:) RxAntennaPos(3,1)],'-o','Color','r','MarkerFaceColor','#D90000');
184 | hold off;
185 | end
186 | daspect([1 1 1]);
187 |
188 | % Recv Plane Display
189 | nexttile;
190 | surf(XRange, YRange, pow2db(abs(sum(RecvReceiveV,3) .^ 2))');
191 | shading interp;
192 | title('Receiver Plane');
193 | view(0,90);
194 | colorbar;
195 | clim([DisplayAttenuationRange -20]);
196 | if(exist('AntennaDisplay','var') && AntennaDisplay)
197 | RxAntennaPosInit = zeros(4, RxChannelNum);
198 | RxAntennaPosInit(1,:) = cos([0 : 1 / RxChannelNum : 1 - (1 / RxChannelNum)] * 2 * pi) * RxAntennaRadius;
199 | RxAntennaPosInit(2,:) = sin([0 : 1 / RxChannelNum : 1 - (1 / RxChannelNum)] * 2 * pi) * RxAntennaRadius;
200 | RxAntennaPosInit(3,:) = RxAntennaPosInit(3,:) + 4;
201 | RxAntennaPosInit(4,:) = ones(1, RxChannelNum);
202 | hold on;
203 | plot3([RxAntennaPosInit(1,:) RxAntennaPosInit(1,1)],[RxAntennaPosInit(2,:) RxAntennaPosInit(2,1)],[RxAntennaPosInit(3,:) RxAntennaPosInit(3,1)],'-o','Color','r','MarkerFaceColor','#D90000');
204 | hold off;
205 | end
206 | daspect([1 1 1]);
207 |
208 | nexttile;
209 | surf(XRange, YRange, angle(sum(RecvReceiveV,3))');
210 | shading interp;
211 | title('Receiver Phase');
212 | view(0,90);
213 | colorbar;
214 | if(exist('AntennaDisplay','var') && AntennaDisplay)
215 | hold on;
216 | plot3([RxAntennaPosInit(1,:) RxAntennaPosInit(1,1)],[RxAntennaPosInit(2,:) RxAntennaPosInit(2,1)],[RxAntennaPosInit(3,:) RxAntennaPosInit(3,1)],'-o','Color','r','MarkerFaceColor','#D90000');
217 | hold off;
218 | end
219 | daspect([1 1 1]);
220 |
221 | xlabel(TiledFigure, 'meter');
222 | ylabel(TiledFigure, 'dBW');
223 | title(TiledFigure, 'Ideal OAM Channel Visualizer-V');
224 |
225 | figure('units','normalized','outerposition',[0 0 1 1]);
226 | TiledFigure = tiledlayout("flow");
227 | % XZ Plane Display
228 | nexttile;
229 | surf(XRange, ZRange, pow2db(abs(sum(XZReceiveH,3) .^ 2))');
230 | shading interp;
231 | title('XZ Plane');
232 | view(0,90);
233 | colorbar;
234 | clim([DisplayAttenuationRange -20]);
235 | daspect([1 1 1]);
236 |
237 | % YZ Plane Display
238 | nexttile;
239 | surf(YRange, ZRange, pow2db(abs(sum(YZReceiveH,3) .^ 2))');
240 | shading interp;
241 | title('YZ Plane');
242 | view(0,90);
243 | colorbar;
244 | clim([DisplayAttenuationRange -20]);
245 | daspect([1 1 1]);
246 |
247 | % XY Plane Display
248 | nexttile;
249 | surf(XRange, YRange, pow2db(abs(sum(XYReceiveH,3) .^ 2))');
250 | shading interp;
251 | title('XY Plane');
252 | view(0,90);
253 | colorbar;
254 | clim([DisplayAttenuationRange -20]);
255 | if(exist('AntennaDisplay','var') && AntennaDisplay)
256 | hold on;
257 | plot3([RxAntennaPos(1,:) RxAntennaPos(1,1)],[RxAntennaPos(2,:) RxAntennaPos(2,1)],[RxAntennaPos(3,:) RxAntennaPos(3,1)],'-o','Color','r','MarkerFaceColor','#D90000');
258 | hold off;
259 | end
260 | daspect([1 1 1]);
261 |
262 | nexttile;
263 | surf(XRange, YRange, angle(sum(XYReceiveH,3))');
264 | shading interp;
265 | title('XY Phase');
266 | view(0,90);
267 | colorbar;
268 | if(exist('AntennaDisplay','var') && AntennaDisplay)
269 | hold on;
270 | plot3([RxAntennaPos(1,:) RxAntennaPos(1,1)],[RxAntennaPos(2,:) RxAntennaPos(2,1)],[RxAntennaPos(3,:) RxAntennaPos(3,1)],'-o','Color','r','MarkerFaceColor','#D90000');
271 | hold off;
272 | end
273 | daspect([1 1 1]);
274 |
275 | % Recv Plane Display
276 | nexttile;
277 | surf(XRange, YRange, pow2db(abs(sum(RecvReceiveH,3) .^ 2))');
278 | shading interp;
279 | title('Receiver Plane');
280 | view(0,90);
281 | colorbar;
282 | clim([DisplayAttenuationRange -20]);
283 | if(exist('AntennaDisplay','var') && AntennaDisplay)
284 | RxAntennaPosInit = zeros(4, RxChannelNum);
285 | RxAntennaPosInit(1,:) = cos([0 : 1 / RxChannelNum : 1 - (1 / RxChannelNum)] * 2 * pi) * RxAntennaRadius;
286 | RxAntennaPosInit(2,:) = sin([0 : 1 / RxChannelNum : 1 - (1 / RxChannelNum)] * 2 * pi) * RxAntennaRadius;
287 | RxAntennaPosInit(3,:) = RxAntennaPosInit(3,:) + 4;
288 | RxAntennaPosInit(4,:) = ones(1, RxChannelNum);
289 | hold on;
290 | plot3([RxAntennaPosInit(1,:) RxAntennaPosInit(1,1)],[RxAntennaPosInit(2,:) RxAntennaPosInit(2,1)],[RxAntennaPosInit(3,:) RxAntennaPosInit(3,1)],'-o','Color','r','MarkerFaceColor','#D90000');
291 | hold off;
292 | end
293 | daspect([1 1 1]);
294 |
295 | nexttile;
296 | surf(XRange, YRange, angle(sum(RecvReceiveH,3))');
297 | shading interp;
298 | title('Receiver Phase');
299 | view(0,90);
300 | colorbar;
301 | if(exist('AntennaDisplay','var') && AntennaDisplay)
302 | hold on;
303 | plot3([RxAntennaPosInit(1,:) RxAntennaPosInit(1,1)],[RxAntennaPosInit(2,:) RxAntennaPosInit(2,1)],[RxAntennaPosInit(3,:) RxAntennaPosInit(3,1)],'-o','Color','r','MarkerFaceColor','#D90000');
304 | hold off;
305 | end
306 | daspect([1 1 1]);
307 |
308 | xlabel(TiledFigure, 'meter');
309 | ylabel(TiledFigure, 'dBW');
310 | title(TiledFigure, 'Ideal OAM Channel Visualizer-H');
311 | drawnow;
312 | end
--------------------------------------------------------------------------------
/tool/OAMFocusing.m:
--------------------------------------------------------------------------------
1 | function [XZReceiveTotal, IndexMaxRecv] = OAMFocusing(TxChannelNum, TxAntennaRadius, TxBFElementNum, TxBFDistance, TxFocusingAngle, Frequency, Method, MaxMode, DisplayRange)
2 | %OAMFocusingComparison Compare the difference between approximate ideal OAM
3 | % and diverging OAM.
4 | %Introduction:
5 | % Compare the difference between approximate ideal OAM(A-OAM), H-OAM and
6 | % diverging OAM(D-OAM).
7 | %Syntax:
8 | % OAMFocusing(TxChannelNum, TxAntennaRadius, TxBFElementNum, TxBFDistance,
9 | % TxFocusingAngle, Frequency, Method, MaxMode, DisplayRange)
10 | %Description:
11 | % OAMFocusing(TxChannelNum, TxAntennaRadius, TxBFElementNum, TxBFDistance,
12 | % TxFocusingAngle, Frequency, Method, MaxMode, DisplayRange)
13 | % no return, will display directly.
14 | %Input Arguments:
15 | % TxChannelNum: (positive integer scalar)
16 | % Number of transmitter channels.
17 | % TxAntennaRadius: (double)
18 | % Radius of transmitter array in meter.
19 | % TxBFElementNum: (positive integer scalar)
20 | % Number of antenna in each line array.
21 | % TxBFDistance: (double)
22 | % Distance between two adjacent antenna in line array in meter.
23 | % TxFocusingAngle: (double)
24 | % The focusing angle of transmitter in rad.
25 | % Frequency: (double)
26 | % Center frequency of OAM beams in Hz.
27 | % Method: (string)
28 | % OAM generating method, it is one of blow:
29 | % 'A-OAM': Approximately ideal OAM.
30 | % 'H-OAM': Holographic OAM.
31 | % 'D-OAM': Diverging OAM.
32 | % MaxMode: (positive integer scalar)
33 | % The maximum simulation mode order of OAM.
34 | % DisplayRange: (matrix)
35 | % Simulation range and precision.
36 | %Output Arguments:
37 | % XZReceiveTotal: (matrix)
38 | % The side view of OAM power distribution.
39 | % IndexMaxRecv: (matrix)
40 | % The best-receiving radius of each distance.
41 | %Author:
42 | % Tifer King
43 | %License:
44 | % Please refer to the 'LICENSE' file included in the root directory
45 | % of the project.
46 |
47 | % Tx array position initialization
48 | TxBFLength = (TxBFElementNum - 1) * TxBFDistance;
49 | TxAntennaNum = TxChannelNum * TxBFElementNum;
50 | TxAntennaPos = zeros(4, TxAntennaNum);
51 | TxAntennaRadiusAll = reshape(ones(TxChannelNum, 1) * ([0 : TxBFDistance : TxBFLength] - TxBFLength / 2 + TxAntennaRadius), 1, []);
52 | TxAntennaThetaAll = reshape([0 : 1 / TxChannelNum : 1 - (1 / TxChannelNum)]' .* 2 .* pi * ones(1, TxBFElementNum), 1, []);
53 | TxAntennaPos(1,:) = cos(TxAntennaThetaAll) .* TxAntennaRadiusAll;
54 | TxAntennaPos(2,:) = sin(TxAntennaThetaAll) .* TxAntennaRadiusAll;
55 | TxAntennaPos(4,:) = ones(1, TxAntennaNum);
56 |
57 | % Wavelength calculation
58 | Lambda = physconst('LightSpeed') * (1 / Frequency);
59 |
60 | if (~exist('DisplayRange', 'var'))
61 | DisplayRange = 'Auto';
62 | end
63 |
64 | if (~exist('Method', 'var'))
65 | Method = 'Auto';
66 | end
67 |
68 | if (isstring(Method) || ischar(Method))
69 | if (strcmp(Method, 'Auto'))
70 | Method = 'A-OAM';
71 | end
72 | end
73 |
74 | if (isstring(TxFocusingAngle) || ischar(TxFocusingAngle))
75 | if (strcmp(TxFocusingAngle, 'Auto'))
76 | % The focus is set at ten times the radius of the transmitting
77 | % antenna by default.
78 | TxFocusingAngle = atan(TxAntennaRadius / 10);
79 | end
80 | end
81 |
82 | if (isstring(DisplayRange) || ischar(DisplayRange))
83 | if (strcmp(DisplayRange, 'Auto'))
84 | TopRange = [(TxAntennaRadius); (TxAntennaRadius); round(TxAntennaRadius ./ tan(TxFocusingAngle) .* 1.4); 0];
85 | BottomRange = [0; 0; round(TxAntennaRadius ./ tan(TxFocusingAngle) .* 0.6); 0];
86 | DisplayRange = [TopRange BottomRange (TopRange - BottomRange) ./ [500; 500; 1; 1]];
87 | DisplayRange(3,3) = 1;
88 | end
89 | end
90 |
91 | % XZ Plane calculate
92 | XSize = round((DisplayRange(1,1) - DisplayRange(1,2)) / DisplayRange(1,3)) + 1;
93 | ZSize = (DisplayRange(3,1) - DisplayRange(3,2)) / DisplayRange(3,3) + 1;
94 | XRange = [DisplayRange(1,2) : DisplayRange(1,3) : DisplayRange(1,1)];
95 | ZRange = [DisplayRange(3,2) : DisplayRange(3,3) : DisplayRange(3,1)];
96 | XZPlaneDistance = zeros(XSize, ZSize, TxAntennaNum);
97 | XZPlanePoint = ones(4, XSize, ZSize);
98 | XZPlanePoint(1,:,:) = reshape(repmat(XRange, ZSize, 1)', [1, XSize, ZSize]);
99 | XZPlanePoint(2,:,:) = XZPlanePoint(2,:,:) .* 0;
100 | XZPlanePoint(3,:,:) = reshape(repmat(ZRange', 1, XSize)', [1, XSize, ZSize]);
101 |
102 | for index = 1 : TxAntennaNum
103 | XZPlaneDistance(:,:,index) = reshape(sqrt(sum((TxAntennaPos(:, index) - XZPlanePoint) .^ 2, 1)), [XSize, ZSize]);
104 | end
105 |
106 |
107 | XZPlaneDistance = ((XZPlaneDistance < Lambda) .* Lambda) + ((XZPlaneDistance >= Lambda) .* XZPlaneDistance);
108 | XZAttenuation = Lambda ./ (4 .* pi .* XZPlaneDistance) / sqrt(TxBFElementNum) / sqrt(TxChannelNum);
109 | XZPhase = exp(XZPlaneDistance ./ Lambda .* 2 .* pi .* 1i);
110 | XZReceive = zeros(XSize, ZSize, TxAntennaNum, MaxMode);
111 |
112 | for i = 1 : MaxMode
113 | OAMMode = i;
114 | for indexcircle = 0 : (TxBFElementNum - 1)
115 | if(strcmp(Method, 'A-OAM'))
116 | BFPhaseOffset = indexcircle .* sin(TxFocusingAngle) .* TxBFDistance / Lambda .* 2 .* pi;
117 | elseif(strcmp(Method, 'H-OAM'))
118 | BFPhaseOffset = sqrt(sum(TxAntennaPos(:, indexcircle * TxChannelNum + 1) .^ 2) - 1 + (TxAntennaRadius ./ tan(TxFocusingAngle)) .^ 2) / Lambda .* 2 .* pi;
119 | else
120 | BFPhaseOffset = 0;
121 | end
122 | for index = 1 : TxChannelNum
123 | txi = indexcircle * TxChannelNum + index;
124 | XZReceive(:, :, txi, OAMMode) = XZAttenuation(:, :, txi) .* XZPhase(:, :, txi) .* exp(2 .* pi .* 1i .* (index - 1) ./ TxChannelNum .* OAMMode - BFPhaseOffset .* 1i);
125 | end
126 | end
127 | end
128 |
129 | XZReceiveTotal = sum(XZReceive,3) .^ 2;
130 |
131 | IndexMaxRecv = zeros(1, ZSize, 1, OAMMode);
132 | for OAMMode = 1 : MaxMode
133 | for z = 1 : ZSize
134 | if(strcmp(Method, 'A-OAM'))
135 | [~,IndexPeakLoc] = findpeaks(abs(XZReceiveTotal(:, z, 1, OAMMode)));
136 | elseif(strcmp(Method, 'H-OAM'))
137 | [~,IndexPeakLoc] = max(abs(XZReceiveTotal(:, z, 1, OAMMode)), [], 1);
138 | else
139 | [~,IndexPeakLoc] = findpeaks(abs(XZReceiveTotal(:, z, 1, OAMMode)));
140 | end
141 |
142 | IndexMaxRecv(1, z, 1, OAMMode) = IndexPeakLoc(1);
143 | end
144 | end
145 | end
146 |
147 |
--------------------------------------------------------------------------------
/tool/OFDMVisualizer.m:
--------------------------------------------------------------------------------
1 | function [] = OFDMVisualizer(IQSignal, SubCarrierNum, CyclicPrefixNum, Filter, SampleRate, DisplayRBWFrequency)
2 |
3 | figure;
4 | TiledFigure = tiledlayout("flow");
5 |
6 | [ChannelNum, IQLength] = size(IQSignal.Signal);
7 | RawLength = IQLength / SubCarrierNum;
8 | RawSignal = reshape(IQSignal.Signal', SubCarrierNum, RawLength, ChannelNum);
9 |
10 | SampleFactor = SampleRate / IQSignal.SampleRate;
11 | SpectrumLength = RawLength * (SubCarrierNum + CyclicPrefixNum) * SampleFactor + size(Filter, 2);
12 | SpectrumWide = round(SpectrumLength / SampleFactor);
13 | SpectrumWideFrequency = SpectrumWide / SpectrumLength * SampleRate;
14 | SpectrumStepFrequency = 1 / SpectrumWide * SpectrumWideFrequency;
15 | DisplayLength = SpectrumWide * 2 + 1;
16 | DisplaySignal = zeros(SubCarrierNum, DisplayLength);
17 | DisplayHorizon = [flip(-[SpectrumStepFrequency : SpectrumStepFrequency : SpectrumWideFrequency]) [0 : SpectrumStepFrequency : SpectrumWideFrequency]];
18 | if (~exist('DisplayRBWFrequency', 'var'))
19 | DisplayRBWFrequency = (IQSignal.SampleRate / SubCarrierNum) * 0.1;
20 | end
21 | DisplayRBWRelatively = DisplayRBWFrequency / SampleRate;
22 | DisplayFilterSigma = DisplayRBWRelatively / 2 / sqrt(log(2));
23 | DisplayFilterDefinitionDomain = [flip(-[(1 / SpectrumLength) : (1 / SpectrumLength) : (7 * DisplayFilterSigma)]) [0 : (1 / SpectrumLength) : (7 * DisplayFilterSigma)]];
24 | DisplayFilter = exp(-(DisplayFilterDefinitionDomain .^ 2) / (2 .* DisplayFilterSigma .^ 2));
25 |
26 | for index = 1 : SubCarrierNum;
27 | AnalyzeWindow = zeros(SubCarrierNum, RawLength, ChannelNum);
28 | AnalyzeWindow(index, :, :) = ones(1, RawLength, ChannelNum);
29 | AnalyzeSignal = RawSignal .* AnalyzeWindow;
30 | BaseSignal = ifft(AnalyzeSignal) * sqrt(SubCarrierNum);
31 | OrthogonalSignal = reshape([BaseSignal((SubCarrierNum - CyclicPrefixNum + 1) : SubCarrierNum, :, :); BaseSignal], RawLength * (SubCarrierNum + CyclicPrefixNum), ChannelNum)';
32 | ShapedSignal = upfirdn([OrthogonalSignal zeros(ChannelNum, 1)]', Filter', SampleFactor)';
33 | SpectrumSignal = (abs(fft(ShapedSignal' .* IQSignal.ReferenceVoltage)' / SpectrumLength) .^ 2) / IQSignal.ReferenceImpedance;
34 | DisplaySpectrum = abs([SpectrumSignal(:, SpectrumLength - SpectrumWide + 1 : SpectrumLength) SpectrumSignal(:, 1 : SpectrumWide + 1)]);
35 | DisplaySignal(index, :) = mean(DisplaySpectrum, 1);
36 | end
37 | [FrequencyUnit, FrequencyFactor] = UnitConvert(SpectrumWideFrequency, 'Hz');
38 |
39 | DisplayFiltered = convn(DisplaySignal, DisplayFilter, "same");
40 |
41 | nexttile;
42 | plot(DisplayHorizon * FrequencyFactor, pow2db(DisplayFiltered'));
43 | xlim([(-SpectrumWideFrequency * FrequencyFactor) (SpectrumWideFrequency * FrequencyFactor)]);
44 |
45 | nexttile;
46 | plot(DisplayHorizon * FrequencyFactor, pow2db(sum(DisplayFiltered, 1)'));
47 | xlim([(-SpectrumWideFrequency * FrequencyFactor) (SpectrumWideFrequency * FrequencyFactor)]);
48 |
49 | xlabel(TiledFigure, [FrequencyUnit ' (RBW = ' num2str(DisplayRBWFrequency * FrequencyFactor) FrequencyUnit ')']);
50 | ylabel(TiledFigure, 'dBW');
51 | title(TiledFigure, 'OFDM Visualizer');
52 | end
53 |
54 |
--------------------------------------------------------------------------------
/tool/ShapingFilterDesign.m:
--------------------------------------------------------------------------------
1 | function [Filter] = ShapingFilterDesign(Beta, ShapingSpan, BaseSampleRate, BaseBitRate)
2 | %ShapingFilterDesign Design channel shaping filter.
3 | %Introduction:
4 | % This function designs the shaping filter utilized in shaping the
5 | % signals to avoid inter symbol interference.
6 | %Syntax:
7 | % Filter = ShapingFilterDesign(Beta, ShapingSpan, BaseSampleRate, BaseBitRate)
8 | %Description:
9 | % Filter = ShapingFilterDesign(Beta, ShapingSpan, BaseSampleRate, BaseBitRate)
10 | % returns the designed filter.
11 | %Input Arguments:
12 | % Beta: (double)
13 | % Rolloff factor for raised cosine FIR pulse-shaping filter.
14 | % ShapingSpan: (positive integer scalar)
15 | % Number of symbols effected.
16 | % BaseSampleRate: (double)
17 | % Baseband signal sample rate in Sa/s.
18 | % BaseBitRate: (double)
19 | % Baseband signal binary stream bit rate in bit/s.
20 | %Output Arguments:
21 | % Filter: (matrix)
22 | % Filter coefficient.
23 | %Author:
24 | % Tifer King
25 | %License:
26 | % Please refer to the 'LICENSE' file included in the root directory
27 | % of the project.
28 |
29 | Filter = rcosdesign(Beta, ShapingSpan, BaseSampleRate / BaseBitRate, "sqrt");
30 | end
31 |
32 |
--------------------------------------------------------------------------------
/tool/SignalResample.m:
--------------------------------------------------------------------------------
1 | function [ResampledSignal] = SignalResample(Signal, SampleRate, Mode)
2 | %SignalResample Resample signals.
3 | %Introduction:
4 | % This function resample signals using 'interp1' that contains more
5 | % method than 'resample'.
6 | %Syntax:
7 | % ResampledSignal = SignalResample(Signal, SampleRate, Mode)
8 | %Description:
9 | % ResampledSignal = SignalResample(Signal, SampleRate, Mode)
10 | % returns the resampled signals.
11 | %Input Arguments:
12 | % Signal: (AnalogSignal)
13 | % Original signals.
14 | % SampleRate: (double)
15 | % Resample target sample rate in Sa/s.
16 | % Mode: (string)
17 | % 'zero' for resample the signal by insert 0.
18 | % 'lowpass' for resample the signal by insert 0 then filtered by
19 | % lowpass filter.
20 | % Please refer to 'interp1' for more detail of other options.
21 | %Output Arguments:
22 | % ResampledSignal: (AnalogSignal)
23 | % Resampled signals.
24 | %Author:
25 | % Tifer King
26 | %License:
27 | % Please refer to the 'LICENSE' file included in the root directory
28 | % of the project.
29 |
30 | if (Signal.SampleRate == SampleRate)
31 | ResampledSignal = Signal;
32 | elseif(Signal.SampleRate > SampleRate)
33 | [UpRate,DownRate] = rat(SampleRate / Signal.SampleRate);
34 | ResampledSignal = InitAnalogSignal(Signal.ChannelNum, Signal.TimeStart, Signal.TimeEndurance, SampleRate, 'Template', 'As', Signal);
35 | for index = 1 : Signal.ChannelNum
36 | TempSignal = resample(Signal.Signal(index,:), UpRate, DownRate);
37 | ResampledSignal.Signal(index,:) = TempSignal(1:size(ResampledSignal.Signal(index,:), 2));
38 | end
39 | else
40 | ResampleRate = Signal.SampleRate / SampleRate;
41 | ResampledSignal = InitAnalogSignal(Signal.ChannelNum, Signal.TimeStart, Signal.TimeEndurance, SampleRate, 'Template', 'As', Signal);
42 | if (strcmp(Mode, 'zero'))
43 | if ((SampleRate / Signal.SampleRate) ~= round(SampleRate / Signal.SampleRate))
44 | warning("The resample rate for 'zero' mode is not integer, this may cause error.");
45 | end
46 | ResampleIndex = [1 : (ResampledSignal.TimeEndurance * ResampledSignal.SampleRate)] * ResampleRate + (1 - ResampleRate);
47 | ResampleIndex = (abs(ResampleIndex - round(ResampleIndex)) < (ResampleRate / 2)) .* round(ResampleIndex) + 1;
48 | ResampleOrigin = [zeros(Signal.ChannelNum, 1) Signal.Signal];
49 | for index = 1 : Signal.ChannelNum
50 | ResampledSignal.Signal(index,:) = ResampleOrigin(index, ResampleIndex);
51 | end
52 | elseif(strcmp(Mode, 'lowpass'))
53 | if ((SampleRate / Signal.SampleRate) ~= round(SampleRate / Signal.SampleRate))
54 | warning("The resample rate for 'zero' mode is not integer, this may cause error.");
55 | end
56 | ResampleIndex = [1 : (ResampledSignal.TimeEndurance * ResampledSignal.SampleRate)] * ResampleRate + (1 - ResampleRate);
57 | ResampleIndex = (abs(ResampleIndex - round(ResampleIndex)) < (ResampleRate / 2)) .* round(ResampleIndex) + 1;
58 | ResampleOrigin = [zeros(Signal.ChannelNum, 1) Signal.Signal];
59 | for index = 1 : Signal.ChannelNum
60 | ResampledSignal.Signal(index,:) = lowpass(ResampleOrigin(index, ResampleIndex), Signal.SampleRate / 2, SampleRate);
61 | end
62 | else
63 | for index = 1 : Signal.ChannelNum
64 | ResampledSignal.Signal(index,:) = interp1([Signal.Signal(index,:) 0], [1 : (ResampledSignal.TimeEndurance * ResampledSignal.SampleRate)] * ResampleRate + (1 - ResampleRate), Mode);
65 | end
66 | end
67 | end
68 | end
69 |
70 |
--------------------------------------------------------------------------------
/tool/ZadoffChuGen.m:
--------------------------------------------------------------------------------
1 | function [ZadoffChuSeq] = ZadoffChuGen(ChannelNum, Length)
2 | %ZadoffChuGen Generate zadoffchu sequence for multi-channel.
3 | %Introduction:
4 | % Compared to the ‘zadoffChuSeq’ by MATLAB, this function can generate a
5 | % more general definition of zadoff-chu sequence. This function can
6 | % generate multiple sequences at once.
7 | %Syntax:
8 | % ZadoffChuSeq = ZadoffChuGen(ChannelNum, Length)
9 | %Description:
10 | % ZadoffChuSeq = ZadoffChuGen(ChannelNum, Length)
11 | % returns the multi-channel zadoff-chu sequence.
12 | %Input Arguments:
13 | % ChannelNum: (positive integer scalar)
14 | % Number of channels.
15 | % Length: (positive integer scalar)
16 | % Zadoff-Chu sequence length.
17 | %Output Arguments:
18 | % ZadoffChuSeq: (matrix)
19 | % Multi-channel zadoff-chu sequence.
20 | %Author:
21 | % Tifer King
22 | %License:
23 | % Please refer to the 'LICENSE' file included in the root directory
24 | % of the project.
25 |
26 | N = Length;
27 | q = 0;
28 | cf = mod(N, 2);
29 | ZadoffChuSeq = zeros(ChannelNum, N);
30 | RootOffset = round(Length / 4);
31 | for index = 1 : ChannelNum
32 | while gcd(nthprime(index + RootOffset), Length) ~= 1
33 | RootOffset = RootOffset + 1;
34 | end
35 | u = nthprime(index + RootOffset);
36 | ZadoffChuSeq(index, :) = exp(-1i * (pi * u * [0 : N-1] .* ([0 : N-1] + cf + 2 * q)) / N);
37 | end
38 | end
39 |
40 |
--------------------------------------------------------------------------------