├── .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 | ![系统结构图](doc/img/SystemStructure.svg "系统结构图") 24 | 25 | 当然,在本项目中的文件目录并不是按照系统结构来的,而是根据文件的不同功能属性进行组合的。本项目中的文件主要分为: 26 | 27 | - ![](doc/img/Document.svg) doc: 项目各模块的介绍文档以及使用手册 28 | - ![](doc/img/Library.svg) lib: 组成通信系统的模块文件 29 | - ![](doc/img/Probe.svg) probe: 用于分析系统状态和观察系统运行的探针 30 | - ![](doc/img/Tool.svg) tool: 一些可能会在构建通信系统时用到的辅助工具 31 | - ![](doc/img/Example.svg) example: 一些基于该项目的样例工程 32 | 33 | 本项目也会不断的增加新的模块,完善现有模块,希望能提供一个强大且易用的仿真工具集。 34 | 35 | ## 模块目录及简介 36 | 37 | - ![](doc/img/Transmitter.svg) ChannelShaping: 信道脉冲成型模块,用来做基带信号的脉冲成型,一般用于解决码间串扰。 38 | - ![](doc/img/Transmitter.svg) DigitalSignalGen: 数字序列生成器,可以生成特定测试序列或者随机的二进制序列。 39 | - ![](doc/img/Transmitter.svg) FrameEncapsulate: 组帧模块,能够将特定的帧头与数据拼接形成一个数据帧。 40 | - ![](doc/img/Transmitter.svg) IQMixing: 正交上混频器,利用正交信号对基带进行上混频。 41 | - ![](doc/img/Transmitter.svg) Modulation: 调制器,将二进制序列调制成复平面上的星座符号。 42 | - ![](doc/img/Transmitter.svg) OrthogonalMatrixLoad: 正交矩阵加载器,用于对发射的多通道信号进行正交矩阵的加载,使其发射的信号具有正交性。这个模块通常用在涡旋电磁波通信等特殊场景中。 43 | - ![](doc/img/Transmitter.svg) PreambleGen: 帧头生成模块,利用该模块可以生成包含特定自相关或互相关序列的帧头。 44 | - ![](doc/img/Transmitter.svg) ![](doc/img/Receiver.svg) CarrierGen: 载波发生器,利用该模块生成载波信号并送入混频器,可以同时用在发射端和接收端。 45 | - ![](doc/img/Channel.svg) Channel: 信道仿真模块,根据信道的冲激响应和噪声水平实现对信道传输的仿真。 46 | - ![](doc/img/Receiver.svg) BaseSymbolSample: 基带符号采样器,根据特定采样率对基带的符号进行采样并输出其对应的复数值。 47 | - ![](doc/img/Receiver.svg) Demodulation: 解调器,将复平面上的星座信号进行判决,将信号还原成为数字信号。 48 | - ![](doc/img/Receiver.svg) FrameDecapsulate: 解帧模块,对收到的数据进行均衡和解帧头,回复字符数据。 49 | - ![](doc/img/Receiver.svg) IQDemixing: 正交下混频器,利用正交信号对射频信号进行下混频。 50 | - ![](doc/img/Receiver.svg) OrthogonalMatrixUnload: 正交矩阵卸载器,将多通道中的正交矩阵进行卸载,恢复成独立的数据通道。这个模块通常用在涡旋电磁波通信等特殊场景中。 51 | - ![](doc/img/Fundamental.svg) ChannelConvolution: 信道卷积核心,对输入信道的信号与信道进行冲击响应的核心计算函数。可以通过优化该函数提升信道仿真性能。 52 | - ![](doc/img/Fundamental.svg) InitAnalogSignal: 模拟信号结构体初始化核心,该函数定义了一种包含模拟信号基础特征等信息的结构体。具体结构和定义请参阅其说明文档。 53 | - ![](doc/img/Fundamental.svg) InitChannelImpulseResponse: 信道冲击响应结构体初始化核心,该函数定义了一种包含信道冲击响应基础特征等信息的结构体。具体结构和定义请参阅其说明文档。 54 | - ![](doc/img/Fundamental.svg) InitDigitalSignal: 数字信号结构体初始化核心,该函数定义了一种包含数字信号基础特征等信息的结构体。具体结构和定义请参阅其说明文档。 55 | 56 | 57 | ## 探针目录及简介 58 | 59 | - ![](doc/img/Probe.svg) ProbeBitError: 误码率探针,该探针用于比对发射信号与接收信号的二进制比特流是否一致,判断其产生的误码数量和误码率。 60 | - ![](doc/img/Probe.svg) ProbeChannelImpulse: 信道响应探针,图像化显示信道冲击响应。 61 | - ![](doc/img/Probe.svg) ProbeConstellation: 星座图探针,图像化显示信号的星座图,并在图中标出预设星座点的位置。 62 | - ![](doc/img/Probe.svg) ProbeDelay: 延迟探针,可以比较两个信号之间的时间差,也可以对比同一组信号经过传输后产生的延迟时间。 63 | - ![](doc/img/Probe.svg) ProbeSignalPower: 功率探针,检测目标信号的功率。 64 | - ![](doc/img/Probe.svg) ProbeSpectrum: 频谱探针,图像化显示信号的频谱,可自行调节频率显示范围及频谱的分辨率带宽(RBW)。 65 | - ![](doc/img/Probe.svg) ProbeWave: 波形探针,图像化显示信号的波形。 66 | - ![](doc/img/Fundamental.svg) UnitConvert: 自动单位转换,对输入数据进行分析,找出最适合其显示范围的缩放倍率和单位,使图像显示的坐标轴更美观。 67 | 68 | ## 工具目录及简介 69 | 70 | - ![](doc/img/Tool.svg) DemixingResampleFilter: 下混频滤波器设计工具,通过输入原始信号采样率和目标采样率,来设计下混频时的抗混叠滤波器。 71 | - ![](doc/img/Tool.svg) IdealOAMChannel: 理想涡旋电磁波(OAM)信道脉冲响应生成器,通过输入收发天线的参数和位置,生成理想涡旋电磁波的信道脉冲响应,天线是均匀圆阵列天线(UCA)。 72 | - ![](doc/img/Tool.svg) ShapingFilterDesign: 脉冲成型滤波器设计工具,针对基带信号的符号速率和目标采样率设计对应的脉冲成型滤波器。 73 | - ![](doc/img/Tool.svg) SignalResample: 信号重采样工具,对信号进行采样率变换以方便运算。 74 | - ![](doc/img/Tool.svg) ZadoffChuGen: Zadoff-Chu序列生成工具,生成一组不同根的Zadoff-Chu序列。 75 | 76 | ## 样例目录及简介 77 | 78 | - ![](doc/img/Example.svg) Example1: 一个简单的基于涡旋电磁波(OAM)的通信系统仿真。 79 | - ![](doc/img/Fundamental.svg) LoadEnvironment: 加载样例运行环境,需要加在样例运行最开始的地方。 80 | - ![](doc/img/Fundamental.svg) 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 | --------------------------------------------------------------------------------