2017年11月23日木曜日

メモ:フェーズドアレイ

 遅延量の計算は以下ページの下の方の図がわかりやすい。
Radar Basics - Phased Array Antenna

 平面(焦点が無限遠)の合成波を作りたい場合、アンテナ面からの角度をθ、素子間隔をdとして、d sin θで遅延量が計算できる(開口が線上にある場合)。
 比較的近い距離に焦点を結びたい場合の計算は書いてない気がするが、焦点から開口までの距離を波長で割った余りが遅延量となる。

 ざっくりと計算してみたけど、意外とサイドローブが多い印象。もっとも、無指向性の開口で計算してるから、半値角90°くらいの開口なら、ある程度サイドローブは減らせそう。
 簡単な計算だと、2次元でしか表示できないので、平面に置こうとすると、開口は1次元にしか作れない。2次元平面に開口を置いた計算をやってみたいけど、かなり面倒そう。

 超音波素子で開口合成をやったら楽しいかな、と思ってた。レーダーだと法的に厳しいし、高周波扱うの面倒だし、光速の計測は大変だし、ということで、遊びでやるなら超音波が楽かな~と思った次第。

 すでに、開口数百素子の超音波フェーズドアレイ素子ってのを作ってる研究室があるらしい。これはもっぱら非接触で物体を操作するための物。
 受粉を機械的に行う、とか応用が書いてあって面白い。現在でも植物(果物、野菜、etc)の受粉は風や蜜蜂任せだが、自然相手なので扱いづらい。特に蜜蜂は使用できる農薬等の制限が厳しいらしい(うろおぼえ)。植物工場とかだと、ほぼ無菌状態で栽培できるので、農薬が一切不要、かつ出荷時・出荷後の洗浄が不要、という利点があるが、蜜蜂等を持ち込むとその利点がなくなってしまう。そういう環境では、多少高コストな機材が必要でも、うまく付加価値をつけれればちゃんとやっていけそう。


 マイコンでフェーズドアレイをやるのはどうやれば低コストでできるかな、と考えてみても、いまいちいい方法が思いつかない。
 STBee F4miniだとFPUがあるので、位相計算は楽だけど、せいぜい32素子くらいしか扱えない。それに外部回路もかなり大量に必要になる。データレートもかなり必要になるので、そのあたりも不安。
 件の研究室でも、やはり小型化・低コスト化がネックっぽい。超音波AESAだと計算量はそれほど多くない気がするけど、ピン数がかなり大量に必要になる(数百~)。こういうのはFPGAでやるより、ASICで量産したほうが低コスト化できそう。基準クロックと位相シフト量を与えればいいだけなので、それほど頻繁に回路を書き換えるわけでもないし、頻繁に変わる部分はFPGAにしちゃえばいいし。


 AESAの計算のついでに、FRESCANも試してみた。並べた開口に、順番に波を入れれば、d=n wavelengthの場合はまっすぐに波が合成されるが、周波数を変えると、d != n wavelengthで、合成波が斜めになる、というタイプ。
 古くは対空レーダーや、最近では車載レーダーに使われてるらしい。メリットはRF周りの低コストさ。パッシブフェーズドアレイでも増幅器は必要だし、移相器も必要になる。アクティブフェーズドアレイも言わずもがな。FRESCANは送受信機が1個で済み、周波数を変えるだけでスキャンできるという利点がある。
 一方で(軍用レーダーでは致命的なことだが)、開口の周期で周波数が固定されてしまう、というデメリットが有る。これは敵からすれば、周波数を見るだけでレーダーがどっちを向いているかがわかってしまう。周波数と角度の関係がわかれば、特定の周波数を入れれば、特定の角度に偽像を表示することができる。他にも色々ありそう。
 FRESCANは電磁波(光速に近い伝搬速度)には便利だが、超音波でやろうとすると、とても面倒なことになる。ということで、この方向は見込み無しということで、あんまりちゃんと確認してない。


 対空ミサイルの仕組みを調べると結構面白いし、レーダーの仕組みも面白いし、F-35Bとかも面白そうだし、こういうの作ってみたいなぁ、とは思うけど。
 ミサイルはモデルロケットで、ターゲットはPhantomでも借りれば、誘導の実験くらいはできるかもしれないが。
 レーダーは法的につらい。超音波で首振りくらいならできそうな気もするけど、あんまりおもしろくなさそう。
 ラジコン飛行機はやってみたい分野だけど、アレもお金かかるからなぁ。2.4GHz化で一気にアビオニクスの値段が上がってしまった感じがある。一昔のラジコン飛行機は安かったのになぁ(10年位前の話。入門用の1.5mくらいのEP機がフルセット1.5万円だった)。

 対空ミサイルは、レーダー誘導ならアンテナでどうにかしてるんだろうなーって気がするし、赤外線誘導もなんとなく想像できるし、GPS誘導爆弾は作れそうな気もするけど、レーザー誘導爆弾は全く想像できない。シーカーってどうなってるんだろうか。


 最近、気力ゲージの減り方がヤバイ。寒いからかなぁ。でも夏は夏で暑いと言って似たような感じだし、いつもこんなもんな気もする。

メモ

 とりあえず無事にWin10をクリーンインストールすることができた。

 インストール時はシリアルナンバーとかプロダクトキーとか入れなくてもインストールできる。まだライセンス云々のメッセージは何も出てきてない。

 11月20日にダウンロードしたWindows 10 x64のISOをDVDに焼いたが、OSバージョン1709、ビルド16299.15で、Fall Creators Update適用済みっぽい。
 見た目は何も変わってないが、タスクマネージャにGPUのグラフが追加されてるので、FCU適用済みだと思う。

 回線が細いので、オンラインでインストーラーを落としてこなければならないソフトウェアはほとんど入ってない。
 来月になれば回線の制限も解除されるので、来月になるのを待ってからOS入れ直せばよかったなぁ、と後悔。


 MBはASUSのB85-PLUSだが、UEFI BIOSでSerial ATAの設定からホットプラグを有効にすると、OSでHDDをリムーバブルドライブとして使うことができる。
 ケースの上面にSATAの3.5インチ?5インチ?が刺さるような部分がある。そこに裸のHDDを刺せば、リムーバブルドライブとして使えた。
 昔、HDDを刺すとOSが落ちる、というのが頻発して、トラウマになってた。なのでほとんどこの機能を使ってなかった。調べてみると、電源が弱いPCでは突入電流でクラッシュするらしい。さもありなん。当時はPCを安く組むために、安い電源を使ってたはず。その後、かなり余裕のある電源に交換したので、HDDの挿抜に耐えられるようになったのではないか、と予想。


 Lightroom5はHDDをコピーしてくるだけで問題なかった。カタログとか全部がひとつのHDDに入ってるので、HDDをまるっとコピーするだけ。
 ドライブレターさえ合わせておけば、カタログの問題は特にない。


 いままでArma3を起動するときに、初回インストールが毎回コケてたのだけど、今回はちゃんと入った。でも相変わらずレーザー測距/誘導ができない。これはバックアップから復元してるから、ファイルが破損してるのかなぁ。来月になったらインストールし直してみよう。
 FPSは相変わらず4kで60fpsくらい出る。でもGeForce Experienceの最適化ができない(設定ファイルを読み込めない)。最適化しなくても60fpsくらい出るので、いいかなという気もするけど。


 今のところ、Cドライブ(4TB)の空き容量が1.87B、データドライブ(6TB)の空き容量が2.2TBほど。写真を撮るペースにもよるけど、2年は持たないかも。Cドライブはこれから色々インストールするけど、それでも1TBは使わないはず。


 試しにVS Codeを入れてみたけど、相変わらずドキュメントのフォーマットができない。何のためにOS入れ直したんだかw(データドライブの容量/寿命が主目的)。ま、オートフォーマットはAStyleでやるので、VS Codeはどうでもいい。

 いくつかバックアップを忘れているファイルがあるので、いつか取ってこなければ。
 今回はドキュメント・ピクチャ・ミュージック・ビデオ・ダウンロードあたりをHDDに丸ごとコピーしてきたけど、プログラムの設定データとかはコピーできてない。
 AppDataとか移動すればいいのかもしれないけど、あんまり古い環境を大量に持ってくると、せっかくクリーンインストールで安定した環境が戻っちゃうからなぁ。


 というあたりで、遅々として進まないダウンロードのプログレスバーを睨みながら、今回はここまで。

2017年11月22日水曜日

メモ:Mk 41 VLS

 セルのハッチの横についてる、穴の空いた小さい四角柱状の突起物って何のためかなぁ、と気になってたんだけど、どうやらstrike down module(日本語版wikipediaでの名称/英語版wikipediaでは"strikedown crane")で、セルを交換するときに使う部品を固定する穴らしい。
 確かに、Flight II以前のStrikedownクレーン搭載艦では突起があるし、Flight IIA以降のStrikedownクレーン非搭載艦では突起が見られない。


フライトI相当の海上自衛隊護衛艦きりしまのVLS付近
Kirishima 06


フライトIIA相当の海上自衛隊護衛艦あたごのVLS付近
JS Atago(DDG-177) Mk.41 Mod20 VLS in Tenpouzan Port 20140426


 どういうときに使う部品かというと、水平状態に置いてあるセルを、クレーンで垂直に立ち上げる際に、支点となる部品らしい。これがあることにより、立ち上げ時の不安定なときにも不用意に動く危険が減らせる。
 同様のモノは地上でVLSの装填を行う際にも使われる。
