GNU Radio自定义sync块运行报s<d_bufsize断言失败求解
问题背景
使用gr_modtool开发具备Kurtosis(峰度)计算能力的sync块,此前基于sink块实现过同类功能,本次改用sync块重构实现。代码可正常编译,但在GRC中运行流图时触发运行时错误,错误出现在返回已消耗数据项数量的环节。
报错日志
WARNING:gnuradio.grc.core.platform.block_loader:Block with id "Kurtosis_Kurtosis_c" loaded from /usr/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_c.block.yml overwritten by /usr/local/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_c.block.yml WARNING:gnuradio.grc.core.platform.block_loader:Block with id "Kurtosis_Kurtosis_p" loaded from /usr/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_p.block.yml overwritten by /usr/local/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_p.block.yml WARNING:gnuradio.grc.core.platform.block_loader:Block with id "Kurtosis_Kurtosis_c" loaded from /usr/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_c.block.yml overwritten by /usr/local/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_c.block.yml WARNING:gnuradio.grc.core.platform.block_loader:Block with id "Kurtosis_Kurtosis_p" loaded from /usr/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_p.block.yml overwritten by /usr/local/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_p.block.yml WARNING:gnuradio.grc.core.platform.block_loader:Block with id "Kurtosis_Kurtosis_c" loaded from /usr/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_c.block.yml overwritten by /usr/local/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_c.block.yml WARNING:gnuradio.grc.core.platform.block_loader:Block with id "Kurtosis_Kurtosis_p" loaded from /usr/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_p.block.yml overwritten by /usr/local/share/gnuradio/grc/blocks/Kurtosis_Kurtosis_p.block.yml Generating: '/home/nomo/j.py' Executing: /usr/bin/python3 -u /home/nomo/j.py 0 7 python3: ../gnuradio-runtime/lib/../include/gnuradio/buffer.h:193: unsigned int gr::buffer::index_add(unsigned int, unsigned int): Assertion `s < d_bufsize' failed.
相关代码
实现源文件
#ifdef HAVE_CONFIG_H #include "config.h" #endif #include <gnuradio/io_signature.h> #include "Kurtosis_only_c_impl.h" namespace gr { namespace Kurtosis_only { using input_type = gr_complex; using output_type = gr_complex; Kurtosis_only_c::sptr Kurtosis_only_c::make(int large_loop, int small_loop) { return gnuradio::get_initial_sptr (new Kurtosis_only_c_impl(large_loop, small_loop)); } /* * The private constructor */ Kurtosis_only_c_impl::Kurtosis_only_c_impl(int large_loop, int small_loop) : gr::sync_block("Kurtosis_only_c", gr::io_signature::make(1, 1, sizeof(input_type) * small_loop), gr::io_signature::make(2, 2, sizeof(output_type) * small_loop)), d_LL(large_loop), d_SL(small_loop) { } /* * Our virtual destructor. */ Kurtosis_only_c_impl::~Kurtosis_only_c_impl() { } void Kurtosis_only_c_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) { for (unsigned int i = 0; i < ninput_items_required.size(); ++i) ninput_items_required[i] = d_SL; } int Kurtosis_only_c_impl::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { /*printf("sizeof(gr_compelx) = %ld", sizeof(gr_complex)); printf("sizeof(gr_complex) * small_loop = %ld", sizeof(gr_complex) * d_SL); printf("in0 = %d, out0 = %d", input_items, output_items); printf("d_N = %d", d_SL);*/ auto in0 = static_cast<const input_type*>(input_items[0]); auto out0 = static_cast<output_type*>(output_items[0]); auto out1 = static_cast<output_type*>(output_items[1]); gr_complex S1r[d_SL], S1i[d_SL], S2r[d_SL], S2i[d_SL], S3r[d_SL], S3i[d_SL], S4r[d_SL], S4i[d_SL]; gr_complex dcnt[d_SL]; gr_complex Myu1r[d_SL], Myu1i[d_SL], Myu2r[d_SL], Myu2i[d_SL], Myu3r[d_SL], Myu3i[d_SL], Myu4r[d_SL], Myu4i[d_SL]; gr_complex M1r[d_SL], M1i[d_SL], M2r[d_SL], M2i[d_SL], M3r[d_SL], M3i[d_SL], M4r[d_SL], M4i[d_SL]; gr_complex Kr[d_SL], Ki[d_SL]; /*for (int k = 0; k < noutput_items; k += d_SL) { unsigned int datasize = noutput_items - k; unsigned int resid = d_SL - d_index;*/ for(unsigned int j = 0; j < d_LL; ++j) { for(unsigned int i = 0; i < d_SL; ++i) { pwr = in0[i].real(); pwi = in0[i].imag(); S1r[i] += pwr; S1i[i] += pwi; S2r[i] += pwr * pwr; S2i[i] += pwi * pwi; S3r[i] += pwr * pwr * pwr; S3i[i] += pwi * pwi * pwi; S4r[i] += pwr * pwr * pwr * pwr; S4i[i] += pwi * pwi * pwi * pwi; dcnt[i] += 1; } } for(unsigned int i = 0; i < d_SL; ++i) { Myu1r[i] = S1r[i] / dcnt[i]; Myu1i[i] = S1i[i] / dcnt[i]; Myu2r[i] = S2r[i] / dcnt[i]; Myu2i[i] = S2i[i] / dcnt[i]; Myu3r[i] = S3r[i] / dcnt[i]; Myu3i[i] = S3i[i] / dcnt[i]; Myu4r[i] = S4r[i] / dcnt[i]; Myu4i[i] = S4i[i] / dcnt[i]; } gr_complex a = 4, b = 6, c = 3, d = 2; for(unsigned int i = 0; i < d_SL; ++i) { M1r[i] = a * Myu3r[i] * Myu1r[i]; M1i[i] = a * Myu3i[i] * Myu1i[i]; M2r[i] = b * Myu2r[i] * pow(Myu1r[i], d); M2i[i] = b * Myu2i[i] * pow(Myu1i[i], d); M3r[i] = c * pow(Myu1r[i], a); M3i[i] = c * pow(Myu1i[i], a); M4r[i] = Myu2r[i] - pow(Myu1r[i], d); M4i[i] = Myu2i[i] - pow(Myu1i[i], d); } for(unsigned int i = 0; i < d_SL; ++i) { Kr[i] = (Myu4r[i] - M1r[i] + M2r[i] - M3r[i]) / pow(M4r[i], d); Ki[i] = (Myu4i[i] - M1i[i] + M2i[i] - M3i[i]) / pow(M4i[i], d); } for(unsigned int i = 0; i < d_SL; ++i) { out0[i] = Kr[i]; out1[i] = Ki[i]; } return 0; } } /* namespace Kurtosis_only */ } /* namespace gr */
头文件
#ifndef INCLUDED_KURTOSIS_ONLY_KURTOSIS_ONLY_C_IMPL_H #define INCLUDED_KURTOSIS_ONLY_KURTOSIS_ONLY_C_IMPL_H #include <Kurtosis_only/Kurtosis_only_c.h> namespace gr { namespace Kurtosis_only { class Kurtosis_only_c_impl : public Kurtosis_only_c { private: gr_complex pwr; gr_complex pwi; int d_LL; int d_SL; int d_index = 0; public: Kurtosis_only_c_impl(int large_loop, int small_loop); ~Kurtosis_only_c_impl(); void forecast(int nouput_items, gr_vector_int &ninput_items_required) override; // Where all the action really happens int work( int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items ); }; } // namespace Kurtosis_only } // namespace gr #endif /* INCLUDED_KURTOSIS_ONLY_KURTOSIS_ONLY_C_IMPL_H */
GRC流图

问题根因与修复方案
触发gr::buffer::index_add断言的核心原因有两个,其余是附带的逻辑错误和警告:
- work函数返回值错误:sync块的work函数约定是返回实际消耗的输入数据项个数,代码中直接
return 0,等于告诉调度器本次调用一个输入项都没处理,调度器不会移动输入缓冲区读指针,会反复把同一段数据喂给块,最终读指针偏移超过缓冲区大小触发断言。 - io_signature配置与forecast逻辑不匹配:构造函数中把单个输入输出项的大小设置为
sizeof(gr_complex)*small_loop,也就是把small_loop个复数打包成1个数据项,但forecast中要求的输入项数是d_SL,相当于实际需要d_SL * d_SL个复数采样,和预期的处理粒度完全不符,进一步加剧缓冲区越界问题。
其余代码问题:
- 栈上数组未初始化:work函数中定义的S1r、S1i、dcnt等所有局部数组没有赋初值,直接执行
+=运算会读取栈上随机值,计算结果完全错误。 - 累加逻辑失效:用于跨多帧累加的S1-S4、dcnt数组全部定义在work函数栈上,每次work调用都会重新创建,根本实现不了
large_loop次的累加逻辑。 - 块重复加载警告:系统中
/usr/share/gnuradio/grc/blocks/和/usr/local/share/gnuradio/grc/blocks/两个路径下存在同名的Kurtosis块yml文件,前者是旧版本安装残留,加载时会被后者覆盖。
修复步骤:
- 修正io_signature配置,输入输出项大小统一设为单个复数大小:
Kurtosis_only_c_impl::Kurtosis_only_c_impl(int large_loop, int small_loop) : gr::sync_block("Kurtosis_only_c", gr::io_signature::make(1, 1, sizeof(input_type)), gr::io_signature::make(2, 2, sizeof(output_type))), d_LL(large_loop), d_SL(small_loop) { // 累加数组和计数器移到成员变量后,在这里做初始化 d_acc_cnt = 0; d_S1r.assign(d_SL, 0.0f); d_S1i.assign(d_SL, 0.0f); // S2r/S2i/S3r/S3i/S4r/S4i/dcnt数组按同样方式初始化 }
- 移除自定义的forecast函数,sync_block默认实现已经保证输入输出项数1:1匹配。如果需要攒够
d_LL*d_SL个采样再计算,在work函数开头判断当前输入的noutput_items是否够一轮计算的长度,不够直接返回0,够了再执行计算。 - 修正work函数返回值:一轮计算实际消耗了多少个输入采样,就返回对应的数字,比如一次处理
process_len = d_LL个采样就返回process_len,禁止无意义返回0。 - 把所有需要跨work调用保留值的累加数组、计数器全部定义为类的成员变量,不要放在work函数栈上。每完成一轮峰度计算、把结果写到输出缓冲区后,把所有累加变量清零,准备下一轮计算。
- 删除
/usr/share/gnuradio/grc/blocks/路径下旧的Kurtosis_*.block.yml文件,消除块重复加载的警告。
内容的提问来源于stack exchange,提问作者yu_20
相关产品推荐
相关产品推荐

