2012年12月31日月曜日

今年も無事に暮れましてお疲れさまでした。

実家で引きこもり中の隠者です。いえ、帰省してるだけですけどね。

TLでコミケ参加者の若さをうらやましがりつつ、実家で一人お留守番を命ぜられてまったりしています。そんなわけで、珍しく日中にブログ書き。

今年は、個人事業主に戻っての1年でした。個人事業主の寄り合いみたいな小さな会社経由で仕事を受けているのですが、ま、仕事探しは基本自分でやる事になってますし、顧客との契約があって初めて報酬が支払われる形式ですので、フリーランスのようなものですね。みんなで良い顧客をそれぞれ捕まえて、何れ若い人を育てたいよね、何か面白い開発もしたいよねって話はいつもしていますが、まだ、それぞれ生きる事に精一杯な感じです。何事も先立つものなりアイディアなりが重要です。

フリーになってからは、以前より一層緊張感を持って、多少無理を押してでも成果をあげようと努力してきました。緊張感の中で少しばかり無理をしすぎている気もしましたが、なんとかかんとか、1年は仕事が途切れずにやってこれました。

結構忙しい常駐勤務になってしまったので、複数仕事を受けられないのが辛い所ですが、来年もこの調子で、仕事途切れずに頑張りたいものです。

勉強関連としては、来年から横浜Android PF部がAndroidの垣根を越えて、色々扱いましょうという事になりました。業界が成熟してきて、仕事で深い人は十分深くやっているし、プライベートでなかなか話せない事が増えてきた人もいます。そんなわけで、発表者を集めるのに、最近部長が苦労されています。

以前は、飲み会でこのあたり知りたいんですよねって人がいると、次回発表お願いしますっていって無茶振れたのですが、参加者が固定化してくるとどうしても。

隠者自身、興味を失ったわけではないけれど、仕事に追われてなかなか発表も準備できませんし、もはや隠者程度が趣味でうろちょろしなくても、完成度が高くなったAndroidがそこにあり、趣味で画像入れ替えられるJCROMがあり、多くの情報がWebで流れて、仕事でやってる詳しい人もいる以上、忙しい中わざわざ二番煎じする事もないかなという気がしてきています。そんなわけで、隠者は、Wayland辺りを中心にしばらく攻めてみたいなと思います。

また、関東Qt勉強会は、主催が暇村村長から、鈴木さんに移行しました。いや、出発時点を考えると、隠者ががんばらなきゃならなかったのでしょうが、自分のブログでさえこの有様な現在、なかなか手が回らなくって。サイトの更新もさっぱりで、@luyikei さんにも怒られちゃいました。ごめんなさい。

来年は、仕事のペースを徐々に落として、勉強時間も作り、なんとか仕事オンリーな生活を脱却して立て直したいなと思っています。

それと、とあるものの開発もやりたいなと思ってます。過去のトラウマから、フリーソフトとか出すのは大嫌いなんですが、友人、知人が色々楽しそうにやってるのをみると、こそこそ自分だけで楽しむのはやめてもっと大きくみんなで遊べるものを作りたいなとも思う訳で。まぁ、まだまだ勉強段階なので、こちらは形になってきたらご報告ということで。

そんなわけで来年は、ちょっと新しい方面へのチャレンジな一年にしたいなと思います。

2012年12月30日日曜日

silkのディレクトリ構成等をぼーっと眺めてみる

silk学習中の隠者です。
さっくりファイル数を数えてみたのですが、予想以上に多いです。

ファイル数・・・・何と862もあります。これ、一人でコーディングしたんだろうか・・・task_jpさん。

1. ディレクトリ構成
  • contents
    • errors
    • root
    • tasks
  • demos
    • blog
    • carpet
    • minimal
    • ssso
  • etc
  • qthttpserver
    • examples
    • src
  • src
    • imports
    • lib
    • plugins
      • mimehandler
      • protocolhandler
    • silk
2. プロジェクト構成
  • silk.pro [subdirs]
    • src/src.pro [subdirs]
      • lib/lib.pro [lib] : libsilk.so
        • include(../../silklib.pri)
        • include(../../qthttpserver/qthttpserver.pri)
      • silk/silk.pro [silkapp] silk
        • include(../../silkapp.pri)
        • include(../../etc/etc.pri)
        • include(../../contents/contents.pri)
      • plugins/plugins.pro [subdirs]
        • mimehandler/mimehandler.pro [subdirs]
          • qml/qml.pro [silkplugin]
            • include(../../../../silkplugin.pri)
        • protocolhandler/protocolhandler.pro [subdirs]
          • http/http.pro [silkplugin]
            • include(../../../../silkplugin.pri)
      • imports/imports.pro [subdirs]
        • Silk/Silk.pro [subdirs]
          • HTML/HTML.pro [silkimports]
            • include(../../../../silkimports.pri)
          • CSS/CSS.pro [silkimports]
            • include(../../../../silkimports.pri)
          • JSON/JSON.pro [silkimports]
            • include(../../../../silkimports.pri)
          • Utils/Utils.pro [silkimports]
            • include(../../../../silkimports.pri)
          • Cache/Cache.pro [silkimports]
            • include(../../../../silkimports.pri)
          • Database [silkimports]
            • include(../../../../silkimports.pri)
          • SMTP [silkimports]
            • include(../../../../silkimports.pri)
          • OAuth [silkimports]
            • include(../../../../silkimports.pri)
          • Process [silkimports]
            • include(../../../../silkimports.pri)
    • contents/contents.pro [app, silkdeployment] phony_target
      • include(../silkdeployment.pri)
    • demos/demos.pro [subdirs]
      • blog/blog.pro [silkapp] blog
        • include(../../silkapp.pri)
ちなみに、あちこちからincludeされているpriファイルは、以下のようなincludeをしています。
  • silkapp.pri [TEMPLATE=app]
    • include(./silk.pri)
    • include(./silkrpath.pri)
    • include(./src/lib/lib.pri)
  • silkplugin.pri [TEMPLATE=lib]
    • include(./silk.pri)
    • include(./src/lib/lib.pri)
    • include(./silkrpath.pri)
  • silkimports.pri [TEMPLATE=lib]
    • include(./silk.pri)
    • include(./src/lib/lib.pri)
  • silkdeployment.pri
    • include(silk.pri)
  • silk.pri
    • include(./silkplatform.pri)
demosで有効になっているのは、今の所blogだけかな。
いやいや、なかなかどうして、結構規模の大きめですね。これは、気合いいれないとなぁ。

なお、プロジェクトですが、qthttpserverの下等にも.proはあるのですが、トップのsubdirsに含まれていなかったので除外しています。
qthttpserverは、別プロジェクト扱いにしてあって、別途ビルドできるようになっているようですね。

2012年12月27日木曜日

Qt商用ライセンスの制限事項

Qt商用ライセンスユーザーの隠者です。

本日は、ツイッターでいただいた情報で初めて知った商用ライセンスの制限事項について、まとめて起きたいと思います。


Qtのライセンスについては、以前まとめた通りですが、そのうちのCommercial Licenseに重要な制限がかかっている事がわかりました。

発端は以下のツイートからでした。


  消費者向けの携帯電話、通信機器タブレット製品開発への適用を条項5.3で禁じています。NokiaはDigiaとは独立にこの制約を履行する権利を有しています。商用ダウンロードページにはこのことだけが抜き出して書かれています。


そこで、商用ダウンロードのページにアクセスして確認すると以下のような表記がありました。
隠者の翻訳は「にゅあんすで~」なので、各自必要に応じて真面目に英語を読み直してください。

License update associated with Digia’s acquisition of all Qt rights: 
Associated with Digia’s acquisition of all rights related to Qt from Nokia, Digia have been able to make the license regulations around use of Qt in consumer and end user devices more straightforward:

Digia社のすべてのQtの権利の買収に伴うライセンス更新: 
Nokia社からのDigia社によるQtに関連したすべてのライセンスの買収に伴い、Digia社は、QtのコンシューマやエンドユーザーデバイスにおけるQtの使用関連のライセンス規則をより簡潔にすることができました。

- Use of Qt to develop mobile phones or tablet computers targeted for consumer end users is prohibited 
- 携帯電話や、コンシューマやエンドユーザー向けのタブレッド型コンピュータの開発に(商用版の)Qtを使用することは禁じられています。

- This restriction does not prohibit licensees from creating applications for any devices, including mobile phones and tablet computers
- この制限は、携帯電話やタブレットを含むあらゆる端末に対するアプリケーションの作成を禁止するものではありません。

- Nokia is named as a third party beneficiary for this restriction. Hence, Nokia has the same rights as Digia under the Qt license with respect to the restriction, and Nokia can exercise those rights independently from Digia.
- Nokia社はこの制限の第三者受益者として指名されています。このため、Nokia社はこの制限についてQtライセンスの下、Digia社と同等の権利を有しており、Nokia社はDigia社から独立に、これらの権利を行使する事ができます

Please refer to section 5.3 in the Qt license agreements : http://www.digia.com/Qt/License-Agreements/
Qtライセンス許諾書の5.3項を参照してください。

