def _update_output(self): """ Runs the power monitor """ # Check for/implement changes to settings #self.update_settings(0) # Get all current values try: p_in = self.pm.get_power(1) split_in = split(p_in) # Handle zero error except OverflowError: p_in = 0 split_in = (0, 0) try: p_ref = self.pm.get_power(2) split_ref = split(p_ref) except OverflowError: p_ref = 0 split_ref = (0, 0) try: efficiency = np.sqrt(p_ref / (p_in * self.calibration[0])) except ZeroDivisionError: efficiency = 0 values = [p_in, p_ref, efficiency] # For the two power readings, reformat. # E.g., split(0.003) will return (3, -3) # And prefix(-3) will return 'm' formatted_values = [split_in[0], split_ref[0], efficiency] value_prefixes = [ prefix(split_val[1]) for split_val in [split_in, split_ref] ] # Update GUI for plot_no in range(self.num_plots): # Update Number self.widgets['number_widget'][plot_no].setValue( formatted_values[plot_no]) # Update Curve self.plotdata[plot_no] = np.append(self.plotdata[plot_no][1:], values[plot_no]) self.widgets[f'curve_{plot_no}'].setData(self.plotdata[plot_no]) if plot_no < 2: self.widgets["label_widget"][plot_no].setText( f'{value_prefixes[plot_no]}W')
def _on_channels_updated(message): ''' Message keys: - ``"n"``: number of actuated channel - ``"actuated"``: list of actuated channel identifiers. - ``"start"``: ms counter before setting shift registers - ``"end"``: ms counter after setting shift registers ''' global actuated_area global actuated_channels actuated_channels = message['actuated'] if actuated_channels: actuated_electrodes = \ dmf_device.actuated_electrodes(actuated_channels).dropna() actuated_areas = \ dmf_device.electrode_areas.ix[actuated_electrodes.values] actuated_area = actuated_areas.sum() else: actuated_area = 0 # m^2 area area = actuated_area * (1e-3**2) # Approximate area in SI units. value, pow10 = si.split(np.sqrt(area)) si_unit = si.SI_PREFIX_UNITS[len(si.SI_PREFIX_UNITS) // 2 + pow10 // 3] status = ('actuated electrodes: %s (%.1f %sm^2)' % (actuated_channels, value**2, si_unit)) _L().debug(status) channels_updated.set()
def si(value, wrap_span_class=None): base, exp = split(int(value), precision=1) prefix_unit = "" if exp: prefix_unit = prefix(exp) if wrap_span_class: prefix_unit = f'<span class="{wrap_span_class}">{prefix_unit}</span>' if base == int(base): return mark_safe(f"{base:.0f}{prefix_unit}") else: return mark_safe(f"{base:.1f}{prefix_unit}")
def scale_f(frequency): """Scale the axis and add the corresponding SI prefix. Arguments: frequency {np.ndarray} -- the variable along an axis Returns: str, np.ndarray, int -- the prefix, the scaled variables, the exponent corresponding to the prefix """ freq = np.copy(frequency) exp = sip.split(np.max(freq))[1] freq /= 10 ** exp pre = sip.prefix(exp) return pre, freq, exp
def actuate(proxy, dmf_device, electrode_states, duration_s=0, volume_threshold=0, c_unit_area=None): ''' XXX Coroutine XXX Actuate electrodes according to specified states. Parameters ---------- electrode_states : pandas.Series duration_s : float, optional If ``volume_threshold`` step option is set, maximum duration before timing out. Otherwise, time to actuate before actuation is considered completed. c_unit_area : float, optional Specific capacitance, i.e., units of $F/mm^2$. Returns ------- actuated_electrodes : list List of actuated electrode IDs. .. versionchanged:: 2.39.0 Do not save actuation uuid for volume threshold actuations. .. versionchanged:: 2.39.0 Fix actuated area field typo. .. versionchanged:: 2.39.0 Compute actuated area for static (i.e., delay-based) actuations. ''' requested_electrodes = electrode_states[electrode_states > 0].index requested_channels = ( dmf_device.channels_by_electrode.loc[requested_electrodes]) actuated_channels = pd.Series() actuated_area = 0 channels_updated = asyncio.Event() def _on_channels_updated(message): ''' Message keys: - ``"n"``: number of actuated channel - ``"actuated"``: list of actuated channel identifiers. - ``"start"``: ms counter before setting shift registers - ``"end"``: ms counter after setting shift registers ''' global actuated_area global actuated_channels actuated_channels = message['actuated'] if actuated_channels: actuated_electrodes = \ dmf_device.actuated_electrodes(actuated_channels).dropna() actuated_areas = \ dmf_device.electrode_areas.ix[actuated_electrodes.values] actuated_area = actuated_areas.sum() else: actuated_area = 0 # m^2 area area = actuated_area * (1e-3**2) # Approximate area in SI units. value, pow10 = si.split(np.sqrt(area)) si_unit = si.SI_PREFIX_UNITS[len(si.SI_PREFIX_UNITS) // 2 + pow10 // 3] status = ('actuated electrodes: %s (%.1f %sm^2)' % (actuated_channels, value**2, si_unit)) _L().debug(status) channels_updated.set() proxy.signals.signal('channels-updated').connect(_on_channels_updated) # Criteria that must be met to set target capacitance. threshold_criteria = [ duration_s > 0, volume_threshold > 0, len(requested_electrodes) > 0, c_unit_area is not None ] _L().debug('threshold_criteria: `%s`', threshold_criteria) result = {} actuated_areas = ( dmf_device.electrode_areas.ix[requested_electrodes.values]) actuated_area = actuated_areas.sum() if not all(threshold_criteria): # ## Case 1: no volume threshold specified. # 1. Set control board state of channels according to requested # actuation states. # 2. Wait for channels to be actuated. actuated_channels = \ db.threshold.actuate_channels(proxy, requested_channels, timeout=5) # 3. Connect to `capacitance-updated` signal to record capacitance # values measured during the step. capacitance_messages = [] def _on_capacitance_updated(message): message['actuated_channels'] = actuated_channels message['actuated_area'] = actuated_area capacitance_messages.append(message) proxy.signals.signal('capacitance-updated')\ .connect(_on_capacitance_updated) # 4. Delay for specified duration. try: yield asyncio.From(asyncio.sleep(duration_s)) finally: proxy.signals.signal('capacitance-updated')\ .disconnect(_on_capacitance_updated) else: # ## Case 2: volume threshold specified. # # A volume threshold has been set for this step. # Calculate target capacitance based on actuated area. # # Note: `app_values['c_liquid']` represents a *specific # capacitance*, i.e., has units of $F/mm^2$. meters_squared_area = actuated_area * (1e-3**2) # m^2 area # Approximate length of unit side in SI units. si_length, pow10 = si.split(np.sqrt(meters_squared_area)) si_unit = si.SI_PREFIX_UNITS[len(si.SI_PREFIX_UNITS) // 2 + pow10 // 3] target_capacitance = volume_threshold * actuated_area * c_unit_area logger = _L() # use logger with function context if logger.getEffectiveLevel() <= logging.DEBUG: message = ('target capacitance: %sF (actuated area: (%.1f ' '%sm^2) actuated channels: %s)' % (si.si_format(target_capacitance), si_length** 2, si_unit, requested_channels)) map(logger.debug, message.splitlines()) # Wait for target capacitance to be reached in background thread, # timing out if the specified duration is exceeded. co_future = \ db.threshold.co_target_capacitance(proxy, requested_channels, target_capacitance, allow_disabled=False, timeout=duration_s) try: dropbot_event = yield asyncio.From( asyncio.wait_for(co_future, duration_s)) _L().debug('target capacitance reached: `%s`', dropbot_event) actuated_channels = dropbot_event['actuated_channels'] capacitance_messages = dropbot_event['capacitance_updates'] # Add actuated area to capacitance update messages. for capacitance_i in capacitance_messages: capacitance_i['actuated_area'] = actuated_area capacitance_i.pop('actuation_uuid1', None) result['threshold'] = { 'target': dropbot_event['target'], 'measured': dropbot_event['new_value'], 'start': dropbot_event['start'], 'end': dropbot_event['end'] } # Show notification in main window status bar. if logger.getEffectiveLevel() <= logging.DEBUG: status = ('reached %sF (> %sF) over electrodes: %s (%.1f ' '%sm^2) after %ss' % (si.si_format(result['threshold']['measured']), si.si_format(result['threshold']['target']), actuated_channels, si_length**2, si_unit, (dropbot_event['end'] - dropbot_event['start']).total_seconds())) logger.debug(status) except asyncio.TimeoutError: raise RuntimeError('Timed out waiting for target capacitance.') yield asyncio.From(channels_updated.wait()) actuated_electrodes = ( dmf_device.electrodes_by_channel.loc[actuated_channels]) # Return list of actuated channels (which _may_ be fewer than the # number of requested actuated channels if one or more channels is # _disabled_). result.update({ 'actuated_electrodes': actuated_electrodes, 'capacitance_messages': capacitance_messages, 'actuated_channels': actuated_channels, 'actuated_area': actuated_area }) raise asyncio.Return(result)