search
LoginSignup
2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

本記事は、株式会社ピー・アール・オーアドベントカレンダーの12日目です。

やりたいこと

前回、前々回で、Garmin Connectからfit形式の自分の計測データをダウンロードすることができましたが、これではまだランサムウェアに負けないための予防的措置でしかありません。
本来やりたいことは、fitデータから必要なデータを抽出し、好きなように分析してみたいという事なので、ここらでfitデータに立ち向かう必要があります。
今回も超地味かつ共感を得にくい内容で攻めていきます。

VSCodeのHex Editor

さて、対象がバイナリファイルなので当然バイナリエディタが必要になります。
バイナリエディタというと、なかなか「これ」というものが無くて、viのバイナリモードが結局使いやすかったりしてたイメージなんですが、2020年7月にVSCodeの拡張としてMicrosoftが出したHex Editorというものが結構良さそうだったので、使ってみることにしました。
Untitled.png

Hex Editorを入れた状態でバイナリファイルをVSCodeで開こうとすると
image.png
こんなポップアップが出て、バイナリモードで開くかどうか選べるようになります。

解析してみます

参考資料

Garmin公式です。図解もあり読み解いていくためには必須のドキュメントですね。
※図は、本記事には著作権的にどうなのかわからんかったので転載していません。

File header

まず始めはFile headerで、これは以下仕様となっているそうな。

Byte Parameter Size(Bytes) Description
0 Header Size 1 ヘッダサイズを示す。最少は12byteで、この場合は12番目と13番目のCRCを含まないとのこと
1 Protocol Version 1 Protocolバージョン
2 Profile Version LSB 2 Profileバージョン
3 Profile Version MSB
4 Data Size LSB 4 Data Recordsセクションのサイズ(bytes)を示す(ヘッダ及びCRCは含まず)
5 Data Size
6 Data Size
7 Data Size MSB
8 Data Type Byte[0] 4 ここはテキストファイルで開かれたときに何のファイルかがわかるように、".FIT"というテキスト値が入る
9 Data Type Byte[1] 同上
10 Data Type Byte[2] 同上
11 Data Type Byte[3] 同上
12 CRC LSB 2
13 CRC NSB

ファイルヘッダの中で重要そうなのは、Header SizeとData Sizeっぽいですね。
実際のファイルを見てみます。
image.png

先頭byteによるとヘッダは14byteのようです。上記の表でいうとCRCを含むということですね。

次にData Sizeです。ここは4byteなので4*8(bit)=int32の値を参照しましょう。16703bytesですね。
image.png

Data Records

15byte目からがData Recordsということですが、Record Headerは冒頭1byteがRecord Headerというものらしいです。

Record Header

Record Headerには2種類あり、最上位ビットで見分けるようです。

Normal Header

最上位ビット=0で、以下の仕様をとる

bit value Description
7 0 Normal Headerを示す
6 0 or 1 Message Type
1: Definition Message
0: Data Message
5 0(default) Message Type Specific (Developer data flag)
4 0 Reserved
0-3 0-15 Local Message Type

先ほど見ていたデータでは以下のようになってました。仕様と照らし合わせるとDefinition Messageですね。
image.png

Timestamp Header

最上位ビット=1がTimestamp Headerと呼ばれるヘッダとのこと
いったん省略(出てきたら追記)

Definition Message

Definition Messageの場合は以下構成を取るようです。

Byte Description Length Value
0 Reserved 1Byte 0
1 Architecture 1Byte Architecture Type
0: Definition and Data Messages are Little Endian
1: Definition and Data Message are Big Endian
2-3 Global Message Number 2Byte 0:65535 – Unique to each message
*Endianness of this 2 Byte value is defined in the Architecture byte
4 Fields 1Byte Number of fields in the Data Message
5-4 + Fields * 3 Field Definition 3Bytes(per Field)
5 + Fields * 3 # Developer Fields 1Byte Number of Self Descriptive fields in the Data Message(Only if Developer Data Flag is set)
6 + Fields * 3 - END Developer Field Definition 3bytes(per Field)

Architecture

実際のデータを見てみます。まずはArchitectureから
image.png
0なので、Little Endianですね。

Global Message Number

次にGlobal Message Numberは0でした。このファイルの一番初めのデータだからですね。
image.png

Fields

次にフィールドが何個あるか?です。7個ですね。
image.png

Field Definition

さて次が少々難解ですが、5-4 + Fields * 3ってどういうことなんでしょうね。フィールドは7個あるということだったので、5byte目から4+7*3=25byte目までってことなんでしょうかね。
↓Field Definitionと思われる範囲
image.png

