source: orbit/iOS/Orbit/Orbit/SignalConverter.m @ ab9d63b

RawEEGServoTab_Interfacepyramid
Last change on this file since ab9d63b was ab9d63b, checked in by Jonathon Horsman <jonathon@…>, 9 years ago

Initial check in to convert into a 4 tab app with tutorial, flight, advanced and support tabs.
Old controller code remains for now but about to be removed

  • Property mode set to 100644
File size: 6.3 KB
Line 
1//
2//  SignalConverter.m
3//  orbit
4//
5//  Created by Jonathon Horsman on 11/07/2013.
6//  Copyright (c) 2013 Puzzlebox Productions, LLC. All rights reserved.
7//
8
9#import "SignalConverter.h"
10
11#define AUDIO_FILE_NAME @"throttle_hover_ios.wav" // @"iOS_noflip.wav"
12#define POOR_SIGNAL_KEY @"poorSignal"
13#define ATTENTION_KEY @"eSenseAttention"
14#define MEDITATION_KEY @"eSenseMeditation"
15
16// Converts signals received from the EEG headset to the audio played to fly the helicopter.
17//
18@implementation SignalConverter {
19    // these 2 arrays hold the values of the thrust which should be applied to the helicopter
20    // (by way of volume level through the headphones) at each level of attention and meditation.
21    // this could be calculated on the fly, but because it happens ~20 times per second, the
22    // better option is to store all the values in arrays, and recalculate when the sliders are changed.
23    float attentionPower[101];
24    float meditationPower[101];
25   
26    int signalStrength, attentionLevel, meditationLevel; // the latest readings from the headset
27}
28
29@synthesize attentionThreshold, meditationThreshold, running;
30
31- (void) setValuesForAttention:(float) attention meditation:(float) meditation
32{
33    attentionThreshold = attention;
34    meditationThreshold = meditation;
35    [self calculatePowerValues];
36}
37
38- (void) prepare
39{
40    NSURL *audioFilePath = [[NSBundle mainBundle] URLForResource:AUDIO_FILE_NAME withExtension:nil];
41    audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFilePath error:nil];
42    if (audioPlayer) {
43        [audioPlayer prepareToPlay];
44    }
45}
46
47- (void) appClosed
48{
49    if ([[TGAccessoryManager sharedTGAccessoryManager] connected]) {
50        [[TGAccessoryManager sharedTGAccessoryManager] stopStream];
51    }
52}
53
54#pragma mark - TGAccessoryDelegate methods
55
56- (void)dataReceived:(NSDictionary *)data {
57    [self performSelectorOnMainThread:@selector(updatedSignalReceived:)
58                           withObject:data
59                        waitUntilDone:NO];
60}
61
62// Updated signal received from the EEG headset.
63- (void) updatedSignalReceived:(id) data
64{
65    [self setValuesFromData:data];
66    // notify listening delegates of updated values so the UI can be updated
67    if (_delegate != nil)
68    {
69        [_delegate updatedValuesForSignal: signalStrength
70                                    attention: (float)attentionLevel / 100
71                                   meditation: (float)meditationLevel / 100
72                                        power: [self currentPowerLevel]];
73    }
74    [self playAudio];
75}
76
77- (void) setValuesFromData:(id) data
78{
79    [self setSignalStrength:(NSNumber *)[data valueForKey:POOR_SIGNAL_KEY]];
80    [self setAttentionLevel:(NSNumber *)[data valueForKey:ATTENTION_KEY]];
81    [self setMeditationLevel:(NSNumber *)[data valueForKey:MEDITATION_KEY]];   
82}
83
84- (void) setSignalStrength:(NSNumber *) value
85{
86    if (value != nil) {
87        signalStrength = (200.0 - [value intValue]) / 200.0;
88    }
89}
90
91- (void) setAttentionLevel:(NSNumber *) value
92{
93    if (value != nil) {
94        attentionLevel = [value intValue];
95    }
96}
97
98- (void) setMeditationLevel:(NSNumber *) value
99{
100    if (value != nil) {
101        meditationLevel = [value intValue];
102    }
103}
104
105// The headset was switched on, start the data stream
106- (void)accessoryDidConnect:(EAAccessory *)accessory {
107    if (_delegate != nil)
108    {
109        [_delegate notifyHeadsetConnect];
110    }
111    if ([[TGAccessoryManager sharedTGAccessoryManager] accessory] != nil) {
112        [[TGAccessoryManager sharedTGAccessoryManager] startStream];
113    }
114}
115
116// The headset was switched off (or the Bluetooth signal was dropped).
117- (void)accessoryDidDisconnect {
118    if (_delegate != nil)
119    {
120        [_delegate notifyHeadsetDisconnect];
121    }
122}
123
124#pragma mark start / stop methods
125
126- (void) startProcessing
127{
128    running = YES;
129    EAAccessory *accessory = [[TGAccessoryManager sharedTGAccessoryManager] accessory];
130    if (accessory != nil) {
131        if (_delegate != nil)
132        {
133            [_delegate notifyDeviceConnected: accessory.name];
134        }
135        [[TGAccessoryManager sharedTGAccessoryManager] startStream];
136    }
137}
138
139- (void) stopProcessing
140{
141    running = NO;
142    if ([[TGAccessoryManager sharedTGAccessoryManager] connected]) {
143        [[TGAccessoryManager sharedTGAccessoryManager] stopStream];
144    }
145    [audioPlayer stop];
146    [self prepare];   
147}
148
149#pragma mark internal processing methods
150
151// calculate the total power level to output through the headphones, a value between zero and 1.
152// this is the attention signal level and meditation signal level added together
153- (float) currentPowerLevel
154{
155    float powerLevel = attentionPower[attentionLevel] + meditationPower[meditationLevel];
156    if (powerLevel > 1) {
157        return 1.0;
158    }
159    return powerLevel;
160}
161
162- (void) playAudio
163{
164    //   audioPlayer.volume = [self currentPowerLevel];
165    if ([self currentPowerLevel] > 0) {
166        audioPlayer.volume = 1.0;
167        [audioPlayer play];
168    } else {
169        [audioPlayer stop];
170        [self prepare];
171       
172    }
173}
174
175#pragma mark - display calculation and update methods
176
177// when the user adjusts one of the 2 threshold sliders we recalculate the power values at each of the 101 possible levels
178- (void) calculatePowerValues
179{
180    for (int i = 0; i < 101; i++) {
181        attentionPower[i] = [self calculatePowerAt:(float)i/100 withThreshold:attentionThreshold];
182    }
183    for (int i = 0; i < 101; i++) {
184        meditationPower[i] = [self calculatePowerAt:(float)i/100 withThreshold:meditationThreshold];
185    }
186}
187
188// Convert a value (attention or meditation) into the corresponding power output (which is the volume level)
189// given the threshold is set at specified value.
190// The threshold is the value set by the user as the minimum value (attention or meditation) to be reached
191// before the helicopter is given the signal to fly.
192// If the value if below the threshold, the value will be zero (threshold not yet met).
193// Otherwise the returned power value will be between 0 and 1.
194- (float) calculatePowerAt:(float)value withThreshold:(float)threshold
195{
196    if (value < threshold) { // threshold not met
197        return 0;
198    }
199    // e.g. if the threshold is 0.55 and the current value is 0.7:
200    // 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)
201    return (value - threshold) / (1 - threshold);
202}
203
204@end
Note: See TracBrowser for help on using the repository browser.