SDR

SDR

友人に勧められて、 別府 伸耕 さんのSDR解説を見てたら、面白い亊を言ってた。

設計とは「未来予測」である ~ 高校数学からはじめるソフトウェア無線 超入門

そういう考えもあるんだな。そんな亊では、いかんぜよ。ぼーと見ているんじゃないぞ。 11'10のあたりに未来の位置を計算する図が出て来ている。これって 前回やった、ローレンツの式そのものじゃん。

 dx
---- = a(y -x)
 dt

こういう微分方程式がある(面倒なので、dx/dt のみ例示)

dx = A * (y - x);
x += dx * D

それに対して、実際の数値計算式。Dは、微小な時間間隔ね。x = x + dx * D だから、動画で説明してるのと全く一緒。確かに未来予測だな。

そして、いよいよ本丸、 第6回 「SDR自作キット "Pico Stack SDR"」 の紹介 ~ 高校数学からはじめるソフトウェア無線 超入門

それから、第5回でSDRに最適な石の紹介もあった。 AD9364 が、アナログ・デバイスからの提案みたいだ。入門用らしいけど、一体幾らするんだ? この石を搭載したボードは、16万円ですって。まあ、トランシーバーの心臓部にあたるから、それぐらいはするか。

3Dplotter

前回のplotter.rbドライバーで、少々疑問が有ったので調べてる。gnuplotが終了しても描画したWindowが残ってる。どういう仕組み?

鍵は、gnuplotを起動する時のオプションだな。

`-persist`  plot windows survive after main gnuplot program exits

出力先はターミナルを指定して決定。

gnuplot> help terminal
    canvas            cgm               context           domterm
    dumb              dxf               emf               epslatex
    fig               gif               hpgl              jpeg
    latex             mf                mp                pcl5
    pict2e            png               pop               postscript
    pslatex           pstex             pstricks          push
    sixelgd           svg               tek40xx           tek410x
    texdraw           tkcanvas          vttek             x11
    xlib              xterm

色々なターミナル(表示エリア)が用意されてる。canvasなんかはjavascriptでコントロール出来るhtml画面だ。勿論X11向けも用意されてる。gnuplotがX11に描画を依頼。普通はgnuplotの終了と共に使っていたX11のWindowを破棄するんだけど、それを防止するのが、-prersistオプションなんだな。もっと壮大な亊をやってるかと思ったら、以外な組み合わせで実現してた。

canvas使って、ブラウザーで閲覧。

set term canvas
set output "hoge.html"
plot sin(x)

ちょいと確かめるには、gnuplot提供のjavascriptがハードリンクされてるので、注意する亊。 今、term/canvas.trmを覗いたら

/* use hardcoded _absolute_ path */
CANVAS_scriptdir = strdup(GNUPLOT_JS_DIR);
 :
if (canvas_dashed)
    fprintf(gpoutfile,
        "<script src=\"%sgnuplot_dashedlines.js\"></script>\n"
        , CANVAS_scriptdir);

こんな風になってたから、(多分)環境変数で指定出来るんだろうね。 OSSをいい亊に、いきなりソースに手を出すより、先にマニュアル嫁。

set terminal canvas {size <xsize>, <ysize>} {background <rgb_color>}
                    {font {<fontname>}{,<fontsize>}} | {fsize <fontsize>}
                    {{no}enhanced} {linewidth <lw>}
                    {rounded | butt | square}
                    {dashlength <dl>}
                    {standalone {mousing} | name '<funcname>'}
                    {jsdir 'URL/for/javascripts'}
                    {title '<some string>'}

Gnuplot 5.2 demos (HTML canvas terminal)

行列の出来る野菜売場で、行列の中の人とダベリング。山菜の王様な何かとかね。こしあぶら最高、いや、たらの芽でしょう、たけのこかもとか、よもやま話は楽しい。 で、その方が、空を見上げて、さっきから、あの鳥、全く羽撃きもしないで、悠々と空を飛んでますねって教えてくれた。眼がよい方だ。オイラーには点のようにしか見えないぞ。鳥は夜眼はだけだけど、日中なら素晴しい視力してる。空から俯瞰して獲物を物色。軍用のドローンみたいだ。

オイラーも鳥になって、気流に乘ってみるか。前回のプロッターを3D化して、気流に身をゆだねてみる。ひょっとしたら、渡り鳥かも。

