2017年9月17日日曜日

C#でエンコーディング変換

 基本的にC#はエンコーディングの事を考えなくても良いのだけど、今回諸事情でUInt16で表現された文字を別のエンコードにする必要があったので、試してみた。

 今回はShift JISとUnicodeの変換を行った。

static UInt16 ShiftJis2Unicode(UInt16 ShiftJisChar)
{
 byte[] buff2 = Encoding.Unicode.GetBytes(
  new[] {
   Encoding.GetEncoding("Shift_JIS").GetChars(
    new [] {
     (byte)(ShiftJisChar / 256),
     (byte)(ShiftJisChar % 256),
    }
   )[0]
  });

 return ((UInt16)(buff2[0] | buff2[1] << 8));
}

static UInt16 Unicode2ShiftJis(UInt16 UnicodeChar)
{
 byte[] buff2 = Encoding.GetEncoding("Shift_JIS").GetBytes(
  new[] {
   Encoding.Unicode.GetChars(
    new[] {
     (byte)(UnicodeChar % 256),
     (byte)(UnicodeChar / 256),
    }
   )[0]
  });

 return ((UInt16)(buff2[0] << 8 | buff2[1]));
}

 Shift JISはASCIIと互換性があり、また半角カナも1バイトで表現できる。が、今回はマルチバイト文字しか考えていないので、シングルバイト文字を入れると問題が起きるはず。
 Unicodeは文字にかかわらず2バイトで表現するので、たぶん動くはず。

 UnicodeとUTF-8とかの変換は、また別の問題なので今回は扱わない。

 ちなみにC#のcharはUnicodeらしく、intに入れるとUnicodeのインデックスになる。intをcharにキャストするとUnicodeで文字化される。今回はEncoding.Unicode.GetCharsとか使ったけど、Unicode対ならcharのキャストでも良いかも。

 他のエンコーディングとの変換も同じようにすれば動くはずだけど、それぞれバイトオーダーや固定長・可変長みたいなバリエーションがあるので、ちょっと面倒。

 今回は文字単位での変換だったけど、文字列の変換だともっと簡単にできるみたいね(未確認)。

2017年9月14日木曜日

PNGファイルシグネチャ

 ソース:PNG 仕様書:原理 12.12.PNGファイルシグネチャ
 ソースの文章を理解できるなら、以下の文章は読まなくていい。

 
 PNGファイルには最初の1バイトが0x89で、次の7バイトが"PNG\r\n\x1A\n"となっている。これの理由について。

 まず最初の0x89だが、これは2進数だと10001001になる。当時、上位1bitを捨てて、7bitで転送するシステムが有ったため、これを通したPNGデータは最初の1バイトが0x09となり、データが破損していることが確認できる。
 また、ASCIIコードは0x00から0x7Fの範囲を使用するので、ソフトウェアによっては明らかにASCIIコードでないファイルとして、テキストデータと誤認されることを防ぐことができる。とはいえ、他の文字コードでは1文字目が何らかの文字として誤認されることもある。例えば、Shift JISでは"\x89P"の一連の文字(0x8950)は’臼’(ウス)の文字が割り当てられている。そのため、日本向けWindowsのメモ帳でPNGファイルを開くと、先頭は"臼NG"で始まる。

 次の"PNG"の3文字は、誤ってPNGファイルをテキストエディタで開いてしまった際に、"PNG"の文字列を表示することにより、PNGデータだと人間に通知することができる。ただし、上記の通り、システムのエンコードによっては正しくPNGと表示されない場合もある。

 次の"\r\n"は、ファイルシステムによって"\r\n"を"\n"に変換されたことを検出することができる。またその後の"\n"も、ファイルシステムによって"\n"が"\r\n"に変換されたことを検出することができる。
 これは、OSによって異なる改行コードの違いをファイルシステムで吸収する際に発生する問題を検出することができる。例えば、C言語のfopenでテキストファイルとして開くと、\r\nと\nの変換が行われる(バイナリファイルとして開けば変換は行われない)。

 その間にある、'\x1A'は、一部のファイルシステムではEnd of Fileを意味し、この文字を検出したシステム(テキストエディタ)はそれ以降のファイルの読み込みを終了する場合がある。

 PNGのファイルシグネチャはこの8バイトだが、場合によっては続けて4バイト程度を読み出す場合もある。
 この4バイトはIHDRチャンクのサイズで、32bitのネットワークバイトオーダーで13が記録されている。これは0x00 0x00 0x00 0x0Dの4バイトだが、システムによってはヌル文字(0x00)を省略する場合があり、その場合は00 00 00 0Dを正しく読めないため、データが破損していることが確認できる。


 以上、PNGのファイルシグネチャの話でした。
 古いフォーマットを見てると当時のシステムの特徴とか出てきて面白いよね。

2017年9月13日水曜日

PNG

 C#で64bit(16bit x 4ch)のPNGを吐き出すライブラリを作っていました。といっても、まだライブラリと言うほどまとまってはいませんが。

 気付いた点とか。

・フィルタについて。
 フィルタは1バイト毎に行われる。1色16bitでも、フィルタは1バイト単位で行われる。なので8bitでも16bitでも同じ関数を使いまわすことができる(1行の幅を、画素数ではなく、バイト数で持たせる)。

追記:2017/09/15
 フィルタの計算量は、None<Sub<Up<Average<Paethという感じか。Noneは計算量ゼロ。SubはUpより少し少ない(ソースコード量とトレードオフ)。
 フィルタは各行毎に設定されるので、forで行を回し、その中でフィルタに応じてforで列を回すことになる。
 Subはfor[y]の中で直接for[x-1]を回せるので、計算量が最小(Noneを除くと)。
 Upはfor[y]の中でy==0をチェックし、trueならNoneとして扱う。それ以外はfor[x]で回すので、ifがあること、1列多く回すこと、等からSubよりも計算量が若干多い。
 Averageはfor[y] for[x]の中で2個のifがあり、また四則演算も多いので遅くなる。ただ、四則演算を除けば、分岐予測が間違わない限りはかなり早いと思う。分岐が必用なのはx==0とy==0のときだけなので、これもソースコード量とトレードオフかな。
 Paethもfor[y] for[x]のループだが、中にifが6個ある。内3個はx==0、y==0、x==y==0なので、予測分岐がミスらなければ早く、ソースコード量とトレードオフになる。一方、残りの3個はピクセル値によって変化するので、それなりの確率で予測が外れることになる(if-else if-else ifなので、1回目で通ればハザードは少なくて済む。が、コレばっかりは画像次第)。
 予測分岐については、CPUとかコンパイラの判断になるだろうから、こればっかりはどうしようもない気がする。

 フィルタリングは、エンコードは減算、デコードは加算で、計算式自体は共通。ただしループの向きが違うので、そこはエンコードとデコードで作り分ける必要がある。
 ループの中でテーブルに対して関数の加算・減算を行う、という流れが楽でいい。ただし計算内容で必要な情報が多いので、関数を呼ぶとスタック操作が発生する。計算式自体はかなり少ないので、インライン展開を期待するか、あるいはべた書きで同じコードを2箇所に書いてしまうか。

・圧縮について。
 PNGの圧縮はDeflateのラッパーであるzlibを使う。が、なぜかDeflateでもpaint.netで読めた(詳細未確認)。


 とりあえず、64bitRGBAのPNGを保存することができるようになった。IDATの圧縮がなんとなくわかってきたので、多倍長独自チャンクも似たような感じで作れそう。

 PNGにはテキストを保存できるチャンクがあるので、中間データとして使う際は、ここにファイルの内容を入れると良さそう。
 基本的にNULL文字終端っぽいので、PNGで使われるテキスト2組の後ろに、バイナリデータを入れられないかと考えている。ソフトウェアで中間データを扱う際に、続くデータチャンクの内容を知らせるためのモノ。
 バイナリデータを入れるのがマズければ、テキスト形式である程度種類を識別させて、続くデータチャンクにバイナリデータを入れても良い。

 以前に考えた時は、IDATが8bit、idBTが16bit、idCTが32bit、のようにしようと思っていたが、コレだとPNGのフォーマットにより8bitデータが1個しか持てない。これはちょっとまずいと思うので、もうちょっと違う形式にする必要がありそう。ま、idATとかでも良いのだけど。
 あと、データチャンクの符号をどうするか。ベクトルデータとかを扱うなら、当然符号なしはマズイわけで、とはいえすべてを符号ありとして扱うと画像データを扱う時にマズイわけで。3文字目の大文字小文字で分けてもいいけど、いちおう予約されてる以上はいじらずに置いたほうが良い気がする。
 例えばidATがu8bit、idBTがs8bit、idCTがu16bit、idDTがs16bit、みたいな感じだとどうだろうか。この形式でも最大65536bitまでの値を持てるから、30年先くらいは安心して使えると思う。


 とりあえず、中間データをPNGに埋め込むライブラリは、PNG画像に中間データを注入する、あるいは中間データを抽出するような動きになると思う。もしくは、画像はBitmapかImageにして扱うとか。画像処理ライブラリまで作り込むのは面倒なので。
 ま、そのあたりは追々。

追記:2017/09/14
 よく考えれば、というか、普通に考えれば、PNGパケットからデータを抜き出す際に、画像を別に取り出す必要性は皆無なんだよな。だってC#のImageなりBitmapで開けば画像は取り出せる。ということは、PNGパケットからデータを取り出す部分と、PNGパケットにデータを埋め込む処理を作ればいい。

C#でDeflateをMemoryStreamで

 C#でMemoryStreamを使って(ファイル操作をせずに)Deflateの圧縮をしようと思ったら、結構穴にハマったのでメモ。

byte[] src = new byte[1024],dst;

for(int i = 0; i < src.Length; i++)
{
 src[i] = (byte)(i % 256);
}

using (MemoryStream ms = new MemoryStream())
using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress, true))
{
 ds.Write(src, 0, src.Length);
 ds.Dispose();
 ms.Position = 0;

 dst = new byte[ms.Length];
 ms.Read(dst, 0, dst.Length);
}

src = dst;

using (MemoryStream msSrc = new MemoryStream())
using (MemoryStream msDst = new MemoryStream())
using (DeflateStream ds = new DeflateStream(msSrc, CompressionMode.Decompress))
{
 msSrc.Write(src, 0, src.Length);
 msSrc.Position = 0;

 ds.CopyTo(msDst);
 msDst.Position = 0;

 dst = new byte[msDst.Length];
 msDst.Read(dst, 0, dst.Length);
}

 圧縮と展開、両方のソース。

 圧縮はCompressionMode.Compressで行う。
 DeflateStreamのコンストラクタで第3引数を省略するとfalseになるが、この場合は圧縮が終わった時点で第1引数に指定したStreamをCloseする。この動作は、FileStreamを使ったときには問題にならないが、MemoryStreamを使った場合は、あとから読めなくなる。なので、trueを指定してStreamがCloseされないようにする。
 また、DeflateStreamは、StreamがCloseされた際に圧縮を行うらしい。ということで、DeflateStream.Dispose()で強制的に閉じておく。
 そして、StreamのPositionはStreamの最後に移動しているので、あらかじめ0に戻しておく。
 最後に、Stream.Lengthを使って圧縮後の容量を確認し、Stream.Readでメモリに戻す。

 展開はCompressionMode.Decompressで行う。
 この際は読み込みStreamは閉じられてもかまわないので、第3引数は省略可能。
 まずはSrcStreamにデータをコピーする。DeflateStreamは入力StreamのPositionを戻さないので、自分でSrcStream.Position=0にしておく。
 そしてDeflateStream.CopyToでDstStreamにデータを移動する。CopyToやReadでDeflateStreamを読み出した際に、SrcStreamからの展開を行うらしい。
 またCopyToで読み出すと、Positionも移動するので、ここでも0にしておく。
 最後に、DstStreamのLengthで容量を確認し、Readでメモリに戻す。


 とりあえず、上記のサンプルデータだとちゃんと展開はできることを確認している。

 最初、入力に乱数を使っていて、全く圧縮されなく悩んだ。Deflateはハフマン符号圧縮と違い、過去のメッセージと同じデータを探してくるので、乱数のような同じメッセージが複数回出てこないようなデータに対しては圧縮率が極めて低い。
 今回のサンプルのような、同じメッセージが複数回出てくるようなデータに対しては、かなり圧縮率が良い。例えば、今回のサンプルデータは0x00から0xFFまでの256バイトのメッセージが4回出てくる。最初の1回は圧縮できないが、残りの3回は「nバイト前から長さlバイトと同じ」という情報があれば良いので、その分を圧縮できる。今回は1024バイトが280バイトまで圧縮された。

/*
 そう考えると、PNGの圧縮効率ってあんまり高くない気がする。ピクセルデータを直接ハフマン符号圧縮したほうが効率良さそう。当時のコンピュータの処理能力とか、そういう制限だったんだろうなぁ。
 PNGは浮動小数点演算とかいらない分、JPEGよりは組み込み向けといえるのか? どうだろう。JPEGはブロック単体で展開できるはずだけど、PNGを展開するには最大で圧縮窓全体をカバーするメモリ(=32KiB)が必要になりそう。PNGはフォーマットや圧縮方法にバリエーションが多いので、自前でライブラリ書くのは面倒だな。
 最近扱った組み込みの画像を扱う処理だと、マイコン内の処理が8bitIndexedだったので、BMP8bppIndexed限定にした。コレなら読み込みプログラムも凄まじく簡単に書ける。JPEGだとマイコン内で減色アルゴリズムを走らせる必要がある。PNGは8bppIndexedも保存できるけど、大抵のドローソフトでは8bppPNGでは保存できない気がする。結局、リソースの制限された組み込み系では8bppIndexedBMPが楽だと思う。読み込み早いし。プログラム楽だし。大抵のドローソフトで保存できるし。
*/