By clicking the links below you accept the above terms.
以下のリンク(商用版のダウンロード)をクリックしたら、あなたは上記を受託したこととなります。

言い訳がましく、アプリケーションの作成は妨げないと書いてありますけど、商用ライセンスのライブラリを別途インストールしてまで使うなんて、まったく持ってアホな話です。

そういうのは、Qtどっぷりの変態さんたち(ほめてます)のすることで、こんなことやってたら一般の人はQtなんて面倒なライブラリだと思うだけでしょうね。嘆かわしいです。

ともあれ、気を取り直して、該当するライセンス許諾書の該当項目をみると以下の項目が追加されていました。

5.3 Further Requirements

It is expressly acknowledged and understood by Licensee, that Licensee is strictly prohibited from using
Licensed Software for creation of mobile phones or tablet computers targeted for consumer end users.

The aforementioned shall not prohibit Licensee from using Licensed Software for the purpose of creating of applications for any devices, including mobile phone and tablet computers.

Notwithstanding anything contrary to this Agreement, it is expressly acknowledged and understood by
Licensee, that Nokia shall hereby be named as a third party beneficiary under this Agreement with respect to this Section 5.3. Therefore, Nokia shall have the same rights as Digia under this Agreement with respect to this Section 5.3, and shall be entitled to exercise such rights independent from Digia.

The licenses granted in this Section 5 by Digia to Licensee and Licensee Affiliates are subject to Licensee and Licensee Affiliate's compliance with Section 8 of this Agreement.

汚い・・・汚いです。真っ黒くろすけも驚きの黒さです。とか、心の中で思っても、口にしてはいけません。いわゆる、大人の事情というやつなのでしょう。

まぁ、正直なところ、Digia社さんには、販売対象に制限がかかってしまうだけで、メリットの無さそうな条項ですから、買収時にごねられたと見るべきでしょうか。

狙いとしては、携帯電話やタブレット開発用のフレームワークに使う場合はLGPLに制限させたうえで、将来返り咲いたときはその利益を享受したいという気持ち満々な感じですね。いやらしいですね。気持ちはわからなくはないですが、私は、こんな大人には成らないと硬く決意しました*

以前@bluepickyさんがちらりとつぶやいたときに見たDesktop版の許諾書(3.9.1)にはこんな項目なかったんですけどねぇ。確かに、Embedded版にはあったのですけど。


5.3 Further Requirements

The licenses granted in this Section 5 by Digia to Licensee are subject to Licensee's compliance with Section 8 of this Agreement.

ま、ぶっちゃけ、商用ライセンスで携帯電話やタブレット開発する予定は無いのでどうでも良いことだったのですが、Qtを人に勧めて歩く以上、こういう罠ははっきりさせておく必要があるかと思って書いておきました。


*すでに汚いおっさんだろうとか言う突っ込みはなしの方向でお願いします。

2012年12月26日水曜日

デザイン変えてみました


なるほど4時じゃないか!というわけで隠者です。

最近、主にプライベート的につらい事、苦しい事、悩ましい事も結構あって、「だう〜ん」な精神状態になる事も多いので、自分のブログにくると色の暗さに「どこか知らない場所で、楽しいって何か考えたい」とか思ってしまいます。

そんなわけで、少し明るくなれるように、記事のテンプレートをいじってみました。

過去の色の方が好みだというお客さんもいるかもしれませんが、基本、自分による、自分のための、自分らしいブログと考えてやってますので、ご理解いただけると幸いです。

え、何があったのかって?まぁ、まだ現在進行形な事もあるので、語れる時がきたら、その時にでも。

来年は、もう少し気楽に、楽しいこと中心に生きて行きたいところです。

2012年12月25日火曜日

silkビルドしてみました


また間が空いてしまった3日坊主の隠者です。メリークリスマス・・・にはちょっと遅いかな。

さて、@task_jp さんが始めた、qmlでコンテンツを書ける変態素敵なWeb serverのsilkを触りはじめました。とりあえず本日はビルドにつまづいた所を少しだけ。

1. 環境構築

環境としては、Ubuntu 12.10(64bit)版を利用しました。いや、MBPの中にVMのバックアップが転がっていたもので。Qt 5.0.0をビルドするために途中まで準備していた環境ですので、以下の事をしてあります。

$ sudo apt-get install build-essential perl python git
$ sudo apt-get install "^libxcb.*" libx11-xcb-dev libglu1-mesa-dev libxrender-dev
$ sudo apt-get install flex bison gperf libicu-dev libxslt-dev ruby

それと、ビルド途中にエラーになって気がついたのですが、zlibが必要でした。
$ sudo apt-get install zlib1g-dev

Qtはバイナリパッケージを使いました。色々問題があるらしいので、自分でビルドするのが適切なんでしょうが、まぁ、それだと普通の人には敷居が高いので。

$ wget http://releases.qt-project.org/qt5/5.0.0/qt-linux-opensource-5.0.0-x86_64-offline.run
$ chmod 755 qt-linux-opensource-5.0.0-x86_64-offline.run
$ ./qt-linux-opensource-5.0.0-x86_64-offline.run


2.ソースコードの入手

$ git clone git://git.qtquick.me/silk.git
$ cd silk
$ git submodule update --init


3. Build

$ ${HOME}/Qt5.0.0/5.0.0/gcc_64/bin/qmake
$ make

でビルドできるはずでしたが、以下のようなエラーが出ました。

g++ -m64 -Wl,-z,origin '-Wl,-rpath,/home/hermit4/Qt5.0.0/5.0.0/gcc_64:$ORIGIN/../lib' -Wl,-O1 -Wl,-rpath,/home/hermit4/Qt5.0.0/5.0.0/gcc_64/lib -o ../../bin/silk .obj/release-shared/main.o .obj/release-shared/qrc_etc.o .obj/release-shared/qrc_errors.o -L/home/hermit4/silk//lib -lsilk -L/home/hermit4/Qt5.0.0/5.0.0/gcc_64/lib -lQt5Network -lQt5Core -lpthread
/home/hermit4/silk//lib/libsilk.so: undefined reference to `deflateInit_'
/home/hermit4/silk//lib/libsilk.so: undefined reference to `deflate'
/home/hermit4/silk//lib/libsilk.so: undefined reference to `deflateEnd'
collect2: error: ld returned 1 exit status
make[2]: *** [../../bin/silk] Error 1


どうやら、zlibとリンクが取れていなようです。@task_jp さんの環境だと、Qt5のビルド時にQtバンドル版のzlibを使うような指定をしているのかもしれませんね。


どこに入れるのが適切なのかはまだ検討できていませんので、とりあえず適当に以下のような修正でしのいでいます。

--- a/src/lib/lib.pri
+++ b/src/lib/lib.pri
@@ -6,4 +6,4 @@ include(../../qthttpserver/include.pri)
 LIBS *= -L$$SILK_BUILD_TREE/$$SILK_TARGET_PATH/$$SILK_LIBRARY_PATH
-LIBS *= -l$$qtLibraryName(silk)
+LIBS *= -l$$qtLibraryName(silk) -lz

src/lib/lib.pri のLIBS指定に、-lz を追加しました。

2012/12/29 訂正。task_jpさんが対処済みでした。いまは、git cloneしてmakeすればそのまま通りそうですね。

とりあえず、しばらくはソースツリーとか、内部の実装を色々見て行きたいなと思っています。

2012年12月2日日曜日

Qtの情報源について

近いうちにQtの仕事を一部、会社の人間に引き継がなくてはな隠者です。
会社に人間にQtを覚えてもらう必要がでてきたのですが、大問題。Qt Labs Japanが無くなっている。

以前、Qtの日本語情報を仕入れるにはとてもよいサイトなので、英語めんどくさいって人には紹介していたのですが、ついにQt売却による影響が・・・。

でも、ご心配なく。朝木さんのツイートによると、以下の通りです。

Qt Labs JapanはDigiaが引き取ると言うことでデータを渡していますが、再公開は遅れている(?)ようです。 

というわけで、きっと近いうちに復帰するでしょう。
まぁ、会社の同僚N氏には、英語でさくっと読んでもらうことになるでしょうが、現状についてちょっとまとめておきましょう。

最近の動向

Qtは2008年に開発元であったTrolltech社からNokia社が買収し、Qtの開発者はNokia社へ。Nokia社主導の元、ライセンスにLGPLを入れ、大きな注目を浴びていたものの、携帯事業への展開の柱だったMeeGoの開発途上で、スマートフォン競争の激化にさらされMeeGo撤退。

Qtの開発はオープンコミュニティーに移行を始めていましたが、ついに、2011年3月には、商用ライセンス事業をDigia社に売却、2012年の8月に、すべての事業を売却する流れとなりました。


現在の公式コミュニティ・商用ライセンス

・Qt Project - 開発コミュニティー 
http://qt-project.org/

以前は、Trolltech社、その後はNokia主導で行われてきたQtの開発ですが、現在はコミュニティーへと移管されました。
Qt Developer Networkだったものは、現在、Qt Projectに移管されています。