$xpen = 0; $ypen = 0;  $zpen = 0;

def gr3_on
  $gplot = IO.popen("gnuplot -persist", "w")
  $gplot.puts 'splot "-" w l'
end

def gr_off
  $gplot.puts "end"
  $gplot.close
end

def gr_wline3(x1, y1, z1,  x2, y2, z2)
  $gplot.puts "#{x1} #{y1} #{z1}\n#{x2} #{y2} #{z2}\n\n"
end

def move3(x, y, z)
    $xpen = x;  $ypen = y; $zpen = z
end

def draw3(x, y, z)
    gr_wline3($xpen, $ypen, $zpen,  x, y, z)
    $xpen = x;  $ypen = y; $zpen = z
end

-persistを省き、$gplot.closeをコメントにして使うと、描画が終了してもgnuplotが生きたままになる。マウスで、3Dグラフをグリグリ出来て、不思議な軌跡を色々な角度から眺められるぞ。

mruby 1.0.0

前回raccを調べた。raccをデフォで実行すると、hoge.tab.rbみたいなのが出来る。 現代版のmrubyでは、コンパイラーの所が、こうなってる。

sakae@deb:~/mruby/mrbgems/mruby-compiler/core$ wc parse.y y.tab.c lex.def
  7824  20420 201646 parse.y
 14055  60889 531920 y.tab.c
   207   1186   9818 lex.def

ファイル名の表現からすると、既にbisonした結果が掲載されてるみたい。

そこで、タイムマシンで、1.0.0の時代に戻ってみた。buildの環境が違うね。たとえば、 mruby_core.rake が、srcの下にあったりする。

# Parser
file "#{current_build_dir}/y.tab.c" => ["#{current_dir}/parse.y"] do |t|
  yacc.run t.name, t.prerequisites.first
end

y.tab.c は、parse.yを使って作れってなってる。確かに、y.tab.cは存在していない。

sakae@deb:/tmp/mruby/src$ wc parse.y lex.def
  6199  15533 123794 parse.y
   212   1204   8947 lex.def
sakae@deb:/tmp/mruby/src$ yacc parse.y
parse.y:983.1-12: warning: POSIX Yacc does not support %pure-parser [-Wyacc]
  983 | %pure-parser
      | ^~~~~~~~~~~~
parse.y:983.1-12: warning: deprecated directive: ‘%pure-parser’, use ‘%define api.pure’ [-Wdeprecated]
  983 | %pure-parser
      | ^~~~~~~~~~~~
      | %define api.pure
parse.y: warning: fix-its can be applied.  Rerun with option '--update'. [-Wother]
sakae@deb:/tmp/mruby/src$ wc y.tab.c
 12036  55251 439716 y.tab.c

yaccの同等品でも実行してみる。

sakae@deb:/tmp/mruby/src$ bison parse.y
parse.y:983.1-12: warning: deprecated directive: ‘%pure-parser’, use ‘%define api.pure’ [-Wdeprecated]
  983 | %pure-parser
      | ^~~~~~~~~~~~
      | %define api.pure
parse.y: warning: fix-its can be applied.  Rerun with option '--update'. [-Wother]
sakae@deb:/tmp/mruby/src$ wc parse.tab.c
 11906  54860 438341 parse.tab.c

最新版では、yacc若しくはbisonを使わなくてもいいように、あらかじめ変換したものを提供しているんだな。

引数の数

関数にしろ、メソッドにしろ、括弧を省いて、英語っぽく記述出来るようになってるのは、matzさんの非常に強いこだわりだと思う。

括弧が明示的にあれば、ああ、引数はその中に入っているんだなと、素人でも想像がつく。 引数が2つ必要な関数が有ったとしよう。カッコが来て、第一引数、カンマが有って、次に第二引数、最後は綴じカッコ。それに反すれば、引数の数エラーだ。

じゃ、括弧を省略出来るこだわりの場合は? 引数の解析をどこで始めて、どこで終了していいかのタイミングを計れなくなる。

どんなふるまいになるか実験。まずは、括弧が有る場合。

> def add(x,y) x + y end
 => :add
> add()
wrong number of arguments (given 0, expected 2) (ArgumentError)
> add(3)
wrong number of arguments (given 1, expected 2) (ArgumentError)
> add(3,4)
 => 7
