# Gnuradio BER block
<details>
<summary>BER Block.cc</summary>
```
/* -*- c++ -*- */
/*
* Copyright 2013-2014 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* 如果定義了 HAVE_CONFIG_H,則包含 config.h 配置文件,這是自動化工具(如autotools)生成的配置頭文件 */
#include "ber_bf_impl.h"
#include <gnuradio/io_signature.h>
#include <volk/volk.h>
#include <boost/format.hpp>
#include <cmath>
/* 包含必要的頭文件:
- ber_bf_impl.h: 本模組的聲明
- io_signature.h: GNU Radio用於描述block端口的數據類型和大小
- volk.h: 用於SIMD優化的數學函數
- format.hpp: Boost庫的格式化工具,用於輸出格式化文本
- cmath: 標準數學函數庫 */
namespace gr {
namespace fec {
ber_bf::sptr ber_bf::make(bool test_mode, int berminerrors, float ber_limit)
{
return gnuradio::make_block_sptr<ber_bf_impl>(test_mode, berminerrors, ber_limit);
}
ber_bf_impl::ber_bf_impl(bool test_mode, int berminerrors, float ber_limit)
: block("fec_ber_bf",
io_signature::make(2, 2, sizeof(unsigned char)),
io_signature::make(1, 1, sizeof(float))),
d_total_errors(0),
d_total(0),
d_test_mode(test_mode),
d_berminerrors(berminerrors),
d_ber_limit(ber_limit)
{
}
inline float ber_bf_impl::calculate_log_ber() const
{
return log10(((double)d_total_errors) / (d_total * 8.0));
}
inline void ber_bf_impl::update_counters(const int items,
const unsigned char* inbuffer0,
const unsigned char* inbuffer1)
{
uint32_t ret;
for (int i = 0; i < items; i++) {
volk_32u_popcnt(&ret, static_cast<uint32_t>(inbuffer0[i] ^ inbuffer1[i]));
d_total_errors += ret;
}
d_total += items;
}
/* 更新錯誤數和總數據數
- 逐個比較兩個輸入緩衝區的數據
- 使用 VOLK 函數計算 Hamming 距離(即位錯誤數) */
ber_bf_impl::~ber_bf_impl() {}
int ber_bf_impl::general_work(int noutput_items,
gr_vector_int& ninput_items,
gr_vector_const_void_star& input_items,
gr_vector_void_star& output_items)
{
unsigned char* inbuffer0 = (unsigned char*)input_items[0];
unsigned char* inbuffer1 = (unsigned char*)input_items[1];
float* outbuffer = (float*)output_items[0];
int items = ninput_items[0] <= ninput_items[1] ? ninput_items[0] : ninput_items[1];
/* general_work 函數處理數據塊
- 比較兩個輸入緩衝區,計算BER,並將結果輸出到 outbuffer */
if (d_test_mode) {
// 如果在測試模式下,當錯誤數達到設定的最小值時停止
if (d_total_errors >= d_berminerrors) {
return WORK_DONE; // 完成工作
} else {
if (items > 0) {
update_counters(items, inbuffer0, inbuffer1);
}
consume_each(items);
if (d_total_errors >= d_berminerrors) {
outbuffer[0] = calculate_log_ber();
GR_LOG_INFO(d_logger,
boost::format(" %1% over %2% --> %3%") % d_total_errors %
(d_total * 8) % outbuffer[0]);
return 1;
}
// 在高信噪比模擬中檢查總錯誤數以防止過早停止
else if (calculate_log_ber() < d_ber_limit && d_total_errors > 0) {
GR_LOG_INFO(d_logger, " Min. BER limit reached");
outbuffer[0] = d_ber_limit;
d_total_errors = d_berminerrors + 1;
return 1;
} else {
return 0;
}
}
} else { // 流模式下,持續處理
if (items > 0) {
update_counters(items, inbuffer0, inbuffer1);
outbuffer[0] = calculate_log_ber();
consume_each(items);
return 1;
} else {
return 0;
}
}
}
} /* namespace fec */
} /* namespace gr */
```
</details>
<details>
<summary>BER Block.h</summary>
```
/* -*- c++ -*- */
/*
* Copyright 2013-2014 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef INCLUDED_FEC_BER_BF_IMPL_H
#define INCLUDED_FEC_BER_BF_IMPL_H
#include <gnuradio/fec/ber_bf.h>
namespace gr {
namespace fec {
class FEC_API ber_bf_impl : public ber_bf
{
private:
long d_total_errors;
long d_total;
bool d_test_mode;
int d_berminerrors;
float d_ber_limit;
inline float calculate_log_ber() const;
inline void update_counters(const int items,
const unsigned char* inbuffer0,
const unsigned char* inbuffer1);
public:
ber_bf_impl(bool d_test_mode = false, int berminerrors = 100, float ber_limit = -7.0);
~ber_bf_impl() override;
int general_work(int noutput_items,
gr_vector_int& ninput_items,
gr_vector_const_void_star& input_items,
gr_vector_void_star& output_items) override;
long total_errors() override { return d_total_errors; };
};
} /* namespace fec */
} /* namespace gr */
#endif /* INCLUDED_FEC_BER_BF_IMPL_H */
```
</details>
### update_counters
1. inbuffer0[i] ^ inbuffer1[i]: 對兩個緩衝區中的相同位置的數據進行 XOR 運算。XOR 運算的結果是如果相應的位相同,則結果為0;如果不同,則結果為1。
2. static_cast<uint32_t>(): 將 XOR 的結果轉型為 uint32_t 型。
3. volk_32u_popcnt(): 計算轉型後的 uint32_t 數值中1的個數,也就是計算兩個字節間的漢明距離(Hamming distance)。漢明距離即不同位的數量,這裡也就是位錯誤數。
```
inline void ber_bf_impl::update_counters(const int items,
const unsigned char* inbuffer0,
const unsigned char* inbuffer1)
{
uint32_t ret;
for (int i = 0; i < items; i++) {
volk_32u_popcnt(&ret, static_cast<uint32_t>(inbuffer0[i] ^ inbuffer1[i]));
d_total_errors += ret;
}
d_total += items;
}
```
### items
ninput_items[0] 和 ninput_items[1] 分別是兩個輸入緩衝區中可用的數據項的數量。這段程式碼使用了 C++ 的條件運算符(也稱為三元運算符)? :,其格式為 [條件 ? 表達式1 : 表達式2:]
如果條件為真(在這裡是檢查 ninput_items[0] 是否小於或等於 ninput_items[1]),則表達式的結果是 表達式1(ninput_items[0])。
如果條件為假,則結果是 表達式2(ninput_items[1])。
因此,items 變量被賦值為兩個輸入中可用數據項最小的那個數量。這樣做可以保證在接下來的處理中,不會讀取任一輸入緩衝區中不存在的數據,從而避免越界錯誤。
```
int items = ninput_items[0] <= ninput_items[1] ? ninput_items[0] : ninput_items[1];
```
### calculate_log_ber
(d_total * 8.0):這部分代表總比特數。d_total 變量應該是指處理過的數據項的總數(例如字節數)。因為每個數據項(假設為字節)包含 8 個比特,所以這裡乘以 8 將字節轉換成比特。
((double)d_total_errors) / (d_total * 8.0):這個表達式計算的是位錯誤率(BER),即總錯誤比特數除以總比特數。d_total_errors 是錯誤比特的計數,這裡被顯式轉換成 double 類型,以確保進行浮點除法,從而得到準確的 BER 值。
```
inline float ber_bf_impl::calculate_log_ber() const
{
return log10(((double)d_total_errors) / (d_total * 8.0));
}
```