ラベル プログラミング の投稿を表示しています。 すべての投稿を表示
ラベル プログラミング の投稿を表示しています。 すべての投稿を表示

2024年9月6日金曜日

NUCLEO の USB Storage 問題

STMicroelectronicsのNUCLEOボードを試作(または趣味)で使う。
開発環境は、Cross-gcc +Eclipse +OpenOCD で用意している。

NUCLEOボードは、ST-LinkというデバッグI/Fがあり便利だ。
しかし、JTAG I/Fのみならず、USB Storageデバイスが接続されたようになる。
害がないならそれも良いのだが、Linuxでは問題になる。

OpenOCD User's guide の USB ST-LINK based に/etc/modprobe.confを書き換えることが書いてある。この文書は少し古い。
最近のUbuntuなら、/etc/modprobe.dの下にそれぞれの設定ファイルを置く。
私は /etc/modprobe.d/stlink.conf を以下の内容で作った。
# Prevent ST-LINK from showing up as a USB disk
options usb-storage quirks=483:3744:i,483:374b:i,483:374e:i
これで、ST-Linkを接続しても、自動マウントされなくなる。

解決したと思っていたが、職場のPCではおかしなことになった。
上記、/etc/modprobe.d/stlink.conf を用意しても効かない。

起動時、initrdの段階で、usb-storage.ko が読まれているのでは?と、思った。

この設定は、モジュールロード時のパラメータだ。
initrdの段階では、/etc/modprobe.d/の設定は読めない。VAIO-C1で遊んでいるときに経験していた。
職場のPCにはUSBカードリーダが常時接続されている。
bootシーケンスのinitrdの段階でそれを検出すると、usb-storage.koがロードされる。
その時点では/etc/modprobe.d/の設定はアクセスできないので、結果的にパラメータなしでロードされる。

ルートファイルシステムマウント後(/etc/modprobe.dがアクセス可能になる)、ST-Linkを挿しても、すでにusb-storage.koが読み込まれているので、再ロードは行われない。
そのため、/etc/modprobe.d/stlink.conf が、無視されたように見える。
rmmodで、アンロード(uasを先にアンロード)してNUCLEOを挿すと、再ロードが行われる。
このとき、/etc/modprobe.d/stlink.conf が適用されるため、ST-LinkのUSB Storageがマウントされない。

わざわざrmmodするのは面倒だ。
何もせずとも、ST-Link の USB Storage はマウントしないでほしい。
initrdでのモジュールのパラメータを調整するには、VAIO-C1のときのように、/etc/initramfs-tools/modules を書き換える。以下のようになる。
# List of modules that you want to include in your initramfs.
# They will be loaded at boot time in the order below.
#
# Syntax:  module_name [args ...]
#
# You must run update-initramfs(8) to effect this change.
#
# Examples:
#
# raid1
# sd_mod
usb-storage quirks=483:3744:i,483:374b:i,483:374e:i
最後の行が追加したもの。
コメントを読めばわかるが、書き換え後initrdを作り直す必要があり、update-initramfs を実行しなければならない。以下のように:
$ sudo update-initramfs -u
[sudo] <ユーザ名> のパスワード: 
パスワードを入力すれば、initrdが更新される。

これをやれば、/etc/modprobe.d/stlink.confが不要というわけではない。
起動時にUSBメモリやUSBカードリーダが接続されていない場合は、initrd内でusb-storage.koは読み込まれない。
Linuxの起動が完了した後は、/etc/modprobe.d/stlink.confが必要になるので、両方用意しておく必要がある。

最初にSTM32を使ったのは、雑誌(CQ出版 Design Wave 2008年5月号)の付録のSTM32F103VBだった。
(さっき発掘してきた)

開発環境は、cross-gcc + eclipse + OLIMEXのJTAG(千石で購入) で揃えた。

最初に開発環境を自力で揃えてしまったので、未だにSTM-STUDIOを使わない(Cubeは利用するけど)。
もしかしたら、STM-STUDIOを使っている場合はこんな問題に遭遇しないのかもしれない。

最近ではarm-none-eabi用のgccを自分でビルドしなくても良くなり、OpenOCDもUbuntuのもので十分だ。
多少楽になったが、それでも自前で揃えるには何気に大変だ。
バカバカしいかもしれないけど、何かを開発するのは苦労の連続だ。
苦労と考えず、楽しむつもりでやるのが「馬鹿の道」。ついでに、問題解決力が鍛えられる。

2024年4月14日日曜日

古いマイコンボード#2

先月末、年度末のドタバタから開放されたあとで、新しいPCの環境整理を始めた。
数年前の記事のマイコンボード、まだまだ遊びで使うつもりなので、クロスGCCも用意していた。
新しいPCがイイ感じなので、調子に乗って新しいgcc-13.2を使った。
Super−H用のクロスGCCをnewlibと一緒にビルドしたのだが、クロスコンパイルをするとリンク時にエラーになっていた。
細かいことは忘れたけど、newlibを少しいじったように思う。
ターゲット用のバイナリができることを確認したあとで、UART経由で書き込んで、簡単な動作確認をしていた。
その時は、動作しているように思えたが…。
他の作業もあるので、あまり細かく見ていなかった。

先週、引っ張り出してみると、何故かうまく動かない。
まず、IIC接続のRTCがまともにアクセスできない。
このコードは2006年頃に書いたままだ。当時はかなり忙しく、せいぜい一日30分ぐらいしか使えないので、ギリギリまで手抜きをしている。コメントに書いてある内容とコードの動作が違っていたりする。そのためコンパイラが変わると動かない可能性は十分にある。
最初はそれを疑っていたのだが、SPI接続のSDカードのアクセスもできない。

gcc-13.2のクロスコンパイラを作ったときにnewlibをいじっていたので、もしや開発環境側に問題があるか?とその時は考えた。
今まで使ってきたGCCのソースを一応保存してあるので、古いGCCのクロスコンパイラを用意してビルドしてみた。
それでもだめ。
さらに古いPC内に残っている動作していた実行形式も動かない。
考えてみると、UARTからの書き込みプログラムは正常に動作しているし、kermitでのUART経由の操作もうまく行っている。
すなわち、コンパイラは問題ないだろう。

ということは、H/W?とうとう壊れてしまったのか?

先週も今週も、季節が良いので、やっぱり外に出たくなる。
菜の花と桜がきれいだし、暖かくて気持ちいい。外で弁当をたべるとおいしい。
特に今日は、「さくら草まつり」。パンサー尾形さんや小島奈津子さんなどが来て盛り上げてくれるらしい。
これに行かない手はない。
(見に行ったけど、面白かった)

ということで、調査の作業は午前中と夜だけ。
花見でビールを飲んでしまうので、夜は調査に向かない。
結果的に午前中の数時間で調査する。

マイコンボードに接続しているドーターボード?(こっちのほうが大きいんだけど)の回路図がもうどこにしまっているかわからない。
それを探し出すのに手間がかかった。そして、回路図と実機を追いかけながら、故障している箇所を探す。
するとSDカードの電源制御の部分の抵抗の定数を間違えている場所が2箇所も見つかった。
今まで動いていたのが不思議なくらいだ。

正しい部品に交換して、試してみると正常に動作した。

ちょうど開発環境の入れ替えと故障が重なって問題が複雑に見えたが、要は18年前のミスが原因だった。
今まで発生しなかった現象が出るようになった原因は?
じつは先月の動作確認時に乱暴に扱って、どこかが短絡して変なことになった。
そのときに、どこかにダメージがあったのかもしれない。
それも細かいことは覚えていない。

ほぼほぼむき出しなので、ケースにいれるなり、もう少し安全に気を使おう。

2023年4月16日日曜日

UTF-8

昨日の記事で、Shift_JISの5C問題について書いた。
「だから、Shift_JISなんてダメダメだ!」なんていう人もいるだろう。
文字を「バイト」として扱うから問題になる。
文字を「文字」として扱えば、'\'記号か、2バイト文字の2バイト目なのかは区別がつく。
ようはそれだけだ。

以前は、組み込み開発では、メモリサイズの都合から基本的に2バイト文字に対応することはなかった。
しかし、マイコン内蔵メモリも増えてきた。ファイルシステムを扱うようになると、ファイル名に2バイト文字が含まれるため、しばしば2バイト文字の扱いが必要になることもある(2バイト文字を扱わないというソリューションもあると思うけど)。
そのため、十数年前から組み込み環境へマルチバイト文字を扱う関数群を移植することも増えた。

UTF-8は、C5問題のような問題はない。
UTF-8では、複数バイトで構成される文字のバイト値に、ASCII 7bit文字のバイト値が含まれないためだ。
そのため、5Cは常に'\'であり、文字を「文字」として扱わなくてもこの問題は生じない。

じゃ、何も考えなくていいのかというと、そうでもない。
例えば「泣き別れ問題」は、Shift_JIS同様存在する。
例えば、何らかの理由で文字列の一部を区切る必要があるとき、複数バイトで構成される文字の途中で区切るわけにはいかない。
UTF-8の1文字を構成するバイト列が泣き別れになると、Unicodeへデコードできなくなる。
そのため、任意のバイトでは区切れない。結局、文字を「文字」として扱う必要がある。

文字列を区切る理由の1つに、物理的な表示幅の制限がある。
プロポーショナルフォントの場合、文字毎に、場合によると文字の組み合わせや順序により、文字列の幅が変わる。
そのため、常に文字列として幅を調べなければならないため、1バイト文字もマルチバイト文字も関係ない。

厄介なのは端末アプリだ。
日本語対応端末は伝統的に、英数や半角カナは半角(1カラム)、漢字は全角(2カラム)で表示される。
Unicodeでは、半角、全角以外に、N(中立)、'A'(曖昧な幅)があり、これらの文字のカラム数はコンテキスト依存して変化する。
(Wikipediaの「東アジアの文字幅」参照)
すなわち「よくわからない」幅なのだ。
やや乱暴なソリューションとして、『「よくわからない」を使用しない』というのも考えられる。
しかし、Shift_JISでも存在していたギリシャ文字やキリル文字、一部の記号も、'A'に分類されている。
これらの文字を使わないとするのは、さすがに乱暴すぎるだろう。
コード体系もJIS漢字とは異なるため、これらの文字幅を自力で解決しなければならない。

