1 """Panel to control and orchestrate simulation and protocols.
2
3 Copyright 2008-2014 Research Foundation State University of New York
4 This file is part of QUB Express.
5
6 QUB Express is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 QUB Express is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License,
17 named LICENSE.txt, in the QUB Express program directory. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 """
21
22 from itertools import izip
23 from qubx.model import *
24 from qubx.faces import *
25 from qubx.GTK import pack_item, pack_space, pack_hsep, pack_vsep, pack_label, pack_button, pack_check, pack_radio, pack_scrolled, build_menuitem
26 from qubx.toolspace import *
27 from qubx.simulation_protocol import *
28 from qubx.table import *
29 from qubx.tableGTK import *
30 from qubx.task import *
31 import qubx.pyenv
32 from numpy import *
33 from qubx.maths import *
34 import qubx.settings
35 from qubx.settings import Property, Propertied
36 import qubx.settingsGTK
37 import qubx.fast.filter
38 import qubx.fast.simulate
39
40 CONSTBORDER=2
41 COLOR_CONSTBG=('qubx.simulate.constbg', (0,0,1,.2))
42 ColorInfo[COLOR_CONSTBG[0]].label = 'Simulation constants background'
43 COLOR_CONSTBORDER=('qubx.simulate.constborder', (1,1,1,.6))
44 ColorInfo[COLOR_CONSTBORDER[0]].label = 'Simulation constants border'
45
46 Tools = ToolRegistry()
50 """
51 Provides a rotating collection of "scratch" trees for simulated data.
52 Was going to serve files on disk, but it was crash-prone."""
53
55 self.path = path
56 if not os.path.exists(path):
57 os.makedirs(path)
58 self.avail = Queue.Queue()
59 self.ready = Queue.Queue()
60 self.next_i = 0
76
77 self.ready.put(tree)
79 return self.ready.get()
82
86 """Returns a shape of the appropriate class from L{qubx.simulation_protocol}, initialized from a L{qubx.tree.Node_numpy}."""
87 allowed = [ShapeClass.name for ShapeClass in Shapes]
88 return Shapes[allowed.index(str(node.data))](node)
89
92 """Panel to control and orchestrate HMM simulation.
93
94 @ivar sampling_khz: sampling rate in kHz
95 @ivar duration: length of each segment, in milliseconds
96 @ivar segment_count: number of segments to simulate
97 @ivar usePeq: (bool) True to start at equilibrium; False to start at model.states:Pr
98 @ivar reset_prob: (bool) False to carry over occupancy probability across segments
99 @ivar shuffle: generate stimulus segments out of order
100 @ivar force_Q: use Q matrix method (instead of A matrix) even if it would be slower
101 @ivar continuous: (bool) True to start a new simulation as soon as the last one ends
102 @ivar paused: (bool) True to prevent re-simulation
103 @ivar robot: L{qubx.task.Robot} - worker thread
104 @ivar protocol: L{SimProtocol}
105 @ivar constants: L{SimConstants}
106 @ivar OnFinish: L{WeakEvent}()
107 """
108
109 __explore_featured = ['sampling_khz', 'duration', 'segment_count', 'usePeq', 'reset_prob', 'shuffle', 'force_Q', 'continuous', 'paused',
110 'robot', 'protocol', 'constants', 'files', 'noise', 'model', 'update', 'get_stimulus', 'OnFinish']
111
112 - def __init__(self, name='Simulation', global_name='QubX.Simulation'):
113 Face.__init__(self, name, global_name)
114 self.__ref = Reffer()
115 self.__state = qubx.settings.SettingsMgr['Simulation'].active
116 qubx.settings.SettingsMgr['Simulation'].OnSet = self.__ref(self.__onSettings)
117 self.__sampling = self.__state['Sampling kHz']
118 if not self.__sampling.data:
119 self.__sampling.data = 10.0
120 self.__segcount = self.__state['RepeatTimes']
121 if not self.__segcount.data:
122 self.__segcount.data = 1
123 self.__duration = self.__state['Duration']
124 if not self.__duration.data:
125 self.__duration.data = 1000.0
126 self.__usePeq = self.__state['Use Peq']
127 if not self.__usePeq.data:
128 self.__usePeq.data = 0
129 self.__reset_prob = self.__state['reset_prob']
130 if not self.__reset_prob.data:
131 self.__reset_prob.data = 1
132 self.__shuffle = self.__state['Randomize']
133 if not self.__shuffle.data:
134 self.__shuffle.data = 0
135 self.__force_Q = self.__state['ForceQ']
136 if not self.__force_Q.data:
137 self.__force_Q.data = 0
138 self.__really_big = self.__state['ReallyBig']
139 if not self.__really_big.data:
140 self.__really_big.data = 100000000
141 self.__continuous = self.__paused = False
142 self.files = SimuFiles( os.path.join(qubx.pyenv.env.folder, 'Simulations') )
143 self.QubX = qubx.pyenv.env.globals['QubX']
144 self.QubX.OnQuit += self.__ref(self.__onQuit)
145 self.robot = Robot('Simulate')
146 self.robot.OnException += self.__ref(self.__onException)
147 self.robot.OnInterrupt += self.__ref(self.__onInterrupt)
148 self.models = self.QubX.Models
149 self.models.OnSwitch += self.__ref(self.__onSwitchModel)
150 self.datas = self.QubX.Data
151 self.datas.OnSwitch += self.__ref(self.__onSwitchData)
152 self.__model = None
153 self.__update_on_show = True
154 self.__serial = 0
155 self.noise = SimNoiseDialog()
156 self.noise.OnChangeEnabled += self.__ref(self.__onChangeNoiseEnabled)
157 self.noise.OnChange += self.__ref(self.__onChangeNoise)
158 line = pack_item(gtk.HBox(), self)
159 pack_label('Sampling (kHz):', line)
160 self.txtSampling = pack_item(NumEntry(self.__sampling.data[0], acceptFloatGreaterThan(0.0), width_chars=5), line)
161 self.txtSampling.OnChange += self.__ref(self.__onChangeSampling)
162 pack_label('Segments:', line)
163 self.txtSegments = pack_item(NumEntry(self.__segcount.data[0], acceptIntGreaterThan(0), width_chars=4), line)
164 self.txtSegments.OnChange += self.__ref(self.__onChangeSegCount)
165 pack_label('Duration (ms):', line)
166 self.txtDuration = pack_item(NumEntry(self.__duration.data[0], acceptFloatGreaterThan(0.0), width_chars=7), line)
167 self.txtDuration.OnChange += self.__ref(self.__onChangeDuration)
168 self.btnAddStimulus = pack_button('Add Stimulus', line, at_end=True, on_click=self.__onClickAddStimulus)
169 self.btnNoise = pack_button('Edit noise...', line, at_end=True, on_click=self.__onClickEditNoise)
170 self.chkNoise = pack_check('Extra noise', line, at_end=True, active=self.noise.enabled, on_toggle=self.__onToggleNoise)
171 self.protocol = SimProtocol()
172 self.constants = pack_item(SimConstants(self.protocol), self)
173 self.constants.OnSet += self.__ref(self.__onEditModel)
174 self.constants.update(self.models.file)
175 pack_item(self.protocol, self, expand=True)
176 self.protocol.protocol = self.__state
177 self.protocol.segment_count = self.segment_count
178 self.protocol.OnEdit += self.__ref(self.__onEditProtocol)
179 line = pack_item(gtk.HBox(), self, at_end=True)
180 self.chkPeq = pack_check('Start at equilibrium', line, active=bool(self.__usePeq.data[0]), on_toggle=self.__onTogglePeq)
181 self.chkResetProb = pack_check('Reset entry prob. each segment', line, active=bool(self.__reset_prob.data[0]), on_toggle=self.__onToggleResetProb)
182
183 self.chkForceQ = pack_check('force Q', line, active=bool(self.__force_Q.data[0]), on_toggle=self.__onToggleForceQ)
184 self.chkForceQ.set_tooltip_text('By default, QUB Express may use the A matrix method for some models because it can be faster')
185 self.chkContinuous = pack_check('Non-stop', line, on_toggle=self.__onToggleContinuous)
186 self.chkPause = pack_check('pause', line, on_toggle=self.__onTogglePause)
187 self.preset_additions = []
188 self.mnuPresets = pack_item(qubx.settingsGTK.PresetsMenu('Simulation', qubx.pyenv.env.globals['QubX'].appname, additions=self.preset_additions),
189 line, at_end=True)
190 gobject.timeout_add(20000, self.refresh_preset_additions)
191 self.__onEditProtocol()
192 self.OnFinish = WeakEvent()
197 sampling_khz = property(lambda self: self.__sampling.data[0], lambda self, x: self.set_sampling_khz(x))
202 duration = property(lambda self: self.__duration.data[0], lambda self, x: self.set_duration(x))
208 segment_count = property(lambda self: self.__segcount.data[0], lambda self, x: self.set_segment_count(x))
213 usePeq = property(lambda self: bool(self.__usePeq.data[0]), lambda self, x: self.set_usePeq(x))
218 reset_prob = property(lambda self: bool(self.__reset_prob.data[0]), lambda self, x: self.set_reset_prob(x))
220
221 self.__shuffle.data[0] = int(x)
222 self.update()
223 shuffle = property(lambda self: bool(self.__shuffle.data[0]), lambda self, x: self.set_shuffle(x))
228 force_Q = property(lambda self: bool(self.__force_Q.data[0]), lambda self, x: self.set_force_Q(x))
234 continuous = property(lambda self: self.__continuous, lambda self, x: self.set_continuous(x))
240 paused = property(lambda self: self.__paused, lambda self, x: self.set_paused(x))
249
250 additions = self.preset_additions
251 additions[:] = []
252 tools = Tools.by_cat['presets']
253 for label in sorted(tools.keys()):
254 action, tool_class = tools[label]
255 additions.append((label, action))
261 traceback.print_exception(typ, val, trace)
340 if (self.datas.index == 0) and self.__update_on_show:
341 self.update()
343 if x == self.__model: return
344 if self.__model:
345 for event in (self.__model.states.OnInsert,
346 self.__model.states.OnRemoved,
347 self.__model.rates.OnInsert,
348 self.__model.rates.OnRemoved,
349 self.__model.classes.OnSet,
350 self.__model.OnSetChannelCount,
351 self.__model.OnSetIeqFv,
352 self.__model.OnSetVRev):
353 event -= self.__ref(self.__onEditModel)
354 self.__model.rates.OnSet -= self.__ref(self.__onSetRate)
355 self.__model.states.OnSet -= self.__ref(self.__onSetState)
356 self.__model = x
357 if self.__model:
358 for event in (self.__model.states.OnInsert,
359 self.__model.states.OnRemoved,
360 self.__model.rates.OnInsert,
361 self.__model.rates.OnRemoved,
362 self.__model.classes.OnSet,
363 self.__model.OnSetChannelCount,
364 self.__model.OnSetIeqFv,
365 self.__model.OnSetVRev):
366 event += self.__ref(self.__onEditModel)
367 self.__model.states.OnSet += self.__ref(self.__onSetState)
368 self.__model.rates.OnSet += self.__ref(self.__onSetRate)
369 self.__onEditModel()
370 model = property(lambda self: self.__model, lambda self, x: self.set_model(x))
372 self.constants.update(self.models.file)
373 if self.datas.index == 0:
374 self.update()
375 else:
376 self.__update_on_show = True
377 - def __onSetRate(self, index, field, val, prev, undoing):
381 if field in ('From', 'To', 'Class', 'Pr'):
382 self.__onEditModel()
384 if n >= self.__really_big.data[0]:
385 'really?'
386 nBytes = n*4
387 nK = nBytes / 1024
388 nM = nK / 1024
389 nG = nM / 1024
390 if nG:
391 s = "%.1f GiB" % (nG + (nM-nG*1024)/1024.0)
392 else:
393 s = "%.1f MiB" % (nM + (nK-nM*1024)/1024.0)
394 response = qubx.pyenvGTK.show_message("Requested simulation is really big (%s), continue?" % s,
395 gtk.BUTTONS_OK_CANCEL, "QUB Express - Simulation - Warning", self.parent_window)
396 if response != gtk.RESPONSE_OK:
397 'not'
398 receiver(False)
399 return
400 else:
401 'really.'
402 if self.__really_big.data[0] < 2**30:
403 self.__really_big.data[0] = 2 * self.__really_big.data[0]
404 else:
405 self.__really_big.data[0] = 2**30
406 receiver(True)
407 return
409 """Requests re-simulation."""
410 if not self.paused:
411 self.__update_on_show = False
412 self.__serial += 1
413 self.robot.do(self.robot_try_update, self.__serial)
438 """Simulates on the worker thread and/or pool."""
439 with self.robot.main_hold:
440 if (not self.model) or (not self.model.states): return
441 simfile = self.datas.views[0].file
442 vars, counts, stim_segs = self.get_stimulus()
443 v_signal = self.model.IeqFv and vars.index('Voltage') or 0
444 if self.shuffle:
445 re_ix = range(len(counts))
446 random.shuffle(re_ix)
447 counts = [counts[i] for i in re_ix]
448 stim_segs = [stim_segs[i] for i in re_ix]
449 fastmodel = ModelToFast(self.model, vars)
450 self.robot.fastmodel = fastmodel
451 usePeq = self.usePeq
452 sampling_sec = 1e-3 / self.sampling_khz
453 Nsegment = self.segment_count
454 Nchannel = self.model.channelCount
455 vary = [chan.label for chan in self.protocol.channels]
456 reset_prob = self.reset_prob
457 force_Q = self.force_Q
458 noise_enabled, baselineFluct, baselineFluctN, baselineFluctLifetime, baselineFluctMaxAmp, \
459 baselineDrift, baselineDriftSlope, baselineDriftIntercept, \
460 baselinePeriodic, baselinePeriodicFreq, baselinePeriodicPhase, baselinePeriodicAmp, \
461 baselineJumps, baselineJumpsAmpStd, \
462 baselineJumpsFastW, baselineJumpsFast, baselineJumpsSlow, baselineJumpsLifetime, \
463 baselineLGM, baselineLGM2State, baselineLGMProcStd, baselineLGMX0, baselineLGMX0p = \
464 self.noise.enabled, self.noise.baselineFluct, self.noise.baselineFluctN, self.noise.baselineFluctLifetime, self.noise.baselineFluctMaxAmp, \
465 self.noise.baselineDrift, self.noise.baselineDriftSlope, self.noise.baselineDriftIntercept, \
466 self.noise.baselinePeriodic, self.noise.baselinePeriodicFreq, self.noise.baselinePeriodicPhase, self.noise.baselinePeriodicAmp, \
467 self.noise.baselineJumps, self.noise.baselineJumpsAmpStd, \
468 self.noise.baselineJumpsFastW, self.noise.baselineJumpsFast, self.noise.baselineJumpsSlow, self.noise.baselineJumpsLifetime, \
469 self.noise.baselineLGM, self.noise.baselineLGM2State, self.noise.baselineLGMProcStd, self.noise.baselineLGMX0, self.noise.baselineLGMX0p
470 n = numpy.sum(counts)
471 if not self.robot.gui_call_recv(self.__check_really_big, n, self.robot)[0]:
472 print 'False!'
473 return
474 tree = self.files.get_fresh('QubData')
475 tree_done = False
476 try:
477 tree["Sampling"].data = sampling_sec
478 tree["SignalCount"].data = 1+len(vary)
479 tree_signals = tree["Signals"]
480 sig = tree_signals["Signal"]
481 sig["Name"].data = 'Current'
482 sig["Units"].data = 'pA'
483 for name in vary:
484 sig = tree_signals.append("Signal")
485 sig["Name"].data = name
486 sig['Units'].data = (name == 'Voltage') and 'mV' or ''
487 tree_segs = tree["Segments"]
488 tree_idl = tree["IdealSignals"]
489 sig_idl = tree_idl["IdealSignal"]
490 sig_idl["SignalIndex"].data = 0
491 sig_idl_segs = sig_idl['Segments']
492 seg_idl = qubx.tree.NullNode()
493 seg_ix = 0
494 sam_ix = 0
495 samples_out = []
496 seg = qubx.tree.NullNode()
497 for ct, stim in izip(counts, stim_segs):
498 seg = tree_segs.insert("Segment", seg)
499 samples = seg["Signals"]["Signal"]["Samples"]
500 samples.data.setup(qubx.tree.QTR_TYPE_FLOAT, ct, 1)
501 samples_out.append(samples.storage.data)
502 for c, nm in enumerate(vary):
503 samples = seg["Signals"].append("Signal")["Samples"]
504 samples.data.setup(qubx.tree.QTR_TYPE_FLOAT, ct, 1)
505 samples.storage.data[:,0] = stim[c-len(vary)]
506 seg["SampleCount"].data = ct
507
508 if noise_enabled:
509 qubx.fast.simulate.simulate_baselines(samples_out, sampling_sec,
510 baselineFluct, baselineFluctN, baselineFluctLifetime, baselineFluctMaxAmp,
511 baselineDrift, baselineDriftSlope, baselineDriftIntercept,
512 baselinePeriodic, baselinePeriodicFreq, baselinePeriodicPhase, baselinePeriodicAmp,
513 baselineJumps, baselineJumpsAmpStd,
514 baselineJumpsFastW, baselineJumpsFast, baselineJumpsSlow, baselineJumpsLifetime,
515 baselineLGM, baselineLGM2State, baselineLGMProcStd, baselineLGMX0, baselineLGMX0p)
516
517 start_states = zeros(shape=(Nchannel,), dtype='int32')
518 start_states -= 1
519 if reset_prob:
520 results = qubx.fast.simulate.simulate(fastmodel, start_states, True, usePeq, v_signal, sampling_sec, stim_segs, counts, samples_out, self.__robot_on_status, force_Q)
521 else:
522 results = []
523 for ss, ct, sam in izip(stim_segs, counts, samples_out):
524 results.append(qubx.fast.simulate.simulate(fastmodel, start_states, True, usePeq, v_signal, sampling_sec, [ss], [ct], [sam], self.__robot_on_status, force_Q)[0])
525
526 for result, ct in izip(results, counts):
527 states, st_counts, st_amp, st_std = result
528 seg_idl = sig_idl_segs.insert("Segment", seg_idl)
529 seg_idl.data = sam_ix, sam_ix + ct - 1
530 seg_idl['amp'].data.setup(qubx.tree.QTR_TYPE_DOUBLE, fastmodel.Nclass, 1)
531 amp = seg_idl['amp'].storage.data
532 seg_idl['sd'].data.setup(qubx.tree.QTR_TYPE_DOUBLE, fastmodel.Nclass, 1)
533 sd = seg_idl['sd'].storage.data
534 for i in xrange(fastmodel.Nstate):
535 amp[fastmodel.clazz[i]] = st_amp[i]
536 sd[fastmodel.clazz[i]] = st_std[i]
537 seg_idl['DwellCount'].data = len(states)
538 seg_idl['Firsts'].data.setup(qubx.tree.QTR_TYPE_INT, len(states), 1)
539 seg_idl['Lasts'].data.setup(qubx.tree.QTR_TYPE_INT, len(states), 1)
540 seg_idl['Classes'].data.setup(qubx.tree.QTR_TYPE_INT, len(states), 1)
541 qubx.fast.simulate.make_idealized_flc(seg_idl['Firsts'].storage.data,
542 seg_idl['Lasts'].storage.data,
543 seg_idl['Classes'].storage.data,
544 fastmodel.clazz, states, st_counts, sam_ix)
545 sam_ix += ct
546 seg_ix += 1
547 tree_const = tree["Constants"]
548 for nm,val in self.constants.valudict.iteritems():
549 tree_const.insert(nm).data = val
550 tree_done = True
551 finally:
552 pass
553
554
555 if tree_done:
556 self.files.put_ready(tree)
557 gobject.idle_add(self.__swap)
559 """Displays the newly simulated data, on the gui thread."""
560 simfile = self.datas.views[0].file
561 old_tree = simfile.tree
562 simfile.tree = self.files.get_ready()
563 self.OnFinish()
564 if old_tree:
565 self.files.put_done(old_tree)
566 if (self.datas.index == 0) and self.continuous:
567 self.update()
569 try:
570 self.robot.progress = 100.0 * frac
571 return 0
572 except:
573 return -1
574
575
576 @Propertied(Property('enabled', 0, ""),
577 Property('baselineFluct', 0, ""),
578 Property('baselineFluctN', 1000, ""),
579 Property('baselineFluctLifetime', 1.0, ""),
580 Property('baselineFluctMaxAmp', 1.0, ""),
581 Property('baselineDrift', 0, ""),
582 Property('baselineDriftSlope', 0.0, ""),
583 Property('baselineDriftIntercept', 0.0, ""),
584 Property('baselinePeriodic', 0, ""),
585 Property('baselinePeriodicFreq', 60.0, ""),
586 Property('baselinePeriodicPhase', 0.0, ""),
587 Property('baselinePeriodicAmp', 1.0, ""),
588 Property('baselineJumps', 0, ""),
589 Property('baselineJumpsAmpStd', 5.0, ""),
590 Property('baselineJumpsFastW', 0.5, ""),
591 Property('baselineJumpsFast', 1e-3, ""),
592 Property('baselineJumpsSlow', 1.0, ""),
593 Property('baselineJumpsLifetime', 1.0, ""),
594 Property('baselineLGM', 0, ""),
595 Property('baselineLGM2State', 1, ""),
596 Property('baselineLGMProcStd', 1e-3, ""),
597 Property('baselineLGMX0', 0.0, ""),
598 Property('baselineLGMX0p', 0.0, ""))
600 __explore_featured = ['global_name', 'OnChange', 'OnChangeEnabled', 'run']
601 - def __init__(self, global_name="QubX.Simulation.noise"):
602 gtk.Dialog.__init__(self, 'QUB Express - Simulation - Extra Noise', qubx.global_namespace.QubX, gtk.DIALOG_MODAL,
603 buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
604 self.__ref = Reffer()
605 self.global_name = global_name
606 self.propertied_connect_settings('SimulationNoise')
607 self.set_default_response(gtk.RESPONSE_ACCEPT)
608
609 self.OnChange = WeakEvent()
610 self.OnChangeEnabled = WeakEvent()
611
612 columns = pack_item(gtk.HBox(True), self.vbox, expand=True)
613 column = pack_item(gtk.VBox(), columns, expand=True)
614 h = pack_item(gtk.HBox(), column)
615 chk = pack_check('Random fluctuations:', h)
616 self.propertied_connect_check('baselineFluct', chk)
617 h = pack_item(gtk.HBox(), column)
618 pack_space(h, x=20)
619 pack_label('Lifetime [sec]:', h)
620 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True)
621 self.propertied_connect_NumEntry('baselineFluctLifetime', txt)
622 h = pack_item(gtk.HBox(), column)
623 pack_space(h, x=20)
624 pack_label('Max amp:', h)
625 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True)
626 self.propertied_connect_NumEntry('baselineFluctMaxAmp', txt)
627 h = pack_item(gtk.HBox(), column)
628 pack_space(h, x=20)
629 pack_label('N:', h)
630 txt = pack_item(qubx.GTK.NumEntry(1, acceptIntBetween(1, 100000), '%s', width_chars=6), h, at_end=True)
631 self.propertied_connect_NumEntry('baselineFluctN', txt)
632
633 h = pack_item(gtk.HBox(), column)
634 chk = pack_check('Deterministic periodic component:', h)
635 self.propertied_connect_check('baselinePeriodic', chk)
636 h = pack_item(gtk.HBox(), column)
637 pack_space(h, x=20)
638 pack_label('Amplitude:', h)
639 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True)
640 self.propertied_connect_NumEntry('baselinePeriodicAmp', txt)
641 h = pack_item(gtk.HBox(), column)
642 pack_space(h, x=20)
643 pack_label('Frequency [Hz]:', h)
644 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.1f', width_chars=6), h, at_end=True)
645 self.propertied_connect_NumEntry('baselinePeriodicFreq', txt)
646 h = pack_item(gtk.HBox(), column)
647 pack_space(h, x=20)
648 pack_label('Phase [deg]:', h)
649 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatBetween(0.0, 360.0), '%.1f', width_chars=6), h, at_end=True)
650 self.propertied_connect_NumEntry('baselinePeriodicPhase', txt)
651
652 h = pack_item(gtk.HBox(), column)
653 chk = pack_check('Linear Gaussian model:', h)
654 self.propertied_connect_check('baselineLGM', chk)
655 h = pack_item(gtk.HBox(), column)
656 pack_space(h, x=20)
657 pack_label('Process std:', h)
658 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True)
659 self.propertied_connect_NumEntry('baselineLGMProcStd', txt)
660 h = pack_item(gtk.HBox(), column)
661 pack_space(h, x=20)
662 pack_label('x0:', h)
663 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloat, '%.3g', width_chars=6), h, at_end=True)
664 self.propertied_connect_NumEntry('baselineLGMX0', txt)
665 h = pack_item(gtk.HBox(), column)
666 pack_space(h, x=20)
667 pack_label("x0':", h)
668 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloat, '%.3g', width_chars=6), h, at_end=True)
669 self.propertied_connect_NumEntry('baselineLGMX0p', txt)
670 h = pack_item(gtk.HBox(), column)
671 pack_space(h, x=20)
672 chk = pack_check('2 state model', h)
673 self.propertied_connect_check('baselineLGM2State', chk)
674
675 column = pack_item(gtk.VBox(), columns, expand=True)
676 h = pack_item(gtk.HBox(), column)
677 chk = pack_check('Random jumps and exp recovery:', h)
678 self.propertied_connect_check('baselineJumps', chk)
679 h = pack_item(gtk.HBox(), column)
680 pack_space(h, x=20)
681 pack_label('Jump amp std:', h)
682 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThanOrEqualTo(0.0), '%.3g', width_chars=6), h, at_end=True)
683 self.propertied_connect_NumEntry('baselineJumpsAmpStd', txt)
684 h = pack_item(gtk.HBox(), column)
685 pack_space(h, x=20)
686 pack_label('Lifetime [sec]:', h)
687 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True)
688 self.propertied_connect_NumEntry('baselineJumpsLifetime', txt)
689 h = pack_item(gtk.HBox(), column)
690 pack_space(h, x=20)
691 pack_label('Fast w [0..1]:', h)
692 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatBetween(0.0, 1.0), '%.3f', width_chars=6), h, at_end=True)
693 self.propertied_connect_NumEntry('baselineJumpsFastW', txt)
694 h = pack_item(gtk.HBox(), column)
695 pack_space(h, x=20)
696 pack_label('Fast rec [sec]:', h)
697 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True)
698 self.propertied_connect_NumEntry('baselineJumpsFast', txt)
699 h = pack_item(gtk.HBox(), column)
700 pack_space(h, x=20)
701 pack_label('Slow rec [sec]:', h)
702 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True)
703 self.propertied_connect_NumEntry('baselineJumpsSlow', txt)
704
705 h = pack_item(gtk.HBox(), column)
706 chk = pack_check('Deterministic linear drift:', h)
707 self.propertied_connect_check('baselineDrift', chk)
708 h = pack_item(gtk.HBox(), column)
709 pack_space(h, x=20)
710 pack_label('Slope:', h)
711 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloat, '%.3g', width_chars=6), h, at_end=True)
712 self.propertied_connect_NumEntry('baselineDriftSlope', txt)
713 h = pack_item(gtk.HBox(), column)
714 pack_space(h, x=20)
715 pack_label('Intercept:', h)
716 txt = pack_item(qubx.GTK.NumEntry(1.0, acceptFloat, '%.3g', width_chars=6), h, at_end=True)
717 self.propertied_connect_NumEntry('baselineDriftIntercept', txt)
718
719 pack_space(column, y=60)
720 h = pack_item(gtk.HBox(), column)
721 pack_item(qubx.settingsGTK.PresetsMenu('SimulationNoise', qubx.global_namespace.QubX.appname), h, at_end=True)
722
729
735
739 """Displays, edits and generates stimulus protocols.
740
741 @ivar segment_count: number of segments
742 @ivar channel_count: number of varying signals
743 @ivar protocol: L{qubx.tree.Node_numpy}, basically U{QPF<http://www.qub.buffalo.edu/qubdoc/files/qpf.html>}
744 """
745
746 __explore_featured = ['segment_count', 'channel_count', 'protocol', 'channels', 'duration', 'zoom', 'OnEdit', 'shape_properties',
747 'add_channel', 'remove_channel', 'set_proto', 'update_properties', 'get_sample_counts', 'get_samples',
748 'reset', 'data_signal']
749
775 segment_count = property(lambda self: self.__segment_count, lambda self, x: self.set_segment_count(x))
796 channel_count = property(lambda self: self.__channel_count, lambda self, x: self.set_channel_count(x))
829 protocol = property(lambda self: self.__proto, lambda self, x: self.set_proto(x))
831 """Updates the protocol to match the L{qubx.tree.Node_numpy} updates."""
832 if updates.find('ChannelCount').data:
833 self.channel_count = updates['ChannelCount'].data[0]
834 for c, ch in enumerate(qubx.tree.children(updates['Stages']['Stage'], 'Channel')):
835 if ch.find('Label'):
836 self.channels[c].label = str(ch['Label'].data)
837 while self.channels[c].shapes:
838 self.channels[c].remove_shape(0)
839 for s, sh in enumerate(qubx.tree.children(ch, 'Shape')):
840 self.channels[c].append_shape(BuildShape(sh.clone()))
841 self.OnEdit()
861 chan_ix = self.channels.index(channel)
862 shape_ix = channel.shapes.index(shape) if (not (shape is None)) else -1
863 if shape_ix >= 0:
864 qubx.pyenv.env.OnScriptable('QubX.Simulation.protocol.channels[%i].select_shape(%i)' % (chan_ix, shape_ix))
865 for chan in self.channels:
866 if chan != channel:
867 chan.active_i = -1
868 self.shape_properties.shape = shape.properties if shape else None
869 About = qubx.pyenv.env.globals['QubX'].About
870 if shape:
871 try:
872 About.show_face('Shape')
873 except:
874 pass
876 dlg = gtk.MessageDialog(None, flags=gtk.DIALOG_MODAL, buttons=gtk.BUTTONS_OK_CANCEL,
877 message_format = 'Remove the entire stimulus signal "%s"?'%channel.label)
878 if dlg.run() == gtk.RESPONSE_OK:
879 chan_ix = self.channels.index(channel)
880 qubx.pyenv.env.OnScriptable('QubX.Simulation.protocol.remove_channel(%i)' % chan_ix)
881 self.remove_channel(chan_ix)
882 dlg.destroy()
908
943
944
945 COLOR_SIG_NAME = ('simulate.signal.name', (1, 1, 1, 1))
946 ColorInfo[COLOR_SIG_NAME[0]].label = 'Simulation signal names'
947 COLOR_SIG_VAL = ('simulate.signal.value', LAYER_FG[1])
948 ColorInfo[COLOR_SIG_VAL[0]].label = 'Simulation signal values'
949 COLOR_SIG_VARY = ('simulate.signal.vary', (1, .3, .3, 1))
950 ColorInfo[COLOR_SIG_VARY[0]].label = 'Simulation signal vary'
951 COLOR_SIG_REM = ('simulate.signal.remove', (1, .2, .2, 1))
952 ColorInfo[COLOR_SIG_REM[0]].label = 'Simulation signal remove'
953 COLOR_SHAPE_POPUP = ('simulate.shape.popup', (0, .8, 0, .9))
954 ColorInfo[COLOR_SHAPE_POPUP[0]].label = 'Simulation signal menu'
957 """One signal in a L{SimProtocol}.
958
959 @ivar zoom: the shared L{ZoomBar} among all signals
960 @ivar channel: L{qubx.tree.Node_numpy} (sub-tree) describing this signal
961 @ivar segment_count: total number of segments
962 @ivar active_i: index of highlighted shape
963 @ivar group_duration: all signals display as being this long, together
964 @ivar OnEdit: L{WeakEvent}C{(SimProtoChannel)} when anything changes
965 @ivar OnSelect: L{WeakEvent}C{(SimProtoChannel, Shape)} when a shape is highlighted
966 @ivar OnRemove: L{WeakEvent}C{(SimProtoChannel)} when a shape is deleted
967 """
968
969 __explore_featured = ['zoom', 'channel', 'segment_count', 'active_i', 'group_duration', 'OnEdit', 'OnSelect', 'OnRemove',
970 'duration', 'shapes', 'shape_bounds', 'label', 'filter_kHz',
971 'shape_index', 'remove_shape', 'select_shape', 'insert_shape', 'insert_shape_dict', 'append_shape', 'move_shape',
972 'copy_shape_to_clipboard', 'paste_shape_from_clipboard', 'get_duration', 'update_duration', 'count',
973 'generate_into']
974
976 self.rep_samples = Product(self.__get_rep_samples)
977 ToolSpace.__init__(self)
978 self.__ref = Reffer()
979 self.zoom = zoom
980 self.__ref = Reffer()
981 self.appearance.OnSetFontSize += self.__ref(self.__onSetFontSize)
982 self.set_size_request(100, 6*self.appearance.emsize)
983 self.__channel = None
984 self.__segment_count = 0
985 self.__active_i = -1
986 self.duration = 0.0
987 self.__group_duration = 1.0
988 self.shapes = []
989 self.shape_bounds = []
990 self.OnDraw += self.__ref(self.__onDraw)
991 self.tool = SimProtoPicker()
992 self.OnEdit = WeakEvent()
993 self.OnSelect = WeakEvent()
994 self.OnRemove = WeakEvent()
995
996 self.layName = Layer(0, 0, 24, 2)
997 self.subName = SubLayer_Label('', 0, 0, x=1, y=0, w=16, h=2, color=COLOR_SIG_NAME,
998 action=self.__ref(self.__onClickName),
999 tooltip="click to rename")
1000 self.layName.add_sublayer(self.subName)
1001 self.subFilter = SubLayer_Label('F:none', 0, 0, x=18, y=0, w=6, h=2, color=COLOR_SIG_NAME,
1002 action=self.__ref(self.__onClickFilter),
1003 tooltip="Low-pass filter; click to edit")
1004 self.layName.add_sublayer(self.subFilter)
1005 self.add_layer(self.layName)
1006 x = 25
1007 def SubLayer_Shape(ShapeClass):
1008 return SubLayer_Label(ShapeClass.name, 0, 0, x=1, y=0, w=6, h=2,
1009 action=self.__ref(lambda x,y,e: self.__onClickShape(ShapeClass)))
1010 for shape in Shapes:
1011 layer = Layer(x, 0, 8, 2)
1012 layer.add_sublayer(SubLayer_Shape(shape))
1013 self.add_layer(layer)
1014 x += 9
1015 self.layRem = Layer(-2, 0, 2, 2)
1016 self.subRem = SubLayer_Label('X', 0, 0, x=0, y=0, w=2, h=2, hover_color=COLOR_SIG_REM,
1017 action=self.__ref(lambda x,y,e: gobject.idle_add(self.OnRemove, self)))
1018 self.layRem.add_sublayer(self.subRem)
1019 self.add_layer(self.layRem)
1020 self.layMenu = Layer(0, 2, 2, 2)
1021 self.subMenu = SubLayer_Popup(x=0, y=0, w=2, h=2, color=COLOR_SHAPE_POPUP, popup=self.tool.popup, on_popup=self.__ref(self.tool.on_popup))
1022 self.layMenu.add_sublayer(self.subMenu)
1023 - def redraw_canvas(self, invalid=True, immediately=False, invalid_layers=False):
1028 if self.__channel:
1029 self.__channel['Label'].data = x
1030 self.subName.label = x
1031 label = property(lambda self: self.subName.label, lambda self, x: self.set_label(x))
1036 filter_kHz = property(lambda self: self.__channel['filter_kHz'].data[0], lambda self, x: self.set_filter_kHz(x))
1040 group_duration = property(lambda self: self.__group_duration, lambda self, x: self.set_group_duration(x))
1059 channel = property(lambda self: self.__channel, lambda self, x: self.set_channel(x))
1064 segment_count = property(lambda self: self.__segment_count, lambda self, x: self.set_segment_count(x))
1068 active_i = property(lambda self: self.__active_i, lambda self, x: self.set_active_i(x))
1070 """Returns the index of the shape at time x; -1: before; len(shapes): after"""
1071 if x < 0: return -1
1072 for i, bounds in enumerate(self.shape_bounds):
1073 f, l = bounds
1074 if f <= x <= l:
1075 return i
1076 return len(self.shape_bounds)
1078 """Deletes the i'th shape."""
1079 QubX = qubx.pyenv.env.globals['QubX']
1080 chan_ix = QubX.Simulation.protocol.channels.index(self)
1081 qubx.pyenv.env.OnScriptable('QubX.Simulation.protocol.channels[%i].remove_shape(%i)' % (chan_ix, i))
1082 reselect = self.__active_i == i
1083 self.channel.remove(self.shapes[i].properties)
1084 del self.shapes[i]
1085 self.update_duration()
1086 if reselect:
1087 gobject.idle_add(self.select_shape, max(0, i-1))
1088 self.OnEdit(self)
1090 """Highlights the i'th shape."""
1091 self.__active_i = min(i, len(self.shapes)-1)
1092 if 0 <= self.__active_i < len(self.shapes):
1093 self.OnSelect(self, self.shapes[self.__active_i])
1094 if not (self.layMenu in self._layers):
1095 self.add_layer(self.layMenu)
1096 else:
1097 self.OnSelect(self, None)
1098 if self.layMenu in self._layers:
1099 self.remove_layer(self.layMenu)
1100 self.redraw_canvas()
1114 """Appends a shape at the end."""
1115 self.insert_shape(len(self.shapes), shape)
1124 """Moves the shape at index i to index j."""
1125 if i == j: return
1126 QubX = qubx.pyenv.env.globals['QubX']
1127 chan_ix = QubX.Simulation.protocol.channels.index(self)
1128 qubx.pyenv.env.OnScriptable('QubX.Simulation.protocol.channels[%i].move_shape(%i, %i)' % (chan_ix, i, j))
1129 self.channel.remove(self.shapes[i].properties)
1130 self.__insert_shape_at(j, self.shapes[i].properties)
1131 self.shapes[i], self.shapes[j] = self.shapes[j], self.shapes[i]
1132 if i == self.__active_i:
1133 self.__active_i = j
1134 elif j == self.__active_i:
1135 self.__active_i = i
1136 self.redraw_canvas()
1146 """Returns the total duration of this signal, for segment number C{rep}."""
1147 dur = 0.0
1148 for shape in self.shapes:
1149 dur += shape.duration(rep)
1150 return dur
1152 """Recalculates self.duration, using the last segment as rep."""
1153 self.duration = 0.0
1154 for shape in self.shapes:
1155 self.duration += shape.duration(self.__segment_count-1)
1156 - def count(self, sampling):
1157 """Returns the number of samples in this signal, current segment."""
1158 return int(round(self.duration / sampling))
1159 - def generate_into(self, node, rep, first, last, sampling, shape_bounds=None):
1160 """Fills node.data with sampled stimulus."""
1161 t_start = first * sampling
1162 t = 0.0
1163 i = 0
1164 start = 0
1165 while (t < t_start) and (i < len(self.shapes)):
1166 shape = self.shapes[i]
1167 rem_samples = int(round((t_start - t) / sampling))
1168 if not rem_samples: break
1169 n = shape.count(t, sampling, rep)
1170 if rem_samples < n:
1171 t += rem_samples * sampling
1172 start = rem_samples
1173 else:
1174 if not (shape_bounds is None): shape_bounds.append( (-1, -1) )
1175 t += shape.duration(rep)
1176 i += 1
1177 start = 0
1178 p = 0
1179 arr_size = last-first+1
1180 while (i < len(self.shapes)) and (p < arr_size):
1181 shape = self.shapes[i]
1182 shape.setup()
1183 n = shape.count(t, sampling, rep) - start
1184 if n > 0:
1185 n = min(arr_size-p, n)
1186 if not n: break
1187 shape.generateInto(start, start+n-1, t, sampling, rep, node, p)
1188 if not (shape_bounds is None): shape_bounds.append( (p, p+n-1) )
1189 p += n
1190 t += shape.duration(rep)
1191 i += 1
1192 start = 0
1193 if not (shape_bounds is None):
1194 while i < len(self.shapes):
1195 shape_bounds.append( (arr_size+1, arr_size+1) )
1196 i += 1
1197 if p < arr_size:
1198 hold = 0.0
1199 if p > 0: hold = node.data[p-1]
1200 node.storage.data[p:arr_size,0] = hold
1201 filter_kHz = self.__channel['filter_kHz'].data[0]
1202 if filter_kHz:
1203 node.storage.data[:,:] = qubx.fast.filter.filter(node.storage.data, sampling, 1e3*filter_kHz)
1204 return p
1206 self.set_size_request(100, 6*self.appearance.emsize)
1208 dlg = NumEntryDialog('%s - Enter a variable name'%qubx.pyenv.env.globals['QubX'].appname,
1209 None, 'Model variable e.g. Voltage:',
1210 str(self.channel['Label'].data), acceptString)
1211 if gtk.RESPONSE_ACCEPT == dlg.run():
1212 QubX = qubx.pyenv.env.globals['QubX']
1213 chan_ix = QubX.Simulation.protocol.channels.index(self)
1214 qubx.pyenv.env.OnScriptable('QubX.Simulation.protocol.channels[%i].label = %s' % (chan_ix, repr(dlg.value)))
1215 self.label = dlg.value
1216 self.OnEdit(self)
1217 dlg.destroy()
1237 sampling = (self.__group_duration * 1.0 / w) / zoom
1238 t_start = self.__group_duration * start_frac
1239 i_start = int(round(t_start / sampling))
1240 rep_samples = qubx.tree.Node()
1241 one_rep = qubx.tree.NullNode()
1242 hi = -1e10
1243 lo = 1e10
1244 for rep in xrange(self.segment_count):
1245 one_rep = rep_samples.insert("rep", one_rep)
1246 one_rep.data.setup(qubx.tree.QTR_TYPE_FLOAT, w, 1)
1247 self.shape_bounds = []
1248 valid_count = self.generate_into(one_rep, rep, i_start, i_start+w-1, sampling, self.shape_bounds)
1249 one_rep['valid_count'].data = valid_count
1250 if w:
1251 arrd = one_rep.storage.data[:,0]
1252 lo = min(lo, min(arrd))
1253 hi = max(hi, max(arrd))
1254 if hi <= lo:
1255 lo, hi = lo - 1, lo + 1
1256 rep_samples['lo'].data = lo
1257 rep_samples['hi'].data = hi
1258
1259 self.__last_rep_samples = rep_samples
1260 return rep_samples
1262 if not w: return
1263 rep_samples = self.rep_samples.get_val(w, self.zoom.zoom / 100.0, self.zoom.adjustment.value / 100.0)
1264 em = self.appearance.emsize
1265 context.set_source_rgb(0,0,0)
1266 context.paint()
1267 context.translate(0, 2*em)
1268 h = float(max(1, h-2*em))
1269 context.set_line_width(3.0)
1270 lo = rep_samples['lo'].data[0]
1271 hi = rep_samples['hi'].data[0]
1272 last_rep = None
1273 for rep, arr in enumerate(qubx.tree.children(rep_samples, 'rep')):
1274 last_rep = arr
1275 if self.segment_count > 1:
1276 r,g,b = HSVtoRGB(rep*.67/self.segment_count, 1.0, 1.0)
1277 else:
1278 r,g,b = (1,1,1)
1279 context.set_source_rgba(r, g, b, .5+(rep*.5/self.segment_count))
1280 arrd = array(arr.storage.data[:,0], copy=True)
1281 arrd -= hi
1282 arrd *= - h / (hi - lo)
1283 context.move_to(0, arrd[0])
1284 for i in xrange(1, w):
1285 context.line_to(i, arrd[i])
1286 context.stroke()
1287
1288 context.set_source_rgb(1,1,1)
1289 context.set_line_width(1.0)
1290 for s in xrange(len(self.shape_bounds)):
1291 lo, hi = self.shape_bounds[s]
1292 if (lo <= w) and (hi >= 0):
1293 context.rectangle(lo+.5, 0, hi-lo+1, h)
1294 if s == self.__active_i:
1295 context.set_source_rgba(0,.6,1,.3)
1296 context.fill_preserve()
1297 context.set_source_rgb(1,1,1)
1298 context.stroke()
1299 if last_rep:
1300 valid_count = last_rep['valid_count'].data[0]
1301 if valid_count < w:
1302 context.rectangle(valid_count+1, 0, w-valid_count, h)
1303 context.set_source_rgba(.3, .3, .3, .3)
1304 context.fill()
1305 if 0 <= self.__active_i < len(self.shape_bounds):
1306 self.layMenu.x = max(0, min(self._dim[0]-6*em, self.shape_bounds[self.__active_i][1] - 2*em)) * self._layer_scale[0] / em
1307
1311 return insert
1312
1314 """Mouse interactions for L{SimProtoChannel}."""
1316 Tool.__init__(self)
1317 self.__ref = Reffer()
1318 self.mnuInsert = gtk.Menu()
1319 for shape in Shapes:
1320 build_menuitem(shape.name, InsertShape(self, self.index_for_insert, shape), menu=self.mnuInsert)
1321 self.popup = gtk.Menu()
1322 self.itemCut = build_menuitem('Cut shape', self.__ref(self.__onCut), menu=self.popup)
1323 self.itemCopy = build_menuitem('Copy shape', self.__ref(self.__onCopy), menu=self.popup)
1324 self.itemPaste = build_menuitem('Paste shape', self.__ref(self.__onPaste), menu=self.popup)
1325 self.itemInsert = build_menuitem('Insert', submenu=self.mnuInsert, menu=self.popup)
1326 self.itemAddToData = build_menuitem('Add signal to data file', self.__ref(self.__onAddToData), menu=self.popup)
1327 self.itemRemove = build_menuitem('Remove shape', self.__ref(self.__onRemove), menu=self.popup)
1328 self.itemRemoveChannel = build_menuitem('Remove stimulus', self.__ref(self.__onRemoveChannel), menu=self.popup)
1351 on_shape = (0 <= self.space.active_i < len(self.space.shapes))
1352 self.itemCut.set_sensitive( on_shape )
1353 self.itemCopy.set_sensitive( on_shape )
1354 self.itemPaste.set_sensitive( qubx.treeGTK.CanPaste() )
1355 self.itemRemove.set_sensitive( on_shape )
1356 if on_shape:
1357 self.__i = self.space.active_i
1358 self.__x = self.space.shape_bounds[self.space.active_i][0]
1359 return True
1361 if self.__i < 0:
1362 return 0
1363 elif self.__i >= len(self.space.shapes):
1364 return len(self.space.shapes)
1365 else:
1366 lo, hi = self.space.shape_bounds[self.__i]
1367 if (self.__x - lo) <= (hi - self.__x):
1368 return self.__i
1369 else:
1370 return self.__i + 1
1395 gobject.idle_add(self.space.OnRemove, self.space)
1396
1399
1400 ty = type(node.data[0])
1401 def accept(s):
1402 v = eval(s, qubx.pyenv.env.globals)
1403 try:
1404 return [ty(x) for x in v]
1405 except:
1406 return ty(v)
1407 return accept
1408
1411 """Panel with protocol shape properties, for QubX.About.
1412
1413 @ivar lbl: gtk.Label with the shape's name
1414 @ivar tableview: (when active) a L{qubx.table.TableView}
1415 @ivar table: (when active) temporary table of Shape's params
1416 @ivar shape: L{qubx.simulation_protocol.Shape}
1417 """
1418
1419 __explore_featured = ['scroll', 'tableview', 'shape', 'OnEdit', 'set_property']
1420
1421 - def __init__(self, name='Shape', global_name='QubX.Simulation.protocol.shape_properties'):
1433 if self.tableview:
1434 self.table.OnSelect -= self.__ref(self.__onSelect)
1435 self.table.OnSet -= self.__ref(self.__onSet)
1436 self.scroll.remove(self.tableview)
1437 self.tableview = None
1438 self.__shape = x
1439 if self.__shape:
1440 self.lbl.set_text(str(self.__shape.data))
1441 self.__accept_rows = []
1442 self.__nodes = []
1443 self.__names = []
1444 self.table = SimpleTable('Shape')
1445 self.table.add_field('Field', '', acceptNothing, str, '')
1446 self.table.add_field('Value', 1.0, self.__accept, str, '')
1447 for child in qubx.tree.children(self.__shape):
1448 if child.data:
1449 self.__nodes.append(child)
1450 self.__names.append(child.name)
1451 if child.data.type == qubx.tree.QTR_TYPE_STRING:
1452 self.table.append({'Field': child.name,
1453 'Value': str(child.data)})
1454 self.__accept_rows.append(str)
1455 else:
1456 cell = child.data[0] if (len(child.data) == 1) else child.data[:]
1457 self.table.append({'Field': child.name,
1458 'Value': cell})
1459 self.__accept_rows.append(acceptSimProtoParam(child))
1460 self.table.OnSelect += self.__ref(self.__onSelect)
1461 self.table.OnSet += self.__ref(self.__onSet)
1462 self.table.fields.remove('Index')
1463 self.table.fields.remove('Group')
1464 self.tableview = TableView(self.table)
1465 self.tableview.show()
1466 self.scroll.add(self.tableview)
1467 else:
1468 self.lbl.set_text('')
1469 shape = property(lambda self: self.__shape, lambda self, x: self.set_shape(x))
1471 if row >= 0:
1472 self.__accept_row = self.__accept_rows[row]
1474 return self.__accept_row(x)
1475 - def __onSet(self, row, field_name, val, prev, undoing):
1476 qubx.pyenv.env.OnScriptable('QubX.Simulation.protocol.shape_properties.set_property(%s, %s)' % (repr(self.__names[row]), repr(val)))
1477 self.__nodes[row].data = val
1478 self.OnEdit()
1480 self.__nodes[self.__names.index(field_name)].data = val
1481 self.OnEdit()
1482
1484 """Panel for display and editing simdata.constants; makes sure model vars are listed unless they vary in protocol."""
1485
1486 __explore_featured = ['OnSet', 'protocol', 'names', 'layers', 'labels', 'subs', 'model', 'valudict', 'values', 'update',
1487 'append_const', 'remove_const', 'set_const']
1488
1490 ToolSpace.__init__(self)
1491 self.__ref = Reffer()
1492 self.OnSet = WeakEvent()
1493 self.appearance.OnSetFontSize += self.__ref(self.__onSetFontSize)
1494 self.set_size_request(100, 4*self.appearance.emsize)
1495 self.protocol = protocol
1496 self.OnDraw += self.__ref(self.__onDraw)
1497 self.names = []
1498 self.layers = []
1499 self.labels = []
1500 self.subs = []
1501 self.model = None
1502 self.valudict = {}
1503 self.protocol.OnEdit += self.__ref(self.__onEditProtocol)
1504 self.update(self.model)
1505 layer = Layer(0, .5, 9, 3)
1506 sub = SubLayer_Label('Constants:', 0, 1, x=0, y=0, w=9, h=3)
1507 layer.add_sublayer(sub)
1508 self.add_layer(layer)
1509 self.defer_scriptable_scroll = qubx.pyenv.DeferredScriptableScroll()
1512 values = property(get_values)
1536 i = len(self.names)
1537 self.names.append(nm)
1538 self.valudict[nm] = value
1539 layer = Layer(10+i*20, .5, 19, 3, cBG=COLOR_CONSTBG, border=CONSTBORDER, cBorder=COLOR_CONSTBORDER)
1540 sub = SubLayer_Label(nm, 0, 1, x=0, y=0, w=8, h=3, color=COLOR_SIG_NAME)
1541 self.labels.insert(i, sub)
1542 layer.add_sublayer(sub)
1543 sub = SubLayer_Label('%.3g' % value, 0, 1, x=8, y=0, w=6, h=3,
1544 action=self.__ref(lambda x,y,e: self.__onClickConstant(nm)),
1545 scroll=self.__ref(lambda x,y,e,o: self.__onScrollConstant(nm, e, o)))
1546 self.subs.insert(i, sub)
1547 layer.add_sublayer(sub)
1548 sub = SubLayer_Label('[vary]', 1, 1, x=14, y=0, w=4, h=3, color=COLOR_SIG_VARY,
1549 action=self.__ref(lambda x,y,e: self.__onVaryConstant(nm)))
1550 layer.add_sublayer(sub)
1551 self.layers.insert(i, layer)
1552 self.add_layer(layer)
1553
1554
1570 self.set_size_request(100, 4*self.appearance.emsize)
1596 context.set_source_rgb(0,0,0)
1597 context.paint()
1598
1599
1600 @Propertied(Property('sweep_duration', 10000.0, 'milliseconds in a segment (sweep)'),
1601 Property('sweep_repeat', 1, 'number of times to repeat each set of levels'),
1602 Property('sampling_kHz', 10.0, 'sampling rate'),
1603 Property('name_1', 'Ligand', 'name of first stimulus variable'),
1604 Property('start_rep_1', 1, 'first stimulus stays at resting level until this rep'),
1605 Property('resting_1', 0.0, 'first stimulus resting level'),
1606 Property('prepulse_duration_1', 100.0, 'milliseconds before first stimulus rises'),
1607 Property('pulse_duration_1', 10.0, 'milliseconds at level'),
1608 Property('pulse_levels_1', [1.0], 'list of first stimulus pulse amplitudes'),
1609 Property('pulse_filter_1', False, 'True to apply low-pass Gaussian filter'),
1610 Property('pulse_filter_kHz_1', 1.0, 'low-pass filter freq'),
1611 Property('stim_2', False, 'True to simulate with two stimulus variables'),
1612 Property('name_2', 'Ligand', 'name of 2nd stimulus variable'),
1613 Property('start_rep_2', 1, '2nd stimulus stays at resting level until this rep'),
1614 Property('resting_2', 0.0, '2nd stimulus resting level'),
1615 Property('prepulse_duration_2', 100.0, 'milliseconds before 2nd stimulus rises'),
1616 Property('pulse_duration_2', 10.0, 'milliseconds at level'),
1617 Property('pulse_levels_2', [1.0], 'list of 2nd stimulus pulse amplitudes'),
1618 Property('pulse_filter_2', False, 'True to apply low-pass Gaussian filter'),
1619 Property('pulse_filter_kHz_2', 1.0, 'low-pass filter freq'),
1620 Property('channel_count', 1000, 'number of simulated channels'))
1623 gtk.Dialog.__init__(self, "Stimulus ladder wizard - QUB Express", qubx.global_namespace.QubX.Simulation.parent_window, gtk.DIALOG_MODAL)
1624 self.propertied_connect_settings('Dose-Response')
1625 self.__ref = Reffer()
1626
1627 h = pack_item(gtk.HBox(), self.vbox)
1628 pack_label('Sweep duration (ms):', h)
1629 txt = pack_item(qubx.GTK.NumEntry(self.sweep_duration, acceptFloatGreaterThan(0.0), "%.4g", width_chars=6), h)
1630 self.propertied_connect_NumEntry('sweep_duration', txt)
1631 pack_space(h, expand=True)
1632 pack_label('Repeat each sweep:', h)
1633 txt = pack_item(qubx.GTK.NumEntry(self.sweep_repeat, acceptIntGreaterThan(0), width_chars=3), h)
1634 self.propertied_connect_NumEntry('sweep_repeat', txt)
1635 pack_space(h, expand=True)
1636 pack_label('Sampling (kHz):', h)
1637 txt = pack_item(qubx.GTK.NumEntry(self.sampling_kHz, acceptFloatGreaterThan(0.0), width_chars=6), h)
1638 self.propertied_connect_NumEntry('sampling_kHz', txt)
1639 pack_space(h, expand=True)
1640
1641 pack_space(self.vbox, y=6)
1642 h = pack_item(gtk.HBox(), self.vbox)
1643 pack_label('Variable 1:', h)
1644 txt = pack_item(qubx.GTK.SuggestiveComboBox(self.name_1, str, str), h)
1645 txt.OnPopup += self.__ref(self.__onPopupName)
1646 self.propertied_connect_NumEntry('name_1', txt)
1647 pack_space(h, expand=True)
1648 pack_label('starts on repeat number:', h)
1649 txt = pack_item(qubx.GTK.NumEntry(self.start_rep_1, acceptIntGreaterThan(0), width_chars=3), h)
1650 self.propertied_connect_NumEntry('start_rep_1', txt)
1651 pack_space(h, expand=True)
1652
1653 h = pack_item(gtk.HBox(), self.vbox)
1654 pack_label('Resting level:', h)
1655 txt = pack_item(qubx.GTK.NumEntry(self.resting_1, acceptFloat, "%.4g", width_chars=6), h)
1656 self.propertied_connect_NumEntry('resting_1', txt)
1657 pack_space(h, expand=True)
1658 pack_label('Pre-pulse duration (ms):', h)
1659 txt = pack_item(qubx.GTK.NumEntry(self.prepulse_duration_1, acceptFloatGreaterThanOrEqualTo(0.0), "%.4g", width_chars=6), h)
1660 self.propertied_connect_NumEntry('prepulse_duration_1', txt)
1661 pack_space(h, expand=True)
1662 pack_label('Pulse duration (ms):', h)
1663 txt = pack_item(qubx.GTK.NumEntry(self.pulse_duration_1, acceptFloatGreaterThan(0.0), "%.4g", width_chars=6), h)
1664 self.propertied_connect_NumEntry('pulse_duration_1', txt)
1665 pack_space(h, expand=True)
1666
1667 h = pack_item(gtk.HBox(), self.vbox)
1668 pack_label('Pulse levels:', h)
1669 txt = pack_item(qubx.GTK.NumEntry(self.pulse_levels_1, acceptFloatList(), formatList("%.4g")), h, expand=True)
1670 self.propertied_connect_NumEntry('pulse_levels_1', txt)
1671 chk = pack_check('Filter:', h)
1672 self.propertied_connect_check('pulse_filter_1', chk)
1673 txt = pack_item(qubx.GTK.NumEntry(self.pulse_filter_kHz_1, acceptFloatGreaterThan(0.0), '%.2g', width_chars=5), h)
1674 self.propertied_connect_NumEntry('pulse_filter_kHz_2', txt)
1675 pack_label('[kHz]', h)
1676
1677 pack_space(self.vbox, y=6)
1678 h = pack_item(gtk.HBox(), self.vbox)
1679 chk = pack_check('Variable 2:', h)
1680 self.propertied_connect_check('stim_2', chk)
1681 self.pan2name = pack_item(gtk.HBox(), h)
1682 txt = pack_item(qubx.GTK.SuggestiveComboBox(self.name_2, str, str), self.pan2name)
1683 txt.OnPopup += self.__ref(self.__onPopupName)
1684 self.propertied_connect_NumEntry('name_2', txt)
1685 pack_space(self.pan2name, expand=True)
1686 pack_label('starts on repeat number:', self.pan2name)
1687 txt = pack_item(qubx.GTK.NumEntry(self.start_rep_2, acceptIntGreaterThan(0), width_chars=3), self.pan2name)
1688 self.propertied_connect_NumEntry('start_rep_2', txt)
1689 pack_space(self.pan2name, expand=True)
1690 self.pan2name.set_sensitive(self.stim_2)
1691
1692 self.pan2 = pack_item(gtk.VBox(), self.vbox, show=self.stim_2)
1693 h = pack_item(gtk.HBox(), self.pan2)
1694 pack_label('Resting level:', h)
1695 txt = pack_item(qubx.GTK.NumEntry(self.resting_2, acceptFloat, "%.4g", width_chars=6), h)
1696 self.propertied_connect_NumEntry('resting_2', txt)
1697 pack_space(h, expand=True)
1698 pack_label('Pre-pulse duration (ms):', h)
1699 txt = pack_item(qubx.GTK.NumEntry(self.prepulse_duration_2, acceptFloatGreaterThanOrEqualTo(0.0), "%.4g", width_chars=6), h)
1700 self.propertied_connect_NumEntry('prepulse_duration_2', txt)
1701 pack_space(h, expand=True)
1702 pack_label('Pulse duration (ms):', h)
1703 txt = pack_item(qubx.GTK.NumEntry(self.pulse_duration_2, acceptFloatGreaterThan(0.0), "%.4g", width_chars=6), h)
1704 self.propertied_connect_NumEntry('pulse_duration_2', txt)
1705 pack_space(h, expand=True)
1706
1707 h = pack_item(gtk.HBox(), self.pan2)
1708 pack_label('Pulse levels:', h)
1709 txt = pack_item(qubx.GTK.NumEntry(self.pulse_levels_2, acceptFloatList(), formatList("%.4g")), h, expand=True)
1710 self.propertied_connect_NumEntry('pulse_levels_2', txt)
1711 chk = pack_check('Filter:', h)
1712 self.propertied_connect_check('pulse_filter_2', chk)
1713 txt = pack_item(qubx.GTK.NumEntry(self.pulse_filter_kHz_2, acceptFloatGreaterThan(0.0), '%.2g', width_chars=5), h)
1714 self.propertied_connect_NumEntry('pulse_filter_kHz_2', txt)
1715 pack_label('[kHz]', h)
1716
1717 pack_space(self.vbox, y=6)
1718 h = pack_item(gtk.HBox(), self.vbox)
1719 pack_label('Channel count:', h)
1720 txt = pack_item(qubx.GTK.NumEntry(self.channel_count, acceptIntGreaterThan(0), width_chars=6), h)
1721 self.propertied_connect_NumEntry('channel_count', txt)
1722
1723 h = pack_item(gtk.HBox(), self.vbox, at_end=True)
1724 pack_item(qubx.settingsGTK.PresetsMenu('Dose-Response', parent=self), h)
1725 pack_button('OK', h, bind(self.response, gtk.RESPONSE_ACCEPT), at_end=True)
1726 pack_space(h, x=6, at_end=True)
1727 pack_button('Cancel', h, bind(self.response, gtk.RESPONSE_REJECT), at_end=True)
1740 response = gtk.Dialog.run(self)
1741 self.hide()
1742 if response == gtk.RESPONSE_ACCEPT:
1743 sim = qubx.global_namespace.QubX.Simulation
1744 sim.paused = True
1745 sim.sampling_khz = self.sampling_kHz
1746 have_2 = self.stim_2 and self.name_2 and (self.name_2 != self.name_1)
1747 sweep_count = max(len(self.pulse_levels_1), len(self.pulse_levels_2)) if have_2 else len(self.pulse_levels_1)
1748 sim.segment_count = self.sweep_repeat * sweep_count
1749 def build_stim(name, start_rep, resting, predur, dur, levels, filter, filter_kHz):
1750 postdur = self.sweep_duration - (predur + dur)
1751 sim.protocol.add_channel(name)
1752 rep_levels = []
1753 ll = resting
1754 for s in xrange(sweep_count):
1755 if s < len(levels):
1756 ll = levels[s]
1757 for r in xrange(self.sweep_repeat):
1758 if (r+1) < start_rep:
1759 rep_levels.append(resting)
1760 else:
1761 rep_levels.append(ll)
1762 chan = sim.protocol.channels[len(sim.protocol.channels)-1]
1763 chan.insert_shape_dict(0, {'postAmp factor': 1.0, 'postAmp': resting, '__data': 'Step', 'Amp factor': 1.0, 'preAmp': resting, '__root_name': 'Shape', 'Duration factor': 1.0, 'preDuration': 1e-3*predur, 'Amp incr': 0.0, 'preDuration incr': 0.0, 'preAmp incr': 0.0, 'preDuration factor': 1.0, 'postAmp incr': 0.0, 'postDuration incr': 0.0, 'Duration': 1e-3*dur, 'Amp': rep_levels, 'Duration incr': 0.0, 'postDuration factor': 1.0, 'postDuration': 1e-3*postdur, 'preAmp factor': 1.0})
1764 chan.filter_kHz = filter_kHz if filter else 0.0
1765
1766 sim.protocol.reset()
1767 if self.name_1:
1768 build_stim(self.name_1, self.start_rep_1, self.resting_1, self.prepulse_duration_1, self.pulse_duration_1, self.pulse_levels_1, self.pulse_filter_1, self.pulse_filter_kHz_1)
1769 if have_2:
1770 build_stim(self.name_2, self.start_rep_2, self.resting_2, self.prepulse_duration_2, self.pulse_duration_2, self.pulse_levels_2, self.pulse_filter_2, self.pulse_filter_kHz_2)
1771 qubx.global_namespace.QubX.Models.file.channelCount = self.channel_count
1772 sim.paused = False
1773
1774 return response
1775
1780
1781 Tools.register('presets', 'Stimulus ladder wizard...', doseresp_ladder_wizard)
1782
1783
1784
1785 @Propertied(Property('varname', 'Ligand', 'name of stimulus variable'),
1786 Property('level_rest', 0.0, 'resting level'),
1787 Property('level_cond', 1.0, 'conditioning level'),
1788 Property('level_test', 1.0, 'test level'),
1789 Property('dur_cond', 100.0, 'conditioning pulse duration'),
1790 Property('dur_test', 100.0, 'test pulse duration'),
1791 Property('dur_recovery', [1.0, 10.0, 100.0], 'list of recovery durations'),
1792 Property('channel_count', 1000, 'number of simulated channels'),
1793 Property('ratio_cond', [1.0], 'list of k0_cond/k0_rest per rate'),
1794 Property('ratio_test', [1.0], 'list of k0_test/k0_rest per rate'))
1797 gtk.Dialog.__init__(self, "Paired pulse wizard - QUB Express", qubx.global_namespace.QubX.Simulation.parent_window, gtk.DIALOG_MODAL)
1798 self.propertied_connect_settings('PairedPulse')
1799 self.__ref = Reffer()
1800 self.__installed = False
1801
1802 self.figure = pack_item(ToolSpace(), self.vbox)
1803 self.figure.set_size_request(-1, 100)
1804 self.figure.OnDraw += self.__ref(self.__onDrawFigure)
1805
1806 h = pack_item(gtk.HBox(True), self.vbox)
1807 pack_label('Variable name:', h)
1808 txt = pack_item(qubx.GTK.SuggestiveComboBox(self.varname, str, str), h)
1809 txt.OnPopup += self.__ref(self.__onPopupVarname)
1810 self.propertied_connect_NumEntry('varname', txt)
1811
1812 h = pack_item(gtk.HBox(True), self.vbox)
1813 pack_label('Resting level:', h)
1814 txt = pack_item(qubx.GTK.NumEntry(self.level_rest, acceptFloat, "%.4g", width_chars=6), h)
1815 self.propertied_connect_NumEntry('level_rest', txt)
1816 txt.OnChange += self.__ref(self.__onParamChange)
1817 h = pack_item(gtk.HBox(True), self.vbox)
1818 pack_label('Conditioning level:', h)
1819 txt = pack_item(qubx.GTK.NumEntry(self.level_cond, acceptFloat, "%.4g", width_chars=6), h)
1820 self.propertied_connect_NumEntry('level_cond', txt)
1821 txt.OnChange += self.__ref(self.__onParamChange)
1822 txt.modify_base(gtk.STATE_NORMAL, gdk.color_parse("#EEEEFF"))
1823 h = pack_item(gtk.HBox(True), self.vbox)
1824 pack_label('Test level:', h)
1825 txt = pack_item(qubx.GTK.NumEntry(self.level_test, acceptFloat, "%.4g", width_chars=6), h)
1826 self.propertied_connect_NumEntry('level_test', txt)
1827 txt.OnChange += self.__ref(self.__onParamChange)
1828 txt.modify_base(gtk.STATE_NORMAL, gdk.color_parse("#FFFFDD"))
1829 h = pack_item(gtk.HBox(True), self.vbox)
1830 pack_label('Conditioning duration [ms]:', h)
1831 txt = pack_item(qubx.GTK.NumEntry(self.dur_cond, acceptFloat, "%.4g", width_chars=6), h)
1832 self.propertied_connect_NumEntry('dur_cond', txt)
1833 txt.OnChange += self.__ref(self.__onParamChange)
1834 txt.modify_base(gtk.STATE_NORMAL, gdk.color_parse("#EEEEFF"))
1835 h = pack_item(gtk.HBox(True), self.vbox)
1836 pack_label('Test pulse duration [ms]:', h)
1837 txt = pack_item(qubx.GTK.NumEntry(self.dur_test, acceptFloat, "%.4g", width_chars=6), h)
1838 self.propertied_connect_NumEntry('dur_test', txt)
1839 txt.OnChange += self.__ref(self.__onParamChange)
1840 txt.modify_base(gtk.STATE_NORMAL, gdk.color_parse("#FFFFDD"))
1841
1842 h = pack_item(gtk.HBox(), self.vbox)
1843 pack_label('Recovery durations [ms]:', h)
1844 txt = pack_item(qubx.GTK.NumEntry(self.dur_recovery, acceptFloatList(acceptFloatGreaterThan(0.0)), formatList("%.4g")), h, expand=True)
1845 self.propertied_connect_NumEntry('dur_recovery', txt)
1846 txt.OnChange += self.__ref(self.__onParamChange)
1847 txt.modify_base(gtk.STATE_NORMAL, gdk.color_parse("#FFDDDD"))
1848
1849 h = pack_item(gtk.HBox(True), self.vbox)
1850 pack_label('Channel count:', h)
1851 txt = pack_item(qubx.GTK.NumEntry(self.channel_count, acceptIntGreaterThan(0), width_chars=6), h)
1852 self.propertied_connect_NumEntry('channel_count', txt)
1853
1854 self.panK = pack_item(gtk.VBox(), self.vbox)
1855
1856 h = pack_item(gtk.HBox(), self.vbox, at_end=True)
1857 pack_button('OK', h, bind(self.response, gtk.RESPONSE_ACCEPT), at_end=True)
1858 pack_space(h, x=6, at_end=True)
1859 pack_button('Cancel', h, bind(self.response, gtk.RESPONSE_REJECT), at_end=True)
1913 for i in reversed(xrange(len(dur_recovery))):
1914 draw_trace(i)
1928 model = qubx.global_namespace.QubX.Models.file
1929 self.channel_count = model.channelCount
1930 rates = model.rates
1931 self.panK.foreach(lambda item: self.panK.remove(item))
1932 h = pack_item(gtk.HBox(True), self.panK)
1933 pack_item(gtk.EventBox(), h)
1934 pack_label('Resting', h)
1935 pack_label('Conditioning', h)
1936 pack_label('Test', h)
1937 ratio_cond = self.ratio_cond[:]
1938 ratio_test = self.ratio_test[:]
1939 self.txtRest = []
1940 self.txtCond = []
1941 self.txtTest = []
1942 while len(ratio_cond) < rates.size:
1943 ratio_cond.append(1.0)
1944 ratio_test.append(1.0)
1945 for rate in rates:
1946 k0 = rate.k0
1947 h = pack_item(gtk.HBox(True), self.panK)
1948 pack_label('k %i->%i' % (rate.From, rate.To), h)
1949 self.txtRest.append(pack_item(qubx.GTK.NumEntry(k0, acceptFloatGreaterThan(0.0), width_chars=8), h))
1950 txt = pack_item(qubx.GTK.NumEntry(k0*ratio_cond[rate.Index], acceptFloatGreaterThan(0.0), width_chars=8), h)
1951 txt.modify_base(gtk.STATE_NORMAL, gdk.color_parse("#EEEEFF"))
1952 self.txtCond.append(txt)
1953 txt = pack_item(qubx.GTK.NumEntry(k0*ratio_test[rate.Index], acceptFloatGreaterThan(0.0), width_chars=8), h)
1954 txt.modify_base(gtk.STATE_NORMAL, gdk.color_parse("#FFFFDD"))
1955 self.txtTest.append(txt)
1956 model_stim = model.get_stimulus()
1957 if model_stim and not (self.varname in model_stim):
1958 self.varname = model_stim.iterkeys().next()
1960 model = qubx.global_namespace.QubX.Models.file
1961 model.channelCount = self.channel_count
1962 rates = model.rates
1963 ratio_cond = []
1964 ratio_test = []
1965 for i in xrange(rates.size):
1966 k0 = self.txtRest[i].value
1967 rates.set(i, 'k0', k0)
1968 k1r = self.txtCond[i].value / k0
1969 rates.set(i, 'k1', log(k1r))
1970 ratio_cond.append(k1r)
1971 rates.set(i, 'Voltage', 'InCond')
1972 k2r = self.txtTest[i].value / k0
1973 rates.set(i, 'k2', log(k2r))
1974 ratio_test.append(k2r)
1975 rates.set(i, 'Pressure', 'InTest')
1976 self.ratio_cond = ratio_cond
1977 self.ratio_test = ratio_test
1979 sim = qubx.global_namespace.QubX.Simulation
1980 sweep_count = max(1, len(self.dur_recovery))
1981 sim.segment_count = sweep_count
1982 sim.protocol.reset()
1983 sim.protocol.add_channel(self.varname)
1984 chan = sim.protocol.channels[-1]
1985 chan.insert_shape_dict(0, {
1986 '__root_name': 'Shape', '__data': 'Step',
1987 'preAmp': self.level_rest, 'preAmp incr': 0.0, 'preAmp factor': 1.0,
1988 'preDuration': 1e-3/sim.sampling_khz, 'preDuration incr': 0.0, 'preDuration factor': 1.0,
1989 'Amp': self.level_cond, 'preAmp incr': 0.0, 'preAmp factor': 1.0,
1990 'Duration': 1e-3*self.dur_cond, 'preDuration incr': 0.0, 'preDuration factor': 1.0,
1991 'postAmp': self.level_rest, 'postAmp incr': 0.0, 'postAmp factor': 1.0,
1992 'postDuration': 0.0, 'postDuration incr': 0.0, 'postDuration factor': 1.0
1993 })
1994 preDur = [1e-3*recovery for recovery in self.dur_recovery]
1995 chan.insert_shape_dict(1, {
1996 '__root_name': 'Shape', '__data': 'Step',
1997 'preAmp': self.level_rest, 'preAmp incr': 0.0, 'preAmp factor': 1.0,
1998 'preDuration': preDur, 'preDuration incr': 0.0, 'preDuration factor': 1.0,
1999 'Amp': self.level_test, 'preAmp incr': 0.0, 'preAmp factor': 1.0,
2000 'Duration': 1e-3*self.dur_test, 'preDuration incr': 0.0, 'preDuration factor': 1.0,
2001 'postAmp': self.level_rest, 'postAmp incr': 0.0, 'postAmp factor': 1.0,
2002 'postDuration': 1e-3*self.dur_test, 'postDuration incr': 0.0, 'postDuration factor': 1.0
2003 })
2004 sim.protocol.add_channel('InCond')
2005 chan = sim.protocol.channels[-1]
2006 chan.insert_shape_dict(0, {
2007 '__root_name': 'Shape', '__data': 'Step',
2008 'preAmp': 0.0, 'preAmp incr': 0.0, 'preAmp factor': 1.0,
2009 'preDuration': 1e-3/sim.sampling_khz, 'preDuration incr': 0.0, 'preDuration factor': 1.0,
2010 'Amp': 1.0, 'preAmp incr': 0.0, 'preAmp factor': 1.0,
2011 'Duration': 1e-3*self.dur_cond, 'preDuration incr': 0.0, 'preDuration factor': 1.0,
2012 'postAmp': 0.0, 'postAmp incr': 0.0, 'postAmp factor': 1.0,
2013 'postDuration': 1e-3/sim.sampling_khz, 'postDuration incr': 0.0, 'postDuration factor': 1.0
2014 })
2015 sim.protocol.add_channel('InTest')
2016 chan = sim.protocol.channels[-1]
2017 preDur = [1e-3*(1/sim.sampling_khz + self.dur_cond + recovery) for recovery in self.dur_recovery]
2018 chan.insert_shape_dict(0, {
2019 '__root_name': 'Shape', '__data': 'Step',
2020 'preAmp': 0.0, 'preAmp incr': 0.0, 'preAmp factor': 1.0,
2021 'preDuration': preDur, 'preDuration incr': 0.0, 'preDuration factor': 1.0,
2022 'Amp': 1.0, 'preAmp incr': 0.0, 'preAmp factor': 1.0,
2023 'Duration': 1e-3*self.dur_test, 'preDuration incr': 0.0, 'preDuration factor': 1.0,
2024 'postAmp': 0.0, 'postAmp incr': 0.0, 'postAmp factor': 1.0,
2025 'postDuration': 1e-3/sim.sampling_khz, 'postDuration incr': 0.0, 'postDuration factor': 1.0
2026 })
2049 sim = qubx.global_namespace.QubX.Simulation
2050 if not self.is_relevant():
2051 sim.OnFinish -= self.__ref(self.__onFinish)
2052 self.__installed = False
2053 return
2054 dataview = qubx.global_namespace.QubX.Data.view
2055 data = dataview.file
2056 lst = data.lists.show_list('Segments')
2057 tRC = self.dur_recovery
2058 tCond = self.dur_cond
2059 nPre = [1 + int(round((tCond + recovery)*1e-3/data.sampling)) for recovery in tRC]
2060 nPost = [1 + int(round((tCond + recovery)*1e-3/data.sampling)) for recovery in tRC]
2061 for i in xrange(lst.size):
2062 lst[i, 'Trc'] = tRC[i]
2063 model = qubx.global_namespace.QubX.Models.file
2064 if model.channelCount == 1:
2065
2066 for i,fln in enumerate(data.segmentation.segments):
2067 f,l,n = fln
2068 data.ideal[0].idl.set_dwell(f, f+nPre[i]-1, -1)
2069 ff, ll, cc = data.ideal[0].idl.get_dwells(f, l, True)
2070 if len(cc):
2071 iDel = 2 if (cc[0] == 0) else 1
2072 if iDel < len(cc):
2073 data.ideal[0].idl.set_dwell(ff[iDel], l, -1)
2074 qubx.global_namespace.QubX.Figures.DurHist.request_show()
2075 else:
2076
2077 for i,fln in enumerate(data.segmentation.segments):
2078 f,l,n = fln
2079 samples = dataview.get_segmentation_indexed(f+nPre[i], l)[0].get_samples().samples
2080 iPeak = numpy.argmax(samples)
2081 lst[i,"Ipeak"] = samples[iPeak]
2082 lst[i,"Tpeak"] = 1e3 * data.sampling * (nPre[i] + iPeak)
2083 qubx.global_namespace.QubX.Charts.add_two_plot('List', 'Trc', 'Ipeak', False, False)
2084 qubx.global_namespace.QubX.Charts.request_show()
2085
2086
2087 gPairedPulseWizard = None
2093
2094 Tools.register('presets', 'Paired pulse wizard...', paired_pulse_wizard)
2095