2017年9月12日火曜日

STM32/FreeRTOSでCPU使用率的な

 STM32のTIMを使ってFreeRTOSのCPU使用率的なパルスを出す。

 まずTIMxをPWM1で初期化する。プリスケーラは500kHzくらいになるように設定。カウンタは最大(65535)に設定する。1周約0.13秒くらいになる。パルス幅は1に設定し、約2usecくらいのパルスを作る。
 FreeRTOSのIdleHookでTIMx.CNT = 0を行う。これにより、IdleHookが呼ばれてから2usecのパルスが出力される。
 優先度が0より高いタスクが実行中の場合、IdleHookが呼ばれなくなるので、最後にIdleHookが呼ばれてから2usec以降はパルスがLowとなる。
 つまり、このGPIOを見ていればMCUがアイドル状態の割合を知ることができる。
 2usec未満の処理であれば出力されないが、168MHzのコアでは2usecに300命令くらいしか処理できないし、OSのタスクスケジューラでも結構な命令数が必用なので、2usecくらいの分解能でも充分だと思う。

 この方法だとTIM1個とGPIO1本が必用で、マイコン内でアイドル率を知ることができないが、結構信頼性の高いデータだと思う。
 あとはZEROPLUSのロジアナでデューティー比を見れれば最高なんだけど、LAP-Cではデューティー比は見れないらしい。デジオシだとデューティー比を表示できるけど、あんまり信頼性高くない。

 もう一本タイマを使えるなら、TIMxをIdleHookでクリアしてアイドル率のパルスを作り、TIMyをGatedして、10msec毎にTIMyのカウンタを確認して、という感じだろうか。これならマイコン内で使用率を知ることができる。それにGPIOも必要ない。

銃撃ち比べ

 なろうで現代兵器チート的なの読んでると、主人公が銃を持つ時に、安全管理は重要だぜっ!みたいなスタンスの割に、結構簡単に銃のセーフティーを外している描写が見受けられます。討伐系のクエスト受けて森に入って、物音がしたら安全装置解除、とか。おそらくは即応性を高めるための描写ですが、本当に事前にセーフティーを解除したら早くなるの? ってことで、試してみました。
 といっても、統計的には相当酷い計測方法なので、あくまで参考程度に。そもそも僕は武器操作マスターチートとか一切無い素人だしね。

 計測方法は、Stealth-Target ST17のタクティカルターゲットで65枚のターゲットを撃ち終わるまでの時間です。銃はM4を使用しました。弾倉は30発入りを3本使用し、エマージェンシーリロードを使用しています。


 今回はハイレディで1セットごとにセーフティを使用する撃ち方と、ハイレディで最初から最後までセーフティをセミにして撃つ方法を試しました。他に依託射撃(セーフ有りのみ)とローレディ(セーフ有りのみ)も計測しました。

 結果
1) ハイレディ/セーフティ使用:160秒/70発(5発ミス)
2) ハイレディ/セーフティ未使用:166秒/76発(11発ミス)
3) 依託/セーフティ使用:145秒/74発(9発ミス)
4) ローレディ/セーフティ使用:180秒(62枚で時間切れ)/68発(6発ミス)

 という感じでした(先頭の数字は射撃順)。
 セーフティを使うほうが早いですが、後に撃ったセーフティ未使用は、疲れてきたためにミスファイアが増えて時間が遅くなったものと思われます。
 三脚を使った依託射撃をまじめにやったのは初めてなので、調整が合わずにちょっと撃ちづらかったです。もうちょっと早くなるかも。でも銃の先端が固定されてしまうと、照準を変えるのに体の位置を動かす必要があるので、立射よりはかなり撃ちづらいです。
 ハイレディは銃の重心をあまり移動させることがないので、重量物を持ってる程度の疲れしかありません。一方、ローレディは銃を大きく移動させるため、サポートハンドがかなり疲れます。

 ということで、簡単に計測した限りでは、セーフティの有無では有意差は無い気がします。
 機会があれば、セーフティ未使用→セーフティ使用で時間を計測して、平均を取ったほうが良いかな、と思っています。


 今回は操作性のいいM4を使いましたが、89式小銃とかだとどうなるんだろう。アレも慣れてる人は操作が凄まじく早いので、89式の操作に慣れてる人に計測してもらいたいなぁ。セーフ・セミ・3点・フルのセレクタの写真が出てきている以上、セーフ・フル・3点・セミは遅い(あるいは同じ速度でも面倒)ってことなんでしょうけども。


 そもそも、何らかのイベントでセーフティを解除する、という描写は、「このキャラクタは安全装置を使用しているんだ」という描写をするのが最大の目的だと思います。なので即応性とかどうでもいいのでわ

 個人的には使う銃をコロコロ変える主人公は信用ならないわけですが、でも操作マスターチートあれば良いのかなー。いや、チーム内で弾薬の共通化すらできてない主人公はダメだッ。そういうこと考えてるから楽しく読めないんだろうなぁ。

メモ:STM32F4 USBブートローダーで"USB デバイスが認識されません"

 STM32F4のシステムメモリからUSB DFUで起動しようとした時に"USB デバイスが認識されません"「このコンピュータに最後に接続された USB デバイスが正しく機能していないため、Windows によって認識されません」という警告。
 この場合、STMのペリフェラルに外部がマスターになるバスが通信を行っていないか確認すること。
 僕の場合は、相手の都合を考えずにUSART3にPCからデータを送り続けるソフトを動かしていたのが原因。この場合、STMはUSART3からファームウェアを更新するモードに入ってしまい、USBのポートは初期化されるのにUSBデバイスとして動作しないため、PCから警告が出る。もちろん、USB DFUで更新することもできない。

 STMのアプリケーションノート AN2606(Rev31)によると、デバイスによって異なるが、USART1, USART2, USART3, I2C1, I2C2, I2C3, I2C4, SPI1, SPI2, SPI3, SPI4, CAN1, CAN2 からファームウェアの更新が行える(もっとあるかも)。
 ブートローダーはの優先順位は、USART > I2C > SPI > CAN > USB となっているらしい(デバイスによってペリフェラルの有無が異なったり、場合によっては優先度の違いがあるかも)。
 USARTでデータをブロードキャストするような使い方をする場合、ブートモードに入るときにはバスが切断されるような工夫をする、あるいは、ブートローダーで使用されないUSARTでデータを受信する、といった使い方が必用そう。

 CANでファームウェア書き換えってのは、車載とかを想定してるんだろうなぁ。どうやってデバイスを指定するんだろうか。あとどうやってブートローダを起動するんだろうか。ファームウェアはバス上に流せばいいとしても、マイコンがブートローダーを起動しなきゃいけないから、完全にリモートで操作するのは難しそう。CANから特定のデータを受け取ったらブートローダーモードでリブートする、みたいなプログラムが入ってるんだろうか。

 ということで、USB DFUが起動しない場合は回りの怪しいデータの流れを気にしてみよう、という話でした。
 気がつくまでに結構手間取ったので、危ないところはマーキング。

2017年9月11日月曜日

Luaで日時の計算

 Luaではos.timeで数値を取り出すことができ、os.dateで数値から人間が扱いやすい数字(グレゴリオ暦)に変換する。また、os.timeにグレゴリオ暦のテーブルを渡すと、数値が帰る。

function printDate(d)
 print(d.year .. "/" .. d.month .. "/" .. d.day .. " " .. d.hour .. ":" .. d.min .. ":" .. d.sec)
end

t = 1234567890
d = os.date("*t", t)
printDate(d)

d = {
 year = 2001,
 month = 9,
 day = 9,
 hour = 1,
 min = 46,
 sec = 40,
}
t = os.time(d) + 9 * 60 * 60
print(t)

 このスクリプトは
2009/2/14 8:31:30
1000000000
のようになる。
 一見、UNIX時間のようだが、これは環境依存のようだ。

 Lua言語のライブラリ関数#os.timeによると、「os.timeの戻り値は、os.dateおよびos.difftimeの引数としてのみ、意味を持ちます」と書いてあり、os.dateとos.difftime以外には使えないことが示唆されている。
 公式のマニュアルにも、似たようなことが書いてある。
 マニュアルによると「In POSIX, Windows, and some other systems, this number counts the number of seconds since some given start time (the "epoch")」(POSIX、Windows、およびその他のシステムでは、この番号は、指定された開始時刻(「エポック」)からの秒数をカウントします)ということで、とりあえずはUNIX時間として扱っちゃって大丈夫だと思う。

 マイコンで上記1234567890を文字列にすると"2009/2/13 23:31:30"という風になる。上記の結果とは異なるが、マイコンはUTC+0で実行され、PCはUTC+9で実行されたことによる違い。

 マイコンで日時を返すには、"int _gettimeofday(struct timeval *tv, struct timezone *tz)"を実装すればいい。今回試した限りでは、tzはnullが指定されていた。
 Luaはgmtimeとlocaltimeを使うらしいので、タイムゾーンの決定はCライブラリ側で行われているっぽい。Cのタイムゾーンは変更できるらしいのだが、ちょろっと調べても方法がわからなかった。

 日時の処理はかなり泥沼な感じなので、できれば近寄らずにいたい。

 とりあえず、Luaで日時を扱うなら、os.timeでsecに変換して、操作して、os.dateで日時に戻して、という感じで扱えそう(環境依存)。


 マイコンのLuaで時間を扱いたいけど、RTCのバックアップ電池を実装するスペースがない。有ってもコイン電池ケースなんて持ってないからできないんだが。
 せっかくSTM32F4のRTCは人間に扱いやすいようになってるのにね。って、それだとUNIX時間に変換したりしなきゃいけないから面倒なんだ。こうなると、STM32F1の32bitカウンタのほうが便利な気がするなぁ。
 マイコンのRTCモジュール、64bitカウンタとかで、通常動作時はMHzを分周して、スリープ時は32.768kHzの水晶で、みたいな機能になって欲しい。あと外部の1PPSで水晶を補正する機能とか欲しい。

Anker SoundBuds Lifeを買ってみた

 Anker SoundBuds Lifeという、Bluetoothのネックバンド型イヤホンを買ってみた。さっき届いたのでメモ。

 ちょっと古いiOS端末でも問題なく使えてる。OSのミュージックアプリや、Amazon Musicで曲送りとかできる。
 再ペアリングでWindowsに接続すれば、音はすべて聴ける。
 ただし、Windows Media Playerは曲送りができない。一時停止・再生は非アクティブでも可能。iTunesは非アクティブでも曲送りができる。一時停止・再生は非アクティブではできない。
 PCでは使えないことは無いが、フルに使うにはモバイルOSのほうが相性がいい。

 到達距離は、自室の中を歩き回るくらいなら問題ない。木造なら隣の部屋でも大丈夫かも。別の階だとかなり途切れる。

 ネックバンド型なので、首にかけるタイプ。ライフルのスリングと共存させるのはちょっとむずかしそう。カメラの2ポイントスリングみたいな、左右の移動がないタイプはギリギリ大丈夫かな。ただ配線が細いので、巻き込まれると簡単に千切れそう。

 ボタンがちょっと硬いので、再ペアリングのような同時長押し操作はちょっと厳しい。が、無理というほどではない。
 ステータスLEDが左側の先端についているが、首にかけた状態で見るのは厳しい。

 最初にニュースサイトのサムネで単体の画像を見てカッコイイと思って、人間が装着してる画像を見てかっこ悪いなぁと思ったけど、実際に使ってると、自分からは見えないので、人からの視線を気にしないなら問題ない。某うどんよりはマシだと思う。

 IPX5なので、多少の防水性はある。風呂の中で音楽聞くのとかに良いかなーと思いつつ、買っていきなり水場に持ち込むのは勇気がいるので、しばらくはやらないかなぁ。

 多少の飛んだり走ったり、くらいであれば大きな問題はない。ただ走ってるとだんだん回転していくので、時々位置を直す必要はある。
 歩いていればネックバンドが跳ねることはないが、今度は風切り音が聞こえてくる。
 あと周りの音が聞きづらいので、歩きながら着用する時は要注意。


 とりあえず、ファーストインプレッションはこんなところ。

追記:2017/09/15
 ネックバンド左側、ケーブルの付け根からその後ろのLマークのあたりを指で触ると、音量が変化してしまう不具合が出ている。右側のイヤホンには問題はなく、もっぱら左側だけで発生している。
 断線のような雰囲気ではないのだけど、レビューで断線とかそういうのが多いのは、これが原因かも。
 雰囲気としてはテルミンみたいな感じかな。静電容量で音量が変化してる感じ。電子ボリュームICの左側の部品実装をミスってコントロールのインピーダンスが高いのかな、とかそういう感じの挙動。電子ボリュームがアナログで制御されるはずないだろうし、相当な高周波じゃないと手の影響なんて受けないだろうけど。
 Bluetoothの不具合という感じではないんだよな。なんせ右側には問題がないので。左右が別になってるタイプなら、左側だけRFがマズったのかとかあるけど、ネックバンド型ならRFは1個しか無いし、コレがダメなら右側も影響受けるだろうし。
 とりあえず原因は不明。まぁ僕の使い方だと大きな問題になるほどじゃないので、しばらくは様子見かな。18ヶ月サポートなのであんまり急がなくても良さそう。

 あと電池の持ちについて。おもったよりも持たないかも。丸1日使う分には問題ないと思うけど、まだ大丈夫だろ、と思って1日充電するのサボるといきなり切れる。泊まりでどっか行くときはモバイルバッテリー持って行かないとダメだねぇ。あぁ、これがマッチポンプか。


