2014年5月27日火曜日

Processingでアナログ時計

Processingでアナログ時計を表示する関数です

void drawClock(String config) {
  String configs[] = split(config, ',');
  
  int x = width / 2, y = height / 2;
  int size = min(width, height);
  float hour   = 0; boolean isHour    = false; int hourColor   = #000000;
  float minute = 0; boolean isMinute  = false; int minuteColor = #000000;
  float sec    = 0; boolean isSec     = false; int secColor    = #000000;
  
  
  for (int i = 0; i < configs.length; i++) {
    if (configs[i] == null) { continue; }
    String[] val = split(configs[i], ' ');
    
    if (val.length < 1) { continue; }
    if (val[0] == null) { continue; }
    
    if (val.length < 2) { continue; }
    if (val[1] == null) { continue; }
    if (val[0].equals("h"))    { hour   = parseFloat(val[1]); isHour   = true; continue; }
    if (val[0].equals("m"))    { minute = parseFloat(val[1]); isMinute = true; continue; }
    if (val[0].equals("s"))    { sec    = parseFloat(val[1]); isSec    = true; continue; }
    if (val[0].equals("hc"))   { hourColor   = parseInt(val[1]); continue; }
    if (val[0].equals("mc"))   { minuteColor = parseInt(val[1]); continue; }
    if (val[0].equals("sc"))   { secColor    = parseInt(val[1]); continue; }
    if (val[0].equals("size")) { size = parseInt(val[1]); continue; }
    if (val[0].equals("x"))    { x = parseInt(val[1]); continue; }
    if (val[0].equals("y"))    { y = parseInt(val[1]); continue; }
  }
  
  pushMatrix();
  translate(x, y);
  
  noFill();
 
  sec    *=  6;
  minute *=  6; 
  hour   *= 30;
  
  hour   += minute / 12;
  minute += sec    / 60;
  
  ellipse(0, 0, size, size);
  
  {
    float eSize =  size * PI / 60 * 0.6;
    float len   = size / 2 * 0.95;
    for (int i = 0; i < 60; i++) {
      float px =  sin(radians(6 * i)) * len;
      float py = -cos(radians(6 * i)) * len;
      ellipse(px, py, eSize, eSize); 
    }
  }
  
  if (isSec) {
    float len = size / 2 * 0.75;
    float px =  sin(radians(sec)) * len;
    float py = -cos(radians(sec)) * len;
    stroke(secColor);
    line(px, py, 0, 0);
    line(-px * 0.25, -py * 0.25, 0, 0);
  }
  
  if (isMinute) {
    float len = size / 2 * 0.80;
    float px =  sin(radians(minute)) * len;
    float py = -cos(radians(minute)) * len;
    stroke(minuteColor);
    line(px, py, 0, 0);
    line(-px * 0.25, -py * 0.25, 0, 0);
  }
  
  if (isHour) {
    float len = size / 2 * 0.60;
    float px =  sin(radians(hour)) * len;
    float py = -cos(radians(hour)) * len;
    stroke(hourColor);
    line(px, py, 0, 0);
    line(-px * 0.25, -py * 0.25, 0, 0);
  }
  
  popMatrix();
}

引数は文字列で渡します
時針 分針 秒針 の表示が可能です
試しに作っただけなので、あんまり使いやすくないですけども

2014年5月25日日曜日

TFTLCD

秋月で売っている3000円TFTLCDを試してみました
ちなみに秋月には3000円TFTLCDが2種類あります
今回使ったのは単色の横400縦240のモノです
3色480x272とかSTM32F1じゃ扱えないので(少なくとも内部メモリでは)



現状
・1ピクセルのSet/Reset/Write
・8ピクセルのWrite(Mask有)
・線(1ピクセルWriteのラッパー)
・円(1ピクセルWriteのラッパー)
・文字(8ピクセルWriteのラッパー)
を実装しています

フォントはWindowsでC#を使ってバイナリに変換しています
変換時に左右に空白が数ピクセル作られるため、かなり幅の空いた文字列になってしまいます
この辺りは今後の改善点


液晶への転送の実装ですが、ちょっと効率の悪い方法を使っています
普通400x240で1ピクセル1bitの表示の場合 バッファーは12000バイトで足ります
(横400*縦240*(1ピクセルのバイト数0.125))
しかし今回は12482バイトのバッファを作りました
こうすることによりハードウェア(DMA)を使った転送が容易になります