まあ1フィールドは3byteずつということなので、一つ一つ見ていきましょう。
各フィールドの定義は以下

Byte Name Description
0 Field Definition Number Defined in the Global FIT profile for the specified FIT message
1 Size Size (in bytes) of the specified FIT message’s field
2 Base type Base type of the specified FIT message’s field

ここでField Definition Numberってやつが公式のドキュメント読んでもよくわからないんですが、計測値の種類?を一意に識別する番号のようです。SDK見てねと書いてあったので、見たら理解が深まるかもしれません。
次のSizeはそのままですね。最後のBase Typeってやつもよくわからんですが、データの型を定義するフィールドのようです(unsigned charとかsigned shortとか)。
見方は以下表のとおりらしいんですが、正直よくわかりません。

bit Name Description
7 Endian Ability 0 - for single byte data
1 - if base type has endianness
(i.e. base type is 2 or more bytes)
5-6 Reserved Reserved
0-4 Base Type Number Number assigned to Base Type (provided in SDK)

公式ドキュメントでは、(SDKの)fit.hに書いてあるよって書かれてるので、fit.hも見てみます。
image.png
あー。FIT_BASE_TYPE_XXXで定義されてるのがこれですね。何となく理解。

じゃあ実際のデータの方を見てみます。
Field Definitionの1byte目、Field Definition Numberは3。
image.png

Field Definitionの2byte目、Sizeは4。
image.png

Field Definitionの3byte目は0x8Cです。
image.png
これをfit.h上の定義で探すと、
image.png
FIT_BASE_TYPE_UINT32Zってやつらしいですね。

こんな感じで全7個のフィールドを紐解いてみたところ、以下のようになりました。

# Field Definition Number Size BaseType
0 3 (SN) 4 0x8C (FIT_BASE_TYPE_UINT32)
1 4 (timeらしい) 4 0x86 (FIT_BASE_TYPE_UINT32)
2 7 4 0x86 (FIT_BASE_TYPE_UINT32)
3 1 (mfg) 2 0x84 (FIT_BASE_TYPE_UINT16)
4 2 (prod) 2 0x84 (FIT_BASE_TYPE_UINT16)
5 5 2 0x84 (FIT_BASE_TYPE_UINT16)
6 0 (type) 1 0x00 (FIT_BASE_TYPE_ENUM)

やはりField Definition Numberが謎すぎますね・・・。これについてはまだ正直理解が追い付いてません。すいません。

Developer Fields & Developer Field Definition

さて、Definition Messageの最後、# Developer FieldsとDeveloper Field Definitionですが、これはDeveloper Data Flagがセットされているときのみ有効とのことです。Developer Data Flagとは、Record Headerにおける5bit目を指すようです。今見ているデータはフラグ0でしたので今は無視します。

ここまでがDefinition Messageに含まれる情報です。

Data Message

ようやくデータそのものにたどり着きました。Data Messageは別のレコードとして来るので、新たに6bit目が0のレコードヘッダがあるはずです。
image.png
OK。想定通りですね。
ここからはField Definitionで定義されたFieldの数、サイズ別にデータが記録されているはずです。
試しに一つ目のデータを見てみます。データサイズは4byte, UINT32ですね。
serial.png
※シリアル番号らしいので、一応消しておきます。
他のFieldも見てみた結果が以下です。

# Field Definition Number Size BaseType Value
0 3 (SerianNumber) 4 0x8C (FIT_BASE_TYPE_UINT32) 3347XXXXXX
1 4 (time?) 4 0x86 (FIT_BASE_TYPE_UINT32) 976363898
2 7 (?) 4 0x86 (FIT_BASE_TYPE_UINT32) 4294967295
3 1 (manufacturer) 2 0x84 (FIT_BASE_TYPE_UINT16) 1
4 2 (product) 2 0x84 (FIT_BASE_TYPE_UINT16) 3779
5 5 (?) 2 0x84 (FIT_BASE_TYPE_UINT16) 65535
6 0 (type) 1 0x00 (FIT_BASE_TYPE_ENUM) 4

ちなみに、製品に同梱されていたシリアルナンバーは上記のシリアルナンバーとは違うものでしたが、
Garmin Connectで製品情報を見た際のURLがこのシリアルナンバーと同じでした。
内部的な管理のための番号のようですね。

https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_221091_14e42ccf-6512-de68-b3f6-b6c37298be79.png

まだ理解が追い付いてないところもありますが、通しでデータを見てみてFitデータについて少し理解深めることができた気がします。
次は、SDKを利用して実際にFitデータを読み込み、解析するようなことをしてみたいですね。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2
Help us understand the problem. What are the problem?