source: fact/tools/PyDimCtrl/pyfactctrl.py@ 13812

Last change on this file since 13812 was 13812, checked in by neise, 14 years ago
Evolving
  • Property svn:executable set to *
File size: 23.0 KB
Line 
1#!/usr/bin/python -tti
2from factdimserver import *
3
4# List of Wobble Positions
5source_list = { '1ES 1218+304' : [( 0.6, -5), (0.6, 175)] ,\
6 'Crab' : [(0.6, 50), (0.6, -130)], \
7 'Mrk 421' : [(0.6, 90),(0.6, -90)] \
8 'Mrk 501' : [(0.6, 90),(0.6, -90)] }
9
10def TakeFullSet( source = 'Crab', verbose = True):
11 msg = MSG( verbose )
12 drs_calib_done = False # Drs Calib is only done in the 1st wobble pos
13
14 if not source in source_list:
15 raise ValueError('source not in source_list')
16 msg("Possible sources are:")
17 for src in source_list.keys():
18 msg(src)
19 return False
20
21
22 # set is a list of wobble positions, stored as 2-tuple
23 for set in source_list(source):
24 # wobble is a 2-tuple: (Shift, Angle)
25 for wobble in set:
26 TrackSource( source, wobble[0] , wobble[1] , CalmDownTime=45)
27
28 if not drs_calib_done:
29 TakeDrsAmplitudeCalibration( roi=1024 )
30 TakeDrsTimeCalibration( mode='upshifted' )
31 AddDrsAmplitudeCalibration( roi=300 )
32 drs_calib_done = True
33
34 VoltageOn( mode='current' )
35 for i in range(4):
36 msg("Taking Data Run "+str(i+1)+" of 4")
37 TakeDataRun()
38 VoltageOff()
39
40 StopTracking()
41 return True
42
43#=========== TRACKING =========================================================
44def TrackSource( Shift=0.6, Angle=50, SrcName='Crab', CalmDownTime = None, verbose=True):
45 drive = drive_control
46 msg = MSG( verbose )
47 SrcName += '\0'
48
49 if not StopTracking( timeout = 5, verbose = False ):
50 msg.fail('Cannot start Tracking, because I cannot Stop the Drive')
51 return False
52
53 drive.track_source( Shift, Angle, SrcName)
54
55 if CalmDownTime != None:
56 if not WaitToCalmDown( CalmDownTime, verbose = True):
57 msg.fail("a problem in WaitToCalmDown(). drive state: "+drive.sts)
58 return False:
59
60 msg.ok('Tracking Source '+SrcName + ' Angle:'+str(Angle)+' Shift:'+str(Shift))
61 return True
62
63def WaitToCalmDown( CalmDownTime = 45, time_until_moving = 10, time_until_tracking = 40, verbose = True):
64 """ before calling this function, one should call TrackSource()
65 this function waits until Drive is in state 'Tracking'
66 and then sleeps *CalmDownTime* seconds in order to give
67 the Drive time to calm down.
68
69 """
70 drive = drive_control
71 msg = MSG(verbose)
72
73 starttime = time.time()
74 while not drive.stn == 6: #Moving
75 if time.time() - starttime > time_until_moving:
76 msg.fail("DRIVE not in Moving after "+str(time_until_moving))
77 return False
78 time.sleep(0.5)
79 msg.ok("Drive is Moving, waiting for Tracking")
80
81 starttime = time.time()
82 while not drive.stn == 7: #Tracking
83 if time.time() - starttime > time_until_tracking:
84 msg.fail("DRIVE not in Tracking after "+str(time_until_tracking))
85 return False
86 time.sleep(0.5)
87 msg.ok("Drive is Tracking, waiting to calm down...")
88
89 time.sleep(CalmDownTime)
90 msg.ok("Drive hopefully has calmed down")
91 return True
92
93def StopTracking( timeout = 3, verbose = True ):
94 msg = MSG(verbose)
95 drive = drive_control
96 starttime = time.time()
97 while not drive.stn == 5:
98 if time.time() - starttime > timeout:
99 msg.fail('Did not get DRIVE into Armed state within '+str(timeout)+' seconds')
100 return False
101
102 if drive.stn < 5:
103 msg.fail('DRIVE in state'+drive.sts+" I don't know how to STOP")
104 if drive.stn >= 256:
105 msg.fail('DRIVE in state'+drive.sts+" I don't know how to STOP")
106
107 if drive.stn in [6,7]:
108 drive.stop()
109 time.sleep(0.5)
110
111 msg.ok('DRIVE Stopped: current state:'+drive.sts)
112 return True
113
114#=========== DRS CALIBRATION ==================================================
115def TakeDrsAmplitudeCalibration( roi = None, verbose = True):
116 """ Takes DRS Amplitude Calibration
117 *roy* integer: 300, 1024 or
118 *roy* string : 'both'
119 in case of both, the roi=300 calibration is done last.
120
121 after each complete AmpliteCalibration a 4th pedestal run of the same
122 roi is taken, for crosschecking.
123 """
124 msg = MSG( verbose )
125 bias = bias_control
126 fad = fad_control
127 if not roi in [300,1024,'both']:
128 raise ValueError("roi must be 300,1024 or the string 'both'")
129
130
131 bias.set_zero_voltage()
132 if not bias.wait(7, 10):
133 msg.warn("bias has not switched of after 10sec")
134 return False
135
136 fad.start_drs_calibration()
137 Take( -1, 1000, 'drs-pedestal')
138 Take( -1, 1000, 'drs-gain')
139 if roi == 300:
140 Take( -1, 1000, 'pedestal')
141 fad.set_file_format(2)
142 Take( -1, 1000, 'pedestal')
143 elif roi == 1024:
144 Take( -1, 1000, 'drs-pedestal')
145 fad.set_file_format(2)
146 Take( -1, 1000, 'drs-pedestal')
147 elif roi == 'both':
148 Take( -1, 1000, 'drs-pedestal')
149 fad.set_file_format(2)
150 Take( -1, 1000, 'drs-pedestal')
151 fad.reset_secondary_drs_baseline()
152 Take( -1, 1000, 'pedestal')
153 fad.set_file_format(2)
154 Take( -1, 1000, 'pedestal')
155
156
157def TakeDrsTimeCalibration( mode = 'upshifted', verbose = True):
158 """ Takes DRS Time Calibration
159 *mode* can be 'upshifted', 'old' or 'both'
160 """
161 msg = MSG( verbose )
162 bias = bias_control
163 fad = fad_control
164 if not mode in ['upshifted', 'old', 'both']:
165 raise ValueError("mode must be: 'upshifted','old' or 'both'")
166
167 bias.set_zero_voltage()
168 if not bias.wait(7, 10):
169 msg.warn("bias has not switched of after 10sec")
170 return False
171
172 fad.set_file_format(2)
173 if mode == 'old':
174 Take(-1, 1000, 'drs-time')
175 if mode == 'upshifted':
176 Take(-1, 1000, 'drs-time-upshifted')
177 if mode == 'both':
178 Take(-1, 1000, 'drs-time')
179 Take(-1, 1000, 'drs-time-upshifted')
180
181
182
183def AddDrsAmplitudeCalibration( roi = None, verbose = True):
184 """ Adds a DRS Amplitude Calibration,
185 do not call, in case you did not *Take* one before
186 *roy* integer: 300, 1024
187
188 after each complete AmpliteCalibration a 4th pedestal run of the same
189 roi is taken, for crosschecking.
190 """
191 msg = MSG( verbose )
192 bias = bias_control
193 fad = fad_control
194 if not roi in [300,1024]:
195 raise ValueError("roi must be 300,1024")
196
197 if not bias.stn == 'VoltageOff':
198 msg.fail('a DRS amplitude calibration should not be taken with Voltage On')
199 return False
200
201 fad.reset_secondary_drs_baseline()
202 if roi == 300
203 Take( -1, 1000, 'pedestal')
204 fad.set_file_format(2)
205 Take( -1, 1000, 'pedestal')
206 elif roi == 1024:
207 Take( -1, 1000, 'drs-pedestal')
208 fad.set_file_format(2)
209 Take( -1, 1000, 'drs-pedestal')
210
211 return True
212
213
214#============ BIAS VOLTAGE ====================================================
215def VoltageOn( mode = None, Watchdog = None, verbose = True):
216 """ High Level Method for switching on the bias Voltage
217 can be called, in whatever state the bias and the feedback might be.
218 In case the voltage is on and the feedback is already in the mode
219 requested, nothing happens.
220 In all other cases, the method will perform the needed steps to switch
221 on the bias voltage, the way the user requested
222 """
223 msg = MSG( verbose )
224 bias = bias_control
225 fb = feedback
226
227 if not mode in ['temperature', 'current', 'feedback']:
228 raise ValueError("mode must be 'temperature', 'current' or 'feedback'")
229
230
231
232 bias.set_global_dac(1)
233 if not bias.wait(9, 10):
234 msg.warn("bias not in Voltage ON after 10sec")
235 return False
236 if not bias.wait(5, 10):
237 msg.warn("bias not Ramping after 10sec")
238 return False
239 if not bias.wait(9, 30):
240 msg.warn("bias not fully ramped up after 30sec")
241 return False
242
243 msg("waiting 45sec...")
244 time.sleep(45)
245 return True
246
247
248
249def IsReadyForDataTaking( verbose = True ):
250 drive = drive_control
251 bias = bias_control
252 fad = fad_control
253
254 msg = MSG()
255 msg.output = verbose
256 msg("Checking if System isready for data taking... ")
257
258 ok = True
259 if not drive.stn == 7:
260 msg.warn(drive.name + ':' + drive.sts + " NOT ok")
261 ok = False
262 else:
263 msg.ok(drive.name + ':' + drive.sts + " OK")
264
265 if not feedback.stn == 12:
266 msg.warn(feedback.name +':'+ feedback.sts + " NOT ok")
267 ok = False
268 else:
269 msg.ok(feedback.name +':'+ feedback.sts + " OK")
270
271 if not bias.stn == 9:
272 msg.warn(bias.name +':'+ bias.sts + " NOT ok")
273 ok = False
274 else:
275 msg.ok(bias.name +':'+ bias.sts + " OK")
276
277 if not fad.stn == 4:
278 msg.warn(fad.name +':'+ fad.sts + " NOT ok")
279 ok = False
280 else:
281 msg.ok(fad.name +':'+ fad.sts + " ok")
282
283
284 if ok:
285 msg.ok( " all ok " )
286
287 return ok
288
289def TakeDataRun( verbose = True ):
290 fad = fad_control
291 msg = MSG()
292 msg.output = verbose
293 if not IsReadyForDataTaking( verbose=False ):
294 msg.fail("System not Ready for DataTaking")
295 IsReadyForDataTaking( verbose=True )
296 return False
297 mcp.start(300,-1,'data\0')
298 if not fad.wait(8, 10):
299 msg.warn("FAD not in Writing Data after 10seconds")
300 return False
301 msg("... taking data: 300 seconds - normal data run")
302 if not fad.wait(4, 330):
303 msg.warn("FAD did not return to Connected, after 330 seconds")
304 return False
305 return True
306
307def TakePedestalOnRun( verbose = True ):
308 msg = MSG()
309 msg.output = verbose
310 if not IsReadyForDataTaking( verbose=False ):
311 msg.fail("System not Ready for DataTaking")
312 IsReadyForDataTaking( verbose=True )
313 return False
314 mcp.start(-1,1000,'pedestal\0')
315 if not fad.wait(8, 10):
316 msg.warn("FAD not in Writing Data after 10seconds")
317 return False
318 msg("... taking ped: 1000evts @25Hz")
319 if not fad.wait(4, 50):
320 msg.warn("FAD did not return to Connected, after 50 seconds")
321 return False
322 return True
323
324def TakeExtLpRun( verbose = True ):
325 msg = MSG()
326 msg.output = verbose
327 if not IsReadyForDataTaking( verbose=False ):
328 msg.fail("System not Ready for DataTaking")
329 IsReadyForDataTaking( verbose=True )
330 return False
331 mcp.start(-1,1000,'light-pulser-ext\0')
332 if not fad.wait(8, 10):
333 msg.warn("FAD not in Writing Data after 10seconds")
334 return False
335 msg("... taking light-pulser-ext: 1000evts @25Hz")
336 if not fad.wait(4, 50):
337 msg.warn("FAD did not return to Connected, after 50 seconds")
338 return False
339 return True
340
341def TakeData( verbose = True):
342 msg = MSG()
343 msg.output = verbose
344 TakePedestalOnRun()
345 TakeExtLpRun()
346 for i in range(4):
347 i +=1
348 msg("Taking Data Run "+str(i)+" of 4")
349 TakeDataRun()
350
351def Take( time=0, events=0, runtype='drs-pedestal', verbose=True):
352 msg = MSG( verbose )
353 fad = fad_control
354 runtype += '\0'
355 if not fad.wait(4, 10):
356 msg.warn("fad not connected after 10sec")
357 return False
358 mcp.start( time, events, runtype)
359 if not fad.wait(8, 30):
360 msg.warn("fad not Writing Data after 30sec")
361 return False
362 timeout = float('inf')
363 if time != -1:
364 timeout = time*1.1
365 if not fad.wait(4, timeout):
366 msg.warn("Data Writing not finished after "+str(timeout)+"sec")
367 return False
368 return True
369
370
371def BlinkenLights(verbose = True):
372 msg = MSG(verbose)
373 fad = fad_control
374
375 for i in range(10):
376 fad.set_file_format(2)
377 time.sleep(1)
378 fad.set_file_format(0)
379 time.sleep(1)
380
381
382
383
384#==============================================================================
385def Connect():
386 prepare_ftm_control()
387 prepare_fsc_control()
388 feedback.stop()
389 prepare_bias_control()
390 prepare_feedback()
391 prepare_data_logger()
392
393def prepare_fsc_control( verbose = True, timeout = 10, delay = 0.2):
394 msg = MSG( verbose )
395 fsc = fsc_control
396 start_time = time.time()
397 if timeout == None:
398 timeout = float('inf')
399 if delay < 0.:
400 delay = 0.
401
402 while not fsc.stn == 2: #Connected
403 if time.time() > start_time + timeout:
404 return False
405
406 if (fsc.stn <= 0) or (fsc.stn >= 256):
407 msg.fail("FSC_CONTROL is in state "+fsc.sts)
408 msg.fail(prepare_fsc_control.__name__ + "does not know how to handle this")
409 return False
410 elif fsc.stn == 1: # Disconnected
411 fsc.reconnect()
412 return True
413
414def prepare_ftm_control( verbose = True, timeout = 10, delay = 0.2):
415 msg = MSG( verbose )
416 ftm = ftm_control
417 start_time = time.time()
418 if timeout == None:
419 timeout = float('inf')
420 if delay < 0.:
421 delay = 0.
422
423 while not ftm.stn == 3:
424 if time.time() > start_time + timeout:
425 return False
426
427 if (ftm.stn <= 0) or (ftm.stn >= 256):
428 msg.fail("FMT_CONTROL is in state "+ftm.sts)
429 msg.fail("ftm_to_Idle() does not know how to handle this")
430 return False
431 elif ftm.stn == 1: # Disconnected
432 ftm.reconnect()
433 elif ftm.stn == 2: #Connected
434 # just wait a second
435 time.sleep(1)
436 elif ftm.stn == 4: #TriggerOn
437 ftm.enable_trigger(0) #switch trigger off
438 elif ftm.stn in [5,6,7]: # some Configure state
439 ftm.reset_configure()
440
441 return True
442
443def prepare_data_logger( verbose = True, timeout = 10, delay = 0.2):
444 msg = MSG(verbose)
445 dl = data_logger
446 if timeout == None:
447 timeout = float('inf')
448 if delay < 0.:
449 delay = 0.
450 start_time = time.time()
451 raise NotImplementedError('prepare_data_logger() is not yet implemented')
452
453def prepare_fad_control( verbose = True, timeout = 10, delay = 0.2):
454 msg = MSG(verbose)
455 fad = fad_control
456 ftm = ftm_control
457 if timeout == None:
458 timeout = float('inf')
459 if delay < 0.:
460 delay = 0.
461 start_time = time.time()
462
463 while not fad.stn == 4:
464 # Timeout
465 if time.time() > start_time + timeout:
466 msg.fail("Timeout in " + prepare_fad_control.__name__)
467 return False
468 # Strange States
469 if (fad.stn <= 0) or (fad.stn >= 256):
470 msg.fail("FAD_CONTROL is in state "+fad.sts)
471 msg.fail(prepare_fad_control.__name__ + "does not know how to handle this")
472 return False
473 elif fad.stn == 1: # Disengaged
474 fad.start()
475 elif fad.stn == 2: # Disconnected
476 fad.start()
477 elif fad.stn == 3: # Connecting
478 # it might just need time
479 if time.time() - fad.last_st_change < 5:
480 time.sleep(2)
481 timeout += 1
482 else:
483 # there might be a problem with one of the boards
484 conns = map( ord, fad.connections()[0] )
485 problems = {}
486 for fad,conn in enumerate(conns):
487 if conn < 255:
488 print "FAD:", fad, "has a problem"
489 if not fad/10 in problems:
490 problems[fad/10] = []
491 else:
492 problems[fad/10].append( fad%10 )
493
494 for fad in range(10):
495 for crate in problems.keys():
496 fad.disconnect(crate*10+fad)
497 ftm.toggle_ftu(crate*10+fad)
498 for crate in problems.keys():
499 time.sleep(1)
500 ftm.reset_crate(crate)
501
502 for fad in range(10):
503 for crate in problems.keys():
504 time.sleep(3.5)
505 fad.connect(crate*10+fad)
506 ftm.toggle_ftu(crate*10+fad)
507
508 elif fad.stn == 5: # Configuring1
509 if time.time() - fad.last_st_change < 5:
510 time.sleep(1)
511 else:
512 msg.warn("FAD is in Configuring1 since more than 5sec")
513
514 elif fad.stn == 6: # Configuring2
515 if time.time() - fad.last_st_change < 5:
516 time.sleep(1)
517 else:
518 msg.warn("FAD is in Configuring2 since more than 5sec")
519
520 elif fad.stn == 7: # Configured
521 if time.time() - fad.last_st_change < 5:
522 time.sleep(1)
523 else:
524 msg.warn("FAD is in Configuring2 since more than 5sec")
525
526 elif fad.stn == 8: #WritingData
527 # I don't know how to get from WritingData to Connected
528 # well. .. one could wait for a timeout, but I hate that.
529 fad.close_open_files()
530
531
532 time.sleep(delay)
533 return True
534
535def prepare_bias_control( verbose = True, timeout = 10, delay = 0.2):
536 msg = MSG(verbose)
537 bias = bias_control
538
539 if timeout == None:
540 timeout = float('inf')
541 if delay < 0.:
542 delay = 0.
543 start_time = time.time()
544
545 while (bias.stn != 4) and (bias.stn != 7): #Connected or VoltageOff
546 # Timeout
547 if time.time() > start_time + timeout:
548 msg.fail("Timeout in " + prepare_bias_control.__name__)
549 return False
550 # Strange States
551 if (bias.stn <= 0) or (bias.stn >= 256):
552 msg.fail("BIAS_CONTROL is in state "+bias.sts)
553 msg.fail(prepare_bias_control.__name__ + "does not know how to handle this")
554 return False
555 elif bias.stn == 1: # Disconnected
556 bias.reconnect()
557 elif bias.stn == 2: # Connecting
558 if time.time() - bias.last_st_change < 5:
559 time.sleep(1)
560 else:
561 msg.warn("BIAS_CONTROL is in Connecting since more than 5sec")
562 elif bias.stn == 3: # Initializing
563 if time.time() - bias.last_st_change < 5:
564 time.sleep(1)
565 else:
566 msg.warn("BIAS_CONTROL is in Initializing since more than 5sec")
567 elif bias.stn == 5: #Ramping
568 if time.time() - bias.last_st_change < 10:
569 time.sleep(1)
570 else:
571 msg.warn("BIAS_CONTROL is in Ramping since more than 10sec")
572 bias.stop()
573 elif bias.stn == 6: #OverCurrent
574 time.sleep(0.5)
575 bias.set_zero_voltage()
576 time.sleep(0.5)
577 bias.reset_over_current_status()
578 time.sleep(0.5)
579 elif bias.stn == 8: #NotReferenced
580 time.sleep(0.5)
581 bias.set_zero_voltage()
582 time.sleep(0.5)
583 elif bias.stn == 9: # VoltageOn
584 time.sleep(0.5)
585 bias.set_zero_voltage()
586 time.sleep(0.5)
587 elif bias.stn == 10: # ExpoertMode
588 msg.fail("BIAS is in ExportMode ... wtf")
589 return False
590
591 time.sleep(delay)
592 return True
593
594def prepare_feedback( verbose = True, timeout = 10, delay = 0.2):
595 msg = MSG(verbose)
596 fb = feedback
597 if timeout == None:
598 timeout = float('inf')
599 if delay < 0.:
600 delay = 0.
601 start_time = time.time()
602
603 while not fb.stn == 6: #Connected
604 # Timeout
605 if time.time() > start_time + timeout:
606 msg.fail("Timeout in " + prepare_feedback.__name__)
607 return False
608 # Strange States
609 if (fb.stn <= 1) or (fb.stn >= 256):
610 msg.fail("FEEDBACK is in state "+fb.sts)
611 msg.fail(prepare_feedback.__name__ + "does not know how to handle this")
612 return False
613 elif fb.stn in [2,3,4,5]:
614 if time.time() - fb.last_st_change < 10:
615 time.sleep(1)
616 else:
617 msg.warn("BIAS_CONTROL is in "+fb.sts+" since more than 10sec")
618
619 elif fb.stn in [7,8,9]:
620 fb.stop()
621
622 elif fb.stn in [10,11,12]:
623 fb.enable_output(0)
624 fb.stop()
625
626 elif fb.stn == 13:
627 # maybe waiting helps
628 if time.time() - fb.last_st_change < 20:
629 time.sleep(1)
630 else:
631 msg.warn("FEEDBACK is in "+fb.sts+" since more than 20sec \n sending STOP")
632 fb.stop()
633
634 time.sleep(delay)
635 return True
636
637
638
639#==============================================================================
640# standard watchdog:
641def std_bias_watchdog( state, verbose=True ):
642 msg = MSG(verbose)
643 if state == 1: #Disconnected
644 msg.warn("BIAS just DISCONNECTED .. trying to reconnect")
645 msg.warn("current runnumber:"+fad_control.runnumber() )
646 bias.reconnect()
647 time.sleep(1)
648 bias.set_zero_voltage()
649 # TODO: This should work, but it is stupid.
650 bias.reset_over_current_status()
651 bias.reset_over_current_status()
652 bias.set_zero_voltage()
653 bias.reset_over_current_status()
654 bias.set_global_dac(1)
655
656# create introduction:
657class INTRO(object):
658 def __init__(self):
659 # nothing to do
660 pass
661
662 def __repr__(self):
663 print "welcome to PyDimCtrl V0.1:"
664 print "--------------------------"
665 print "If the hardware is ON but not all the software is connected try:"
666 print " Connect()"
667 print "If all the programs are already up and running, you might want to"
668 print " TakeDrsAmplitudeCalibration(roi=1024) or "
669 print " TakeDrsAmplitudeCalibration(roi=300)"
670 print " i.e. ped, gain, trigger_offset + extra ped(for X-check)"
671 print " TakeDrsTimeCalibration( type='upshifted' ) or "
672 print " TakeDrsTimeCalibration( type='old' ) or "
673 print " "
674 print "In case you would like to ramp up the Bias voltage try:"
675 print " VoltageOn( mode='temperature' ) or"
676 print " VoltageOn( mode='current' ) or"
677 print " VoltageOn( mode='light_pulser' )"
678 print "Switch off with:"
679 print " VoltageOff()"
680 print ""
681 print "In case you would like a Watchdog to check, if the Voltage is up"
682 print "and the BiasCrate is not disconnected, then try:"
683 print " VoltageOn( mode='--see above--', Watchdog=True )"
684 print "PLEASE NOTE: The Watchdog will monitor the State of BIAS_CONTROL"
685 print "and in case of a connection loss, it will: "
686 print " * print a warning"
687 print " * issue a the RECONNECT command"
688 print " * issue SET_ZERO_VOLTAGE - since this is the only way to get referenced"
689 print " * Ramp up again, using the same settings as before"
690 print " * possible OverCurrent situation will be treated as usual"
691 print "The Watchdog will not react on any other state change, like OverCurrent or VoltageOff"
692 print ""
693 print ""
694 return ""
695
696
697intro = INTRO()
698
699if __name__ == '__main__':
700 print "welcome:"
701 print " type: intro"
702 print " for a short introduction"
703
704
705 #IsReadyForDataTaking()
706 #TrackSource( Shift=0.6, Angle=50, SrcName='Crab', verbose=True)
707 #WaitForTracking( CalmDownTime = 30, verbose = True)
708 #TakeDataRun()
709 #StopTracking()
Note: See TracBrowser for help on using the repository browser.