OpenCASCADEの最低限のサンプル

OpenCASCADE を用いた最低限のサンプルを書いてみます。下記は、2つのベクトルが平行か否かを検証するコードです。Standard_Macro.hxx は不要かも。

[c]

include <stdio.h>

include <Standard_Macro.hxx>

include <gp_Pnt.hxx>

include <gp_Vec.hxx>

int main()
{
gp_Pnt p1(0, 0, 0);
gp_Pnt p2(1, 0, 0);
gp_Vec v1(p1, p2);

gp_Pnt p3(1, 2, 0);
gp_Pnt p4(2, 3, 0);
gp_Vec v2(p3, p4);

if (v1.IsParallel(v2, 0) == Standard_True)
puts("parallel!");
else
puts("not parallel!");

return 0;
}
[/c]

ビルドします。

[bash]g++ -g -o hoge -I/usr/include/opencascade -lTKernel -lTKMath hoge.cpp[/bash]

実行します。

[bash]
$ ./hoge
not parallel
[/bash]

上手く動作している模様。次に、依存関係を調べてみます。

[bash]
$ ldd hoge
linux-gate.so.1 => (0xb77a0000)
libTKernel-6.3.0.so => /usr/lib/libTKernel-6.3.0.so (0xb7549000)
libTKMath-6.3.0.so => /usr/lib/libTKMath-6.3.0.so (0xb73f2000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb72fc000)
libm.so.6 => /lib/i686/cmov/libm.so.6 (0xb72d6000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb72b8000)
libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7171000)
libdl.so.2 => /lib/i686/cmov/libdl.so.2 (0xb716d000)
libpthread.so.0 => /lib/i686/cmov/libpthread.so.0 (0xb7153000)
/lib/ld-linux.so.2 (0xb77a1000)
[/bash]

使っていない直接参照を調べます。

[bash]
$ ldd -u hoge
Unused direct dependencies:

    /usr/lib/libTKMath-6.3.0.so
    /usr/lib/libstdc++.so.6
    /lib/i686/cmov/libm.so.6
    /lib/libgcc_s.so.1

[/bash]

あれ、TKMath は使われてないな。っと思ってビルド時に -lTKMath を外すと

