Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sensor_toolbar.py
diff options
context:
space:
mode:
authorWalter Bender <walter.bender@gmail.com>2011-12-14 05:54:31 (GMT)
committer Walter Bender <walter.bender@gmail.com>2011-12-14 05:54:31 (GMT)
commit46a22d0f213a54c9465407a0797881a70bba7a3a (patch)
tree25ffd2f751e6242c74085f6d78df3b11f1d035e9 /sensor_toolbar.py
parent66dfdf405803f089b8f2216fb7aa44d65baa19f2 (diff)
consolidated onto sensor_toolbar
Diffstat (limited to 'sensor_toolbar.py')
-rw-r--r--sensor_toolbar.py457
1 files changed, 343 insertions, 114 deletions
diff --git a/sensor_toolbar.py b/sensor_toolbar.py
index ed6e8d5..b03c34d 100644
--- a/sensor_toolbar.py
+++ b/sensor_toolbar.py
@@ -21,71 +21,110 @@ import gobject
import os
from gettext import gettext as _
-from config import ICONS_DIR
+from config import ICONS_DIR, CAPTURE_GAIN, MIC_BOOST, XO1, XO15, XO175
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.combobox import ComboBox
from sugar.graphics.toolcombobox import ToolComboBox
+from sugar.graphics.radiotoolbutton import RadioToolButton
import logging
-log = logging.getLogger('Measure')
+log = logging.getLogger('measure-activity')
log.setLevel(logging.DEBUG)
+def _is_xo(hw):
+ """ Return True if this is xo hardware """
+ return hw in [XO1, XO15, XO175]
+
+
class SensorToolbar(gtk.Toolbar):
- """ The toolbar for resitance and voltage sensors """
+ """ The toolbar for specifiying the sensor: sound, resitance, or
+ voltage """
+
+ LOWER = 0.0
+ UPPER = 1.0
+ STR_DC = \
+ _("Sensors, DC (connect sensor to pink 'Mic In' on left side of XO)") \
+ + ' '
+ STR_AC = _('Sound') + ' '
+ STR_RESISTANCE = _('Resistance') + ' ' + _('Ohms') + ' '
+ STR_VOLTAGE = _('Voltage') + ' ' + _('Volts') + ' '
+ STR_TIME = _('Time Base') + ' '
+ STR_FREQUENCY = _('Frequency Base') + ' '
+ STR_INVERT = ' ' + _('Invert') + ' '
+ STR_XAXIS_TEXT = _('X Axis Scale: 1 division = %(division)s %(unit)s')
+ # TRANSLATORS: This is milli seconds.
+ MS = _('ms')
+ # TRANSLATORS: This is Hertz, so 1/second.
+ HZ = _('Hz')
def __init__(self, activity, channels):
""" By default, start with resistance mode """
gtk.Toolbar.__init__(self)
- self.mode = 'resistance'
+ self.activity = activity
self._channels = channels
self.values = []
for i in range(self._channels):
- self.values.append(0.0)
+ self.values.append('')
- self._STR_BASIC = \
- _("Sensors, DC (connect sensor to pink 'Mic In' on left side of XO)") \
- + ' '
- self._STR_R = _('Bias/Offset Enabled') + ' ' + _('Ohms') + ' '
- self._STR_V = _('Bias/Offset Disabled') + ' ' + _('Volts') + ' '
- self._STR_I = ' ' + _('Invert') + ' '
+ self.string_for_textbox = ''
- self.string_for_textbox = ""
+ self.gain = 1.0
+ self.y_mag = 3.0
+ self.capture_gain = CAPTURE_GAIN
+ self.mic_boost = MIC_BOOST
- self.activity = activity
- self.activity.audiograb.set_sensor(self)
+ self.mode = None
+
+ # Set up Time-domain Button
+ self.time = RadioToolButton(group=None)
+ self.time.set_named_icon('media-audio')
+ self.insert(self.time, -1)
+ self.time.set_tooltip(_('Audio'))
+ self.time.connect(
+ 'clicked', self.analog_resistance_voltage_mode_cb, 'sound')
# Set up Resistance Button
- if self.activity.has_toolbarbox:
- self.resistance = ToolButton('bias-on')
- else:
- self.resistance = ToolButton('bias-on2')
- self.insert(self.resistance, -1)
+ self.resistance = RadioToolButton(group=self.time)
+ self.resistance.set_named_icon('resistance')
+ if _is_xo(self.activity.hw):
+ self.insert(self.resistance, -1)
self.resistance.show()
self.resistance.set_tooltip(_('Resistance Sensor'))
- self.resistance.connect('clicked', self.resistance_voltage_mode_cb,
- 'resistance')
+ self.resistance.connect('clicked',
+ self.analog_resistance_voltage_mode_cb,
+ 'resistance')
# Set up Voltage Button
- self.voltage = ToolButton('bias-off')
- self.insert(self.voltage, -1)
+ self.voltage = RadioToolButton(group=self.time)
+ self.voltage.set_named_icon('voltage')
+ if _is_xo(self.activity.hw):
+ self.insert(self.voltage, -1)
self.voltage.set_tooltip(_('Voltage Sensor'))
- self.voltage.connect('clicked', self.resistance_voltage_mode_cb,
- 'voltage')
+ self.voltage.connect('clicked',
+ self.analog_resistance_voltage_mode_cb,
+ 'voltage')
separator = gtk.SeparatorToolItem()
separator.props.draw = True
self.insert(separator, -1)
- # Set up Logging Interval combo box
- self.log_interval_img = gtk.Image()
- self.log_interval_img.set_from_file(os.path.join(ICONS_DIR,
- 'sample_rate.svg'))
- self.log_interval_img_tool = gtk.ToolItem()
- self.log_interval_img_tool.add(self.log_interval_img)
- self.insert(self.log_interval_img_tool, -1)
+ # Set up Frequency-domain Button
+ self.freq = ToolButton('domain-time')
+ self.insert(self.freq, -1)
+ self.freq.show()
+ self.freq.set_tooltip(_('Time Base'))
+ self.freq.connect('clicked', self._timefreq_control_cb)
+
+ # Set up Frequency-control Slider and corresponding buttons
+ if not self.activity.has_toolbarbox:
+ self.add_frequency_slider(self)
+
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = True
+ self.insert(separator, -1)
self._log_interval_combo = ComboBox()
self.interval = [_('1/10 second'), _('1 second'), _('30 seconds'),
@@ -112,10 +151,234 @@ class SensorToolbar(gtk.Toolbar):
self._record.set_tooltip(_('Start Recording'))
self._record.connect('clicked', self.record_control_cb)
+ if self.activity.has_toolbarbox:
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = True
+ self.insert(separator, -1)
+
+ # Set up Trigger Combo box
+ self._trigger_combo = ComboBox()
+ self.trigger = [_('None'), _('Rising Edge'), _('Falling Edge')]
+ self.trigger_conf = [self.activity.wave.TRIGGER_NONE,
+ self.activity.wave.TRIGGER_POS,
+ self.activity.wave.TRIGGER_NEG]
+ self._trigger_changed_id = self._trigger_combo.connect('changed',
+ self.update_trigger_control)
+ for i, s in enumerate(self.trigger):
+ self._trigger_combo.append_item(i, s, None)
+ self._trigger_combo.set_active(0)
+ if hasattr(self._trigger_combo, 'set_tooltip_text'):
+ self._trigger_combo.set_tooltip_text(_('Create a trigger'))
+ self._trigger_tool = ToolComboBox(self._trigger_combo)
+ self.insert(self._trigger_tool, -1)
self.show_all()
- def set_sample_value(self, value=0.0, channel=0):
- """ Write a sample value to the textbox """
+ def add_frequency_slider(self, toolbar):
+ """ Either on the Sound toolbar or the Main toolbar """
+ self._freq_stepper_up = ToolButton('freq-high')
+ self._freq_stepper_up.set_tooltip(_('Zoom out'))
+ self._freq_stepper_up.connect('clicked', self._freq_stepper_up_cb)
+ self.activity.adjustmentf = gtk.Adjustment(
+ 0.5, self.LOWER, self.UPPER, 0.01, 0.1, 0)
+ self.activity.adjustmentf.connect('value_changed', self.cb_page_sizef)
+ self._freq_range = gtk.HScale(self.activity.adjustmentf)
+ self._freq_range.set_inverted(True)
+ self._freq_range.set_draw_value(False)
+ self._freq_range.set_update_policy(gtk.UPDATE_CONTINUOUS)
+ self._freq_range.set_size_request(120, 15)
+
+ self._freq_stepper_down = ToolButton('freq-low')
+ self._freq_stepper_down.set_tooltip(_('Zoom in'))
+ self._freq_stepper_down.connect('clicked', self._freq_stepper_down_cb)
+
+ self._freq_range_tool = gtk.ToolItem()
+ self._freq_range_tool.add(self._freq_range)
+
+ toolbar.insert(self._freq_stepper_up, -1)
+ toolbar.insert(self._freq_range_tool, -1)
+ toolbar.insert(self._freq_stepper_down, -1)
+ return
+
+ def update_trigger_control(self, *args):
+ """ Callback for trigger control """
+ active = self._trigger_combo.get_active()
+ if active == -1:
+ return
+
+ self.activity.wave.set_trigger(self.trigger_conf[active])
+ return
+
+ def _timefreq_control_cb(self, button=None):
+ """ Callback for Freq. Button """
+ if self.activity.wave.get_fft_mode():
+ self.freq_mode = False
+ self.activity.wave.set_fft_mode(False)
+ self.freq.set_icon('domain-time')
+ self.freq.set_tooltip(_('Time Base'))
+ else:
+ self.freq_mode = True
+ self.activity.wave.set_fft_mode(True)
+ self.freq.set_icon('domain-freq')
+ self.freq.set_tooltip(_('Frequency Base'))
+ self._update_string_for_textbox()
+ return False
+
+ def analog_resistance_voltage_mode_cb(self, button=None,
+ mode_to_set='sound'):
+ """ Callback for Analog/Resistance/Voltage Buttons """
+ if self.mode == mode_to_set:
+ return
+ if self.activity.CONTEXT == 'sound':
+ self.sound_context_off()
+ else:
+ self.sensor_context_off()
+ self.set_mode(mode_to_set)
+ # Force time domain when switching modes
+ if self.activity.wave.get_fft_mode():
+ self._timefreq_control_cb()
+ if mode_to_set == 'sound':
+ self.set_sound_context()
+ self._update_string_for_textbox()
+ if self.activity.has_toolbarbox:
+ self.activity.label_mode_img.set_from_pixbuf(
+ self.activity.mode_images['sound'])
+ self.activity.label_mode_img.set_tooltip_text(_('Time Base'))
+ elif mode_to_set == 'resistance':
+ self.set_sensor_context()
+ self._update_string_for_textbox()
+ if self.activity.has_toolbarbox:
+ self.activity.label_mode_img.set_from_pixbuf(
+ self.activity.mode_images['resistance'])
+ self.activity.label_mode_img.set_tooltip_text(
+ _('Resistance Sensor'))
+ elif mode_to_set == 'voltage':
+ self.set_sensor_context()
+
+ self._update_string_for_textbox()
+ if self.activity.has_toolbarbox:
+ self.activity.label_mode_img.set_from_pixbuf(
+ self.activity.mode_images['voltage'])
+ self.activity.label_mode_img.set_tooltip_text(
+ _('Voltage Sensor'))
+ else:
+ logging.error('unknown mode %s' % (mode_to_set))
+ return False
+
+ def set_mode(self, mode='sound'):
+ """ Set the mixer settings to match the current mode. """
+ self.mode = mode
+ log.debug('>>>>>>>>>>>>>>> setting sensor type to %s' % (mode))
+ self.activity.audiograb.set_sensor_type(self.mode)
+ # FIXME: turn off logging
+ for i in range(self._channels):
+ self.values[i] = 0.0
+ return
+
+ def get_mode(self):
+ """ Get the mixer settings. """
+ return self.mode
+
+ def _freq_stepper_up_cb(self, button=None):
+ """ Moves the horizontal zoom slider to the left one notch,
+ where one notch is 1/100 of the total range. This correspond
+ to zooming out as a larger number of Hertz or milliseconds
+ will be represented by the same space on the screen. """
+ new_value = self._freq_range.get_value() +\
+ (self.UPPER - self.LOWER) / 100.0
+ if new_value <= self.UPPER:
+ self._freq_range.set_value(new_value)
+ else:
+ self._freq_range.set_value(self.UPPER)
+
+ def _freq_stepper_down_cb(self, button=None):
+ """ Moves the horizontal zoom slider to the right one notch,
+ where one notch is 1/100 of the total range. This corresponds
+ to zooming in. """
+ new_value = self._freq_range.get_value() -\
+ (self.UPPER - self.LOWER) / 100.0
+ if new_value >= self.LOWER:
+ self._freq_range.set_value(new_value)
+ else:
+ self._freq_range.set_value(self.LOWER)
+
+ def cb_page_sizef(self, button=None):
+ """ Callback to scale the frequency range (zoom in and out) """
+ if self._update_page_size_id:
+ gobject.source_remove(self._update_page_size_id)
+ self._update_page_size_id =\
+ gobject.timeout_add(250, self.update_page_size)
+ return True
+
+ def update_page_size(self):
+ """ Set up the scaling of the display. """
+ self._update_page_size_id = None
+ new_value = round(self.activity.adjustmentf.value * 100.0) / 100.0
+ if self.activity.adjustmentf.value != new_value:
+ self.activity.adjustmentf.value = new_value
+ return False
+ time_div = 0.001 * max(self.activity.adjustmentf.value, 0.05)
+ freq_div = 1000 * max(self.activity.adjustmentf.value, 0.01)
+ self.activity.wave.set_div(time_div, freq_div)
+ self._update_string_for_textbox()
+ return False
+
+ def set_sound_context(self):
+ """ Called when analog sensing is selected """
+ self.set_show_hide_windows(mode='sound')
+ # self.sensor_context_off()
+ # gobject.timeout_add(500, self.sound_context_on)
+ gobject.timeout_add(1000, self.sound_context_on)
+ self.activity.CONTEXT = 'sound'
+
+ def set_sensor_context(self):
+ """ Called when digital sensing is selected """
+ self.set_show_hide_windows(mode='sensor')
+ # self.sound_context_off()
+ # gobject.timeout_add(500, self.sensor_context_on)
+ gobject.timeout_add(1000, self.sensor_context_on)
+ self.activity.CONTEXT = 'sensor'
+
+ def set_show_hide_windows(self, mode='sound'):
+ """ Shows the appropriate window identified by the mode """
+ self.activity.wave.set_context_on()
+ for i in range(self.activity.audiograb.channels):
+ self.activity.side_toolbars[i].set_show_hide(True, mode)
+
+ def sensor_context_off(self):
+ """ Called when a DC sensor is no longer selected """
+ # self.activity.audiograb.pause_grabbing()
+ self.activity.audiograb.stop_grabbing()
+
+ def sensor_context_on(self):
+ """ Called when a DC sensor is selected """
+ # self.activity.audiograb.set_sensor_type(self.mode)
+ self._update_string_for_textbox()
+ # FIXME
+ self.activity.wave.set_trigger(self.activity.wave.TRIGGER_NONE)
+ # self.activity.audiograb.resume_grabbing()
+ self.activity.audiograb.start_grabbing()
+ return False
+
+ def sound_context_off(self):
+ """ Called when an analog sensor is no longer selected """
+ self.gain, self.y_mag = self.activity.wave.get_mag_params()
+ self.capture_gain = self.activity.audiograb.get_capture_gain()
+ self.mic_boost = self.activity.audiograb.get_mic_boost()
+ # self.activity.audiograb.stop_sound_device()
+ self.activity.audiograb.stop_grabbing()
+
+ def sound_context_on(self):
+ """ Called when an analog sensor is selected """
+ # self.activity.audiograb.set_sensor_type('sound')
+ self.activity.wave.set_mag_params(self.gain, self.y_mag)
+ self._update_string_for_textbox()
+ self.update_trigger_control()
+ # self.activity.audiograb.start_sound_device()
+ self.activity.audiograb.start_grabbing()
+ return False
+
+ def set_sample_value(self, value='', channel=0):
+ """ Write a sample value to the textbox. """
gtk.threads_enter()
self.values[channel] = value
self._update_string_for_textbox()
@@ -123,33 +386,38 @@ class SensorToolbar(gtk.Toolbar):
return
def record_control_cb(self, button=None):
- """Depending upon the selected interval, does either a logging
- session, or just logs the current buffer"""
-
- if not self.activity.LOGGING_IN_SESSION:
+ """ Depending upon the selected interval, does either a logging
+ session, or just logs the current buffer. """
+ if self.activity.audiograb.we_are_logging:
+ self.activity.audiograb.set_logging_params(start_stop=False)
+ self._record.set_icon('media-record')
+ self._record.show()
+ self._record.set_tooltip(_('Start Recording'))
+ else:
Xscale = (1.00 / self.activity.audiograb.get_sampling_rate())
Yscale = 0.0
interval = self.interval_convert()
username = self.activity.nick
- self.activity.data_logger.start_new_session(
- username, Xscale, Yscale, _(self.logging_interval_status),
- channels=self._channels)
- self.activity.audiograb.set_logging_params(True, interval, False)
- self.activity.LOGGING_IN_SESSION = True
+ if self.activity.wave.get_fft_mode():
+ self.activity.data_logger.start_new_session(
+ username, Xscale, Yscale, _(self.logging_interval_status),
+ channels=self._channels, mode='frequency')
+ else:
+ self.activity.data_logger.start_new_session(
+ username, Xscale, Yscale, _(self.logging_interval_status),
+ channels=self._channels, mode=self.mode)
+ self.activity.audiograb.set_logging_params(
+ start_stop=True, interval=interval, screenshot=False)
self._record.set_icon('record-stop')
self._record.show()
self._record.set_tooltip(_('Stop Recording'))
- else:
- self.activity.audiograb.set_logging_params(False)
- self.activity.LOGGING_IN_SESSION = False
- self._record.set_icon('media-record')
- self._record.show()
- self._record.set_tooltip(_('Start Recording'))
+ self.activity.new_recording = True
def interval_convert(self):
- """Converts interval string to an integer that denotes the number
- of times the audiograb buffer must be called before a value is written.
- When set to 0, the whole of current buffer will be written"""
+ """ Converts interval string to an integer that denotes the
+ number of times the audiograb buffer must be called before a
+ value is written. When set to 0, the whole of current buffer
+ will be written. """
interval_dictionary = {'1/10 second': 0.1, '1 second': 1,
'30 seconds': 30,
'5 minutes': 300, '30 minutes': 1800}
@@ -168,70 +436,31 @@ class SensorToolbar(gtk.Toolbar):
self.logging_interval_status = \
intervals[self._log_interval_combo.get_active()]
- def resistance_voltage_mode_cb(self, button=None,
- mode_to_set='resistance'):
- """ Callback for Resistance/Voltage Buttons """
-
- # Make sure the current context is for sensor capture.
- if self.activity.CONTEXT != 'sensor':
- self.activity.set_sensor_context()
-
- self.set_mode(mode_to_set)
- if mode_to_set == 'resistance':
- self.resistance.set_icon('bias-on2')
- self.voltage.set_icon('bias-off')
- self.resistance.show()
- self.voltage.show()
- self._update_string_for_textbox()
- if self.activity.has_toolbarbox:
- self.activity.label_button.set_icon('bias-on2')
- self.activity.label_button.set_tooltip(_('Resistance Sensor'))
- elif mode_to_set == 'voltage':
- self.resistance.set_icon('bias-on')
- self.voltage.set_icon('bias-off2')
- self.resistance.show()
- self.voltage.show()
- self._update_string_for_textbox()
- if self.activity.has_toolbarbox:
- self.activity.label_button.set_icon('bias-off2')
- self.activity.label_button.set_tooltip(_('Voltage Sensor'))
- else:
- logging.error('unknown mode %s' % (mode_to_set))
- if self.activity.has_toolbarbox:
- self.activity.sound_toolbar.time.set_icon('domain-time')
- self.activity.sound_toolbar.freq.set_icon('domain-freq')
- return False
-
- def set_mode(self, mode='resistance'):
- """ Set the mixer settings to match the current mode. """
- self.mode = mode
- self.activity.audiograb.set_sensor_type(self.mode)
- for i in range(self._channels):
- self.values[i] = 0.0
- return
-
- def context_off(self):
- """ Called when sensor toolbar is no longer selected. """
- self.activity.audiograb.pause_grabbing()
-
- def context_on(self):
- """ Called when sensor toolbar is selected. """
- self.activity.audiograb.resume_grabbing()
- self.activity.audiograb.set_sensor_type(self.mode)
- self._update_string_for_textbox()
- self.activity.wave.set_trigger(self.activity.wave.TRIGGER_NONE)
- return False
-
def _update_string_for_textbox(self):
""" Update the status field at the bottom of the canvas. """
- self.string_for_textbox = ""
- self.string_for_textbox += (self._STR_BASIC + "\n")
- if self.mode == 'resistance':
- self.string_for_textbox += self._STR_R
+ if self.mode == 'sound':
+ self.string_for_textbox = (self.STR_AC + '\t')
+ elif self.mode == 'resistance':
+ self.string_for_textbox = (self.STR_DC + "\n")
+ self.string_for_textbox += self.STR_RESISTANCE
+ else:
+ self.string_for_textbox = (self.STR_DC + "\n")
+ self.string_for_textbox += self.STR_VOLTAGE
+ if self.activity.wave.get_fft_mode():
+ scalex = self.STR_XAXIS_TEXT % {
+ 'unit': self.HZ, 'division': self.activity.wave.freq_div}
+ self.string_for_textbox += self.STR_FREQUENCY
+ self.string_for_textbox += ('\n' + scalex)
+ elif self.mode == 'sound':
+ scalex = self.STR_XAXIS_TEXT % {
+ 'unit': self.MS,
+ 'division': self.activity.wave.time_div * 1000}
+ self.string_for_textbox += self.STR_TIME
+ self.string_for_textbox += ('\n' + scalex)
else:
- self.string_for_textbox += self._STR_V
+ for i in range(self._channels):
+ self.string_for_textbox += '\t(%s)' % (self.values[i])
+ # FIX ME
if self.activity.wave.get_invert_state():
- self.string_for_textbox += self._STR_I
- for i in range(self._channels):
- self.string_for_textbox += '\t(%0.3f)' % (self.values[i])
+ self.string_for_textbox += self.STR_INVERT
self.activity.text_box.set_data_params(0, self.string_for_textbox)