2017年9月10日日曜日

STM32/FreeRTOSでLua

 とりあえずSTM32F4のFreeRTOSでLuaのはろーわーるどが動いたのでメモ。

 まずソースファイルをすべて追加する。
 環境によってはコレだけで動くと思う。


 追記:2017/09/11
 僕の環境でmallocが使えるようになりました。ってことでLuaのソースコードそのままで動くようになりました。あとprintfの浮動小数点出力も正常になりました。

 いちおう、mallocじゃなく、FreeRTOSのpvPortMallocを使った方法も残しておきます↓。


道具とか

 普段使ってる道具。主にはんだ付け回り。

妄想:PNG独自チャンク


2017年9月9日土曜日

C#でAPNGを作る

 64bppPNGを作ろうと調べてた際に、APNGが結構簡単に作れるらしいというのがわかったので、試しにC#で生成してみた。

 サンプル画像を貼りたいけど、BloggerはAPNG非対応らしいので画像は無し。


 今回はとりあえず動けばいいという考えで作ったので、あんまりソースきれいじゃない。でもAPNGを作るのはそんなに難しくない。今回は本当に最低限しか実装してないので、別の用途に使おうとすると大変かも。

 とりあえずGraphicsでサンプル画像を作ってるが、たぶんファイルから読み込んだ画像でも問題ないはず。入力形式はBitmapになってれば良いので、BMPでもJPGでもPNGでも何でも問題ないはず。

 今回はフレーム待ち時間は決め打ちで、ソースコードに書き込んでいる。delay_num, delay_denというのがそれで、1フレームあたりの待ち時間はdelay_num / delay_den [sec]になる。num=1, den=8なら1フレーム0.125秒(8fps)になる。
 この方法だと、30fps(0.0333...sec)みたいな秒で循環小数になるディレイでもある程度は正確に表記できる。そういえばJPGだったかのEXIFもこういうフォーマットだった気がする。あとMS FSXのAPIにもこういうのあったね。マルチメディアではよくあるフォーマットなのかも。
 APNGでは16bit2個なので、1フレームあたり約15マイクロ秒から18時間くらいまで設定できる。
 本来、ディレイはフレームごとに設定できるので、ある1フレームだけ表示時間を増やすとか、そういう表現もできる。

 APNGはあくまでPNGのラッパーなので、おそらくPNGが対応する形式はすべて対応できる。階調のある透過や、24bitフルカラーも使える。たぶん64bitでも。もちろんPNGとして圧縮されるので、ファイル容量も(ちょっとしたアニメーションなら)大きくなりすぎることはない。
 Bitmapのコンストラクタで32bitを指定して、背景に透過色を設定すると、Chromeでは背景が市松模様になるので、たぶんちゃんと透過も処理できてる。

 WikipediaのAPNGのページを見てみると、最初のフレームをアニメーションに使うか否かを設定できたりするらしいが、今回参考にしたページにはそういうのは見当たらなかった(見落としてるだけかも)。
 もしかしたら、IDAT単体ならサムネイルにIDATを使ってアニメーションには使用せず、fcTLとIDATを並べるとIDATもアニメーションに使う、みたいなことなのかも。
 まぁ、ちゃんと使うなら本来の仕様を確認した方がいい。

 ソースはそこそこ長い(300行ちょっとある)ので、続きを読むからどうぞ。

2017年9月6日水曜日

Luaのコードを文字列として持つ

 Luaのコードをファイルから読むサンプルはよくあるが、それじゃただのインタプリタなので、自分で処理を書く意味がない。ということで、とりあえず変数にコードの文字列を持つようにしてみた。

#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

// [スクリプト化可能なアプリケーションに Lua を組み込む](https://www.ibm.com/developerworks/jp/linux/library/l-embed-lua/index.html)

int main(void)
{
    static const char *codes[] = {
        "print(\"hello world\")",
        "abc=123+456\n"
        "print(abc)\n",
    };
    const int codeCount = sizeof(codes) / sizeof(codes[0]);

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    int i;
    for (i = 0; i < codeCount; i++)
    {
        const char *code = codes[i];

        if (luaL_loadbuffer(L, code, strlen(code), "code"))
        {
            fprintf(stderr, "lua couldn't parse '%s': %s.\n", code, lua_tostring(L, -1));
            lua_pop(L, 1);
            continue;
        }

        if (lua_pcall(L, 0, 1, 0))
        {
            fprintf(stderr, "lua couldn't execute '%s': %s.\n", code, lua_tostring(L, -1));
            lua_pop(L, 1);
            continue;
        }

        lua_pop(L, lua_gettop(L));
    }

    lua_close(L);

    return (0);
}

 makefileは前回と同様。
 このサンプルではスクリプト言語の利点が皆無だが、変数に入った文字列を処理できるようになったというのが大きな進化点。これならマイコンのシリアルコンソールで受け取ったコードを処理したりができる。マイコンのFlashに入れたコードを走らせることだってできるはず。

 ちなみに、このサンプルではLuaのコードを文字列で書いているが、luacでバイナリに変換したコードでも処理できる。ただしバイナリだとstrlenが使えないので、そのあたりがちょっと面倒。もしかしたらヘッダとかに容量書いてあるのかもしれないけど。
 まぁ、これくらいのコードだとASCIIよりバイナリのほうが容量がでかいという事態になるので、バイナリを使う利点はあんまりないが。
 LuaはコンパイラとVMのバージョンが違うと動かないらしいので、マイコンでLuaを走らせて誰かにコードを書いて貰う場合は、そのあたりも色々考える必要がありそう。

CでLua

 マイコンでインタープリタつかいたいなーと思ってLuaを試してみた。
 公式ページのダウンロードからソースを落としてきてビルドするだけ。楽。



今回は全部まとめてビルドしてるので、ちょっと時間がかかる。4-6秒くらい。

 lua.c/luac.cはバイナリとして配布されてる実行ファイルのmainが入っている。当然、これを含めてmakeすると重複定義エラーになる。

 今回は動作確認だけなので簡単な処理だけ作ってみた。スタンドアロン動作だし。
 ちゃんと使うならちゃんと使い方を調べにゃ。でも結構簡単にいろいろできそう。

 STM32F4で動くLuaVMとかやりたいな。HALをもうちょっと抽象化してLuaで実行できるようにしたりして。

追記:2017/09/09
 l_hoge関数の戻り値が1だけど、これは0にしなきゃいけないみたい。
 C関数の戻り値は、関数の戻り値の数を渡す必要がある。今回は戻り値がないので0を返す。
 Luaは関数呼び出しの際にスタックが1個作られ、引数がスタックに積まれる。また戻り値もスタックに積まれる。引数が3個、戻り値が2個の場合、スタックは5段となり、Cの戻り値は2になる。この際、print(C関数())のようにするとスタックの上2個が表示される。Cの戻り値に6を指定すると、戻り値の2個、引数の3個、そして不定値の1個の変数が表示された。この不定値は何らかの定数かもしれないが、調べてない。

2017年9月4日月曜日

アンドロメダ銀河


 いくつかの処理を変更して、入力は前回と同じデータを使用しました。
 ただ、今回は新たにパターンノイズのデータを追加で使用しています。パターンノイズは本撮影後に、暗そうな地面に向けて撮影した映像を使用しています。
 あらかじめ動画を静止画に変換し、全静止画の加算を行った後、フレーム数で割った値をパターンノイズのデータとして使いました。

 本撮影データは静止画が7500枚ですが、まず読み込んだデータを無加工でOPM処理に回し、マッチングができれば再び読み込んで、パターンノイズを除去してから位置の変更と加算を行います。
 すべての画像の加算が終わったら一旦バイナリで書き出し、48bppPNGに変換した後に、Lr5で輝度などを調整してjpgで書き出しています。

 パターンノイズの除去が強すぎるのか、特にノイズが多い領域が暗くなっています。16bitで書き出してLrで調整しているというのが大きいんでしょうが、アンドロメダ銀河も見やすくなってる気がします。

 今回は中間データとして独自フォーマットのバイナリを使いました。Width, Height, Chが任意町で、Depthは32bit固定です。ヘッダに幅高さCh数が入ってるだけで、あとはバイナリなので扱うのは楽ですが、中間データは50MBくらいあります。
 C#のZipなりDeflateなりで圧縮掛けても良いんでしょうが、画像データだから単純に圧縮しただけなら圧縮率は悪いだろうなぁ。かといって複雑な圧縮を行うのも面倒だし、そもそもPC内で使うだけのデータなので容量も数十GBとかにならなければ問題ないですし。


 気軽に遊べる程度のソフトウェアでは、だいぶ良いところまで作り込んだと思ってるので、最後に1回くらいちゃんと撮影したら、もう満足かな、という気がしています。
 でもこれから満月になるので、あんまり良いコンディションじゃないんだよなぁ。

C#で64bitPNG

 C#で64bitPNGを出したかったので、やってみた。

using (Bitmap bmp = new Bitmap(256, 256, PixelFormat.Format64bppArgb))
{
    BitmapData bmd = bmp.LockBits(
        new Rectangle(Point.Empty, bmp.Size),
        ImageLockMode.WriteOnly,
        PixelFormat.Format64bppArgb);

    for (int y = 0; y < bmp.Height; y++)
    {
        for (int x = 0; x < bmp.Width; x++)
        {
            int l = y + x;
            byte[] data = BitConverter.GetBytes((UInt16)l);

            int pos = x * 8 + bmd.Stride * y;

            Marshal.WriteByte(bmd.Scan0, pos + 0, data[0]);
            Marshal.WriteByte(bmd.Scan0, pos + 1, data[1]);
            Marshal.WriteByte(bmd.Scan0, pos + 2, data[0]);
            Marshal.WriteByte(bmd.Scan0, pos + 3, data[1]);
            Marshal.WriteByte(bmd.Scan0, pos + 4, data[0]);
            Marshal.WriteByte(bmd.Scan0, pos + 5, data[1]);
            Marshal.WriteByte(bmd.Scan0, pos + 6, 255);
            Marshal.WriteByte(bmd.Scan0, pos + 7, 255);
        }
    }

    bmp.UnlockBits(bmd);

    bmp.Save("./log.png", ImageFormat.Png);
}

 まずBitmapのコンストラクタにPixelFormat.Format64bppArgbを指定する。これで64bitの画像が作れる。
 あとは色を載せるだけだが、今回は直接値を書き込んでいる。
 試してないが、たぶんSetPixelはダメだと思う。Color.FromArgbだと256以上は例外が投げられるはずなので、16bit画像は作れないはず(未確認)。
 画像を保存する際は、ImageFormat.Pngで保存すれば、16bitのPNG(16bit*RGBA =64bit)で保存される。

 ちなみに、このサンプルは下位9bitだけを使ってグレースケール画像を生成している。8bit換算で下位約2bitしか操作していないから、普通に表示しても何も見えない。

 これをpaint.netで輝度をいじると以下のような感じ。
下位8bitが切り捨てられて、上位8bitのみが使用されている。

 Lr5で輝度をいじると以下のような感じ。
てきとーに調整したのでガタガタだが、ちゃんとグレースケールで表示されてる。つまり、少なくとも下位8bitの内の数bitは使用されている。


 ということで、天体写真合成で16bitの出力ができる目処が立ったので、あとから輝度を調整しやすくなった。
 もともと自前で48bitPNGを出すライブラリを作ろうと思ってて、PNGって面倒くさいな~と思ってたところなので、.Net単体で簡単に64bitが出せたので楽になった。
 BitmapDataを使わなきゃいけないのは面倒だけど、そもそもBitmap.SetPixelはクッソ遅いので、どっちにしろ自前でBitmapDataの処理は作らにゃいかん。

 僕としては96bitPNGが欲しいところw。 16bitだと256枚加算まではロスレスだが、それ以上の枚数を重ねると情報が劣化してしまう。96bitだと8bit画像を16777216枚まで重ねられるので、当面は心配いらない。いちおうPNGのヘッダは1024bppまで対応しているっぽいけど、.Netは64bppが上限のはず。
 一般的なユーザーでは32bppPNGで充分だろうけども、データ処理の中間データとして使うならやはり128bppPNGくらいあると心強い。あとはIntだけじゃなくてFloatを持てたりとか、1/3/4ch以外にも、任意のch数を持てたりとかすると、学術データの標準フォーマットとして使えると思うんだけど。
 APNGのフォーマットは調べたことがないが、このコンテナに入れれば2次元画像をもう1次元増やせるので、3次元空間に4次元のベクトルをもたせることができる。現状だと、3次元空間に16bitの4次元ベクトルを入れるのが限界かな。それでも大したもんだが。16bit整数から浮動小数点に変換する処理とかを入れればさらに情報量は増やせる。
 APNGだとWebブラウザでも表示できるから、3次元空間の3次元ベクトルデータをざっくりと確認するのにWebブラウザで開けばいい、という利点もあるかも?
 PNG、クッソ古い規格なのに、拡張性やばい。

妄想:PRNマスキングテープ


2017年9月3日日曜日

CでStack

 たまに欲しくなるスタック(LIFO)のコード。

typedef struct
{
    uint16_t size;
    uint16_t width;
    uint16_t count;
    void *end;
} Stack_t;

void stack_Init(Stack_t *stack, uint16_t size, uint16_t width, void *buff)
{
    stack->size = size;
    stack->width = width;
    stack->count = 0;
    stack->end = buff + size * width;
}

int stack_Push(Stack_t *stack, void *data)
{
    return (stack->count < stack->size ? memcpy(stack->end - ++stack->count * stack->width, data, stack->width), 1 : 0);
}

