端末キャプチャツール「captty」

筑波大の古橋貞之(viver)氏が開発されたターミナルのキャプチャツールcapttyを使ってみました。ターミナルサイズの動的な変更のサポートに対応しています。

続きを読む 端末キャプチャツール「captty」

JAN コードのチェックスクリプト

財団法人 流通システム開発センターで公開されている JAN コードの仕様に基き、与えられた文字列が正しい JAN コードであるか否かを返すシェルスクリプト関数です。

続きを読む JAN コードのチェックスクリプト

ビープ音でモールス信号を再生

以前、ブログでも取りあげたbsdgamesパッケージにあるmorseコマンドを利用して、ビープ音(PCスピーカー)でモールス信号を再生するスクリプトを書いてみました。 ビープ音の再生には、自由に周波数と再生時間を指定する事が出来るbeepコマンドを利用しました。

#!/usr/bin/env bash

f=1800 # 周波数

#正確な再生単位
b=60 # 単位長さ
t=$(($b*1)) # 短点長さ
w=$(($b*3)) # 長点長さ
s=$(($b*1)) # 無音長さ
n=$(($b*3)) # 次の文字との無音長さ

#こっちの方が聞き易い気がする
#b=50 # 単位長さ
#t=$(($b*2)) # 短点長さ
#w=$(($b*6)) # 長点長さ
#s=$(($b*1)) # 無音長さ
#n=$(($b*3)) # 次の文字との無音長さ

morse $@ | while read line
do
    echo $line
    opt=$(echo '$line' | sed \
    -e 's/dit/ -n -f $f -l $t -n -f 1 -l $s/g' \
    -e 's/daw/ -n -f $f -l $w -n -f 1 -l $s/g' \
    -e 's/^ *-n *//')
    if [ "$opt" ]; then
        beep $opt -n -f 1 -l $n
    else
        beep -f 1 -l $n
    fi
done

$(()) での計算を行っている為、bash依存です。適宜、exprなどに置き換えれば他のシェルでも使えるはずです。 無音部分は、周波数1Hzの音を再生する事によって実現しています。 実行すると、以下のように表示され、ビープ音でモールスが再生されます。

$ sh bmorse hello
dit dit dit dit
dit
dit daw dit dit
dit daw dit dit
daw daw daw

dit dit dit daw dit daw

ビープ音に加えて、赤外線送信デバイスをチカチカ光らせて遊んでみたいです。

※ 2020/07/02 度重なるブログ移転・ブログシステムのアップデートにより崩れた記事を校正。

NEC LifeTouch NOTE with SKK for Android IS01 ed. その2

NEC LifeTouch NOTE に、SHARP IS01 のハードウェアキーボード向けにカスタマイズされた SKK for Android をインストールし使ってみた感想を書きましたが、作者の ray_m さんにコメントを頂き、修正を施したとの事だったので、早速使ってみました。

修正がほどこされた点をかいつまんで紹介します。

  • Shift キー押下しっぱなしでの連続入力に対応。修正前のバージョンでは、「入れる」と入力する際、タイプでは「IReru」となりますが、I と R を Shift キーを押しっぱなしにして連続で入力する事が出来ませんでした。( Shift+i、Shift+r の間で、Shift キーを”離す”必要があった。) ray_m さんによると、IS01 のタイプ方法をベースに作成されていた為という事でそれを受けてすぐに修正して頂きました。LifeTouch NOTE は持ってらっしゃらないようなのに、迅速に修正して頂けるなんて頭が下がるばかりです。

IS01 の特殊キーは、同時押しではなく、キーを押した次のキーに作用するように出来ています。 これは QWERTY ハードウェアキーボードを搭載していても、サイズ自体が小さい為、同時押しがしづらい物理的な問題に対処するものだと考えられます。例えば、IS01 で大文字の ‘A’ を入力する場合、Shift キーを押した上で、( 押しっぱなしにしても、離してしまっても、Shift 押下状態が続くので、その直後に ) a キーを押す事によって入力する事が出来ます。自分の IS01 は電話専用機になってしまっているので、SKK を導入する必要はないですが、LifeTouch NOTE や Dynabook AZ のようなネットブックに匹敵するサイズの QWERTY キーボードを搭載している Android 端末上では、SKKer にとって、キラーアプリになるくらい外せないアプリになっています。

  • z キーでの記号入力に対応。カタカナ入力モードのトグルキーである q キーや、半角英数モードにする l キーは以前から利用出来ていましたが、日本語でよく利用する記号を 2 タイプで呼び出す z キー機能がありませんでした。これ自体はクリティカルに SKK の使い勝手を落とすわけではないのですが、新しいバージョンではサポートされるようになりました。これで、『』~・…‥←↓↑→ といった記号を 2 タイプで入力する事が出来ます。
  • 一時的に半角英数を入力する際、▽の表示に対応。ちょっと細かな話ですが、/ キーを押して一時的に半角英数文字をタイプする際に、最初の 1 文字を入力するまで、SKK による入力中である事を示す▽が出てきませんでした。最新版では、これもちゃんと出ている模様で、/ キーを押した状態で「えーと、スペルどうだっけ」と一瞬ディスプレイから目を離した時に、再度、/ キーを押してしまって「▽/」という入力状態にならなくなりました。
  • 通常の Android 端末にはまず付いていないであろう Delete キーは、LifeTouch NOTE 専用のテキストエディタである「ライフノート」では、IM がオンの状態でも使う事が出来ます。コピー C-c、ペースト C-v、カット C-x、アンドゥ C-z、全選択 C-a などのライフノート・ショートカットキーを使うにはは、IM をオフにする必要があります。