US Navy 111108-N-BE353-060 Sailors aboard the submarine tender USS Frank Cable prepare to load a training Tomahawk vertical launch system container

 地上だと置くだけなので大きめの部品。セルを置いておく台も兼ねてそう。
 一方、艦載型は固定して使えるので、もう少し小型になってる(wikimediaで画像が見つからなかった/下記リンク参照)。

 Strikedown craneはこういうもの。
020805-N-XP218-008 USS Hopper VLS Strikedown Crane

110909-N-XQ375-035 USS Mitscher VLS Strikedown Crane folded

 ミサイルは水平の状態で移動する。
USS Frank Cable activity 121018-N-UE250-082

 装填するときは、もちろん垂直。
Flickr - Official U.S. Navy Imagery - USS Winston S. Churchill loads missiles.


 艦載のモジュールの写真は以下リンクで。
 Technical Discussions On International Military Equipments Doctrines Tactics Etc - Foreign Affairs (192) - Nigeria
 右下の茶色い服の人の肩の高さにある。
 VLS8セルの中央付近に支点があるので、設置する側と位置を移動することにより、8セルのどの位置にでも挿入することができる。
 画像右下にも別の部品があり、この2個1組で水平に置いたセルを支えて置くらしい。

 Stikedown craneが入っているセルにも固定用の金具がついているので、クレーン周辺の5セルにも挿入可能。
VLS MK41 СG-70 Lake Erie20080218


 蛇足だが、こういう釣り上げるときの支点を使う方法は、長物を扱うときは共通らしい。これは原子力巡航ミサイル潜水艦に搭載するトマホークミサイルの例。
Flickr - Official U.S. Navy Imagery - to load a Tomahawk aboard USS Michigan.

 旧来、潜水艦からトマホークミサイルを発射する場合は、魚雷発射管から水平に発射されていたが、巡航ミサイル潜水艦では垂直に発射することになる。そのため、装填も垂直に行うことになる。


 大きさは違うが、シースパローミサイルだともうちょっと簡単な装填方法になる。

US Navy 061022-N-2970T-003 Fire Controlmen lock a RIM-7 missile into the missile loader while Chief Fire Controlman Jesse Souza stands by as the safety observer

 シースパローは重量230kg程度と、かなりの重さではあるが、軍人数人程度がいれば人力で移動することができる重量となる。航空母艦なら重量のある物を移動するためのドーリーもあるだろうから、複雑な機械等はなるべく使わずに装填できるように設計されている。
 シースパローの発射機はそれなりの高さにあるので、人間が持ち上げるのはかなり大変。ということで、ランチャーの後ろにレールを追加し、ランチャーが仰角をとるとレールが俯角をとり、レール後端が下がって、甲板に置いたミサイルに近くなる、というわけ。

***

 ちょっと気になって調べてただけだけど、かなり時間がかかってしまった。これでも文字列検索ができるオンラインだからこの時間で済んだわけで、20年くらい前の紙ベースの資料を漁ることになったら、いったいどれだけの時間がかかることか。でもまぁ誰か詳しい人に聞いたらこんなこと一瞬で教えてくれるんだろうなぁ。
 そしてこの知識が役に立つときは来るのだろうか。「VLSのハッチの横にある突起って何なの?」とか聞かれるシチュエーションが想像できない。
 ま、知識なんて自己満足なんだから、知りたいことを調べればそれで十分なのだ。


 Wikimedia、意外と色々と画像があるけど、検索性が悪い。Wikiでタグ付けできるようになってたり、機械学習で関連するタグも検索してきたり、できれば便利なんだけど。

Win10クリーンインストールで2TBの壁

 Win10をクリーンインストールしようと思ったけど、4TBのHDDが2TBの壁に阻まれた。
 4TBくらいのパーティションを作ろうとしても、2TB(2047.5GB)くらいしか作れず、拡張しようとしても「新しいパーティションを作成できません」みたいなエラーが出て来る。
 調べてみると「必要ないストレージをはずせ」とか書いてあるけど、心当たりがない。

 どうやら、起動モードに問題があるらしい。

 起動時にBIOSを立ち上げて、ブートモードで光学ドライブの内、UEFIの併記があるドライブを選択する。そしてpress any keyとか出てきたところを見逃さず、キーを押す。
 押さなくてもOSインストール画面が出てくるが、起動モードが違うために2TBの壁に阻まれるらしい。

 僕もよくわかってないが、とりあえずこの方法で4TB近いパーティションを作ることができた。
 最初は2TBくらいしか作れないが、「拡張」をクリックすれば4TBくらいに広げることができる。


 OSのインストールなんて2年ぶりだからすっかり忘れちゃってるよ。
 このまま何事もなく進みますように。

2017年11月21日火曜日

武装メイド(妄想ダンプファイル)

 色々トラブルが有りつつも、新しいHDDを買ったので、データ移動待ち。

2017年11月16日木曜日

うにおん

 要素の大きさが固定のキューで様々な情報をやり取りする際は、union(共用体)が役に立つ。
 1つの情報は構造体によりデータを持つが、複数の情報を排他で保持したい場合、それらの構造体をさらに構造体に入れるのは、メモリ効率の点で無駄が多い。

 構造体を使う場合、1回の操作に必要な情報をひとまとめにして渡すことができる、という利点がある。例えばキューが整数型1個(と内容を示す列挙型)しか扱えない場合、1回の設定で複数の変数を設定する必要がある場合、何回もキューに入れる操作が必要になる。

 共用体が使えるなら、複数の構造体を選んでキューに入れられるので、キュー1本で様々なデータを受け渡しすることができる。
 共用体を使わなくても、例えばキューの要素サイズを64バイトの固定長にする、といった方法で複数の構造体を入れることができる。しかし、後から要素数の大きい構造体を使おうと思った際に、保守が困難となる。共用体を使えば、コンパイラが自動的に要素サイズを設定してくれる。


 ということで、共用体を使いたいんだけど、コーディングルールで共用体が使えない。
 どうやってデータを渡そうかなーといろいろ考え中。

***

 最近、軽くスランプ気味。
 すっごい簡単な試作基板(コネクタ何個か乗ってて、ジュンフロン線数本という程度)を作っただけでも気力がゴッソリ削り取られた。コーディングもなかなか進まない。
 まぁ、いつもこんな感じな気もするけど。
 なんかぱーっと気分転換したい。

 そういえば、今季もアニメ1本も見てない気がする。いくつか録画はしてるけど、見る気が出ない。

2017年11月14日火曜日

ルームランナー

 某所でルームランナーに乗ってエアガンを撃ってきた。
 イメージはこんな感じ。


 この人すごい上手。

 でも意外となんとかなる。M4で、4km/hくらいなら5m先の直径20cmくらいにほとんど命中する。12km/hでも50cmくらいにほとんど命中する。リロードも特に問題なく可能だった。
 もしも相手が自分を撃ってでも確保したい、あるいは射殺したい、と思っているようであれば、走って逃げても絶対に逃げ切れない。
 サバゲだとどうだろう。こっちが走っていて、相手を撃つ状態。相手がカバー無しで見えてるなら当たるだろうけど、隠れてたらたぶん無理。逆に、相手はむき出しのこっちを撃てる。とはいえエアガンだと弾速が遅いので、リード射撃が必要。相手が距離や速度を見誤れば、撃たれずに移動できるかも。それにこっちは相手を壁の向こうに押し込めるし。とはいえ、それは1対1の状況で、相手が複数人だと誰かに撃たれる。

 ある人曰く、「相手を見てから撃つまでに、どんなに訓練しても3秒未満にはできない」らしいので、逃げるなら10m毎に遮蔽物があるルートを探すしか無いのかな。この遮蔽物は、弾が抜けない強度が必要。エアソフトならベニヤ板で十分だけど、実弾だと鉄筋コンクリート位ないと厳しい(ブロック塀や自動車くらいなら実弾は抜ける)。
 この3秒というの、たぶん正規軍のような統制が取れた組織での話なので、「動くものはすべて撃つ」というテロリストとかトリガーの軽いハンターみたいな相手だと、たぶんもっと早い。ローレディから挙銃して正面の的に撃つのもかなり早いから、3秒ってのはかなり条件が厳しそう。

 ランダムなタイミングで起き上がって、5発くらい撃ったら倒れる的、というのがあれば、ルームランナーの周りに10個くらい置いておけば、かなり楽しそう。5x5mくらいの部屋でも、かなり楽しめそうだ。うちにもルームランナーほしい。

2017年11月13日月曜日

暗黙の型変換

 なぞのきょどうに遭遇して結構ハマった。

#include 
#include 

