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."""
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
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()
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
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
183 if field == 'Name':
184 self.mnuSignal.get_model()[i][0] = val
186 self.__setting = True
187 self.mnuSignal.remove_text(i)
188 self.__setting = False
189 - def on_set(self, field, val, prev):
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()
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()
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)
299 self.txtShaping.value = shaping
300 self.__defer('Shaping', shaping)
320
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=""):
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
362
363
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)
374 active_names = property(lambda self: self.get_active_names())
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)
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()
429 model = property(lambda self: self.__model, lambda self, x: self.set_model(x))
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)
460 if field == 'Value':
461 cname = self.__data.constants.get(i, 'Name')
462 try:
463 self.OnChange(self, cname)
464 except ValueError:
465 pass
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)
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)
523 traceback.print_exception(typ, val, trace)
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)
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])
579 with self.robot.main_hold:
580 stim = datafile.stimuli.get_row(stim_ix)
581
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
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
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
635 - def read(self, f, l, main_hold):
636 return numpy.zeros(shape=(l-f+1,), dtype=numpy.float32)
637
639 - def __init__(self, segs, signal, latency, 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
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
668 - def __init__(self, segs, func, latency):
685 - def read(self, f, l, main_hold):
686 if not self.sources:
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:
692 return numpy.array([self.func(*args) for args in itertools.izip(*argses)], dtype=numpy.float32)
693
697
699 - def __init__(self, source, expr, *var0, **params):
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
748
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
771 self.expr = str(self.__profile["Expr"].data)
800 expr = property(lambda self: self.__expr, lambda self, x: self.set_expr(x))
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())
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())
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)
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
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
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'))
859 __explore_featured = ['global_name', 'robot', 'txtStatus', 'outStatus', 'shared_controls', 'idealize']
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
882
883
884
885
886
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):
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())
939 traceback.print_exception(typ, val, trace, file=self.outStatus)
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'))
947 __explore_featured = ['global_name', 'robot', 'txtStatus', 'outStatus', 'shared_controls', 'idealize']
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):
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())
1021 traceback.print_exception(typ, val, trace, file=self.outStatus)
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