> add(3,4,5)
wrong number of arguments (given 3, expected 2) (ArgumentError)

次は、括弧が無い場合。

> add
wrong number of arguments (given 0, expected 2) (ArgumentError)
> add 1
wrong number of arguments (given 1, expected 2) (ArgumentError)
> add 1,2
 => 3
> add 1,2,3
wrong number of arguments (given 3, expected 2) (ArgumentError)

いずれも、関数が必要とする引数の数を記憶してる。この引数の数の亊を、アリティーなんて風に呼んでいる。

どこで、このエラーを出しているんだろう? 家捜ししてみる。

sakae@deb:/tmp/mruby$ grep 'wrong number of arguments' src/*
src/error.c:#define FMT(exp) "wrong number of arguments (given %i, expected " exp ")"
src/vm.c:  str = mrb_format(mrb, "wrong number of arguments (given %i, expected %i)", argc, num);

on gdb

def add(x,y) x + y end
add(3)

こんなBUG入り危険を走らせる。

vbox$ bin/mruby z.rb
trace (most recent call last):
        [1] z.rb:2
z.rb:1:in add: wrong number of arguments (given 1, expected 2) (ArgumentError)

勿論、エラーした。トレースも出てくるのね。

vbox$ gdb -q bin/mruby
Reading symbols from bin/mruby...
(gdb) b mrb_format
Breakpoint 1 at 0x67f19: file src/error.c, line 415.
(gdb) r z.rb
Starting program: /home/sakae/mruby/bin/mruby z.rb

Breakpoint 1, mrb_format (mrb=0x46db9000,
    format=0x1b0dadcb "wrong number of arguments (given %i, expected %i)")
    at src/error.c:415
415       va_start(ap, format);

肝心なのは、ここに至る経路だ。

(gdb) bt
#0  mrb_format (mrb=0x46db9000,
    format=0x1b0dadcb "wrong number of arguments (given %i, expected %i)")
    at src/error.c:415
#1  0x1b186282 in argnum_error (mrb=0x46db9000, num=2) at src/vm.c:1007
#2  0x1b178034 in mrb_vm_exec (mrb=0x46db9000, proc=0x5812af30,
    pc=0x7d0a22a4 "\001\004\001\001\005\002<\004\070\004") at src/vm.c:1952
#3  0x1b173248 in mrb_vm_run (mrb=0x46db9000, proc=0x5812af48, self=...,
    stack_keep=0) at src/vm.c:1130
#4  0x1b171e43 in mrb_top_run (mrb=0x46db9000, proc=0x5812af48, self=...,
    stack_keep=0) at src/vm.c:3056
#5  0x1b194526 in mrb_load_exec (mrb=0x46db9000, p=0x5dbbd010, c=0x7d0a3480)
    at mrbgems/mruby-compiler/core/parse.y:6889
#6  0x1b194994 in mrb_load_detect_file_cxt (mrb=0x46db9000,
    fp=0x2d6eafd0 <usual>, c=0x7d0a3480)
    at mrbgems/mruby-compiler/core/parse.y:6932
#7  0x1b1203ee in main (argc=2, argv=0xcf7e6924)
    at mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:347

vm.cにどうやって突入するか? ソースをザット見では解らなかったので、騒ぎを起こして、敵の手の内を炙り出した次第。

で、ちゃんとしたrubyコードを実行した時、どんな風にvmの中で動くか? 今度はそれを追跡したい。単純に mrb_vm_exec とかにBPを設定しても、徒労に終わる。目的地につくまでに疲れはててしまう。

symbol.c

B   hash = symhash(name, len);
  =>if (hashp) *hashp = hash;

こんな場所、182行目あたりにおけばよい。

(gdb) set unwindonsignal on
(gdb) b symhash if *key == "myadd"

が、さっぱりヒットしない。何か考え違いをしていそうだな。一旦塩漬しよう。 break if の使いかた、間違えていないよな。

(gdb) b fact if n == 7
Breakpoint 1 at 0x19a0: file fact.c, line 3.
(gdb) r
Starting program: /tmp/t/a.out

Breakpoint 1, fact (n=7) at fact.c:3
3       int fact(int n){
(gdb) bt
#0  fact (n=7) at fact.c:3
#1  0x00000a296a06f9df in fact (n=7) at fact.c:7
#2  0x00000a296a06f9df in fact (n=8) at fact.c:7
#3  0x00000a296a06f9df in fact (n=9) at fact.c:7
#4  0x00000a296a06f9df in fact (n=10) at fact.c:7
#5  0x00000a296a06fa2c in main () at fact.c:11
(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000a296a06f9a0 in fact at fact.c:3
        stop only if n == 7
        breakpoint already hit 1 time

mruby/c

ちょっと趣を変えて、mruby/c を見てみる。島根の人が作っているらしい。mrubyがコンパイラーを内蔵してるのに対して、こちらは、実行するだけの機能しかない。その分小型に出来て、小さな石でも動く。実行コードは、mrubyでコンパイルしたバイナリーを使う。

vm.cには

int mrbc_vm_run( struct VM *vm )
{
  while( 1 ) {
    mrbc_value *regs = vm->cur_regs;
    uint8_t op = *vm->inst++;           // Dispatch

    switch( op ) {
    case OP_NOP:        op_nop       (vm, regs EXT); break;
    case OP_MOVE:       op_move      (vm, regs EXT); break;
    case OP_LOADL:      op_loadl     (vm, regs EXT); break;
     :

vm->instがプログラムカウンターに相当する。それが指し示す所から、命令語opを取出して、デコードし、対応するルーチンを呼び出す。命令が使うレジスターは、動的に扱われている。

/*! OP_MOVE

  R[a] = R[b]
*/
static inline void op_move( mrbc_vm *vm, mrbc_value *regs EXT )
{
  FETCH_BB();

  mrbc_incref(&regs[b]);
  mrbc_decref(&regs[a]);
  regs[a] = regs[b];
}