int stack_Pop(Stack_t *stack, void *data)
{
    return (stack->count ? memcpy(data, stack->end - stack->count-- * stack->width, stack->width), 1 : 0);
}

int stack_Peek(Stack_t *stack, void *data, uint16_t index)
{
    return (stack->count > index ? memcpy(data, stack->end - ++index * stack->width, stack->width), 1 : 0);
}

int stack_PeekTop(Stack_t *stack, void *data, uint16_t index)
{
    return (stack->count > index ? memcpy(data, stack->end - (stack->count - index) * stack->width, stack->width), 1 : 0);
}


Add 2017/09/04
 stack_Peek関数

Add 2017/09/05
 stack_PeekTop関数
 Peek関数はスタックの底から、PeekTopは上からインデックスが始まる。PeekTopで0を指定すると最後に入れたデータが帰る。Peekで0を指定すると最初に入れたデータが帰る。PeekTopはPeekでもStack.Countとか使えば同じ動作になるのでPeekのみ作ってたが、やっぱり一発で上から取り出せるほうが便利なのでPeekTopも追加した次第。

アンドロメダ銀河






 運良く晴れてたので、アンドロメダ銀河を撮ってみました。
 上から動画からキャプチャ、動画変換後、変換後のヒストグラムを操作、撮影中、静止画で撮影、という感じです。
 動画はISO20万、シャッター1/4で5分ほど撮影しました。静止画はISO10万、シャッター4秒です。
 さすがにISO20万だとノイズは相当なもんですが、平均後はかなりノイズが減っています。とはいえあんまり綺麗に写ってないですね。SNRが改善してNFが下がったことによって暗い部分も浮き上がってきてはいますが、なんともイマイチな感じ。
 ISO10万で4秒だと、ノイズは多少改善しますが、星はかなり流れています。銀河は全体的に明るいのであんまり流れてるのはわからないかな。あと動画と静止画では撮影後の処理が違うので、静止画はさらに綺麗になってる感じ。動画で撮影するより、1/5sec, ISO40万くらいで2万枚くらいインターバル撮影したほうが良さそう。天体ならサイレント撮影でも充分だからシャッター機構の寿命も気にしなくていいし。問題はインターバル撮影ができないことだけど。


 動画処理後であんまり明るくならないのは、8bitにスケーリングする際に他の星が明るすぎるためというのが大きいと思いますが、もともとの輝度が低いというのも影響しそうです。ISO20万でここまでノイズ減らせるなら、ISO40万でも良いかも。
 あと雲が出てきたので5分ほどで撮影を終了しましたが、コンディションが良ければ20分位撮影したかった。
 全部で7500フレームくらいですが、合成は1時間程度で終了していたようです。8時間位かかるかなと思って寝てる間に回してたんだけど。。これは望遠鏡で拡大したことにより恒星が疎になり、ラベリングやマッチングの負荷が軽くなったのが要因かもしれません。20分の動画でも同じような密度なら4倍ですから、4時間で済みます。感度を上げて恒星が増えれば処理時間も増えるでしょうが、それでも8時間もあれば終わるはずです。

 あとはパターンノイズがかなり気になります。左側上下や、フレーム上下左のピンク色は熱雑音によるものだと思います。ランダムな位置にあるピクセル単位のパターンノイズは合成中に除去されますが、面積の大きいパターンノイズは除去しきれないので、これは何らかの対策が必用そうです。


 とはいえ、5万円の入門用天体望遠鏡(8cm手動経緯台)でここまで撮れるのは面白いです。1分位の間隔でだいたいの位置に合わせれば良いので、完全手動追尾みたいな苦労もないし、ライブビューでも銀河が見えるので位置を合わせるのはとても楽だし。
 でももうちょっと改善の余地がありそうなので、なんとかしたいところ。
 ま、市場にあるソフトウェアを使えば簡単にもっと綺麗にできるんでしょうが、目的は天体写真撮影じゃなくてソフトウェア作成なので、こんな調子で。

2017年8月31日木曜日

OPMで画像処理



 α7sで撮影した1分ほどの夜空の映像を加工してみた。
 左下の部分は撮影した動画をキャプチャしたまま。
 右上の部分は全フレームの平均を出力した。
 フレーム間で平均化した画像はノイズが減っているのがわかる。あとSNRが改善して暗い星が浮き出ている気がする。

 言うまでもないが、夜空の星は常に移動しているから、単純に平均化するとうまくいかない。今回はOPM(Optimistic Pattern Matching)というアルゴリズムで画像間の移動量を計算して合成した。

 なお、左側の紫色のノイズはカメラ自体の熱雑音によるパターンノイズである。

 1分間(2000フレーム)程度の映像を合成するのに3.5時間ほどかかってしまった。いくら動作確認最優先で最適化してないとはいえ、これは辛いなぁ。

 ISO感度を上げることにより発生するノイズはパターンノイズとランダムノイズがあり、パターンノイズは簡単に除去することができるらしい(やったことない)。ランダムノイズは今回の処理である程度消えることがわかった。パターンノイズでも相対的に移動していくから、平滑化で消えるかも。そう考えると、カメラのノイズの大部分は消しされる。
 となると、天体望遠鏡にK-5を付けて高感度でインターバル撮影して、それを合成したりとかすればある程度の解像度を持った銀河とか撮れるかも?
 2年ほど前にオリオン大星雲とかアンドロメダ銀河を撮ったけど、当然ながら経緯台固定だからかなり解像度が悪かった。近いうちにリベンジしたい。
 F11の光学系だとK-5のISO51200でもちょっと厳しいかも。かなりシャッター長くする必要があるから流れそう。α7sの409600だとシャッターは短くてもいいけど、α7sはインターバル撮影ができないというアホカメラだから使いづらい。わざわざノートPC持ってくのもなぁ。

 どっちにしろ、最近は天気が悪くてずーっと曇ってるから、いつになるやら。

2017年8月29日火曜日

Graphicsで変形後の位置を計算する

 C#では1つのBitmapに対して複数のGraphicsを作成できます。これを使うと、オフセットを個別に設定できるので、画像解析とかの結果を1枚の画像にまとめるのが楽ですが、画像1の点aから画像2の点Aに対して線を書く、といったことができません。ということで、Graphics.TransformからPointFを計算するようなメソッドを作ってみました。といっても単なる行列の計算ですが。



 0はGraphicsの変形で書き込んでおり、1はあらかじめ変形した位置を書き込んでいます。
 GraphicsのTransformはピクセル単位で計算されますが、自前の計算では位置しか計算できないので、線幅や図形の形状は計算されません。上図の比較はLineとEllipseしか使っていませんが、Rectangleとか使うともっと顕著にわかると思います。

 とりあえず今回の目的はGraphics1のaからGraphics2のAに対して直線を引きたいだけだったので、位置の計算だけで充分なので、目標は達成です。

 やろうと思えばピクセル単位での変形も可能でしょうが、そこまでするとグラフィックエンジンを自前で作ることになるので、相当に面倒なことになると思います。


 C#のImage, Bitmap, Graphicsはちょっとクセがありますが、そこに触れない程度の使い方ならかなり便利だと思います。
 でもGraphics.DrawImageUnscaledにImageを渡すとスケーリングされる?バグはどうにかならんのか。。。

2017年8月26日土曜日

JavaScriptでURLパラメータを分割

 いつも忘れるのでメモっとく。

 以下の関数は、引数無しで呼ぶとページのURLパラメータを返す。引数にパラメータの文字列を入れると、それを分割して返す。
 XMLHttpRequestとか使う時に、request.responseURL.slice(request.responseURL.indexOf("?"))みたいなのを引き数に渡すと、リクエストした際のパラメータを取り出すことができる。ま、どっかにメモっとけばいいだけという話なのだけど。
 戻り値はオブジェクトに格納されるので、forとかで回したり、必用な値だけキーで取り出したりできる。
 あんまり詳しく動作確認してない。

function getURLparameterStrings(url) {
    if (url === void 0) {
        url = window.location.search;
    }

    let result = [];
    if (1 < url.length && url.substr(0, 1) === "?") {
        let query = url.substring(1);
        let params = query.split('&');

        for (let i = 0; i < params.length; i++) {
            let element = params[i].split('=');

            let paramName = decodeURIComponent(element[0]);
            let paramValue = decodeURIComponent(element[1]);

            if (element.length == 1) {
                result[paramName] = void 0;
            } else if (element.length == 2) {
                result[paramName] = paramValue;
            }
        }
    }

    return (result);
}


fix(2017/08/26)
 パラメータのないキーに"undefined"の文字列が入る問題を修正。

2017年8月24日木曜日

KiCadでGitのライブラリを追加する

 KiCadはGit上にある部品形状(フットプリント)を使うことができる。
 登録するには、Pbcnewの"設定"→"フットプリントライブラリの管理"をクリックして"PCBライブラリ一覧"を開き、"ウィザードを使用して追加"をクリック。"Githubリポジトリ"を選択してテキストボックスにアドレスを入れる。Nextでちょっと待つとKiCadで使えるライブラリが表示されるので、欲しいライブラリのチェックボックスを有効にする。あとは何回かNextを押していけば追加される。

 注意点として、GithubのURLはリポジトリのURLではなく、ユーザーのURLを入れる必要がある。例えばKiCadプロジェクトが配布しているライブラリは https://github.com/KiCad/kicad-library にあるが、ウィザードでは https://github.com/KiCad/ を指定する。
 それと、途中で欲しいライブラリが見つからなくて別のGitのURLを指定する際は、ウィザードを一旦閉じる必要がある。


 かなり久しぶりにKiCadを使ったけど、かなり使いやすくなってる気がする。
 あとPCBを編集する際に回路図エディタを開いていると、部品をクリックすると回路図でその付近が表示されたりする。4k液晶で便利だと思った数少ない作業がKiCadのPCB設計だと思う。
 回路設計をする際は回路図エディタとデータシートを並べて表示、基板設計をする際は回路図エディタと基板エディタを並べて表示、みたいな感じで、4k液晶がかなり便利だった。

妄想:海上浮遊プラットフォーム


2017年8月20日日曜日

QZS-3

 QZS-3のTLEが出ていたのでJS Orbitに追加しました。
 JS Orbit


 QZS-2の時と比べ、初期の軌道傾斜角が抑えられている気がします。ロケット側でいじっているのか、衛星で移動しているのかはわかりませんが。

 JS Orbitに与えるTLEデータは、C#で作ったプログラムで生成しており、データのスキャンからアップロードまでをおよそ自動でできるようになっています。データはスクリプトで範囲を指定できるので、衛星名だったり軌道傾斜角だったり平均運動だったり、様々な要素を指定できます。
 とはいえ、結局そのプログラムの実行を指示するのは人間なので、僕がその気にならないと更新されません。このあたりはなんとかしたいなぁと思うところ。面倒と思う以外に特に困っていることはないので、優先順位が低くてズルズルとそのままで運用してるのですが。

2017年8月9日水曜日

amazonのISBNなURL

 本のISBNからタイトルが欲しいのだけど、いい感じのAPIが見つからない。ISBNで検索できるサイトは掃いて捨てるほど有るけど、ISBNで直リンクになるようなところが無い。

 しかし、どうやらamazonがISBNで商品ページにリンクを作れるらしい、ということでやってみた。
 https://www.amazon.co.jp/dp/487783219X みたいなURLでいけるらしい。ただし、ISBN10限定であり、ISBN13では404になってしまう。
 ISBN10とISBN13は相互に変換が可能らしく、頭の978の有無でISBN10かISBN13かが決まるらしい。ただしチェックデジットは有効だから、ISBN13でamazonリンクを作るにはチェックデジットを再計算する必要がある。ということで、勝手にやってくれるJavaScriptコードを書いてみた。

 下のテキストボックスにISBN10/ISBN13を入れればamazonのリンクを作ってくれる。チェックデジットは評価されるから、入力ミスをするとエラーになる。ただし9文字/12文字の場合は入力値の正しさは気にせず、入力された値からチェックデジットを生成してくれる。間違いなく入力できるなら、チェックデジットを省いてもURLを作れる。
 例のごとく、ちゃんと動作確認してないから、動かなかったらごめんね。

 amazonのリンク、ISBN13やJANでも開けるようになったらすっげー便利だと思うんだが。。。

ISBN:

2017年8月8日火曜日

SDカードアダプタ

 ZEROPLUSのSDカードアダプタを買ってみた。今回買ったのはSDカード用とmicroSDカード用の2種類。

 ZEROPLUS SDカードアダプタ - ネット販売
 ZEROPLUS マイクロSDカードアダプタ - ネット販売

 ZEROPLUSは言わずと知れたロジックアナライザのメーカー。
 近年ではいくつかのロジックアナライザがホビー向けとして投入されているが、やはりZEROPLUSは一日の長があると思う。特に最近では各種プロトコルすべてが無料公開されており、デジタルバスを見るには必需品と化している。

 話を戻して。

 SDカードアダプタというのは商品画像を見ればわかると思うが、SDカードソケットとSDカードの間にピンヘッダを生やすための基板。
 基板の途中にピンをはんだ付けしてもいいが、ちょっと気軽じゃない。あらかじめロジックアナライザ用のピンヘッダを用意しておくのも手だが、後からは使わないのは確実だし、何より面積取って邪魔。
 その点、カードアダプタを使えば気軽にロジックアナライザを挿入することができる。

 microSDカードアダプタの方は、商品説明に挿入しづらいと書いてあるが、僕が買ったものは基板がt0.7mmくらいと、microSDカードとほぼ同程度の厚さになっており、特に挿抜で引っかかるということは感じなかった。


 SDカードアダプタは8bitアクセス対応で、ちゃんと等長配線されている。microSDカードアダプタの方は大きさが小さいせいか、パターンは直線で構成されていた。その代わりmicroSDの方はダンピング抵抗が実装されている。SDカードの方はパターンは有るが、抵抗は未実装。ソルダブリッジすらないので、ダンピング抵抗とは全く別の用途なのかも。あとSDカードの方はパスコンや電源ランプがついている。

 カードアダプタのデメリットとして、単体ではCDやWPを監視できないという点が有る。WPは正直どうでもいいが、CDはカード挿入を検出して自動的に初期化するようなコードを作る場合に、タイミングを予想するのが難しいかも。
 逆に、アダプタのVDDをロジアナで監視し、SDカードに給電されてからどれくらいの時間で初期化が始まるか、といったことは見れるかも。

 それにしても、マイコンから15cmでSDカードソケット基板へ、そこからアダプタで延長し、さらに15cmのワイヤを通ってロジアナへ。ノイズ対策もへったくれもないな。まぁ現在のところSPIは328kbpsくらいの速度なので気にするまでもないという感じだけど。

 気軽にSDカードのロジックを覗けるようになると、FATアクセス全体を見たくなってくる。でもメモリ128kモデルを持ってしても全くメモリが足りない。この上となると、最上位の32ch2Mモデルになるのか。お値段20万円。ZEROPLUSの計略恐るべし。

