DMR:LC和A–E分组的形成。
数字移动无线电协议(DMR)仍然是可靠的专业通信的标准. 对其行为进行建模对于测试和分析非常重要。 在[原始示例](https://engee.com/community/ru/catalogs/projects/sistemnaia-model-dmr 数据生成和同步的基本逻辑已经奠定。 然而,它没有包括控制信道和分组类型的传输的逻辑的详细处理。
在这个修订版本中,我们增加了以下几点。
*形成LC数据包(逻辑信道数据包)的完整逻辑。
A、B、C、D和E类型的数据包的正确形成*:这些数据包由服务头、信号字段组成,包括同步序列(A)或短格式LC(B、C、D、E),还包含控制位。
*使用xcorr的流的帧同步的机制也在这里得到了改进。
该扩展适用于协议的全面分析,时间延迟的模拟和在真实硬件上的测试。
我们将通过分析编写的数据包生成函数并在脚本中测试此函数来开始实施此项目,以便随后使用Engee功能块将其传输到模型。
首先,我们将连接一个带有辅助函数的文件。 每个函数在与编码、错误检查或消息结构形成相关的数据处理中执行特定任务。 下面是一个功能列表,简要说明了它们的用途。
-
de2bi(x,n)
转换十进制数x到长度的二进制数组n(右侧的低阶位)。 -
bi2de(位)
转换二进制数组bits到十进制数(将其解释为位字符串)。 -
编码(味精)
计算消息的控制位(CRCmsg使用多项式POLY和Reed-Solomon编码算法。 -
lc_header_mask(奇偶校验)
敷面膜START_MASK到奇偶校验位parity使用XOR操作。 -
log_mult(a,b)
使用对数表在Galois字段(GF(256))中执行乘法LOG_TABLE和EXP_TABLE. -
CS5bit(LCcrc)
计算72位数据块的5位校验和LCcrc(将字节求和,并将除法的剩余部分除以31)。 -
HemR(l)
计算矩阵的水平(逐行)汉明验证位l尺寸为9x11。 -
HemC(l,HR)
计算矩阵的垂直(列)汉明验证位l考虑到横向检查HR. -
打字机(CC)
基于4位代码生成20位消息类型CC从表中添加校验和ENCODE_2087.
path = "$(@__DIR__)/dmr_lib.jl"
println("库路径:$path")
include(path)
以下是我们实现的功能 Gen_Pkg 处理输入参数,生成并返回数据以比特序列的形式传输,控制它们的发送,以及
生成用于在通信系统中传输的数据帧。 以下是该功能的主要阶段。
-
服务领域的形成(FLCO,FID);
-
地址处理;
-
编码和校验和;
-
抗噪声编码的数据准备;
-
传输帧的形成;
-
传输顺序控制;
-
完成的数据包或LC块的返回。
# 初始化状态跟踪的全局变量
global E = 0 # 帧计数器
global l_block = 0 # 当前数据块
function Gen_Pkg(block, AFLCO, BFID, Switch, AdrP, AdrI, Ecstro, Shiroko, OVCM, Pr, ELC, CC)
global E, l_block
# 1. FLCO(功能逻辑通道组织)的形成
FLCO = get(Dict(
1 => [0,0,0,0,0,0], # 第一类
2 => [0,0,0,0,1,1], # 第二类
3 => [0,0,0,1,0,0], # 第三类
4 => [0,0,0,1,0,1], # 类型4
5 => [0,0,0,1,1,0], # 类型5
6 => [0,0,0,1,1,1], # 第六类
7 => [0,0,1,0,0,0] # 第七类
), AFLCO, zeros(Int,6)) # 默认值为零。
# 2. 根据交换机生成Fid(功能ID)
FID = Switch == 0 ? get(Dict(
1 => [0,0,0,0,0,0,0,0], # 个人资料1
2 => [0,0,0,0,0,1,0,0], # 配置文件2
3 => [0,1,1,1,1,1,1,1] # 个人资料3
), BFID, zeros(Int,8)) : zeros(Int,8) # 如果开关=1-零
# 3. 将地址转换为位向量
AdrP_vec = (AdrP == 0) ? de2bi(1, 24) : de2bi(AdrP, 24) # 收件人地址
AdrI_vec = (AdrI == 0) ? de2bi(1234, 24) : de2bi(AdrI, 24) # 来源地址
# 4. 形成一个完整的LC块(72位)
FullLC = vcat([0, 0], FLCO, FID, [Ecstro], [0, 0, 0], [Shiroko], [OVCM],
de2bi(Pr - 1, 2), AdrP_vec, AdrI_vec)
# 5. 编码和校验和计算
FullLCdec = [bi2de(FullLC[i:i+7]) for i in 1:8:72] # 字节细分
parity = encode(FullLCdec) # 里德-所罗门编码
CRC = lc_header_mask(parity) # 将掩码应用于校验和
LCcrcDec = vcat(FullLCdec[1:9], CRC) # 结合数据和CRC
LCcrc = vcat([reverse(digits(b, base=2, pad=8)) for b in LCcrcDec]...) # 以比特为单位
# 6. 为BPTC准备数据(块产品Turbo码)
R = [0, 0, 0, 0] # 备份位
I = vcat(reverse(R[1:3]), LCcrc) # 信息块的形成
l = reshape(I[1:99], 11, 9)' # Матрица 9x11
CS = CS5bit(LCcrc) # 5位校验和
HR = HemR(l) # 水平锤击检查
HC = HemC(l, HR) # 垂直锤击检查
type20bit = typegen(CC) # 生成20位消息类型
# 7. 帧传输管理
E = E == 0 ? ELC * 12 + 1 : E + 1 # 更新帧计数器
Enabled = mod(E, 2) == 0 ? 1 : 0 # 传输活动标志
LCs = 0 # 立法会标志
LC_next = false # 下一个LC块的标志
# 传输控制逻辑
if E == ELC * 12 + 1
LC_next = true
elseif E == ELC * 12 + 2
Enabled = 0
LCs = 1
elseif E == ELC * 12 + 3
E = 1
Enabled = 0
end
# 8. LC块形成(288位)
LC_block = zeros(Int, 288)
LC_block[121:168] = [1,1,0,1,0,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1] # 同步序列
LC_block[111:120] = type20bit[1:10] # 类型的前10位
LC_block[169:178] = type20bit[11:20] # 类型的最后10位
# 9. BPTC矩阵的形成(13x15)
BPTC = zeros(Int, 13, 15)
BPTC[1:9, 1:11] .= l # 基本数据
BPTC[10:13, 1:15] .= HC # 垂直检查
BPTC[1:9, 12:15] .= HR # 水平检查
BPTCl = vec(permutedims(BPTC)) # 转换为矢量
# 10. 数据交织
LCper = zeros(Int, 195)
for i in 1:195
idx = mod(i * 181, 196)
idx == 0 && (idx = 196)
LCper[idx] = BPTCl[i] # 交织算法
end
# 11. 填写LC块
LC_block[14:110] .= LCper[1:97] # 数据的第一部分
LC_block[179:276] .= LCper[98:195] # 数据的第二部分
LC_block[13] = 0 # 备份位
# 12. 生成快速数据
FullLC = LCcrc[1:72]
CC = reverse([1, 0, 0, 0]) # 更正代码
QR = [1, 0, 0, 0, 1, 0, 1, 1, 1] # 快速数据
Pi = 0 # PI旗
QR_rev = reverse(QR[1:8]) # 反向快速数据
# 13. 快速数据的BPTC生成(8x16)
BPTC = zeros(Int, 8, 16)
BPTC[1:2, 1:11] = reshape(reverse(FullLC[51:72]), 2, 11) # 部分数据
BPTC[3:7, 1:10] = reshape(reverse(FullLC[1:50]), 5, 10) # 基本数据
BPTC[1:7, 12:16] = [1 0 1 1 1; 0 0 0 0 0; 0 1 0 0 0; 0 0 0 0 0; 1 1 1 1 0; 0 0 0 0 0; 0 0 0 0 1] # 控制位
BPTC[8, :] = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1] # 交替位
BPTC[3:7, 11] = CS # 校验和
# 14. 从输入数据形成位块
bit_block = zeros(Int, 288)
for i in 1:27
bits = reverse(digits(block[i], base=2, pad=8)) # 字节到位
if i < 14
bit_block[(1:8) .+ ((i-1)*8) .+ 12] = bits # 前13个字节
elseif i == 14
bit_block[117:120] = bits[1:4] # 第14字节的特例
bit_block[169:172] = bits[5:8]
else
bit_block[(1:8) .+ ((i-1)*8) .+ 12 .+ 48] = bits # 其余字节
end
end
# 15. 数据帧的形成
DataFrames = [
# 同步框架
vcat(zeros(120), [0,1,1,1,0,1,0,1,1,0,0,1,0,0,0,1,1,0,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,0,0,1,0,1,0,0,1,1,1,1,1,0,0], zeros(120)),
# 数据帧1-4
vcat(zeros(120), CC, [Pi], [0,1], [QR[9]], vec(BPTC[:, 1:4]), QR_rev, zeros(120)),
vcat(zeros(120), CC, [Pi], [1,1], [QR[9]], vec(BPTC[:, 5:8]), QR_rev, zeros(120)),
vcat(zeros(120), CC, [Pi], [1,1], [QR[9]], vec(BPTC[:, 9:12]), QR_rev, zeros(120)),
vcat(zeros(120), CC, [Pi], [1,0], [QR[9]], vec(BPTC[:, 13:16]), QR_rev, zeros(120))
]
# 16. 最终包装的形成
package = zeros(Int, 288)
if Enabled == 1
l_block += 1
package = copy(DataFrames[l_block])
package[13:120] .= bit_block[13:120] # 第一部分的数据
package[169:276] .= bit_block[169:276] # 第二部分的数据
if l_block == 5
l_block = 0 # 重置块计数器
end
end
# 返回LC块或数据包和LC_next标志
return LCs == 1 ? copy(LC_block) : package, LC_next
end
接下来,我们将测试所描述的功能。 测试由三个主要部分组成。
-
数据准备:
*正在创建一个2670字节(等于1)的测试数组
*输入数组以零字节添加到27的倍数(块大小)
-
分区和处理:
*将数据分成27字节的块。
*为每个块调用函数
Gen_Pkg(),其形成数据帧,由此我们基本上检查我们的函数在仿真条件下的操作。 -
结果分析:
*收集所有生成的位块
*计算每个块中的位的总和(用于验证)
*仅过滤和输出非零量(我们丢弃每第二帧)
bytes = Int.(ones(2670))
remainder = length(bytes) % 27
bytes = vcat(bytes, remainder == 0 ? Int[] : zeros(Int, 27 - remainder))
bit_blocks = Vector{Vector{Int}}()
buffer = Int[]
pending_block = nothing
i = 1
while i <= length(bytes)
block = bytes[i:min(i+26, length(bytes))]
data_bits, LC_next = Gen_Pkg(block, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2, [0, 0, 0, 1])
push!(bit_blocks, data_bits)
i += 27
end
sum_bit = sum.(bit_blocks)
filtered_vector = filter(x -> x != 0, sum_bit)
println(filtered_vector)
现在让我们检查LC数据包是否以正确的频率发生。 基于ELC=2设置,我们将每2个超帧看到它,即每第十三帧应该是104。
indices = ((findall(x -> x == 104, filtered_vector)).-1)/13
现在让我们删除LC并检查其余帧是否交替。
# 删除所有104(LC)
filtered_vector = filter(x -> x != 104, filtered_vector)
# 所需的组合
pattern = [52, 41, 38, 41, 44] # A, B, C, D, E
function check_pattern(vec, pat)
# 将矢量裁剪到最近的合适长度
pattern_length = length(pat)
suitable_length = div(length(vec), pattern_length) * pattern_length
trimmed_vec = vec[1:suitable_length]
# 检查模式匹配
for i in 1:pattern_length:length(trimmed_vec)
if trimmed_vec[i:i+pattern_length-1] != pat
return false
end
end
return true
end
# 检查filtered_vector
is_correct = check_pattern(filtered_vector, pattern)
println("$模式组合是否重复? ", is_correct ? "是的" : "非也。")
正如我们所看到的,该功能正常工作。 现在让我们测试模型。
让我们运行为函数创建绑定的模型。
# 启用辅助模型启动功能。
function run_model( name_model)
Path = (@__DIR__) * "/" * name_model * ".engee"
if name_model in [m.name for m in engee.get_all_models()] # 检查将模型加载到内核的条件
model = engee.open( name_model ) # 打开模型
model_output = engee.run( model, verbose=true ); # 启动模型
else
model = engee.load( Path, force=true ) # 上传模型
model_output = engee.run( model, verbose=true ); # 启动模型
engee.close( name_model, force=true ); # 关闭模型
end
sleep(0.1)
return model_output
end
根据下面的图表,我们看到,首先,LC_next控制信号警告我们下一帧工作正常,其次,我们看到该函数在模型和脚本中的行为相同。 另外,为了方便在Engee函数中定义包参数,我们使用参数声明,从而减少了块的输入端口数。
display(run_model("test_new_function"))
gr()
display(plot(vcat(collect(simout["test_new_function/开关。1"]).value...)))
plot(sum_bit, seriestype = :steppre, linewidth=3, legend = false)
plot!(vcat(collect(simout["test_new_function/元素之和。1"]).value...), seriestype = :steppre, linewidth=3, legend = false)
现在我们已经测试了功能本身,我们可以继续改进我们的DMR系统模型。
从上面的截图中我们可以看到,该块 Gen_Pkg 控制块内的数据输入 DMR Physical Layer 在此示例的先前版本中描述的逻辑位于,并且在块 Frame_Synchronization 有两个函数执行我们的流的帧同步。 让我们来看看这些块,让我们从 Xcorr.
``'茱莉亚
用于存储延迟的全局变量
全局延迟=0
处理数据块的主要功能
函数(c::块)(T::Real,BitSignal)
全球延迟
US=false#成功同步标志
#参考同步序列(48位)
数据同步= [-1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1,
1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1,
-1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1]
#计算输入信号和同步序列之间的互相关
MS=xcorr_simple((2*BitSignal.-1),Data_Sync)
#搜索相关峰(阈值>46)
峰=findall(MS.>46)
如果!isempty(峰值)
P=Peak[1]#取第一个显着峰
#检查峰值是否在可接受的范围内
如果P>120&&P<456
#根据峰值的位置计算延迟
如果P>288
延迟=P-408
其他
延迟=P-120
结束
结束
US=true#设置成功同步标志
结束
D=Delay#当前延迟值
返回我们,D,MS#我们返回同步状态,延迟和
端相关阵列。
现在让我们进入街区 Selector.
# 用于管理处理状态的全局变量:
信号处理活动的全局U=false#标志
全局j=0#时间间隔计数器
# 处理数据块的主要功能
函数(c::块)(T::Real,BitSignal,US,延迟)
全局U,j#访问全局变量
#输出信号的初始化(216位)
Sig=零(216)
#处理权限标志
启用=错误
#同步信号处理(US):
#如果接收到同步信号(我们!=0),重置状态
我们!= 0 ? (U=US;j=0):无
#如果处理处于活动状态(U==1)
如果U==1
j+=1#递增计数器
#我们只处理奇数间隔
如果isodd(j)
#我们通过从BitSignal中选择位来生成输出信号,同时考虑到延迟:
#-块1:位13-120(108位)
#-块2:位169-276(108位)
#总长度:216位
西格=BitSignal[Int。([收集(13:120);收集(169:276)]。+延迟)]
Enable=true#激活权限标志
结束
结束
#12间隔后重置状态
j==12? (U=false;j=0):无
我们回来了:
#-Enable:有效数据的标志
#-Sig:已处理信号(216位)
返回启用,Sig
结束
现在,让我们运行模型并通过计算输出处相对于输入的错误数来检查其正确性。
更有趣的测试在模型的第一个版本中呈现。 如果您愿意,您也可以将它们应用到这个模型中,但我们决定不在这里这样做,以免人为地增加在这个例子中被分析的材料的体积。
display(run_model("DMR_V2"))
println("以字节为单位的错误总数为: ",sum(vcat(collect(simout["DMR_V2/err_symbol"]).value...)))
输出
在这个例子中,分析了DMR协议的相当重要的部分。 下次,如果您对此主题感兴趣,我们将分析MAC层,即对信道帧和控制帧的响应逻辑。