次にハードウェアレベルですが
今回はSTM32F1のSPI2を使いました
SPIは上位ビットファーストですが、このLCDモジュールは下位ビットファーストです
幸いにしてSTM32F1のSPIは下位ビットファーストで転送を行うモードが有るため、これを利用しています
SPIは16分周で初期化しています システムは72MHzなので、SPIクロックは2.25MHzと、MAXの2MHzを超えていますが、現状では正常に動作しているようです
ちゃんとモノを作るときは32分周にして1.125MHzにするか、システムを64MHzにして2MHzにするなどの対策をするべきです

それとCOM信号ですが、今回はソフトウェアで送っています
そのため定期的に何らかのデータを送りつける必要があります
今回はフレームバッファを内蔵しているので、ソフトウェアループの中でCOMを反転した後、フレームを転送しています

本来であればタイマ割り込み等を使用してハードウェアで転送を… というのが一番いいのですが、そういう実装にするとSPIポートを選べなくなるので迷うところです


参考までにSPIのキャプチャを置いておきます

上の画像はSCSの立ち上がりから数バイト分を 下の画像は1フレーム分の転送を示しています
DMAを使用して転送しているため、非常に高速に転送できていることがわかります
(ソフトウェアで転送した場合このように綺麗にクロックが連続することはありません)

また1フレームの転送時間は44.381mSecとなっています
これは12482バイトを2.25MHzで転送する場合の理論値(44.38044msec)と同じです
DMAを使うことにより非常に高速に転送できていると言えます



2014年5月23日金曜日

ARMでビットリバーサル

ARMでビットリバーサルを行う方法について

inline uint32_t REV(uint32_t din) {
    uint32_t dout;
    __asm__("REV %[Rd], %[Rs1]" : [Rd] "=r" (dout) : [Rs1] "r" (din));
    return(dout);
}

inline uint32_t RBIT(uint32_t din) {
    uint32_t dout;
    __asm__("RBIT %[Rd], %[Rs1]" : [Rd] "=r" (dout) : [Rs1] "r" (din));
    return(dout);

}


RBITがビットリバーサルです
RBIT命令は32bitのI/Oなので、8bitの入れ替えをしようと思うと予期しない結果になります
解決手段としては
1) ビットシフトで移動する
2) エンディアン変換で移動する
3) ポインタで正常な位置から読み出す
    (もしくは最初にポインタでオフセットした位置に書き込む)
という方法があります

1と3については説明は不要だと思います

2のエンディアン変換はREV関数で行います
これも32bitI/Oです


動作確認については
    uint32_t data = 0x12345678;
    uint8_t *p = (uint8_t*)&data;
       
    xprintf("%02X %02X %02X %02X\n", p[0], p[1], p[2], p[3]);
    data = REV(data);
    xprintf("%02X %02X %02X %02X\n", p[0], p[1], p[2], p[3]);
    data = RBIT(data);
    xprintf("%02X %02X %02X %02X\n", p[0], p[1], p[2], p[3]);
という感じ
1回目のprintfでは78 56 34 12が出てきます
2回目はエンディアンを入れ替えた12 34 56 78が出てきます
3回目はビットリバーサルした結果の1E 6A 2C 48が出てきます

8bit変数のビットリバーサルを行いたい場合は
REV(RBIT( (uint8_t)hoge ))
という感じになります
REVとRBITはどちらが先でもいいはずです

でもビットリバーサルをやりたいだけならビットシフトのほうがらくだと思います


追記:2014-07-04
core_cm3.hに__RBIT及び__REV関数がありました
どちらも引数は32bitが1個 戻り値が32bit それぞれ符号無しです
やっていることはほぼ同じです

2014年5月18日日曜日

HSSC定例会に参加してきた

北海道スポーツシューティングクラブの定例会?に参加してきました
たしか今回で4回目です(忘年会除く)

いつも通り1800からの開始ですが、諸々の都合で僕は1430頃現地着

今回は撃ち合いがメインでした

僕の装備は
こんな感じ
\アウターバレル折れました/

フルオートトレーサーがついていて全長が長く、地面にぶつけた時に折れたようです

一応Contourを付けていましたが ほとんど録画出来てないですね
敵側のフルオートトレーサーのマズルフラッシュは写っていました


夜戦を平地でやる際の手順として
1. 自陣に入る
2. 敵陣を見る
3. 後ろを振り返る
4. 少しでも明かりがあったら負ける

真っ暗な中走り回ってるのに敵全員から集中攻撃喰らってあっという間にヒットです
動きまわると光が途切れるので、そこにいるというのが簡単にわかってしまうようです
なので本格的な夜戦をするなら完璧に真っ暗な場所が必要ですね 大変です。。