int main(void)
{
    {
        int32_t v1 = -104;
        uint32_t v2 = 32768;
        int32_t v3 = 450869;
        int32_t v4 = v1 * v2 * 32 / v3;
        printf("1:%d\n", v4);
    }
    {
        int32_t v1 = -104;
        int32_t v2 = 32768;
        int32_t v3 = 450869;
        int32_t v4 = v1 * v2 * 32 / v3;
        printf("2:%d\n", v4);
    }

    return(0);
}


 というコード。最初のブロックは当初のコード、次のブロックは修正後のコード。値は実際に使われた値を書いている。
 どちらも、結果は-241を予想している。
 -104 * 32768 * 32 / 450869 = 約-241.870で、切り捨てて-241となる。関数電卓とかで計算しても、このような結果になる。
 しかし、実際には1では9284という結果になる。
 arm-none-eabi-gcc 4.9.3でも、WSLのgcc 4.8.4でも同じ結果。

 これは、符号ありと符号なしで演算を行う場合、符号なしに変換されて計算されるためらしい(よくわかってない)。
 -104はu32で0xFFFFFF98となる。これに32768を掛けると0x7FFFFFCC0000となるが、32bitに切り落とされるので0xFFCC0000となる。32を掛けて同0xF9800000、450869で割って9284となる。

 型変換が行われるのは、違う形で演算を行うためであり、同じ型同士であれば変換は行われない。ということで、v2をs32にすれば正しい結果となる。


 JSF++で、「符号付きおよび符号なしの値は、算術演算または比較演算で混在してはなりません(理論的根拠: 符号付き値と符号なし値を混在させると、演算が多数の算術変換および積分型昇格のルールに従うため、エラーが発生しやすくなります)」(AV Rule 162 shall not)というのがある。
 今回、身をもって実感した。
 ついこの間、JSF++を翻訳してたので、そう言えば混在しちゃいけないルールが有ったな、と思い出したけど、これを読んでなかったらもっと深みにハマってた気がする。


 このv2は、元のコードでは、ハードウェアの符号なし32bitレジスタから読み取った値なので、そのままu32に突っ込んでいた。実際に取りうる範囲は0x00000000-0x0000FFFFなので、s32でも足りたのだが。

2017年11月11日土曜日

メモ:浮動小数点数

 浮動小数点数は誤差があるから使ってはいけないのだという。例として、「0.1ですら性格に表現できない」とか言われている。実際、JSF++でも浮動小数点数の演算は一部の例外を除き、明確に禁止されている。

 そもそもなぜ浮動小数点数は誤差を持つかというと、大抵の実数は2進数の有限長では表現できないためで、整数値の範囲であれば問題はない。例えば整数同士の加減乗は問題ない。除算の場合は、整数でも1.0/3.0=3.33...のように実数となり、また有限長の2進数では表現できない。

 じゃぁdoubleでも実数しか使わないなら問題ないね、ということになるが、それならint使ったほうが良いだろ、となってしまう。

 参考までに、浮動小数点数の表現範囲は、単精度が23bit、倍精度が52bit、4倍精度が112bitとなる(さらに符号ビットや指数がある)。
 23bitではfloor(log10(2^23))で 10進6桁分の精度がある。52bitでは15桁、112bitでは33桁となる(4倍精度はまだ一般的ではない感じだが)。
 ちなみに8bitから128bitまでだと、8bitで2桁、16bitで4桁、32bitで9桁、64bitで18桁、128bitで38桁を表現できる(符号ありで1bit少ない幅の場合)。
 浮動小数点でも整数でも、演算途中を含め、数値がこの桁数の範囲に収まるのであれば、誤差は発生しない。
 例えば1/128は0.0078125だが、これは有効数字5桁なので、単精度浮動小数点数型でも誤差は発生しない。しかし1/2048は0.00048828125で8桁が必用なので、単精度浮動小数点数型では表現できない。倍精度浮動小数点数型は15桁まで表現できるので、8桁なら問題なく表現できる。
 単精度浮動小数点数型は約6桁しか表現できないから、例えば12345678という数値も正しく表現できない。

 浮動小数点にかかわらず、プログラミングで変数を使う場合には、必要な精度を見極める必要がある。無闇矢鱈と大きな変数を使えば、丸め誤差は発生しないが、それではメモリを余計に使用してしまう。


 例えば、GPSの本によると、単精度では足りず、倍精度を使う必要があると書いてある。
 GPS衛星は約20,000kmの高度にいるから、単精度では100メートル単位でしか表現できない。これでは数百mの計算誤差を生じてしまう。倍精度ならマイクロメートルの単位まで表現することができる。
 実際にはGPSの位置情報は数メートルの誤差があるが、精密測量機器ではミリメートルの精度で計測できるし、汎用型でも誤差数百メートルは許容できないだろうから、倍精度が必用、というわけ。


 整数型で計算を行う場合、加減乗は範囲だけ気にすればいいので気が楽だが、除算はかなり面倒になる。例えば10/6は1.666を丸めた2を期待するが、整数型では切り捨てのために1となる。このあたりの誤差をどう処理するか、といった、本来の処理からは外れた部分を考える必要があるのが大変。
 STM32F4にはFPUが乗ってるんだし、多少誤差が有ったって問題ないんだから、浮動小数点を使ってもいいでしょ、とは思うのだが、一旦整数型縛りを始めるとなかなか。

2017年11月9日木曜日

メモ

 FreeRTOSのコルーチン。日本語ドキュメントだと「FreeRTOSではコルーチンを使用できます」くらいしか書いて無くて、どうやって使うのかが謎い。公式のページが一番わかりやすい。
 Example code creating scheduling and using RTOS co-routines
 Quick Co-routine Exampleの通り、簡単にわかる。

 FreeRTOSのコルーチンはRTOSによるスタックの割当は行われず、FreeRTOS外のスタック領域を使うらしい。またQueueとかも使えない気がする(詳細未確認)。デバッグメッセージをprintfからQueue経由でUARTに出そうとしたらハングアップした。

 基本的に、OSで管理するほどではないけど、ISRとかに書くには大きい、位の低レイヤ処理をターゲットにしてそう。サンプルだとLチカとかやってる。とはいえ、Lチカするにしたってその指令を出すのはタスクなわけで、じゃぁその間の橋渡しはどうやるんだ、という問題が有る。グローバル変数とか?

 今回はRTCの校正をコルーチンで行おう、と思っていた。処理自体はISRでもできないことはないくらいに小さいコードだけど、printfデバッグとかしたいので。ということで、printfが使えない?コルーチンも不可ということに。

***

 STM32F4RTCは、SSRの分解能を優先すると、キャリブレーションが面倒な感じになるっぽい。F4RTCはDIV_AとDIV_Sの2本のプリスケーラの積で1Hzを作るが、DIV_Aを小さくしてDIV_Sを大きくするとSSRの分解能が向上する。ただしDIV_Aが3未満の場合は正の方向にキャリブレーションできないらしい。
 対策として、わざとDIV_Sを外した値にして、キャリブレーションで負のレンジの真ん中に設定しておく、という感じにするらしい。キャリブレーションは9bitのレジスタと1bitの符号で10bit分の分解能があるが、DIV_S<3の場合は9bitの分解能しか得られない。
 ま、今試してるボードでは5bit+Signで足りてるので、9bitでも十分なのだけど。

***

 かなり面倒なことになってきてる。ステッピングモータで遊びたいだけだったのに、どうしてこうなった。

2017年11月8日水曜日

F4RTCメモ

 微妙に痒いところに手が届かない。
 例えばRTCの動作中は位相をモニタしたい。ということで1Hz出力を有効にする。すると、外部イベントによるタイムスタンプの記録ができなくなる。

 とりあえず、32.768kHzの水晶がそれなりに誤差が大きいらしい。といっても21ppmくらいなので、時計としては十分に高精度。だけどオシロで見るとどんどん位相が動いていく(GPS基準)。

 周期はCALR(キャリブレーションレジスタ)で調整する。CALIBRという似たようなレジスタが有るけど、こっちは互換性のために残している?らしい。約1ppm単位で調整ができる。調整幅が約488ppmを超えると、調整幅が倍になって分解能が4分の1になる。それでも調整しきれない場合は、さらに調整幅が倍になって分解能が4分の1になる。最大2000ppmくらいの範囲で8ppm単位、くらいかな。

 一方、位相の調整は、SHIFTR(シフトレジスタ)で調整する。デフォルトでは1/256秒(約4msec)の分解能で移動できる。
 リファレンスマニュアルによると、最大約30usecの分解能が得られるらしい。ただし「消費電力が増加する場合がある」と書いてある。ま、モーター制御に使ったりするなら誤差のレベルであろう。

 とりあえず、大まかな位相差はシフトで取り除き、それより細かい分解能ではキャリブレーションで移動すると良いかもしれない。
 ただし、キャリブレーション値を変化させたままでコアが停止した場合、そのままずーっと移動し続けてしまう。電源供給断で割り込みがかかるようにして、その後数十msec分の電力をキャパシタに入れておき、ISRの中でキャリブレーション値を戻す、みたいな工夫が必用そう。
 あるいは、シフトレジスタの分解能を限界まで上げてしまうか。これで30usecの精度で位相を合せられる。ただ、原因がわからないが、ジッターが100usecくらいある。なので30usecで合せてもあんまり意味がない。


 ま、とりあえず、今の用途なら50msec程度で十分なので、まずは1PPSから自動で位相を合せられるような処理を作らなければ。

 でも、これでもEXTIとISRを使う方法とか、タイムスタンプトリガを使う方法とか、いろいろやり方がある。
 精度を求めるならタイムスタンプだけど、EXTIだとデバッグが楽。そのあたりは追々。


 デスクトップ機のVS Codeが不調で、ドキュメントのフォーマットができない。症状としては、右クリックメニューが変更・切り取りコピー・貼り付け・コマンドの4個以外表示されない。PCを再起動したり、VS Codeを入れ直したりしてもダメだった。ショートカットキーも反応しないので、右クリックメニュー周りのバグではなく、コンポーネントの読み込みがされてないらしい。しかしショートカットキーリストにはフォーマットとかのキーコンフィグが出てる。

 とりあえずしょうがないので、ノートにリモートデスクトップ接続して、ノートからデスクトップ機のコードを置いてあるフォルダをVS Codeで開いて、ビルドと書き込みはデスクトップ機で、みたいなわけのわからないことになってる(ノートは解像度が低いので開発には向かない/ノートから4k液晶は30pしか出力できない/机の上にそもそも置くスペースがない/etc)。

