Home | History | Annotate | Download | only in WALT
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #import "TapLatencyController.h"
     18 
     19 #import "NSArray+Extensions.h"
     20 #import "UIAlertView+Extensions.h"
     21 #import "WALTAppDelegate.h"
     22 #import "WALTClient.h"
     23 #import "WALTLogger.h"
     24 #import "WALTTouch.h"
     25 
     26 @interface TapLatencyController ()
     27 - (void)updateCountDisplay;
     28 - (void)processEvent:(UIEvent *)event;
     29 - (void)appendToLogView:(NSString *)string;
     30 - (void)computeStatisticsForPhase:(UITouchPhase)phase;
     31 @end
     32 
     33 @implementation TapLatencyController {
     34   WALTClient *_client;
     35   WALTLogger *_logger;
     36 
     37   // Statistics
     38   unsigned int _downCount;
     39   unsigned int _downCountRecorded;
     40   unsigned int _upCount;
     41   unsigned int _upCountRecorded;
     42 
     43   NSMutableArray<WALTTouch *> *_touches;
     44 }
     45 
     46 - (void)viewDidLoad {
     47   [super viewDidLoad];
     48 
     49   self.logView.selectable = YES;
     50   self.logView.text = [NSString string];
     51   self.logView.selectable = NO;
     52 
     53   _logger = [WALTLogger sessionLogger];
     54   _client = ((WALTAppDelegate *)[UIApplication sharedApplication].delegate).client;
     55 }
     56 
     57 - (void)viewWillAppear:(BOOL)animated {
     58   [super viewWillAppear:animated];
     59 
     60   [_logger appendString:@"TAPLATENCY\n"];
     61   [self reset:nil];
     62 }
     63 
     64 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
     65   [self processEvent:event];
     66   [self updateCountDisplay];
     67 }
     68 
     69 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
     70   [self processEvent:event];
     71   [self updateCountDisplay];
     72 }
     73 
     74 - (void)updateCountDisplay {
     75   NSString *counts = [NSString stringWithFormat:@"N %u (%u) %u (%u)",
     76                       _downCountRecorded, _downCount, _upCountRecorded, _upCount];
     77   self.countLabel.text = counts;
     78 }
     79 
     80 - (void)processEvent:(UIEvent *)event {
     81   // TODO(pquinn): Pick first/last coalesced touch?
     82 
     83   NSTimeInterval kernelTime = event.timestamp;
     84   NSTimeInterval callbackTime = _client.currentTime;
     85 
     86   NSError *error = nil;
     87   NSTimeInterval physicalTime = [_client lastShockTimeWithError:&error];
     88   if (physicalTime == -1) {
     89     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Connection Error" error:error];
     90     [alert show];
     91     return;
     92   }
     93 
     94   WALTTouch *touch = [[WALTTouch alloc] initWithEvent:event];
     95   touch.callbackTime = callbackTime;
     96   touch.physicalTime = physicalTime;
     97 
     98   NSString *actionString = nil;
     99   if (touch.phase == UITouchPhaseBegan) {
    100     _downCount += 1;
    101     actionString = @"ACTION_DOWN";
    102   } else {
    103     _upCount += 1;
    104     actionString = @"ACTION_UP";
    105   }
    106 
    107   if (physicalTime == 0) {
    108     [_logger appendFormat:@"%@\tX\tno shock\n", actionString];
    109     [self appendToLogView:[NSString stringWithFormat:@"%@: No shock detected\n", actionString]];
    110     return;
    111   }
    112 
    113   NSTimeInterval physicalToKernel = kernelTime - physicalTime;
    114   NSTimeInterval kernelToCallback = callbackTime - kernelTime;
    115 
    116   if (physicalToKernel < 0 || physicalToKernel > 0.2) {
    117     [_logger appendFormat:@"%@\tX\tbogus kernelTime\t%f\n", actionString, physicalToKernel];
    118     [self appendToLogView:
    119         [NSString stringWithFormat:@"%@: Bogus P  K: %.3f s\n", actionString, physicalToKernel]];
    120     return;
    121   }
    122 
    123   [_logger appendFormat:@"%@\tO\t%f\t%f\t%f\n",
    124       actionString, _client.baseTime, physicalToKernel, kernelToCallback];
    125 
    126   [self appendToLogView:
    127       [NSString stringWithFormat:@"%@: P  K: %.3f s; K  C: %.3f s\n",
    128         actionString, physicalToKernel, kernelToCallback]];
    129 
    130   [_touches addObject:touch];
    131   if (touch.phase == UITouchPhaseBegan) {
    132     _downCountRecorded += 1;
    133   } else {
    134     _upCountRecorded += 1;
    135   }
    136 }
    137 
    138 - (IBAction)reset:(id)sender {
    139   _downCount = 0;
    140   _downCountRecorded = 0;
    141   _upCount = 0;
    142   _upCountRecorded = 0;
    143   [self updateCountDisplay];
    144 
    145   _touches = [[NSMutableArray<WALTTouch *> alloc] init];
    146 
    147   NSError *error = nil;
    148   if (![_client syncClocksWithError:&error]) {
    149     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Connection Error" error:error];
    150     [alert show];
    151   }
    152 
    153   [_logger appendString:@"RESET\n"];
    154   [self appendToLogView:@"===========================================\n"];
    155 }
    156 
    157 - (IBAction)computeStatistics:(id)sender {
    158   [self appendToLogView:@"-------------------------------------------\n"];
    159   [self appendToLogView:@"Medians:\n"];
    160   [self computeStatisticsForPhase:UITouchPhaseBegan];
    161   [self computeStatisticsForPhase:UITouchPhaseEnded];
    162 
    163   [self reset:sender];
    164 }
    165 
    166 - (void)computeStatisticsForPhase:(UITouchPhase)phase {
    167   NSMutableArray<NSNumber *> *p2k = [[NSMutableArray<NSNumber *> alloc] init];
    168   NSMutableArray<NSNumber *> *k2c = [[NSMutableArray<NSNumber *> alloc] init];
    169 
    170   for (WALTTouch *touch in _touches) {
    171     if (touch.phase != phase) {
    172       continue;
    173     }
    174 
    175     [p2k addObject:[NSNumber numberWithDouble:touch.kernelTime - touch.physicalTime]];
    176     [k2c addObject:[NSNumber numberWithDouble:touch.callbackTime - touch.kernelTime]];
    177   }
    178 
    179   NSNumber *p2kMedian = [p2k medianValue];
    180   NSNumber *k2cMedian = [k2c medianValue];
    181 
    182   NSString *actionString = (phase == UITouchPhaseBegan ? @"ACTION_DOWN" : @"ACTION_UP");
    183   [self appendToLogView:
    184       [NSString stringWithFormat:@"%@: P  K: %.3f s; K  C: %.3f s\n",
    185         actionString, p2kMedian.doubleValue, k2cMedian.doubleValue]];
    186 }
    187 
    188 - (void)appendToLogView:(NSString*)string {
    189   self.logView.selectable = YES;
    190   self.logView.text = [self.logView.text stringByAppendingString:string];
    191   [self.logView scrollRangeToVisible:NSMakeRange(self.logView.text.length - 2, 1)];
    192   self.logView.selectable = NO;
    193 }
    194 @end
    195