冬はバリケードとして雪を積んで壁を作りましたが
今回は http://www.amazon.co.jp/dp/B00BKTEHRC を使いました
20枚くらい使ったのかな?
簡単に移動できるので設営が楽で、鉄板なので撃ち込まれるとかなりいい音がします
また下に隙間が開いているので、タクトレ風に敵に撃ちこむことができます
敵も下のスリットからヒットを狙ってくるので膠着することが少ないです

夜戦ではウェアラブルカメラが基本的に使いものにならないので、SONY α7Sとか使いたいなー と思ったり これを5台くらい置いといたら面白そうだなー



AR Five seveN
SUREFIREのマガジンで撮影したかったのですが、まだ届いてないとのことでした
AR Five seveNは「1丁買ったけど不調で、個体が悪いかもしれないからもう1丁買ったけどそれも同じだった 初期ロットがダメなだけかもしれないからまた1丁買ったけど変わってない」とのことでした
どんどん増えていくネ…


こちらが武器庫(?)

コレ以外にもスチールロッカーに小物(ハンドガンとかブルパップ)が入っていたりします
また貸出用のP90とかMP5は別のところに置いてあります

正面から撮って並べてパノラマみたいにして全面を撮影してみたいですが、換算27mmのレンズだと長辺がアサルトライフルの半分くらいの長さしか撮れません
狭い場所でも平面に映せるいい方法ないかな


それと、TOP M4(カートモデル)も試し撃ちさせてもらいました
すっげー楽しかったよ!!!
撃ったM4はレールがついていないモデルだったのでContourを付けて120fps撮影はできませんでした
でも楽しかったからいいや

サバゲに使うとカート回収とかが面倒だし、だからといってカートキャッチャーを使うとカートモデルの面白さがなくなりますが プレート撃ちとかで使うと楽しいかもしれません


次回の定例会は7月19日の予定です
直前になるとFacebookグループに詳細が記載されるはずなので、時間が有るor興味がある人はどうぞ

2014年5月15日木曜日

NOAA APTのデコード

久しぶりの更新です

NOAA APTですが
コレはただの音声信号なので、比較的簡単にデコードできそうだな とか思ったのが運の尽き 自分でデコードするプログラムを書いています

ネットを探せば色々とありますが、基本的にそれらはリアルタイムのデコードを重視しており、僕のように一旦WAVとして保存してから変換する ということをやりたい場合 別のPCなどで再生した音を読み込ませる必要があります

おそらく探せばファイルからデコードできるソフトも有ると思いますが、今回は自作してみました

ということで現状ですが
同期信号の検出と輝度の補正以外を除いた、画像化処理はできるようになりました

手順として
1) WAVファイルを扱いやすいテキストファイルに変換
2) 48kHzのサンプルを4.8kHzにダウンコンバート
3) 1サンプルを1ピクセルとして画像化(横解像度2400ピクセル)
という感じです

本来NOAAは1秒に4160ピクセルですが、処理のし安さの点から横4800ピクセル(1秒に2ラインなので2400ピクセル)としています

4.8kHzの信号から同期信号を検出することができれば、画像の垂直同期と輝度調整ができるようになります

上の画像の場合、縦1318ピクセルですが、このデータをWAVからBMPに変換するのに50秒ほどかかりました
1秒に2ライン受信しているので、1318ピクセルだと659秒=約11分です
現状でもリアルタイムにデコードするより10倍ほど早いようです
また、WAVをCSVに変換し、それをもう一度CSVに変換してからTXTで書き出し、それをBMPに変換しています この部分を一気に処理できるようにすれば更に高速化ができるはずです

とりあえず次の目標は同期信号の検出で、最適化はその後になると思います


それとアンテナですが、近所のスーパーで3.2mmのステンレス線が売っていたので買ってきました
この太さになるとニッパー等での切断は不可能で、ディスクグラインダー等で切る必要があります
また巻いてある線なので、直線に伸ばすのも大変です
ワイヤーフォーミングの安いところがあったらとっとと外注したいですが、なかなかヨさそげなところが見当たらない というのが現状です

2014年5月6日火曜日

アンテナを作る

最近、amazonのワンセグチューナを購入し、NOAAを受信したりして遊んでいます

アンテナは45cmくらいのモノポールアンテナを使用していましたが、NOAAからの信号をほとんど受信できません

ということでとりあえずQFHアンテナを作ってみました