・Digia社 - ライセンス事業者
http://qt.digia.com/

商用ライセンスや、事業としてQtに積極的に開発参加しているのが、Digia社です。
もともとソフトウェア会社だったようで、サポートは予想以上に良いです。

LGPLでお金を払わずに商用利用ができるようになったので心配していましたが、商用ユーザー向けの追加のフレームワーク提供や、バグフィックスを行い安定させたものをユーザーに提供したりと、がんばっているように感じます。

・SRA社 - 日本のQt商用ライセンス販売代理店
http://www.sra.co.jp/qt/
隠者が商用ライセンス買うのにお世話になっている代理店さんです。ほかにも代理店さんはあるようですが、情報が一番充実していたので。


日本のコミュニティ

@luyikeiさんという超若手が、日本全国のQt情報がばらばらに発信されているのを嘆いて、なんとか統一できませんかと動いているサイトです。忙しくなりすぎてしまって、さっぱり手伝えなくてごめんなさい。


Qtのあれこれ(仮)

本日朝木さんからツイートのあったサイト。Qt Labs Japanの続き的なことをちらほらやるとの事ですね。協力者募集とのことです。楽しみですね。


福岡で熱心に行われているQt勉強会です。日本で最初に行われたQt勉強会ってたぶん福岡なんじゃないかな。
Qtプログラミング入門というコンパクトで読みやすい入門本を書かれた津田さんが出入りされている勉強会です。

@himamuraさんが主催されている勉強会で、現状は、Tizenとかと一緒になっていますが、元Trolltechの鈴木さん、朝木さん、オライリー本の翻訳関係やってらっしゃる杉田さんなんかも顔をだされることがあります。

関西Qt勉強会
MeeGo勉強会なんかも熱心にやっておられた@moguriso さんが音頭とってらっしゃる勉強会ですね。まだ盛り上がりに欠けているようなんですが、関西の明るいノリでがんばってほしいところです。

名古屋Qt勉強会
名古屋の重鎮、@androidzaurus 先生や、日本からQt Ambassadorに選ばれた理ログの@IoriAYANEさんなんかがいらっしゃる勉強会です。名古屋おそるべし。名古屋いいなぁ。名古屋こわいとか思います。

松山Qt勉強会
初めてしりましたが、松山でもがんばろうとしている方がおられました。
残念ながら人の集まりが悪かったようですが、がんばって欲しいですよねぇ。


Qt4以降のQt関連書籍

・Qtプログラミング入門

出ているQt本の中では一番薄く、C++経験者がQtをさっくり使ってみるには気楽にできるサイズかと思います。ツールの使い方等はあまり言及されていませんが、実際作ってみる系で良いかと。



・入門 Qt4プログラミング
・実績 Qt4プログラミング


Qtプログラミングやるなら定番の2冊。

ちなみに、実践編の内容は
・WebKit
・QSound,QMovie
・モデル・ビュー
・QtConcurrentによるスレッド化
・QThreadでのマルチスレッドプログラミング
・QTextDocument
・QGraphicsView,
・アニメーションとステートマシーン
といった内容です。まぁ、使わない人は使わないあたりなので、需要があるかはその人次第でしょうか。




・Introduction to Design Patterns in C++ with Qt

翻訳本は出ていませんけど、C++の解説もちらほら入っている、C++入門レベルの人向けの書籍はこちら。とはいえ、プログラミングの初歩程度はある程度できる人向けになっていますけど。




まだまだこんなのあるよっていうような情報があれば、ぜひぜひご連絡ください。

2012年12月1日土曜日

近況報告

はてさて、また3ヶ月放置してしまった隠者です。お久しぶりです。
早いもので、12月になりました。今年も残すところあと1ヶ月ですね。カレンダーをみると、どうにも狐につままれた気分になるのですが、まぁ、悔いても嘆いても今年も終わろうとしています。

お仕事の件

ツイッターでぼやきぼやきしていたのでご存じの方も多いかと思いますが、非常に忙しかったです。

世界全体の景気もずぶずぶと沈みつつあり、特に日本の状況の真っ暗感といったらかなり厳しいところです。

隠者は、結局、個人事業主の寄り合いみたいな吹けば飛ぶ会社の一員として過ごしているので、かなり厳しい状況でしてねぇ。お仕事なくなったら、即無収入というリスクの高さ。

幸いお客様の信頼をいただいて、厳しいながらも、ずいぶん苦労して予算を確保してくださってるようで、まだ仕事を続けられています。その代わり、多少の無茶でもがんばって通す意気込みで行かないとならず、結構無茶な状況が続いていました。

とりあえず、なんとか一山越えて、この後に向けての準備中です。来年は、さらに厳しい一年になりそうですから、お客様の仕事を手伝いつつ、自分でも何かあっても食いつなげるだけの準備をしていかないとなりません。

このまま日本が落ちるところまで落ちれば、重い腰を上げる人、風を切って先頭を歩き始める人も増えて、もう一度盛り上げるために、各所で動きがでるだろうとか楽観視はしていますが。
ともあれ、他人の事より、他人への期待より、まずは自分。たち止まり、過去をうらやましがり、恨み辛みをはき出すだけで満足するような姿は、可愛い甥っ子、姪っ子には見せられません。前を歩いて行けるよう、努力したいです。

体調の件

皮膚病の方は、一進一退ですが、なんとか紫外線治療のおかげで、もう死にたいとか思うところまでは行かずにすんでいます。ただまぁ、治療を受けた後の疲労感と、痛痒さがちょっと辛いところですね。嬉しい事に、デルモベートからワンランク下の薬になりました。

ぜんそくの方は、また咳込むことが増えました。一応空気清浄機兼加湿器を購入し、寝室でかけっぱなしにするようになってから、少し落ち着いてきていますけど。いい加減、もう一度真面目に病院通わないとなぁと思ってます。

勉強の件

Androidで組み込みの勉強をということを中心にしていたのですけど、最近の興味はWaylandに移ってしまいました。
大昔に、お仕事でXlibでアプリを書かなくてはならないことがありまして。いや、組み込み機器にTinyX乗せてで、ツールキット入れる容量がないって話でしてねぇ・・・無茶をしたものです。
あれ以来、もっとコンパクトですっきりしたWindow Systemが必要と思ってきたので、これは!!ということで。

最近、YAPFも発表者が限られてしまってネタ的にも厳しいということで、ほかのプラットフォームなども解禁になるとのこと。来年は、このネタで突っ走りたいなと思っています。

Qtの件

最近の仕事は、Qtから少し遠ざかってしまったので、つまらんとつぶやいたら鈴木さんから手伝ってといわれてしまいました。そんなわけで、とりあえず、仕事で使えそうもないので、当分見送りだったQt5ですが、環境復活してライフワークにしようかなと。
しばらく触って無い間にこちらもずいぶん・・・げふん。まぁ、この話は大事なブログネタなので追々。

ネットワークゲームの件

以前、別の場所で書いたような気もしますが、隠者はネットワークゲームとアニメをほぼ唯一の生きがいにして来ました。何せ、皮膚病で酒もタバコもNGですし、若い頃には、服の下は家族にすら、人目にさらすくらいなら首にナイフを突き立てる方がましという感じで家庭も持ちませんでしたからねぇ。
Diablo,UO,EQ,リネージュ、Diablo2, FF11,AO,EQ2,RiftときてDiablo3と。まぁ、さすがに社会人なので、それほど時間がとれる訳でもないのですが、ちまちまと遊んで来ました。

まぁ、そのようなわけで、ごめんなさい。ブログ書く時間とれなかったのは、夜のわずかな空き時間はDiablo3をちょろっとやっていた事も一因かも・・・いや、勉強の方もやってるのですが、そのアウトプットを出せる程までにはという程度なのですが。まぁ、隠者の精神を安定させるためのお遊びなので、こればかりはという感じなのです。すいません。

初期の頃から色々な人と出会いと別れを繰り返しつつ、驚くことに10年以上、ゲームの世界でつきあってきた人たちもいます。ただ、みんな社会人になり、家庭ができ、子が生まれて、時はどんどん流れてきました。一緒にプレーする仲間は現在わずかに3人。一人はレアポップになり、そしてもう一人とは、ここ2ヶ月、隠者が忙しくなってなかなかログインできなくなったことで、ぎくしゃくとしてしまいました。

丁度、新しいイベントで自分の分を手伝ってもらった後、友人の分をというあたりで、急にログインしなくなりましたからねぇ・・・・ごめんなさいと伝えたものの、まぁ、いろいろなプレッシャーと忙しさで追い込まれていた時期ですから、余計な事言ったりもしてしまいまして。それもぎくしゃくとする原因になった気もしています。長いつきあいとはいえ、さすがに愛想つかされてそうな気もしますが・・・。

もう少し余裕がでてきたら、またのんびりと一緒に冒険してもらえたら嬉しいのですけどねぇ。

明日からに向けて

とりあえず、今日からがんばろうとか思ってます。
明日からと言っといてなんですが、明日は、明日の風にまかせるとして、今目の前でやるべきと思える事を積み重ねる時期かなと。

