test_itaEimarMotorControl.m 15.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
classdef test_itaEimarMotorControl < itaMeasurementTasksScan
    % Measurements with the ITA Italian Turntable (Nanotec motors), ITA Arm and the ITA Slayer
    %
    %
    
    % *********************************************************************
    %
    % E = Everything
    % I = Is
    % M = Moving
    % A = And
    % R = Rotating
    %
    % *********************************************************************
    %
16
17
    % BE AWARE: THE MOTOR CONTROL HAS BEEN SEPARATED FROM THE ORIGINAL
    % itaEimar
18
    %
19
    % Author:       Jan-Gerrit Richter (jri@akustik.rwth-aachen.de)
20
    %
21
    % itaEimar:     Benedikt Krechel - November-June 2012
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    %               Johannes Klein - March/April/May 2012
    %
    % Contact:      Benedikt.krechel@rwth-aachen.de
    %               Johannes.klein@rwth-aachen.de
    %
    %
    % Special thanks to: Oliver Strauch for changing the e in Eimer to an a!
    %
    % *********************************************************************
    % *********************************************************************
    %
    % HOW TO USE:
    %
    % iMS = itaEimar;
    % -> Will call init and give note about which motor is connected.
    %
    % IMPORTANT COMMAND:
    % -  iMS.stop
    %
    % Make the reference move:
    % ( - without this you are not allowed to move the arm or the slayer (but the turntable)! - )
    % -  iMS.reference
    %
    % Move commands:
46
47
    %   This lets the motor decide what part of the coordinates it is
    %   responsible for
48
49
50
    % -  iMS.moveTo( itaCoordinate Object )
    %
    % Separate move commands:
51
    % -  iMS.moveTo(MotorName, step in degrees)
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    %
    % ************************************
    % STATIC MEASUREMENT:
    %
    % How to prepare the measurement:
    % -  iMS.measurementPositions = ita_sph_sampling_gaussian(30); (i.e.)
    % -  iMS.measurementSetup = [ YOUR MEASUREMENT SETUP i.e. itaMSTF ]
    % -  iMS.dataPath = [ PATH / FOLDER FOR RESULTS ]
    %
    % Run measurement
    % -  iMS.run
    %
    %
    % ************************************
66
67
68
69
70
71
72
73
74
75
76
    % CONTINUOUS MEASUREMENT:
    %
    %   This moves the arc into reference, then moves some angle against
    %   the turn direction and sends all commands but start for the full
    %   rotation
    % -  iMS.prepareForContinuousMeasurement;
    %   
    %   Starts the previously prepared rotation and measures
    %    iMS.runContinuousMeasurement;
    %   
    %   See ita_tutorial_hrtfMeasurement
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    %
    % Run measurement
    % -  iMS.run
    %
    % *********************************************************************
    %
    % <ITA-Toolbox>
    % This file is part of the application Movtec for the ITA-Toolbox. All rights reserved.
    % You can find the license for this m-file in the application folder.
    % </ITA-Toolbox>
    
    properties
        doSorting           =     true;   % sort the measurement coordinates for arm due to shorter measurement time 
    end
    % *********************************************************************
    properties (Access = protected, Hidden = true)
93
        actual_status       =   []; % Store status - don't send the same command again!
94
95
96
97
        old_position        =   itaCoordinates(2); % Avoid to move two times to the same position
        wait                =   true;        % Status - do we wait for motors to reach final position? -> Set by prepare-functions!
        
        mMotorControl = [];
98
        mRepetitionsSave = 0;
99
100
101
102
103
    end
    % *********************************************************************
    properties(Constant, Hidden = true)

    end
104
    
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
    methods
        %% -----------------------------------------------------------------
        % Basic stuff:
        function this = test_itaEimarMotorControl(varargin)
            % Constructor
            this.init;
        end
        function reset(this)
            %reset the Object. Ready to start from scatch again...
            this.clear_actual_status;
            this.mIsInitialized     =   false;
            this.mCurrentPosition   =   cart2sph(itaCoordinates(1));
            this.mLastMeasurement   =   0;
        end
        
        function clear_actual_status(this)
            % Clear confirmed commands which do not need to be send again.
            % Usefull if motor is turned off and on while class-object in
            % Matlab remains without a change.
            this.actual_status   =   [];
        end
 %         
        function init(this, varargin)
            % Initialize class
            
            %do a reset and also reset the serialObj
            this.reset(); % Do a reset first.
            this.actual_status           =   [];



             
            % Init RS232 and return handle
            try
                if isempty(this.measurementSetup)
                elseif isempty(this.measurementSetup) && isempty(this.measurementSetup.inputMeasurementChain(1).coordinates.x)
                    for idx = 1:numel(this.measurementSetup.inputMeasurementChain)
                        this.measurementSetup.inputMeasurementChain(idx).coordinates = itaCoordinates([0 0 0]);
                    end
                end
                
            % init the motorclass object;
            this.mMotorControl = itaMotorControlNanotec;
            this.mIsInitialized = true;

            catch errmsg