20年以上前から、自家製FATファイルシステムドライバを実装していた。
当初はFAT12/16のみ対応だったけど、途中からFAT32に対応し、Long File Nameにも対応した。
Long File Nameにしっかり対応するとなると、Unicode - CP932(Shift_JIS)との相互変換が必要になる。
そのころ、UTF-8-cjk.txtという文書が入手可能だったのだので、その情報を元に、変換テーブルを作った。

なんと、そのファイルの末尾には、Unicode文字の文字幅の情報も付いていた。
この文字幅情報を使って、日本語UTF-8テキストファイルのTAB-Space変換を実装したりもした。
さらに、端末を用いた組み込みでのコマンドライン処理で、UTF-8文字を扱う際の、カラム数の算出にも使った。
今では自家製組み込みコマンドライン処理は、Shift_JISとUTF-8の両方に対応している(コンパイル時に決定)。

ところが、そのファイルはいつの間にかダウンロードできなくなっていた。
今は、EastAsianWidth.txtで文字幅の情報が得られる。

ここまでやれば、UTF-8での文字列の幅に対応できるようになる。
私は、Shift_JISであれ、UTF-8であれ、どっちでも同じように実用上問題なく扱えている。
(Shift_JISというか、CP932というか、には例外的なものが多く、Unicodeは今でも成長を続けているので、完璧ではないし、完璧と言えるものは無いだろう)

「UTF-8がいい」「Shift_JISなんてダメダメだ!」なんて言っている人、ちゃんと理解できているかな?

2023/04/23 追記:文字幅0の文字について 制御文字やウムラウト(日本語では濁点や半濁点)等、一部の文字は幅がない。
これらの文字幅も正しく扱うなら、UnicodeData.txt から情報を抽出する必要がある。
検索すればすぐに出てくる。とはいえ、ダウンロードして手元に置いておくほうがいいだろう。
メンテ等で時々ダウンロードできなくなる。

抽出方法については、UTF-8-cjk.txtに書いてあった。
抜粋すると、
% Character width according to Unicode 5.0.0.
% - Default width is 1.
% - Double-width characters have width 2; generated from
%        "grep '^[^;]*;[WF]' EastAsianWidth.txt"
%   and  "grep '^[^;]*;[^WF]' EastAsianWidth.txt"
% - Non-spacing characters have width 0; generated from PropList.txt or
%   "grep '^[^;]*;[^;]*;[^;]*;[^;]*;NSM;' UnicodeData.txt"
% - Format control characters have width 0; generated from
%   "grep '^[^;]*;[^;]*;Cf;' UnicodeData.txt"
% - Zero width characters have width 0; generated from
%   "grep '^[^;]*;ZERO WIDTH ' UnicodeData.txt"
実は、文字幅0の一部が文字幅2と被っている。
これもまた表示環境に依存する。

2023年4月15日土曜日

Shift_JIS

PC-98+MS-DOSの頃、文字列はShift_JISが当たり前だった。
私が考えるShift_JISの最大の利点は、半角文字は1バイト、全角文字は2バイトということだろう(一部例外を除く)。
これのどこが良いのかと言えば、バイト数が表示カラム数に一致するのだ。

しかしながら、'\'記号(5C)問題がある。
Shift_JISは2バイトで構成され、2バイト目は7bit文字のコードとかぶる部分がある。
そして、2バイト目が'\'記号(5C)になっている文字の場合、C言語のエスケープと見なされ、コンパイル時におかしな処理となる。

80年代、Lattice-CやMS-CでShift_JISで書かれたソースコンパイルしようとするとおかしなことになることがあった。
対策としては、2バイト目が'\'記号の文字の次に'\'記号を挿入するフィルタを実装して、make時にソースコードを変換してコンパイルする。
ちなみに、Turbo-C(後のBoarland C)では、Shift_JISを正しく扱っていた。
個人でプログラミングをする時は、Boarland C/C++ばかり使うようになっていった。

しかし、職場では住友電気のU-Stationを使っていた。
これはUnix機だったが、Shift_JISでソースコードを書いていたため、また'\'記号問題の対策(フィルタを実装して...)をする必要があった。

それから20年ぐらいだろうか。
そんな感じで開発環境が変わるたびに、毎度毎度同じ対策をし続けて、すっかり慣れていた。
そのため「UTF-8で文字の扱いが楽になる」という話を聞いた時、「Shift_JISで充分じゃん。」みたいに考えていた。

UTF-8では、'\'記号問題が無いという利点はあるが、なんと多くの全角文字が3バイトになるのだ。
さらには、半角カナも3バイトになる。
すなわち、文字のバイト数とカラム数が合わなくなる。
多くのprintf()の"%s"の「フィールド幅」や「精度」は、バイト数を指定するもので、カラム数ではない。
この記事のbookinfoの表示がズレズレになっているのも関係する。
実際には、EUC-JPの半角カナも2バイトだったため、このbookinfoは文字のカラム数を調整する機能も実装されている。
しかし、UTF-8には対応しておらず、ズレズレ表示になっている。
これはこれで、面倒な問題だ。

ちょっと話が脱線したが、Shift_JISでの問題は誰であれ自分なりの対処方法を持っていると思っていた。
しかし、こんなニュースを発見した。
キャラ名に「ソ」をいれるとバグる! 古参開発者「うっ……頭の中で何かが……」
記事の末尾にあるが、もはや「過去の話」なのかもしれない。

2023年2月19日日曜日

Type Generic Mathematics Header

仕事(組み込み)で数学的な処理が必要になった。
昔なら、整数演算して、関数の類はテーブル解決にしたりして、そして出来上がったコードは複雑怪奇で、メンテナス性が低く、作った本人もすっかり忘れて、ブラックボックスになっていく...というパターン。
最近は組み込み用のマイコンでも単精度ながら浮動小数点演算器が入っているものもある(Cortex-M4とか)。
そういうのなら、整数演算でなくても良い。メンテしやすいコードでも性能が出る。

C言語の実数型は一般的に、単精度と倍精度がある。80年代には拡張精度もあった。最近では4倍精度も使える。
処理速度や計算精度の最高のバランスを得ようとするとき、実数の型(精度)を入れ替えながら検証したいところだ。

四則演算だけなら、マクロやtypedefで変数の型を定義直すことで、一発で簡単に実数の型を変更できる。
しかし、算術ライブラリmath.h内の関数を切り替えるのは、少し面倒だ。
C言語には、C++で言うところのオーバーロードの機能が無いので、やや複雑なマクロを使って切り替えなければならない。
マクロは型を認識できないためだ(少なくとも昔ながらの標準的なCコンパイラでは)。

マクロはプリプロセッサが処理する。プリプロセッサはその名の通り、前処理をするもので字面を置き換えることしかできない。
型が意味を持つのは、パーサーやコードジェネレータの段階だ。
マクロは型を知らないので、マクロで型を何にするかを示して、その値を使って型と関数の置き換えを定義するような2段階構造にしなければならなかった。

math.hが提供する関数全てにマクロを用意するのは面倒なので、リストファイルを参照(gccなら"objdump -t")し、使用しているシンボルを確認して、使っている関数のみ定義する。
すなわち、その場しのぎのやっつけ実装。
そんな感じなので、いつもやり直すことになる。

今回、移植しようとしたコードがatan()をそのまま使っていた。
atan()と言えば、この記事に示すように、そのまま使うべきではない。
y/xが無限にならない事が解っているならそれでも良いが、不要な疑念を避けるためにもatan2()に置き換えるべきだ。

それは解っているのだが、自称「おっちょこちょいリンピック日本代表」のこの私が5年前の記事をハッキリ憶えてるわけがない。
80年代からの自分の解決方法 "atanXY()" が邪魔をして、"atan2()"の名前が出てこない。プログラミングは、名前が重要だ。

思い出そうとしても無理なので、/usr/include の下の全ヘッダファイルに対して、文字列'atan'を検索してみた。
大量の一致がみつかり、絞り込みをしている時に、"/usr/include/tgmath.h" を発見した。
"/usr/include" 直下にあることから、かなり標準的なもののようだが、これは一体なんだ?
と思って、調べ始めた。

"tgmath" = "Type Generic Mathematics" の意味で、日本語にするなら「型一般化算術」とでもいうのかな?
"math.h"の代わりに、このヘッダを使うだけで、引数の型から自動的に呼び出す関数を切り替えてくれるすぐれものだ。

ISO C標準 11 (C11)で追加され、以降標準的に使えるらしい。
型を知るためにコンパイラが強化されていて、2段階の定義は要らない。
(逆に古いコンパイラではヘッダファイルだけあっても正しく動かないだろう)

