SignalProcessingブロックの作成:C++


※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

Pythonとの切り分けは?

From the Python point of view, GNU Radio provides a data flow abstraction.
パイソンはデータフロウの概要を提供する。
From the high level point-of-view, infinite streams of data flow through the ports. At the C++ level, streams are dealt with in convenient sized pieces, represented as contiguous arrays of the underlying type.
C++ブロックは潜在に潜む配列によって切り分けられたストリームが扱われる。

3つのファイルが必要

  • xxx.h, xxx.cc #新しいクラスを定義するのに必要。
  • xxx.i, SwigにどうやってクラスをPythonにインポートさせるかを教えるのに必要。

型の指定

入力出力という順で以下のpreffix定義の元クラス名に慣例的に使われる。
  • f - single precision floating point
  • c - complex<float>
  • s - short (16-bit integer)
  • i - integer (32-bit integer)

データの流れはgr_vector_XXX(配列)が基本!

gr.vector_source_f :source of float's that gets its data from a vector
floatのソースをベクトルから取得する。
gr.vector_source_b :source of unsined char's that gets its data from a vector
unsined char'sのベクトルをソースから取得する。
14      def test_001_square_ff (self):
15          src_data = (-3, 4, -5.5, 2, 3)
16          expected_result = (9, 16, 30.25, 4, 9)
17          src = gr.vector_source_f (src_data)
18          sqr = howto.square_ff ()
19          dst = gr.vector_sink_f ()
20          self.fg.connect (src, sqr)
21          self.fg.connect (sqr, dst)
22          self.fg.run ()
23          result_data = dst.data ()
24          self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6)

79  int howto_square_ff::general_work (int noutput_items,
80                                 gr_vector_int &ninput_items,
81                                 gr_vector_const_void_star &input_items, //input_items(gr_vector_const_void_star)はポインタのポインタ
82                                 gr_vector_void_star &output_items)
83  {
84    const float *in = (const float *) input_items[0]; //gr_vector_const_void_starを(const float *)にキャスト
85    float *out = (float *) output_items[0]; //gr_vector_void_starを(float *)にキャスト
86  
87    for (int i = 0; i < noutput_items; i++){ //noutput_itemsの数だけ配列に格納
88      out[i] = in[i] * in[i];
89    }
90  
91    // Tell runtime system how many input items we consumed on
92    // each input stream.
93  
94    consume_each (noutput_items);
95  
96    // Tell runtime system how many output items we produced.
97    return noutput_items;
98  }
gr_vector_XXXは配列のポインタのポインタ?

ブロックが止まるのはEOF

gr.vector_source_f(src_data) will source the elements of src_data and then say that it's finished.
The returned value of general_work() is the number of items actually written to each output stream, or -1 on EOF.