2017年8月7日月曜日

Speed Wi-Fi HOME L01を買った

 ここ数年、plalaの「ぷららモバイルLTE」というモバイル回線をメインで使っていたのだが、11月末でサービス終了とのことなので、代替サービスを探す必要に迫られた。
 僕は月50-100GBくらいの帯域を使っているようなので、よくあるモバイル回線は使用不可能のため、とりあえず今のところ大きな制限のないUQ WiMAXのUQ Flatツープラスギガ放題(3年)を契約してみた。このプランは月々の容量に対する制限はないが、直近3日に10GBという制限がある。日3GB程度までなので月90GBくらいまで。全く足りないというほどではないが、十分というほどでもない。いままでの回線でかなり制限されて月60GBとかなので、制限無しで使ったらどうなることやら。
 
 まず地域的な話をすると、僕が住んでいる上富良野町は約1万人程度が住んでいる町で、人口密集地では光回線が使えるらしい。しかし僕が住んでいるところは数百mで町境という過疎地域で、固定回線はISDNしか使えない。昔は国の政策によりADSLを敷設していたらしいが、だいぶ前に回線は撤去されている。

 本当はぷららモバイルが終了する直前くらいにUQ WiMAXを契約するつもりだったが、日に日に回線品質が悪化しているのが体感できてしまうので、慌ててUQ WiMAXを契約したという次第。なんで日に日に悪化するか、というのはぷららモバイルが鳴り物入りで始まった頃のネット記事に書いてあるのでそちらを参照のこと。

 で、回線はUQ WiMAXを使うとして、モバイルルータを何にするか、という問題がある。
 ネットの評価を見ると、「固定型は持ち運べないし、可搬型とくらべて性能がいいわけでもないし、可搬型一択」みたいな評価が多い。が、今回は強い意志を持って固定型のSpeed Wi-Fi HOME L01というモデルを購入した。
 理由は、今まで使ってきたモバイルルータはことごとくバッテリーが膨らんで使い物にならなくなったため。基本的に常にUSBで給電して使っているが、本来のモバイルルータの想定としてこういう使い方はされていないから、浅いとは言え常に充放電が繰り返されている状態となり、バッテリー寿命をどんどん削っていくらしい。
 ということで、電池内蔵モデルを除外して、固定型を選択した。
 可搬型は電池が入ってるから停電しても使える、という利点は魅力的だが、そもそも停電時はデスクトップPCが使えないから本格的な作業はできないし、数日間とかそういうレベルで停電するのが確実ならカーバッテリーからDCACコンバータを使えばいいし、みたいな割り切り方をしてる。
 経験的にこの地域では落雷によって数分間停電するということは有るが、それ以上に長時間停電することは無いから、停電時の想定をする必用はない、という見込みも有る。

 ということで、HOME L01の話。
 買う前は円筒形という外見から、ゴミ箱MacProくらいの大きさをイメージしていた。が、届いてみるとかなり小さかった。太さはそれなりにあるが、高さは500mlのペットボトルより少し低いくらい。


 保護色で見づらくて申し訳ない。心の目で見てください。
 LEDは計6個あり、稼動状態や受信強度の表示がされる。上3個が受信強度で、1/3だからだいぶ環境が悪い。
 LEDがかなり明るい気がしたので、気休め程度にカプトンテープで輝度を落としてある。

 設置作業はかなり簡単。コンセントを刺してLANケーブルをつなぐだけ。電池の容量とか、電源スイッチとか、一切気にする必要がない。
 ただデフォルトのIPアドレスが192.168.100.1/255.255.255.0で、DHCPが100-200なので、いままで使ってたルータとはちょっと違う。そのため、明示的にDHCP再割当てを行わないと通信できない。
 Winならネットワークアダプタを無効化、有効化することで再割り当てが行われる。RasPiはピンヘッダにTTL232を接続して固定IPのコンフィグを書き換えてから再起動を行った。残るはHDDレコーダだが、コイツはどうやったら再割り当てされるのか不明。まぁ数日ほっとけば自動再割当てされるかなーと思っているが、どうなることやら。たぶんネット接続がないので自動録画とかができないので、早いとこ解決しておく必要があるが。
 HOME L01のIPアドレスにブラウザからアクセスするとステータスや設定を表示できるが、内臓のWebサーバーはかなり応答が遅く、表示するのに時間がかかったり、表示途中で切断されたりする。ただ、ファームウェアのアップデートを行えば改善するかも(届いてから未アップデート)。

 まだ届いて数時間しか経っていないのだが、ぷららモバイルとくらべてかなり快適になったと実感できる。
 ぷららモバイルは朝の通勤・通学時間帯、昼休みの時間帯、そして夕方から深夜にかけての時間帯はHTMLの読み込みも危ういほどに回線が遅かった。なのでこの時間帯はほぼネットが使えない状況だった。
 しかしUQ WiMAXでは、今の時間(23時頃)でもほぼストレス無くニコ動をストリーミングできる。もちろんyoutubeも。今まではこの時間HTMLが読めるか読めないかくらいに遅かったから、雲泥の差と言える。
 ちょっと大げさな言い方をすると、インターネットが遅いと世界から拒絶されてるような気分になるが、逆にインターネットが快適だと、世界中とつながっていられるような気になる。精神安定的にインターネットの快適さはかなり重要な要素だと思う。昨日までは世間の動きを考慮するのが当然だったが、そういうのが気にならないというのはすごく安心感が有る。
 まだ使い始めて数時間だが、既に3GBほど通信しているらしい。かなり久しぶりにニコ動をBGVにしてるから、それが効いてるかな。この調子で使い続けるとあっという間に帯域制限されそうだが、まぁしばらくすれば落ち着くでしょう。

追記:2017/08/08
 DIGA(DMR-BRW1000)のDHCP再割当ては、スタートボタンから左に入ってネットワーク関係の初期設定を開き、IPアドレスを手動に設定してから戻り、再びIPアドレスの設定を開いて自動を設定して戻る。この際、手動に設定した際に、IPアドレス等は打ち込まなくても良い。これでDHCP再割当てが行われる。注意点として、IPアドレス設定画面で手動・自動を切り替えただけでは再割当ては行われない。手動に設定してからIPアドレス設定画面を閉じる事により、DHCP割当されたアドレスを開放するような挙動。

 あとPCからRasPiにPINGは通るのに、インターネットにつながらない(PINGが通らない)という問題にだいぶ手間取った。症状からしてDNSだろうとあたりは付けてたが、コンフィグファイルにDNSを明示しても反映されない。ゲートウェイのIPを指定したり、オンラインのDNSを指定してもダメ。
 結局、別の設定ファイルに書いてあった旧ネットワークの設定が優先されていた、というオチだった。何年も前に設定したヤツだから、どこに設定したかなんて覚えちゃいない。「別のファイルにDNSの設定書いてみるか~」という軽い気持ちで開いたら、たまたまそれに古い設定が書いてあって判明した、という次第。

追記:2017/08/12
 数日に1回程度の頻度で、インターネットの接続ができなくなる不具合がある。HOME Wi-Fiのコンフィグ画面とかは開けるけど、ルーターの向こうへパケットが通らない感じ。ローカルのPINGは通るけど、オンラインのサーバーへはPINGは通らず、IPアドレスを指定したPINGも通らない。
 解決策としては、とりあえずHOME Wi-Fiのコンフィグ画面で再起動を行うくらいしかできない。それでも数回再起動しても復旧しないことがあり、その場合は電源を抜挿しての強制再起動しかない。さすがに電源を落としてしまえばどうにかなる。
 まだHOME Wi-Fi L01は発売から半年足らずで、バグはいろいろありそう。
 現在のバージョンは11.191.01.00.824で、これが最新らしい。それにしてもなんというバージョンナンバーだ。

 当初の予想通りというか、やはり10GB/3dayは簡単に超えてしまう。いままで制限してそれ未満だったから、制限が外されたらどうなるかなんて、ねぇ。
 でも時間を問わずyoutubeがストリーミングできて、アニマックスオンデマンドとかでアニメが見れて、みたいなのは結構快適。特に4k液晶だとHDのストリーミングでも画面の片隅で見れるし。
 帯域制限は、20時頃まではあんまり気にならない感じ。0時頃だとかなり制限されてる気がする。規則正しく早寝早起きすれば問題ない感じ。

 サービスエリアの隅の方で使ってるから、設置場所は結構シビア。ちょっと動かすだけでいきなり電波がつかめなくなったりする。WiMAXの波長は11cmくらいなので、5cmくらい動かすと改善するかも。最初に置いた位置に印をつけて、そこから半径10cmくらいで動かせばある程度受信強度高い場所は見つからるかな、という感じ。
 契約的にはLTEも追加無しで使えるはずだけど、こっちはまだ試してない。

XNAでサウンド出力

 C#で音をリアルタイムに作って出したかったので、XNAでやってみた。前にWin32でやった気がするけど覚えてない。

 XNAが開発終了って以外は、あんまり欠点のない方法、だと思う。

 DynamicSoundEffectInstanceのコンストラクタでサンプリングレートとチャンネル(Mono/Stereo)を選択。最初のデータをSubmitBufferで設定してPlayで開始。データが無くなるとBufferNeededイベントが来るので、ハンドラ内でデータを生成してSubmitBufferで追加。たぶんStopとか使えば止められるんじゃないかなー。
 Playは最初の1回だけでいいが、予めSubmitBufferでダミーデータでも追加しておかないと、ブツブツ途切れて再生してしまう。ちょっとしたダブルバッファみたいな感じになってるんだと思う。ということでPlayの前にBufferNeededを蹴っている。
 あとFrameworkDispatcher.Update()のタイミングが結構シビア。場所を変えると例外が投げられる。とりあえず今は動いているが、いつ動かなくなるかはわからない。
 それとFrameworkDispatcher.Updateを呼ぶタイミングは、SubmitBufferで設定した時間よりも短い間隔で呼ぶ必要がある。例えばBufferが50msec分なら、Updateは50msec以下のインターバルで呼ぶ必要がある。とはいえ、Thread.Sleepは往々にして設定時間の数割増しのインターバルとなるが、それでも正常に動いてるから、よほど大きく離れてない限りは問題ない様子。

 C#は例外的にSerialPortがある程度で、ファイルアクセス以外はほとんどIOが無い(マウスやキーボードはFormの機能)。音声IOも例外ではなく、使いたいならWin32とかで工夫する必要がある。
 XNAは前述の通り開発が終了しているが、ちょっとした音声IOで遊びたいなら有効なツールだと思う。


using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication9
{
    public partial class Form1 : Form
    {
        Thread t;
        bool loop;

        public Form1()
        {
            InitializeComponent();

            loop = true;

            t = new Thread(new ThreadStart(hoge));
            t.IsBackground = true;
            t.Start();

            FormClosed += Form1_FormClosed;
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            loop = false;
        }

        void hoge()
        {
            using (DynamicSoundEffectInstance audio = new DynamicSoundEffectInstance(44100, AudioChannels.Mono))
            {
                audio.BufferNeeded += Audio_BufferNeeded;
                Audio_BufferNeeded(audio, EventArgs.Empty);

                FrameworkDispatcher.Update();

                audio.Play();

                while (loop)
                {
                    Thread.Sleep(10);
                    FrameworkDispatcher.Update();
                }
            }
        }

        private void Audio_BufferNeeded(object sender, EventArgs e)
        {
            DynamicSoundEffectInstance audio = sender as DynamicSoundEffectInstance;
            if (audio == null)
            {
                return;
            }

            byte[] buff = new byte[audio.GetSampleSizeInBytes(new TimeSpan(0, 0, 0, 0, 50))];

            Random rand = new Random();

            for (int i = 0; i < buff.Length;)
            {
                Int16 data = (Int16)rand.Next(-16384, +16383);
                byte[] datas = BitConverter.GetBytes(data);
                Array.Copy(datas, 0, buff, i, datas.Length);
                i += datas.Length;
            }

            audio.SubmitBuffer(buff);
        }
    }
}

2017年8月4日金曜日