Arma 3は異常終了しました


 こーゆーの。

 Touch Keyboard and Handwriting Panel Serviceというサービスが問題らしいので、ゲームプレイ時はそれを停止させる。


 タスクバーを右クリックして"タスクマネージャー"をクリック、タスクマネージャーを開いたら"サービス"タブを開いて下側の"サービス管理ツールを開く"をクリック、てきとーなサービス(歯車アイコンのリスト)をクリックし、"touch"とキーボードを入力。そうするとtouchで始まるサービス名が選択される。"Touch Keyboard and Handwriting Panel Service"というのがあるので、コレを右クリックして"停止"をクリック。これでArmaがクラッシュしなくなる。
 この手順はPCを再起動する毎に必用。「そもそもサービスを起動しないようにするべき(自動起動を停止)」という過激派も居るけど、必要があってサービスが起動している以上は、徹底的に殺すのはやりすぎだろ、という気がする。タッチパネルでキーボドを使うならこのサービスが必用。タッチパネルなんてつかわねーよ、という人は知らん。どうぞ勝手に。


 4k液晶でArma 3、Core i5 4570(第4世代/3.2GHz)とGeForce GTX950の組み合わせでも60fpsくらい出る。
 「総合品質」を一番低いクオリティに設定してから、必要に応じて高めていく、という手順。隠しパラメータでもあるのか、総合品質を高いところから各々落としていくと、あんまりFPS出ない。
 地形は低くすると草とかが生えない不毛の大地になる。最高以外では浮遊物体が発生してしまうけど、背に腹は代えられない。
 PIPはバックミラーとかバックモニタに使われる、Picture In Pictureという機能だが、コレがかなりのリソースを食う。2画面分とか3画面分のレンダリングをするわけだから、リソースがn倍という単位で必要になる。無効にするとかなりFPSが改善する。後ろが見えないけど、「バックモニタは故障中」ということで。
 視程はよほど大量のオブジェクトが無い限りは、2kmくらい表示するなら問題ない。あんまり短いと、車載火器の射程に視程外から狙われる(敵アルゴリズムで視程の切り落とししてるかな? 未確認)。ドローンが使えるフィールドとかで苦労するので、ある程度の視程は必須。

 4kになったからと言って状況認識が改善するかというと、微妙な感じ。完全に見通せるなら角分解能が高いほうが有利だけど、敵も木や草の影に隠れるから、あんまり角分解能は重要ではない。
 4kでフルクオリティのレンダリングをすればそりゃ綺麗だけど、1-3fps位しか出ない。移動もままならない。


 僕が使ってるDIGA DMR-BRW1000という機種。取説によると4kアップコンバートに対応しているらしい。再生中にサブメニューから24p/30p切り替えで30pとかにすれば、FHD60pから4k30p/4k24pに変換されて出力される。4k60pではないらしい。
 デジカメの4k出力も30pだしなぁ。まだ4k60pの動作確認ができてない。
 Xbox One Xの在庫、いつ入ってくるのかなぁ。1月中に買えると良いけど。

2017年11月7日火曜日

F4RTC

 STM32F4のリアルタイムクロックは高機能だ。F1の32bitカウンタとは比べ物にならないほど高機能だ。
 しかし、使いづらい。

 せっかく超高精度に時間を読み出せるのに、メーカー想定の使い方ではシステムリセット時に秒のプリスケーラがクリアされる。つまり、秒の信頼性が著しく低い。
 これをキャンセルする機能がある。しかし、それを使うにはユーザーが複雑なプログラムを書く必要がある。

 キャリブレーションも非常に使いづらい。リファレンスマニュアルによると「数秒の校正で0.95ppmの精度」だという。1秒で1usec程度の誤差ということだ。1ヶ月では2.5秒ほどの誤差だから、一般的な時計より1桁良い性能だ。
 しかしその方法が書いていない。

 基準クロックはどうだ。50Hzか60Hzを入れれば正確な1Hzを刻むのだという。しかしマイコンで時間を測りたいのに、わざわざコンセントに繋ぎっぱなしにしなければいけないのは面倒だ。
 それに50Hzか60Hzというのがいただけない。1Hzを基準にできれば、GPSモジュールを併用して、世界標準時と0.1マイクロ秒単位で正確に同期した時計を作れるというのに。


 どうにも、F4RTCは使いづらい。ま、F1よりは便利になったんだけどね。
 いまいち使い方がよくわかってないので、もうちょっと試行錯誤する必要がありそう。
 時計というか、時間処理系で、リアルタイムに動かさないとダメなモノだと、1箇所のパラメータの動作確認に5-10分程度かかることもザラ。カレンダ機能に至っては、数日間の連続動作を行わないとわからない、ということも有る。とても面倒くさい。


 結局のところ、EXTIで1Hzを監視して、それによって校正値を生成することにより、位相を合せたり、校正したり、というのを自前で用意する必要がありそう。
 ハードウェアで勝手にやってくれれば便利なんだけどねぇ。

2017年11月3日金曜日

論理(or/and)代入演算子が欲しい

 失敗時に0、成功時に0以外(不定/実装依存)を返す関数hoge,fugaが有ったとする。

int flag = 1;
flag &= hoge();
flag &= fuga();
if (flag)
{
}
else
{
}

 意図する処理は、双方ともに成功したらifを処理し、片方でも失敗したらelseを処理する、という流れ。
 しかし、これは上手く動かない。
 hogeが成功時に1を返し、fugaが成功時に2を返した場合、1&2は0になるからだ。この場合、期待通りの処理を行うには、ビットAND代入演算子(&=)ではなく、論理AND代入演算子(&&=)が必要になる。しかしそんなものはない。

flag = flag && hoge();
flag = flag && fuga();
 と書けば期待する処理になるが、なんとも冗長なコードになる。

 どうにかならんものか。


flag &= !!hoge();
flag &= !!fuga();
 というコードであれば、1回目のnotで反転し、2回目のnotで元の論理値に戻り、この際にコンパイラのfalse/true値に置き換わるので、コンパイラのtrue同士のANDとなり、うまく動く。でも、なんか綺麗じゃない。


 変数や配列やポインタの説明とか、関数の使い方とかじゃなくて、こういう小ネタが書いてある本を読みたい。

2017年10月31日火曜日

妄想:異世界現代兵器チートっぽいやつ

 ぼくの好みの作品があんまり無い。だれかこういう作品を知ってる人がいて教えてくれたらいいなーって一縷の望みをかけて。


2017年10月26日木曜日

CとC++

 とりあえずJSF++は全部機械翻訳したので(データは某所Gitにおいてあります)、じゃぁ次は使う番だ、ということで、試しにマイコンでC++のコードを走らせてみた。

 いままではCしか使っていないので、C++は使ったことがなかった。
 makefileをちょっと書き換えて、CPPもビルドできるようにした。gcc c++のオプションはgccのそれをそのまま使ったので、c++では使えないぞ、という警告がいくつか出てる。

 ヘッダファイルとか作るのが面倒だったので、externとかべた書きしてる。明らかにルール違反。

 とりあえず、C++といえばクラスだろ、ということで、試しに書いてみた。

#include 

extern "C" void test_func(void);

class Hoge_class
{
public:
 void set_value(int value)
 {
  val = value;
 }
 void print_value(void)
 {
  printf("value:%d\n", val);
 }
protected:
private:
 int val;
};

void test_func(void)
{
 printf("test_func\n");

 Hoge_class cls1, cls2;

 cls1.set_value(123);
 cls2.set_value(456);

 cls1.print_value();
 cls2.print_value();
}


extern void test_func(void);
test_func();

 結果としては、ちゃんと予想通りに表示される。

 ということで、簡単ながらマイコンでもC++が動くのは確認できた。

 もっとも、STM32Cubeが吐くコードはCコードだから、これを全部C++に移植するのは無理ゲー。おそらくF-35だって全部が全部C++じゃないだろうから、このあたりは妥協することにする。

***

 JSF++の4.2.1 "Should, Will, and Shall Rules"がいまいちよくわからなかったので、英語が得意そうな知り合いに聞いてきた。会うの何年ぶりだろうか。積もる話もいろいろ。頭の中も似たような感じなので、ちょっと話題を出せばあっという間に膨らんで、いろいろ話せて楽しかった。ウチの周りじゃこういう話できる人は皆無なので、話して楽しい人と話す機会ってのはかなり貴重。
 閑話休題。

 JSF++ではルールに関してShould, Will, Shallの3段階があるけど、曰く、Shouldは提案、Willはもっと強い感じ、Shallは強制的、という感じらしい。
 もっとも、JSF++ではShould, Will, Shallにかかわらず、ルールを無視するには手順を踏む必要がある。
 「「shall we dance?」というのは、聞いている感じだけど、shallでほとんど強制」みたいな例えが普通に出てくる。知識ある人ってすげーな、と。


 大昔にmbedを使ってた頃はC++を少し触った気がするけど、もうすっかり忘れてる。C++でパフォーマンスがどれくらい変化するのかはわからないけど、キチキチまで最適化する必用があるようなコードが必要になる機会はめったにないので、とりあえずC++で遊んで見る予定。

2017年10月23日月曜日

