2015年1月19日月曜日

Fr24にフィード

しばらく使う予定のないRasPiB+をFr24フィード用に固定設置してみました
この辺りは旭川空港や新千歳空港の付近の受信局でカバーされているので、Fr24に反映されることはなさそうですが、Fr24にはそれ以外にもメリットが有ります

例えば受信したデータを送ると、どの方向からどれくらい受信しているか どれくらいの距離から受信しているか という統計データを表示することができます

西に強烈に指向性が出ているのがわかりますが、これは設置位置の関係と一致します
受信距離はおよそ100kmまでくらいでしょうか

また、Fr24にフィードしている間はFr24のプレミアムアカウントとして判断されます
そのため、アカウント無しの場合に発生する一定時間ごとにリロードする必要がありません
他にも画面に表示するデータを必要に応じて設定することが可能です


使用しているアンテナは1/2λ17段+1/4λ1段のコリニアですが、窓の内側に設置しています
そのために西しか見えません
またPC等に近いのでSNRも相当に悪いと思われます
暖かくなったらベランダに常設できるようにする予定ですが、どれくらい改善するか楽しみです

2015年1月15日木曜日

GCCのコンパイラ警告に依存したコードの書き方について

ちょっと気になることが有ったのでまとめ
(諸事情で回線速度が80kbpsくらいしか出てないのでネットの情報を漁ったりはしてません 詳しく書いてるサイトがあったら教えてください)

1. 普通に書く

まずこういうコードを用意します

#include <stdio.h>

typedef enum { hoge1_1, hoge1_2, hoge1_3, hoge1_4 } hoge1_t;
typedef enum { hoge2_1, hoge2_2, hoge2_3, hoge2_4 } hoge2_t;

typedef struct {
    hoge1_t hoge1;
    hoge2_t hoge2;
} type_t;

int main(void) {

    type_t type;
    type.hoge1 = hoge1_1;
    type.hoge2 = hoge2_2;

    printf("type size:%d\n", (int)(sizeof(type_t)));

    printf("type.hoge1: ");
    switch (type.hoge1) {
    case hoge1_1: printf("hoge1\n"); break;
    case hoge1_2: printf("hoge2\n"); break;
    case hoge1_3: printf("hoge3\n"); break;
    case hoge1_4: printf("hoge4\n"); break;
    }
    
    printf("type.hoge2: ");
    switch (type.hoge2) {
    case hoge2_1: printf("hoge1\n"); break;
    case hoge2_2: printf("hoge2\n"); break;
    case hoge2_3: printf("hoge3\n"); break;
    case hoge2_4: printf("hoge4\n"); break;
    }

    return(0);
}

見るからにCのコードですが、g++でビルドします
$ g++ test.c -Wall
という感じで
もちろん警告は出ません
実行すると
$ ./a.exe
type size:8
type.hoge1: hoge1
type.hoge2: hoge2
という感じになります

2. ちょっと端折った書き方をしてみる

列挙型の一部は使用していません switchで使用していない分岐を持つのはコード効率が悪いのでコメントアウトしてみましょう

#include <stdio.h>

typedef enum { hoge1_1, hoge1_2, hoge1_3, hoge1_4 } hoge1_t;
typedef enum { hoge2_1, hoge2_2, hoge2_3, hoge2_4 } hoge2_t;

typedef struct {
    hoge1_t hoge1;
    hoge2_t hoge2;
} type_t;

int main(void) {

    type_t type;
    type.hoge1 = hoge1_1;
    type.hoge2 = hoge2_2;

    printf("type size:%d\n", (int)(sizeof(type_t)));

    printf("type.hoge1: ");
    switch (type.hoge1) {
    case hoge1_1: printf("hoge1\n"); break;
    case hoge1_2: printf("hoge2\n"); break;
    case hoge1_3: printf("hoge3\n"); break;
    //case hoge1_4: printf("hoge4\n"); break;
    }
    
    printf("type.hoge2: ");
    switch (type.hoge2) {
    case hoge2_1: printf("hoge1\n"); break;
    case hoge2_2: printf("hoge2\n"); break;
    case hoge2_3: printf("hoge3\n"); break;
    //case hoge2_4: printf("hoge4\n"); break;
    }

    return(0);
}