スライドスイッチ機能付感圧センサ

 秋月で売ってるアナログなスイッチを試してみた。
 スライドスイッチ機能付感圧センサ: センサ一般 秋月電子通商 電子部品 ネット通販


 青が位置、赤が圧力。圧力が0の時は位置データを無効としている。
 まずスイッチの端から端へスライド、次に逆へスライド。中央を押し、端を押し、反対の端を押し、中央を押す。その後、中央部に指を置いたまま、押す力を変える。そして強めに押しながら指をスライド、最後に触れる程度にスライド、という感じでテストしてみた。だいたいその通りの結果になっている。
 押す強さを変えた時に位置が動いたり、指をスライドする際に圧力の変動が大きかったり、といったところはあるが、それ以外は特に問題なさそう。

 軽く触れて動かせばスクロール、強く押せば確定、みたいな感じで指1本をひとつのパネルから動かさずに操作できるUIが作れそうで、面白そう。
 ミュージックプレイヤーなら、スクロールで音量変更、1回押し込み後のスクロールで曲移動、1回強押し込み後のスクロールでシーク、とかかな。電源ボタンを除けば、スイッチ1個だけのミュージックプレイヤーとか作れそうだ。
 このスイッチはクリック感等は皆無だが、裏に圧電素子とかつけて振動を与えればいろいろな表現ができそう。

 ちょろっと遊ぶくらいなら、かなり面白いスイッチだと思う。
 ただ、機器に組み込むのは、ちょっと面倒な感じ。端子部のフレキ基板がかなり硬いので、基板の向きの自由度がかなり低い。まぁそのあたりは工夫次第かな、という感じ。

2017年8月3日木曜日

OKL-T/6-W12N-C

 秋月で売ってる、ムラタのOKL-T/6-W12N-CというPOL降圧DCDCを試してみた。
 ムラタ表面実装DC-DCコンバーター OKL-T/6-W12N-C: 電源一般 秋月電子通商 電子部品 ネット通販

 単純に降圧DCDCとして使いたいなら、基板実装済みのモジュールが有るので、それを使うほうが遥かに楽。
 しかし今回は比較的狭いスペースに2個入れたかったので、ユニ基板に実装してみた。


 とりあえず、なんとか動いてるみたい。
 実装する際は、GND/Vin/Vout以外のパターンをカプトンテープで絶縁し、GND/Vin/Voutが接するスルーホールにハンダを充填する。またGND/Vin/Voutもハンダメッキする。メッキというか、BGAのように盛り上げる感じ。
 その後ユニ基板に正確に位置を合わせてVinあるいはVoutの裏側からハンダゴテで熱しながらハンダを入れる。GNDはパターンが広いのでかなりの熱量が必用のため、まずはVin・Voutで位置合わせをした方がいい。
 基板とDCDCの間には0.5-0.8mmくらいの隙間を開けると良さそう。薄いプラスチックカード(使い古しのamazonギフト券とか、もうちょっと薄いもの)をジグにして高さを調整すると良いかも。
 ある程度ハンダを入れて、基板とDCDCの間にボールになるようにする。ただし入れすぎるとGND/Vin/Voutで短絡するので注意した方がいいと思う。

 その他の端子、TrimとON/OFFとSenseを接続する必要があるが、これらの端子はほとんど電流は流れないはずなので、モジュール横の端面スルーホールに配線を通せばいい。

 ちゃんと6Aまで負荷をかけたわけじゃないが、とりあえずちゃんと動いている気がする。
 効率は素晴らしいと言うほどではないが、酷いと言うほどでもない。よほど電位差がない限りは、発熱はそこまで多くないはず。とはいえそれなりの発熱量は有るので、何らかの放熱は必用だと思う。



 Trimの抵抗値と電圧はこんな感じ。横軸が抵抗値、縦軸が電圧。
 代表的な電圧はデータシートに書いてあるが、3.3Vなら抵抗が2個、5Vなら抵抗が3個必要になる。中途半端な抵抗器を特注できるとかなら1個でも良いだろうが。


 ま、ちょっと大電流な降圧DCDCが欲しいだけなら秋月の変換基板を使ったほうが楽。多回転ボリュームがついてるので電圧調整が楽だし。人間がユニ基板に実装したモノの信頼性とかかなり低そうだし。


 僕は普段、片面基板を多用している。今回、久しぶりにスルーホールのユニ基板を使った。どんどんハンダが吸われるし、かなり面倒。ノンスルーホールな両面基板とかあると便利そうだけど、そうなると今回みたいに裏に熱を伝えたい時に困るんだよなぁ。かといってスルーホールを全部ドリルで削るのも大変だし。

2017年7月31日月曜日

Cの関数に多次元配列

 * 関数
static void hogeFunc(int y, int x, int arr[y][x])
{
    int i, j;

    printf("arr[%d][%d]\n", y, x);

    for (i = 0; i < y; i++)
    {
        for (j = 0; j < x; j++)
        {
            printf("%d ", arr[i][j]);
        }

        printf("\n");
    }

    printf("****\n");
}

 * 呼び出し
int arr[2][4] =
    {
        {
            1, 2, 3, 4,
        },
        {
            5, 6, 7, 8,
        },
    };
hogeFunc(sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]) / sizeof(arr[0][0]), arr);

 * 結果
arr[2][4]
1 2 3 4
5 6 7 8


C言語の引数に多次元配列を渡す - Qiita
BohYoh.com-C/C++ FAQ 2次元配列の要素数を取得するにはどうすればよいですか。

 関数呼び出しでsizeofが多くて面倒な気はする。あと呼び出し先関数でsizeofを使うとうまく動かない(配列じゃなくてポインタ扱い?)。
 この書き方はC99以降だそうだ。WSLのarm-none-eabi-gccの4.xだとgnu99を指定してコンパイルできる。
 組み込み界隈で本当に可変長の多次元配列が必用なのかという話は置いておいて。。。

2017年7月30日日曜日

フルカラー7セグLED

 秋月で売ってるフルカラー7セグメントLEDで遊んでみました。
 モノとしては、WS2812Bを内蔵した7+1セグメントLEDです。数字部の7セグに加え、右下にドットがあり、計8セグメントです。
 一般的に、7セグメントLEDを駆動するには、8chや16chといったLEDドライバを使用します。これにより、7セグLED1個を駆動できますが、複数個の7セグLEDを並べたい場合、例えば時計なら少なくとも4個、年月日も含めれば12個ですが、それだけの数を表示する場合、いくつかの方法があります。1つは、1個のドライバを使用し、複数のLEDを逐次切り替えて表示するという方法。もう1つは、7セグLED1個に対して1個のドライバを使用するという方法です。前者は比較的配線が少なくてよく、消費電流も比較的少なくて済みます。代わりに7セグLEDの輝度は低くなります。対して後者は、LEDの輝度を高くできますが、配線が多く、コストも高くなります。どちらにしろ、配線数は膨大になります。
 WS2812B内蔵7セグLEDの場合、電源の2本とシリアルバスの1本の計3本の配線があれば、任意個数の7セグLEDを表示することができます。今回試した限りでは、十分な輝度があり、表現性もかなり高いと感じました。
 一般的な7セグLEDは、輝度を変えるのは不可能ではありませんが、色を変えるのはかなり難しいです。一方、シリアルバスLEDの場合は24bitで色を指定できますから、自由に色を表現できます。もちろん輝度も自由です。
 ただし、2個で全点灯3.5W程度消費すること、全点灯だとそれなりに発熱すること、といった問題があります。また、単価がかなり高いのも問題でしょうか。値段については、複数個のLEDを並べた際の地獄のような配線作業を考えれば、4個程度までなら耐えられるでしょうか。それ以上になると地獄の配線作業のほうが安いような。。。

 7セグでも充分に表現力がありますが、理想を言えば16+2セグくらい有ると良いかな、という気がします。16セグあればアルファベットの表示も可能ですし、シリアルバスの利点も効いてきます。でも凄まじい値段になるんだろうなぁ。







 最後の1枚は輝度255,255,255の設定。それ以外は0以上32未満の乱数で設定。最大輝度だと明るすぎて撮影には不向き。

 他のWS2812Bを使ったモジュールと同様、FT232(Inv)から制御することができます。PCに接続してCPU使用率を表示して、使用率が低い時は緑、高い時は赤に近い色とか、数字でCPU使用率、色でCPU温度、とか、いろいろと表示ができると思います。
 もちろん複数個並べてワールドクロックとかもできますが、7セグLEDが帯に短したすきに長しな大きさで、時計に使うには小さいかもしれません。枕元に置く時計くらいの大きさでしょうか。少なくとも壁掛けの大きさではありません。

2017年7月22日土曜日

STM32F4で1Wire

 STM32F4で1Wireの温度センサを使ってみた。

 例によってロジアナキャプチャ。







 上から、全景、AD変化開始付近、データ読み出し付近、全景、ADC変換開始付近、データ読み出し付近。最初の3枚はロジアナ、残りの3枚はオシロ。

 ZEROPLUS LAP-Cは最近になってプロトコル解析が全て無料で行えるようになった。1Wireも含まれているので、データ転送を見るだけなら便利。ただしLAP-CではSEARCH ROM COMMANDの、アドレスツリー探索は正常にデコードできない。まぁアドレスツリー探索は例外的な動作ということで、脳内デバッグをすれば。とても面倒だけど。
 あと、LAP-Cの1Wire解析はデフォルトでMSBファーストとなっている。1WireはLSBファーストな仕様なので、ちゃんと設定しておくこと。

 1Wireはだいぶ前にFT232で温度センサを試したことが有る。1Wire通信だけなら問題ないが、パラサイトパワーではAD変換時にストロングプルアップ機能が必要になり、FT232ではこれを実現できないので、あんまり作り込んでいなかった。

 今回はSTM32F4のUART4を使用して1Wire通信を行っている。手持ちのセンサはパラサイトパワーのタイプなので、ストロングプルアップが必用。
 UART4はAF_ODで初期化するが、AD変換コマンドを送信後にOUT_PPに変更する。予めOutputはSetしてあるので、OUT_PPにすればマイコンからおよそ20mA(66mW)程度の電力を取り出すことができる。DS18B20のAD変換/EEPROM書き込み時の電流はtyp1mA max1.5mA @5Vとのことなので、マイコンからの出力でも問題ない。はず。
 本来はオープンエミッタ、あるいはオープンソースのようなピンモードがあれば楽なのだが、STM32F4にはそういう機能はない。ぐぐってもそういう使い方はないようだ。大体オープンコレクタ・オープンドレインで事足りるもんね。
 
 今回はDQを3.3Vに4.7kOhmで釣り上げ、20kOhmでGNDに落として、約2.7Vになっている。またストロングプルアップの時は3.3Vに短絡状態となるため、3.3Vになる。
 実際、オシロの波形ではAD変換中は3.3V、それ以外は2.6V程度となっている。
 ちなみにDS18B20の動作範囲は3.0-5.5Vなので、仕様どおりに使いたいなら分圧抵抗は外す必要がある。とりあえずストロングプルアップの動作確認に一時的に装着してるということで。

 1Wireはかなりアナログ寄りなデジタル通信なので、オシロとかあると便利。ロジアナも有ったほうがわかりやすいけど。
 アナログ寄りのデジタル通信というと、I2Cがあるが、それよりもさらにアナログ寄りだと思う。I2Cはクロックストレッチは有るが大抵のデバイスは必要ないし、クロックストレッチだってロジアナである程度把握することができるはず。ストロングプルアップを分圧抵抗で見ようとするとオシロスコープが必須。まぁ、I2Cのレベル変換ICとかはインピーダンスで自分の信号かほかデバイスの信号かを切り分けてるから、そのあたりは超アナログだけど、少なくともセンサをちょっと使いたいくらいなら気にする必要ないし。

 1Wireの通信は、今回はリセットパルスの生成に18.75kbaud、送信に125kbaud、受信に180kbaudを使っている。
 送信時は、0を出す時は0x00、1を出す時は0xFFを送る。受信時は0xFFを送り、0xFF以外なら0、0xFFなら1と認識する。
 リセットパルスは0x00を送信し、2バイトを受信できればデバイス有り、1バイトしか受信できなければデバイスなし、1バイトも受信できなければハードウェアに不具合がある。不具合の種類は1) 送信の問題、2) 受信の問題、3) 送受信間の接続の問題、等が考えられる。だいたいCANバスの自己診断に近い感じ。

 この通信方法では1Wireの1bitを送るのに、TTL232の10bitを送る必要がある。送信は12.5kbit/sec、受信は18kbit/secくらいの速度になる。
 デバイスの指定に64bitが必要だし、コマンドを送るのにも8bitが必用。データの読み出しの場合はさらに72bitが必要になる。AD変換開始コマンドを送るには約6ミリ秒、データの受信には約10ミリ秒がかかる。ただしDMAやNVICを使えるので、CPU負荷はあまり大きくない。
 すべてソフトウェアで作るとなると数マイクロ秒の精度で制御する必要があるので、ソフトウェアで回す必要があるし、割り込みを禁止したりとかする必要もあるだろうから、かなり厳しいと思う。その点、UARTモジュールを使うのは理にかなったやり方だと思う。


 ということで、STM32で1Wireを使ってみたよ、という話でした。
 今のところ、手持ちのデバイスがDS18B20が1個しか無いので、あんまり1Wireらしくないけど。もうしばらくしたら秋月でいくつか買って、多点温度計測をやってみたいなーと思う次第。

 ちなみに現在の室温は30.5±0.5℃くらいらしい。今日は曇ってるので日照は強くないけど、暑いことに変わりはない。



2017年7月21日金曜日