%                 this.mIsInitialized                     =   false;
                ita_verbose_info(errmsg.message,0);
                error('i: Unable to initialize correctly');
            end
            
            % subfolder for data - speed reasons for lots of files
            if ~isdir(this.finalDataPath)
                mkdir(this.finalDataPath);
            end
        end
161
162
        
        
163
164
165
166
167
168
169
170
        %% -----------------------------------------------------------------
        % Reference stuff:
        function reference(this)
            % Move to reference position
            ita_verbose_info('Everything is moving... (TAKE CARE OF YOUR HEAD!)',0);
            this.mMotorControl.reference;
            ita_verbose_info('Reference done!',1);
        end
171
172
        
        
173
174
175
176
177
178
179
180
181
182
183
        %% -----------------------------------------------------------------
        % Move commands:
        function stop(this)
            % DO NOT ASK - JUST STOP ALL MOTORS!
            for i = 1:5 % repeat several times to ensure that every motor stops!
                this.mMotorControl.stopAllMotors;
            end
        end
        
        function moveTo(this,varargin)
            % Check if it is initialized:
184
%             varargin{1}.phi_deg
185
186
187
188
189
190
191
192
193
194
195
            if ~this.isInitialized
                ita_verbose_info('Not initialized - I will do that for you...!',0);
                this.initialize
            end
            % Check if it is referenced:
            if ~this.isReferenced
                % Otherwise warn the user that the reference is at power-up
                % position:
                ita_verbose_info('Be aware! No reference move done! Reference = Power-Up-Position!',2)
            end
            
196
            % let the motor control do the move
197
198
199
            this.mMotorControl.moveTo(varargin{:});
        end
  
200
        function prepareContinuousMeasurement(this,varargin)
Jan-Gerrit Richter's avatar
Jan-Gerrit Richter committed
201
202
203
204
205
            
            if isempty(this.measurementSetup)
                error('Measurement Setup is unset')
            end
            
206
            % calculate the pre angle
Jan-Gerrit Richter's avatar
Jan-Gerrit Richter committed
207
208
            % the pre angle is needed because the measurement setup will
            % not start recording imidiately
209
            numRepetitions = this.measurementSetup.repetitions;
210
            this.measurementSetup.repetitions = numRepetitions;
211
212
            timePerRepetition = this.measurementSetup.twait*length(this.measurementSetup.outputChannels);
            speed   =   360/(numRepetitions*timePerRepetition);
213
214
215
216
217
218
219
            motorControl = this.getMotorControl;
            motorName = motorControl.motorList{1}.getMotorName;
            
            if strcmp(motorName,'HRTFArc')
                % preangletime
                preAngle = 45;
                preAngleTime = preAngle/speed; % it takes 2 seconds to start recording
220

221
222
                postAngle = -35;
                postAngleTime = postAngle/speed;
223

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
                additionalReps = ceil((postAngleTime+preAngleTime + 1)/timePerRepetition);
                numTotalRepetitions = numRepetitions+additionalReps;
                this.measurementSetup.repetitions = numTotalRepetitions;
            else
                % preangletime
                preAngle = 15;
                preAngleTime = preAngle/speed; % it takes 2 seconds to start recording

                postAngle = 5;
                postAngleTime = postAngle/speed;

                additionalReps = ceil((postAngleTime+preAngleTime + 1)/timePerRepetition);
                numTotalRepetitions = numRepetitions+additionalReps;
                this.measurementSetup.repetitions = numTotalRepetitions;    
                
            end
240
            %prepare motors for continuous measurement
241
            this.mMotorControl.prepareForContinuousMeasurement('speed',speed,'preAngle',preAngle,'postAngle',postAngle);
242
            
Jan-Gerrit Richter's avatar
Jan-Gerrit Richter committed
243
            % calculate the excitation as this takes quite a long time
244
            this.measurementSetup.excitation;
Jan-Gerrit Richter's avatar
Jan-Gerrit Richter committed
245
246
            
            % pre init playrec to save the second delay
247
248
            if playrec('isInitialised')
                playrec('reset');
249
            end
250
            playrec('init', this.measurementSetup.samplingRate, 0, 0);
251
252
253
254
255
            
            
        end
        
        
256
        function [result, result_raw] = runContinuousMeasurement(this)
257
258
            this.mMotorControl.setWait(false);
            this.mMotorControl.startContinuousMoveNow;
259
            pause(0.1);
260
261
            result_raw = this.measurementSetup.run_raw_imc;
            result = this.measurementSetup.deconvolve(result_raw);