これを1.と同じようにビルドしてみましょう
$ g++ main.c -Wall
main.c: 関数 ‘int main()’ 内:
main.c:20:9: 警告: 列挙値 ‘hoge1_4’ は switch 内で取り扱われません [-Wswitch]
  switch (type.hoge1) {
         ^
main.c:28:9: 警告: 列挙値 ‘hoge2_4’ は switch 内で取り扱われません [-Wswitch]
  switch (type.hoge2) {
         ^
警告がでてしまいました
これは便利ですね 列挙型の書き忘れを防ぐことができます

3. 消費メモリをカリカリ削る

しかし実行してみるとtype_t型は8バイトのメモリを使用しています
それぞれの列挙型は4種類しか値を持たないのでそれぞれ2bitあれば事足りるはずです
ということでビットフィールドで2bitを指定しましょう

#include <stdio.h>

typedef enum { hoge1_1, hoge1_2, hoge1_3, hoge1_4 } hoge1_t;
typedef enum { hoge2_1, hoge2_2, hoge2_3, hoge2_4 } hoge2_t;

typedef struct {
    hoge1_t hoge1 : 2;
    hoge2_t hoge2 : 2;
} type_t;

int main(void) {

    type_t type;
    type.hoge1 = hoge1_1;
    type.hoge2 = hoge2_2;

    printf("type size:%d\n", (int)(sizeof(type_t)));

    printf("type.hoge1: ");
    switch (type.hoge1) {
    case hoge1_1: printf("hoge1\n"); break;
    case hoge1_2: printf("hoge2\n"); break;
    case hoge1_3: printf("hoge3\n"); break;
    case hoge1_4: printf("hoge4\n"); break;
    }
    
    printf("type.hoge2: ");
    switch (type.hoge2) {
    case hoge2_1: printf("hoge1\n"); break;
    case hoge2_2: printf("hoge2\n"); break;
    case hoge2_3: printf("hoge3\n"); break;
    case hoge2_4: printf("hoge4\n"); break;
    }

    return(0);
}

同じようにビルドしてみましょう もちろん警告はでません
では実行してみましょう

$ ./a.exe
type size:4
type.hoge1: hoge1
type.hoge2: hoge2

消費メモリは4バイトに減っています
メモリ効率が倍になりました めでたしめでたし

4. 消費メモリをカリカリ削る + 端折った書き方

では、使っていない列挙のcaseをコメントアウトしてみましょう

#include <stdio.h>

typedef enum { hoge1_1, hoge1_2, hoge1_3, hoge1_4 } hoge1_t;
typedef enum { hoge2_1, hoge2_2, hoge2_3, hoge2_4 } hoge2_t;

typedef struct {
    hoge1_t hoge1 : 2;
    hoge2_t hoge2 : 2;
} type_t;

int main(void) {

    type_t type;
    type.hoge1 = hoge1_1;
    type.hoge2 = hoge2_2;

    printf("type size:%d\n", (int)(sizeof(type_t)));

    printf("type.hoge1: ");
    switch (type.hoge1) {
    case hoge1_1: printf("hoge1\n"); break;
    case hoge1_2: printf("hoge2\n"); break;
    case hoge1_3: printf("hoge3\n"); break;
    //case hoge1_4: printf("hoge4\n"); break;
    }
    
    printf("type.hoge2: ");
    switch (type.hoge2) {
    case hoge2_1: printf("hoge1\n"); break;
    case hoge2_2: printf("hoge2\n"); break;
    case hoge2_3: printf("hoge3\n"); break;
    //case hoge2_4: printf("hoge4\n"); break;
    }

    return(0);
}

このようになります
ビルドしてみると
$ g++ main.c -Wall
警告がでません
これはこまった…

メモリ効率を優先すると未使用のcaseに対する警告がでなくなってしまいました

組み込みの場合は消費メモリは減らしたいし、case分岐忘れは致命的だし という場合がありますが、どうしたらいいのでしょう…

というのが今日の本題
本題というかもうこれしかネタが無いのですが、こういう時はどうすればいいんでしょうかねぇ…


ちなみに


#include <stdio.h>
#include <stdint.h>

typedef enum { hoge1_1, hoge1_2, hoge1_3, hoge1_4 } hoge1_t;
typedef enum { hoge2_1, hoge2_2, hoge2_3, hoge2_4 } hoge2_t;

typedef struct {
    uint8_t hoge1 : 2;
    uint8_t hoge2 : 2;
} type_t;

int main(void) {

    type_t type;
    type.hoge1 = hoge1_1;
    type.hoge2 = hoge2_2;

    printf("type size:%d\n", (int)(sizeof(type_t)));

    printf("type.hoge1: ");
    switch (type.hoge1) {
    case hoge1_1: printf("hoge1\n"); break;
    case hoge1_2: printf("hoge2\n"); break;
    case hoge1_3: printf("hoge3\n"); break;
    case hoge1_4: printf("hoge4\n"); break;
    }
    
    printf("type.hoge2: ");
    switch (type.hoge2) {
    case hoge2_1: printf("hoge1\n"); break;
    case hoge2_2: printf("hoge2\n"); break;
    case hoge2_3: printf("hoge3\n"); break;
    case hoge2_4: printf("hoge4\n"); break;
    }

    return(0);
}

というコードにすると、実行結果は
$ ./a.exe
type size:1
type.hoge1: hoge1
type.hoge2: hoge2
となります
メモリ消費量が1バイトになりました(もちろんビットフィールドを使っているのでcase忘れ警告はでませんが)

列挙型では4バイトを使ってしまうのは、暗黙的にint(4バイト)を使用し、そのビットを区切って使っているからだと思われます


メモリがカツカツな環境で構造体を使いたい場合はどうすればいいんですかねぇ
enumは#defineと同じような感覚で使って、uint8_tのビットフィールドで使用メモリを減らす case忘れはコーダーが気をつける という感じになるんでしょうか

そもそもコンフィグレジスタの値をtypedefで持とうとしてる時点でメモリ消費量なんて… という感じではありますが

僕の知る限りどのルートを通ってもあっちを立てればこっちが立たずです
なにかいい方法があったら教えてください

2015年1月14日水曜日

コリニアアンテナを作りなおしてみた

ちょっと前に6段のコリニアアンテナを作ったと書きましたが、また作ってみました
今回は1/2λ17段+1/4λ1段です
ホームセンターで買ってきたS-4Cを使いました
そしてそれを塩ビパイプの中に入れています

一応ベランダに設置できるように作ったつもりですが、この時期は寒いので、暖かくなったら外に出そうと思っています
が、ちょっと作ってる時に細々とミスをしているので、もしかしたら暖かくなるまでにもう1本作るかもしれませんが

性能はこんな感じです

層雲峡の方で1回取れていますが、これは本当に不思議です なんでこんなところで取れるのか
場所的には家を貫通する方向なので、相当に運が良かったのか

その次は上富良野の上空あたりまで取れませんでした そして夕張の上空が最後の受信です
以前の6段コリニアのほうがはるかに性能がいい気がします
と言っても17段のほうはサンプル数が1なのでまだわかりませんが

受信性能が悪い原因は10mの同軸ケーブルをぐしゃっと丸めているからかもしれません
この周波数は約1GHzで、同軸ケーブルでの損失は5dBくらいになるみたいです
入力したエネルギーの30%くらいしか出てきません 7割を失うとは相当にデカい損失です
外にアンテナを建てる場合でも、ケーブルは1m程度にして、アンテナ直下にRasPiを設置することも考えたほうがいいかもしれません
ただRasPiなどの放射ノイズが悪影響を与えるという意見もあるようなので、そのあたりは何らかの対策を考える必要がありそうです
タカチの防水シールドケースとかあればそれを使うのが簡単かもしれないですね

とりあえず暖かくなるまでは部屋の中においた状態で様子見ですね


とりあえずあまりに長いコリニアアンテナはちょっとつらいので、次は特小帯の3段とか作ってみようかな と思っています それでも1m位になりますが…

2015年1月8日木曜日

C#でbyte列をfloatに変換する

C#ではBitConvertを使用することによりintやfloat等をbyte列に変換することができます
同じようにバイト列をintに変換することも可能です

が、なぜかバイト列をfloatに変換することは不可能です 実に不思議です

とりあえず必要なら何らかの方法で実装する必要があるので、とりあえず試しに実装してみました

using System;

class Program {
    unsafe static float byte2float(byte[] data) {
        if (data.Length < 4) { return (float.NaN); }
        float f;
        ((byte*)&f)[0] = data[0];
        ((byte*)&f)[1] = data[1];
        ((byte*)&f)[2] = data[2];
        ((byte*)&f)[3] = data[3];
        return (f);
    }

    static void Main(string[] args) {
        Console.WriteLine(byte2float(BitConverter.GetBytes((float)(12.34))));
        Console.ReadLine();
    }
}

この方法の問題点はunsafeを使用することです
つまり「危険な処理」を実行する必要があります
本来はやるべきではないですが、関数名でunsafeを宣言しないと危険な処理は許可されないので、可能な限り局所化することで妥協しています
内部でどういう処理がされているかはわかりませんが、ソースコードを見る限りでは最速の方法だと思います

このbyte2float関数では入力されたバイト列が4未満の場合はNaNを返しています
入力された値が本当にNaNなのか、入力自体が不正なのかを判定することはできません

本当のNaNはバイト列が固定なので、NaNが帰った場合はifで比較する というのもアリですが、おそらくTryBytes2Float関数を作って戻り値をbool、引数をbyte[]とout floatにする という感じで実装するほうがいいでしょう


以上、C#ならポインタも扱える!自由度高い!!という話でした
なんでポインタ使う必要があるって、標準で用意されてる機能では足りないからなんですけども…