数年前までは、3年先、5年先、10年先をいつも考えてきました。
けど、どうにも手の出せないところで、とんでもない事が起きていて、さっぱり先行きが見えません。

そんなわけで、どう転ぶかわからない明日の事より、今日一日をどう過ごすのか、それを考えてやっていこうと思います。

というわけで、どうでもよい近況報告で、とりあえず、12月のはじめの日記とさせていただきました。



2012年7月31日火曜日

Qtのライセンス

さて、すっかりご無沙汰しておりました隠者でございます。
ネタにしたい話題は多いものの、忙しさに負けてすっかり放置でした。

そんなところに、Qt良いよって熱心に勧誘してみた友人が、Qtのお勉強を始めてみてくれたようです。そんな中で、友人もまた職業プログラマですから、気になるのはライセンスのようでして、どのようになってるのかなぁとの質問を受けました。

ま、そんなわけで、少しばかりライセンスを中心にQtネタを書いて見ます。ただ、僕は著作権法にはあまり詳しくないので、一般論レベルでしかかけませんので、ご容赦ください。

Qtとは

Qtとは、Haavard Nord と Eirik Chambe-Engの二人が1991年頃から開発をはじめた、C++ベースのGUIフレームワークです。元々はTrolltech社(旧Quasar Technologies社)として開発・サポートを続けておりましたが、2008年にNokia社にTrolltech社が買収され、現在、Nokia社にCopyrightが移っています。

Nokia社は、Qtをベースにした携帯電話向けフレームワークの作成を行っておりましたが、現在はWindowsベースのOSにビジネスをシフト、昨年からQt自体の開発はオープンなコミュニティに、商用的なサポートはDigia社へと移管されました。

クロスプラットフォームが大きな売りの一つで、同じコードで、Windows、Linux、Macをはじめ、現在では、VxWorksや、Androidといった組込みOSでも、同じコードからアプリを生成できます(もちろん、機種依存のコードを入れないように注意が必要なのですが)

現在でもコミュニティにより熱心な開発が続けられており、GUIフレームワークの枠を超え、アプリケーション開発プラットフォームに近いものになってきています(専用のIDE,エディタ、モバイル端末向けシミュレータ、独自実装の開発言語とスクリプトエンジン等も含まれています)。

ライセンス

Qtのライセンスはバージョンを経る毎に変遷してきましたが、現在のライセンスは、大きくわけでオープンソースのGPL、LGPL.と、商用ライセンスの3種類からなります。

コミュニティによる開発にシフトはしましたが、Qt自体の開発に貢献する場合は、Nokia社との間にLeagl Acceptを締結することになります。このため、提供されたコードは、商用ライセンス版にも反映されることになります。

GPLv3

GPL自体は著名なライセンスなので、改めて語るまでもないかもしれません。
The GNU General Public License で、GPLとリンクするアプリケーション、ライブラリのすべては、GPLもしくはGPLと同等のライセンスにしなくてはならないと定められています。
つまり、Qtへの改変はもちろん、リンクしたプログラムのソースコードも配布する義務が生じますので、仕事で使う場合にはかなり慎重に検討する必要が生じてしまいます。

LGPL 2.1+ Nokia Qt LGPL Exception version 1.0

もともとオープンソース版ライセンスとしてはGPLが使われてきたのですが、Nokia社により買収された後、より多くの開発者を取り込むためか、GPLよりも緩い、The GNU Lesser General Public Licenseが適用されました。

GPLの場合、Qtを使ったアプリケーションもまたGPLにする必要がありましたが、LGPLの下では、LGPLと緩い結合ーダイナミックリンクするプログラムに関してはLGPLを適用する必要はないとされています。つまり、ダイナミックリンクしている自分のアプリケーションについては、ソースコードの公開義務はありません。
ただし、QtのライブラリはLGPLですので、ライブラリ自体に変更を加えた場合はソースコードの開示義務が生じますし、改変を加えていなくても、ソースコードの提供を求められた場合、何らかの方法で提供する義務が生じます。

なお、Qtはライブラリであるため、ユーザーのアプリケーションには一部ヘッダファイルに定義されているコードをincludeしなくてはなりませんが、例外規定が用意されていて、ヘッダファイルのincludeによりLGPLの適用は必要ないとされているため、インクルードによりLGPLにしなくてはならないといった事態は避けることが可能です。

ただし、LGPLの場合は、以下を担保しなくてはなりません。
  • LGPLの部分は、利用者が置き換えられなくてはならない(スタティックリンク禁止)
  • 利用者が置き換えるために、リバースエンジニアリングを許可しなくてはならない
つまり、スタティックリンクや、リバースエンジニアリング禁止条項は入れられないことになります。そのほかに、著作権表示の必要性や、もろもろありますので、詳しくはLGPLのライセンスファイルを参照してください。

Qt Commercial License

上記2つが無償で利用できるオープンソースのライセンスだったのに対し、商用ライセンスは、ライセンス費用を支払う代わりに、すべてのソースコードを非公開で改変可能、リバースエンジニアリングも禁止することができるライセンスです。
日本では、SRA社さんと、ISB社さんが代理店を行っており、グローバルではDigiaがサポートする企業となっています。

ただし、これも条件が一つあって、商用ライセンスユーザーは、商用ライセンスとオープンソースライセンスの選択的な利用が可能ですが、ソースコードを共用するプロジェクトで、商用ライセンス開発者とオープンソース開発者の混在は不可能なようです。

つまり隠蔽したい部分のライブラリだけ商用ライセンス保持のAさんが作って、全体的な部分は、B,C,DさんがLGPLで作るという使い方はできないようです。

まぁ、独立したプログラムはライセンスの影響を及ぼさないとされていますので、CUIで作った商用ライセンスエンジンにオープンソース版で作ったGUIラッパーをかぶせるなんてほは、可能だとは思いますけど。

なお、SRA社さんに確認した限りにおいて、保守契約がある間に取得したQtの最終版と、保守契約期間中に行った改変に関しては、保守契約満了後も商用ライセンスの保護下にあり、満了時点までのQtで開発を行う限りは、保守の更新は必要ないとのことです。

*ただ、Qtのライブラリを変更できなくなるようなので、脆弱性が見つかるとアウトですね

また、開発を一切行わない、商用版とのリンクを行うビルドだけの作業においては、商用ライセンスを必要としないそうで、開発者のライセンスファイルを使って、第三者が変わりにビルドすることは可能との事でした。たとえば、ビルドサーバーとそのメンテナの分までライセンスを購入する必要はないようでした。

なお、ここまでは、Qt自体のライセンスでしたが、実はQtがさらに呼び出すライブラリなども存在しており、それらには、それぞれに別のライセンスと著作権が存在しています。

リンクするQtモジュールによっては、それらも確認し、著作権表示等が必要になるケースがありますので、注意が必要です。

詳しくは、Qtのライセンスに関するドキュメントを参照することをお勧めします。


えー、めんどくさいって声が聞こえそうでなりませんが、まぁ、Qtやその利用するライブラリ郡の機能を全部自分で実装することを思えば、ずいぶん安上がりな対策だと思いますよ。

2012/12/27追記 Qtの商用ライセンスには、携帯電話、タブレットの開発に重大な制限がありますのでご注意ください。詳しくはこちら

2012年2月14日火曜日

はっぴーばれんたいん

きっとバレンタインと無縁なお前たちに告げる!とか言われてごめんなさいしそうな隠者です。
まぁ、甘いものは好きなんだけど、キモオタなので、こういう甘ったるい日とは無縁です。

最近、思いっきり日記をさぼってしまいましたが、締切・・・もといいわゆる納期に追われておりました。まぁ、現在でも落ち着いたわけではないものの、少し余裕がでてきたということで。


なんか途中まで何かを調査していた気もするのですが、少し呆けているので、今日は少しQt日記です。


最近の仕事では、Qtをがりがり使っていたのですが、諸事情でQtとリンクしてはいけない部分も作りこむことになりました。しかしまぁ、Qtにどっぷり浸ってしまうと、やはりC++とSTLのみでのコーディングだと、とくにコンテナ周りの速度だとか気になったりすることも出てきます。

たとえば、あるバイナリファイルをメモリに読み込むとします。
エラー処理をがっつり省いて、効率考えずに簡略化してC++で書くと


    std::istream file(path, std::ios::in|std::ios::binary);
    std::istreambuf_iterator<char> begin(file), end;
    std::vector<char> buf(begin, end);

のような書きっぷりになるわけですね。
それでは、Qtだとどうかというと

    QFile file(path);
    file.open(QIODevice::ReadOnly);
    QByteArray buf = file.readAll();

という書きっぷりになります。
え、同じ3行じゃんと言われればその通りですし、前者のほうがC++っぽくて好きという人も時折いたりしますが、前者の書きっぷりだと、速度的に大きなファイルの読み込みには向かないんですね。

前者の書き方だと、std::vector<char>は、1byteずつcharを読み込みながら、必要に応じて内部のバッファを拡張していきます。実は、このコストが意外と馬鹿にならないくらいにかかります。

