iOS8の高度計(気圧計)

iOS8の高度計(気圧計)

iPhone6 Plus および iPhone6には、気圧計が入っています。

今どき気圧計は高精度で、気圧の値の変化から、1メートル程度の高さの変化が十分にわかります。これがあると、例えば、ユーザが今ビルの何階にいるかがわかるわけです。

サンプルコードはこんな感じ。

1秒に1回の頻度で、気圧値が更新されて通知されてきます。相対高度(relativeAltitude)は、前回の通知から今回の通知までの高度の変化分を通知しています。今の実装は、おそらく、単純に気圧値の差分に定数をかけるとか、そんな感じなのかもしれません。

更新頻度を指定する方法は、今のところないみたい。

ハードウェから見ると、iPhone6の分解 http://www.techinsights.com/teardown.com/apple-iphone-6/ から、センサは Bosch Sensortec BMP280 Barometric Sensor

仕様を見ると、動作範囲(full accuracy) 300 ~ 1100 hPa、平均測定時間 5.5ミリ秒、分解能0.01 hPa (< 10cm)、温度 0.1度、絶対精度(950 ~ 1050 hPa) +- 1hPa、相対精度 +ー0.12 hPa (高さ+ー1m相当)。 (1hPa(ヘクトパスカル) は 100パスカル。)

センサの仕様から、今の振る舞いは、ほぼセンサの値をそのまま利用する単純な実装をしているのかな、と思いました。

/post/2014-10-03-ios8-barometer/20141003_1.jpg

Githubのサンプルアプリ。 https://github.com/reinforce-lab/ObjC_Codes/tree/master/barometer

ここの気圧と高度の計算式で、標高ゼロメートル、温度27度(絶対温度で300K)のときに、1mあたりの気圧の変化は、11パスカル。

http://keisan.casio.jp/has10/SpecExec.cgi?path=05000000.%95%A8%97%9D%8C%F6%8E%AE%8FW%2F02100100.%92n%8Aw%2F12000200.%95W%8D%82%82%A9%82%E7%8BC%88%B3%82%F0%8Cv%8EZ%2Fdefault.xml

/post/2014-10-03-ios8-barometer/20141003_2.jpg

アプリの値の変化を見ていると、たしかにそれっぽい感じで、とれている。

ソースコード

@interface ViewController () {
    CMAltimeter *_altimater;
    bool _isFirstSample;
    double _currentValue;
    double _averageValue;
    double _altitudValue;
}

@property (weak, nonatomic) IBOutlet UILabel *currentValueTextLabel;
@property (weak, nonatomic) IBOutlet UILabel *averageValueTextLabel;
@property (weak, nonatomic) IBOutlet UILabel *variationValueTextLabel;
@property (weak, nonatomic) IBOutlet UILabel *warningTextLabel;
@property (weak, nonatomic) IBOutlet UILabel *altitudeValueTextLabel;

@end


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.
    _isFirstSample = YES;
    
    self.warningTextLabel.hidden = [CMAltimeter isRelativeAltitudeAvailable];
    if([CMAltimeter isRelativeAltitudeAvailable]) {
        _altimater = [[CMAltimeter alloc] init];
        [_altimater startRelativeAltitudeUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAltitudeData *data, NSError *error) {
            [self altitudeDataHandlr:data error:error];
        }];
    }
}

-(void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    [_altimater stopRelativeAltitudeUpdates];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)altitudeDataHandlr:(CMAltitudeData *)data error:(NSError *)error {
    if(error != nil) return;
    
    double altitude = [[data relativeAltitude] doubleValue];
    double pressure = [[data pressure] doubleValue];

    _currentValue = pressure;
    _altitudValue = altitude;

    if(_isFirstSample) {
        _averageValue = pressure;
    } else {
        _averageValue = 0.1 * _currentValue + (1 - 0.1) * _averageValue;
    }
    _isFirstSample = NO;
    
    self.currentValueTextLabel.text  = [NSString stringWithFormat:@"%1.5e", _currentValue];
    self.averageValueTextLabel.text  = [NSString stringWithFormat:@"%1.5e", _averageValue];
    self.altitudeValueTextLabel.text = [NSString stringWithFormat:@"%1.3e", _altitudValue];
    
}