SourceTreeでアカウントを追加したい

 SourceTreeでGitのアカウントを追加したいんだけど、方法がわからない!と結構時間食われたのでメモ。

 調べると、SourceTreeをインストールした際にアカウントを追加できるらしい、ということはわかった。が、後から追加する方法は出てこない。
 後から追加できないことはないだろ、と思っていろいろ探したけど、一番有り得そうな"ツール"→"オプション"→"認証"でもアカウントの追加はできない。
 どうしたものか。

 灯台下暗しでした。

 

新しいタブを開いて"Remote"を選べば、"アカウントを追加"というボタンが出てくる。普段ローカルでしかGitを使ってないので全然知らなかった。こんなに簡単に追加できたのか。。。

 そもそもなんでアカウントを追加しなきゃいけなくなったのかというと、"remote: Empty password"というエラーでプッシュができなくなったため。
 アカウントを消したり追加したりしてもダメで、結局Gitのバージョンを上げたら治りました。古いバージョン使ってるとダメだね。

 Gitは"ツール"→"オプション"の"Git"タブにある"内蔵Gitを更新"をクリックすればアップデートできます。

2017年10月20日金曜日

妄想:サバゲでピンチ!


テトリス


 最低限、テトリスっぽい遊びをすることはできるようになりました。
 描画のAPIはCで作っていますが、ゲーム本体はLuaで作っています。コード的には、ほとんどニコ動のコピーですけれども。

 【プログラミング】テトリスを1時間強で作ってみた【実況解説】 - ニコニコ動画

 とりあえず遊ぶことはできるようになったので、次は何をやるかなーと思ってyoutubeのゲームボーイ動画を見たんですが、やっぱあれ解像度高いなぁ。素人の片手間で市販ゲームの再現ができるとは思ってないですけども。

 パネルは1枚位買い足してもいいかなと思ってますが、それでも64x64pxにしかならないので、ファミコンはおろか、ゲームボーイにも遠くおよびません。

 とりあえず遊びからは離れて、もっと実用的な方向に行くべきかな。
 CANバスが使えるようになれば遠距離から表示データを送ることができるので、少し離れたところに置いた風速計の情報を表示したりとか、そういうことができるようになります。映画に出てくるような、いかにもな発射ボタンを作って、モデルロケットのカウントダウンを表示することもできます。でもどっちも僕は使わないので、動機が薄い。


 電光掲示板は、かなり飽きてきました。
 何か次のネタを探さないと。

 超音波風速計の続きは、校正環境が無いし。
 人工衛星の追尾は、安物光学系だと厳しくなってきたし、寒い季節は嫌だし。
 そういえば、超音波風速計は前の冬にやってたのか。断熱が薄い家だから、冬はΔtが稼げるという。。

2017年10月19日木曜日

メモ:ぬるぽ

 我が国も購入を決定したF-35、開発時の名をJoint Strike Fighterと呼ぶが、この戦闘機のプログラムにはC++が使用されている。その前に開発されたF-22では信頼性を重視して米国防総省が開発したAdaという言語を使っている。
 C言語を使おうとしたことが有る人ならわかると思うが、C言語には「未定義」「実装依存」が多い。またプログラミングの勉強の最初のほうで使うであろうscanfは、少し調べると「危険だから使うな」という話が溢れてくる。

 話は変わるが、アリアン5というロケットの初号機は、ソフトウェアのバグによって打ち上げに失敗している。この時使用された言語はAdaで、浮動小数点数の計算に問題が有った。このロケットは無人であり、比較的高い高度で爆発したために人的被害はなかった。しかしロケット1機の損失コストは膨大なものになる。
 他にも、パトリオットミサイルは内部の時間処理に浮動小数点数を使用しており、時計が約0.3秒ずれたことにより迎撃に失敗、多数の死傷者・負傷者を出す結果となってしまった。

 航空宇宙・防衛産業では少しのプログラムミスが大きな結果となってしまう。プログラムミスが戦闘機で発生した場合、対価は国民の命で支払うことになる。
 そこでF-35ではどうしたか。
 JSF++というC++のコーディングルールを作った。


 と、シリアスムードで書いてみたけど、「軍用規格ってどんなもんぞ」って軽く読んでみただけ。

 JSF C++ コーディング ルール - MATLAB & Simulink - MathWorks 日本

 基本的にはstdio.hやtime.h等は使うな、#if #endif #defineはヘッダの多重インクルード対策以外に使うな、読み間違いしやすい名前は避けろ、とかそういう感じ。あんまり厳しくない。


 1つ面白いのは、「ポインタはnullとの比較、nullの代入はしません。nullの代わりに0を使え」みたいなのが書いてある。
 「組み込み現場のCプログラミング標準コーディングガイドライン」という本によると、「ポインタにはnullを使い、0は使うな」というふうに書いてある。

 NULLマクロを使え、という主張では、Cでは0には様々な意味があり、整数の0、浮動小数点数の0(0.0)、真偽値の0(FALSE)、ポインタの0(NULL)等がある。それらの型が混在している場所では hoge = 0 と書いてあった際に、hogeの型を覚えていない限りは何を目的とした代入かがわからない。
 一方、hoge = FALSEと書いてあればフラグだとわかるし、hoge=NULLと書いてあればポインタだと理解できる。ということで、NULLの使用が推奨されているっぽい。

 一方、JSF++の方では、そもそも#defineマクロの使用が禁止されているので、NULLマクロが使えないという点がある。ただそれは const void * NULL = (void* )0; とか書いてヘッダで共有すればいい話であって、大きな理由ではない気がする。
 JSF++ではコード内で数値をハードコードすることを禁止しているので、int hoge = 0;というのはルール違反となる。そのため、hoge = 0というのは例外なくポインタに対する代入である、ということになるため、「紛らわしいからやめろ」ということにはならない。

 というのが、ちょっと考えて浮かんだ理由なんだけど、本来はどうなんだろうか。元文見ろという話だけど、英文140pの文章はきつい。だれか翻訳してくれ。

 他にもC++になってNULLの意味が変わったとか、いろいろありそう。


 企業で大規模な開発とかするならJSF++その他のルールに対応したテストツールを使うんだろうけど、個人でフリーに使えるJSF++テストツールって有るんだろうか。
 とりあえず「こういうのはやんないほうが良いね~」とか「こうすると良いね~」ってのを頭の片隅にでも入れておけば、何も考えないでやるよりは役に立つんじゃないかな、と思いつつ。

2017年10月18日水曜日

メモ:Lua

 Luaでテトリス作ってる。電光掲示板の低レイヤAPIが必用なのでPCだと動かないけど、文法チェックはPCのLuaインタラプタでもできる。シリアルポートで転送するとハンドシェイク作ってないので遅いし、転送後だとエラーメッセージがわかりづらいので、PCで文法チェックできるのは便利。

 Cだと当たり前な文法ができないのがつらい。特にインクリメントができないのが厳しい。

 変数のswapとかは楽。a, b = b, aでswapできる。あとはx, y = -y, xで回転できたりするのが便利。

 Cだとa = +1みたいな文法は問題ない。でもLuaでは不可。Cだとa = +-+-+1という書き方はエラー無しで通ってしまう(+--+のような、マイナス記号の連続はエラー)。
 ちなみにa = +-+-+1はa=1となります。+記号は連結に使われて、「符号を反転しない」という動作。マイナス記号は符号を反転させるので、反転が3回で正数になる。

 forループはCと違いちょっと独特。for i = 1, 5 do end だと for (i = 1; i <= 5; i += 1) { } みたいな動作。for i = 1, 5, 2 do end だと for (i = 1; i <= 5; i += 2) { } みたいな動作。for i = 5, 1 do end では1回も回らないが、for i = 5, 1, -1 do endで増加方向を指定すれば for (i = 5; i >= 1; i -= 1) { } のような動作となる。


 Luaは結構面白い。特にコルーチンは便利。もっといろんな便利な使い方がありそう。
 ヒープ領域が85Kほどあるので、メモリには余裕ありそう。どれくらい使ってるか見積もりができないので、実はもう余裕ないのかもしれないけど。
 テトリスはニコニコに有る1時間プログラミングのアルゴリズムをそのままコピーしてる。コピーしてるだけなのに動画よりはるかに時間かかってるわけだが。。

 ロータリーエンコーダはあんまり性能が出ないので使わない方向。たまに他のキーに誤認するので、使うならそのあたりの対策をしてから。

2017年10月17日火曜日