エレキットのタッチセンサ

 エレキットのタッチセンサを買ってみた。


 ↑コレは手持ちの部品でだいぶ改造してるけども。

 大雑把に説明すると、PIC12F1822のタッチセンサ機能を使ったモジュール。ピンアサインとかはamazonカスタマーレビューに書いてあるので省略。
 PIC使う人ならコレ単体でもコードを書き換えていろいろ遊べると思う。

 電圧範囲は3-5V。PIC自体は1.8Vから動作するけど、このモジュールは3Vからなので注意。
 ジャンパピンを開放すればモーメンタリスイッチ、短絡すればオルタネートスイッチとして動作する。モード切替は起動中でも変更できる。
 タッチスイッチは一瞬触れた程度では反応しない。短い一呼吸程度、気持ち程度のディレイが必用。

 タッチセンサのパッドはミシン目で切り離せる。ワイヤで延長するためのパッドもあるので、ちょっとした距離なら延長できる。

 短時間触れて遊ぶ程度なら誤動作とかは特に無い。
 10個とか15個とか並べればキーパッドを作れると思う。凄まじい値段になると思うけど。
 電源スイッチ1個だけとか、せいぜい2,3個程度までのモノを作るのに使えば良いんじゃないかな。

 タッチセンサーキットの基板はかなり単純なパターン。必用とあらばテキトーなユニ基板とか、空中配線とかでも使えると思う。ここまで来るとプログラム書き込み済みのPIC単体を売ってくれとか、そういう話になってきてしまうけども。

 僕の使いたいものは、電池駆動でタッチセンサを使って起動するみたいなモノだけど、自分で電源を切ったりもできるようなのがいい。
 ということはLDOとかでタッチセンサを駆動して、モーメンタリの出力でメインのDCDCのENを駆動、DCDCの出力を使ってENを自己保持、マイコンからbreak、みたいな感じになるのかな。
 このあたりはいろいろ試す必要があるだろうが。 




#if 0
 今更だけど、4月期のアニメを見始めた。1話だけみて面白そうだと思って録画してたんだけど、結局2話以降全然見てなかった。2クールやるらしいのでまだ放送中。だいぶいい感じだ。でも僕がこういう好みのベクトルのやつ見始めると終盤ろくなことがないからなぁ。

 最近いろんなアニメの再放送をやってる。SAOとか、涼宮ハルヒの憂鬱とか。この際にと録画してるけど、HDDの空き容量がやばい。
 DIGA買った時は1TBあれば充分とか思ってたけど、あっという間に埋まってしまった。外付けの3TBを買って今度こそ安心!と思ったらこの世代のDIGAは外付けHDDに1k本までしか録画できない仕様(当時の型落ちで安いやつを買ったけど、現行モデルはもうちょっと緩和されてる、はず)。

 そう言えばナイツ&マジックのアニメ化も見てる。Web版で途中まで読んでたけど、途中までしか読んでないってことは、微妙に僕の好みと違ってたんだろうなぁ。
 オーディオコメンタリーで「ロボットに乗るかどうかを葛藤するアニメはよくあるが、自分から乗るような作品は少ない」みたいな話をしてた。さもありなん。
 アルドノア・ゼロは進んで乗ってた気がする。まぁ上官が葛藤してたが。でもA/Zはロボットが好きだから乗る、じゃなくて、最適解がロボットだからそれを使う、みたいな感じかも。
 カタフラクトくらいの地上にへばりついたロボットは好きかな。ガスタービンエンジンで発電?して走り回るってのもエネルギー的に無理がなくて良さそう。人形で空を飛んだりしちゃうとちょっと僕の想像の埒外でつらいかんじ。
 そう言えばカタフラクトは空を飛ばないんだよな。ちょーっと長い距離を跳ねたりとか、パラシュートで降下したりはするけど。あと宇宙に行くけど。地上を歩き回ってる限りならはいふり縛りでもできそうだ。はいふり世界にカタフラクト出して何に使うかって問題は有るけど。はいふり世界にも揚陸艦ってあるのかな。海岸から侵攻とかしない世界だと揚陸艦はなさそう。でも水害が多い地域だと救難その他で使えそうだし、海面とか地盤とか動いて沈み込むような世界なら揚陸艦は発展するかも。ならブルーマーメイドが操艦する揚陸艦でカタフラクトがを輸送するみたいな妄想も……
#endif

17042

 夜空に最も明るい星が新たに誕生? - その正体はロシアの宇宙灯台「Mayak」 | マイナビニュース

 こんなのが打ち上がってたんですねぇ。

 毎度のJS Orbitリンク

 同時に投入された物体が70個以上あり、一部は未確定です。おそらく、名前が出た後も正しいラベリングになるには相当時間がかかると思います。

 件の衛星の名前はMayak(マヤーク)だそうです。まだそれらしい名前は軌道情報にはありません。


 打ち上げられてからそれなりの日数が経っているため、かなり散らばってしまっています。真っ先に展開に成功していれば、一番先頭にいるのがMayakでしょうが、展開がトラブっててまだ展開してから時間が経っていない、とかであれば中途半端な位置にいる可能性が高いです。
 ある程度時間が経てば、軌道半径の経過からMayakを特定することは可能でしょう。実際、膜展開を行ったFREEDOMというキューブサットは、同時期に投入された他のキューブサットと比べ、明らかに落下が早かったです。
 でもまぁファーストライトの観測を目指すなら、そんな悠長な事はしてられませんから、総当たりで行くしか無いかもしれません。


 僕がやるならベランダにα7s放置で30分くらい撮影かなぁ。本当にVmag-10になるなら比較明合成をすれば一発でわかるはずですし、そこにいるのがわかれば30分の動画を見れば済むことですから。
 でも家のあたりは来週中頃まで曇りとか雨の予報ですから、しばらくは静観ということで。


 軌道高度は約600kmのほぼ円軌道ですから、日照条件はISSよりはマシだと思います。地上がある程度暗く、かつ軌道上は日照状態、となる時間が、ISSよりも長いはずです。ということは、ISSの肉眼観測よりは観測可能ウインドウが広いはずです。

 しかし、日本上空で地上が暗く、上空が日照という条件は、少なくとも半月ほどはかなり厳しい感じです。


 地上が暗く衛星が日照になるのは、日本では北海道の北端付近しかありません。今後半月ほどはこの調子のようです。
 北米大陸や南米大陸はかなり条件が良さそうです。

 とはいえ、衛星の反射膜の素材によって、輝点が見える範囲も変わるはずです。乱反射するような素材であれば広範囲で暗く見えるし、綺麗に反射する素材であれば狭い範囲で明るく見えるはずです。イリジウムよりも明るいということは、可視範囲はかなり狭いはずです。また反射膜の写真を見ると、アルミホイルみたいな感じですから、反射光はかなり狭い範囲にしか届かないと思われます。
 望遠鏡で追尾すれば乱反射成分でも見えるかもしれませんが、衛星を追尾でき、かつ暗い目標でも見える望遠鏡となると数が限られますし、候補が70個以上ありますから、1個1個調べるだけでも一苦労だと思います。


 とりあえず、数日で落ちてくるような衛星ではないでしょうから、もうちょっと様子を見てみましょう。


追記:2017/07/23

 2017-42で打ち上がった物体の軌道半径をプロットしたグラフ。中央に1個物体が有り、上下に2組有る。実際に軌道を地図にプロットしてみると、中間に物体が1個あり、前後に物体が有る。
 少なくとも5日程度のプロットで目に見えて高度が下がってる物体はない感じ。
 それにしてもこんな軌道にどうやって投入したんだろうか。軌道進行方向側に放り投げた物体と、後方側に放り投げた物体の違いなのかな。

 このグラフを作るのに、自前のTLEデータを捜索するプログラムと、エクセルのファイルを2個、使っている。
 TLEデータは過去1年分ほどのTLEファイルがあるが、テキストファイルで350MB、2万5千ファイルくらいある。コレだけのファイルを全部開いてチェックするとなると、アンチウイルスソフトのチェックのほうがボトルネックになってる感じ。
 エクセルの方は1つめが26MBほど、2つめが73MBくらいある。開くのにはちょっと時間かかるが、まぁほっとけばちゃんと表示できるし。

 今日は今のところ晴れてる。けどウチの上空を通るパスは23時頃か。どっちにしろ日照条件が悪いけども。ISS放出なら少なくともISSは写るだろうけど、ロケット放出だとそれもないからなぁ。
 そう言えば今回のロケットの上段ってどれくらいの規模なんだろうか。少なくともキューブサットよりは遥かにデカいだろうし、高感度で撮影しておけば映り込むかも。
 件のキューブサットとの切り分けが面倒そうだねぇ。こっちは理想条件ならvmag-10とからしいけど、そんな明るさはめったに出ないだろうし。

2017年7月17日月曜日

LEDストリング

 WS2812BというLEDチップを内蔵した、テープ状のLEDストリングを買ってみた。以前にも同じチップを内蔵した物を試したが、その時はマイコンのGPIOで操作していた。
 今回はマイコンのプログラムを書くのが面倒だったので、FT232とC#で制御してみた。FT232H(高速品)が必要かと思ったが、昔からあるチップでも制御できた。ただしTTL RS232は負論理のため、WS2812Bの正論理の信号を作れない。
 NOT回路を作ろうかと思ったが、ちょうどいいFETの手持ちがなく、手持ちのトランジスタでは6MHzの矩形波は通らないので、FT progでFT232の論理を反転させた。
 ボーレートは最大の3Mbaudに設定し、1バイトで2bitを送信するタイミングで信号を送信した。本来のタイミングからはそれなりに逸脱しているが、WS2812Bはかなり信号幅に余裕がようで、とりあえず動作確認程度に制御するなら問題なさそう。

 今回試したストリングは1mあたり60個のチップが実装されており、1.66...cmピッチとなる。
 5m(300個)で非点灯時の消費電流が約0.12A(4V時)、20個点灯させて0.9A(4V時)くらいだった。静止電流が0.4mA/個/4V、点灯時に40mA/個/4Vくらいかな。
 5m全体を点灯させると12Aくらいになる。50Wくらい。そんなに流せる電源は持ってない。手持ちの電源だと最大でも80個くらいしか表示させられない。電圧範囲は5Vくらいまで有るが、5Vで動作させるなら100Wクラスの電源が必用かも。
 とはいえ今回買ったLEDストリングの電源ラインはめっちゃ細いので、連続で10A流すのは無理だと思う。せいぜい1mで分割してそれぞれ電源を接続する感じかなぁ。電線を太いのに交換することも可能だろうが、そもそもフレキ基板の細いパターンだから数Aが限界じゃないかな。
 1mだと2.5Aくらいなので、6Aまで流せるPOL降圧DCDCを使えば2m分を駆動できそうだ。降圧DCDCを通すならLiPoやNiMHのような不安定な電源でも問題ないかも。いろいろ遊べそうだ。


2017年7月16日日曜日

メモ:MacBook Pro (13-inch, Early 2011)で外部液晶が表示されない

 MacBook Pro (13-inch, Early 2011)で外部液晶を使用しようとしたところ、表示されなくなった。症状は、外部液晶を接続したところ、内蔵モニタのバックライトが消え、外部液晶の信号入力もない。つまりすべてのディスプレイが無信号状態。
 数日前には別の液晶を使っていたので、ここ数日で壊れたとかでもない限りは問題ないはずなのだが。
 液晶の問題かと思って型番でググっても特にクレームとかは見つからなかった、ということでPC側を疑う。

 結局、BootCampで入れたWin7に入ってるIntel Graphicsの関係だったっぽい。

 設定方法としては、タスクバーのアイコンでインテルHDグラフィックスのアイコンをクリックして、”グラフィックプロパテイ”を選択。
 ”インテル(R)グラフィック/メディア・コントロール・パネル”で”詳細設定モード”を選択し”OK”をクリック。
 左側の”オプションとサポート”で”ホットキーマネージャー”から”内蔵ディスプレイの有効化”のキーを確認する。デフォルトではCtrl-Alt-F3だが、MBPではFキーが使えないのでテキトーなキーを割り当てた。
 そして外部液晶を接続する。まだ何も対策をしていないので、内蔵ディスプレイも外部ディプレイも沈黙する。その時点で先程の”内蔵ディスプレイの有効化”キーを押す。そうすると内蔵ディスプレイの表示が復活する。
 あとはWin-Pを押して、外部ディスプレイのモード(切断・複製・拡張・外部のみ)を選択する。
 最後にホットキーの設定画面で”デフォルトの復元”をクリックしてキーコンフィグを戻す。


 運良く短時間で解決することができた。どうして外部液晶接続時に内部・外部両方共OFFになる設定になったのかは分からないが。

 それにしても、このノートPCも6年以上使ってるのかぁ。いや、特に不満とかないんだけど。
 次はTOUGHBOOKとかほしいなぁ。トカイッテミル

VL53L0X

 VL53L0Xというセンサを試してみた。
 STMicro製で、ToF(Time of Flight)というタイプの距離センサ。ざっくり説明すると、超音波距離計の光波バージョン。
 以前にSTMが似たセンサを出していたが、そっちはレンジがかなり狭かった。VL53L0Xでは最大2m程度までレンジングできるらしい。



 テスト環境。STBee F4miniの上にVL53L0X変換基板を載せてる。穴位置がかなりちょうどいい。センサ本体はかなり小型だけど、付加回路がいろいろ必用だったりコネクタが大きかったりで、結局DIP16の変換基板くらいの規模。

上からIRで撮影。さすがにレーザーなので明るい。焼付きが怖いので真上からは撮影しなかった。

ちょっと斜めから撮影。すぐ見えなくなる。