命令実行の一例。最初に命令のオペレンドを取出し、それの示す所から、データを移動。まさに手作りの実行マシンだ。

typedef struct VM {
  uint8_t vm_id;                        //!< vm_id : 1..MAX_VM_COUNT
  volatile int8_t flag_preemption;
  unsigned int flag_need_memfree : 1;
  unsigned int flag_stop : 1;
  unsigned int flag_permanence : 1;
  uint16_t        regs_size;            //!< size of regs[]
  mrbc_irep       *top_irep;            //!< IREP tree top.
  const mrbc_irep *cur_irep;            //!< IREP currently running.
  const uint8_t   *inst;                //!< Instruction pointer.
  mrbc_value      *cur_regs;            //!< Current register top.
  mrbc_class      *target_class;        //!< Target class.
  mrbc_callinfo   *callinfo_tail;       //!< Last point of CALLINFO link.
  mrbc_proc       *ret_blk;             //!< Return block.
  mrbc_value      exception;            //!< Raised exception or nil.
  mrbc_value      regs[MAX_REGS_SIZE];
} mrbc_vm;

実行機械は複数持てる設計になってる(最大5CPU)。これで、複数のCPUを切り換えながら実行できる。5つのプログラムが実行出来る優れもの。mrubyには多分無い機能だ。rubyだと、3.2で実現されるのかな。

Raspbian

冒頭のSDRの資料に、ラズパイ・ピコなんてのが出て来た。こんな石でも、バックエンドを構成出来るのかと感心。

ならば、普通のラズパイならもっと凄い亊が出来るだろう。久し振りに、 RaspbianをQEMUで動かす をやってみる。オイラーもはるか昔にやってた。 qemu-arm-static

元になるイメージは、こちらから、 Index of /raspbian/images

2020-02-13-raspbian-buster.zip 1.2G を展開すると 3.6G になった。後は、手順通りにやるだけ。

sakae@deb:~/sim$ sudo QEMU_CPU=arm11mpcore chroot /mnt/raspbian
root@deb:/#
root@deb:/# cat /etc/debian_version
10.3
root@deb:/# uname -m
armv6l

ひと昔のやつだな。で、

root@deb:/# su pi
Allocating guest commpage: Operation not permitted

わけわかめなエラー。ぐぐると、 i686-arm-user-static - Allocating guest commpage: Operation not permitted

どうも、パッチを当てると直るとの亊だけど、それってqemuの再構築を意味する、やる気がしないよ。


This year's Index

Home