引数の型を自動認識するのは強力で、イミディエート値でも、typedefし直した型でも、正しく解決してくれる。
例えば、以下のように、atan2()を呼び出すと仮定してみよう。
void func( MY_REAL x, MY_REAL y )
{
    MY_REAL angle = atan2( y, x ); 
    :
    : (省略)
    :
このとき、MY_REAL が
typedef float  MY_REAL;
のようになっていると、生成されるコードは、atan2f()を呼び出す。また、
typedef long double  MY_REAL;
のようになっていると、生成されるコードは、atan2l()を呼び出す。
生成された.oファイルを、"objdump -t"で見てみると良い。

この問題、世界中の人がうんざりしていたのかな。
速度と精度のトレードオフのために、実装した後で、単精度、倍精度、4倍精度を切り替えて評価を行うこともあるだろう。
その作業が楽になる。

整数(固定小数点)演算に置き換えるのは、計算の桁数や精度を考え、検証しなければならなかった。
この作業は実装結果だけみても、苦労が理解されにくく、評価されない。
作業量を見積もっても「なんで?」みたいになることが多い。
うっかりBugでも出そうものなら、「簡単なのに何やってるの?」みたいな。
さらに、入出力の精度や桁数に強く依存しており、出来上がったコードは奇妙奇天烈複雑怪奇魑魅魍魎渦巻く地雷原であり、再利用が困難になる。
それが単精度浮動小数点が使えるだけで、ものすごく楽になる。言うならば「素人でも実装できる」。
メンテナス性も向上するだろう。

単精度、倍精度の切り替えは、マクロで解決できる内容ではあるが、それすら用意する必要が無くなる。
いい時代になったものだ。

2022年11月18日金曜日

Ubuntu22.04のいいところ

関連記事:
VirtualBoxで、Ubuntu22.04を試しに使っている。
過去に何度かその記事を書いている。

まだ本格的な使用ではないので、細かい部分に気が付かない。
特に困らないなら、気づくチャンスもない。

先日、Meldに文字コード選択機能が付いていることに気がついた。
コメントを日本語で書く時もある。
さらに、例えば自家製iTRONやFATファイルシステムは、当初Shift_JISで書いていた。
そのため、古いソースコードと比較する時、Shift_JIS, EUC-JP, UTF-8がごちゃまぜになることがある。
全部文字コードを変換してから比較すれば良いのだが、急いでいる時はそのちょっとの手間が「イライラ」につながる。

また、文字コードの自動認識に失敗することもあり、その場合、認識ミスしている部分を、二分岐探索で絞り込んで特定する。
これはかなり手間がかかる。

イライラが少しでも減るのなら、価値があるだろう。
だんだんと、22.04が良いと感じるようになってきた。

番外編:
パッケージを見ると、Meldは'Architecture: all'で、Python3で書かれている。
高級言語で書かれているなら、抽象化度も高そうだ。
「18.04でもうごくんじゃね?」と思って、Ubuntu packagesから、22.04のmeldのバイナリパッケージをダウンロードして、"dpkg -i <package-file.deb>"でインストールしたら、あっさり動いた。
ただし、この方法はめちゃくちゃで、全く保証できない。やるなら、自己責任で。

2022年4月9日土曜日

カーミット

Kermit(カーミット)といえば、Sesame street のキャラクタを思い出す人も多いだろう。
やたらと早口で喋るカエル(びっき)のKermit だ。時々リポーターの仕事をする。
かく言う私も、Kermitは大好きなキャラクタだ。

プログラマ、特に組み込み系の人にとって、Kermitと言えば、もう一つある。
通信ソフトウエアパッケージの Kermit だ。
私はLinux上での組み込みマイコン開発時、シリアルポートの操作のために使っている。
Kermitは膨大な機能があるため、man-pageを見ても、便利なヘルプ機能を使っても、どこに何が書いてあるかわからない。私も全てを使ったことがあるわけではない。
そのせいで敷居が高いと感じる人も少なくはないだろう。
しかし、シリアルポートでの最低限の使用に限れば、けっして難しくはない。
今回は、UbuntuでのKermitでのシリアルポートの操作について書く。

インストールしよう

UbuntuでKermitを使うなら、aptでckermitパッケージをインストールするのが手っ取り早い。
こんな感じ:
$ apt-get install ckermit

起動しよう

インストールができたら、起動しよう。
シリアルポート使うなら、起動オプションでポートのスペシャルファイルを指定して起動するのが良い。
$ kermit -l /dev/ttyUSB0
上記は、USBシリアルデバイスの0番を指定している。レガシーポートのあるPCなら、COM1は"/dev/ttyS0"、COM2は"/dev/ttyS1"を指定する。
起動後のコマンドモードでも指定できるのだが、コマンドオプションの方が短く済むし、後で示す.kermrcを使った設定のためにも、起動時に指定したほうが良い。

通信設定しよう

Kermitは起動直後、インタラクティブコマンドモードになる。
起動時に、ポートを指定していないなら、"set line"コマンドで指定する。
(/home/foo/) C-Kermit>set line /dev/ttyUSB0
起動時に入力したほうが多少短い。起動時に'-l'で指定したほうが、楽だろう。

起動時にポートを指定していれば、必要な設定は、
  • フォーマット(キャラクタ長、パリティ、ストップビット長)
  • ボーレート
  • フロー制御
  • キャリア監視
キャラクチャ長、パリティ、ストップビット長をまとめて、フォーマットと書いているが、これらをまとめてセットするのが便利なためだ。
これらは、"set serial"でまとめてセットする。
たとえば、よく使われる(であろう)「キャラクタ8bit, パリティなし、1ストップビット」なら、以下のように指定する。
(/home/foo/) C-Kermit>set serial 8n1
"8n1"の3文字のそれぞれが、キャラクタ長('8'=8bit)、パリティビット('n'=none parity)、ストップビット長('1'=1stop)を示している。
使える組み合わせは、"set serial"と入力後に'?'を打つとダダっと表示される。
このコマンドに限らず、いろいろな場面で'?'を打つと、インタラクティブなヘルプが表示される。憶えておくといい。
キャラクタ長は 7, 8 が使える。
パリティは、キャラクタ長が8bitの場合、'n'=なし、'e'=偶数パリティ、'o'=奇数パリティから選べる。7bitの場合、それに加えて、'm'=マーク、's'=スペースも選べる。
ストップビット長は、1, 2 が使える。
それぞれ個別に指定もできるのだが、この1発入力表現がとにかく便利だ。

ボーレートは、"set speed"で指定する。
115.2Kbpsなら、
(/home/foo/) C-Kermit>set speed 115200
原因不明ながら、フォーマット指定後にボーレートを指定しないと、フォーマットが無視されることがある。
そのため、後からボーレートをセットしたほうが良い。

フロー制御は、"set flow-control"で指定する。
フロー制御無しなら、
(/home/foo/) C-Kermit>set flow-control none
俗に言うハードウエアフロー制御(RTS/CTS)なら"rts/cts"を、ソフトウエアフロー制御(XON/XOFF)なら"xon/xoff"を指定する。
ここでも忘れてしまった時は'?'だ。

最後の「キャリア監視」は、ピンとこないだろう。
KermitはシリアルポートのCD(Carrier Detect)をデフォルトで監視しており、これが落ちていると接続状態にならない。
使用するクロスケーブルや、開発機のハードウエア構成にもよるのだが、CDがオープンの場合も多いだろう。
そういう時は、"set carrier-watch"を使って、
(/home/foo/) C-Kermit>set carrier-watch off
として、監視を止める。

接続・切断しよう

これで設定ができたので、接続してみよう。
接続には、"connect"コマンドを使うのだが、これは短縮形の"c"でいい。
(/home/foo/) C-Kermit>c
接続すると、以下のようになる。
(/home/foo/) C-Kermit>c
Connecting to /dev/ttyUSB0, speed 57600
 Escape character: Ctrl-\ (ASCII 28, FS): enabled
Type the escape character followed by C to get back,
or followed by ? to see other options.
----------------------------------------------------

英語が読める人はもうおわかりと思うが、インタラクティブコマンドモードに戻るには、CTRL-\押下後、'C'を入力する。
'C'の代わりに'?'を入力すると、ヘルプが表示される。
'C'の他によく使うものは、'U'と'Q'かな。
'U'は接続を切断し、コマンドモードに戻る。
'Q'は切断して、Kermitも終了する。

CTRL-\ + 'C'で戻っても、シリアルデバイスはopen状態にある。
設定を変えて接続し直す場合、'U'で終えるか、'C'で戻った後で明示的に'close'コマンドを引数無しで使う。

ファイルを垂れ流し送信しよう

マイコンでの開発をしていると、まとまったデータをマイコン側へ送りたいときがある。
Motorola-SやIntel-hex形式のファイルをROMライタへ送りたい時(いつの時代だよ!)などだ。
ROMライタに限らず、マイコン内蔵Flashの内容を書き換えるコード(ローダ)をマイコンに用意しておいて、JTAGなしでプログラムを書き換える機能を用意することもあるだろう(最近は無いのかな…)。
その場合は、インタラクティブコマンドモードで、"transmit"コマンドを使う。
(/home/foo/) C-Kermit>transmit ファイル名
これで送信できるのだが…。やってみればわかるけど、とても遅い。
実は、kermitは、行毎に待ちを挟む。
受信側に配慮したやさしい動作であるが、大量のデータをやり取りすることを目的としている場合、受信側もバリバリにチューンしているだろう。最近のRISCマイコンなら、十数MHzのマイコンでも、115.2kbpsを取りこぼすのは稀だ。
Flash消去・書き換えにはさすがに時間がかかるが、それは毎行に待ちを挟むと言うよりも、消去ブロック単位や書き込み単位で待ちを挟むべきであり、その場合はフロー制御を使うべきだ。

ちょっと脱線したけど、transmitの際に、'/NOWAIT'オプションを指定すると、待ちを除去できる。
(/home/foo/) C-Kermit>transmit /NOWAIT ファイル名
ボーレートが高いほど、待ちの影響も大きい。
バリバリチューンを無駄にしないためにも、憶えておこう。
その他にも、transmitコマンドのオプションがある。'?'で表示されるので見ておくと良い。

受信内容を記録しよう

マイコンの動作確認やDebugの際に、シリアルポートを使うことも多い。
この場合、行った作業を記録しておきたい。
インタラクティブコマンドモードで、"log session"を使う。
(/home/foo/) C-Kermit>log session ログファイル名
ログファイル記録されるのは、プレーンテキストであり、エスケープシーケンスは記録されない事に注意せよ。

BSキーとDELキーの扱い

TeraTermを使っていると、BSキーを押した時、0x08(CTRL-H)が送信される。
BSのコードは0x08なので、この動作は正しい。

しかし、Linuxの端末では、BSキーを押すとDEL(0x7F)が押されたものとして動作する。
Kermitは、通信アプリであり厳密には端末エミュレータではない。端末としての振る舞いは、それを起動した端末に依存する。
そのため、KermitもBSキーが押された場合、DEL(0x7F)を送信することになる。
すなわち、TeraTermと違う動作になる。

このねじれ現象は歴史的な経緯により生じたものだ。
そもそもDELキーはBSキーの動作をしていた時期があり、その後現在と同じような動作になるのだがBSとは違うので、0x7Fなどというコード(それ以外の制御文字はすべて0x20未満)を持つ。
この件、かなり古いもので、知る人ぞ知る情報だったのだが、最近はネット上に転がっているので検索して読んでみるのもいいだろう。
2022年4月15日 追記: DELが0x7Fなのは、紙テープを使っていた頃の影響

改めて、Wikipediaの「削除文字」の部分を読むと、全ビット穴が空いている(7bit文字なら0x7F)部分を読み飛ばすという動作になっており、それを使って修正液のように文字を消すので、0x7Fだそうだ。
それとは別に、BSとDELの混乱も書かれている。とにかく、ごちゃごちゃしている。

そして、Linuxではこのねじれに対して、VT100のキー配列とLinus氏の判断により、BSキーはDEL(0x7F)を発行する。
詳しくは、Linuxの文書、"The Linux Keyboard and console HOWTO"の、"5.2 How to tell Linux what code to generate when a key is pressed"のあたりにチラッと書いてある。

色々対処方法はあるが、私はKermitでコードを変換するようにしている。
以下のようにすると、BSキーがBS(0x08)を送信するようになる。
(/home/foo/) C-Kermit>set key \127 \8
これで、他のアプリに影響を与えず、TeraTermと相互運用しやすくなる。

自動設定しよう

Kermitのインタラクティブコマンドモードのコマンドを、${HOME}/.kermrcに書いておくこと、起動時にそれが実行され、設定される。
私の.kermrcは以下のようになっている。
$ cat .kermrc 
set line /dev/ttyUSB0
set speed 57600
set serial 8N1
set flow-control none
set carrier-watch off
set key \127 \8
ボーレートが57.6kbpsになっているのは、例のマイコンボードを使ってるためだ。
こうしておけば起動すればすぐに使えるし、コマンドを忘れたとしてもこの.kermrcを見れば思い出すだろう。

エスケープシーケンスとキー入力

ここからは、kermitと言うよりもマイコン側の話だ。
エスケープシーケンスは、端末ごとに微妙に違っており、マイコン側で全てに対応するのは無駄だろう。
基本的にはANSIエスケープシーケンスに対応すればいい。
しかしながら、PC-98(だから、いつの時代だよ!)で、デバッグしていたコードを移植する場合、カーソルON/OFFのコードが違う。
意味PC-98ANSI
カーソルON ESC [>5lESC [?25h
カーソルOFFESC [>5hESC [?25l
また、LinuxではDELキーは(0x7FがBSなので)、エスケープシーケンスで表現される。
さらにこのへんにも書いてあるが、 キー入力のスキームはいくつも存在し、残念なことに多くの端末は異なるスキームのシーケンスを混ぜているので、ホストソフトウエアはあらゆるスキームを使った入力シーケンスが扱えなければならないのだ。
すなわち、DELキーは、0x7Fのみならず、エスケープシーケンス表現でも扱えるように実装しておくべきだ。

一通り使えるであろう方法を示した。

kermitは、Windows以前の'80年代から存在しているので、「あー、あれね。」と思う人もいるだろう。
昔からパソコンをいじっている人の中には特に。

厳密には端末エミュレータではないが、端末上で動作し、その端末の機能を利用することで、端末エミュレータのように使うこともできる。

しかしながら、日本ではPC-9801のDOS上で、CCT-98など高速動作する端末エミュレータが使われることが増え、さらにWindows後ではTeraTermに移行していった。

私は、'90年代末頃からPC Linuxを使うようになった。
そのころからGCCが日立SuperHを扱えるようになったので、それを使うようになった。
端末エミュレータとしては、当初はWindowsのTeraTermを使っていたものの、「今でもKermitがあるんじゃね?」と思って探すとすぐに見つかった。
そのため、またLinux上でKermitを使うようになった。
使わなくなってから数年経過していたため、使い方をかなり忘れていたが、使い始めればすぐに思い出していった。

Kermitをまた使うようになってから20年ぐらいになるだろう。途中、使わなかった時期があったことも最近では忘れていた。

ずっと使っているし、昔から存在しているので誰でも知っていると思っていた。
若手はKermitを知らない場合もあるが、若者は経験がまだ少ないのだからそういう事もあると、思っていた。
最近になって、色々見てみると意外にみんなKermitを知らないようだ。

考えてみれば'80年代では広く使われていたが、現在と比べ'80年代は、PCを使う人そのものが少なかっただろう。
Windows以降、大量の人の流入により、Kermitを知っている人は少数派になっているようだ。

いつの間にやら、世界は変わっていた。

2022年3月11日金曜日

古いマイコンボード

このマイコンボードを、未だに時々いじって遊んでいる。
自家製ファイルシステムドライバに、いろいろな機能を実装して拡張して、だんだんと充実してきた。
ファイルシステムドライバがあれば、ファイル操作は何でもできそうに思うかもしれないが、実際にはU/Iを実装しないと使えない。
ファイルシステムドライバより、U/Iは簡単と考える人も多い。
1つ1つのルーチンは簡単かもしれないが、量がある。便利にすればするほど、どんどんコード量が増える。
実際に全てを書いてみれば、量こそが問題だと感じるようになる。

今日、とうとうマイコン内蔵Flashがいっぱいになった。
最初の頃にH/Wの動作確認用に書いていたコード等(結構な量がある)を削って、実装したかった機能を実装した。
満足な結果が得られた。

それは良かったが、そろそろ限界だ。
遊びで使うマイコンボードを、新たに選ぶかな?

2020年8月23日日曜日

いまさらの電子辞書 #2

前回からの続き。
catdumpでCATALOGSファイルを作成するには、まず、テキストファイル形式のカタログファイルを作成する必要がある。
テキストファイルのカタログファイルは、このページに参考例がある。
とはいえ、ただの例なので、自分の辞書CDがどのような構成になっているのかを知る必要がある。

普通にCDを見てみると、ファイルの構成はこのようになっている。
$ tree xxx
xxx
├── README.FreeBSD
├── README.LINUX
├── TRANS.TBL
└── chujiten
    ├── TRANS.TBL
    ├── data
    │   ├── TRANS.TBL
    │   └── honmon
    └── gaiji
        ├── TRANS.TBL
        ├── gai16f00
        ├── gai16h00
        ├── gai24f00
        ├── gai24h00
        ├── gai30f00
        ├── gai30h00
        ├── gai48f00
        └── gai48h00

3 directories, 15 files
(xxxはCDのマウントポイント)

"chujiten/data/honmon"が辞書データそのものだろう。
前回ビルドしたepwutil内にあるbookinfoで見てみると以下のように表示された。
$ bookinfo dict/chujiten/data/honmon 
; 電子ブック/EPWING 書籍管理情報 (generated by bookinfo v1.0)

対象書籍ファイル = dict/chujiten/data/honmon
情報所在ブロック = 1
書籍構成要素数 = 13
予備領域1(書籍種別/保護情報) = 2000H
  書籍種別: 英和辞典
  保護情報: 表示許可, 印刷許可, テキスト引用許可, 図版引用許可,
            カラー図版引用禁止, 動画引用禁止, 大量引用禁止
INDEX情報の取扱法 = 02H (INDEX情報は有効)

 ID 名称                                 先頭 サイズ INDEX情報 予備3/4
*00 本文                              34398  44941           00/0000
*91 前方一致表記形インデックス         4244   4236 .a....... 00/0000
*71 後方一致表記形インデックス            8   4236 .a....... 00/0000
*02 著作権表示                         7      1           00/0000
*01 メニュー                        26941      1           00/0000
 FF 複合検索                          ***   1969           00/0000
 FF 複合検索                          ***  33305           00/0000
 FF 複合検索                          ***  16261           00/0000
 FF 複合検索                          ***      5           00/0000
 FF 複合検索                          ***     23           00/0000
*05 前方一致表記形見出し        26946   3726           00/0000
*07 後方一致表記形見出し        30672   3726           00/0000
 D8 PCM音声                           79339  83391           00/0000

表示方法の初期値情報:
  有効無効フラグ = 01H (初期値情報は有効)
  一覧表表示 = 00H (直接本文表示)
  本文表示法 = 00H (検索結果を項目単位に連続して表示)

書籍ファイルサイズ = 318MB
マーク部分のサイズ = 119MB
文字のサイズと文字のバイト数が合わないので、ズレズレ表示になっているが、読めなくはない。
CDが壊れているかもと思っていたが、辞書データは使えるようだ。

このページの例を参考に、上記情報とディレクトリ構成を元に、catalogs.txtという名前で、以下のようなテキストファイルを作成した。
[Catalog]
FileName   = catalogs
Type       = EPWING1
Books      = 1

[Book]
BookType   = 2000
Title      = "新英和・和英中辞典"
Directory  = "chujiten"
InfoBlock  = 0001
ZenGaiji   = "gai16f00gai24f00gai30f00gai48f00"
HanGaiji   = "gai16h00gai24h00gai30h00gai48h00"
このテキストファイルは、UTF-8で書いている。
ソースコードやMakefileを読めば解るが、Unix系でのcatdumpは、EUC-JPを期待した作りになっている(WindowsではShift_JIS)。
そのためEUC−JPに変換しなければならない。
以下のようにして、変換する。
$ nkf -e catalogs.txt > catalogs.euc
一応、確認。
$ nkf -g catalogs.euc
EUC-JP
よしよし。

これを使ってバイナリのCATALOGSを作成する。
$ catdump -u catalogs.euc CATALOGS

これでカタログファイルはできたけど、相手はCDROMだ。書き込むことはできない。
壊れているとはいえ勝手にコピーしてしまうのも問題だ。
こういうときは、overlayマウント(unionマウントとも言う)してしまおう。

いくつか方法はあるが、以前からoverlayfsを使っているので、それを使う。
デフォルトではoverlayfs用のカーネルモジュールが読み込まれていないので、モジュールを読み込む。
$ sudo modprobe overlay 
つぎに、読み書きディレクトリ、作業ディレクトリ、マウントポイントを用意する。
$ mkdir rw wk dict
そして、overlayマウントする(xxxにCDがマウントされているものとする)。
$ sudo mount -t overlay overlay dict/ -o lowerdir=xxx,upperdir=rw,workdir=wk
これで、dict/を通してxxx/をアクセスでき、書き込みもできるようになる。
dict/へ作成したCATALOGSファイルをコピーする。
$ cp CATALOGS dict/
これで、多くの辞書アプリでアクセス可能になる。
試しに、eblookで見てみよう。以下のようにしてインストールする。
$ sudo apt install eblook
インストール後、overlayマウントしたマウントポイントを指定して起動する。
以下は、英単語"english"をひいたときの様子。
$ eblook dict/
eblook> list
 1. chujiten	新英和・和英中辞典
eblook> select 1
eblook> search english
 1. 37402:692	English
eblook> content english
Invalid entry number: english
eblook> content 1
<gaiji=ha123>En・glish /<gaiji=ha137><gaiji=ha22f>gl<gaiji=ha228><gaiji=ha22d>/→<gaiji=za339>
<gaiji=za34e><gaiji=za323>
1 イングランドの; 《俗に》 イギリスの,英国の.
2 イングランド人の; 《俗に》 イギリス人の,英国人の.
3 英語の.<reference>→<gaiji=za33a></reference=53513:1184>
<gaiji=za34e><gaiji=za321>
1 <gaiji=za32f> 英語.<reference>→<gaiji=za33a></reference=53513:1284>
2 [the 〜; 複数扱い]
a イングランド人; 《俗に》 イギリス人,英国人 (cf. the Scots,the Welsh,the Irish).
b 英軍.
<gaiji=ha126>古期英語 ‘of the race of Angles' の意; ⇒ENGLAND<gaiji=ha127>
<prev><reference><gaiji=ha123>En・gland /<gaiji=ha137><gaiji=ha22f>gl<gaiji=ha226>nd/→<gaiji=za339></reference=37401:1782></prev>
<next><reference><gaiji=ha155>nglish br<gaiji=ha168>akfast</reference=37402:1602></next>
eblook> quit
外字が<gaiji=xxxxx>のように表示されて読みにくいが、動作しているのはわかる。
GUIアプリのebviewで表示すると、発音記号その他外字も表示される。

外字の大きさが、文字と合っていないのが少し気になる…。
メニューから、ツール->オプションで、「設定項目」から「外部プログラム」を選んで、「音声再生プログラム」を"aplay %f"にすると、音声も再生できるようになる。

これでネットにつながらなくても、電子辞書が使える。

いまさらの電子辞書#1

何故?
先日、CDを整理していたら、Stingの"Ten Summoner's Tales"のCDの中から、電子辞書のCDが出てきた。
宮崎生まれの私にとっては、Stingは SEAGAIA のこけら落としに来てくれた特別なアーチストだ。

十数年前はネット上に電子辞書は少なかったし、PCもネットも遅く、広告の表示がひどい足かせとなり、実用的ではなかった。紙の辞書も使っていたが、電子辞書も売られるようになっていたので、それを使うようになった。
しかし...、何故StingのCDのケースに入っていたのだろう?

その当時、Linuxで、この辞書CDを使うのは一苦労だった事を、だんだん思い出した。
今は、ネット上に電子辞書がたくさんある。それどころか、電子翻訳も発達している。さらに、電子辞書はスマホアプリが中心となり、電子辞書CDや、専用電子辞書は下火になっている。
そういう時代だからこそ、もう一度この辞書CDを扱えるようにしてみよう。
無駄なのは解っている。しかし、遊びとはそういうものだ。実用性や採算性を追求するのは「仕事」だ。

CDの表面にはEPWINGと書かれているので、EPWING形式なのだろうが、どういうわけかCATALOGSファイルが無い。
壊れているのだろうか?
ネット上で見ても、そんなCDの話はない。さらにいろいろ探しているとCATALOGSファイルの作り方が見つかった。
1999年(20世紀末!)ごろ太田純氏により開発されたcatdumpというアプリを使うらしい。
Debian系では、epwutilというパッケージに入っている。このパッケージはDebian Jessieを最後に、以降のDebianでは無くなっている。
しょうがないので、Jessie の epwutil のソースをダウンロードしてきた(以下の3ファイル)。
http://deb.debian.org/debian/pool/main/e/epwutil/epwutil_1.1-8.1.dsc.
http://deb.debian.org/debian/pool/main/e/epwutil/epwutil_1.1.orig.tar.gz
http://deb.debian.org/debian/pool/main/e/epwutil/epwutil_1.1-8.1.debian.tar.gz

dchでローカルでバージョンを上げておいて(存在しないので必要ないかも)、Ubuntu 18.04でビルドしようとするが、やはりそのままでは通らない。
こんな感じでエラーになる。
 : (省略)
dh_clean: Compatibility levels before 5 are no longer supported (level 4 requested)
debian/rules:27: recipe for target 'clean' failed
make: *** [clean] Error 25
dpkg-buildpackage: error: fakeroot debian/rules clean subprocess returned exit status 2
debian/compatの値が4になっているためだ。このへんを見ると、最近では10が普通らしい。
Lennyの頃から組み込みでdebianを使っていたが、たしかJessieの頃にMulti-arch対応等でパッケージ管理システムが大きく変わり後方互換を完全には維持できなくなった(と記憶している)。それ以降、どのような変更があったのかは見ていなかったが、古いものとの互換性を維持するために手間をかけるよりも、より良い形に変えていくために手間をかけたほうが良い。以降も色々あったのだろう。
epwutilは単純なMakefile形式だし、.dscを読んでもビルド依存物はdevhelperだけだ。
単純に数字を大きくしても、ビルドできるだろう。とりあえず、10 にする。

この状態で、"dpkg-buildpackage -us -uc" を行うと、Warningがでるが一応ビルドできた。
とはいえ、日本語文字コードがEUC-JPになっており、ビルドされたバイナリのエラーメッセージやヘルプがバケバケだ。20年以上前のものなのでしょうがない。
現代なら、UTF-8にするべきだろう。以下のようにして、UTF-8に置き換える。
$ nkf -w80 -Lu -x --in-place=.BAK *.c *.h *.man
ついでに、Warningを直しておこう。以下を行う。
  • catdump.c と squeeze.cで、unistd.h を #include する。
  • bookinfo.cの、126行付近、「情報所在ブロック」の書式化文字列の"%d"を"%ld"にする。
  • bookinfo.cの、ローカル関数hex()のプロトタイプが無いので、プロトタイプを書く。(他と合わせて、昔の書き方にしたほうが良いだろう)
  • squeeze.c内のlog()がビルドイン関数と名前がかぶっているので、wrlog()に置き換える。
    "sed -e 's/\<log(/wrlog(/g' -i.BAK squeeze.c"
この状態で、そのままビルドしようとしても、以下のようなエラーになる。
 : (省略)
dpkg-source: info: you can integrate the local changes with dpkg-source --commit
dpkg-source: error: aborting due to unexpected upstream changes, see /tmp/epwutil_1.1-8.1ubuntu1.diff.HodWKA
dpkg-buildpackage: error: dpkg-source -b epwutil-1.1 subprocess returned exit status 2
書かれているように、"dpkg-source --commit"を実行する。
言われるがままパッチ名を付けたり、サマリや説明を書いたりする。

この状態で、"dpkg-buildpackage -us -uc"をすると、新しいバイナリパッケージやソースパッケージができる。
 : (省略)
dpkg-deb: building package 'epwutil' in '../epwutil_1.1-8.1ubuntu1_amd64.deb'.
dpkg-deb: building package 'epwutil-dbgsym' in 'debian/.debhelper/scratch-space/build-epwutil/epwutil-dbgsym_1.1-8.1ubuntu1_amd64.deb'.
	Renaming epwutil-dbgsym_1.1-8.1ubuntu1_amd64.deb to epwutil-dbgsym_1.1-8.1ubuntu1_amd64.ddeb
 dpkg-genbuildinfo
 dpkg-genchanges  >../epwutil_1.1-8.1ubuntu1_amd64.changes
dpkg-genchanges: info: not including original source code in upload
 dpkg-source --after-build epwutil-1.1
dpkg-buildpackage: info: binary and diff upload (original source NOT included)

$ ls ../
epwutil-1.1
epwutil-dbgsym_1.1-8.1ubuntu1_amd64.ddeb
epwutil_1.1-8.1.debian.tar.gz
epwutil_1.1-8.1.dsc
epwutil_1.1-8.1ubuntu1.debian.tar.xz
epwutil_1.1-8.1ubuntu1.dsc
epwutil_1.1-8.1ubuntu1_amd64.buildinfo
epwutil_1.1-8.1ubuntu1_amd64.changes
epwutil_1.1-8.1ubuntu1_amd64.deb
epwutil_1.1.orig.tar.gz

このepwutil_1.1-8.1ubuntu1_amd64.debをインストールする。
sudo dpkg -i ../epwutil_1.1-8.1ubuntu1_amd64.deb

"catdump -h" を実行すると以下のように表示される。
$ catdump -h
電子ブック/EPWING カタログ/テキスト変換 Ver.1.1 (1999/01/13)
    Written by Junn Ohta (ohta@src.ricoh.co.jp), Public Domain.

使用法: catdump [-g] [-d] [-u <テキストファイル>] <カタログファイル>

オプション:
    -g: EBG 専用モードにする
    -d: カタログファイルを標準出力にダンプする
    -u: テキストファイルをカタログファイルに変換する
ギリギリ入手できたけど、こういうアプリは無くなっていくのだろうな…。

とりあえず、epwutilのビルドまで。
次は、実際にCATALOGSファイルを作成する。
つづく。

2019年10月26日土曜日

馬鹿の道

おそらく誰も使っていないのだろう。
数年前から、シンプルで軽いテキストエディタとして、medit(http://mooedit.sourceforge.net/)を使っている。
Ubuntuのパッケージで言うとこれだ。 以前から、時々日本語入力の最中に入力の確定ができなくなることがあったのだが、6月にUbuntu18.04に入れ替えてから、特にその現象が出やすくなった。

あえて18.04のインストールを1年待つのは、不具合の多くが解決されているだろうと期待しての行動だ。
しかし、それでも解決されていない。
おそらく解決を望む人がいないか、気にする人がいないのだろう。

こんな記事は誰も読まないだろうと思うが、meditで日本語入力ができるように改造(修正?)する方法を紹介する。
現象の原因は、キー入力ルーチンにgtk_text_view_im_context_filter_keypress ()が足りていないのだ。
meditのソースパッケージ内の場所で言うと、medit-1.2.0/moo/mooedit/mootextview-input.c 内の以下の部分、赤文字で示した部分を追加する。
:
1243     moo_accel_translate_event (widget, event, &keyval, &mods);
1244 
1245     if (keyval == GDK_KP_Enter || keyval == GDK_Return)
1246     {
1247         if (gtk_text_view_im_context_filter_keypress(text_view,event))
1248             return TRUE;
1249         gtk_text_buffer_begin_user_action (buffer);
1250         handled = handle_enter (view, event);
1251         gtk_text_buffer_end_user_action (buffer);
1252     }
:
ソース修正後に、dchとdpkg-buildpackageでバイナリパッケージを作って、インストールしなおせばいい。
数カ月使っていて、明確な問題はない(BSキーに対しても行ったほうが良いかもと思うが)。
とはいえ、本家の活動もほとんど無いようだ。いまどきgtk+2だし、仕方ない。
放置され続けていたので、すでに「使い物にならない!」って逃げ出してしまった人も多いだろう。
仮にこれを修正できたとしても、喜ぶ人はいないのだろうな。

NemoでRabbitVCSを使ったり、fbtermでw3mを使ったり、本流ではないことばかりしている。
しかし、これこそ、馬鹿の道。

2019年2月11日月曜日

動画圧縮し直しスクリプトを直した

ここで使った動画は、このカメラ HC-V480MSを使って撮影して、以前書いたshell-scriptなどを使って、作成した。
ひさしぶりにこのscriptを使ったので、すっかり忘れていたので、吐くメッセージをよく読みながら作業した。
そのとき、ビットレート8192Kbpsで圧縮されているのに気がついた。

このビットレートは、動画のサイズや秒あたりのフレーム数(フレームレート)が不明なときに使う、いわばデフォルト値だ。
実際にそれら情報をffprobeで読みだして、レートを手計算すると、全然違う値になる。
すなわち、うまく処理できずにデフォルト値で動作してしまっているのだ。

よく見ると、カメラで作ったH.264動画のときのffprobeの出力フォーマットが微妙に違うので、サイズやフレームレートを正しく抽出できていなかった。
というわけで、もう少し上手く扱えるようにscriptを直した。
#!/bin/bash
SRCFILE=$1
DSTFILE=$2
if [ -z "$DSTFILE" ]; then
  BASENAME=$(basename $SRCFILE)
  DSTFILE=${BASENAME%.*}.mp4
fi
#---------------------------------------------------------------#
#  Extract informations from source video.   #
#---------------------------------------------------------------#
CREATION_TIME=""
ENCODER=""
videoFormat=""

while read s; do
    s=$(echo $s | sed -e "s/^ *//g");

    if [[ "$s" == creation_time\ :* ]]; then
        if [ -n "$CREATION_TIME" ]; then
     echo "creation_time duplicated. Discard candidate $CREATION_TIME"
        fi
 CREATION_TIME=$( echo $s | sed -e "s/^[ \t]*creation_time[ \t]\+:[ \t]\+//g" )
    fi

    if [[ "$s" == encoder\ :* ]]; then
 ENCODER=$( echo $s | sed -e "s/^[ \t]*encoder[ \t]\+:[ \t]\+//g" )
    fi

    if [[ "$s" == Stream\ *:\ Video:* ]]; then
 videoFormat=$( echo $s | sed -e "s/^[ \t]*Stream \+#.*: \+Video: \+//g" )
    fi
done < <( ffprobe ${SRCFILE} 2>&1 )


echo creation_time=$CREATION_TIME, ENCODER=$ENCODER, videoFormat=$videoFormat

#exit 0
#CREATION_TIME=$(ffprobe $SRCFILE 2>&1 | grep "creation_time" | head -n 1 | sed -e "s/[ \t]*creation_time[ \t]*:[ \t]*//g")

echo SRC: $SRCFILE
echo DST: $DSTFILE
if [ -z "$CREATION_TIME" ]; then
    echo "No \"creation_time\" metadata. use ctime."
    CREATION_TIME=$(stat "$SRCFILE" | grep "^Change:" | head -n 1 | sed -e "s/^Change:[ \t]*//g" | sed -e "s/\.[0-9]* +.*$//g")
fi
echo CERATION_TIME: \""$CREATION_TIME"\"

#---------------------------------------------------------------#
# Auto bitrate       #
#   H.264には、エンコード品質を表すBPP(Bit Per Pixel)という #
#   指標がある。      #
#   BPPを用いて、ビットレートは以下のように計算される。  #
#        #
#                       BPP x fps x width x height  #
#     bitrate[kbps] = ------------------------------  #
#                                  1000                         #
#                                                               #
#   H.264のBPPは、動画の動きの速さと、品質で以下のように #
#   決まっている。      #
#          |               品  質                         #
#                |---------+---------+---------+--------        #
#                |   最高  |   高    |   中    |   低           #
#     -----------+---------+---------+---------+--------        #
#       高  速   |  0.225  |  0.175  |  0.125  |  0.100         #
#       中  速   |  0.200  |  0.150  |  0.100  |  0.075         #
#       低  速   |  0.175  |  0.125  |  0.075  |  0.050         #
#                                                               #
#           高速: スポーツまたはミュージックビデオ  #
#           中速: 映画      #
#           低速: ナレータ(ニュースや演説など)                  #
#                                                               #
#---------------------------------------------------------------#
#videoFormat=$( ffprobe ${SRCFILE} 2>&1 | \
#               grep "Stream \+#[0-9]:[0-9]: \+Video: \+" | \
#        sed -e "s/^ \+Stream \+#[0-9]:[0-9]: \+Video: \+//g" );

bpp="0.225"
width=$(echo ${videoFormat}  | sed -e 's/^.*, \+\([0-9]\+\)x[0-9]\+ .*$/\1/g' )
height=$(echo ${videoFormat} | sed -e 's/^.*, \+[0-9]\+x\([0-9]\+\) .*$/\1/g' )
fps=$(echo ${videoFormat}    | sed -e 's/^.*, \+\([0-9.]\+\) \+fps,.*$/\1/g' )

if [ -z "$width" -o -z "$height" -o -z "$fps" ]; then
    echo "cann't get information for bitrate. Use 8192kbps"
    vBitrate=8192k
else 
    echo "bpp=$bpp, width=$width, height=$height, fps=$fps"
    vBitrate=$(echo "scale=0; $bpp * $width * $height * $fps / 1000" | bc)"k"
fi

echo "bitrate=${vBitrate}"
#exit 0

#---------------------------------------------------------------#
# Convert video format      #
#---------------------------------------------------------------#
ffmpeg -i $SRCFILE -nostdin -y -acodec libvo_aacenc -profile:a aac_low -ab 128k -ar 44100 -ac 2 -vcodec libx264 -profile:v main -b:v ${vBitrate} -pix_fmt yuv420p -f mp4 -metadata "creation_time"="$CREATION_TIME" -metadata "encoder"="$ENCODER" $DSTFILE

# No audio
#ffmpeg -i $SRCFILE -nostdin -vcodec libx264 -profile:v main -b:v ${vBitrate} -pix_fmt yuv420p -f mp4 -metadata "creation_time"="$CREATION_TIME" -metadata "encoder"="$ENCODER" -map 0:0 $DSTFILE

touch -r ${SRCFILE} ${DSTFILE}

2019/07/04 一部の動画で、画面のサイズ取得が正しく動作しない。
新しいやつは、これ。

2018年4月8日日曜日

cairo_arc_to() #3

cairo_arc_to()#1 cairo_arc_to()#2
先週記事にした自家製cairo_arc_to()を使って、飛び出す道具箱のアニメーションを作ってみた。
多少手抜きをして、直線と円弧のみで構成した。
データを作るのが大変だったが、それさえできればcairoで簡単に描画できる。
アニメーションgifで動画にしたが、ffmpegで動画にしたほうが良かったかな。

2018年4月1日日曜日

cairo_arc_to() #2

cairo_arc_to() #1

一応動作確認をした、自家製版コードを以下に示す。
cairo_arc_to()はすでに存在しているようなので、自分で作ったものはpseudo_arc_to()とした。
実際に、円弧ではなくベジエ曲線なので、擬似円弧だ。
#include <gtk/gtk.h>
#include <glib.h>
#include <math.h>

/*----------------------------------------------*/
/*   Get difference of angle between angles.    */
/* Arguments:                                   */
/*    st:  start angle. -π 〜 +π              */
/*    ed:  end angle.   -π 〜 +π              */
/*    dir: direction.                           */
/*          0: Normal direction.                */
/*          1: Reverse direction.               */
/* Return value:                                */
/*   Difference of angle.                       */
/*   if dir is zero, result will be positive    */
/*   value, otherwise, result will be negative. */
/*----------------------------------------------*/
static double get_angle_difference( double st, double ed, int dir )
{
    double angle = ed -st;

    if( dir == 0 ) {
        if( angle < 0.0 ) {
            angle += (M_PI *2.0);
        }
    }
    else {
        if( 0.0 < angle ) {
            angle -= (M_PI *2.0);
        }
    }
    return( angle );
}

/*--------------------------------------------------------------*/
/*      pseudo arc to                                           */
/* Arguments:                                                   */
/*    cr:  a cairo context                                      */
/*    edX: the X coordinate of the end of the new line          */
/*    edY: the Y coordinate of the end of the new line          */
/*    ox:  X position of the center of the arc                  */
/*    oy:  Y position of the center of the arc                  */
/*    dir: direction of drawing arc                             */
/*           0: Normal direction                                */
/*           1: Reverse direction                               */
/*                                                              */
/*--------------------------------------------------------------*/
void pseudo_arc_to( cairo_t *cr, 
        double edX, double edY, // End point
        double oX,  double oY,  // Origin of arc
        int    dirFlag )        // Direction of rotation
                                //  0: Normal direction
                                //  1: Counter direction
{
    double stX, stY;    // Start point.
    double st,  ed;     // Angle of start point and end point.
    double r;           // Radius of arc
    double angle;       // Angle from st to ed. 

    int    n, i;
    double l;           // Distance to Ctrl point for each divided arc.
    double prvX, prvY;

    // Prepare each value.
    cairo_get_current_point( cr, &stX, &stY );  // Get start point.
    st    = atan2( (stY -oY), (stX -oX) );      // Angle of start point.
    ed    = atan2( (edY -oY), (edX -oX) );      // Angle of end point.
    r     = sqrt( ((edX -oX) *(edX -oX)) +((edY -oY) *(edY -oY)) ); // Radius of arc
    angle = get_angle_difference( st, ed, dirFlag );    // Angle from st to ed.

    // Draw divided arc.
    n    = ceil( fabs(angle / (M_PI*0.5)) );    // Number of division.
    l    = fabs( r *4.0 *tan((angle/n)/4.0) /3.0 );   // Distance to Ctrl point.
    prvX = stX;
    prvY = stY;
    for( i=1; i<=n; i++ ) {
        double c1X, c1Y;                // Ctrl1 point.
        double c2X, c2Y;                // Ctrl2 point.
        double a = st +(angle *i /n);
        double nowX = r *cos(a) +oX;
        double nowY = r *sin(a) +oY;

        if( i == n ) {                  // Last divided arc.
            // Only last part of arcs will be exactly drawn
            // with given end point.
            nowX = edX;
            nowY = edY;
        }

        if( dirFlag == 0 ) {
            c1X = prvX + (l * (-(prvY -oY)/r)); // cos(θ+π/2) = -sin(θ)
            c1Y = prvY + (l * ( (prvX -oX)/r)); // sin(θ+π/2) =  cos(θ)
            c2X = nowX + (l * ( (nowY -oY)/r)); // cos(θ-π/2) =  sin(θ)
            c2Y = nowY + (l * (-(nowX -oX)/r)); // sin(θ-π/2) = -cos(θ)
        }
        else {
            c1X = prvX + (l * ( (prvY -oY)/r)); // cos(θ-π/2) =  sin(θ)
            c1Y = prvY + (l * (-(prvX -oX)/r)); // sin(θ-π/2) = -cos(θ)
            c2X = nowX + (l * (-(nowY -oY)/r)); // cos(θ+π/2) = -sin(θ)
            c2Y = nowY + (l * ( (nowX -oX)/r)); // sin(θ+π/2) =  cos(θ)
        }

        cairo_curve_to( cr, c1X, c1Y, c2X, c2Y, nowX, nowY );
        prvX = nowX;
        prvY = nowY;
    }

    // 半径は、原点と終点の距離から計算される。
    // 回転方向は解りにくい。
    // 数学的には左回り(CCW:時計と逆)が順方向であるが、コンピュータの座標系は
    // Y軸が反転しているため、右回り(CW:時計方向)が順方向になる。
    // 回転方向は、表示装置の座標系のとり方に依存するもので、計算や処理に
    // 依存するものではない。
}
コメントにも書いてあるが、回転方向には注意が必要だ。
多くの処理系、表示デバイスでは、下方向がY軸の増加になり、時計回りが順方向になる。
この向きは、cairo_arc()の描画方向に一致している。

実装はしたものの、迷っている部分がある。
始点と終点が一致している場合、全く書かないのが良いのか?全周を描くのが良いのか?
これについては、描画方向も考慮するのが良いかもしれない。
現在は、一番シンプルな形で実装している。

2018年3月24日土曜日

atan2()なんて知らなかった

1980年台からコンピュータをいじっている。
C言語に触れたのは専門学校へ行っている頃からなので、1987年頃からだ。もう30年になるのか。

昨日書いた記事のプログラムを書くために、逆正接関数atan()を使っている。
atan()に限らず、三角関数の中でtan()系列は、無限が出てくる時があるため、使用には注意が必要だ。Bugの原因にもなるので、ちらっと見かけただけでもなんか嫌な気分になる。
またatan()は、y/xの計算した値を引数に与えるため、第一象限と第四象限しかまともに計算できない。
というわけで、昔から以下のようなラッパを組んで、y/xではなく、(x,y)で計算するようにしていた。
static double atanXYabs( double x, double y )
{
    double absX = fabs(x);
    double absY = fabs(y);

    if( absX < absY ) {
        return( M_PI/2 - atan(absX/absY) );
    }
    return( atan(absY/absX) );
}
double atanXY( double x, double y )
{
    double angle = atanXYabs(x,y);

    if( 0.0 <= y ) { 
        if( 0.0 <= x ) {
            return( angle );    // 第一象限
        }
        return( M_PI - angle ); // 第二象限
    }

    if( 0.0 <= x ) {
        return( -angle );       // 第四象限
    }
    return( angle - M_PI );     // 第三象限
}
昔はM_PIも無かったりして、 '80年台に使っていたものとは微妙に違うがだいたい同じ。

「いつもいつも面倒だな。だからtan()系は嫌いなんだ」と思っていたが、他の人も困っているのでは?と思って、調べてみたら atan2() という関数を発見。
引数の順序は違うが、ほぼ同じ関数だ。

atan2()はいつ頃からあったのだろう?
自力で問題を解決してしまうと、それ以降困らないので、わざわざ調べない。
インターネットが全く普及していない'80年台ごろだったので、何を調べるのも大変だった。

とはいえ、なんと馬鹿なんだろう。
atan2()で組み直した。

2018年3月23日金曜日

cairo_arc_to() #1

以前、Borland C++ Builderで作ったコンタープロットのプログラムがある。先日、NASを整理している時に発掘した。
ひさしぶりにいじろうと思ったが、ウチにはBuilderどころかWindowsも無い。
というわけで、g++ & gtk+ に移植しようと思ったが、gtk+は2010年頃に少し勉強してほったらかしにしていた。
その当時購入した本を本棚から発掘して見てみると、メモが細かく書いてある。何でもかんでも興味本位で手を出すが、やり過ぎて頭の中がごちゃごちゃに混乱し、1ヶ月もすると忘れてしまう。いや、忘れているわけではなく、ぐちゃぐちゃになってしまって、正しい物が何かわからなくなるのだ。
どっちにしても、もう一度勉強し直す必要があるのだが、体の中には概念が残っているし、勉強していた時のメモがあれば、頭にすっと入ってくる。
思い出した事で、思い出しやすくなるというのもあるかもしれないが、記憶もしっかりしてくるような気がする。

gtk+は、cairoでベクタグラフィックを扱うことができる。
この辺は2010年頃には全く勉強していなかった部分だ。コンター図を書くのに使ってみようと思い調べ始めた。
自家製コンタープロットでは、直線しか使っていない。そのため直線と多角形だけ調べれば良いのだが、ついでなので円弧やベジエ曲線についても調べ始めた。

これが脱線の始まりだった。

円弧はcairo_arc()で描画するのだが、これは時計方向しか円弧を描けない。
そのため、多角形の一部に凸向きの円弧と凹向きの円弧を同時に含むことができないと思った。
(この時点ではcairo_arc_negative()を知らなかった) しかし、ベジエ曲線が使えるのだから、円弧をベジエ曲線で表現すればいいと思った。
そもそも円弧はROM-BASICの頃から反比例補完などで表現されきた。それらと同じことだ。
そうと決まれば善は急げで、いろいろ調べた。良い記事を見つけた。

ベジェ曲線の円の近似で90度以外でもよいが180度以上はよくない

上記ページの中ほどに、任意の角度の円弧をベジエ曲線で表現する(誤差はあるが)時の制御点の距離を求める式がある。
k=3/4 tan(θ/4) がそれだ。自分で計算しようとして行き詰まっていたので助かった。

これを利用して、今日の午前中 cairo_arc_to()のような関数を作った。
cairo_line_to()のように終点を指定して円弧を書くのだが、円弧のためには中心と円弧の向き(右回り/左回り)も指定する。
また、90度以上の円弧をベジエ曲線で表現すると誤差が大きくなるようなので、90度以上の場合いくつかのベジエ曲線に分割するようにもした。

簡単な動作確認をして満足したところで、「このような機能は誰でも欲しくなるよな。同じように誰か作っているんじゃない?」と思って、探したら発見した。

[cairo] arc_to again

私が実装したものと多少違っているがやはり存在しているようだ。
さらに、cairo_arc_negative()を発見した。これを使って逆向きに円弧を書くことができるだろう。逆向きにも円弧が書ければ、凹凸両方の円弧を持つパスをつなぐことができる。

またまた無駄なプログラムを書いてしまった。いろいろ勉強になったから良いか。

いつも脱線して、脇道にどんどん入っていくので、頭の混乱につながっていく。
とはいえ、やりかかったのだから、最後までやろう。
もう少し細かい動作確認と、Brush upをしておこう。

つづき。
cairo_arc_to() #2

2017年10月22日日曜日

LibreOffice Draw でアニメーション作製#5

LibreOffice Draw でアニメーション作製
LibreOffice Draw でアニメーション作製#2
LibreOffice Draw でアニメーション作製#3
LibreOffice Draw でアニメーション作製#4

以前、LibreOffice Draw でアニメーション作製#2で、ダイアログで値を指定できるように、などと書いておいてほったらかしだった。
台風21号の接近で外は大雨なので、それをやることにした。
(ちなみに昨日は西伊豆の井田でダイビングをしていた)
まず、Drawでダイアログを作るには、[ツール(T)]→[マクロ(M)]→[ダイアログの管理(D)...]をクリックして、「LibreOffice Basicマクロの管理」ダイアログを開き、「新規作成」ボタンをクリックする。
「新しいダイアログ」ダイアログが表示されるので、適当に名前をつけて、[OK]ボタンを押す。
とりあえず、"PageSizeDialogDef"という名前にした。

そうすると、ダイアログエディタが現れる。
このへんのダイアログの説明を読んでから、適当に部品を配置して、適当に設定して...

こんな感じにする。
そしてハンドラを終了時に入力データを読みだしたり、幅の値の書き換えハンドラで高さの値を更新するようにしたりする。
なんという手抜きな説明だろう!
そんなんじゃ、全然わからないよ!!って、なるだろう。

というわけで、LibreOffice Basicのソースと、ダイアログのエクスポートファイルをダウンロードできるようにしておく。
Basicのソース: SavePagesAsBMP.bas
ダイアログのエクスポート: PageSizeDialogDef.xdl

実は、どうやればソースコード等を取り出せるのか、全然知らなかった。
作るだけなら、全く不要な操作だからだ。
しかし、1台のPCだけなら取り出し方なんか要らないが、デスクトップPCとノートPCを使っていると、もう一方のPCでも同じ機能を使いたくなるし、Backupする必要もある。
というわけで、Uploadした。
まだまだ手抜きだけど。

2017年10月20日金曜日

LibreOffice Draw でアニメーション作製 #4

LibreOffice Draw でアニメーション作製
LibreOffice Draw でアニメーション作製#2
LibreOffice Draw でアニメーション作製#3
LibreOffice Draw でアニメーション作製#4
LibreOffice Draw でアニメーション作製#5
今日も雨だ。
またもや、How to fold IKEA poncho. IKEAレインポンチョのたたみ方。で作ったアニメーションのコマを増やしてみた。

前回、コマ数を増やしてもわかりやすさはあまり変わらない。ストップモーションのほうがわかりやすいと書いた。
よく考えてみたら、アニメーションは動くばかりではなく、止めることもできる。
途中にタメを作るようにすれば、ストップモーションのようにすることができ、わかりやすさを損なわない。

途中のコマを書き足したり、途中にタメを入れたりして、アニメーション直した。
遊びで、最後の折り目が勢いよく反転する感じも入れてみた。
コミカルな動きになった。

今になれば簡単なことだが、滞りなくなめらかに動かすことばかり考えていて、止めることを考えてなかった。
何事も経験だな。

2017年10月7日土曜日

LibreOffice Draw でアニメーション作製 #3

LibreOffice Draw でアニメーション作製
LibreOffice Draw でアニメーション作製#2
LibreOffice Draw でアニメーション作製#3
LibreOffice Draw でアニメーション作製#4
LibreOffice Draw でアニメーション作製#5

ソースコードを見ていたら、直接GIFアニメーションをエクスポートするコードもあるようだ。
でも呼び出し方がわからないし、Drawではないのかもしれない。
無駄骨だったかもしれないが、いつものことだ。いろいろ勉強になったし、目的は達成できたのだから、良かった事にしよう。

それはさておき、せっかくLibreOfficeのマクロを書いたし、今日は雨で外に出れないので、 How to fold IKEA poncho. IKEAレインポンチョのたたみ方。で作ったアニメーションのコマを増やしてみた。
(雨ならば、ポンチョを着て外出しろって?)

以下がその結果。


コマ数はおよそ2倍になっている。
コマ数を増やしても、あまりわかりやすさは変わらない感じがする。
むしろストップモーション的な方が、1コマずつしっかりと見ることができて、解りやすい。

動画は動画でわかりやすいが、隠れて見えない部分もあるし、理解の前にどんどん進んでしまうという欠点もある。
絵は、見えない部分を表現することもできるが、動きがわかりにくい。
アニメーションは、その中間だ。

中間的な存在が、良いとこ取りになればいいが、悪いとこ取りになってしまうこともある。
コマ数を増やしたのは、悪いとこ取りになってしまったかもしれない。

最初の選択が、意外に良かったようだ。「この形だけは表現しないといけない」と思う絵を描いて、その途中を無理なくつなぐような必要最低限の絵だけを挿入していた。
過剰な情報は、混乱の元になる。技術的にコマ数が増やせるとしても、良いことかどうかは別だ。
技術に踊らされてはいけない。

2017年10月6日金曜日

LibreOffice Draw でアニメーション作製 #2

先週のLibreOffice Draw でアニメーション作製のつづき。
手動で.bmpにExportする場合、解像度を指定できる。
しかし、先週作ったマクロでは解像度を指定できず、デフォルト値の37ピクセル/cm(と表示されているが、正確には96dpi)で出力されることになる。
これを変更したい。しかし、ネット上で探すと情報がまとまっておらず、錯綜しており、混乱の極みだった。

まず、storeToURL()メソッドに渡すcom.sun.star.beans.PropertyValue型の.Name = "FilterData"でフィルタ固有の設定の入った配列を渡す。この配列もまたcom.sun.star.beans.PropertyValueで、いわば入れ子構造になっている。

ここまでは、先週の段階で解っていたところだ。
"FilterData"について、真剣に調べるとはまりそうな気がしたので、先週の段階では無視した。また、最初の1歩目は、あまり凝らずにさっさと閉じて、まず形にするのが私のやり方だ。

というわけで、今週の通勤時に、スマホで検索して調べた。
やっと見つけた記事が、OpenOffice-Basic GraphicExportFilter documentation?。
まさにこれだと思ったが、コードの断片であり、説明はやはり無い。
下の方のリンクの文書を読んでみたが、"FilterData"の説明は書かれていない。
数日間、通勤時間の朝夕ほとんどをつぎ込んでも、結局"FilterData"について、まとめられた文書はどこにも見当たらなかった(自分の検索の方法が悪いのかもしれないが...)。

しょうが無いので、ソースコードを読んでみることにした。
https://github.com/LibreOfficeで、スマホでもソースコードを参照できる。
現在利用しているLibreOfficeのバージョンは5.1なので、そのGraphicFilterのソースコードを見てみる。
"FilterData"設定で見ているのは、PixelWidthとPixelHeightだけだ。
派生クラスなどでは他の値も使っているかもしれないが、基本的にはこれだけのようだ。
これらの値が、aRenderer.renderToGraphic()に渡されることになる。

サイズの指定がこれだけだとすれば、ドキュメントの縦横幅をピクセル数で指定しているだけと想像できる。
すなわち、dpiやpixel/cmのような物理量と関係する値ではない。そのページを縦横何ピクセルで画像にするかというだけだ。

試しに、先週のコードを以下のように改造して、実行してみた。
sub SavePagesAsBMP
    dim doc  as Object
    dim controller as Object
    dim pages  as Object
    dim prevCurrentPage as Object
    dim args(2) as new com.sun.star.beans.PropertyValue
    dim filterData (2) As New com.sun.star.beans.PropertyValue

    dim dirDialog as Object
    dim ucb as Object
    dim directory as String

    GlobalScope.BasicLibraries.LoadLibrary("Tools")
    doc = ThisComponent

    'MsgBox doc.DBG_properties
    'MsgBox doc.DBG_Methods
    'MsgBox doc.drawPages.DBG_properties
    'MsgBox doc.drawPages.DBG_Methods

    '------------------------------------------------------------
    ' Select directory by Directory Selection dialog
    '------------------------------------------------------------
    dirDialog = CreateUnoService( "com.sun.star.ui.dialogs.FolderPicker" )
    ucb = createUnoService("com.sun.star.ucb.SimpleFileAccess")

    directory = DirectoryNameoutofPath(doc.getURL(),"/")
    if ucb.Exists( directory ) then
        dirDialog.SetDisplayDirectory( directory )
    end if

    if dirDialog.Execute() <> 1 then
        exit sub
    end if

    directory = dirDialog.getDirectory
 
    '--------------------------------
    ' Save each page as .BMP
    '--------------------------------
    controller = doc.CurrentController
    pages = doc.drawPages

    args(0).Name = "FilterName"
    args(0).Value = "draw_bmp_Export"
    args(1).Name = "FilterData"
        filterData(0).Name =  "PixelWidth"
        filterData(0).Value = 900
        filterData(1).Name = "PixelHeight"
        filterData(1).Value = 800
    args(1).Value = filterData

    prevCurrentPage = controller.getCurrentPage ' preserve current page

    For i=0 to pages.Count -1
        controller.setCurrentPage( pages(i) )
        'url = directory + "/" + controller.getCurrentPage.Name +".bmp"
        url = directory + "/" + right( "0000000" + i, 8) +".bmp" 
        'MsgBox url
        doc.storeToURL( url, args )
    Next i

    controller.setCurrentPage( prevCurrentPage ) ' restore current page

end sub
ちゃんと、900x800で画像が保存できた。

一応動いたとはいえ、もう少しソースコードを追いかけてみたほうがよさそうだ。
また、独自のダイアログを作ることもできそうなので、次はそれを使って値を指定するようにしてみよう。

LibreOffice Draw でアニメーション作製
LibreOffice Draw でアニメーション作製#2
LibreOffice Draw でアニメーション作製#3
LibreOffice Draw でアニメーション作製#4
LibreOffice Draw でアニメーション作製#5
2017/10/20 リンクが間違っていた。張り直した。