前回の記事は、SKK for Android をインストールしたその夜に使い勝手を調べながら書いたものですが、それから 1 ヶ月以上使ってみて、ますます満足しています。残すは変換候補の表示位置のみといったところでしょうか。

LifeTouch NOTE は、せっかく左にある Menu キー( IBM-PC キーボードで言う CapsLock の位置 )を Ctrl キーに変更する設定項目まで搭載してくれているのに、Android 自体の仕様が全体的に Ctrl キーをサポートしていないのが残念な限りです。自分に課題を出すとすれば、確定 C-j やキャンセル C-g を多用している SKKer としても、”C” になるキーを指定させてそれらをサポートしたいところです。

2011/09/23 追記

ray_m さんから新しいバージョン 1.4 のご連絡があり,早速試してみると,変換候補の表示位置がずれてしまう現象が解消されたようです.

また,上の記事には書いていませんでしたが x キーによる小文字(っ,ぁ,ぃ,ぅ …など)にも今回のバージョンから対応しているようで,使用感はほとんど Emacs や SKKIME と変わらなくなっています.

さらに SKK 使いでフルキーボードを搭載している Android 端末ユーザには,離せないアプリになっていると思います.

※ 2020/07/02 度重なるブログ移転・ブログシステムのアップデートにより崩れた記事を校正。

bsplinecurve コマンド引数( knot 値)を相互変換

NURBS 曲線の knot 値( Bスプライン基底関数を決定する為の値 )は、一般的に下記のように ( 階数 + 制御点数 ) 個の単調増加する配列として表されます。

T = [ 0 0 0 0 1 2 3 3 3 3 ]

一方、OpenCASCADE Draw Test Harness の bsplinecurve コマンドにて上に示す knot 値を持つ NURBS 曲線を生成するには、下記のように指定する必要があります。

0 4 1 1 2 1 3 4

太字部分は knot 値自体を示すのではなく、”直前にある knot 値をくりかえす回数” を示します。Draw Test Harness の公式リファレンスでは誤った記載で引数の解説がなされていた為、少し迷いました。仮に、前者を Plain knots array、後者を Compressed knots array と呼ぶと、下記のような相互変換を行う Perl スクリプトが組めます。

#!/usr/bin/env perl

# Plain knots array to Compressed knots array
# for Draw Test Harness of OpenCASCADE

sub pKnot2cKnot {
  my @pKnot = @_;
  my @cKnot = ( );
  my $bfr = -1, $nb = 0, $i = 0;
  foreach $val ( @pKnot ) {
    if ( $val != $bfr ) {
      $cKnot[$i++] = $nb if ( $i > 0 );
      $cKnot[$i++] = $bfr = $val;
      $nb = 1;
    }
    else {
      $nb++;
    }
  }
  $cKnot[$i] = $nb;
  return @cKnot;
}

# Compressed knots array to Plain knots array
sub cKnot2pKnot {
  while ( length( $val = shift( @_ ) ) ) {
    $buf .= ( "$val " x ( shift( @_ ) ) );
  }
  return split( " ", $buf );
}

このような仕組みを使って、ある三次元 CAD 形式のファイルから Draw Test Harness 経由で、BRep モデルを作成する事が出来ました。ひょっとしたら pKnot2cKnot() は、もう少しスマートに出来るかも。

2020/07/02 追記

度重なるブログ移転・ブログシステムのアップデートにより崩れた記事を校正。ついでに Ruby で書き直してみました。

#!/usr/bin/ruby

def pknot2cknot(str)
  ar = []
  c = 1
  x = nil
  str.split(' ').each_cons(2) do |n, m|
    if n != m
      ar << [n, c]
      c = 1
    else
      x = n
      c += 1
    end
  end
  (ar << [x, c]).join(' ')
end

def cknot2pknot(str)
  Hash[*str.split(' ')].map{|n, m|"#{n} "*m.to_i}.join.strip
end

p pknot2cknot("0 0 0 0 1 2 3 3 3 3") # => "0 4 1 1 2 1 3 4"
p cknot2pknot("0 4 1 1 2 1 3 4")     # => "0 0 0 0 1 2 3 3 3 3"

劇的に小さくなると思いきや、そんなことなかったのが残念。

Array#partition でも Array#group_by でもなく、配列から隣合う要素を比較し、区切りを見つけて多次元配列を返すメソッド…ないかなぁ。

Python で IRC クライアント的な

基本

無駄がなくて綺麗なコード。やっぱり Python って素敵。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket, string