100Wの電源を買ったと言ったな。あれは(以下略

 70Wって書いてやんの。

 RWS100B-5という電源を買いました。ピーク100W、定格70Wの5V電源です。
 ガワの固定ビスが1本なので、触るとカチャカチャうるさいです。あと端子台がかなりヤワイ印象。もっとも、どちらも製品に組み込んでしまえば触らない場所なので、本来の用途なら気にならない部分でしょう。黄緑のパワーLEDがついてますが、これは好みの分かれるところかな。
 端子台のビスはM3.5です。Y端子や丸端子が使えます。ビスを完全に抜いても脱落しない構造ですが、端子台を上にした向きだと丸端子を入れるにはピンセットが必須です。丸端子は「使えないことはない」程度ですね。
 メーカー不詳詳細不詳の古い12V4Aの電源も持ってますが、それと比べて体積で1/2.3くらいです(突起物除く)。容量1.5倍で1/2.3倍なので、7分の2くらいでしょうか。技術は進歩してるな。


 電源とパネルの間にINA226モジュールを入れたので、電圧と電流をオンボードで計測可能になりました。

 全面白の最大輝度で24Wくらい食ってます。今までは最大輝度で全点灯は避けてましたが、5V4Aの電源を使っていたので、かなりギリギリの運用でした。
 新しい電源は5V14A(ピーク20A)なので、かなり余裕があります。とはいえ、4Aでもかなり鳴きがうるさいです。TDKラムダなのでそんなに変な電源じゃないと思うんですが、開発陣の平均年齢が高いのかなぁ。電光掲示板はかなり変な消費電力パターンなので、それが原因かもしれません。

 容量的にはかなり余力が有るはずですが、短時間でも暖かくなるのがわかるくらいの発熱があります。推奨設置向きで上面に発熱体が配置されてます。それ以外の設置向きでも、発熱体が下面になる向きはありません。
 素人考えだと、発熱体が下にあれば全体にエアフローが発生しますが、エアフローの利点以上に周囲への熱移動のデメリットが大きいんでしょう。そりゃ発熱しない部品を冷やす必要はないし、ましてや温める利点は無いですからね。
 電源が発熱すると言っても、それ以上にパネルが発熱するので、全体で見ればあんまり気にならない範囲です。むしろ鳴きのほうが気になる。

 最大輝度では、ノイズは100mVくらいかな。Vppはもうちょっと大きいけど。FFTで見てみると、130kHzあたりにピークがあり、高調波も1個あります。他にも低周波にいくつかのピークが出てますが、面倒なので割愛。


 一応全点灯ならパネル3枚分、輝度を制限すれば4枚くらいは表示できます。もちろん大幅に輝度を下げればいくらでも枚数は増やせますが。ちなみに最低輝度だと全点灯で0.2Aなので、70枚分くらいです。


 とりあえず前回のエントリで書いた、外部のスイッチやロータリーエンコーダも動作確認ができました。とりあえずテトリスとか作りたいな。パネル1枚買い足して正方形にして、オセロとか。

 I2Cの動作確認ができたので、動作確認をしていないのはCANドライバとDACのみとなります。CANは使う予定ないので後回し。DACもデータを作るのが面倒だからなぁ。とりあえずテトリスとか作ってみて、効果音が欲しくなったらその時に考えるということで。

2017年10月16日月曜日

1ピンでスイッチ

 せっかく電光掲示板があるんだから、何かやりたい。でも動画はかなり高負荷。じゃぁどうしよう、ということで、テトリスでも作ろうか、という流れ。
 でも諸々の予約を考えると、スイッチ入力に使えるピンは1本しかない。スイッチ1個でどうやってテトリスをやれと。
 幸いにして、アナログ入力ピンが開いてるので、そこにスイッチを付けることにした。

 回路はこういうイメージ。


 今回はスイッチを計8個使い、ゲームボーイとかファミリーコンピュータっぽい配置になってる。タクトスイッチだけど。
 ついでに、ロータリーエンコーダもあれば面白いかな、と思って追加してみた。
 この回路では、基本的に同時押しは許可されない。右側のスイッチ群では、同時押しをした場合、根本に近い方が優先される。スイッチAとスイッチBを押したらスイッチCとして認識される、ということはない。
 一方、ロータリーエンコーダは同時に2本のスイッチを監視する必要があるので、ちょっと回路が違う。

 とりあえず、こういう回路でマイコンのADCを使って電圧を見てみると、こんな感じ。

 ADCは10kspsで、FallingEdgeから50サンプル分を出力している。
 タクトスイッチは予想より遥かに綺麗な波形。今のところチャタリングも全く無い。経年劣化とか怖いけど、そんなに長く使う気はないし、イザとなればコントローラ部分だけ作り直せるし。
 一方、ロータリーエンコーダの波形はかなり汚い。一応、AとBとABで電圧が違うのはわかるので、A相/B相の位相は把握できる。でもこれならFallから5サンプル後くらいの電圧を見て、A相かB相かを判定したほうが楽そうだなぁ。


 とりあえず、タクトスイッチの入力は問題なさそうなので、最悪でもファミコンやゲームボーイの操作系は作ることができる。
 ま、ゲームボーイと比べても、横幅で2.5分の1、縦幅で4.5分の1しかないのだけど。パネルを買い足せばゲームボーイの5分の1くらいの面積か。フルカラー8bit256色くらい表現できるのはこっちのほうが圧倒的に優位。動作確認はしていないが、2chの12bitDACもあるので、はいれぞおーでぃおも出力できる。画面解像度に目をつぶれば、ゲームボーイより高性能。

 とりあえず、もうちょっとエンコーダの動作確認をしてみる予定。

2017年10月14日土曜日

メモ:STM32F4のメモリ配置

 STBee F4miniに使われてるSTM32F405RGTには、メモリが192kB搭載されている。
 0x10000000からの64kB、0x20000000から112kB、0x2001C000からの16kBの3種類。
 112kと16kは連続していて、128kBとして使える。この2箇所は同時にアクセスすることができるので、例えば112kをメインのメモリとして使い、16kはUSBやEthernetのバッファとして使う、というような用途らしい。
 64kはARMコアとは接続されているが、DMAバスとは接続されていないので、64k領域にある配列をDMA転送しようとするとハングアップするらしい。

 今までは128kをメインのメモリとして使い、64kをスタックとして使っていた。これはねむいさんのリンカそのもの。しかし、FreeRTOSを使っているので、スタックはほぼ全く使っていない。
 今回、試しにFreeRTOSのスタックを64k領域に配置し、128kをそれ以外に使うようにしてみた。FreeRTOSは今のところ16kほど使ってるので、まだ48kほど開いてる。DMA転送で使う場合はstaticで宣言して128k領域から確保すればいい。またmallocも128k領域から取ってくるようになる。

 配列のアドレスを指定するのは、リンカスクリプトとかでうまくできるのだろうが、方法がわからなかったので、 static uint8_t *ucHeap = (uint8_t *)0x10000000; のように、直接指定することにした。これはFreeRTOS/portable/MemMang/heap_4.cの100行目あたりに書いてある。配列の宣言をポインタに置き換えたもの。リンカスクリプトには64k領域を書いていないので、コンパイラがこの領域を使うことはできない。configTOTAL_HEAP_SIZEは64KiBにしておけばok。


 ということで、微妙に使い勝手の悪い64k領域をそれなりに活用しつつ、128k領域を目一杯使えるようになった。
 mallocで確保できる領域も増えたので、動的にメモリを使う機能も使いやすくなった。


 Luaを走らせたいんだけど、ちょっと苦戦中。

追記
 もうちょっとスッキリした方法(たぶん)。
 FreeRTOSConfig.hに #define configAPPLICATION_ALLOCATED_HEAP 1 を追加する。
 freertos_heap.cという、FreeRTOS.hをインクルードし、uint8_t ucHeap[configTOTAL_HEAP_SIZE]; と配列だけを宣言したソースを作る。
 リンカスクリプトのセクションに
.freertos_heap_sec :
{
*freertos_heap.o(.bss .bss.*)
} >RAM3
 を追加する。
 これで、FreeRTOSのヒープ領域をRAM3に確保できる。

 main.cの最初のほうでスタックのアドレスを変数に保存して、後で表示してみると、0x1000000の領域に確保されている。FreeRTOSのスレッド内でスタックのアドレスを見ても、0x10000000の領域に確保されている。
 ということで、64kの領域にFreeRTOSのヒープを確保しつつ、残りの領域はOS外のスタックを確保し、残りの128kをmallocに使う、という感じになった。今のところ、FreeRTOSでは16kくらいしか使ってないらしいが、とりあえず32kを確保している。

 今までのheap_n.cを書き換える方法だと、Cubeで出力するたびに戻されるので、面倒だと思う(未確認)。今回の方法だと、一応Cubeの制御外のはず(未確認)。

2017年10月12日木曜日

電光掲示板



 だいぶ良くなった。写真で撮ると走査線が入ってしまうが、肉眼だとあんまり気にならない。
 肉眼で見る時は、十分に離れた位置から見る必要がある。十分に離れれば空間的なLPFとなるので、ピクセルドットが目立たない。LEDは点灯密度が狭いので、何らかのLPFを通さないと変な感じ。




 同じ写真だけど、下はガウシアンフィルタを掛けてある。LPFを通したほうが見やすい。気がする。

 いままで、コントラストを稼ごうと1bit暗いピクセルは10倍くらい暗くしてたけど、考えればそれだと低輝度bitは捨ててるのと同じで、それじゃコントラストが稼げるわけがない。ということで、とりあえず1bit減る毎に半分の点灯時間にしてみた。だいぶいい感じ。
 輝度は点灯時間で制御しているが、点灯時間はBLANK(OE)端子をLowに引っ張っている時間なので、これのパルスはタイマで作っている。分解能1usecだが、一番暗い設定では最低輝度が1usec、最高輝度が16usec、みたいな設定。一番明るいピクセルでも16msecの内で32usecしか点灯してないのに、結構明るい。
 一番明るい設定だと、たぶん800usecくらい点灯させられるので、単純計算で25倍くらい明るくできる。
 ただ、今使ってる電源が5V4A20Wなので、輝度を上げると電源の悲鳴が聞こえてくる。とりあえず5V20A100Wというバケモノみたいな電源を買ったので、それの到着待ち。100WもあればこのLEDパネル3枚くらいは接続できる。意外と少ない。ストリナのINA226ボードが有るので、マイコンからI2Cを生やせば25Aまで計測できる。それができれば消費電力の評価ができる。マイクロ秒なんてパルスを計測できるかはさておき。
 こういうモノならシャント抵抗+オシロみたいな組み合わせのほうが良いかもなぁ。シャント抵抗+LPFでマイコンのADCに突っ込むとか。

 あと、いままではパレットは256個だったが、1024個に増やしてみた。画像はPNG8bitなので256色だが、2枚目の写真は1024色で生成している。64x32pxだと2048色まで表示できるが、メモリが足りなかった。データ変換の高速化のためにパレットをビット深度分持ってるので、かなりデカい。このあたりは表現能力とRAMのトレードオフなので、いい感じの点を探す必要がある。

 無圧縮8bppPNGを10fpsで読み込めるので、アニメのOPを表示してみた。結構ちゃんと見える。コレ以上の高速化はちょっと厳しそう。RasPiでやってる人がいるけど、さすがRasPiの処理能力。向こうは900MHzだから、ウチの5.3倍か。うーん。解像度が5分の1くらいだから、こっちでも30fps出せても良いはずなんだが。。
 マイコンのアイドル率が結構高い代わりにSDカードのバス使用率がかなり高い。ちょっとした圧縮データにして、バス使用率を下げつつCPU使用率を上げればもうちょっと性能出るかも。
 あとはデータの読み込みがブロックリードでものすごい効率が悪いので、シーケンシャルリードで8KiBくらい一気に読んでしまうとか。あぁ、ここでもRAMとの戦いが。。。


 せっかく電光掲示板がある程度使えるようになったんだから、何か表示したいなぁ。でも時計くらいしか思いつかない。いちおうCANバスとか有るから、屋外に引き出した環境センサの情報を表示したりとかはできると思うんだけども。あんまり家の周りの情報は興味ないからなぁ。

メモ:天馬いれと庫

 天馬いれと庫というシリーズ、何種類か有る。僕が使ってるのはCDボックス、DVDボックス、文庫ボックス、コミックボックスの4種類。

 寸法はamazonにもあるけど、とりあえずCADで。


 基本的に寸法違いはスタックできない。ただし、幅広の箱の上に幅狭の箱を載せるのは、多少不安定になるのを妥協すれば可能。
 コミックとDVDの違いは、DVDが幅で5mm、高さで10mm大きいだけなので、DVDの箱にコミックを入れることは可能。スタックすることを考えると、DVDにコミックを入れたほうが良いかも。寸法的にはDVDの箱にCDを入れることも可能だが、転倒防止の仕切り板が高さ方向に余裕が無いので、CDはCDで入れたほうが良い。それにCDとDVDは高さ以外は同じなので、スタック可能。
 CDやDVDは、特殊なケースに入っているものでなければ大抵は入る。DVDにはDVDサイズのPCソフトも入れることができる。


 部屋に本が平積みされてるので、今ある本棚より一回り大きいモノを作りたい。ついでにいれと庫も置きたいので寸法を調べた次第。

2017年10月10日火曜日

HUB75パネル

ドイツ娘あらためティアさん。

 amazonで64x32pxのパネルが安く売ってたので、試しに買ってみました。
 かなりコントラスト悪いですねぇ。もっとも、これは制御の問題だと思うので、もうちょっと改善すると思います。

 画像データは無圧縮PNGで持たせてみました。意外に読み込みに時間がかかります。とはいえ、カードアクセスはもうちょっと最適化できそうです。画像読み込みもかなり冗長なファイルアクセスなので、そこも最適化できそう。あとDMAを使えばさらに早くなるかもしれませんが、ちょっとDMAに痛い思い出があるので。。うまく動くといいなぁ。。

 ところで、このパネル、64x32ピクセルで計2048ドット有るわけですが、パネルは3400円で売ってて、LED1素子あたり1円くらいの計算です。
 秋月だとフルカラーチップLEDが5個200円くらいですから、中国製って凄まじいな。

 さすがに64x32pxだと解像度が足りない感じです。あと3枚位買い足して128x64pxとかにすればもうちょっと表現度も上がりそうだけど、ネタでそこまでやるにはなぁ。
 以前使ったやつとか借り物とくらべ、今回のパネルはドット密度が4倍ほどあります。消費電力は2倍ですが、それでも密度が高い分、放熱が悪くて発熱も大きいです。


 いろいろ最適化したいところはありますが、一番懸案のHUB75出力処理は、アセンブリコードを見る限り、これ以上の最適化は難しそう。
 現在約64fpsくらい出てますが、1フレームでHUB75のデータ変換が64回必用です。ということで、1秒間に約4096回のデータ変換が行われています。1回のデータ変換が約33usecなので、1秒の内140msec程度はデータ変換に費やされている計算になります。
 フレームバッファでCPUの使用率が14%と考えるとかなり余裕がありますが、自前でデータを作ったりする必要が有ることを考えると、もうちょっとなんとかしたいところです。解像度を増やした時に破綻してしまいます。複数パネル使うことを考えると、もう余裕がありません。
 とはいえ、処理内容を考えると、33usecの内、大半はFreeRTOSのタスクスイッチのような気もします。


 今回試作した制御ボードは、2x HUB75、I2C、CAN、2x DAC、microSDソケット、2x UART(1本はデバッグ用)という構成です。HUB75は、1chを4bit、もう1chを3bitとしていますが、ジャンパを1本飛ばせば4bit2本にもできます。ただ、32x32パネルでも3bitだったりするので、とりあえず3bitと4bitの構成にしています。4bitパネルを3bitバスに接続すると、パネルの半分が表示できませんし、3bitパネルを4bitバスに接続すると、アドレスの上位1bit分は地絡するのでよくありません。

 色深度3bitで256色のパレットですが、CANで遠くにおいたセンサのデータを表示したり、SDカードから画像を読み込んで表示したり、I2Cで近くのセンサを読んだり、UART経由でPCからコマンドを送ったり、といったことができるようになる予定です。
 今のところ使ってるのはHUB75を1ch分とSDソケットだけなので、まだまだいろいろな機能が作れるわけですが、どうなることやら。


非圧縮PNG

 PNG推しな僕ですが、マイコンからするとPNGはかなりつらいフォーマットです。
 一番大変なのは、大量のメモリとそれなりの計算リソースが必用な点でしょう。
 例えばPNGを読み込む場合は、Deflateを展開するのに約32kByteのバッファが必要になります(計算に使うメモリがさらに必用です)。また、PNGは隣のピクセルの輝度を参照する場合があるので、少なくとも上1行部のピクセルデータも必用です。ピクセルのバッファは、通常は横解像度の4倍程度で足ります。マイコンで使う画像ならせいぜい横数百ピクセルとして、約1KiBです。STM32F4ならこの程度は問題ないですが、計算リソースが必用なことに変わりはありません。

 ところで、PNGには他のピクセルを参照しないフィルタリングモードがあります。また、Deflateも無圧縮データを入れるモードがあります。ではこれらを組み合わせればどうなるかというと、非圧縮画像の出来上がりです。もちろん、PNGデータの仕様内ですから、通常のビューアで表示可能ですし、圧縮データではないので、任意のピクセルを直接取り出すことができます。
 シークする際は、ピクセル位置のちょっと面倒な計算以外に、Deflateの仕様による詰め物が有るので、その計算が面倒ですが、ピクセル間の計算や圧縮データの展開に比べれば微々たるものでしょう。


 今回、Deflateにかなりつまずきました。
 PNGはビッグエンディアンですが、Deflateはリトルエンディアンです。コレに気がつくのにかなり時間がかかりました。また、Deflateはビットストリームを下位ビットファーストで詰め込んでいくので、例えば「最初の1bitが1の8bitデータ」は0x01が正解となります。
 Deflateに無圧縮データを入れる場合、4バイトのヘッダを入れます。内訳は16bitの長さデータと、それをビット反転した16bit分です。つまりデータ長は65535バイトが最大になります。これを超える長さの場合は、都度4バイトのデータ長情報をもたせる必要があります。
 さらにDeflate(を格納したzlib)を格納するPNGチャンクは32bitのデータ長を持たせ、これに収まらない場合はチャンクを分ける必要があります。しかし、32bitでは4GB程度を詰め込むことができ、1ピクセル4バイトとしても3万px四方くらいになります。この面積をマイコンで処理することはありえないでしょうし、PCでデータを処理するにしても、そんなに大きな画像はめったにないので、今回は32bit制限は気にしないことにしています。

 ということで、マイコンでも扱いやすいPNGデータを作ることができるようになりました。あとはHUB75を制御するマイコンで、非圧縮PNGに対応したローダーを作れば画像を表示できるようになります。画像が表示できるようになれば、APNGに対応させてアニメーションが表示できるようになります。拡張チャンクにPCMデータを入れれば、音声も再生できるかもしれません。

 とりあえず簡単にDecompPNGをロードするコードを書いてみましたが、結構面倒ですね。例えばBitmapならファイルヘッダにイメージへのオフセットが書いてあるので、データの先頭54バイトを読んで、いくつかの情報をチェックし、あとは画像データを直接取り出すことができます。
 あと将来的にAPNGに対応したいので、決め打ちで画像を読み込むことができません。そのあたりもどうにかしないと。
 ガーッと全部データをメモリに読み込んで置ける、PCのプログラムは楽だなぁ、と改めて実感。


2017年10月9日月曜日

セメダイン SX720W

 SX720Wという接着剤を買ってみた。

 データはここ。
 スーパーXシリーズ:データ|工業用接着剤|セメダイン
 難燃型のタブにある。
 読み方、というか、パラメータの意味ががいまいちよくわからない。

 指触乾燥時間が23℃で9分、つまり、指で触っても表面が変化しなくなるまでの時間?が23℃で9分。これは表面だけの時間なので、内部まで硬化するにはさらに時間がかかる。とはいえ塗布厚にもよるだろうから、1液型だと完全硬化の時間はデータシートに出てこないっぽい。
 せん断とか剥離のパラメータもある。試験方法はパナソニックのページがわかりやすい。
 接着剤評価試験方法のご紹介|信頼性|パナソニックプロダクト解析センター情報館 | Panasonic

 破断時伸びは、破断する時にどれくらい伸びるか、というパラメータで、100%なら2倍に伸びた時に破断、50%なら1.5倍に伸びた時に破断、ということらしい。
 1倍(伸びずに破断)で100%、2倍に伸びて破断なら200%、とかのほうがわかりやすい気がする。


 接着剤なのでもちろん部品の接着にも使えるけど、今回は充填剤みたいな感じで買ってみた。空中配線を固定したりとか、そういう感じ。
 試しに壊れても苦しくない基板のジュンフロン線にちょっと塗ってみた。直径6mmくらいでも結構しっかり固定できる。剥がすのはかなり大変なので、基本的に不可逆。
 塗る時は爪楊枝みたいなので塗りたいところに置いていく感じ。塗ったときにはツノが出るが、水平においておけば硬化前にはなめらかになる。

 ユニ基板に塗布した様子。だいたい24時間くらい経過してる。左側は試しにピンセットで削ってみたところ。基板から剥離するより先に樹脂が割れてくる感じ。触るとシリコンっぽい感じ。感じってシリコン樹脂そのものなんだけども。



 200g入りで結構デカイので、躊躇なく怪しいところは全部固定したりするのに良さそう。とはいえ前述したように、剥がすのはかなり大変なので、修正する可能性があるところには使わないほうが無難。修正する可能性のある基板って、修正が終わった頃には用済みになってしまうというジレンマがあるのだけど、まぁソレはソレ。
 ハンダで接続したコネクタの断線防止とかにも良いかも。ただ硬化するのに多少の時間がかかるので、熱収縮チューブのほうがお手軽。でも綺麗な見た目は樹脂のほうが作りやすいかも。

 結構量が多いので、もうちょっと少なくて安く売ってくれれば買いやすいと思うけど、まぁ業務用向けだから仕方ないね。
 ぜひ一家に1本SX720Wを!


2017年10月3日火曜日

HUB75メモ

 HUB75の制御はRGB各2本の計6本と、アドレス(3bitあるいは4bit)、それからCLKとLAT、BLANKの計12本あるいは13本を使用する。

 CLKは立ち上がりでRGBをキャプチャする。CLKがHの状態でRGBを動かしても反映されない。
 LATはHの時にキャプチャされ、Lの時は動かない。実質立ち下がりエッジでキャプチャ。ただしLATがHの時はシフトレジスタからLEDドライバへ常に反映されるので、基本的にLATを動かす時はBLANKをHにして非表示にしておく必要がある。
 LATがLならLEDは変化しないので、BLANK↑, LAT↑,ADR set,LAT↓,BLANK↓の順で制御すれば、最短時間で処理できる。LATトグルの間にADRを動かすのは、LATのパルス幅を確保するため。NOPでも良いけど、ADRを動かしたほうが低コスト。

 サクッと試したところでは、ピクセルクロック20MHzまでは問題なく動作する。これ以上の速度だとSTBee F4miniのGPIOからは出せない。FMCとか使えばできるかもしれないが、SF4mにはFMCがないので試せない。もちろん最大速度は配線の引き回しや、フラットケーブルの長さにも依存する。クロストークとかタイミングとかはATA(IDE)の歴史に近い。

 STBee F4miniを使った場合、9KiPix3bit程度までは問題なく表示できることを確認している。うまく処理すれば32KiPix3bitくらいは表示できそうな気もする。ただそこまで来るとかなりのデータ転送速度が必用な気がする。Cortex-Mでやることじゃない。

/*
 そういえば、ドットマトリクスLEDパネルは6年くらい前にも作ったんだった。ソレは単色で40x8pxくらいだった気がするけど。それはTLC5940を使って、コイツもBlankとかLATで苦労した気がする。デイジーチェーンも。なんか似たようなことやってるなぁ。フルカラーになったし解像度も30倍近くになってるんだけど。
*/

2017年10月1日日曜日

C#でWavの無音削除

 手伝ったイベントの運営で使ってた特小を録音してたんだけど、結構面白かったのでデータとして残したい。でも6時間分は長すぎる。ということで無音部分を削除したい。と思ったけど、ちょっと調べた限りだとSoundEngine Freeでは処理できないらしい。いちいち新しいソフトの使い方を調べるのも面倒。
 ということでC#でサクッと作ってみた。サクッとといっても、この程度のコードで1時間もかかってるのだが。

using (FileStream fsr = new FileStream(path, FileMode.Open, FileAccess.Read))
using (FileStream fsw = new FileStream(path + " sd.wav", FileMode.Create, FileAccess.Write))
{
 byte[] buff = new byte[36];

 fsr.Read(buff, 0, 36);
 fsw.Write(buff, 0, 36);

 if (Encoding.ASCII.GetString(buff, 0, 4) != "RIFF" ||
  Encoding.ASCII.GetString(buff, 8, 4) != "WAVE" ||
  Encoding.ASCII.GetString(buff, 12, 4) != "fmt ")
 {
  throw (new Exception());
 }

 if (
  BitConverter.ToUInt16(buff, 20) != 1 || // fmt id
  BitConverter.ToUInt16(buff, 22) != 1 || // ch
  BitConverter.ToUInt16(buff, 34) != 16 || // bit depth
  false)
 {
  throw (new Exception());
 }

 UInt32 SamplingRate = BitConverter.ToUInt32(buff, 24);

 while (true)
 {
  fsr.Read(buff, 0, 4);
  if (Encoding.ASCII.GetString(buff, 0, 4) == "data")
  {
   break;
  }

  fsr.Read(buff, 4, 4);
  int size = BitConverter.ToInt32(buff, 4);

  byte[] tmp = new byte[size];
  fsr.Read(tmp, 0, size);
 }

 fsr.Read(buff, 0, 4);
 UInt32 len = BitConverter.ToUInt32(buff, 0);

 fsw.Write(Encoding.ASCII.GetBytes("data"), 0, 4);
 long sizePos = fsw.Position;
 fsw.Write(BitConverter.GetBytes((UInt32)0), 0, 4);

 double thrStart = -30; // 録音開始レベル(dB)
 double thrEnd = -30; // 録音停止レベル(dB)
 double delay = 3; // 録音停止レベルを下回ってから、実際に録音を停止するまでの時間(秒)

 UInt32 delayCount = (UInt32)(delay * SamplingRate * 2 * 1);
 bool record = false;

 UInt32 count = 0;
 UInt32 end = 0;

 double fs = Math.Pow(2, 15);
 int thrStartInt = (int)(Math.Pow(10, thrStart / 20) * fs);
 int thrEndInt = (int)(Math.Pow(10, thrEnd / 20) * fs);

 // len = Math.Min(len, (UInt32)(1 * 60 * 60 * SamplingRate * 2 * 1)); // とりあえず1時間分だけ処理

 for (UInt32 i = 0; i < len; i += 2)
 {
  fsr.Read(buff, 0, 2);

  Int16 val = BitConverter.ToInt16(buff, 0);

  if (val < 0)
  {
   val *= -1;
  }

  if (record)
  {
   if (val > thrEndInt)
   {
    end = i + delayCount;
   }
   if (i == end)
   {
    record = false;
   }
  }
  else
  {
   if (val > thrStartInt)
   {
    record = true;
   }
  }

  if (record)
  {
   fsw.Write(buff, 0, 2);
   count++;
  }
 }

 fsw.Position = sizePos;
 fsw.Write(BitConverter.GetBytes((UInt32)(count * 2)), 0, 4);
}

 string pathにファイルパスを設定しておけば、thrStart[dB]を超えたタイミングで書き出しを開始し、thrEnd[dB]を下回ったタイミングから一定時間経過すると書き出しを停止する。これをずーっと繰り返す、という処理。
 入力されるファイル形式は16bit1chのみに対応。

 意外と時間がかかる。整数データと侮ってると、秒88.2kByteのデータ量に飲まれる。
 最初、forの中でvalをdBに変換し、thrStart/thrEndと比較していた。先にIntに変換して、forの中で浮動小数点演算を行わないようにすると、4倍近く高速化した。
 6時間分のWavを処理するのも、ちょっと待ってれば終わる程度。

 6時間分のWavは、delay=3で1時間分くらいになった。BGM代わりに聞けるくらいの量かな。

 某声優の声が空電ノイズ混じりに聞こえてくるのに興奮したりとか、やっぱ僕はこういうのが好きなんだなぁ。グフフ。

 録音は僕が背負ってたバックパックに入れたIC-R6にICレコーダーを接続して行ったので、僕から離れた位置で送信された声は聞き取りづらい。けどまぁ面白い。
 ちゃんと記録として残すなら、利得の高いアンテナを使ったりとかする必要があるんだろうけど、まぁ遊び半分だからね。
 サバゲ中の無線とか取れば面白そうだけど、僕が参加するサバゲだとTRは全然使わないからなぁ。
 スケルチが閉じて十分に音量が下がれば良いので、エアバンドとかにも使える。けどウチの地域はエアバンドほとんど入らないから使えない。
 もちろんアマチュア無線にも使えるけど、このあたりは違法無線しか無いので聞いても面白みも何もない。

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の計略恐るべし。