Jan-Gerrit Richter's avatar
Jan-Gerrit Richter committed
262
%             this.stop;
263
            this.mMotorControl.setWait(true);
264
            this.measurementSetup.repetitions = this.mRepetitionsSave;
265
266
267
            % add history line
            commitID = ita_git_getMasterCommitHash;
            if ~isempty(commitID)
268
269
270
               result_raw = ita_metainfo_add_historyline(result_raw,'Continuous Measurement',commitID); 
               result = ita_metainfo_add_historyline(result,'Continuous Measurement',commitID); 

271
            end
272
273
        end
        
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
        function rescueArcFromSwitch(this)
            % this function is intended for the HRTFArc when it is stuck
            % between the two switches.
            % this can happen if "prepareForContinuousRotation" is called
            % and the run function does not move the arc back over the
            % reference switch
            
            % this function will disable the reference switch, move the arc
            % over it and enable it again. 
            
            % only call this, if you know why you are doing it.
            motor = this.mMotorControl.motorList{1};
            name = motor.getMotorName;
            if ~strcmp(name,'HRTFArc')
                error('Only for HRTFArc');
            end

            prompt = 'Type "''RESCUE''" and press return ';
            x = input(prompt);
            if ~strcmp(x,'RESCUE')
                error('Aborted due to wrong string');
            end
            
            
            this.mMotorControl.motorList{1}.disableReference(1);
            this.moveTo('HRTFArc',70,'absolut',false);
            this.mMotorControl.motorList{1}.disableReference(0);
            this.reference;
            
        end
        
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
        function motorControl = getMotorControl(this)
           motorControl = this.mMotorControl; 
        end
        %% -----------------------------------------------------------------
        % GUI:
        function gui(this) %#ok<*MANU>
            %call GUI
            errordlg('There is no GUI - sorry! But you can build one if you like!')
        end
        
    end %% methods
    % *********************************************************************
    % *********************************************************************
    methods(Hidden = true)
        % Sort measurement positions and delete points outside of the allowed range:
        function this = sort_measurement_positions(this,varargin)
            ita_verbose_info('I will sort your measurement positions for better performance.', 1);
            % Delete every point below ARM_limit:
            this.measurementPositions = this.measurementPositions.n(this.measurementPositions.theta <= this.ARM_limit(2)/180*pi);
            this.measurementPositions = this.measurementPositions.n(this.measurementPositions.theta >= this.ARM_limit(1)/180*pi);
            % Sort:
            this.measurementPositions = this.do_coordinate_sorting(this.measurementPositions); 
        end
 
%         % -----------------------------------------------------------------
        function res = finalDataPath(this)
            % Final data path string
            res                 =   [this.dataPath filesep 'data'];
        end
        %-----------------------------------------------------------------

        
        function run(this)
            if this.doSorting
                this.sort_measurement_positions();
            end
341
342
            motorList = this.mMotorControl.motorList;
            motorList{1}.disableReference(1);
343
            run@itaMeasurementTasksScan(this);
344
            motorList{1}.disableReference(0);
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
        end
        
    end
    methods(Static)
        function s = do_coordinate_sorting(s)
            % phi should be between -pi..pi
            %             s.phi = mod(s.phi + pi, 2*pi) - pi;
            s.phi(s.phi > pi) = s.phi(s.phi > pi) - 2*pi;
            
            % sort the phi angles in both directions
            [~, index_phi_ascend] = sort(round_tol(s.phi), 'ascend');
            [~, index_phi_descend] = sort(round_tol(s.phi), 'descend');
            % sort the theta angles
            [~, index_theta_phi_ascend] = sort(s.theta(index_phi_ascend), 'descend');
            [~, index_theta_phi_descend] = sort(s.theta(index_phi_descend), 'descend');
            
            % get a list of unique elevation angles
            unique_theta = unique(round_tol(s.theta));
            
            % this is the complete coordinates sorted with ascending and
            % descending phi angle
            sph_ascend = s.sph(index_phi_ascend(index_theta_phi_ascend),:);
            sph_descend = s.sph(index_phi_descend(index_theta_phi_descend),:);
            
            % check when we want to use ascending, when descending
            isAscending = ismember(round_tol(sph_ascend(:,2)), unique_theta(1:2:end));
            % the last row with the lowest theta (at the beginning of the
            % unique-list) should be ascending in order to avoid problems
            % with the reference move later on
            
            % overwrite the data to the coordinates object
            s.sph(isAscending,:) = sph_ascend(isAscending,:);
            s.sph(~isAscending,:) = sph_descend(~isAscending,:);
            
%             % phi should be again between 0..2*pi
%             s.phi = mod(s.phi, 2*pi);
            
            function theta = round_tol(theta)
                constant = 1e10;
                theta = round(theta * constant) / constant;
            end
        end
    end
end