Package qubx :: Module stimulus
[hide private]
[frames] | no frames]

Source Code for Module qubx.stimulus

   1  """Panel to idealize stimulus signal(s). 
   2   
   3  Copyright 2008-2013 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  import cStringIO 
  23  import gobject 
  24  import gtk 
  25  from gtk import gdk 
  26  import itertools 
  27  import numpy 
  28  import traceback 
  29   
  30  import qubx.data_types 
  31  import qubx.dataGTK 
  32  import qubx.faces 
  33  import qubx.fast.data 
  34  import qubx.GTK 
  35  import qubx.maths 
  36  import qubx.settings 
  37  import qubx.task 
  38  from qubx.accept import * 
  39  from qubx.GTK import pack_item, pack_space, pack_hsep, pack_vsep, pack_label, pack_button, pack_check, pack_radio, pack_scrolled, build_menuitem 
  40  from qubx.settings import Property, Propertied 
  41  from qubx.util_types import * 
  42   
  43  PULSATE_TIME = 50 
  44  PULSATE_CYCLES_PER_SEC = .666 
  45  PULSATE_DTHETA = 2*numpy.pi * PULSATE_TIME * 1e-3 * PULSATE_CYCLES_PER_SEC 
  46   
  47  Source_None = Anon(segs=[]) 
  48  Shapers = {'source' : Source_None} 
  49  """'locals' dictionary when evaluating the Shaping expression""" 
  50   
  51  Shaper_Wizards = {} 
  52  """Shaping menu: label -> lambda: show_wizard_or_set_shaping(recv_shaping); recv_shaping=lambda shaping: whatever""" 
  53   
  54  EXPR_CAPTION = "Please enter a function to calculate %s." 
  55   
  56  EXPR_HELP = """f can be any expression using the series names and "x" (time). 
  57  For example, f = 1.0 - 2.3 * Voltage 
  58   
  59  Expressions use Python syntax.  Full documentation is available at www.python.org. 
  60   
  61  Construct piecewise functions using "and" and "or" 
  62     ((x<0.0) and (-1e21)) or ((x==0) and -1e20) or log(x) 
  63   
  64  basic operators: +, -, *, / 
  65  exponents: pow(base, exp) or base**exp 
  66   
  67  standard math: pow, exp, log, log10, ceil, floor, fabs, cos, sin, tan, acos, asin, atan, atan2, ... 
  68  Type e.g. "help(atan2)" at the ">>>" prompt for details. 
  69  """ 
  70   
  71  ODE_HELP = """Enter a system of first-order ordinary differential equations, 
  72  separated by ';'  Refer to the input as "signal."  Position first.  e.g.: 
  73      position' = velocity; velocity' = strength * (position - signal) 
  74  Undefined words such as "strength" are taken as parameters below.""" 
75 76 -class OneStimulusFace(qubx.faces.Face):
77 __explore_featured = ['data', 'stim_index', 'set_defaults', 'to_apply', 'attach', 'detach', 'on_set']
78 - def __init__(self, data, stim_index, set_defaults, global_name=""):
79 stim = data.stimuli.get_row(stim_index) 80 qubx.faces.Face.__init__(self, stim.Name, global_name=global_name) 81 self.__ref = Reffer() 82 self.__setting = True 83 self.data = data 84 self.stim_index = stim_index 85 self.set_defaults = set_defaults 86 sides = pack_item(gtk.HBox(True), self, expand=True) 87 side = pack_item(gtk.VBox(), sides) 88 line = pack_item(gtk.HBox(), side) 89 self.chkConst = pack_radio('Constant:', line, active=True, on_toggle=self.__onToggleConst) 90 self.txtConst = pack_item(qubx.GTK.NumEntry(0.0, acceptFloat, '%.3g', width_chars=6), line, expand=True) 91 self.txtConst.OnChange += self.__ref(self.__onChangeConst) 92 line = pack_item(gtk.HBox(), side) 93 self.chkSignal = pack_radio('Signal:', line, group=self.chkConst, on_toggle=self.__onToggleSignal) 94 self.mnuSignal = pack_item(gtk.combo_box_new_text(), line, expand=True) 95 for s in xrange(self.data.signals.size): 96 self.mnuSignal.append_text( self.data.signals.get(s, 'Name') ) 97 self.evt_signal_changed = self.mnuSignal.connect('changed', self.__onChooseSignal) 98 self.mnuSignal.set_sensitive(False) 99 self.chkExpr = pack_radio('Expression [f(t,s{,signals...})]:', side, group=self.chkConst, on_toggle=self.__onToggleExpr) 100 line = pack_item(gtk.HBox(), side) 101 acceptExpr = acceptF(static=[], custom=True) 102 self.txtExpr = pack_item(qubx.GTK.NumEntry(acceptExpr('0.0'), acceptExpr), line, expand=True) 103 self.txtExpr.OnChange += self.__ref(self.__onChangeExpr) 104 self.txtExpr.set_sensitive(False) 105 self.btnExprHelp = pack_button('...', line, on_click=self.__onClickExprHelp, sensitive=False) 106 line = pack_item(gtk.HBox(), side) 107 pack_label('Shaping:', line) 108 self.txtShaping = pack_item(qubx.GTK.NumEntry("", str), line, expand=True) 109 self.txtShaping.OnChange += self.__ref(self.__onChangeShaping) 110 self.mnuShaping = pack_item(qubx.GTK.TriangleButton(), line) 111 self.mnuShaping.set_size_request(26, 26) 112 self.evt_mnu_shaping = self.mnuShaping.connect('button_press_event', self.__onClickMnuShaping) 113 side = pack_item(gtk.VBox(), sides) 114 line = pack_item(gtk.HBox(), side) 115 pack_label('Latency (ms):', line) 116 self.txtLatency = pack_item(qubx.GTK.NumEntry(0.0, acceptFloat, '%.3g'), line, expand=True) 117 self.txtLatency.OnChange += self.__ref(self.__onChangeLatency) 118 line = pack_item(gtk.HBox(), side) 119 pack_label('Known amps:', line) 120 self.txtKnown = pack_item(qubx.GTK.NumEntry([], acceptFloatList(), formatList('%.3g')), line, expand=True) 121 self.txtKnown.OnChange += self.__ref(self.__onChangeKnown) 122 line = pack_item(gtk.HBox(), side) 123 self.chkAddDeltas = pack_check('Add amps with delta:', line, on_toggle=self.__onToggleAddDeltas) 124 self.txtDelta = pack_item(qubx.GTK.NumEntry(0.5, acceptFloatGreaterThan(0.0), '%.3g'), line, expand=True) 125 self.txtDelta.OnChange += self.__ref(self.__onChangeDelta) 126 line = pack_item(gtk.HBox(), side) 127 pack_label('Min dur (ms):', line) 128 self.txtMinDur = pack_item(qubx.GTK.NumEntry(0.0, acceptFloatGreaterThanOrEqualTo(0.0), '%.3g'), line, expand=True) 129 self.txtMinDur.OnChange += self.__ref(self.__onChangeMinDur) 130 line = pack_item(gtk.HBox(), side, at_end=True) 131 self.btnSetAsDef = pack_button('Set as default', line, on_click=self.__onClickSetAsDef) 132 # only shown when changes pending 133 self.btnApply = pack_button('Apply', line, at_end=True, show=False, on_click=self.__onClickApply) 134 self.btnRevert = pack_button('Revert', line, at_end=True, show=False, on_click=self.__onClickRevert) 135 136 self.to_apply = {} 137 self.on_set('Type', stim.Type, qubx.data_types.STIM_TYPE_CONST) 138 self.on_set('Signal_index', stim.Signal_index, self.mnuSignal.get_active()) 139 self.on_set('Expr', stim.Expr, self.txtExpr.value) 140 self.on_set('Latency', stim.Latency, self.txtLatency.value) 141 self.on_set('Shaping', stim.Shaping, "") ## 142 self.on_set('Known_amps', stim.Known_amps, self.txtKnown.value) 143 self.on_set('Add_deltas', stim.Add_deltas, self.chkAddDeltas.get_active()) 144 self.on_set('Delta_amp', stim.Delta_amp, self.txtDelta.value) 145 self.on_set('Min_dur', stim.Min_dur, self.txtMinDur.value) 146 147 self.__setting = False 148 self.attach()
149 - def attach(self):
150 self.data.constants.OnSet += self.__ref(self.__onSetConstant) 151 self.data.signals.OnInsert += self.__ref(self.__onInsertSignal) 152 self.data.signals.OnSet += self.__ref(self.__onSetSignal) 153 self.data.signals.OnRemoving += self.__ref(self.__onRemovingSignal)
154 - def detach(self):
155 """Important: call this before discarding the face.""" 156 self.data.constants.OnSet -= self.__ref(self.__onSetConstant) 157 self.data.signals.OnInsert -= self.__ref(self.__onInsertSignal) 158 self.data.signals.OnSet -= self.__ref(self.__onSetSignal) 159 self.data.signals.OnRemoving -= self.__ref(self.__onRemovingSignal) 160 self.data = None 161 for chk in (self.chkConst, self.chkSignal, self.chkExpr, self.chkAddDeltas): 162 chk.disconnect(chk.evt_toggled) 163 for btn in (self.btnExprHelp, self.btnSetAsDef, self.btnApply, self.btnRevert): 164 btn.disconnect(btn.evt_clicked) 165 self.mnuSignal.disconnect(self.evt_signal_changed) 166 self.mnuShaping.disconnect(self.evt_mnu_shaping) 167 self.__ref.clear()
168 - def __onSetConstant(self, i, field, val, prev, undoing):
169 if ((field == 'Value') and 170 (self.data.constants.get(i, 'Name') == self.data.stimuli.get(self.stim_index, 'Name'))): 171 self.txtConst.value = val
172 - def __onInsertSignal(self, i, undoing):
173 self.__setting = True 174 self.mnuSignal.insert_text(i, self.data.signals.get(i, 'Name')) 175 if 'Signal_index' in self.to_apply: 176 sig = self.to_apply['Signal_index'] 177 else: 178 sig = self.data.stimuli.get(self.stim_index, 'Signal_index') 179 if sig < len(self.mnuSignal.get_model()): 180 self.mnuSignal.set_active(sig) 181 self.__setting = False
182 - def __onSetSignal(self, i, field, val, prev, undoing):
183 if field == 'Name': 184 self.mnuSignal.get_model()[i][0] = val
185 - def __onRemovingSignal(self, i, undoing):
186 self.__setting = True 187 self.mnuSignal.remove_text(i) 188 self.__setting = False
189 - def on_set(self, field, val, prev):
190 self.__setting = True 191 if field == 'Type': 192 chks = {qubx.data_types.STIM_TYPE_CONST : self.chkConst, 193 qubx.data_types.STIM_TYPE_SIGNAL : self.chkSignal, 194 qubx.data_types.STIM_TYPE_EXPR : self.chkExpr} 195 enables = {qubx.data_types.STIM_TYPE_CONST : [self.txtConst], 196 qubx.data_types.STIM_TYPE_SIGNAL : [self.mnuSignal], 197 qubx.data_types.STIM_TYPE_EXPR : [self.txtExpr, self.btnExprHelp]} 198 chks[prev].set_active(False) 199 for x in enables[prev]: 200 x.set_sensitive(False) 201 chks[val].set_active(True) 202 for x in enables[val]: 203 x.set_sensitive(True) 204 if val == qubx.data_types.STIM_TYPE_CONST: 205 self.txtConst.value = self.data.constants.get_by_name(self.data.stimuli.get(self.stim_index, 'Name'), 'Value') 206 #elif val == qubx.data_types.STIM_TYPE_SIGNAL: 207 # if not (0 < self.data.stimuli.get(self.stim_index, 'Signal_index') < self.data.signals.size): 208 # self.data.stimuli.set(self.stim_index, 'Signal_index', self.data.signals.size-2) 209 elif field == 'Signal_index': 210 self.mnuSignal.set_active(val) 211 elif field == 'Expr': 212 self.txtExpr.value = val 213 elif field == 'Latency': 214 self.txtLatency.value = val 215 elif field == 'Shaping': 216 self.txtShaping.value = val 217 elif field == 'Known_amps': 218 self.txtKnown.value = val 219 elif field == 'Add_deltas': 220 self.chkAddDeltas.set_active(val) 221 self.txtDelta.set_sensitive(val) 222 elif field == 'Delta_amp': 223 self.txtDelta.value = val 224 elif field == 'Min_dur': 225 self.txtMinDur.value = val 226 self.__setting = False
227 - def onShow(self, showing):
228 if not showing: 229 self.__apply()
230 - def __apply(self):
231 i = self.stim_index 232 for k,v in self.to_apply.iteritems(): 233 qubx.pyenv.env.OnScriptable('QubX.Data.file.stimuli[%i,%s] = %s' % (i, repr(k), repr(v))) 234 self.data.stimuli[i, k] = v 235 self.to_apply = {} 236 self.btnApply.hide() 237 self.btnRevert.hide()
238 - def __revert(self):
239 i = self.stim_index 240 self.to_apply = dict([(field, self.data.stimuli.get(i, field)) for field in self.to_apply.iterkeys()]) 241 self.__apply()
242 - def __defer(self, field, value):
243 self.to_apply[field] = value 244 if not (self.btnApply.flags() & gtk.VISIBLE): 245 self.btnApply.show() 246 self.btnRevert.show() 247 gobject.timeout_add(PULSATE_TIME, self.__pulsate, 0.0)
248 - def __pulsate(self, theta):
249 self.btnApply.modify_bg(gtk.STATE_NORMAL, gdk.Color(*[int(65535*x) for x in HSVtoRGB(.333, .333, .8-sin(theta)/5)])) 250 self.btnRevert.modify_bg(gtk.STATE_NORMAL, gdk.Color(*[int(65535*x) for x in HSVtoRGB(0.0, .333, .8-sin(theta)/5)])) 251 if self.btnApply.flags() & gtk.VISIBLE: 252 gobject.timeout_add(PULSATE_TIME, self.__pulsate, theta + PULSATE_DTHETA)
253 - def __onToggleConst(self, chk):
254 if self.__setting: return 255 if chk.get_active(): 256 self.__defer('Type', qubx.data_types.STIM_TYPE_CONST) 257 self.txtConst.set_sensitive(True)
258 - def __onToggleSignal(self, chk):
259 if self.__setting: return 260 if chk.get_active(): 261 self.__defer('Type', qubx.data_types.STIM_TYPE_SIGNAL) 262 self.mnuSignal.set_sensitive(True)
263 - def __onToggleExpr(self, chk):
264 if self.__setting: return 265 if chk.get_active(): 266 self.__defer('Type', qubx.data_types.STIM_TYPE_EXPR) 267 self.btnExprHelp.set_sensitive(True) 268 self.txtExpr.set_sensitive(True)
269 - def __onChangeConst(self, txt, val):
270 i = self.data.constants.index(self.data.stimuli.get(self.stim_index, 'Name')) 271 self.data.constants.set(i, 'Value', val)
272 - def __onChooseSignal(self, mnu):
273 if self.__setting: return 274 self.__defer('Signal_index', mnu.get_active())
275 - def __onChangeExpr(self, txt, val):
276 self.__defer('Expr', val)
277 - def __onClickExprHelp(self, btn):
278 bind = lambda func: qubx.pyenv.env.globals.__setitem__('f', func) 279 write_test = lambda func: "print f(%s)" % ', '.join(["%s=1.0"%nm for nm in func.args]) 280 ## TODO: enforce series names in test 281 dlg = qubx.pyenvGTK.HelpTestDialog(self.txtExpr.accept, EXPR_CAPTION % self.data.stimuli.get(self.stim_index, 'Name'), 282 "f = ", EXPR_HELP, bind, write_test) 283 if gtk.RESPONSE_ACCEPT == dlg.run(self.txtExpr.get_text()): 284 func = self.txtExpr.accept(dlg.expr) 285 self.txtExpr.value = func 286 self.__defer('Expr', func) 287 self.txtExpr.grab_focus() 288 dlg.destroy()
289 - def __onChangeShaping(self, txt, val):
290 self.__defer('Shaping', val)
291 - def __onClickMnuShaping(self, btn, evt):
292 popup = gtk.Menu() 293 for k in sorted(Shaper_Wizards.keys()): 294 build_menuitem(k, self.__ref(bind_with_args_before(self.__onClickShaper, k)), menu=popup) 295 popup.popup(None, None, None, 0, evt.time)
296 - def __onClickShaper(self, item, k):
298 - def __receive_shaping(self, shaping):
299 self.txtShaping.value = shaping 300 self.__defer('Shaping', shaping)
301 - def __onChangeLatency(self, txt, val):
302 self.__defer('Latency', val)
303 - def __onChangeKnown(self, txt, val):
304 self.__defer('Known_amps', val)
305 - def __onToggleAddDeltas(self, chk):
306 if self.__setting: return 307 self.__defer('Add_deltas', chk.get_active()) 308 self.txtDelta.set_sensitive(chk.get_active())
309 - def __onChangeDelta(self, txt, val):
310 self.__defer('Delta_amp', val)
311 - def __onChangeMinDur(self, txt, val):
312 self.__defer('Min_dur', val)
313 - def __onClickSetAsDef(self, btn):
314 self.set_defaults(self.txtLatency.value, self.txtKnown.value, self.chkAddDeltas.get_active(), 315 self.txtDelta.value, self.txtMinDur.value)
316 - def __onClickApply(self, btn):
317 self.__apply()
318 - def __onClickRevert(self, btn):
319 self.__revert()
320
321 -class StimulusFace(qubx.faces.ToolsFace):
322 """ 323 Presents stimulus settings, coordinates mapping of model vars to data stimulus, 324 and performs stimulus idealization. 325 """ 326 327 __explore_featured = ['robot', 'OnChange', 'OnInsert', 'OnRemoving', 'set_defaults', 'get_active_names', 328 'get_stimulus_idl', 'call_with_stimulus_idl', 'model', 'kick_robot'] 329
330 - def __init__(self, QubX, global_name=""):
331 qubx.faces.ToolsFace.__init__(self, 'Stimulus', pos=qubx.faces.POS_DROPDOWN, dropdown_label="Variable:", global_name=global_name) 332 self.__ref = Reffer() 333 self.robot = qubx.task.Robot('Stimulus', self.__ref(lambda: qubx.task.Tasks.add_task(self.robot)), 334 self.__ref(lambda: qubx.task.Tasks.remove_task(self.robot))) 335 self.robot.OnException += self.__ref(self.__onRobotException) 336 self.__serial = 0 337 QubX.OnQuit += self.__ref(lambda: self.robot.stop()) 338 self.OnChange = WeakEvent() # (face, name or None{all incl # and pos of stims}) 339 self.OnInsert = WeakEvent() # (face, name) 340 self.OnRemoving = WeakEvent() # (face, name) 341 self.__remodeling = False 342 self.Data = QubX.Data 343 self.Data.OnSwitch += self.__ref(self.__onSwitchData) 344 self.__data = None 345 self.Models = QubX.Models 346 self.Models.OnSwitch += self.__ref(self.__onSwitchModel) 347 self.__model = None 348 self.__vars = [] 349 self.__vars_in_model = [] 350 self.__invalid = [] 351 self.__serving = [] 352 self.__onSwitchData(self.Data, self.Data.file) 353 self.__onSwitchModel(self.Models, self.Models.file) 354 self.__defaults = qubx.settings.SettingsMgr['StimDefs'].active 355 def set_if_empty(node, data): 356 if not node.data: node.data = data
357 set_if_empty(self.__defaults['Latency'], 0.0) 358 set_if_empty(self.__defaults['Add_deltas'], 1) 359 set_if_empty(self.__defaults['Delta_amp'], 0.5) 360 set_if_empty(self.__defaults['Min_dur'], 1.0)
361 #self.OnChange += self.__ref(lambda face, name: printer('OnChange', name)) 362 #self.OnInsert += self.__ref(lambda face, name: printer('OnInsert', name)) 363 #self.OnRemoving += self.__ref(lambda face, name: printer('OnRemoving', name))
364 - def set_defaults(self, latency, known_amps, add_deltas, delta_amp, min_dur):
365 self.__defaults['Latency'].data = latency 366 self.__defaults['Known_amps'].data = known_amps 367 self.__defaults['Add_deltas'].data = add_deltas 368 self.__defaults['Delta_amp'].data = delta_amp 369 self.__defaults['Min_dur'].data = min_dur
370 names = property(lambda self: self.__vars)
371 - def get_active_names(self, model=None, datafile=None):
372 mdl = self.__model if (model is None) else model 373 return mdl.variables
374 active_names = property(lambda self: self.get_active_names())
375 - def __get_vars(self, *args, **kw):
376 return self.get_active_names(*args, **kw)
377 - def get_stimulus_idl(self, segs, names=[], datafile=None, model=None):
378 """Enters a recursive main loop until the stimulus idl is ready. 379 380 @param segs: list of (first,last) index pairs 381 @returns: (signames, [ [(firsts, lasts, classes, amps) for seg] for signal]) 382 """ 383 return qubx.pyenv.call_async_wait(self.call_with_stimulus_idl, segs, names, datafile, model)
384 - def call_with_stimulus_idl(self, func, segs, names=[], datafile=None, model=None):
385 """ 386 Synchronizes with the idealization thread, gets the most up-to-date idealization of the named variable, 387 then calls func(signames, [ [(firsts, lasts, classes, amps) for seg] for signal] on the gobject thread. 388 """ 389 data = self.__data if (datafile is None) else datafile 390 mdl = self.__model if (model is None) else model 391 nms = mdl.variables if (not names) else names 392 self.__serving.append(Anon(func=func, names=nms, segs=segs, datafile=data, model=mdl)) 393 self.kick_robot()
394 - def __onSwitchData(self, Data, file):
395 if file == self.__data: return 396 try: 397 self.__remodeling = True 398 front_name = None 399 if self.book.get_current_page() >= 0: 400 front_name = self.__data.stimuli.get_row( self.book.get_current_page() ).Name 401 self.robot.interrupt() 402 if self.__data: 403 self.__data.OnChangeSamples -= self.__ref(self.__onChangeSamples) 404 self.__data.constants.OnSet -= self.__ref(self.__onSetConstant) 405 self.__data.stimuli.OnInsert -= self.__ref(self.__onInsertStimulus) 406 self.__data.stimuli.OnSet -= self.__ref(self.__onSetStimulus) 407 self.__data.stimuli.OnRemoving -= self.__ref(self.__onRemovingStimulus) 408 for i in reversed(xrange(self.__data.stimuli.size)): 409 self.__onRemovingStimulus(i, False) 410 self.__data = file 411 if self.__data: 412 self.__data.OnChangeSamples += self.__ref(self.__onChangeSamples) 413 for name in self.__vars_in_model: 414 self.__data.get_stimulus(name) 415 self.__data.constants.OnSet += self.__ref(self.__onSetConstant) 416 self.__data.stimuli.OnInsert += self.__ref(self.__onInsertStimulus) 417 self.__data.stimuli.OnSet += self.__ref(self.__onSetStimulus) 418 self.__data.stimuli.OnRemoving += self.__ref(self.__onRemovingStimulus) 419 for i in xrange(self.__data.stimuli.size): 420 name = self.__data.stimuli.get(i, 'Name') 421 self.__onInsertStimulus(i, False) 422 if front_name in self.__vars_in_model: 423 gobject.idle_add(self.show_face, front_name) 424 finally: 425 self.__remodeling = False 426 self.OnChange(self, None)
427 - def __onSwitchModel(self, Models, file):
428 self.model = file
429 model = property(lambda self: self.__model, lambda self, x: self.set_model(x))
430 - def set_model(self, file):
431 if file == self.__model: return 432 if self.__model: 433 self.__model.OnChangeVariables -= self.__ref(self.__onChangeVariables) 434 self.__model = file 435 if self.__model: 436 self.__model.OnChangeVariables += self.__ref(self.__onChangeVariables) 437 self.__onChangeVariables(self.__model, self.__model.variables) 438 else: 439 self.__onChangeVariables(None, [])
440 - def __onChangeVariables(self, model, variables):
441 last_vars_in_model, self.__vars_in_model = self.__vars_in_model, variables[:] 442 if self.__data: 443 self.robot.interrupt() 444 for var in variables: 445 if not (var in self.__vars): 446 self.__data.get_stimulus(var, 447 self.__defaults['Latency'].data[0], 448 self.__defaults['Known_amps'].data[:], 449 bool(self.__defaults['Add_deltas'].data[0]), 450 self.__defaults['Delta_amp'].data[0], 451 self.__defaults['Min_dur'].data[0]) 452 if self.faces and not (self.__data.stimuli.get(self._face.stim_index, 'Name') in variables): 453 for i in xrange(self.__data.stimuli.size): 454 if self.__data.stimuli.get(i, 'Name') in variables: 455 self.show_face( self.__data.stimuli.get(i, 'Name') ) 456 break 457 if set(last_vars_in_model) != variables: 458 self.OnChange(self, None)
459 - def __onSetConstant(self, i, field, val, prev, undoing):
460 if field == 'Value': 461 cname = self.__data.constants.get(i, 'Name') 462 try: 463 self.OnChange(self, cname) 464 except ValueError: 465 pass
466 - def __onInsertStimulus(self, i, undoing):
467 stim = self.__data.stimuli.get_row(i) 468 face = OneStimulusFace(self.__data, i, self.set_defaults, global_name=self.global_name and ('%s.%s' % (self.global_name, SafeName(stim.Name))) or "") 469 self.insert_face(i, face) 470 self.__invalid.insert(i, False) 471 self.__vars.insert(i, stim.Name) 472 for face in self.faces[i+1:]: 473 face.stim_index += 1 474 self.__invalid[i] = (stim.Type != qubx.data_types.STIM_TYPE_CONST) and not self.__is_idealized(self.__data, i) 475 if self.__model and (stim.Name in self.__model.variables): 476 self.show_face(stim.Name) 477 self.OnInsert(self, stim.Name) 478 if self.__invalid[i]: 479 self.kick_robot() 480 elif not self.__remodeling: 481 self.OnChange(self, stim.Name)
482 - def __onSetStimulus(self, i, field, val, prev, undoing):
483 if not (0 <= i < min(len(self.__data.stimuli), len(self.__invalid))): 484 return 485 if 0 <= i < len(self.faces): 486 self.faces[i].on_set(field, val, prev) 487 stim = self.__data.stimuli.get_row(i) 488 invalid = self.__invalid[i] 489 if field == 'Type': 490 if val != qubx.data_types.STIM_TYPE_CONST: 491 invalid = True 492 elif stim.Type != qubx.data_types.STIM_TYPE_CONST: 493 if field in ['Signal_index', 'Expr', 'Latency', 'Shaping', 'Known_deltas', 'Add_deltas', 'Delta_amp', 'Min_dur']: 494 invalid = True 495 self.__invalid[i] = invalid 496 if invalid: 497 self.kick_robot() 498 elif stim.Active and not self.__remodeling: 499 self.OnChange(self, stim.Name)
500 - def __onRemovingStimulus(self, i, undoing):
501 if not self.__remodeling: 502 self.OnRemoving(self, self.__data.stimuli.get(i, 'Name')) 503 self.faces[i].detach() 504 self.remove_face(i) 505 for face in self.faces[i:]: 506 face.stim_index -= 1 507 del self.__invalid[i] 508 del self.__vars[i]
509 - def __onChangeSamples(self, data, signal):
510 if signal < 0: 511 for i in xrange(len(self.__invalid)): 512 self.__invalid[i] = (self.__data.stimuli.get(i, 'Type') != qubx.data_types.STIM_TYPE_CONST) 513 else: 514 for i in xrange(self.__data.stimuli.size): 515 stim = self.__data.stimuli.get_row(i) 516 if (stim.Type == qubx.data_types.STIM_TYPE_SIGNAL) and (stim.Signal_index == signal): 517 self.__invalid[i] = True 518 elif (stim.Type == qubx.data_types.STIM_TYPE_EXPR): 519 self.__invalid[i] = True 520 if any(invalid for i, invalid in enumerate(self.__invalid) if (self.__vars[i] in self.__model.variables)): 521 self.kick_robot()
522 - def __onRobotException(self, robot, typ, val, trace):
523 traceback.print_exception(typ, val, trace)
524 - def __is_idealized(self, datafile, i):
525 stim = datafile.stimuli.get_row(i) 526 if stim.Type == qubx.data_types.STIM_TYPE_CONST: 527 return True 528 elif stim.Type == qubx.data_types.STIM_TYPE_SIGNAL: 529 ideal = self.Data.file.ideal[stim.Signal_index] 530 elif stim.Type == qubx.data_types.STIM_TYPE_EXPR: 531 ideal = self.Data.file.stim_expr_ideal[stim.Name] 532 for seg in self.Data.view.get_segmentation_file(): 533 ff, ll, cc = ideal.idl.get_dwells(seg.f, seg.l, fragments=True) 534 if (len(ff) == 0) or (ff[0] != seg.f) or (ll[-1] != seg.l) or any(cc < 0): 535 return False 536 return True
537 - def kick_robot(self):
538 self.__serial += 1 539 if self.__serving or qubx.pyenv.env.globals['QubX'].Modeling.showing: 540 self.robot.do(self.robot_update, self.__serial)
541 - def robot_update(self, serial):
542 if serial < self.__serial: return 543 serving = self.__serving[:] 544 self.__serving[:len(serving)] = [] 545 vars = self.__vars 546 data = self.__data 547 if serving: 548 serv_vars = [] 549 for customer in serving: 550 serv_vars.extend(customer.names) 551 serv_vars = set(serv_vars) 552 else: 553 serv_vars = set(self.__model.variables) 554 invalid = [i for i, x in enumerate(self.__invalid) if (x and (vars[i] in serv_vars))] 555 if not invalid and not serving: return 556 frac_sum = 0.0 557 frac_sig = 1.0 / (len(invalid) or 1) 558 def sub_progress(frac): 559 self.robot.progress = 100.0 * (frac_sum + frac_sig * frac)
560 for i in invalid: 561 self.robot_idl_stim(data, i, sub_progress) 562 frac_sum += frac_sig 563 self.__invalid[i] = False 564 for customer in serving: 565 if customer.datafile != data: 566 for name in customer.names: 567 ix = self.robot.idle_wait(customer.datafile.get_stimulus, name).Index 568 if not self.__is_idealized(customer.datafile, ix): 569 self.robot_idl_stim(customer.datafile, ix, sub_progress) 570 with self.robot.main_hold: 571 self.robot_holding_call_with_stimulus_idl(customer.func, customer.names, customer.segs, customer.datafile)
572 - def robot_holding_call_with_stimulus_idl(self, func, names, segs, datafile):
573 data = datafile or self.__data 574 if not data: 575 gobject.idle_add(func, [[([], [], [], [], [])]*len(segs)] * len(names)) 576 else: 577 gobject.idle_add(func, names, [ [data.get_stimulus_idl(name, f, l) for f, l in segs] for name in names])
578 - def robot_idl_stim(self, datafile, stim_ix, progress):
579 with self.robot.main_hold: 580 stim = datafile.stimuli.get_row(stim_ix) 581 #print 're-idealizing',stim.Name ### 582 latency_samp = int(round(stim.Latency*1e-3/datafile.sampling)) 583 segs = datafile.get_segmentation_file(latency=latency_samp) 584 signals = datafile.signals 585 if stim.Type == qubx.data_types.STIM_TYPE_SIGNAL: 586 #print 'signal',stim.Signal_index 587 filter_Hz = 1e3*(signals.get(stim.Signal_index, 'Filter') and signals.get(stim.Signal_index, 'Filter Freq') or 0.0) 588 source = StimSource_Signal(segs, stim.Signal_index, latency_samp, filter_Hz) 589 ideal_out = datafile.ideal[stim.Signal_index] 590 out_ix = stim.Signal_index 591 elif stim.Type == qubx.data_types.STIM_TYPE_EXPR: 592 source = StimSource_Expr(segs, stim.Expr, latency_samp) 593 ideal_out = datafile.stim_expr_ideal[stim.Name] 594 out_ix = -1 595 else: 596 self.__invalid[stim_ix] = False 597 return 598 idealizer = qubx.fast.data.IdlStim(stim.Known_amps, stim.Add_deltas, stim.Delta_amp, stim.Min_dur, datafile.sampling*1e3) 599 if stim.Shaping: 600 try: 601 Shapers['source'] = source 602 source = eval(stim.Shaping, qubx.pyenv.env.globals, Shapers) 603 except: 604 traceback.print_exc() 605 Shapers['source'] = Source_None 606 tot_n = sum(seg.n for seg in segs) 607 read_n = 0 608 for seg in segs: 609 chunks = stim.Shaping and [seg] or seg.chunks # must do whole segments at a time for ODEs -- seam artifacts 610 for chunk in chunks: 611 samples = source.read(chunk.f, chunk.l, self.robot.main_hold) 612 idealizer.add(samples) 613 read_n += chunk.n 614 progress(read_n * 0.618 / tot_n) 615 idealizer.done_add() 616 amps = idealizer.get_amps() 617 idlseg = [idealizer.get_next_dwells(seg.f, seg.n) for seg in segs] 618 progress(.816) 619 gobject.idle_add(self.__set_idl, datafile, stim_ix, ideal_out, out_ix, segs, amps, idlseg)
620 - def __set_idl(self, datafile, stim_ix, ideal_out, out_ix, segs, amps, idlseg):
621 for i in xrange(len(idlseg)): 622 ff, ll, cc = idlseg[i] 623 ideal_out.idl.set_dwells(len(ff), ff, ll, cc) 624 ideal_out.seg[segs[i].index].amp = amps 625 ideal_out.seg[segs[i].index].std = [0.0] * len(amps) 626 if out_ix >= 0: 627 datafile.OnChangeIdealization(self.__data, out_ix) 628 if datafile == self.__data: 629 self.OnChange(self, datafile.stimuli.get(stim_ix, 'Name'))
630
631 632 -class StimSource(object):
633 - def __init__(self, segs):
634 self.segs = segs
635 - def read(self, f, l, main_hold):
636 return numpy.zeros(shape=(l-f+1,), dtype=numpy.float32)
637
638 -class StimSource_Signal(StimSource):
639 - def __init__(self, segs, signal, latency, filter_Hz):
640 StimSource.__init__(self, segs) 641 self.file = segs[0].file 642 self.signal = signal 643 self.latency = latency 644 self.filter_Hz = filter_Hz
645 - def read(self, f, l, main_hold):
646 with main_hold: 647 if self.filter_Hz: 648 chunk = qubx.data_types.get_file_samples_filtered(self.file, self.signal, f, l, True, self.latency, self.filter_Hz) 649 result = chunk.samples 650 else: 651 result = self.file.analog[self.signal].read_overlaid(self.file, self.signal, f, l, self.latency) 652 return result
653
654 -class StimSource_Time(StimSource):
655 - def __init__(self, segs):
656 StimSource.__init__(self, segs)
657 - def read(self, f, l, main_hold):
658 """Time from begin of seg in segs; not necessarily file segmentation.""" 659 for seg in self.segs: 660 if seg.f <= f <= l <= seg.l: 661 samples = numpy.arange(f - seg.f, l+1 - seg.f, dtype=numpy.float32) 662 samples *= seg.file.sampling 663 return samples 664 else: 665 raise Exception('(%i, %i) not within any segment' % (f, l))
666
667 -class StimSource_Expr(StimSource):
668 - def __init__(self, segs, func, latency):
669 StimSource.__init__(self, segs) 670 self.func = func 671 self.sources = [] 672 signals = segs[0].file.signals 673 signal_ixs = dict([(signals.get(i, 'Name'), i) for i in xrange(signals.size)]) 674 for arg in func.args: 675 if arg in ['x', 't']: 676 self.sources.append(StimSource_Time(segs)) 677 elif arg in signal_ixs: 678 signal = signal_ixs[arg] 679 filter_Hz = 1e3*(signals.get(signal, 'Filter') and signals.get(signal, 'Filter Freq') or 0.0) 680 self.sources.append(StimSource_Signal(segs, signal, latency, filter_Hz)) 681 else: 682 print 'Stimulus expression: no signal named "%s"' % arg 683 print '(%s)' % func 684 self.sources.append(StimSource(segs))
685 - def read(self, f, l, main_hold):
686 if not self.sources: # no arguments (constant) 687 return self.func() + numpy.zeros(shape=(l-f+1,), dtype=numpy.float32) 688 try: 689 argses = [source.read(f, l, main_hold) for source in self.sources] 690 return self.func(*argses) 691 except: # in case the function doesn't numpy broadcast 692 return numpy.array([self.func(*args) for args in itertools.izip(*argses)], dtype=numpy.float32)
693
694 695 -class Shaper(StimSource):
696 pass
697
698 -class Shaper_ODE(Shaper):
699 - def __init__(self, source, expr, *var0, **params):
700 Shaper.__init__(self, source.segs) 701 self.source = source 702 self.ode = qubx.maths.DrivenODESystem(expr) # , method=qubx.maths.METHOD_VODE) # VODE misses sudden jumps in long records 703 self.var0 = var0 704 self.params = params 705 self.last_y = None 706 self.seg_lasts = set(seg.l for seg in source.segs)
707 - def read(self, f, l, main_hold):
708 if not (self.last_y is None): 709 var0 = list(self.last_y) 710 dx = 1 711 else: 712 var0 = list(self.var0) 713 dx = 0 714 xx = numpy.arange(l-f+1+dx) * self.segs[0].file.sampling 715 source_series = numpy.zeros(shape=(l-f+1+dx,), dtype=numpy.float32) 716 source_series[dx:] = self.source.read(f, l, main_hold) 717 718 self.params['signal'] = source_series 719 if var0[0] is None: 720 var0[0] = source_series[0] 721 yy = self.ode(xx, *var0, **self.params) 722 if l in self.seg_lasts: 723 self.last_y = None 724 else: 725 self.last_y = [y for y in yy[-1,:]] 726 result = numpy.array(yy[dx:,0].reshape((yy.shape[0]-dx,)), dtype=numpy.float32, copy=True) 727 return result
728
729 -class Shaper_ODE_Wizard_Param(gtk.HBox):
730 __explore_featured = ['OnChange', 'pname', 'node', 'txt', 'update']
731 - def __init__(self, pname, node):
732 gtk.HBox.__init__(self, True) 733 self.__ref = Reffer() 734 self.OnChange = WeakEvent() 735 if not node.data: 736 node.data = 0.0 737 self.pname = pname 738 self.node = node 739 pack_label('%s:' % pname, self) 740 self.txt = pack_item(qubx.GTK.NumEntry(node.data[0]), self) 741 self.txt.OnChange += self.__ref(self.__onChange)
742 - def __onChange(self, txt, val):
743 self.node.data[0] = val 744 self.OnChange(self, val)
745 - def update(self, node):
746 self.node.data[0] = node.data[0] 747 self.txt.value = node.data[0]
748
749 -class Shaper_ODE_Wizard(gtk.Dialog):
750 - def __init__(self, title):
751 gtk.Dialog.__init__(self, title, None, gtk.DIALOG_MODAL, 752 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, 753 gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) 754 self.__ref = Reffer() 755 self.__presets = qubx.settings.SettingsMgr[title] 756 self.__profile = self.__presets.active 757 self.__presets.OnSet = self.__ref(self.__onPreSet) 758 self.txtAbout = gtk.TextView() 759 self.txtAbout.get_buffer().set_text(ODE_HELP) 760 pack_scrolled(self.txtAbout, self.vbox, size_request=(600, 140)) 761 self.txtExpr = pack_item(qubx.GTK.NumEntry("", str), self.vbox) 762 self.txtExpr.OnChange += self.__ref(self.__onChangeExpr) 763 self.panParams = pack_item(gtk.VBox(), self.vbox, expand=True) 764 self.__expr = "" 765 self.__ode = qubx.maths.DrivenODESystem("p'=0") 766 self.__params = [] 767 self.__lines = [] 768 self.__param_ix = {} 769 self.mnuPresets = pack_item(qubx.settingsGTK.PresetsMenu(title, qubx.pyenv.env.globals['QubX'].appname), self.action_area) 770 ## chkPreview 771 self.expr = str(self.__profile["Expr"].data)
772 - def set_expr(self, x):
773 self.txtExpr.value = x 774 self.__expr = x 775 self.__profile["Expr"].data = x 776 expr = x or "p'=0" 777 try: 778 self.__ode = qubx.maths.DrivenODESystem(expr) 779 for par in self.__params: 780 self.panParams.remove(par) 781 par.OnChange -= self.__ref(self.__onChangeParam) 782 self.__params[:] = [] 783 self.__param_ix = {} 784 for name in self.__ode.diffeq_names: 785 pname = '%s0' % name 786 self.__params.append( Shaper_ODE_Wizard_Param(pname, self.__profile[pname]) ) 787 for name in self.__ode.input_names: 788 if name != 'signal': 789 self.__params.append( Shaper_ODE_Wizard_Param(name, self.__profile[name]) ) 790 for i, par in enumerate(self.__params): 791 par.OnChange += self.__ref(self.__onChangeParam) 792 par.show() 793 self.panParams.pack_start(par, False, True) 794 self.__param_ix[par.pname] = i 795 self.panParams.set_sensitive(True) 796 except: 797 traceback.print_exc() ## show the message? 798 self.panParams.set_sensitive(False) 799 self.__ode = qubx.maths.DrivenODESystem("p'=0")
800 expr = property(lambda self: self.__expr, lambda self, x: self.set_expr(x))
801 - def get_var0(self):
802 if not self.__ode.diffeq_names: return [] 803 return [param.txt.value for param in self.__params[:len(self.__ode.diffeq_names)]]
804 var0 = property(lambda self: self.get_var0())
805 - def get_params(self):
806 if not self.__ode.input_names: return {} 807 return dict([(nm, self.__profile[nm].data[0]) for nm in self.__ode.input_names if (nm != 'signal')])
808 params = property(lambda self: self.get_params())
809 - def __onPreSet(self, settings, updates):
810 if updates.find("Expr"): 811 self.expr = str(updates["Expr"].data) 812 for upd in qubx.tree.children(updates): 813 if upd.name in self.__param_ix: 814 self.__params[ self.__param_ix[upd.name] ].update(upd)
815 - def __onChangeExpr(self, txt, val):
816 self.expr = val
817 - def __onChangeParam(self, param, val):
818 pass ##
819 - def run(self, recv_shaping):
820 response = gtk.Dialog.run(self) 821 if response == gtk.RESPONSE_ACCEPT: 822 if self.expr: 823 var0 = self.get_var0() 824 var0[0] = var0[0] or None # zero initial position: will use initial signal instead 825 params = self.get_params() 826 args = ['source', '"%s"' % self.expr] + [str(x) for x in var0] + ["%s=%f" % (key, val) for key,val in params.iteritems()] 827 shaping = "Shaper_ODE(%s)" % ', '.join(args) 828 else: 829 shaping = "" 830 recv_shaping(shaping) 831 self.hide() 832 return response
833 834 835 Shapers["Shaper_ODE"] = Shaper_ODE
836 837 -def BuildWizards():
838 global ODE_Wizard, SDP_Wizard 839 840 # delay building wizards until qubx.settings.SettingsMgr exists 841 if not (qubx.pyenv.env and ('QubX' in qubx.pyenv.env.globals) and qubx.settings.SettingsMgr): 842 gobject.timeout_add(500, BuildWizards) 843 return 844 845 ODE_Wizard = Shaper_ODE_Wizard("ODE System") 846 Shaper_Wizards["ODE System"] = ODE_Wizard.run 847 848 SDP_Wizard = Shaper_ODE_Wizard("Spring-Dashpot") 849 SDP_Wizard.expr = "pos' = velo; velo' = (-2*Damping*NatFreq*velo) - NatFreq**2 * (pos - signal)" 850 Shaper_Wizards["Spring-Dashpot"] = SDP_Wizard.run
851 BuildWizards() 852 853 854 @Propertied(Property('known_amps', [], 'List of known amplitude values'), 855 Property('add_deltas', True, 'False to use only known amp values'), 856 Property('delta', 1.0, 'Maximum standard deviation of a dwell'), 857 Property('min_dur_ms', 0.0, 'Minimum duration of a dwell, in milliseconds'))
858 -class ByDeltaIdealizer(gtk.HBox):
859 __explore_featured = ['global_name', 'robot', 'txtStatus', 'outStatus', 'shared_controls', 'idealize']
860 - def __init__(self):
861 gtk.HBox.__init__(self) 862 self.global_name = 'QubX.Modeling.Idealize.methods["By Delta (forward)"]' 863 self.__ref = Reffer() 864 self.propertied_connect_settings('Idealize_ByDelta') 865 self.robot = qubx.task.Robot('Idl By Delta', self.__ref(lambda: qubx.task.Tasks.add_task(self.robot)), 866 self.__ref(lambda: qubx.task.Tasks.remove_task(self.robot))) 867 self.robot.OnException += self.__ref(self.__onException) 868 self.robot.OnInterrupt += self.__ref(self.__onInterrupt) 869 870 self.set_size_request(200, 50) 871 self.txtStatus = gtk.TextView() 872 self.outStatus = qubx.GTK.TextViewAppender(self.txtStatus) 873 self.txtStatus.set_editable(False) 874 qubx.GTK.SetFixedWidth(self.txtStatus) 875 pack_scrolled(self.txtStatus, self, expand=True) 876 self.outStatus.write("""Starts a new event when the next point falls outside start +/- delta. 877 878 """) ### 879 880 column = pack_item(gtk.VBox(), self) 881 #h = pack_item(gtk.HBox(), column) 882 #pack_label('Known levels:', h) 883 #txt = pack_item(qubx.GTK.NumEntry(self.known_amps, acceptFloatList(), formatList('%.3g')), column) 884 #h = pack_item(gtk.HBox(), column) 885 #chk = pack_check('Add levels by delta', h) 886 #self.propertied_connect_check('add_deltas', chk) 887 h = pack_item(gtk.HBox(), column) 888 pack_label('Delta:', h) 889 txt = pack_item(qubx.GTK.NumEntry(self.delta, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True) 890 self.propertied_connect_NumEntry('delta', txt) 891 h = pack_item(gtk.HBox(), column) 892 pack_label('Min dur (ms):', h) 893 txt = pack_item(qubx.GTK.NumEntry(self.min_dur_ms, acceptFloatGreaterThanOrEqualTo(0.0), '%.3g', width_chars=6), h, at_end=True) 894 self.propertied_connect_NumEntry('min_dur_ms', txt) 895 self.shared_controls = pack_item(gtk.VBox(), column, at_end=True)
896
897 - def idealize(self, segments, model, on_finish):
898 QubX = qubx.global_namespace.QubX 899 segments = QubX.DataSource.get_segmentation() 900 if segments: 901 self.robot.do(self.robot_idealize, segments, self.known_amps, self.add_deltas, self.delta, self.min_dur_ms, on_finish) 902 else: 903 gobject.idle_add(on_finish)
904 - def robot_idealize(self, segments, known_amps, add_deltas, delta, min_dur_ms, on_finish):
905 try: 906 if segments: 907 datafile = segments[0].file 908 idealizer = qubx.fast.data.IdlStim(known_amps, add_deltas, delta, min_dur_ms, datafile.sampling*1e3) 909 910 tot_n = sum(seg.n for seg in segments) 911 read_n = 0 912 for seg in segments: 913 for chunk in seg.chunks: 914 idealizer.add(chunk.get_samples().samples) 915 read_n += chunk.n 916 self.robot.progress = read_n * 0.618 / tot_n 917 idealizer.done_add() 918 amps = idealizer.get_amps() 919 idlseg = [idealizer.get_next_dwells(seg.f, seg.n) for seg in segments] 920 self.robot.progress = .816 921 gobject.idle_add(self.__set_idl, datafile, segments[0].signal, segments, amps, idlseg) 922 finally: 923 gobject.idle_add(on_finish)
924 - def __set_idl(self, datafile, out_ix, segs, amps, idlseg):
925 ideal_out = datafile.ideal[out_ix] 926 for i in xrange(len(idlseg)): 927 ff, ll, cc = idlseg[i] 928 ideal_out.idl.set_dwells(len(ff), ff, ll, cc) 929 ideal_out.seg[segs[i].index].amp = amps 930 ideal_out.seg[segs[i].index].std = [0.0] * len(amps) 931 datafile.OnChangeIdealization(datafile, out_ix) 932 buf = cStringIO.StringIO() 933 buf.write("Levels:\n") 934 for a in amps: 935 buf.write("\t%.5g\n" % a) 936 buf.write("\n") 937 self.outStatus.write(buf.getvalue())
938 - def __onException(self, robot, typ, val, trace):
939 traceback.print_exception(typ, val, trace, file=self.outStatus)
940 - def __onInterrupt(self, robot, cancel):
941 cancel() # not interruptible yet, sorry (is there a stop flag in the config tree?)
942 943 944 @Propertied(Property('delta', 1.0, 'Maximum standard deviation of a dwell'), 945 Property('min_dur_ms', 0.0, 'Minimum duration of a dwell, in milliseconds'))
946 -class ByDeltaARIdealizer(gtk.HBox):
947 __explore_featured = ['global_name', 'robot', 'txtStatus', 'outStatus', 'shared_controls', 'idealize']
948 - def __init__(self):
949 gtk.HBox.__init__(self) 950 self.global_name = 'QubX.Modeling.Idealize.methods["By Delta (binary)"]' 951 self.__ref = Reffer() 952 self.propertied_connect_settings('Idealize_ByDeltaAR') 953 self.robot = qubx.task.Robot('Idl By Delta', self.__ref(lambda: qubx.task.Tasks.add_task(self.robot)), 954 self.__ref(lambda: qubx.task.Tasks.remove_task(self.robot))) 955 self.robot.OnException += self.__ref(self.__onException) 956 self.robot.OnInterrupt += self.__ref(self.__onInterrupt) 957 958 self.set_size_request(200, 50) 959 self.txtStatus = gtk.TextView() 960 self.outStatus = qubx.GTK.TextViewAppender(self.txtStatus) 961 self.txtStatus.set_editable(False) 962 qubx.GTK.SetFixedWidth(self.txtStatus) 963 pack_scrolled(self.txtStatus, self, expand=True) 964 self.outStatus.write("""Recursively subdivides the record in half until each interval has standard deviation of at most delta. 965 966 """) ### 967 968 column = pack_item(gtk.VBox(), self) 969 h = pack_item(gtk.HBox(), column) 970 pack_label('Delta:', h) 971 txt = pack_item(qubx.GTK.NumEntry(self.delta, acceptFloatGreaterThan(0.0), '%.3g', width_chars=6), h, at_end=True) 972 self.propertied_connect_NumEntry('delta', txt) 973 h = pack_item(gtk.HBox(), column) 974 pack_label('Min dur (ms):', h) 975 txt = pack_item(qubx.GTK.NumEntry(self.min_dur_ms, acceptFloatGreaterThanOrEqualTo(0.0), '%.3g', width_chars=6), h, at_end=True) 976 self.propertied_connect_NumEntry('min_dur_ms', txt) 977 self.shared_controls = pack_item(gtk.VBox(), column, at_end=True)
978
979 - def idealize(self, segments, model, on_finish):
980 QubX = qubx.global_namespace.QubX 981 segments = QubX.DataSource.get_segmentation() 982 if segments: 983 self.robot.do(self.robot_idealize, segments, self.delta, self.min_dur_ms, on_finish) 984 else: 985 gobject.idle_add(on_finish)
986 - def robot_idealize(self, segments, delta, min_dur_ms, on_finish):
987 try: 988 if segments: 989 datafile = segments[0].file 990 idealizer = qubx.fast.data.Idl_AdaptiveResample(delta, min_dur_ms, datafile.sampling*1e3) 991 992 tot_n = sum(seg.n for seg in segments) 993 read_n = 0 994 for seg in segments: 995 for chunk in seg.chunks: 996 idealizer.add(chunk.get_samples().samples) 997 read_n += chunk.n 998 self.robot.progress = read_n * 0.618 / tot_n 999 idealizer.done_add() 1000 amps, stds = idealizer.get_dist() 1001 idlseg = [idealizer.get_next_dwells(seg.f, seg.n) for seg in segments] 1002 self.robot.progress = .816 1003 gobject.idle_add(self.__set_idl, datafile, segments[0].signal, segments, amps, stds, idlseg) 1004 finally: 1005 gobject.idle_add(on_finish)
1006 - def __set_idl(self, datafile, out_ix, segs, amps, stds, idlseg):
1007 ideal_out = datafile.ideal[out_ix] 1008 for i in xrange(len(idlseg)): 1009 ff, ll, cc = idlseg[i] 1010 ideal_out.idl.set_dwells(len(ff), ff, ll, cc) 1011 ideal_out.seg[segs[i].index].amp = amps 1012 ideal_out.seg[segs[i].index].std = stds 1013 datafile.OnChangeIdealization(datafile, out_ix) 1014 buf = cStringIO.StringIO() 1015 buf.write("Levels:\n") 1016 for a in amps: 1017 buf.write("\t%.5g\n" % a) 1018 buf.write("\n") 1019 self.outStatus.write(buf.getvalue())
1020 - def __onException(self, robot, typ, val, trace):
1021 traceback.print_exception(typ, val, trace, file=self.outStatus)
1022 - def __onInterrupt(self, robot, cancel):
1023 cancel() # not interruptible yet, sorry (is there a stop flag in the config tree?)
1024 1025 1026 1027 1028 if __name__ == '__main__':
1029 - class Src(object):
1030 - def read(self, f, l, main_hold):
1031 return numpy.array([float(int(numpy.floor((x+f) * .2)) % 2) for x in xrange(l-f+1)])
1032 source = Src() 1033 source.segs = [Anon(f=0, l=31, file=Anon(sampling=0.1))] 1034 shaper = Shaper_ODE(source, "pos' = vel; vel' = (-2*damping*natfreq*vel) - natfreq**2 * (pos - signal)", None, 0.0, damping=0.4, natfreq=4.0) 1035 print shaper.read(0, 31) 1036 print shaper.read(0,15) 1037 print shaper.read(16,31) 1038