真面目に大きめのバイナリファイルを開くのであれば、上記のような書き方はせずに、先にファイルサイズを取得し、バッファサイズを確保してから読み込まないとボトルネックになったりします(当然、コード量は増えます)。

それはQtでも同じじゃないだろうかと思うのですが、QFileがreadAll()する場合、読み込むサイズやバッファ確保量などを適度に調整しながら読み込んでいるようです。

以下の条件で同じLinux上で速度差を軽く測定してみました。

g++ : 4.0.2
計測方法: time コマンド
読み込みファイル : /usr/local/Trolltech/Qt-4.7.4/lib/libQtWebKit.so.4.7.4 (size: 27,453,072)

C++(STL)版

  • real : 0m3.561s
  • user: 0m2.207s
  • sys : 0m0.116s


Qt(4.7.4)版

  • real: 0m0.064s
  • user: 0m0.006s
  • sys: 0m0.046s


ちなみに、memusageコマンドで見てみると
C++(STL)版

  • heap total: 67,117,407, heap peak 50,340,192, stack peak:924, malloc回数28回


Qt版

  • heap total: 27,458,375,  heap peak 27,458,175, stack peak: 1740, malloc回数 122回


という結果になっています。まぁ、偶然、C++に不利そうなファイルサイズだったようですが、それにしてもずいぶん大きく差が開いていますね。

Qtは継承も多く存在するため、malloc回数は予想以上に多くなっています。しかしながら、グラフのコピペは省略しますが、大きなサイズのメモリ確保は、C++版のほうが回数が多い(読み込みの後半に拡張時に大きなメモリの確保・解放が繰り返される)ようです。

このため、この様な結果になっているようです。


ちなみに、ほかにも、std::stringに比べると、QtのQString等は強力で便利だったりします。
たとえばAsciiのテキストファイルから1行読み込んで、前後の余計な空白文字を削除したいとか考えると

C++(STL)版では

    std::ifstream fin(path);
    std::string line:
    while (std::getline(fin, line)) {
        typedef std::string::const_iterator itr_t;
        typedef std::string const_reverse_iterator ritr_t;
        itr_t l;
        ritr_t r;
        for (l=line.begin() ; l!=line.end() && isspace(*l); ++l);      
        for (r=line.rbegin() ; r!=ritr_t(l) && isspace(*r); ++r);
        line = std::string(l,r.base());
    }

のようなコードが必要なのに対し、
  
Qt版
   QFile file(path);
   if (file.open(QIODevice::ReadOnly)) {
       while (!file.atEnd()) {
           QString line = QString(file.readLine()).trimmed();
       }
    }

といった書きっぷりになりますね(ビルド通してないのでかなり適当コードです。ごめんなさい)。そりゃ、最初からtrimmed()があれば楽だろうさ!って怒られそうだけど、本当にそういった欲しい機能があらかじめ実装されているわけで。

ほかにも、QListやQVector,QString等は、内部データは書き込みや変更が発生するタイミングまで実際のコピーは遅延するなど、極力必要になるまで重い処理は動かないようにできてたりするんですね。

Qtはメモリ食いというイメージは確かにあるのですけど、少ないコード量で、やりたいことが、適度なパフォーマンスで提供されていると考えると、すごくプログラマに便利な道具なんです。

まぁ、嘘だ!と思う人は、C++っぽくないとか、ライブラリがでかすぎるとか、クラス数多すぎとかいわず、ぜひ一度触れてみてはどうでしょうか。


2012年1月6日金曜日

ノブレス・オブリージュ

今日こそは、位置情報の取得について更新しようと思っていたのですが・・・。
帰りの電車で神山健治監督のつぶやきを眺めてしまったのが運の尽き。

https://twitter.com/#!/PH9/status/155257322854301696

21:00『東のエデン』TVシリーズニコ生にて一挙放送! http://goo.gl/XIUwe まずは神山監督 山本P 石井Pでエデンについて語ります!#edenoftheeast #PH9


というわけで、完全に東のエデンモードにはいっています。
東のエデンは、物語にARが登場するお気に入りのアニメの一つでして。

まぁ、僕は残念な半オタクっ子なので、大半のアニメは恥じることなく見ますし、ひとりでアニメを見に映画館にも行くわけですが、それができないおっさんでも、大手を振ってみて問題のない作品かと思います。

ちなみに、21:00からの作品に関する紹介番組で、Blu-ray Boxが発売されることも知ってしまいました。





【Amazon.co.jp限定】『東のエデン』 Blu-ray "Noblesse Oblige" BOX(表紙描き下ろし設定資料集付き)


2009年4~6月フジテレビ“ノイタミナ”ほかにてTVシリーズ11話が放送され話題になり、2009年11月に「東のエデン 劇場版I The King of Eden」、2010年3月に「東のエデン 劇場版II Paradise Lost」が全国劇場公開され大ヒットを記録!
TVシリーズから続き、劇場版でだんだんと明らかになっていく謎を1パッケージですべて全て収録!

これが2万以下なら安いだろう!ってことで、3月発売だから自分への誕生日プレゼントということで予約しちゃいました。


というわけで、今夜は東のエデンの日ということで、技術ブログは2日連続で持ち越し・・・ごめんなさい。


あ、リンクはアフェリエイトのなので、いやだなと思うひとは、自分で検索かけて、予約してください。

ノブレス・オブリージュ、今後も救世主たらんことを。


2012年1月5日木曜日

LARK買ってみました

さて、ちょっと調べものはひと休憩して、買い物記録。
Apple Storeの初売りを眺めようと見に行ったら、安くなってるのと別の品に目が止まりました。




どうみても濃いパッケージなんですが、この歯を出して笑っている男性の腕にまいているのが、今回の品です。こんな形をしていますが、目覚まし時計です。iPhoneとセットで使うようにできています。




小汚い部屋の片隅でとったのでよけいなものが移ってますが、テーブルの上に乗っているのが中身です。就寝までの時間、充電しておくDockとACアダプタ、腕にまくバンド、そしてセンサー付きの目覚ましです。
普通の目覚ましと違うのは、まずアラーム音はしないです。振動して起こすタイプですね。

パッケージの男性がどや顔なのは、傍らの女性を起こさずに目が覚めたからということなんでしょう。まぁ、僕には無縁の話ではあるのですが。
じゃ、なんでこんなのをかったのかというと、睡眠の履歴を見てみたかったからです。




年末の疲れがたっぷり残っていたので、いつもより睡眠時間が長めだったので、まだあまり参考になっていないのですが、眠りについた時間と、起床時間が記録されています。



ただそれだけなのか?というと、実は違っていて、眠っている間の寝返り等の動作から、眠りの深さと周期を計測してくれます。7日間分のデータで、眠りのパターンや改善点を示唆してくれるらしいです。

どこかのサイトでは、おきやすいところで起こしてくれるとかいう記述をみたのですが、どうも気のせいだった模様。今のところアラームの設定時間に起こされている気がします。

ちなみに、起きた時の気分や、その日の状態をメモできる機能もあり、どんな時のどういう眠りの傾向にあるのか記録して分析してみるにはよさそうです。

ちなみに、このデータ、Web上のサーバーにも転送しているらしく、メーカーページに自分のアカウントでログインすると、ブラウザでも状態が表示されます。プライバシーを気にする方には、ちょっと嫌な機能かもしれませんね。

まぁ、僕は、探されると住所も氏名も電話番号も、趣味や性癖までネットで把握されちゃうであろうくらいそのあたりがずぼらにできているので、そんなに気にしていませんけどね。

とりあえず、今年の目標は、最低4時間半睡眠は確保する事で、今のところ順調に進んでいるので、このまま、記録をとりつつ、眠りの質を改善してみたいなと思っています。

P.S.
一応アプリは日本語ですが、コーチの機能や、Webページの多くが英語です。
もう少しがんばって翻訳して欲しいなぁ。

2012年1月3日火曜日

位置情報取得について調べてみた(その3)

さて、ではGpsLocationProviderを中心に調べてみる事にしよう。
前回、GpsLocationProviderを呼び出すとnativeの初期化関数が呼び出されるという話をした。実際に何をやっているのかというと以下のようになっています。


