├── 1.mat ├── pic ├── model.png └── transmitter.png ├── README.md ├── ofdm_syn.m ├── model.py └── otfs_syn.m /1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzhoujg/OTFS_AMC/HEAD/1.mat -------------------------------------------------------------------------------- /pic/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzhoujg/OTFS_AMC/HEAD/pic/model.png -------------------------------------------------------------------------------- /pic/transmitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzhoujg/OTFS_AMC/HEAD/pic/transmitter.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep Learning-Based Automatic Modulation Recognition in OTFS and OFDM systems 2 | 3 | --- 4 | 5 | ### Introduction 6 | 7 | **Background:** Automatic modulation recognition (AMR) is one of the most essential techniques in non-cooperative orthogonal time frequency space (OTFS) and orthogonal frequency division multiplexing (OFDM) communication systems. Since coexistence of OTFS and OFDM is a potential and practical solution in the future wireless communication scenarios, classification of the OTFS scheme and the OFDM scheme will be a challenging and meaningful task. 8 | 9 | This is the code for our paper ["Deep Learning-Based Automatic Modulation Recognition in OTFS and OFDM systems"](https://ieeexplore.ieee.org/document/10200971). In this paper, we propose a deep learningbased method, including multi-layer convolution neural networks (CNNs) and an attention-based residual Squeeze-and-Excitation Module (SE), to extract effective characteristics of OTFS and OFDM signals in multi-path Doppler spread fading channel. 10 | 11 | This repo consists of two parts: the **transmitter* and the **deep learning-based* model. 12 | 13 | --- 14 | 15 | ### Transmitter 16 | 17 | Transmitter 18 | 19 | Transmitter module aims to produce the OTFS data and OFDM data. According to above figure, the process of generating OTFS signals is related to OFDM modulation. 20 | 21 | First, we produce OTFS data and OFDM data via MATLAB. You can refer to the file *otfs_syn.m* and *ofdm_syn.m* for the OTFS modulation and OFDM modulation, respectively. 22 | 23 | --- 24 | 25 | ### Deep Learning-based model 26 | 27 | Then, we employ the classic CNN-5 model to realize the classification. SE module is leveraged to extract global features. Results show that attention-based module can also deal with the signal which is corrupted by Doppler effect. 28 | 29 | ![model](https://github.com/jzhoujg/OTFS_AMC/blob/main/pic/model.png) 30 | 31 | The model is realized in **model.py**. You can run this code directly by 32 | 33 | `python model.py` 34 | 35 | where the file 1.mat is a OTFS sample generated by *otfs_syn.m*. 36 | 37 | If the entire process is requested, you can reproduce a training module and a evalulating module with the file *model.py* by yourself. 38 | 39 | We will update this part next time. 40 | 41 | --- 42 | 43 | ## Citation 44 | 45 | If you find something helpful, you are welcome to cite this work. 46 | 47 | `@INPROCEEDINGS{10200971, 48 | author={Zhou, Jinggan and Liao, Xuewen and Gao, Zhenzhen}, 49 | booktitle={2023 IEEE 97th Vehicular Technology Conference (VTC2023-Spring)}, 50 | title={Deep Learning-Based Automatic Modulation Recognition in OTFS and OFDM systems}, 51 | year={2023}, 52 | volume={}, 53 | number={}, 54 | pages={1-5}, 55 | keywords={Wireless communication;Training;Time-frequency analysis;Vehicular and wireless technologies;Transmitters;OFDM;Modulation;Orthogonal time frequency space (OTFS);automatic modulation recognition (AMR);deep learning;Squeeze-and-Excitation networks}, 56 | doi={10.1109/VTC2023-Spring57618.2023.10200971}}` 57 | -------------------------------------------------------------------------------- /ofdm_syn.m: -------------------------------------------------------------------------------- 1 | clc; 2 | clear; 3 | %% 参数设置 4 | % OFDM的调制参数设置 5 | N_sc=64; %一个OFDM符号中含有的子载波数 6 | N_fft=64; % FFT 长度 7 | N_cp=16; % 循环前缀长度、Cyclic prefix 8 | N_symbo=N_fft+N_cp; % 1个完整OFDM符号长度 9 | N_frm= 32; % 一个OFDM符号之中包含的子帧 10 | 11 | % 调制方式批量生成设置 12 | hub = ['abcdef']; % 调制序号 13 | M_dict = struct('a','2','b','4','c','8','d','16','e','64','f','256') % 调制阶数的字典 14 | mode_dict = struct('a','bpsk','b','qpsk','c','8psk','d','16qam','e','64qam','f','256qam') %调制方式字典 15 | 16 | % 信道条件设置 17 | Sampling_rate = 100e3; %信道采样频率 18 | Path_delay = [0,1.4e-6,3e-6,7e-6,10.4e-6,16.8e-6,17.3e-6]; %信道的多径延迟 19 | path_gain = [0,-1.5,-3.6,-7,-9.1,-12,-16.9]; %信道多径增益 20 | max_doppler_shift = 10e2; %最大多普勒偏移 21 | 22 | % 多径-多普勒信道的生成 23 | rayleighchan = comm.RayleighChannel(... 24 | 'SampleRate',Sampling_rate, ... 25 | 'PathDelays',Path_delay, ... 26 | 'AveragePathGains',path_gain, ... 27 | 'NormalizePathGains',true, ... 28 | 'MaximumDopplerShift',max_doppler_shift); 29 | 30 | % 第一个for循环,主要为了遍历各种预设的调制方式 31 | for mm = hub 32 | 33 | mode = mode_dict.(mm); %选择调制模式 34 | M = str2num(M_dict.(mm)); %调制阶数 35 | SNR=-10; %初始信噪比仿真信噪比 36 | Nd=N_sc*log2(M); % 数据总数 37 | num = 3000; % 一个调制方式的数据单位总量 38 | 39 | % 文件名设置 40 | filename =['./otfs_rice/a',num2str(mm-'a'+1),'_ofdm_',mode,'/']; %保存文件名设置 41 | FILE = [mode,'_']; 42 | snr = num2str(SNR); 43 | 44 | %调制方式的生成 45 | for nn = 1:num 46 | 47 | 48 | sig_rec = [];% 保存信号 49 | 50 | % DD 瑞利信道的生成 51 | rayleighchan = comm.RayleighChannel(... 52 | 'SampleRate',Sampling_rate, ... 53 | 'PathDelays',Path_delay, ... 54 | 'AveragePathGains',path_gain, ... 55 | 'NormalizePathGains',true, ... 56 | 'MaximumDopplerShift',max_doppler_shift); 57 | 58 | % 子帧的生成 59 | for jj = 1: N_frm 60 | 61 | %% 基带数据数据产生 62 | P_data=randi([0 1],1,Nd); 63 | %% 调制 64 | % QPSK 65 | data_temp1= reshape(P_data,log2(M),[])'; %以每组2比特进行分组,M=4 66 | data_temp2= bi2de(data_temp1); %二进制转化为十进制 67 | % modu_data=pskmod(data_temp2,M,pi/M); % QPSK调制 68 | 69 | if M < 10 70 | modu_data=pskmod(data_temp2,M,pi/M); 71 | end 72 | % QAM调制的方式 73 | if M > 10 74 | modu_data=qammod(data_temp2,M,'UnitAveragePower',true); 75 | end 76 | 77 | % 信号进行并串转换 78 | data = modu_data; 79 | 80 | %% IFFT 81 | % 信号做DFT 82 | ifft_data=ifft(data,N_fft)*sqrt(N_fft); 83 | %% 插入保护间隔、循环前缀 84 | Tx_cd=[ifft_data(N_fft-N_cp+1:end,:);ifft_data];%把ifft的末尾N_cp个数补充到最前面 85 | %% 并串转换 86 | Tx_data=reshape(Tx_cd,[],1);%由于传输需要 87 | %% 信道(通过多经瑞利信道) 88 | %生成信道 89 | 90 | % CUM4EST(sig_rec, 1, 2560, 0, 'biased', 0, 0 91 | 92 | % 信号经过信道,相当于作卷积的过程 93 | Tx_data= rayleighchan(Tx_data); 94 | % Tx_data = conv(Tx_data,H); 95 | rx_channel=awgn(Tx_data,SNR,'measured');%添加高斯白噪声 96 | sig_rec = [sig_rec;rx_channel]; 97 | end 98 | savewords = [filename,FILE,num2str(nn),snr,'.mat']; 99 | if mod(nn,500)==0 100 | nn 101 | end 102 | if mod(nn,num/6) == 0 103 | SNR = SNR + 5 104 | end 105 | save(savewords,'sig_rec') 106 | end 107 | end -------------------------------------------------------------------------------- /model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from PIL import Image 3 | from torchvision import transforms 4 | import torch.nn as nn 5 | import numpy as np 6 | import scipy.io as scio 7 | import torch 8 | 9 | class SE_Moudule(nn.Module): 10 | 11 | def __init__(self, in_channel, out_channel, stride=1): 12 | super(SE_Moudule, self).__init__() 13 | 14 | self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=(1, 76), stride=stride, padding=0) 15 | self.linear1 = nn.Linear(32, 8) 16 | self.relu1 = nn.PReLU() 17 | self.drop = nn.Dropout(0.2) 18 | self.linear2 = nn.Linear(8, 32) 19 | self.act = nn.Sigmoid() 20 | 21 | def forward(self, x): 22 | identity = x 23 | inbatch = x.size()[0] 24 | out = self.conv1(x) 25 | out = torch.flatten(out,1) 26 | out = self.linear1(out) 27 | out = self.relu1(out) 28 | out = self.drop(out) 29 | out = self.linear2(out) 30 | out = self.act(out) 31 | temp = torch.zeros(inbatch,32,1,1) 32 | temp[:,:,0,0] = out 33 | out = temp * identity 34 | 35 | return out 36 | 37 | 38 | class OTFS_OFDM_CNN(nn.Module): 39 | 40 | def __init__(self, batch_size = 128, num_channels = 1,num_frames = 34, height = 20, width = 20,device="cpu", Trans=False): 41 | 42 | 43 | super(OTFS_OFDM_CNN, self).__init__() 44 | self.features_1 = nn.Sequential(nn.Conv2d(in_channels=1,out_channels=64,kernel_size= (2,128), stride=4, padding=0), 45 | nn.BatchNorm2d(64), 46 | nn.PReLU(), 47 | ) 48 | self.features_2 = nn.Sequential(nn.Conv2d(in_channels=64,out_channels=32,kernel_size= (1,8),stride=8, padding=0), 49 | nn.BatchNorm2d(32), 50 | nn.PReLU(), 51 | ) 52 | self.head = nn.Sequential( 53 | nn.Linear(2432,256), 54 | nn.PReLU(), 55 | nn.Dropout(0.2), 56 | nn.Linear(256, 64), 57 | nn.PReLU(), 58 | nn.Dropout(0.5), 59 | nn.Linear(64, 2) 60 | ) 61 | self.se_module = SE_Moudule(in_channel=32,out_channel=32) 62 | self.apply(_init_vit_weights) 63 | self.batchsize = batch_size 64 | self.num_channels = num_channels 65 | self.num_frames = num_frames 66 | self.height = height 67 | self.width = width 68 | self.device = device 69 | self.trans = Trans 70 | 71 | def ReadOFDMSymbol(self, data, in_batch=32, lengthofsymbol=2560): 72 | 73 | in_batch = data.size()[0] 74 | data = torch.squeeze(data) 75 | res = torch.zeros(in_batch, 1,2, lengthofsymbol) 76 | res[:,0,0, :] = data.real 77 | res[:,0,1, :] = data.imag 78 | 79 | 80 | return res 81 | 82 | 83 | 84 | def forward(self,x): 85 | x = self.ReadOFDMSymbol(x) 86 | x = self.features_1(x) 87 | x = self.features_2(x) 88 | x = self.se_module(x) 89 | x = torch.flatten(x,1) 90 | x = self.head(x) 91 | 92 | return x 93 | 94 | def _init_vit_weights(m): 95 | """ 96 | ViT weight initialization 97 | :param m: module 98 | """ 99 | if isinstance(m, nn.Linear): 100 | nn.init.trunc_normal_(m.weight, std=.01) 101 | if m.bias is not None: 102 | nn.init.zeros_(m.bias) 103 | elif isinstance(m, nn.Conv3d): 104 | nn.init.kaiming_normal_(m.weight, mode="fan_out") 105 | if m.bias is not None: 106 | nn.init.zeros_(m.bias) 107 | elif isinstance(m, nn.BatchNorm3d): 108 | nn.init.zeros_(m.bias) 109 | nn.init.ones_(m.weight) 110 | 111 | if __name__ == '__main__': 112 | data = scio.loadmat("./1.mat") 113 | python_y = np.array(data['sig_rec']) 114 | am = np.zeros([1,2560,1],dtype=complex) 115 | am[0,:,:] = python_y[:,:] 116 | am = torch.from_numpy(am) 117 | model = OTFS_OFDM_CNN() 118 | python_y = model.forward(am) 119 | print(python_y) -------------------------------------------------------------------------------- /otfs_syn.m: -------------------------------------------------------------------------------- 1 | clc; 2 | clear; 3 | %% 参数设置 4 | 5 | % 基本的调制方式的参数设置 6 | N_ti = 8; % 多普勒索引数;时间间隔 7 | N_sc= 64; % 时延索引数;一个OFDM符号中含有的子载波数 8 | cp_length = 16; % 循环前缀的长度 9 | num = 12000 ; % 每个调制方式生成数目 10 | 11 | 12 | % 文件批处理的方式 13 | hub = ['abcdef']; % 索引 14 | N_frm=4; % 每种信噪比下的仿真帧数、frame 15 | M_dict = struct('a','2','b','4','c','8','d','16','e','64','f','256') % 调制阶数字典 16 | mode_dict = struct('a','bpsk','b','qpsk','c','8psk','d','16qam','e','64qam','f','256qam') %调制模式字典 17 | 18 | for mm =hub 19 | 20 | SNR=-10; %仿真信噪比 21 | M=str2num(M_dict.(mm)); %调制阶数 22 | mode = mode_dict.(mm); %调制模式 23 | Nd=N_ti*N_sc*log2(M); % 数据总数 24 | % L = 1; %信道长度 25 | % K_rician = 20; 26 | % 文件名设置 27 | 28 | 29 | filename =['./otfs_rice/b',num2str(mm-'a'+1),'_otfs_',mode,'/']; 30 | FILE = [mode,'_']; % 文件命名方式 31 | snr = ['_',num2str(SNR),'dB']; %SNR 32 | 33 | %% 信道生成 34 | Sampling_rate = 100e3; %信道的采样频率 35 | Path_delay = [0,1.4e-6,3e-6,7e-6,10.4e-6,16.8e-6,17.3e-6]; %信道延迟 36 | path_gain = [0,-1.5,-3.6,-7,-9.1,-12,-16.9]; %信道增益 37 | max_doppler_shift = 10e2; %最大多普勒频移 38 | rayleighchan = comm.RayleighChannel(... 39 | 'SampleRate',Sampling_rate, ... 40 | 'PathDelays',Path_delay, ... 41 | 'AveragePathGains',path_gain, ... 42 | 'NormalizePathGains',true, ... 43 | 'MaximumDopplerShift',max_doppler_shift); 44 | 45 | % OFDM发射机部分的发射生成 46 | F_M = zeros(N_sc,N_sc); 47 | N_CP=zeros(cp_length,N_sc); 48 | 49 | % CP生成序列的制作 50 | for i = 1:cp_length 51 | N_CP(i,i) = 1; 52 | end 53 | A_CP = [N_CP;eye(N_sc)]; 54 | 55 | % IFFT变换的算子 56 | for i_sc = 0: N_sc-1 57 | for i_ti = 0 : N_sc-1 58 | F_M(i_sc+1,i_ti+1) =sqrt(1/N_sc)*exp (-1j * 2 * pi * i_sc *i_ti /N_sc ); 59 | end 60 | end 61 | 62 | % 63 | for nn = 1:num 64 | % 参数初始化 65 | snr = ['_',num2str(SNR),'dB']; 66 | sig_rec = []; 67 | 68 | %信道生成 69 | rayleighchan = comm.RayleighChannel(... 70 | 'SampleRate',Sampling_rate, ... 71 | 'PathDelays',Path_delay, ... 72 | 'AveragePathGains',path_gain, ... 73 | 'NormalizePathGains',true, ... 74 | 'MaximumDopplerShift',max_doppler_shift); 75 | 76 | for jj = 1: N_frm 77 | 78 | %% 基带数据数据产生 79 | P_data=randi([0 1],1,Nd); % 生成数据的 80 | %% 调制 81 | data_temp1= reshape(P_data,log2(M),[])'; %以每组2比特进行分组,M=4 82 | data_temp2= bi2de(data_temp1); 83 | %二进制转化为十进制 84 | if M < 10 85 | modu_data=pskmod(data_temp2,M,pi/M); 86 | end% QPSK调制 87 | if M > 10 88 | modu_data=qammod(data_temp2,M,'UnitAveragePower',true); 89 | end 90 | % 16QAM 91 | % data_temp1= reshape(P_data,4,[])'; %以每组2比特进行分组,M=4 92 | % data_temp2= bi2de(data_temp1); %二进制转化为十进制 93 | % modu_data=qammod(data_temp2,16); % QPSK调制 94 | 95 | data = reshape(modu_data,[N_sc,N_ti]); 96 | 97 | %% ISFFT 98 | ifft_temp=fft(data,N_sc,1)/sqrt(N_sc); 99 | isfft_data=ifft(ifft_temp,N_ti,2)*sqrt(N_ti); 100 | refe =ifft(data,N_ti,2)*sqrt(N_ti); 101 | 102 | 103 | 104 | %% IFFT 105 | ifft_data = F_M'*isfft_data; %本质上对列做了一个IFFT 106 | %% 插入保护间隔、循环前缀 107 | Tx_cd = A_CP * ifft_data; 108 | % Tx_cd=[ifft_data(N_fft-N_cp+1:end,:);ifft_data];%把ifft的末尾N_cp个数补充到最前面 109 | %% 并串转换 110 | Tx_data=reshape(Tx_cd,[],1);%由于传输需要 111 | 112 | %% 信道(通过多经瑞利信道) 113 | %生成信道 114 | % 信号经过信道,相当于作卷积的过程 115 | Tx_data = rayleighchan(Tx_data); 116 | rx_channel=awgn(Tx_data,SNR,'measured');%添加高斯白噪声 117 | sig_rec = [sig_rec;rx_channel]; %保存数据 118 | end 119 | savewords = [filename,FILE,num2str(nn),snr,'.mat']; 120 | if mod(nn,500) == 0 121 | nn 122 | end 123 | if mod(nn,num/6) == 0 124 | SNR = SNR + 5 125 | end 126 | 127 | 128 | save(savewords,'sig_rec'); 129 | end 130 | end 131 | --------------------------------------------------------------------------------