How to make a signal processing blockこっちはより詳しく


  • noutput_items is the number of output items to write on each output stream.
  • ninput_items gives the number of input items available on each input stream.
  • A block may have x input streams and y output streams.
  • ninput_items is an integer `vector' of length x, the ith element of which gives the number of available items on the ith input stream.
  • input_items is a vector of pointers to the input items, one entry per input stream.
  • output_items is a vector of pointers to the output items, one entry per output stream.

  • gr_make_XXXは、そのクラスをインスタンス化して、そのスマートポインタ(の中のshared pointer)を返してくれえる。これはrawポインタを間違っていじってしまうのを防ぐためらしい。

gr_block.ccを見れば何やってんのか分かる!?

// stub implementation:  1:1
void
gr_block::forecast (int noutput_items, gr_vector_int &ninput_items_required)
{
  unsigned ninputs = ninput_items_required.size ();
  for (unsigned i = 0; i < ninputs; i++) //入力ベクトルの数(ストリームの数)だけループ
    ninput_items_required[i] = noutput_items + history() - 1; //出力ストリームのエレメント数をそのまま入力ストリームの数にしている。
}
入出力の間おいて、各ストリームのエレメント数が違う場合に、forcastでそれを予測するようにしなくてはだめ。
上のサンプルはデフォルトで1:1の場合はこれでよい。

void consume_each (int how_many_items);
consume_eachはhow_many_itemsの数だけ各入力ストリームのアイテムがいくつ消費されかをスケジューラに教える。
それに応じて、スケジューラはアップストリームのバッファや関連ポインタをアレンジする。

gr_io_signature

class gr_io_signature {
  public:
    ~gr_io_signature ();
    int min_streams () const { return d_min_streams; }
    int max_streams () const { return d_max_streams; }
    size_t sizeof_stream_item (int index) const { return d_sizeof_stream_item; }
  private:
    int d_min_streams;
    int d_max_streams;
    size_t d_sizeof_stream_item;
    gr_io_signature (int min_streams, int max_streams, size_t sizeof_stream_item);
    friend gr_io_signature_sptr gr_make_io_signature ( int min_streams,  //ここではストリームの数の最大最小を記述(入力端子の数!?)
                                                       int max_streams,
                                                       size_t sizeof_stream_item);
};
入力端子が二つあるブロックを作りたい場合はmax_streamsを2にすればいいのか?

すべこべ言わずにコンパイルしよう

howto_print_ccの作成

複素信号を出力する。
以下、gr_complex.h及びgr_type.hに宣言されている、Gnuradioの型
//gr_complex.h
#include <complex>
typedef std::complex<float>                     gr_complex;
typedef std::complex<double>                    gr_complexd;

//gr_type.h
typedef std::vector<int>                        gr_vector_int;
typedef std::vector<float>                      gr_vector_float;
typedef std::vector<double>                     gr_vector_double;
typedef std::vector<void *>                     gr_vector_void_star;
typedef std::vector<const void *>               gr_vector_const_void_star;
以下、howto_print_cc.ccの一部
int 
howto_print_cc::general_work (int noutput_items,
		       gr_vector_int &ninput_items,
		       gr_vector_const_void_star &input_items,
		       gr_vector_void_star &output_items)
{
  const float complex *in = (const float complex *) input_items[0];
  float complex *out = (float complex *) output_items[0];
  
  printf("noutput_items = %d\n", noutput_items);
  for (int i = 0; i < noutput_items; i++){
    out[i] = in[i];
    printf("in[%d]=%g+%gj, out[%d]=%g+%gj\n", i, crealf(in[i]), cimagf(in[i]), i, crealf(out[i]), cimagf(out[i]));
  }
  // Tell runtime system how many input items we consumed on
  // each input stream. 
  consume_each (noutput_items);
  // Tell runtime system how many output items we produced.
  return noutput_items;
}

同期型ブロックと非同期型のブロック

gr_blockをそのまま継承したブロックはforcastやconsumeメンバを使って入出力を異なるデータレートで器用に扱うことができる。
でも、大体の入力と出力の関係は固定的なものが要求されるよね?だから入出力さえ事前に分かっていれば、簡便化ができる。それをしてくれたのが同期ブロック。
例えば、間引きも補間も行わない(入出力の数が同じ)FIRフィルタは、それぞれの出力サンプルを作るためにそれぞれNタップ分の過去の入力が必要だよね?
でも実際の入力数と出力数の比は1:1だから、Gnuradoiではこの概念のことを履歴(history)とか予知(look-ahead)って呼ぶんだ。
  • gr_sync_block
これはそのhistoryを使える入出力比が1:1のブロックで、ninput_itemsやそれを入力にするeach_consume()が省略されているworkメソッドを呼ぶんだ。
historyを使いたいときはコンストラクタでset_historyメソッドを呼ぶだけさ!さらにここで実装されているforcastはhistoryに関する要求を操作できるようにパワーアップしているのさ。
  • gr_sync_decimator
これはN:1のgr_sync_block版だよ。
  • gr_sync_interpolator
これも1:Nのgr_sync_block版だよ。