static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
    int err;
    hw_module_t* module;

    method_reportLocation             = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
    method_reportStatus                   = env->GetMethodID(clazz, "reportStatus", "(I)V");
    method_reportSvStatus              = env->GetMethodID(clazz, "reportSvStatus", "()V");
    method_reportAGpsStatus         = env->GetMethodID(clazz, "reportAGpsStatus", "(III)V");
    method_reportNmea                    = env->GetMethodID(clazz, "reportNmea", "(J)V");
    method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
    method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
    method_reportNiNotification     = env->GetMethodID(clazz, "reportNiNotification",
                                                                "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
    method_requestRefLocation      = env->GetMethodID(clazz,"requestRefLocation","(I)V");
    method_requestSetID                   = env->GetMethodID(clazz,"requestSetID","(I)V");
    method_requestUtcTime             = env->GetMethodID(clazz,"requestUtcTime","()V");

    err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {
        hw_device_t* device;
        err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
        if (err == 0) {
            gps_device_t* gps_device = (gps_device_t *)device;
            sGpsInterface = gps_device->get_gps_interface(gps_device);
        }
    }

    if (sGpsInterface) {
        sGpsXtraInterface      = (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
        sAGpsInterface           = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
        sGpsNiInterface         = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
        sGpsDebugInterface = (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
        sAGpsRilInterface     = (const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);
    }
}

やっている事は以下の事になります。
  1. Native層から呼び出すいくつかのJavaのMethod IDを入手し、globalに保存
  2. gpsのHAL実装ライブラリのloadとModuleのopen, Interfaceの入手
  3. 拡張Interface情報の取得
なお、上記で入手している以下の情報については、HAL層の実装(gps.xxxxx.so)で用意する必要があります。
  • hw_module_t
  • hw_device_t (gps_device_t)

これらの基本的なstructは、hardware/libhardware/include/hardware/hardware.h に定義されています。

最初の hw_module_t は、hw_get_module()関数で、以下のようにして取得されます。

    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    hmi = (struct hw_module_t *)dlsym(handle, sym);

この事から、HAL(gps.xxxx.so)内部では、HAL_MODULE_INFO_SYM_AS_STRの名前のhw_module_t構造体が実体定義されている必要があります。

なお、hardware.hの中の定義は以下の通りです。

#define HAL_MODUULE_INFO_SYM HMI
#define HAL_MODUULE_INFO_SYM_AS_STR HMI

typedef struct hw_module_t {
    uint32_t tag;
    uint16_t version_major;
    uint16_t version_minor;
    const char *id;
    const char *name;
    const char *author;
    struct hw_module_methods_t* methods;
    void* dso;
    uint32_t reserved[32-7];
} hw_module_t;

typedef struct hw_module_methods_t {
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);
} hw_module_methods_t;


実際、gpsの実装例であるqcom用のコードでは以下のようになっています。
[hardware/qcom/gps/loc_api/libloc_api/gps.c]

static struct hw_module_methods_t gps_module_methods = {
    .open = open_gps
};

const struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id = GPS_HARDWARE_MODULE_ID,
    .name = "loc_api GPS Module",
    .author = "Qualcomm USA, Inc.",
    .methods = &gps_module_methods,
};

続いて、hw_device_tのポインタの取得になります。ただし、これには1点注意が必要で、構造体のキャストによるハックが含まれています。
hw_module_t.hw_module_methods_t.open() では、hw_device_t** にポインタが含まれて帰ります。ただし、この実体は、hw_device_t型構造体の実体ではありません。 gpsの場合、gps_device_t型の構造体のポインタとなります。

hw_device_t* でアクセスしている場合は、先頭のcommon領域にポインタがあう事になり、gps_device_t*でアクセスした場合は、get_gps_interfaceの関数ポインタまで含めたメモリ領域にアクセスできるようになります。

typedef struct hw_device_t {
    uint32_t tag;
    uint32_t version;
    struct hw_module_t* module;
    uint32_t reserved[12];
    int (*close)(struct hw_device_t* device);
} hw_device_t;

[hardware/libhardware/include/hardware/gps.h]
struct gps_device_t {
    struct hw_device_t common;
    const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev);
};

[hardware/qcom/gps/loc_api/libloc_api/gps.c]
static int open_gps(const struct hw_module_t* module, char const* name, struct hw_device_t** device)
{
    struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
    memset(dev, 0, sizeof(*dev));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (struct hw_module_t*)module;
    dev->get_gps_interface = gps__get_gps_interface;

    *device = (struct hw_device_t*)dev;
    return 0;
}

この辺りからは、HALの中でもGPS固有の処理となります。
const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev) に設定されているgps__get_gps_interfaceで、GPSドライバのインターフェースを入手します。

typedef struct {
    size_t          size;
    int   (*init)( GpsCallbacks* callbacks );
    int   (*start)( void );
    int   (*stop)( void );
    void  (*cleanup)( void );
    int   (*inject_time)(GpsUtcTime time, int64_t timeReference,int uncertainty);
    int  (*inject_location)(double latitude, double longitude, float accuracy);
    void  (*delete_aiding_data)(GpsAidingData flags);
    int   (*set_position_mode)(GpsPositionMode mode, GpsPositionRecurrence recurrence,
            uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time);
    const void* (*get_extension)(const char* name);
} GpsInterface;

つまり、HALのGPSドライバとしてはこのインターフェースを実装する事になります。
さらに、この最低限のAPIに加え、実装に応じて追加の機能を提供するための拡張用のAPIを取得するのが、get_extension関数ですね。


GPS_XTRA_INTERFACE
typedef void (* gps_xtra_download_request)();

typedef struct {
    gps_xtra_download_request download_request_cb;
    gps_create_thread create_thread_cb;
} GpsXtraCallbacks;

typedef struct {
    size_t          size;
    int  (*init)( GpsXtraCallbacks* callbacks );
    int  (*inject_xtra_data)( char* data, int length );
} GpsXtraInterface;


AGPS_INTERFACE
typedef struct {
    size_t          size;
    AGpsType        type;
    AGpsStatusValue status;
    uint32_t        ipaddr;
} AGpsStatus;

typedef void (* agps_status_callback)(AGpsStatus* status);

typedef struct {
    agps_status_callback status_cb;
    gps_create_thread create_thread_cb;
} AGpsCallbacks;

typedef struct {
    size_t          size;
    void  (*init)( AGpsCallbacks* callbacks );
    int  (*data_conn_open)( const char* apn );
    int  (*data_conn_closed)();
    int  (*data_conn_failed)();
    int  (*set_server)( AGpsType type, const char* hostname, int port );
} AGpsInterface;


GPS_NI_INTERFACE
typedef struct {
    size_t          size;
    int             notification_id;
    GpsNiType       ni_type;
    GpsNiNotifyFlags notify_flags;
    int             timeout;
    GpsUserResponseType default_response;
    char            requestor_id[GPS_NI_SHORT_STRING_MAXLEN];
    char            text[GPS_NI_LONG_STRING_MAXLEN];
    GpsNiEncodingType requestor_id_encoding;
    GpsNiEncodingType text_encoding;
    char           extras[GPS_NI_LONG_STRING_MAXLEN];
} GpsNiNotification;

typedef void (*gps_ni_notify_callback)(GpsNiNotification *notification);

typedef struct
{
    gps_ni_notify_callback notify_cb;
    gps_create_thread create_thread_cb;
} GpsNiCallbacks;

typedef struct
{
    size_t          size;
   void (*init) (GpsNiCallbacks *callbacks);
   void (*respond) (int notif_id, GpsUserResponseType user_response);
} GpsNiInterface;

GPS_DEBUG_INTERFACE
typedef struct {
    size_t          size;
    size_t (*get_internal_state)(char* buffer, size_t bufferSize);
} GpsDebugInterface;


AGPS_RIL_INTERFACE
typedef struct {
    agps_ril_request_set_id request_setid;
    agps_ril_request_ref_loc request_refloc;
    gps_create_thread create_thread_cb;
} AGpsRilCallbacks;

typedef struct {
    size_t          size;
    void  (*init)( AGpsRilCallbacks* callbacks );
    void (*set_ref_location) (const AGpsRefLocation *agps_reflocation, size_t sz_struct);
    void (*set_set_id) (AGpsSetIDType type, const char* setid);
    void (*ni_message) (uint8_t *msg, size_t len);
    void (*update_network_state) (int connected, int type, int roaming, const char* extra_in
fo);
    void (*update_network_availability) (int avaiable, const char* apn);
} AGpsRilInterface;

拡張のAPIも含めると、HALでは結構な数のAPIを実装する必要がありそうですね。
ちなみに、先ほどみたqcomの実装だと3種類の拡張を用意しているようですね。

static const void* loc_eng_get_extension(const char* name)
{
    if (strcmp(name, GPS_XTRA_INTERFACE) == 0)
    {
        return &sLocEngXTRAInterface;
    }
    else if (strcmp(name, AGPS_INTERFACE) == 0)
    {
        return &sLocEngAGpsInterface;
    }
    else if (strcmp(name, GPS_NI_INTERFACE) == 0)
    {
        return &sLocEngNiInterface;
    }
    return NULL;
}

というわけで、GpsProviderLocationは、まず真っ先にHALのAPIを読み込んでいます。
その後、GpsLocationProvier.isSupported()が呼ばれているわけですが、これは、

public static boolean isSupported() {
    return native_is_supported();
}

static jboolean android_location_GpsLocationProvider_is_supported()
{
    return (sGpsInterface != NULL);
}

とあるように、HALから基本となるInterfaceが取得できているかのチェックとなります。
さて、実際のLocationProviderのInterfaceですが、以下のように定義されています。

