一汁三菜

自分が楽しいと思うこと、マラソン、旅行、その他日々の記録をしたい。

信号強度の取得

前置き

RSSI (受信信号強度)で検索されて来ている方がいらっしゃったので、信号強度を取得する方法をまずは書いてみたいと思います。

実はそれぞれのNICの信号強度は、プログラムから特別なAPIを呼ぶまでもなく取得できてしまったりします。/proc/net/wirelessの中を見てみると、

$ cat /proc/net/wireless
Inter-| sta-|   Quality        |   Discarded packets               | Missed | WE
 face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon | 22
 wlan0: 0000  100.  -32   -88.       0      0      0      0      0        0

というような出力が得られますが、この"Quality"欄が信号強度を表しています。後はこれを頑張って文字列解析すれば、お望みの値を得られますね。

ここに出てきた数値はもちろんiwconfigコマンドでも得ることが出来て、

wlan0     IEEE 802.11abgn  ESSID:"hogefuga"  
          Mode:Managed  Frequency:2.462 GHz  Access Point: 00:11:22:33:44:55   
          Bit Rate=54 Mb/s   Tx-Power=15 dBm   
          Retry min limit:7   RTS thr:off   Fragment thr=2352 B   
          Power Management:off
          Link Quality=100/100  Signal level:-32 dBm  Noise level=-88 dBm
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:0   Missed beacon:0

ここの"Link Quality", "Signal level", "Noise level"が、/proc/net/wirelessのQuality欄のそれぞれに対応します。

さてさて、こういう所から取得してきたデータを頑張って文字列解析してみてもいいのですが、プログラムからこういうデータを扱う時はAPIを使った方がお手軽かつ速く動きます。なので、API経由で信号強度を取得してみましょう。

コード

前置きが長かったですが、ようやく実際のソースコードです。今回はioctlを使います。

#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/wireless.h>
#include <stdio.h>

int main(void)
{
  struct iwreq iwr;
  struct iw_statistics stat;

  int sk = socket(AF_INET, SOCK_DGRAM, 0);

  memset(&iwr, 0, sizeof(iwr));
  memset(&stat, 0, sizeof(stat));
  strncpy(iwr.ifr_name, "wlan0", IFNAMSIZ);
  iwr.u.data.pointer = &stat;
  iwr.u.data.length = sizeof(stat);

  ioctl(sk, SIOCGIWSTATS, &iwr);
  close(sk);

  printf("link quality: %d\n", stat.qual.qual);
  if(stat.qual.updated & IW_QUAL_DBM) {
    printf("link level %d dBm\n", (char)stat.qual.level);
    printf("noise %d dBm\n", (char)stat.qual.noise);
  } else {
    printf("link level %d\n", (char)stat.qual.level);
    printf("noise %d\n", (char)stat.qual.noise);
  }

  return 0;
}

iwlibは使っていないので、コンパイルは単純に、

$ gcc sig.c

でいけます。

説明

SIOCGIWSTATSを指定してioctlを呼び出すと、その時のNICの状態が見られます。iwreq構造体のifr_nameメンバには、状態を見たいNIC名を指定します。iwreq構造体はサイズに制限があって、そのままでは状態情報を全て格納出来ないので、状態情報を格納する為の構造体iw_statisticsの変数を別に定義して、そのポインタを渡します。u.data.pointerがその為の変数です。

参考にしたWebページ