# ソケット作成、接続、ログイン
irc = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
irc.connect( ( 'irc.example.com', 6667 ) )
irc.send( '''
    USER arianne * * arianne
   NICK arianne
   JOIN #hogefuga
''' )

while True:
   # recv() で、受信するまでブロッキング
   buf = unicode( irc.recv( 1024 ).strip(), 'utf-8' )
   # コマンドプロンプトの場合は、'cp932' を指定
   print buf.encode( 'utf-8' )
   # IRC サーバから PING が来たら PONG を自動応答する
   if string.split( buf )[0] == "PING":
      irc.send( "PONG " + string.split( buf )[1] + "\n" )
   # 文字列に反応してみたり
   if buf.find( u"こんにちは" ) > -1:
      irc.send( u"PRIVMSG #hogefuga :こんにちわぁ\n".encode( 'utf-8' ) )
モジュール化

車輪の再発明だと思いつつ、自分の使い易い形のクラスにしてみた。サーバー応答対応やエラーと例外処理が未実装だけれど「スレッド化した受信監視→外部から指定されたイベントハンドラ実行」は、これで・・・いけるはず・・・。もうちょっとスマートに書けるようになったらいいな。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket, string, threading
import logging

class ircc ( object ):
   """The IRC Client Class"""
   def __init__( self, hostname, port, server_encoding='utf-8',         terminal_encoding='utf-8' ):
      # ソケット作成
      self.server = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
      self.server.connect( ( hostname, port ) )
      self.server_encoding = server_encoding
      self.terminal_encoding = terminal_encoding
      script_encoding   = 'utf-8'
      logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s' )
   def __str__( self ):
      return 'ircc'
   def send( self, message ):
      # 渡された文字列をサーバに送信するだけ
      message = unicode( message, self.script_encoding )
      logging.debug( ">>> " + message.encode( self.terminal_encoding ) )
      self.server.send( message.encode( self.server_encoding ) + "\n")
   def recv( self, receive_size = 1024 ):
      # サーバから文字列を受信するまで待機
      buf = unicode( self.server.recv( receive_size ).strip(), self.server_encoding )
      for line in string.split( buf, '\n' ):
         logging.debug( "<<< " + line.encode( self.terminal_encoding ) )
      return buf
   # こういう細々とした IRC コマンドは、はたして個別に実装する必要があるのかニャー
   def quit( self, message = '' ):
      self.send( 'QUIT :' + message )
   def nick( self, nickname ):
      self.send( 'NICK ' + nickname )
   def join( self, channel ):
      self.send( 'JOIN ' + channel )
   def privmsg( self, to, message ):
      self.send( 'PRIVMSG ' + to + ' ' + ' :' + message )
   # ログイン・フェイズはエラー処理が重要になってくるので、メソッド化しておく。
   def login( self, loginname, nickname, description, wait_for_motd = True ):
      self.send( 'USER %s * * :%s' % ( loginname, description ) )
      self.nick( nickname )
      #if self.recv().find( '433' ):
      #    self.quit()
      #    return 1
      while wait_for_motd:
         if self.recv().find( ' 376 ' ):
            break
   # イベントハンドラ、send()、recv() それぞれのアドレスを渡してスレッド化
   def set_event( self, event_handler ):
      _irc_recv_thread( event_handler, self.send, self.recv )

class _irc_recv_thread( threading.Thread ):
   """ メッセージイベントを検知し、外部から定義された
        イベントハンドラを呼ぶスレッドオブジェクト """
   def __init__( self, event_handler, send_handler, receive_handler ):
      self.__event   = event_handler
      self.__send    = send_handler
      self.__receive = receive_handler
      self.__msg     = ''
      threading.Thread.__init__( self )
      self.setDaemon( True )
      self.start()
   def run( self ):
      while True:
         # メインループ
         self.__msg = self.__receive()
         if len( self.__msg ) > 0:
             for line in string.split( self.__msg, '\n' ):
                 if string.split( line )[0] == 'PING':
                     self.__send( 'PONG ' + string.split( line )[1] )
                     self.__event( line )
                 else:
                     break

使うのは、以下のようなイメージで。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import ircc

# イベントハンドラ
def check_message( msg ):
    if msg.find( u"こん" ) > -1:
        irc.privmsg( '#hogefuga', 'こん' )
    irc = ircc.ircc( 'irc.example.com', 6667, 'utf-8' )
    irc.login( 'arianne', 'arianne', 'Arianne the BOT', False )
    irc.join( '#hogefuga' )
    # イベントハンドラを登録
    irc.set_event( check_message )
    while True:
        pass # 何か他の事でもやる

んー、クラス化するメリットが少ないかも。日々使ってないとすぐ忘れるタチなので、wxPython/PyGame なんかと一緒に IRC プロトコルを使った通信将棋やチェスでも書きたいなぁ。

※ 2020/07/02 度重なるブログ移転・ブログシステムのアップデートにより崩れた記事を校正。Python なのにインデントがすべて吹き飛んでいるので間違いがあるかも…。