How to decode TOB2 formatted file for Campbell Scientific DataLogger CR-5000
on PC linux with gcc, by Hiroki TANAKA 25SEP2003

1st STEP: read ASCII Header
とりあえず、6行あるので、全部読み飛ばす。
ASCIIデータなので、読めば何が書いてあるかは、それなりに判る(と思う)

2nd STEP: read Frame Header
フレームのヘッダは、8バイト(long intが2つ)でタイムスタンプになっている。
LSBらしい。まあ、良くわかんないけど、次のようなコードで無事に正しい数値になった。
unsigned long get_4byte_int() {
        unsigned char c1, c2, c3, c4;
        unsigned long r = 0;

        c1 = fgetc(stdin);
        c2 = fgetc(stdin);
        c3 = fgetc(stdin);
        c4 = fgetc(stdin);

        r = (((c4 * 256L) + c3) * 256L + c2) * 256L + c1;
        return r;
}
で、はじめの4バイトが1990年1月1日からの秒数
long y_sec[40];
set_second_for_year() {
        y_sec[1990 - 1990] = 0L;
        y_sec[1991 - 1990] = 31536000L;
        y_sec[1992 - 1990] = 63072000L;
        y_sec[1993 - 1990] = 94694400L;
        y_sec[1994 - 1990] = 126230400L;
        y_sec[1995 - 1990] = 157766400L;
        y_sec[1996 - 1990] = 189302400L;
        y_sec[1997 - 1990] = 220924800L;
        y_sec[1998 - 1990] = 252460800L;
        y_sec[1999 - 1990] = 283996800L;
        y_sec[2000 - 1990] = 315532800L;
        y_sec[2001 - 1990] = 347155200L;
        y_sec[2002 - 1990] = 378691200L;
        y_sec[2003 - 1990] = 410227200L;
        y_sec[2004 - 1990] = 441763200L;
        y_sec[2005 - 1990] = 473385600L;
        y_sec[2006 - 1990] = 504921600L;
        y_sec[2007 - 1990] = 536457600L;
        y_sec[2008 - 1990] = 567993600L;
        y_sec[2009 - 1990] = 599616000L;
        y_sec[2010 - 1990] = 631152000L;
        y_sec[2011 - 1990] = 662688000L;
        y_sec[2012 - 1990] = 694224000L;
        y_sec[2013 - 1990] = 725846400L;
        y_sec[2014 - 1990] = 757382400L;
        y_sec[2015 - 1990] = 788918400L;
        y_sec[2016 - 1990] = 820454400L;
        y_sec[2017 - 1990] = 852976800L;
        y_sec[2018 - 1990] = 883612800L;
        y_sec[2019 - 1990] = 915148800L;
        y_sec[2020 - 1990] = 946684800L;
}
なんてので、とりあえず必要な辺りの秒数を押さえておく。
次の4バイトは、μsecなので、1000,000で割って足す。

3rd STEP: read Frame Data
データの形式は、ASCII Headerに書いてある。今回は、IEEE4LというのとFS2というのがあった。

(1) FS2のデコード
良くわかんないので、とりあえず2バイト読み込んで、
ビット毎に無理矢理分解(←ビット演算がわかってないので無理矢理)
で、前方から順々に決まったフォーマットで浮動小数点に直してあげる
double get_FP2() {
        int i, j;
        double r;
        unsigned char S, b[16], c, a;
        unsigned int  E;
        unsigned long M;

        for(i = 0; i < 16; i += 8) {
                c = fgetc(stdin);
                for(j = 0, a = 0x80; j < 8; j++, a *= 0.5) {
                        b[i + j] = c / a;
                        c -= b[i + j] * a;
                }
        }

        S = b[0];
        E = (int)b[1] * 2 + (int)b[2];
        M = (long)b[3];
        for(i = 4; i < 16; i++) {
                M *= (long)2L;
                M += (long)b[i];
        }
        r = (double)M;
        for(i = 0; i < E; i++) r *= 0.1;
        if(S == 1) r *= -1.0;

        return r;
}

(2) IEEE4Lのデコード
こちらも良くわかんないので、とりあえず4バイト読み込んで、
ビット毎に無理矢理分解(←悲しいかな、私がやるとああなるのです)
で、FP2の時と同じように前方から順々....と思ったら、うまくいかない。
後で読んだ方から...ということみたいです。
まあ、とにかくIEEE754のフォーマットで浮動小数点に直してあげる
double get_IEEE() {
        int i, j;
        double r, M;
        unsigned char S, b[32], c, a, cc[4];
        unsigned int  E;

        for(i = 24; i >= 0; i -= 8) {
                if(i >= 32) i -= 32;
                if(i < 0) i += 32;
                c = fgetc(stdin);
                cc[i / 8] = c;
                for(j = 0, a = 0x80; j < 8; j++, a *= 0.5) {
                        b[i + j] = c / a;
                        c -= b[i + j] * a;
                }
        }

        S = b[0];
        E = (int)b[1];
        for(i = 2; i <= 8; i++) {
                E *= 2;
                E += (int)b[i];
        }
        for(i = 31, M = 0.0; i >= 9; i--) {
                M += (double)b[i];
                M *= 0.5;
        }
        if(E == 0) {
                r = M;
                for(i = 0; i < 126; i++) r *= 0.5;
        } else if(E > 127) {
                r = 1.0 + M;
                for(i = 0; i < E - 127; i++) r *= 2.0;
        } else {
                r = 1.0 + M;
                for(i = 0; i < 127 - E; i++) r *= 0.5;
        }
        if(S == 1) r *= -1.0;

        return r;
}

4th STEP: read Frame Footer
良くわかんないけど、4バイト読み飛ばす。何かしら意味はあるはずだが。。。

To be continue to next Frame ...