public interface LocationProviderInterface {
    String getName();
    boolean requiresNetwork();
    boolean requiresSatellite();
    boolean requiresCell();
    boolean hasMonetaryCost();
    boolean supportsAltitude();
    boolean supportsSpeed();
    boolean supportsBearing();
    int getPowerRequirement();
    boolean meetsCriteria(Criteria criteria);
    int getAccuracy();
    boolean isEnabled();
    void enable();
    void disable();
    int getStatus(Bundle extras);
    long getStatusUpdateTime();
    void enableLocationTracking(boolean enable);
    /* returns false if single shot is not supported */
    boolean requestSingleShotFix();
    String getInternalState();
    void setMinTime(long minTime, WorkSource ws);
    void updateNetworkState(int state, NetworkInfo info);
    void updateLocation(Location location);
    boolean sendExtraCommand(String command, Bundle extras);
    void addListener(int uid);
    void removeListener(int uid);
}

ちなみに、GpsLocationProviderでは
    /**
     * Returns true if the provider requires access to a
     * data network (e.g., the Internet), false otherwise.
     */
    public boolean requiresNetwork() {
        return true;
    }

    /**
     * Returns true if the provider requires access to a
     * satellite-based positioning system (e.g., GPS), false
     * otherwise.
     */
    public boolean requiresSatellite() {
        return true;
    }

    /**
     * Returns true if the provider requires access to an appropriate
     * cellular network (e.g., to make use of cell tower IDs), false
     * otherwise.
     */
    public boolean requiresCell() {
        return false;
    }

が設定されています。A-GPSかどうかに関わらず、Network必要なのか。

クライアントの処理を見た時の、LocationManager.getBestProvider(criteria,truer); が実行されると条件にあうProviderを探しながら最適なProviderを返すわけですが、この処理が以下のようになっています。

    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
        List<String> goodProviders = getProviders(criteria, enabledOnly);
        if (!goodProviders.isEmpty()) {
            return best(goodProviders).getName();
        }

        // Make a copy of the criteria that we can modify
        criteria = new Criteria(criteria);

        // Loosen power requirement
        int power = criteria.getPowerRequirement();
        while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
            power = nextPower(power);
            criteria.setPowerRequirement(power);
            goodProviders = getProviders(criteria, enabledOnly);
        }
        if (!goodProviders.isEmpty()) {
            return best(goodProviders).getName();
        }

        // Loosen accuracy requirement
        int accuracy = criteria.getAccuracy();
        while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
            accuracy = nextAccuracy(accuracy);
            criteria.setAccuracy(accuracy);
            goodProviders = getProviders(criteria, enabledOnly);
        }
        if (!goodProviders.isEmpty()) {
            return best(goodProviders).getName();
        }

        // Remove bearing requirement
        criteria.setBearingRequired(false);
        goodProviders = getProviders(criteria, enabledOnly);
        if (!goodProviders.isEmpty()) {
            return best(goodProviders).getName();
        }

        // Remove speed requirement
        criteria.setSpeedRequired(false);
        goodProviders = getProviders(criteria, enabledOnly);
        if (!goodProviders.isEmpty()) {
            return best(goodProviders).getName();
        }

        // Remove altitude requirement
        criteria.setAltitudeRequired(false);
        goodProviders = getProviders(criteria, enabledOnly);
        if (!goodProviders.isEmpty()) {
            return best(goodProviders).getName();
        }

        return null;
    }

以下の順で条件を緩くしながら、合致するProviderを探して、条件が合致するProviderが見つかったら、その中から最適なものを返しています。
  1. ユーザー指定のCriteriaに合致するProviderを探す。
  2. Power条件を下げながらその他がCriteriaに合致するProviderを探す。
  3. 精密さの条件を下げながらその他のCriteriaに合致するProviderを探す。
  4. 方角に関する条件を削除してその他のCriteriaに合致するProviderを探す。
  5. 速度に関する条件を削除してその他のCriteriaに合致するProviderを探す。
  6. 高度に関する条件を削除してその他のCriteriaに合致するProviderを探す
この時の、条件合致は、getProviders()でProvider側で実装している
boolean meetsCriteria(Criteria criteria);
でチェックされていますね。ちなみに、GpsLocationProviderの条件はPowerしかみてません。

    public boolean meetsCriteria(Criteria criteria) {
        return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
    }

とりあえず、ここまでで初期処理とProviderの選択のうっすらとしたイメージがつかめたので、次回は、ようやくLocationの取得を調べてみたいと思います。

2012年1月2日月曜日

位置情報取得について調べてみた(その2)

さて、昨日は位置情報の取得技術と、Androidのアプリからの利用方法について軽く調べてみました。というわけで、続きの調査メモです。

前回のエントリで見た位置情報関連クラスは以下の通りです。
  • Address
  • Criteria
  • Geocoder
  • GpsSatelite
  • GpsStatus
  • Location
  • LocationManager
  • LocationProvider
さて、この中で真っ先に取得するのは、LocationManager [ frameworks/base/location/java/android/location/LocationManager.java ]
でした。LocationManagerは、Binderを使ってSystem Location Service(LocationManagerService)へアクセスするためのクラスのようです。

実体は、ContextImplクラスのstaticブロックで生成・登録されています。
[src] frameworks/base/core/java/android/app/ContextImpl.java
        registerService(LOCATION_SERVICE, new StaticServiceFetcher() {
                public Object createStaticService() {
                    IBinder b = ServiceManager.getService(LOCATION_SERVICE);
                    return new LocationManager(ILocationManager.Stub.asInterface(b));
                }});

というわけで、ここでPF部として目を付けなくてはならないのは、LocationManagerServiceですね。で、このLocationManagerServiceが生成されているのは何処か?というと、いつか何処かでみた場所なんですね。この辺りはPF部でも発表してますし、さらっと呼び出しだけ見ておきましょう。

SystemServer::init2()
  ServerThread::start()
    ServerThread::run() [frameworks/base/services/java/com/android/server/SystemServer.java]
        location = new LocationManagerService(context);
        ServiceManager.addService(Context.LOCATION_SERVICE, location);
        final LocationManagerService locationF = location;
        locationF.systemReady(); [frameworks/base/services/java/com/android/server/LocationManagerService.java]
            Thread thread = new Thread(null, this, "LocationManagerService");
            thread.start();

注: 例によって上記はC++チックなメソッド名の表記をしているだけです。

init周りを追いかけた時にでてきたSystemServerのServerThread中で生成され、LocationManagerServiceスレッドがstartされています。

public void LocationManagerService::run()
{
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    Looper.prepare();
    mLocationHandler = new LocationWorkerHandler();
    initialize();
    Looper.loop();
}

Runされると、initialize処理を行った後、Looperでまわしています。このLocationManagerService::initialize()の中で、呼んでいるLocationManagerSerivce::loadProviders()でAndroid共通のProviderの生成と登録、Geocoderの生成が行われています。

LocationManagerService::loadProviders()
    LocationManagerService::loadProvidersLocked()
        LocationManagerService::_loadProvidersLocked()
        {
                if (GpsLocationProvider.isSupported()) {
                    GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
                    mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
                    mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
                    addProvider(gpsProvider);
                    mGpsLocationProvider = gpsProvider;
                }
                PassiveProvider passiveProvider = new PassiveProvider(this);
                addProvider(passiveProvider);
                mEnabledProviders.add(passiveProvider.getName());

                PackageManager pm = mContext.getPackageManager();
                if (mNetworkLocationProviderPackageName != null &&
                    pm.resolveService(new Intent(mNetworkLocationProviderPackageName), 0) != null) {
                    mNetworkLocationProvider =
                        new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
                                mNetworkLocationProviderPackageName, mLocationHandler);
                    addProvider(mNetworkLocationProvider);
                }

                if (mGeocodeProviderPackageName != null &&
                         pm.resolveService(new Intent(mGeocodeProviderPackageName), 0) != null) {
                    mGeocodeProvider = new GeocoderProxy(mContext, mGeocodeProviderPackageName);
                }
                updateProvidersLocked();
        }

生成/AddされているProviderは、3つ
  • public class GpsLocationProvider implements LocationProviderInterface
  • public class PassiveProvider implements LocationProviderInterface
  • public class LocationProviderProxy implements LocationProviderInterface
GeocodeProviderとしては
  • public class GeocoderProxy
が生成されています。

またこのうちPassiveProviderは、mNetworkLocationProviderPackageNameのパッケージがある場合に生成されています。これは、リソース中のcom.android.internal.R.string.config_networkLocationProvider の文字列になります。
また、GeocodeProxyについても、mGeocodeProviderPackageNameのパッケージがある場合にのみ生成されており、こちらは、com.android.internal.R.string.config_geocodeProvider の文字列になります。

4.0のソースコードに含まれるtunaのconfig設定 [
device/samsung/tuna/overlay/frameworks/base/core/res/res/values/config.xml]  ですと

<string name="config_networkLocationProvider">com.google.android.location.NetworkLocationProvider</string>
<string name="config_geocodeProvider">com.google.android.location.GeocodeProvider</string>

となっています。
ちなみに、GplLocationProvider classには

    static { class_init_native(); }

となっています。ですので、最初のif文の時にこの関数が呼び出されます。
こちらは、nativeコードで実装されており、実際には