[bash]
/tmp/ccSaJdhZ?.o: In function gp_Vec::Angle(gp_Vec const&amp;amp;) const':
/usr/include/opencascade/gp_Vec.lxx:111: undefined reference to
gp_VectorWithNullMagnitude?::Raise(char const*)’
/usr/include/opencascade/gp_Vec.lxx:114: undefined reference to `gp_Dir::Angle(gp_Dir const&amp;) const’
collect2: ld returned 1 exit status
[/bash]

gp_Vec クラスの依存関係の解決に失敗して ld から怒られます。

間接参照で、TKMath も使ってるってことかな。

んー、どいつとリンクさせればいいのか、いまいちちゃんと分かっていない感じです。

Numer0n数当てゲーム

フジテレビの深夜枠で放送されていたヌメロンという数当てゲームの動きを書いてみました。このゲームは、プレイヤー二人が向かいあって対戦するもので、基本的なルールは次のとおりになります。

基本ルール

  1. それぞれのプレイヤーが0~9までの数字が書かれたカードのうち3つを使って3ケタの番号を作成する。ただし、「112」「121」といった同じ数字を2つ以上使用した番号は作れない。
  2. 先攻のプレイヤーは相手が作成したと思われる番号をコールする。相手はコールされた番号と自分の番号を見比べ、コールされた番号がどの程度合って いるかを発表する。数字と位置が合っていた場合は「EAT」、数字は合っているが位置は合っていない場合は「BITE」となる。例として相手の番号が 「765」、コールされた番号が「746」であった場合「1EAT-1BITE」となる。
  3. これを先攻・後攻が繰り返して行い、先に相手の番号を完全に当てきったプレイヤーの勝利となる。

海戦ゲームを数字に置きかえてシンプルにし、艦船の位置による限定要因(推測の手掛かり)を、数字の有無であったり数字の位置情報に置きかえたようなゲームです。

ソースコード

このソースコードでは、プログラムが乱数で決定した3桁の数字をユーザが当てる形式をとっています。プログラムはユーザの入力値に基づいて、「イート」「バイト」「ヒット」の情報を正確に答えます。

#include <stdio.h>
#include <stdlib.h> // rand
#include <time.h>

int main()
{
    char x, y, z;
    int a, b, c;
    int n, m;

    /* 3桁の乱数値を生成 */
    srand((unsigned int)time(NULL));
    x = (char)(rand()%10);
    do {
        y = (char)(rand()%10);
    } while(y == x);
    do {
        z = (char)(rand()%10);
    } while(z == x||z == y);

    for (;;) {

        /* ユーザ入力を受け、1の位、10の位、100の位に分ける */
        scanf("%3d", &n);
        a = n/100;
        b = n%100/10;
        c = n%10;

        /* 不正な入力値をはじく */
        if (n < 12||n > 987||a == b||a == c||b == c)
            continue;

        /* 「イート」の数 */
        n = 0;
        if (a == x) n++;
        if (b == y) n++;
        if (c == z) n++;

        /* 「バイト」の数 */
        m = 0;
        if (a == y || a == z) m++;
        if (b == x || b == y) m++;
        if (c == x || c == y) m++;

        if (n != 3)
            printf("%d-%d\n", n, m);
        else {
            puts("Hit!\n");
            break;
        }
    }
    return 0;
}

アイテムなどの特別ルールはありません。基本システムだけです。

コムソート

先日、知人の大学生にソートプログラムの課題についての相談を受けました。ASCIIテキストのみのワードリストファイルを入力し、ソートして出力するという簡単なものだったので、そこそこ早くてかつコードがシンプルなコムソート(comb sort)でコーディングしてみました。

大学の課題ということで、ひょっとしてstring.hすら使えないんじゃないかな、と思い strlen()、strcpy()、strcmp()も自前で準備してみました。これらの文字列用の関数は ASCII 文字列に対しては動いてはいるものの、変なものを食わせたらすぐにお腹を壊すと思います。 使い方は、標準入力にワードリストを食わせてやるか、オプションにリストのパスを指定します。

gcc -o csort csort.c
./csort < wordlist.dic
abc
bbb
dcc
# もしくは ./csort wordlist.dic

PyGame で遊ぶ

PyGameとは、Python用のSDLラッパで「Pythonを使って比較的簡単にゲームが作れるよー」といったライブラリです。自前のIRCモジュールと組み合わせて、キャラクターの操作が可能なアバターチャット的な偽オンラインゲームなど、ガリガリ書いてみました。

今日はとりあえず、メモ代わりにPyGameを用いたスクリプトの基本形を貼っつけておきます。

[python]

!/usr/bin/env python

– coding: utf-8 –

import pygame
from pygame.locals import *

if name == "main":

pygame.init()
pygame.mouse.set_visible( True )
scr = pygame.display.set_mode( (640, 480) )
pygame.display.set_caption( 'test' )
c = pygame.time.Clock()

while True:
    c.tick( 60 )

    scr.fill( [ 63, 127, 127 ] )
    pygame.display.update()

    for event in pygame.event.get():
        if event.type == QUIT:
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                sys.exit()

[/python]

特に解説するところもないですね。読んだままの意味です。

参考リンク

C による Brainfuck インタプリタ

難解プログラミング言語のひとつ、Brainfuckのインタプリタを書いてみました。命令は8つしかありませんが、立派にチューリング完全です。

言語仕様

処理系の構成要素

  • インストラクションポインタ – プログラム中のある文字を指す。
  • 少なくとも30000個の要素を持つバイトの配列 – 各要素はゼロで初期化される。
  • データポインタ – 前述の配列のどれかの要素を指す。最も左の要素を指すよう初期化される。
  • 入力と出力の2つのバイトストリーム

命令

  1. > ポインタをインクリメントする。ポインタをptrとすると、C言語の「ptr++;」に相当する。
  2. < ポインタをデクリメントする。C言語の「ptr--;」に相当。
  3. + ポインタが指す値をインクリメントする。C言語の「(*ptr)++;」に相当。
  4. - ポインタが指す値をデクリメントする。C言語の「(*ptr)--;」に相当。
  5. . ポインタが指す値を出力に書き出す。C言語の「putchar(*ptr);」に相当。
  6. , 入力から1バイト読み込んで、ポインタが指す先に代入する。C言語の「*ptr=getchar();」に相当。
  7. [ ポインタが指す値が0なら、対応する ] の直後までジャンプする。C言語の「while(*ptr){」に相当。
  8. ] ポインタが指す値が0でないなら、対応する [ にジャンプする。C言語の「}」に相当。

コード

hello.bf を食わせてやると、”Hello, world!” と表示されます。

ビルド

gcc -o bfi bfi.c

実行

./bfi hello.bf
# または
./bfi < hello.bf

注意

実行するスクリプトによっては、危険な振舞いをする可能性があります。十分に注意してください。

4ビット仮想CPUを考える

学研の GMC-4Intel 4004 に思いをはせながら、4 ビット命令を搭載した仮想 CPU を考えました。チューリング完全にするだけなら Brainfuck のようにで 3 ビット命令だけでも通用するけど、命令のビット長が中途半端になるので扱いやすい 4 ビットにしました。ポインタ以外のレジスタなし、サブルーチンコールなし、命令コードの格納場所とユーザランドメモリは連続している、という設定です。命令バイナリのバイト配列はビッグエディアンです。

1. CPU 命令一覧

サポートするニーモニックは以下のとおり。4ビット命令なので、1バイト(8ビット)の半分。 つまり16個の命令から成ります。

0  zero    値を0に初期化する
1   inc 値をインクリメント
2   dec 値をデクリメント
3   add 値に前の値を足して代入する
4   sub 値に前の値を引いて代入する
5   swap    値と前の値を入れ替える
6   copy    値に前の値をコピーする
7   not 値をNOT演算して代入する
8   pzero   ポインタ位置を0に初期化する
9   pinc    ポインタ位置をインクリメント
A   pdec    ポインタ位置をデクリメント
B   jump    値が0なら次のラベルへジャンプ
C   back    値が0なら前のラベルへジャンプ
D   get 値に入力値を代入してpinc
E   put 値を出力してpinc
F   label   ラベル

コーディングしながら思ったけど、0x0 をラベルにすれば良かったかな。

2. サンプルコード

上の表を見ながら、簡単な計算プログラムを考えてみます。

2.1. 足し算電卓

二つの値を入力し、結果を印字するプログラムです。

[bash]
get # 一つ目を入力
get # 二つ目を入力
pdec # ポインタを一つ前に戻して
add # 加算して
put # 印字
[/bash]

バイナリで言うと「DD A3 E」。後述のアセンブラでアセンブルするとバイト単位アクセスの都合上、末尾にラベルの「0xF」が付きます。

解説

ありゃ、これは簡単すぎましたね。5命令で実現できてしまいました。

   |       ||           ||
   |       || 0 | 1 | 2 ||
 -------------------------
 D | get   || 3 |[ ]|   || <=== 3 入力
 D | get   || 3 | 2 |[ ]|| <=== 2 入力
 A | pdec  || 3 |[2]|   ||
 3 | add   || 3 |[5]|   ||
 E | put   || 3 |[5]|   || ===> 5  出力

2.2. かけ算電卓

足し算は簡単すぎたので、今度はかけ算に挑戦しようと思います。バイナリで言うと「DD 09 FA 3A A2 B9 99 0C F9 9E」。条件分岐と繰りかえし処理があるし、評価用サンプルにはちょうど良いかもしれません。

[bash]

かけ算を行うプログラム

get # 一つ目の値
get # 二つ目の値

zero
pinc

label

pdec
add
pdec
pdec
dec

jump # 一つ目の値が 0 なら、ループを抜けて結果の印字処理へ

pinc
pinc
pinc
zero

back # 一つ目の値が 0 になるまで、上のラベルへ戻る

結果の印字処理

label

pinc
pinc

put # 印字
[/bash]

解説

「3」「2」と入力した場合、下記のように処理を行い、最終的に 3 × 2 の答え「6」を印字します。 命令実行直後のメモリの値を併記しました。[ ] は、ポインタを示しています。

   |       ||     1 回目    ||     2 回目    ||     3 回目    ||    ラスト     ||
   |       || 0 | 1 | 2 | 3 || 0 | 1 | 2 | 3 || 0 | 1 | 2 | 3 || 0 | 1 | 2 | 3 ||
 --------------------------------------------------------------------------------
 D | get   || 3 |[ ]|   |   ||   |   |   |   ||   |   |   |   ||   |   |   |   || <=== 3 入力
 D | get   || 3 | 2 |[ ]|   ||   |   |   |   ||   |   |   |   ||   |   |   |   || <=== 2 入力
 0 | zero  || 3 | 2 |[0]|   ||   |   |   |   ||   |   |   |   ||   |   |   |   ||
 9 | pinc  || 3 | 2 | 0 |[ ]||   |   |   |   ||   |   |   |   ||   |   |   |   ||
 F | label || 3 | 2 | 0 |[ ]|| 2 | 2 | 2 |[0]|| 1 | 2 | 4 |[0]||   |   |   |   ||
 A | pdec  || 3 | 2 |[0]|   || 2 | 2 |[2]| 0 || 1 | 2 |[4]| 0 ||   |   |   |   ||
 3 | add   || 3 | 2 |[2]|   || 2 | 2 |[4]| 0 || 1 | 2 |[6]| 0 ||   |   |   |   ||
 A | pdec  || 3 |[2]| 2 |   || 2 |[2]| 4 | 0 || 1 |[2]| 6 | 0 ||   |   |   |   ||
 A | pdec  ||[3]| 2 | 2 |   ||[2]| 2 | 4 | 0 ||[1]| 2 | 6 | 0 ||   |   |   |   ||
 2 | dec   ||[2]| 2 | 2 |   ||[1]| 2 | 4 | 0 ||[0]| 2 | 6 | 0 ||   |   |   |   ||
 B | jamp  ||[2]| 2 | 2 |   ||[1]| 2 | 4 | 0 ||[0]| 2 | 6 | 0 ||   |   |   |   ||
 9 | pinc  || 2 |[2]| 2 |   || 1 |[2]| 4 | 0 ||   |   |   |   ||   |   |   |   ||
 9 | pinc  || 2 | 2 |[2]|   || 1 | 2 |[4]| 0 ||   |   |   |   ||   |   |   |   ||
 9 | pinc  || 2 | 2 | 2 |[ ]|| 1 | 2 | 4 |[0]||   |   |   |   ||   |   |   |   ||
 0 | zero  || 2 | 2 | 2 |[0]|| 1 | 2 | 4 |[0]||   |   |   |   ||   |   |   |   ||
 C | back  || 2 | 2 | 2 |[0]|| 1 | 2 | 4 |[0]||   |   |   |   ||   |   |   |   ||
 F | label ||   |   |   |   ||   |   |   |   ||   |   |   |   ||[0]| 3 | 6 | 0 ||
 9 | pinc  ||   |   |   |   ||   |   |   |   ||   |   |   |   || 0 |[3]| 6 | 0 ||
 9 | pinc  ||   |   |   |   ||   |   |   |   ||   |   |   |   || 0 | 3 |[6]| 0 ||
 E | put   ||   |   |   |   ||   |   |   |   ||   |   |   |   || 0 | 3 |[6]| 0 || ===> 6  出力
  1. メモリの0番地と1番地にそれぞれ計算したい値を入力する。
  2. 2番地を0で初期化する。
  3. 2番地に1番地の値を加算する。
  4. 0番地の値をデクリメント(マイナス1)する。
  5. 0番地の値が0になるまで、3から4を繰りかえす。上記例の場合は3回繰りかえす。
  6. 結果が2番地に貯まるので、それを印字して終了。

3. プリプロセッサ

コメントと余分な空白文字を削除してやれば良いいので、下記のようになります。空行は次項のアセンブラによって読み飛ばされるので、この時点で処理する必要はありません。

[bash]
perl -e ‘for(){s/#.*$|[ \t]//g;print;}’ < kakezan.code > kakezan.s
[/bash]

4. アセンブラ

キーワードをバイナリ値に置換しているだけです。 このアセンブラでアセンブルすれば、仮想 CPU 用のバイナリ・ファイルが生成されます。

[c]

include

include

include

int main(int argc, char** argv)
{
char s[256];
char n, nn=0;
FILE *f;
int c=0;
if (argc!=2)
puts("Usage: ./asm output < source.s");
else {
if ((f = fopen(argv[1], "wb"))==NULL)
return 1;
while(fgets(s, sizeof(s), stdin) != NULL){
if (!strcmp(s, "zero\n"))    {n = 0x0;}
else if (!strcmp(s, "inc\n")) {n = 0x1;}
else if (!strcmp(s, "dec\n")) {n = 0x2;}
else if (!strcmp(s, "add\n")) {n = 0x3;}
else if (!strcmp(s, "sub\n")) {n = 0x4;}
else if (!strcmp(s, "swap\n")) {n = 0x5;}
else if (!strcmp(s, "copy\n")) {n = 0x6;}
else if (!strcmp(s, "not\n")) {n = 0x7;}
else if (!strcmp(s, "pzero\n")) {n = 0x8;}
else if (!strcmp(s, "pinc\n")) {n = 0x9;}
else if (!strcmp(s, "pdec\n")) {n = 0xA;}
else if (!strcmp(s, "jump\n")) {n = 0xB;}
else if (!strcmp(s, "back\n")) {n = 0xC;}
else if (!strcmp(s, "get\n")) {n = 0xD;}
else if (!strcmp(s, "put\n")) {n = 0xE;}
else if (!strcmp(s, "label\n")) {n = 0xF;}
else {n = -1;}
if (n>=0x0&&n<=0xF) {
c++;
if (c%2)
nn = n<<4;
else {
nn += n;
fwrite(&nn, sizeof(char), 1, f);
}
}
}
if (c%2) {
nn += 0xF;
fwrite(&nn, sizeof(char), 1, f);
}
fclose(f);
}
return 0;
}
[/c]

4.1. 使い方

[bash]

ビルド

gcc -o asm asm.c

アセンブルの実行。kakezan.exe が生成されます。

./asm kakezan.exe < kakezan.s
[/bash]

バイナリファイルの中身を確認するには xxd コマンドを使用します。

[bash]

ちゃんと出来ているかな

$ xxd kakezan.exe
0000000: dd09 fa3a a2b9 990c f99e …:……
[/bash]

5. エミュレータ

バイナリファイルをコマンドライン引数に渡せば、CPU の動作をエミュレートして実行します。

[c]

include

int main(int argc, char** argv)
{
const int RAMSIZE = 256;
int p; / RAM Pointer /
int *pp; /
Program Pointer /
int ps; /
Program Size /
int r[RAMSIZE]; /
RAM /
FILE *f;
int c, t;
if ((f = fopen(argv[1], "r")) == NULL) return 1;
// Load binary to RAM
p = r;
while ((c = fgetc(f)) != EOF) {
c &= 0xFF;
*p++ = c >> 4;
*p++ = c & 0xF;
}
fclose(f);
ps = p – r;
// Execute
for (pp=r; pp-r<ps; pp++) {
// printf("%1x[%d]: %d %d %d %d\n",
// *pp, p-r-ps, r[ps], r[ps+1], r[ps+2], r[ps+3]);
switch (
pp) {
case 0x0: p = 0; break;
case 0x1: (
p)++; break;
case 0x2: (p)–; break;
case 0x3: *p += *(p-1); break;
case 0x4: *p -= *(p-1); break;
case 0x5: t = *p; *p = *(p-1); *(p-1) = t; break;
case 0x6: *p = *(p-1); break;
case 0x7: *p = !(
p); break;
case 0x8: p = r; break;
case 0x9: p++; break;
case 0xA: p–; break;
case 0xB: if (p==0) while(pp!=0xF) pp++; break;
case 0xC: if (p==0) while(pp!=0xF) pp–; break;
case 0xD: scanf("%d", p++); break;
case 0xE: printf("%d\n", *p++); break;
default: break;
}
}
return 0;
}
[/c]

r が int なので、char な2命令1セットが実メモリに展開される場合、sizeof(int) バイトに拡張されてしまいます。
リトルエディアンの処理系だとビッグエディアンなバイナリとメモリにロードされたイメージとでは差が出てしまいます。実害はないけど、ちょっと変な仕様だなと思いました。

5.1. 使い方

変なバイナリを食わせると危険ですので、自己責任で実行してください。

[bash]

ビルド

$ gcc -o emu emu.c

実行

$ ./emu kakezan.exe
123
321
39483

bc を使って、答え合わせ

$ echo 123 * 321 | bc
39483

よし。

[/bash]

NEC Life Touch NOTE に Debian GNU/Linux を導入

NECのAndroid端末「Life Touch NOTE」にDebian GNU/Linux をインストールした時の覚え書きです。

root 権限の奪還

ホストマシンにAndroid SDKのインストールしたら、USBでホストとLTNをつないで、以下の手順でroot権限を取り返します。

まず、SuperOneClickのZIPをダウンロードし、適当な場所に解凍しておきます。配布物はWindows向けのアーカイブですが、気にせず解凍します。unzip後のディレクトリに移った後、

[bash]
adb push Exploits/psneuter /data/local/tmp
adb push Dependencies/busybox /data/local/tmp
adb push Root/su-v1 /data/local/tmp
adb push Root/su-v2 /data/local/tmp
[/bash]

と、必要そうなファイルをLTNの/data/local/tmpに転送します。次に、LTNに接続し、転送したものに実行権限を与えてからpsneuterを実行します。

[bash]
adb shell
(adb) cd /data/local/tmp
(adb) chmod 755 *
(adb) ./psneuter
[/bash]

上手くいけば勝手に切断されるので、接続しなおします。接続後、プロンプトが「#」になりスーパーユーザになっていることを確認してください。

[bash]
adb shell
[/bash]

次にデフォルトのPATHが設定されている位置に busybox をインストールします。

[bash]
(adb) set

/system/xbin がパスに登録されているが、このディレクトリは存在しない。

カスタムコマンドを設置するにはもってこい。

(adb) df

/system は /dev/block/mmcblk3p6 らしい。

(adb) mount -o remount,rw /dev/block/mmcblk3p6 /system
(adb) mkdir /system/xbin
(adb) chmod 755 /system/xbin
(adb) cd /system/xbin

cp コマンドがなかったので、busybox のアプレットで busybox をコピーする

(adb) /data/local/busybox cp /data/local/busybox .

リンクを展開

(adb) ./busybox –install .
[/bash]

ConnectBot で localhost 接続して、busybox が使える状態になっているか確認します。

[bash]
$ which wget
/system/xbin/wget
$ dc
1 2 + p
3
$ vi
[/bash]

よし、いい具合。これでroot権限が使え、かつ自由に作業する為の足がかりが整いました。

2. Debian GNU/Linux の導入

59414d41さんのハックの賜物であるLTN用Debian(Lenny)配布ページに従ってインストールします。ページを見ると詳しい事が書いてありますが、ここでは要点だけかいつまんで書いておきます。

まず、ホストマシンにSDカードを差して、起動用SDカードを作成します。ホストマシン上で次のコマンドを実行しました。

[bash]
mkdir backup
cp -r /mnt/sdcard/* backup/
su # ←ここからroot権限
mkfs.ext3 /dev/mmcblk0p1
mount -t ext3 -o rw /mnt/sdcard /dev/mmcblk0p1
cd /mnt/sdcard
wget http://205.196.121.246/q4mgjdew5pag/0rnq7nc7lc1d0uq/ltna-debian-010.tar.gz
tar xfpz ltna-debian-010.tar.gz
[/bash]

この時、カレントディレクトリである/mnt/sdcardには次のようなファイルが詰まっています。

busybox … 作業用 busybox。
ltna-debian-010 tar gz … 落としてきたアーカイブ。解凍したのでもう不要。
ltna-debian-rootfs cpio gz … SD カードに展開する予定のルート・ファイルシステム。
recovery2 img … init を書き換えた改造リカバリ領域データ。
setup_recovery sh … リカバリ領域のバックアップと上書きをするスクリプト。
setup_rootfs sh … 必要ファイルをコピーし、ルート・ファイルシステムを展開するスクリプト。
sh … 作業用シェル。

アンマウントしてホストのrootを抜けます。

[bash]
cd
umount /mnt/sdcard
exit
[/bash]

SD カードをホストマシンから LTN に差し替え、USB デバッグ接続します。SDカードを/sdcardにマウントして中を見てみると、準備したファイル群が見えると思います。それらを実行します。

[bash]
(ホストから)$ adb shell

root権限

mount -t ext3 /dev/block/mmcblk2p1 /sdcard
cd /sdcard
./setup_rootfs.sh ….. ルートファイルシステムを SD カード内に展開。
extract…
copy wpa_supplicant.conf
copy /system/lib/BCM4329B1.hcd
copy bcm4329 files…
copy /system/lib/hw/wlan/usibcm4329-RC218-nvram.txt
debian rootfs ready.
adb# ./setup_recovery.sh … リカバリ領域のバックアップと改造データでの上書き。
Backup recovery…
New recovery ready.
cp recovery_org.img /data/local/tmp …. オリジナルのリカバリ領域を移しておく。
cd /
umount /sdcard
exit
[/bash]

LTNで起動中の Android を普通に終了します。電源ボタンから「電源を切る」でOKです。
完全に電源が切れたことを確認し、HOMEボタンを押しながら電源を投入します。これにより、リカバリモードで起動します。NECのロゴが消えるまで、HOMEボタンを押しておけば確実です。

[bash]
Login: root
Password: (root)
passwd
ifup eth0
ping www.yahoo.co.jp .. 外とつながってるか調べる。
apt-get update …….. うちでは失敗したので、リポジトリを修正する。
vi /etc/apt/source.list
– deb http://ftp.ja.debian.org/debian lenny main non-free contrib
+ deb http://ftp2.ja.debian.org/debian lenny main non-free contrib
apt-get update …….. 今度はうまくいった。
apt-get install aptitude
aptitude install bluez-utils ntp ssh vim
aptitude install x-window-system ttf-mona ttf-kochi-gothic ttf-kochi-mincho
aptitude install xserver-xorg-input-evtouch rxvt-unicode ratpoison gdm
cp /opt/ltna/xorg.conf /etc/X11/
echo ‘inet:x:3003:user’ >> /etc/group
[/bash]

ここまで来て、startxとしたいところを我慢して、念の為再起動すると、Android が立ち上がりました。しまった!と思って、ホストマシンと繋ぎ直し、setup_recovery.sh を実行しなおしました。電源を一度切り、HOMEボタンを押しながら電源投入すると、Linux の起動メッセージが流れた後、嬉しい事に GDM がお出迎えしてくれました。

その後、

[bash]aptitude install locales && dpkg-reconfigure locales[/bash]

で日本語化を行いました。

2.1. SD カードの入れ替え

2.2. バッテリー残量を得るスクリプト

モバイル WiFi + Debian on LTN 環境で、バッテリー残量が取れないと不便だった。インストールしてみた acpi では上手く取れなかったので、簡単なスクリプトを書いてみました。もっと上手い方法があるかもしれませんが、これで十分かな。

[bash]

!/bin/sh

dev="/sys/devices/nvec/nvec_battery/power_supply/battery"
[ ! -e $device ] && exit 1

stat=$(cat $dev/status)

emp=$(cat $dev/charge_empty)
ful=$(cat $dev/charge_full)
now=$(cat $dev/charge_now)

par=$(expr $now * 100 / $(expr $ful – $emp))
[ $par -gt 100 ] && par=100

echo $par% $stat
[/bash]

実行すると、残量のパーセンテージと電源の状態(充電中か、など)が表示されます。

カセットテープにデータを記録

人生で初めて使い始めた Macintosh には、既に内蔵ハードディスク、CD ドライブ、FD ドライブが搭載されていたので、コンパクトカセット(音楽カセットテープ)を補助記憶媒体としてリアルタイムに使った事がありません。父が使っていた MZ-700 を譲り受けて持っていますが、肝心のデータレコーダのゴムが劣化していて、まともに使えないのが残念です。
中学生の頃までコンパクトカセットを使って、よくラジオで流れていた好きな音楽を録音していました。そんなコンパクトカセットにデジタルデータが記録されるのが、ひどくアナログな雰囲気だけど、ちゃんとデジタルなところが魅力的で、試してみたいなあ、と考えて、MP3 プレイヤーが 500 円で買えるご時世に、安いステレオラジカセを購入した事もありました。
古いコンピュータのエミュレータ環境のツールの一つとして、コンパクトカセットの情報をデジタルデータに変換するコンバータ・ソフトは、Windows ではよく見かけていました。
Linux で使えるものはないな、と思いリポジトリを漁ってみましたが、需要がなかった模様。
アマチュア無線関連のツールや本物(?)のテープレコーダ用のツール( tar )はあっても、民生用カセット向けのツールは乏しいみたいです。

そこで、Wikipedia でデータレコーダの項を調べてみると、

情報を FSK などの変調方式でオーディオ周波数帯の信号に変調して記録するもので、代表的な記録方式にKCS(カンサスシティスタンダード)があり1200Hz/2400HzのFSK方式で300bpsの記録ができた。NECのPC-8000シリーズなどではキャリア周波数はそのままでシンボル長のみ短縮した600bpsでの記録を標準としていた。シャープのMZシリーズではコンピュータ本体に直接内蔵され、ソフトウェア制御によるパルス幅変調方式で記録を行い、他の機種と比較し、エラーの少ないアクセスと共に、1200bpsの速度を実現していた。

という記事が見つかった。この KCS という記録方式を元に検索してみると、py-kcsというツールを見つけました。Python で書かれた KCS 方式のエンコーダ・デコーダらしい。入出力対象の音声ファイル形式は WAVE。使えそうです。

ダウンロード

http://linux.softpedia.com/get/Multimedia/Audio/py-kcs-59693.shtml

使い方

[bash]

Python 3.1.2 以降が必須。

テキストファイルを音声データにエンコード。

python3 kcs_encode.py input.txt output.wav

音声データをデコードし、内容を標準出力に印字。

python3 kcs_decode.py input.wav

RAW なバイナリデータとして印字する場合は -b オプションを用いる。

python3 kcs_decode.py -b input.wav
[/bash]

作者によるページでは、実際に Ohio Scientific Superboard II という 70 年代のボードに音声信号で BASIC プログラムを転送しています。楽しそうだなあ。

ちょっと使ってみる

Python 3.2 の環境を整えた後、py-kcs をダウンロードしてきます。解凍すると、kcs_decode.py と kcs_encode.py というスクリプトが得られます。

[bash]

試しに、README.txt をエンコードしてみる。

$ python3 kcs_encode.py README.txt hoge.wav

再生してみると、懐しいノイジーな音が出てくる。

$ mplayer hoge.wav

ここで、コンピュータの Line Out または Headphone 端子とラジカセの Line In

または Mic 端子をミニジャックで接続し、コンパクトカセットに録音後、すかさず

$ cat hoge.wav > /dev/dsp

する。再生が終わったら(手元の環境では1分58秒だった)、録音を停止。

[/bash]

これで多分、デジタルデータのアナログ音声化&カセットテープに保存が完了します。
次に、カセットテープを巻き戻し、

[bash]

冒頭の 5 秒くらいのところから再生をスタート。すかさず

$ cat /dev/dsp > fuga.wav

する。再生が終わったら、C-c してコンピュータ側の録音を停止。

得られた fuga.wav に対して、

$ python3 kcs_decode.py fuga.wav > README.new.txt

して、README.new.txt の内容が、正しく復号されたものか確認する。

$ cat README.new.txt
[/bash]

上記の要領で行けるだろうと思いました。

手元にラジカセがなかったので、試しに、変換した WAVE ファイルを一度、MP3 ファイルに変換し、再度 WAVE ファイルにデコードし直してから、kcs_decode に食わせてみました。
すると、一応デコードはしてくれましたが、文字化けをした意味の無いバイトコードが生成されてしまいました。

んー、データの維持に問題がありそうな周波数の変更はしていないし、そんなに劣化させてないハズなんだけどな。
やり方がマズかったのか。後日、実際にラジカセで試してみたいと思います。

Android SDK の導入

Ubuntu 11.10 に NEC LifeTouch NOTE をつないで、いじくる時に操作したメモです。基本的には、以下の手順で OK なはず。

  1. android-sdk_r*-linux.tgz のダウンロード、解凍。
  2. tools/android update sdk –no-ui の実行。

下記では、Java SDK をインストールしていなかったので、その導入からしています。

まず、都合がいい場所に作業ディレクトリを設け、バイナリを落としてきます。

[bash]
mkdir android
cd android
wget http://dl.google.com/android/android-sdk_r16-linux.tgz
tar xvf android-sdk_r16-linux.tgz
cd android-sdk-linux
[/bash]

解凍して出てきたSDK Readme.txtの中にupdate sdkせよ、との記述があるので、それに従います。

[bash]
tools/android update sdk –no-ui

Java がないよ、と怒られる

[/bash]

Java 環境をインストールしていなかったので、ここからJavaインストール・フェイズ。

[bash]
sudo aptitude update
sudo aptitude search java | less

OpenJDK や gcj だけで、Sun 製の Java がない。

Ubuntu11.10 用のリポジトリを追加してやる。

sudo add-apt-repository ppa:ferramroberto/java
sudo apt-get update
sudo apt-get install sun-java6-jdk

デフォルトの Java 環境設定

sudo update-alternatives –config java
[/bash]

気を取りなおして、再度SDKのインストーラを実行します。

[bash]
tools/android update sdk –no-ui
[/bash]

Android4.0 や 3.x 向け環境がわさわさ入ってしまう。うっわ要らねえ、うち 2.2 と 1.6 しか持ってないのに・・・と思ってマニュアルを読んでみると、ターゲットバージョンを指定するオプションがある模様です。
次に、Android 端末をホストに USB 接続します。

[bash]
dmesg # 反応あり。
adb devices # デバイスが表示された。
adb shell # つながったー。
[/bash]

拍子抜けするほど、あっさりつながりました。
adb を使いたいだけなのに、Java SDK をごっそり入れてしまいました。ちゃんと調べれば、必要なものだけ入れることができたのかもしれません。

エルミート曲線の描写

C を覚えたての2003年頃に書いたエルミート曲線の描写プログラムです。グラフィックライブラリの使い方も知らなかった為、モノクロビットマップ画像を直に書いています。( 実行には 256×256 の 256 色ビットマップ画像を head.dat という名前で同一ディレクトリに配置しておく必要があります。 )

[c]
////////////////////////////////////////////////
// エルミート曲線描写テスト
////////////////////////////////////////////////

#define BG_COLOR 0xFF //初期化カラー
#define GL_COLOR 0xD0 //グリッドカラー
#define GL_SCALE 20 //グリッド幅px
#define VC_COLOR 0x80 //ベクトルカラー
#define LN_ACRCY 0.001 //曲線描写精度
#define BITMAP_X 256 //画像横px
#define BITMAP_Y 256 //画像縦px
#define IMAGE_FILE "data.bmp" //出力画像ファイル名

#define HEADER_SIZE 0x435 //Bitmapファイルヘッダサイズ

#define X 0
#define Y 1

#include<stdio.h>
#include<stdlib.h>

void main(void)
{
FILE *fp,*fread;
int x,y,i,j,k;
int vram[256][256],vramt[256][256];

for(y=0;y<BITMAP_Y;y++) //イメージバッファを念のため初期化
for(x=0;x<BITMAP_X;x++)
vram[x][y]=BG_COLOR;

////////////////////////////////////////////////
//グリッド描写

for(i=0;i<256;i+=GL_SCALE) //縦線
{
for(j=0;j<256;j++)
vram[i][j]=GL_COLOR;
}
for(i=0;i<256;i+=GL_SCALE) //横線
{
for(j=0;j<256;j++)
vram[j][i]=GL_COLOR;
}

////////////////////////////////////////////////
//メイン描写

float t,fx,fy;
int v[2][2],p[2][2];

p[0][X]=3;
p[0][Y]=4;

v[0][X]=6;
v[0][Y]=-3;

p[1][X]=10;
p[1][Y]=10;

v[1][X]=-8;
v[1][Y]=1;

for(t=0;t<1.0;t+=LN_ACRCY) //エルミート曲線描写
{
fx=p[0][X]*((2*t+1)*((1-t)*(1-t)))+v[0][X]*(t*((1-t)*(1-t)))+v[1][X]*((-t)*(-t)*(1-t))+p[1][X]*(t*t*(3-2*t));
fy=p[0][Y]*((2*t+1)*((1-t)*(1-t)))+v[0][Y]*(t*((1-t)*(1-t)))+v[1][Y]*((-t)*(-t)*(1-t))+p[1][Y]*(t*t*(3-2*t));
fx=fx*GL_SCALE;
fy=fy*GL_SCALE;
x=fx;
y=fy;
vram[x][y]=0x00;
if(LN_ACRCY>=0.01) //描写精度が低ければ座標を表示。
printf("%5.5f\t%5.5f\t%d\t%d\n",fx,fy,x,y);
}

//ベクトル(ハンドル?)描写
for(k=0;k<2;k++)
{
p[k][X]=GL_SCALE*p[k][X];
p[k][Y]=GL_SCALE*p[k][Y];
v[k][X]=p[k][X]+GL_SCALE*v[k][X];
v[k][Y]=p[k][Y]+GL_SCALE*v[k][Y];
if(p[k][X]>v[k][X])
{
i=v[k][X];
j=p[k][X];
}
else
{
i=p[k][X];
j=v[k][X];
}
for(x=i;x<j;x++)
{
y=x*(p[k][Y]-v[k][Y])/(p[k][X]-v[k][X])+p[k][Y]-p[k][X]*(p[k][Y]-v[k][Y])/(p[k][X]-v[k][X]);
if(x>=0&&x<256&&y>=0&&y<256)
vram[x][y]=VC_COLOR;
}
vram[v[k][X]][v[k][Y]]=0xFF;
vram[v[k][X]][v[k][Y]+1]=0x00;
vram[v[k][X]][v[k][Y]-1]=0x00;
vram[v[k][X]+1][v[k][Y]]=0x00;
vram[v[k][X]+1][v[k][Y]+1]=0x00;
vram[v[k][X]+1][v[k][Y]-1]=0x00;
vram[v[k][X]-1][v[k][Y]]=0x00;
vram[v[k][X]-1][v[k][Y]+1]=0x00;
vram[v[k][X]-1][v[k][Y]-1]=0x00;
}

////////////////////////////////////////////////
//Y軸座標の問題解決(天地入れ替え)

for(i=0;i<256;i++)
{
for(j=0;j<256;j++)
{
vramt[i][j]=vram[i][255-j];
}
}

for(i=0;i<256;i++)
{
for(j=0;j<256;j++)
{
vram[i][j]=vramt[i][j];
}
}

////////////////////////////////////////////////

for(y=0;y<BITMAP_Y;y++) //カラーデータを念のため校正(0x00~0xFF)
{
for(x=0;x<BITMAP_X;x++)
{
if(vram[x][y]>0xFF)
vram[x][y]=0xFF;
if(vram[x][y]<0x00)
vram[x][y]=0x00;
}
}

////////////////////////////////////////////////

//書込ビットマップファイルをバイナリモードでオープン。
if( (fp=fopen(IMAGE_FILE,"wb"))==NULL){
printf("Image File Open Error.\n");
exit( EXIT_FAILURE );}

//ヘッダファイル読み込み
if((fread=fopen("head.dat","rb"))==NULL){
printf("Header File Open Error.\n");
exit( EXIT_FAILURE );}

//ヘッダーファイルからヘッダーを読み取り出力。
rewind(fread);
rewind(fp);
for(i=0;i<HEADER_SIZE;i++)
putc(getc(fread),fp);

//ドット描写
for(x=BITMAP_X;x>0;x–)
for(y=0;y<BITMAP_Y;y++)
fprintf(fp,"%c",vram[y][x]);

for(y=0;y<2;y++) //最後(必要?)
fprintf(fp,"%x",0);
fclose(fp);
printf("ビットマップ画像を出力しました。\n");
exit( EXIT_SUCCESS );
////////////////////////////////////////////////
}
[/c]

無駄が多いし、汚いプログラムですね…。
実行すると、以下のようなビットマップ画像が出力されます。