source: orbit/iOS/Orbit/Orbit/ViewController.m @ eac712d

RawEEGRaw_EEG_PlotServoTab_Interfacepyramid
Last change on this file since eac712d was eac712d, checked in by Jonathon Horsman <jonathon@…>, 7 years ago

Added instructions

  • Property mode set to 100644
File size: 9.0 KB
Line 
1//
2//  ViewController.m
3//  Orbit
4//
5//  Created by Jonathon Horsman on 17/12/2012.
6//  Copyright (c) 2012 Jonathon Horsman. All rights reserved.
7//
8
9#import "ViewController.h"
10#import <AVFoundation/AVFoundation.h>
11
12#define LOGGING true // set to false to stop each received signal being logged
13
14#define AUDIO_FILE_NAME @"throttle_hover_ios.wav" // @"iOS_noflip.wav" //
15#define POOR_SIGNAL_KEY @"poorSignal"
16#define ATTENTION_KEY @"eSenseAttention"
17#define MEDITATION_KEY @"eSenseMeditation"
18
19@implementation ViewController {
20    // these 2 arrays hold the values of the thrust which should be applied to the helicopter
21    // (by way of volume level through the headphones) at each level of attention and meditation.
22    // this could be calculated on the fly, but because it happens ~20 times per second, the
23    // better option is to store all the values in arrays, and recalculate when the sliders are changed.
24    float attentionPower[101];
25    float meditationPower[101];
26   
27    int currentAttentionLevel, currentMeditationLevel; // the latest readings from the headset
28    BOOL demoRunning;
29}
30
31@synthesize log, attention, meditation, signal, power, attentionThreshold, meditationThreshold, demoButton;
32
33- (void)viewDidLoad
34{
35    [super viewDidLoad];
36    [self prepareAudio];
37    [self.attentionThreshold addTarget:self action:@selector(calculatePowerValues) forControlEvents:UIControlEventValueChanged];
38    [self.meditationThreshold addTarget:self action:@selector(calculatePowerValues) forControlEvents:UIControlEventValueChanged];
39    [self calculatePowerValues];
40}
41
42- (void)didReceiveMemoryWarning
43{
44    [super didReceiveMemoryWarning];
45    // Dispose of any resources that can be recreated.
46    [self stopDemo];
47}
48
49// Start the data stream from the bluetooth headset if it's attached, and reset the display output
50- (void) appForegrounded
51{
52    EAAccessory *accessory = [[TGAccessoryManager sharedTGAccessoryManager] accessory];
53    if (accessory != nil) {
54        [self logMessage:[NSString stringWithFormat:@"App opened with %@ connected. Starting data stream", accessory.name]];
55        [[TGAccessoryManager sharedTGAccessoryManager] startStream];
56    }
57    [self resetViews]; // just in case it didn't happen on close
58}
59
60// Set all levels back to zero and clear the log
61- (void) resetViews
62{
63    [self resetOutputToZero];
64    self.log.text = @"";
65}
66
67- (void) resetOutputToZero
68{
69    [self showSignalStrength:[NSNumber numberWithInt:200]];
70    [self setAttentionLevel:[NSNumber numberWithInt:0]];
71    [self setMeditationLevel:[NSNumber numberWithInt:0]];
72    [self showPowerLevel];
73}
74
75- (void) appClosed
76{
77    [self stopDemo];
78    [self resetViews];
79    if ([[TGAccessoryManager sharedTGAccessoryManager] connected]) {
80        [[TGAccessoryManager sharedTGAccessoryManager] stopStream];
81    }
82}
83
84- (void) prepareAudio
85{
86    NSURL *audioFilePath = [[NSBundle mainBundle] URLForResource:AUDIO_FILE_NAME withExtension:nil];
87
88    NSError *error;
89    audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFilePath error:&error];
90    if (!audioPlayer) {
91        [self logMessage:[NSString stringWithFormat:@"Failed to initialize audio player: %@", [error debugDescription]]];
92    } else {
93        [audioPlayer prepareToPlay];
94    }
95}
96
97- (void) logMessage:(NSString *) str
98{
99    log.text = [[NSString alloc] initWithFormat:@"%@\n%@", str, log.text];
100}
101
102- (void) logDataReceived:(NSDictionary *) data
103{
104    NSNumber *sig = [data valueForKey:POOR_SIGNAL_KEY];
105    if (sig != NULL) {
106        int val = (200 - [sig intValue]) / 2;
107        [self logMessage:[NSString stringWithFormat:@"Signal strength: %u%%, Attention: %@%%, Meditation: %@%%",
108                          val, [data valueForKey:ATTENTION_KEY], [data valueForKey:MEDITATION_KEY]]];
109    }
110
111}
112
113#pragma mark - TGAccessoryDelegate methods
114
115- (void)dataReceived:(NSDictionary *)data {
116    [self performSelectorOnMainThread:@selector(updatedSignalReceived:)
117                           withObject:data
118                        waitUntilDone:NO];
119}
120
121- (void) updatedSignalReceived:(id) data
122{
123    if (LOGGING) {
124        [self logDataReceived: data];
125    }
126    [self showSignalStrength:(NSNumber *)[data valueForKey:POOR_SIGNAL_KEY]];
127    [self setAttentionLevel:(NSNumber *)[data valueForKey:ATTENTION_KEY]];
128    [self setMeditationLevel:(NSNumber *)[data valueForKey:MEDITATION_KEY]];
129    [self showPowerLevel];
130    [self playAudio];
131}
132
133// The headset was switched on, start the data stream
134- (void)accessoryDidConnect:(EAAccessory *)accessory {
135    [self logMessage:[NSString stringWithFormat:@"%@ was connected to this device. Starting data stream...", [accessory name]]];
136    if ([[TGAccessoryManager sharedTGAccessoryManager] accessory] != nil) {
137        [[TGAccessoryManager sharedTGAccessoryManager] startStream];
138    }
139}
140
141// The headset was switched off (or the Bluetooth signal was dropped).
142// Reset the outputs back to zero
143- (void)accessoryDidDisconnect {
144    [self logMessage:@"Accessory was disconnected."];
145    [self resetOutputToZero];
146}
147
148#pragma mark - display calculation and update methods
149
150// when the user adjusts one of the 2 threshold sliders we recalculate the power values at each of the 101 possible levels
151- (void) calculatePowerValues
152{
153    float threshold = self.attentionThreshold.value; // this is the user-selected minimum threshold - between 0 and 1
154    for (int i = 0; i < 101; i++) {
155        attentionPower[i] = [self calculatePowerAt:(float)i/100 withThreshold:threshold];
156    }
157    threshold = self.meditationThreshold.value; // this is the user-selected minimum threshold - between 0 and 1
158    for (int i = 0; i < 101; i++) {
159        meditationPower[i] = [self calculatePowerAt:(float)i/100 withThreshold:threshold];
160    }
161}
162
163// Convert a value (attention or meditation) into the corresponding power output (which is the volume level)
164// given the threshold is set at specified value.
165// The threshold is the value set by the user as the minimum value (attention or meditation) to be reached
166// before the helicopter is given the signal to fly.
167// If the value if below the threshold, the value will be zero (threshold not yet met).
168// Otherwise the returned power value will be between 0 and 1.
169- (float) calculatePowerAt:(float)value withThreshold:(float)threshold
170{
171    if (value < threshold) { // threshold not met
172        return 0;
173    }
174    // e.g. if the threshold is 0.55 and the current value is 0.7:
175    // there is 0.45 remaining of the threshold slider, and the value is 0.15 past the threshold (0.7 - 0.55), which is 0.33 (0.15 / 0.45)
176    return (value - threshold) / (1 - threshold);
177}
178
179- (void) showSignalStrength:(NSNumber *) value
180{
181    if (value != nil) {
182        self.signal.progress = (200.0 - [value intValue]) / 200.0;
183    }
184}
185
186- (void) setAttentionLevel:(NSNumber *) value
187{
188    if (value != nil) {
189        currentAttentionLevel = [value intValue];
190        self.attention.progress = (float)currentAttentionLevel / 100;
191    }   
192}
193
194- (void) setMeditationLevel:(NSNumber *) value
195{
196    if (value != nil) {
197        currentMeditationLevel = [value intValue];
198        self.meditation.progress = (float)currentMeditationLevel / 100;
199    }
200}
201
202// updates the power output progress bar
203- (void) showPowerLevel
204{
205    self.power.progress = [self currentPowerLevel];
206}
207
208// calculate the total power level to output through the headphones, a value between zero and 1.
209// this is the attention signal level and meditation signal level added together
210- (float) currentPowerLevel
211{
212    float powerLevel = attentionPower[currentAttentionLevel] + meditationPower[currentMeditationLevel];
213    if (powerLevel > 1) {
214        return 1.0;
215    }
216    return powerLevel;
217}
218
219- (void) playAudio
220{
221    audioPlayer.volume = [self currentPowerLevel];
222    [audioPlayer play];
223}
224
225#pragma mark - button press events
226
227// allow simulation of values being generated by a headset and being received on the phone
228- (IBAction) demoButtonPressed:(id) sender
229{
230    if (demoRunning) {
231        [self stopDemo];
232    } else {
233        [self startDemo];
234    }
235}
236
237- (void) stopDemo
238{
239    demoRunning = NO;
240    [self resetOutputToZero];
241    [demoButton setTitle:@"Demo" forState:UIControlStateNormal];
242    if (audioPlayer != NULL && [audioPlayer isPlaying]) {
243        [audioPlayer stop];
244    }
245}
246
247- (void) startDemo
248{
249    demoRunning = YES;
250    [self logMessage:@"Running demo"];
251    [demoButton setTitle:@"Stop" forState:UIControlStateNormal];
252    [self performSelectorInBackground:@selector(generateDemoData) withObject:nil];
253}
254
255// generate some data in a background thread and invoke the methods the headset would invoke with the data
256// for testing without a headset.
257- (void) generateDemoData
258{
259    for (int i = 0; i < 100; i++) {
260        if (!demoRunning) {
261            break; // break out if we stop the demo
262        }
263        NSDictionary *values = [NSDictionary dictionaryWithObjectsAndKeys:
264                                [NSNumber numberWithInt:i], ATTENTION_KEY,
265                                [NSNumber numberWithInt:i], MEDITATION_KEY,
266                                [NSNumber numberWithInt:0], POOR_SIGNAL_KEY,
267                                nil];
268        [self dataReceived:values];
269        [NSThread sleepForTimeInterval:1];
270    }
271}
272@end
Note: See TracBrowser for help on using the repository browser.