中央右下にセンサ、左上に手のひら。距離は5-10cmくらい? これくらいの距離ならIRで照らされてるのがわかるけど、指向性が低いのでちょっと離れればすぐわからなくなる。


 このセンサはSTMicro製でありながら、わかりやすいデータシートというのが一切ない。曰く「わかりやすいAPIを用意したからそっちを使ってね」ということらしいが、僕が試した限りではAPIは初期化でコケて動作しなかった。
 今回はサードパーティのArduinoライブラリをC言語で書き換えて移植した。一応測距はできるし、距離に応じて値も変化するが、精度はあんまり出ていない感じ。
 結局本家APIを動くようにする必要がありそうな気がする。メンドクサイ。


 センサ自体は0.94umの赤外線レーザーで、データシートにもeye safeとか書いてあるが、0.94umは網膜に結構吸収されるらしいので、あんまり直視しないほうが良いと思う。と言っても非可視光だから、レーザーが目に入っても全く気が付かないのだが。


 なつやすみのじゆうこうさく でVL53L0Xを使いたかったのだが、結構大変そうだ。本当に夏の間に終わるんだろうか。下手したら来年の夏とかになってしまうんじゃなかろうか。。。


追記:2017/07/19
 APIに付属するLinuxDriverMassMarket_1.0.6/kerner/drivers/input/misc/vl53L0X以下のソースを使ったらいちおうレンジングできた。移植するのがとっても、とっても!面倒くさいけど。

 あとLinuxってCCI(Camera Control Interface)にVL53L0Xを突っ込んで動作確認できるみたい。映像出力端子にもI2Cバスは出てるけど、こっちは標準化以外の信号は通すの大変なのかも。カメラインターフェースならメーカー独自機能とかを実装する余地があるとか? とにかく、Winみたいな、外部にマイコンを置いて、それ経由でI2Cを叩いて、みたいな七面倒臭いことはしなくていいらしい。
 それなら最初からマイコンで動くコードを公開しておけよ、という気もするけど。せいぜいI2C R/W回りの関数2個を書き換えれば移植できるだろうから、いちいちプラットフォームに依存した変なサンプルとか作らなくても良いはずなんだが。


追記:2017/07/20

 I2Cバスのパケットをキャプチャ。I2Cは200kbaud。立ち上げに250msecかかっており、1回のデータ転送にも35msecほどかかっている。とにかく1回のデータ転送でのパケット転送量が多い。

 あとLongRangeモードのExampleを試してみた。屋内でたぶん2mくらいまで計測できてるんじゃないかなぁ。だいたい150cmくらいは取れてるし、手のひらをかざせばそれに応じて結果も変化する。ただ精度は不明。

 今回の用途だともうちょっと狭角のほうが使いやすいんだけど、センサの本来の用途としてはこれくらいの角度が良いんだろうなぁ。
 データシートのApplicationsにUser detection for Personal Computers/Laptops/Tabletsとか書いてあって、顔の真正面からレーザーをガンガン浴びせるつもりでいてちょっとアレ。いくらアイセーフ(?)とは言えさぁ。
 でもVL53L0Xはレンジング中以外はレーザーを止めるので、人間がいない時は照射間隔を100msecとか500msecで応答性重視、近くに人間がいそうであれば5000msec周期くらいで照射間隔を広げて、みたいな安全策はできるかも。どこまで効果あるのか走らんけど。
 はっ、このレーザーを浴びれば虹彩の色が抜けて…?


 とにかく、VL53L0Xは使用コストがかなり高そうだ。特に30msec周期近い高レートで測距を行う場合、I2Cバスはほぼ100%近くの使用率になる。当然ポーリングで通信を書いているとCPU使用率も同程度になる。
 I2Cバスに他のセンサをつけたり、VL53L0Xを複数個使おうとすると、相当面倒なことになりそうだ。高レートで2-4個使いたいと思ってたんだけどなぁ。I2C転送中も大抵は1バイト単位での転送だから、DMAとか使ってもほとんどCPU使用率は変わらなそう。1バイトの転送はせいぜい数十usecだから、割り込みとか使ってもOSのオーバーヘッドであんまり効果なさそう。

2017年7月8日土曜日

OpenCvSharpのVideoWriter覚書

 OpenCvSharp2のVideoWriterでファイルを書き出す際の注意点とか。

 VideoWriter.WriteではMat形式の画像を受け取るので、Matで渡す必要がある。OpenCVで画像をいじるだけなら問題ないが、C#のGraphicsで図形を書いたりしようとすると、System.Drawing.Imageから変換する必要がある。
 OpenCvSharp.Extensions.BitmapConverterでToMatやToBitmapがあるが、なぜかToMatで変換した画像をVideoWriter.Writeに渡しても保存されなかった。
 適当なファイルにImage.Saveで保存し、new Matで開いても良いのだが、ディスクアクセスは極力避けるべきであり、工夫する必要がある。
 とりあえず、MemoryStreamを作成し、Image.SaveでStreamを指定、その後Mat.FromStreamでStreamを読み込み、という手順で変換することができ、動画として正常に保存できる。また、逆の方法でMatからImageにすることもできるはず。とはいえ、OpenCvSharp.Extensions.BitmapConverter.ToBitmapでSystem.Drawing.Bitmapに変換してSystem.Drawing.Graphics.FromImageに渡すのは問題ないから、Mat→BitmapはToBitmapで問題ないと思う。


 動画ファイルはAVIやMP4でも大体ヘッダにS32bitとかでファイルサイズが書いてあり、2GB(or4GB?)を超えるファイルは作成できない。しかしFHDのMJPGとかだと数分で2GBを超えてしまう。
 とりあえず、VideoWriter.Write後にFileInfo.Lengthでファイルサイズを確認し、閾値を超えるようであれば新たな動画ファイルを作成する、というクラスを作ってみた。たぶんうまく動いてると思う。


 いつぞやの、OpenCvSharpのオプティカルフローで手ブレ補正に再挑戦してる。まだ上下左右の2軸しか補正してないが、かなり見やすくなる。ブレ補正は米軍のドローンでよく使われてるタイプの補正方法。
 本来は回転方向のブレも修正すれば見やすくなるのであろうが、なかなかうまくいかない。特徴点から回転を検出するのはスタートラッカで作ったので、その応用で簡単に作れそうだなと思ったんだけど、そうは問屋がおろさない。コマッタ。

2017年7月4日火曜日

上富良野駐屯地62周年行事

 地元駐屯地の行事に行ってきた。2017年7月2日開催。
 去年は行ってないので2年ぶり。おおよそ例年通りだと思うけど、第14施設群が新設されたこと、習志野から第1空挺団が展示に来ていたこと、といった違いが有る。細かいところを挙げると、2年前までは有った小火器や無反動砲の展示がなくなっていた。


92式地雷原処理車のライト。右上カドの天守閣みたいなのが箱型ランチャーと本体をつなぐ蛇腹部分。

10式戦車左側後方から砲塔壁面の荷物入れとかセンサとか。荷物入れというかスペースドアーマー。中段右側の3個並んだ窓がセンサー部。その上の丸い円筒がアンテナの基部。

10式戦車のバックカメラ。

バックカメラの部分を拡大したもの。かなり頑丈な雰囲気。

砲塔壁面のセンサの拡大(4箇所の内どこかは忘れた)。防衛装備品で基板やら電子部品やらむき出しってのはあんまりない気がするので面白い。画素むき出しだけどどういう光学系になってるんだろうね。部品はあんまり微細化されてない感じ。

履帯の拡大。ピンぼけしててわかりづらいけど、右側の突起が若干削れてる。

フロントカメラ部分。

観閲式・訓練展示等の放送席? アンテナがいっぱい生えたトラックとか。 駐屯地内ではマンパックの無線機を背負った隊員をよく見かけた。駐屯地内でしか通信しないだろうからもっと小型でも良いと思うんだけど、特小じゃ厳しいだろうし、となるとマンパックのHFとかになるのかな。単体で背負える利点は大きそう。

後ろの黄色いリボンは吹き流し。感度はかなり高い。

こちらも風向を調べるためと思われる風船。等間隔に並んでるのは、上空から見て風船の間隔で傾き具合がわかるように?

風船の右下。風向風速計とかいろいろ。

観閲式の始まり。

結構風強そうだけど、体感ではほとんど無風だったと思う。

空挺降下の展示。3人がスクウェアでフリーフォール。いきなり空中に人が現れたり、しばらくそのまま浮かんでたり、と思えばパラシュートを開いたり、Arma3のパラシュートModと全く同じ雰囲気だった。

パラシュートの前側↑、後ろ側↓。


3人中2人はグラウンドに着地、最後の1人は目の前の道路に着地。

パラシュート回収して集合。

空挺降下が終わると吹き流しは回収。

 








↑ここから5枚ほど、新設された第14施設群の地面をえぐる(orえぐっちゃう)系の装備品。





10式戦車に旭日旗、 96式装輪装甲車に町の町章。

90式戦車と、その後ろに10式戦車 。そう言えば74式はいなかった。


演習展示の開始。

敵役は相変わらずの74式。

偵察にUH-1。3年前はOH-6だったはず。2年前はちょうどOH-6の飛行停止期間で、UH-1のラペリング降下をやった気がする。

82式指揮通信車がM2で空砲射撃。2両いたけど、片方は不発が多かったみたい。



発砲とか、大きな音がする際は隊員が警棒を持ち上げて教えてくれる。といっても、来賓席の前にしかいないので、端の方にいると気が付かない。








203mm自走榴弾砲と、右側にいるのが地雷原処理車。マインスイーパーは今年新設された部隊のもの。

↑投擲する前。
 ↓投擲した瞬間。
  演出用のスモークグレネードやスタングレネード?を偽装網の中から投げ投げ込んでる。


右2両が90式、左2両が10式、兄弟というか親子というか。後ろの方には指揮通信車も。

手前の10式と奥の90式。 見分け方は、笑窪が有る方が10式。


ある程度制圧できたら普通科が射撃。




敵に見立てたターゲットプレートの方まで走り込んで、そのまま窪地にフェードアウト。
 敵陣を制圧して、演習展示は終了。

雨に打たれる10式。

10式の車輪止め。

軽装甲機動車の窓。

グラウンドの隅にある吹き流し。グラウンドの真ん中にはヘリパッドがある。

上富良野駐屯地のカレーは美味しいらしい。「美味しいらしい」という噂はちらほら聞こえてくるけど、「美味しい」という話はあんまり聞かない。気がする。今年はじめて食べた。たぶん美味しかった。

入り口から進んで向かって右側に手前から203mmの弾薬輸送車、203mm自走榴弾砲、MLRS、88式地対艦誘導弾、マインスイーパー、という感じ。
 左側には軽装甲機動車や90式戦車、10式戦車が置いてあり、他にNVの体験や災害救助用の装備が展示してある。ここにも74式戦車はなかった。

軽装甲機動車の 前輪回り。

203mmに乗ってる消化器。基本的に密閉空間(車内等)は粉末型、開放空間はCO2型が搭載されてる(気がする)。

  小型のボートがたくさん積んであった。

戦車の体験搭乗。といっても車体後部のかごに座るだけだが。 結構人が並んでたので今回は乗らなかった。



 ということで写真は一通り終了。

 開始直後あたりは全天が曇ってる感じ。第1空挺団の人と話してても「空挺降下はたぶんできると思うんですけどね~」みたいな感じだった。
 空挺降下が終わって、演習展示が始まった後くらいからポツポツと雨が降り始めて、一時はちょっと強く降ってた。でも演習展示が終わった後はあんまり降ってなかったと思う。曇りで直射日光はなかったので、そんなに暑くなく、過ごしやすいという感じの天気だった。
 降下は「最低高度の1000mから」と言っていた。説明には最低高度は600mあたりと書いてあったけど、平時と戦時や訓練時の基準は違うんであろう。
 降下時はUH-1が若干雲に入ってるように見えたので、もう少し雲底が低かったら中止になってたかも?あるいは雲底付近まで高度を上げた可能性もあるけど。


当日のGPSログ。だいたい歩いたとおりに記録されてる。中央左上寄りのグラウンドが出店の会場。西側から南に出たところが車両の展示。さらに南に下って広い土面が演習展示の会場で、その北側が観客席。

 普段僕はForetrex 401で時間しか表示してないので、大抵の人には腕時計だと思われるんだけど、第1空挺団の人はひと目見てGPSとわかったらしい。さすが精鋭無比の第1空挺団。
 でも彼らは米軍と共同訓練とかすると、大抵の米軍空挺とかはForetrexつけてる気がするし、そういうので見慣れてるのかも。あるいは自分たちも使ってるのかな。そこまでは聞かなかった。

 ちらっと話を聞いた限りでは、空路で潜入した場合は、パラシュートは各自必要な分を切り取って、あとは放置らしい。もちろん訓練の時は回収するけど。
 各自必要な分というのは、ロープとしてのパラコードだったり、防寒としての布部分だったり。マンハントでジョエルがアリゾナでやったような感じ。


 今回はImpact Sport(電子イヤマフ)を持参してみた。203mmの空砲とかでも、耳に痛いような音ではないし、大きな音が消えればちゃんと回りの音も聞こえるので、2年前のSurefire EP4とくらべて状況認識は改善したと思う。とはいえアナウンスの低音量はそのままで、電子イヤマフとはいえSNRは改善できないので、EP4でも十分かなーという感じ。


 北海道防衛局のブースではいろいろお土産をくれた。といってもペラペラからかなりの厚さまでのパンフレットや、ボールペンくらいだけども。自衛隊以上に影の薄い組織っぽいので、アウトリーチに苦労してそう。

 第1空挺団の人はかなり気さくな感じで話しやすかった。上富良野駐屯地の展示は話しやすい人もいれば話しづらい人も色々。やはりコミュ力高そうな人が対応してくれると色々話せるので楽しい。そもそもこっち側のコミュ力が足りないだけじゃないかという気もするけど。


 ということで今回はここまで。