android_location_GpsLocationProvider_class_init_native()

で、その中でhw_get_module("gps",....)が呼び出されます。この辺りはHALの実装の周りの部分でして、必要になるライブラリを探して行きます。4.0のコードでは以下の順にライブラリを探し見つかったものをdlopenを使ってZygoteとリンクします。


  1. /vendor/lib/hw/gps.<ro.hardware>.so
  2. /system/lib/hw/gps.<ro.hardware>.so
  3. /vendor/lib/hw/gps.<ro.product.board>.so
  4. /system/lib/hw/gps.<ro.product.board>.so
  5. /vendor/lib/hw/gps.<ro.board.platform>.so
  6. /system/lib/hw/gps.<ro.board.platform>.so
  7. /vendor/lib/hw/gps.<ro.arch>.so
  8. /system/lib/hw/gps.<ro.arch>.so
  9. /system/lib/hw/gps.default.so

以前は、libがついていた気がするんですけどね。最近はlibがつかないのかな。

ついでにいうと、implements LocationProviderInterfaceなclassは他にもあって
  • public class MockProvider implements LocationProviderInterface
というのがあります。こちらは、LocationManager::addTestProviderを行った時に内部でaddProviderされるProviderのようです。CTS等のテスト時に使われるようですね。


さて、とりあえずここまでで見た限りは、LocationManagerはLocation情報を管理しているわけではなく、LocationProviderの管理をしていると見るのが自然なようです。
というわけで、次回は、LocationProviderについて、Gpsを中心にもう少し調べてみたいなと思います。

2012年1月1日日曜日

位置情報取得について調べてみた(その1)

Androidに関する位置情報についての下回りをちょっと調べてみようかと思っています。GPSやその他の位置情報取得に関する記事なんてそれこそ山のようにあるため、あえてエントリにする必要もないのですが、自分用メモということで。

位置情報取得というと、すぐに思い浮かぶのはGPSや基地局情報だったりするのですが、現在はそれらの組み合わせや、Wi-Fiの位置データベースを使った方法など、多くの方法が存在するようです。Androidと直接関係が無いものもあるでしょうが、まずは基礎知識を知らなければ駄目でしょうということで調べてみました。まぁ、仕様書を読んだわけではなく、いろいろなサイトを眺めて歩いただけなので、間違いもあるかもしれませんけど。

位置情報取得方法


・GPS測位
複数のGPS衛星から送信されている電波を受信することで、位置を特定する技術です。
GPS衛星からは、2種類のデータが30秒周期で送信されています。
  - アルマナックデータ (すべての衛星の軌道データ)
  - エフェメリスデータ (自身の正確な位置データとこのデータ発送時の正確な時刻データ)
GPS受信機は、初期状態ではアルマナックデータを使い、位置測定に利用できる衛星を確認し、最初のエフェメリスデータの時刻情報で時刻を設定後、次のデータでデータ受信の遅延を計測することで、衛星までの距離を計算します。
これを最低3つの衛星で繰り返すと、三角交差法を使い、経度と緯度を導きだす事ができます。4つの衛星で行えば高度も出せるという仕組みです。。
アルマナックデータはだいたい1週間程度、エフェメリスデータは1時間半程度は有効なため、2つのデータの有効時間中は、時刻情報から距離を割り出すだけで、位置情報を取得できますが、データの存在しない初期処理からの測位では数分、その後の位置情報収集は最低でも30秒程度はかかるようですね。

・DGPS(Differential GPS)
もともとGPSは軍事目的の衛星で、軍用に使う正確な位置を割り出せる暗号データとあえてノイズを含めた若干不正確なGPSデータを送信していたそうです。
このため、一般の利用では、100m程度の誤差がでていたものを補正するために考案された手法で、位置のわかっている地上の基地局との比較で誤差補正を行い制度を高める技術です。

・A-GPS(Assisted GPS)
GPSは、情報の取得を開始してから最低でも30秒程度、最悪数分間は位置を特定できません。また、起動データや位置データの受信は建物内にいるとノイズによりデータ受信は難しく、位置の特定が困難になります。
携帯電話は、例えば基地局情報などで、携帯端末のおおざっぱな位置が特定できています。そこで3G回線等別の経路で全衛星軌道データと、衛星位置情報を取得し、GPSからは比較的ノイズに強い時刻情報だけを取得することで、位置情報をGPSより高速に、建物内でもわりと正確に特定できるようにする技術がA-GPSです。


・セルベース測位
現行の携帯電話は、セル方式というものをとり、無線基地局を多数設置し、ある一定の範囲に留めることで、同じ周波数帯域をできるだけ再利用するように設計されているそうです。セルというのは、無線基地局の電波が届く範囲の事になります。携帯電話に通知を行うために、携帯電話が最後に確認できたセルIDが記録されており、定期的に更新されています。このセルIDを元にどの位置に居るのかを特定するという技術です。
携帯でGPSサービスと言われるものが出始めた初期の頃は、実際にGPS受信をしておらず、このおおよその位置情報を使った機種も多かったようですよね。
位置に関する誤差については、セルの広さによりますので、狭い範囲に設置されている都市部は小さく、田園部とか湾岸部等の場合は誤差が大きくなるようです。

・基地局測位
この辺りからキャリアによっていろいろ変わってたりするようで、いまいちつかめていません。
KDDIでは、3つ以上の基地局との同期信号を使って、位置を計算するようです。GPSの衛星からの時刻データの代わりに、基地局との同期信号を使うことで測定するようですね。
DoCoMoさんでは、基地局に同期された時間情報を持っていないため、何か別の方法で測位しているようですが。
ウィルコムは、アンテナの受信強度等から特定しているような記載をみかけましたし、
ソフトバンクはどうなんだろう。もうちょっと調べてみたいなと思っていますが、とりあえずおいておきましょう。

・Wi-Fiアクセスポイントによる測位
Wi-FiのアクセスポイントのMACアドレスと位置情報のデータベースを使って、現在の位置を特定する方法
ローリングで都市を回り、Wi-Fiのアンテナ情報を一気に収集したものと位置情報をあわせて保持している企業のデータを使って特定するようなので。そういえば、僕のHTC AriaのWi-Fiの位置が、自宅に登録されていそうなんですよね。外出先でAria経由で接続すると自宅に居る事になるし・・・・。


とまぁ、こんなあたりが、ざっと主要な位置情報取得技術となるでしょうか。


Android Frameworkでの実装
さて、ようやく本題のAndroidについてです。
いきなり下回りのコードを読むにもあたりがつかないとならないので、クライアント側でおこなうサンプルをみながら、キーになる所を抜粋してみてみました。

(1) LocationManagerの取得
LocationManager locMgr  = (LocationManager)getSystemService(LOCATION_SERVICE);

(2) 最適なLocationProviderを選択
Criteria criteria = new Criteria();
String provider = locMgr.getBestProvider(criteria, true);

(3) 最後にわかっている位置情報を取得
Location location = locMgr.getLastKnownLocation(provider);

(4) 定期的に位置の変化を取得するためのListner登録

LocationListener listener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }
};

long minTime = 15000;
float minDistance = 1.0;
locMge.requestLocationUpdates(provider, minTime, minDistance, listener);

とすると、minTime[msec]以上の間隔で、minDistance[m]以上の変化があれば情報が取得される事になります。


(5) 経度、緯度、高度等の情報から住所情報への変換
Geocoder geocoder = new Geocoder(context, Locale.JAPAN);
List<Address> addressList = geocoder.getFromLocation(latitude, longitude, 5);

(6) GPSの状態取得/衛星情報の取得
GpsStatus gpsStat = locMgr.getGpsStatus(null);
Iterable<GpsSatellite> satellites = gpsStat.getSatellites();

(7) GPSの状態変化イベント取得方法

GpsStatus.Listener statListner = new GpsStatus.Listener() {
    @Override
    public void onGpsStatusChanged(int event) {
        switch(event){
        case GpsStatus.GPS_EVENT_STARTED:
            break;
        case GpsStatus.GPS_EVENT_STOPPED:
            break;
        case GpsStatus.GPS_EVENT_FIRST_FIX:
            break;
        case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
            break;
        }
    }
}
locMgr.addGpsStatusListener(statListner);


とまぁ、位置情報周りの主要なクラスの使い方というのはこんな所でしょうか。

アプリ側は、管理クラス LocationManagerから位置情報を提供するLocationProviderを選択し、Locationを取得するという流れとなっています。ここまで見た感じだと、Frameworkの実装としては、位置情報はGPSに限らずProviderから取得できるように設計されており、アプリケーションは、Criteriaを使ってProviderの条件を取得して、適切なProviderを選択させる事が可能です。もちろん、getBestProvider()を使わずに、providerにLocationManager.GPS_PROVIDERを指定することで、GPSの情報だけを使うようにも実装できるわけですが。

どんなProviderが実装されているのかについては、List<String> getProviders(boolean enabledOnly); を呼び出せば、取得できるようですね。あとで、自分の端末で試してみようかな。

各クラスの詳細やその先の実装はまた次回ということで。