source: orbit/iOS/Orbit/Orbit/ViewController.m @ 2aabdf3

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