Пример #1
0
class EnumSetting(Setting):
    values = List()
    traits_view = View(
        VGroup(
            Item('full_name', label='Name', style='readonly'),
            Item('value', editor=EnumEditor(name='values'),
                 visible_when='confirmed_set and not readonly'),
            Item('value', style='readonly',
                 visible_when='not confirmed_set or readonly'),
            UItem('default_value',
                  style='readonly',
                  editor=MultilineTextEditor(TextEditor(multi_line=True)),
                  show_label=True,
                  resizable=True),
            UItem(
                'description',
                style='readonly',
                editor=MultilineTextEditor(TextEditor(multi_line=True)),
                show_label=True,
                resizable=True),
            UItem(
                'notes',
                label="Notes",
                height=-1,
                editor=MultilineTextEditor(TextEditor(multi_line=True)),
                style='readonly',
                show_label=True,
                resizable=True),
            show_border=True,
            label='Setting', ), )

    def __init__(self, name, section, value, ordering, values, **kwargs):
        self.values = values
        Setting.__init__(self, name, section, value, ordering, **kwargs)
Пример #2
0
 def trait_view(self, name=None, view_element=None):
     return View(
         VGroup(
             Item('full_name', label='Name', style='readonly'),
             Item('value',
                  editor=EnumEditor(name='values'),
                  visible_when='confirmed_set and not readonly'),
             Item('value',
                  style='readonly',
                  visible_when='not confirmed_set or readonly'),
             UItem('default_value',
                   style='readonly',
                   editor=MultilineTextEditor(TextEditor(multi_line=True)),
                   show_label=True,
                   resizable=True),
             UItem('description',
                   style='readonly',
                   editor=MultilineTextEditor(TextEditor(multi_line=True)),
                   show_label=True,
                   resizable=True),
             UItem('notes',
                   label="Notes",
                   height=-1,
                   editor=MultilineTextEditor(TextEditor(multi_line=True)),
                   style='readonly',
                   show_label=True,
                   resizable=True),
             show_border=True,
             label='Setting',
         ), )
Пример #3
0
 def trait_view(self, name=None, view_element=None):
     return View(
         VGroup(
             Item('full_name', label='Name', style='readonly'),
             Item('value',
                  editor=TextEditor(auto_set=False,
                                    enter_set=True,
                                    format_func=self.format),
                  visible_when='confirmed_set and not readonly'),
             Item('value',
                  style='readonly',
                  visible_when='not confirmed_set or readonly',
                  editor=TextEditor(readonly_allow_selection=True,
                                    format_func=self.format)),
             Item('units', style='readonly'),
             Item('value_on_device',
                  style='readonly',
                  visible_when='truncated'),
             Item('setting_type', style='readonly'),
             Item('digits', style='readonly'),
             UItem('default_value',
                   style='readonly',
                   height=-1,
                   editor=MultilineTextEditor(TextEditor(multi_line=True)),
                   show_label=True,
                   resizable=True),
             UItem('description',
                   style='readonly',
                   editor=MultilineTextEditor(TextEditor(multi_line=True)),
                   show_label=True,
                   resizable=True),
             UItem('notes',
                   label="Notes",
                   height=-1,
                   editor=MultilineTextEditor(TextEditor(multi_line=True)),
                   style='readonly',
                   show_label=True,
                   resizable=True),
             show_border=True,
             label='Setting',
         ), )
Пример #4
0
class Setting(SettingBase):
  full_name = Str()
  section = Str()

  traits_view = View(
    VGroup(
      Item('full_name', label='Name', style='readonly'),
      Item('value', editor=TextEditor(auto_set=False, enter_set=True)),
      Item('description', style='readonly'),
      Item('units', style='readonly'),
      Item('default_value', style='readonly'),
      UItem('notes', label="Notes", height=-1,
            editor=MultilineTextEditor(TextEditor(multi_line=True)), style='readonly',
            show_label=True, resizable=True),
      show_border=True,
      label='Setting',
    ),
  )

  def __init__(self, name, section, value, ordering, settings):
    self.name = name
    self.section = section
    self.full_name = "%s.%s" % (section, name)
    self.value = value
    self.ordering = ordering
    self.settings = settings
    self.expert = settings.settings_yaml.get_field(section, name, 'expert')
    self.description = settings.settings_yaml.get_field(section,
                                                           name, 'Description')
    self.units = settings.settings_yaml.get_field(section, name, 'units')
    self.notes = settings.settings_yaml.get_field(section, name, 'Notes')
    self.default_value = settings.settings_yaml.get_field(section, name,
                                                             'default value')

  def _value_changed(self, name, old, new):
    if (old != new and
        old is not Undefined and
        new is not Undefined):
      if type(self.value) == unicode:
        self.value = self.value.encode('ascii', 'replace')
      self.settings.set(self.section, self.name, self.value)
Пример #5
0
class SkylarkView(HasTraits):
    information = String(
        "Skylark is Swift Navigation's high accuracy GNSS corrections service, "
        "delivered over the internet. It removes the need for a base station "
        "or CORS station.")
    skylark_url = String()
    uuid = String()

    view = View(
        VGroup(
            spring,
            HGroup(
                spring,
                VGroup(
                    Item('information',
                         height=5,
                         width=20,
                         show_label=False,
                         style='readonly',
                         editor=MultilineTextEditor(
                             TextEditor(multi_line=True))),
                    Item('skylark_url',
                         label='Skylark URL',
                         width=400,
                         editor=TextEditor(readonly_allow_selection=True),
                         style='readonly'),
                    Item('uuid',
                         label='Device UUID',
                         width=400,
                         editor=TextEditor(readonly_allow_selection=True),
                         style='readonly'),
                ), spring), spring))

    def set_uuid(self, uuid):
        self.uuid = uuid

    def __init__(self):
        self.skylark_url = SKYLARK_URL
Пример #6
0
class SolutionView(HasTraits):
    python_console_cmds = Dict()
    # we need to doubleup on Lists to store the psuedo absolutes separately
    # without rewriting everything
    """
  logging_v : toggle logging for velocity files
  directory_name_v : location and name of velocity files
  logging_p : toggle logging for position files
  directory_name_p : location and name of velocity files
  """
    plot_history_max = Int(1000)
    last_plot_update_time = Float()
    logging_v = Bool(False)
    display_units = Enum(["degrees", "meters"])
    directory_name_v = File

    logging_p = Bool(False)
    directory_name_p = File

    lats_psuedo_abs = List()
    lngs_psuedo_abs = List()
    alts_psuedo_abs = List()

    table = List()
    dops_table = List()
    pos_table = List()
    vel_table = List()

    rtk_pos_note = Str(
        "It is necessary to enter the \"Surveyed Position\" settings for the base station in order to view the RTK Positions in this tab."
    )

    plot = Instance(Plot)
    plot_data = Instance(ArrayPlotData)
    # Store plots we care about for legend

    running = Bool(True)
    zoomall = Bool(False)
    position_centered = Bool(False)

    clear_button = SVGButton(
        label='',
        tooltip='Clear',
        filename=resource_filename('console/images/iconic/x.svg'),
        width=16,
        height=16)
    zoomall_button = SVGButton(
        label='',
        tooltip='Zoom All',
        toggle=True,
        filename=resource_filename('console/images/iconic/fullscreen.svg'),
        width=16,
        height=16)
    center_button = SVGButton(
        label='',
        tooltip='Center on Solution',
        toggle=True,
        filename=resource_filename('console/images/iconic/target.svg'),
        width=16,
        height=16)
    paused_button = SVGButton(
        label='',
        tooltip='Pause',
        toggle_tooltip='Run',
        toggle=True,
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        width=16,
        height=16)

    traits_view = View(
        HSplit(
            VGroup(
                Item(
                    'table',
                    style='readonly',
                    editor=TabularEditor(adapter=SimpleAdapter()),
                    show_label=False,
                    width=0.3),
                Item(
                    'rtk_pos_note',
                    show_label=False,
                    resizable=True,
                    editor=MultilineTextEditor(TextEditor(multi_line=True)),
                    style='readonly',
                    width=0.3,
                    height=-40), ),
            VGroup(
                HGroup(
                    Item('paused_button', show_label=False),
                    Item('clear_button', show_label=False),
                    Item('zoomall_button', show_label=False),
                    Item('center_button', show_label=False),
                    Item('display_units', label="Display Units"), ),
                Item(
                    'plot',
                    show_label=False,
                    editor=ComponentEditor(bgcolor=(0.8, 0.8, 0.8))), )))

    def _zoomall_button_fired(self):
        self.zoomall = not self.zoomall

    def _center_button_fired(self):
        self.position_centered = not self.position_centered

    def _paused_button_fired(self):
        self.running = not self.running

    def _reset_remove_current(self):
        self.plot_data.update_data({'cur_lat_spp': [],
                                    'cur_lng_spp': [],
                                    'cur_lat_dgnss': [],
                                    'cur_lng_dgnss': [],
                                    'cur_lat_float': [],
                                    'cur_lng_float': [],
                                    'cur_lat_fixed': [],
                                    'cur_lng_fixed': [],
                                    'cur_lat_sbas': [],
                                    'cur_lng_sbas': [],
                                    'cur_lat_dr': [],
                                    'cur_lng_dr': [],
                                    })

    def _clear_history(self):
        self.plot_data.set_data('lat_spp', [])
        self.plot_data.set_data('lng_spp', [])
        self.plot_data.set_data('alt_spp', [])
        self.plot_data.set_data('lat_dgnss', [])
        self.plot_data.set_data('lng_dgnss', [])
        self.plot_data.set_data('alt_dgnss', [])
        self.plot_data.set_data('lat_float', [])
        self.plot_data.set_data('lng_float', [])
        self.plot_data.set_data('alt_float', [])
        self.plot_data.set_data('lat_fixed', [])
        self.plot_data.set_data('lng_fixed', [])
        self.plot_data.set_data('alt_fixed', [])
        self.plot_data.set_data('lat_sbas', [])
        self.plot_data.set_data('lng_sbas', [])
        self.plot_data.set_data('alt_sbas', [])
        self.plot_data.set_data('lat_dr', [])
        self.plot_data.set_data('lng_dr', [])
        self.plot_data.set_data('alt_dr', [])

    def _clear_button_fired(self):
        self.tows = np.zeros(self.plot_history_max)
        self.lats = np.zeros(self.plot_history_max)
        self.lngs = np.zeros(self.plot_history_max)
        self.alts = np.zeros(self.plot_history_max)
        self.modes = np.zeros(self.plot_history_max)
        self._clear_history()
        self._reset_remove_current()

    def age_corrections_callback(self, sbp_msg, **metadata):
        age_msg = MsgAgeCorrections(sbp_msg)
        if age_msg.age != 0xFFFF:
            self.age_corrections = age_msg.age / 10.0
        else:
            self.age_corrections = None

    def update_table(self):
        self.table = self.pos_table + self.vel_table + self.dops_table

    def auto_survey(self):
        if self.last_soln.flags != 0:
            self.latitude_list.append(self.last_soln.lat)
            self.longitude_list.append(self.last_soln.lon)
            self.altitude_list.append(self.last_soln.height)
        if len(self.latitude_list) > 1000:
            self.latitude_list = self.latitude_list[-1000:]
            self.longitude_list = self.longitude_list[-1000:]
            self.altitude_list = self.altitude_list[-1000:]
        if len(self.latitude_list) != 0:
            self.latitude = sum(self.latitude_list) / len(self.latitude_list)
            self.altitude = sum(self.altitude_list) / len(self.latitude_list)
            self.longitude = sum(self.longitude_list) / len(self.latitude_list)

    def pos_llh_callback(self, sbp_msg, **metadata):
        if sbp_msg.msg_type == SBP_MSG_POS_LLH_DEP_A:
            soln = MsgPosLLHDepA(sbp_msg)
        else:
            soln = MsgPosLLH(sbp_msg)
        self.last_soln = soln

        self.last_pos_mode = get_mode(soln)
        self.ins_used = ((soln.flags & 0x8) >> 3) == 1
        pos_table = []
        soln.h_accuracy *= 1e-3
        soln.v_accuracy *= 1e-3

        tow = soln.tow * 1e-3
        if self.nsec is not None:
            tow += self.nsec * 1e-9

        # Return the best estimate of my local and receiver time in convenient
        # format that allows changing precision of the seconds
        ((tloc, secloc), (tgps, secgps)) = log_time_strings(self.week, tow)
        if self.utc_time:
            ((tutc, secutc)) = datetime_2_str(self.utc_time)

        if (self.directory_name_p == ''):
            filepath_p = time.strftime("position_log_%Y%m%d-%H%M%S.csv")
        else:
            filepath_p = os.path.join(
                self.directory_name_p,
                time.strftime("position_log_%Y%m%d-%H%M%S.csv"))

        if not self.logging_p:
            self.log_file = None

        if self.logging_p:
            if self.log_file is None:
                self.log_file = sopen(filepath_p, 'w')
                self.log_file.write(
                    "pc_time,gps_time,tow(sec),latitude(degrees),longitude(degrees),altitude(meters),"
                    "h_accuracy(meters),v_accuracy(meters),n_sats,flags\n")
            log_str_gps = ""
            if tgps != "" and secgps != 0:
                log_str_gps = "{0}:{1:06.6f}".format(tgps, float(secgps))
            self.log_file.write(
                '%s,%s,%.3f,%.10f,%.10f,%.4f,%.4f,%.4f,%d,%d\n' %
                ("{0}:{1:06.6f}".format(tloc, float(secloc)), log_str_gps, tow,
                 soln.lat, soln.lon, soln.height, soln.h_accuracy,
                 soln.v_accuracy, soln.n_sats, soln.flags))
            self.log_file.flush()

        if self.last_pos_mode == 0:
            pos_table.append(('GPS Week', EMPTY_STR))
            pos_table.append(('GPS TOW', EMPTY_STR))
            pos_table.append(('GPS Time', EMPTY_STR))
            pos_table.append(('Num. Signals', EMPTY_STR))
            pos_table.append(('Lat', EMPTY_STR))
            pos_table.append(('Lng', EMPTY_STR))
            pos_table.append(('Height', EMPTY_STR))
            pos_table.append(('Horiz Acc', EMPTY_STR))
            pos_table.append(('Vert Acc', EMPTY_STR))
        else:
            self.last_stime_update = time.time()

            if self.week is not None:
                pos_table.append(('GPS Week', str(self.week)))
            pos_table.append(('GPS TOW', "{:.3f}".format(tow)))

            if self.week is not None:
                pos_table.append(('GPS Time', "{0}:{1:06.3f}".format(
                    tgps, float(secgps))))
            if self.utc_time is not None:
                pos_table.append(('UTC Time', "{0}:{1:06.3f}".format(
                    tutc, float(secutc))))
                pos_table.append(('UTC Src', self.utc_source))
            if self.utc_time is None:
                pos_table.append(('UTC Time', EMPTY_STR))
                pos_table.append(('UTC Src', EMPTY_STR))

            pos_table.append(('Sats Used', soln.n_sats))
            pos_table.append(('Lat', soln.lat))
            pos_table.append(('Lng', soln.lon))
            pos_table.append(('Height', "{0:.3f}".format(soln.height)))
            pos_table.append(('Horiz Acc', soln.h_accuracy))
            pos_table.append(('Vert Acc', soln.v_accuracy))

        pos_table.append(('Pos Flags', '0x%03x' % soln.flags))
        pos_table.append(('INS Used', '{}'.format(self.ins_used)))
        pos_table.append(('Pos Fix Mode', mode_dict[self.last_pos_mode]))
        if self.age_corrections is not None:
            pos_table.append(('Corr. Age [s]', self.age_corrections))

        self.auto_survey()

        # set-up table variables
        self.pos_table = pos_table
        self.update_table()
        # setup_plot variables
        self.list_lock.acquire()
        self.lats[1:] = self.lats[:-1]
        self.lngs[1:] = self.lngs[:-1]
        self.alts[1:] = self.alts[:-1]
        self.tows[1:] = self.tows[:-1]
        self.modes[1:] = self.modes[:-1]

        self.lats[0] = soln.lat
        self.lngs[0] = soln.lon
        self.alts[0] = soln.height
        self.tows[0] = soln.tow
        self.modes[0] = self.last_pos_mode

        self.lats = self.lats[-self.plot_history_max:]
        self.lngs = self.lngs[-self.plot_history_max:]
        self.alts = self.alts[-self.plot_history_max:]
        self.tows = self.tows[-self.plot_history_max:]
        self.modes = self.modes[-self.plot_history_max:]
        self.list_lock.release()
        # Updating array plot data is not thread safe, so we have to fire an event
        # and have the GUI thread do it
        if time.time() - self.last_plot_update_time > GUI_UPDATE_PERIOD:
            self.last_plot_update_time = time.time()

    def _last_plot_update_time_changed(self):
        self._solution_draw()

    def _display_units_changed(self):
        self.recenter = True
        self.prev_extents = (self.plot.index_range.low_setting,
                             self.plot.index_range.high_setting,
                             self.plot.value_range.low_setting,
                             self.plot.value_range.high_setting)
        self.prev_offsets = (self.offset[0], self.offset[1])
        self.prev_sfs = (self.sf[0], self.sf[1])

        if self.display_units == "meters":
            self.offset = (np.mean(self.lats[~(np.equal(self.modes, 0))]),
                           np.mean(self.lngs[~(np.equal(self.modes, 0))]),
                           np.mean(self.alts[~(np.equal(self.modes, 0))]))
            if not self.meters_per_lat:
                (self.meters_per_lat, self.meters_per_lon) = meters_per_deg(
                    np.mean(self.lats[~(np.equal(self.modes, 0))]))
            self.sf = (self.meters_per_lat, self.meters_per_lon)
            self.plot.value_axis.title = 'Latitude (meters)'
            self.plot.index_axis.title = 'Longitude (meters)'
        else:
            self.offset = (0, 0, 0)
            self.sf = (1, 1)
            self.plot.value_axis.title = 'Latitude (degrees)'
            self.plot.index_axis.title = 'Longitude (degrees)'

    def rescale_for_units_change(self):
        # Chaco scales view automatically when 'auto' is stored
        if self.prev_extents[0] != 'auto':
            # Otherwise use has used mousewheel zoom and we need to transform
            if self.display_units == 'meters':
                new_scaling = (
                    (self.prev_extents[0] - self.offset[1]) * self.sf[1],
                    (self.prev_extents[1] - self.offset[1]) * self.sf[1],
                    (self.prev_extents[2] - self.offset[0]) * self.sf[0],
                    (self.prev_extents[3] - self.offset[0]) * self.sf[0])
            else:
                new_scaling = (
                    self.prev_extents[0] / self.prev_sfs[1] + self.prev_offsets[1],
                    self.prev_extents[1] / self.prev_sfs[1] + self.prev_offsets[1],
                    self.prev_extents[2] / self.prev_sfs[0] + self.prev_offsets[0],
                    self.prev_extents[3] / self.prev_sfs[0] + self.prev_offsets[0]
                )

            # set plot scaling accordingly
            self.plot.index_range.low_setting = new_scaling[0]
            self.plot.index_range.high_setting = new_scaling[1]
            self.plot.value_range.low_setting = new_scaling[2]
            self.plot.value_range.high_setting = new_scaling[3]

    def _solution_draw(self):
        spp_indexer, dgnss_indexer, float_indexer, fixed_indexer, sbas_indexer, dr_indexer = None, None, None, None, None, None
        soln = self.last_soln
        if np.any(self.modes):
            self.list_lock.acquire()
            spp_indexer = (self.modes == SPP_MODE)
            dgnss_indexer = (self.modes == DGNSS_MODE)
            sbas_indexer = (self.modes == SBAS_MODE)
            float_indexer = (self.modes == FLOAT_MODE)
            fixed_indexer = (self.modes == FIXED_MODE)
            dr_indexer = (self.modes == DR_MODE)

            # make sure that there is at least one true in indexer before setting
            if any(spp_indexer):
                self.plot_data.update_data({'lat_spp': (self.lats[spp_indexer] - self.offset[0]) * self.sf[0],
                                            'lng_spp': (self.lngs[spp_indexer] - self.offset[1]) * self.sf[1],
                                            'alt_spp': (self.alts[spp_indexer] - self.offset[2])})
            else:
                self.plot_data.update_data({'lat_spp': [],
                                            'lng_spp': [],
                                            'alt_spp': []})
            if any(dgnss_indexer):
                self.plot_data.update_data({'lat_dgnss': (self.lats[dgnss_indexer] - self.offset[0]) * self.sf[0],
                                            'lng_dgnss': (self.lngs[dgnss_indexer] - self.offset[1]) * self.sf[1],
                                            'alt_dgnss': (self.alts[dgnss_indexer] - self.offset[2])})
            else:
                self.plot_data.update_data({'lat_dgnss': [],
                                            'lng_dgnss': [],
                                            'alt_dgnss': []})
            if any(float_indexer):
                self.plot_data.update_data({'lat_float': (self.lats[float_indexer] - self.offset[0]) * self.sf[0],
                                            'lng_float': (self.lngs[float_indexer] - self.offset[1]) * self.sf[1],
                                            'alt_float': (self.alts[float_indexer] - self.offset[2])})
            else:
                self.plot_data.update_data({'lat_float': [],
                                            'lng_float': [],
                                            'alt_float': []})
            if any(fixed_indexer):
                self.plot_data.update_data({'lat_fixed': (self.lats[fixed_indexer] - self.offset[0]) * self.sf[0],
                                            'lng_fixed': (self.lngs[fixed_indexer] - self.offset[1]) * self.sf[1],
                                            'alt_fixed': (self.alts[fixed_indexer] - self.offset[2])})
            else:
                self.plot_data.update_data({'lat_fixed': [],
                                            'lng_fixed': [],
                                            'alt_fixed': []})
            if any(sbas_indexer):
                self.plot_data.update_data({'lat_sbas': (self.lats[sbas_indexer] - self.offset[0]) * self.sf[0],
                                            'lng_sbas': (self.lngs[sbas_indexer] - self.offset[1]) * self.sf[1],
                                            'alt_sbas': (self.alts[sbas_indexer] - self.offset[2])})
            else:
                self.plot_data.update_data({'lat_sbas': [],
                                            'lng_sbas': [],
                                            'alt_sbas': []})

            if any(dr_indexer):
                self.plot_data.update_data({'lat_dr': (self.lats[dr_indexer] - self.offset[0]) * self.sf[0],
                                            'lng_dr': (self.lngs[dr_indexer] - self.offset[1]) * self.sf[1],
                                            'alt_dr': (self.alts[dr_indexer] - self.offset[2])})
            else:
                self.plot_data.update_data({'lat_dr': [],
                                            'lng_dr': [],
                                            'alt_dr': []})
            self.list_lock.release()
            # update our "current solution" icon
            if self.last_pos_mode == SPP_MODE:
                self._reset_remove_current()
                self.plot_data.update_data(
                    {'cur_lat_spp': [(soln.lat - self.offset[0]) * self.sf[0]], 'cur_lng_spp': [(soln.lon - self.offset[1]) * self.sf[1]]})
            elif self.last_pos_mode == DGNSS_MODE:
                self._reset_remove_current()
                self.plot_data.update_data({'cur_lat_dgnss': [(soln.lat - self.offset[0]) * self.sf[0]],
                                            'cur_lng_dgnss': [(soln.lon - self.offset[1]) * self.sf[1]]})
            elif self.last_pos_mode == FLOAT_MODE:
                self._reset_remove_current()
                self.plot_data.update_data({'cur_lat_float': [(soln.lat - self.offset[0]) * self.sf[0]],
                                            'cur_lng_float': [(soln.lon - self.offset[1]) * self.sf[1]]})
            elif self.last_pos_mode == FIXED_MODE:
                self._reset_remove_current()
                self.plot_data.update_data({'cur_lat_fixed': [(soln.lat - self.offset[0]) * self.sf[0]],
                                            'cur_lng_fixed': [(soln.lon - self.offset[1]) * self.sf[1]]})
            elif self.last_pos_mode == SBAS_MODE:
                self._reset_remove_current()
                self.plot_data.update_data({'cur_lat_sbas': [(soln.lat - self.offset[0]) * self.sf[0]],
                                            'cur_lng_sbas': [(soln.lon - self.offset[1]) * self.sf[1]]})
            elif self.last_pos_mode == DR_MODE:
                self._reset_remove_current()
                self.plot_data.update_data({'cur_lat_dr': [(soln.lat - self.offset[0]) * self.sf[0]],
                                            'cur_lng_dr': [(soln.lon - self.offset[1]) * self.sf[1]]})
            else:
                pass

            if not self.zoomall and self.position_centered:
                d = (
                    self.plot.index_range.high - self.plot.index_range.low) / 2.
                self.plot.index_range.set_bounds(
                    (soln.lon - self.offset[1]) * self.sf[1] - d,
                    (soln.lon - self.offset[1]) * self.sf[1] + d)
                d = (
                    self.plot.value_range.high - self.plot.value_range.low) / 2.
                self.plot.value_range.set_bounds(
                    (soln.lat - self.offset[0]) * self.sf[0] - d,
                    (soln.lat - self.offset[0]) * self.sf[0] + d)
            if self.zoomall:
                self.recenter = False
                plot_square_axes(self.plot,
                                 ('lng_spp', 'lng_dgnss', 'lng_float',
                                  'lng_fixed', 'lng_sbas', 'lng_dr'),
                                 ('lat_spp', 'lat_dgnss', 'lat_float',
                                  'lat_fixed', 'lat_sbas', 'lat_dr'))
            if self.recenter:
                try:
                    self.rescale_for_units_change()
                    self.recenter = False
                except AttributeError:
                    pass

    def dops_callback(self, sbp_msg, **metadata):
        flags = 0
        if sbp_msg.msg_type == SBP_MSG_DOPS_DEP_A:
            dops = MsgDopsDepA(sbp_msg)
            flags = 1
        else:
            dops = MsgDops(sbp_msg)
            flags = dops.flags
        if flags != 0:
            self.dops_table = [('PDOP', '%.1f' % (dops.pdop * 0.01)),
                               ('GDOP', '%.1f' % (dops.gdop * 0.01)),
                               ('TDOP', '%.1f' % (dops.tdop * 0.01)),
                               ('HDOP', '%.1f' %
                                (dops.hdop * 0.01)), ('VDOP', '%.1f' %
                                                      (dops.vdop * 0.01))]
        else:
            self.dops_table = [('PDOP', EMPTY_STR), ('GDOP', EMPTY_STR),
                               ('TDOP', EMPTY_STR), ('HDOP', EMPTY_STR),
                               ('VDOP', EMPTY_STR)]

        self.dops_table.append(('DOPS Flags', '0x%03x' % flags))

    def vel_ned_callback(self, sbp_msg, **metadata):
        flags = 0
        if sbp_msg.msg_type == SBP_MSG_VEL_NED_DEP_A:
            vel_ned = MsgVelNEDDepA(sbp_msg)
            flags = 1
        else:
            vel_ned = MsgVelNED(sbp_msg)
            flags = vel_ned.flags
        tow = vel_ned.tow * 1e-3
        if self.nsec is not None:
            tow += self.nsec * 1e-9

        ((tloc, secloc), (tgps, secgps)) = log_time_strings(self.week, tow)

        if self.directory_name_v == '':
            filepath_v = time.strftime("velocity_log_%Y%m%d-%H%M%S.csv")
        else:
            filepath_v = os.path.join(
                self.directory_name_v,
                time.strftime("velocity_log_%Y%m%d-%H%M%S.csv"))

        if not self.logging_v:
            self.vel_log_file = None

        if self.logging_v:
            if self.vel_log_file is None:
                self.vel_log_file = sopen(filepath_v, 'w')
                self.vel_log_file.write(
                    'pc_time,gps_time,tow(sec),north(m/s),east(m/s),down(m/s),speed(m/s),flags,num_signals\n'
                )
            log_str_gps = ''
            if tgps != "" and secgps != 0:
                log_str_gps = "{0}:{1:06.6f}".format(tgps, float(secgps))
            self.vel_log_file.write(
                '%s,%s,%.3f,%.6f,%.6f,%.6f,%.6f,%d,%d\n' %
                ("{0}:{1:06.6f}".format(tloc, float(secloc)), log_str_gps, tow,
                 vel_ned.n * 1e-3, vel_ned.e * 1e-3, vel_ned.d * 1e-3,
                 math.sqrt(vel_ned.n * vel_ned.n + vel_ned.e * vel_ned.e
                           ) * 1e-3, flags, vel_ned.n_sats))
            self.vel_log_file.flush()
        if flags != 0:
            self.vel_table = [
                ('Vel. N', '% 8.4f' % (vel_ned.n * 1e-3)),
                ('Vel. E', '% 8.4f' % (vel_ned.e * 1e-3)),
                ('Vel. D', '% 8.4f' % (vel_ned.d * 1e-3)),
            ]
        else:
            self.vel_table = [
                ('Vel. N', EMPTY_STR),
                ('Vel. E', EMPTY_STR),
                ('Vel. D', EMPTY_STR),
            ]
        self.vel_table.append(('Vel Flags', '0x%03x' % flags))
        self.update_table()

    def gps_time_callback(self, sbp_msg, **metadata):
        if sbp_msg.msg_type == SBP_MSG_GPS_TIME_DEP_A:
            time_msg = MsgGPSTimeDepA(sbp_msg)
            flags = 1
        elif sbp_msg.msg_type == SBP_MSG_GPS_TIME:
            time_msg = MsgGPSTime(sbp_msg)
            flags = time_msg.flags
            if flags != 0:
                self.week = time_msg.wn
                self.nsec = time_msg.ns_residual

    def utc_time_callback(self, sbp_msg, **metadata):
        tmsg = MsgUtcTime(sbp_msg)
        seconds = math.floor(tmsg.seconds)
        microseconds = int(tmsg.ns / 1000.00)
        if tmsg.flags & 0x1 == 1:
            dt = datetime.datetime(tmsg.year, tmsg.month, tmsg.day, tmsg.hours,
                                   tmsg.minutes, tmsg.seconds, microseconds)
            self.utc_time = dt
            self.utc_time_flags = tmsg.flags
            if (tmsg.flags >> 3) & 0x3 == 0:
                self.utc_source = "Factory Default"
            elif (tmsg.flags >> 3) & 0x3 == 1:
                self.utc_source = "Non Volatile Memory"
            elif (tmsg.flags >> 3) & 0x3 == 2:
                self.utc_source = "Decoded this Session"
            else:
                self.utc_source = "Unknown"
        else:
            self.utc_time = None
            self.utc_source = None

    def __init__(self, link, dirname=''):
        super(SolutionView, self).__init__()
        self.recenter = False
        self.offset = (0, 0, 0)
        self.sf = (1, 1)
        self.list_lock = threading.Lock()
        self.lats = np.zeros(self.plot_history_max)
        self.lngs = np.zeros(self.plot_history_max)
        self.alts = np.zeros(self.plot_history_max)
        self.tows = np.zeros(self.plot_history_max)
        self.modes = np.zeros(self.plot_history_max)
        self.log_file = None
        self.directory_name_v = dirname
        self.directory_name_p = dirname
        self.vel_log_file = None
        self.last_stime_update = 0
        self.last_soln = None

        self.counter = 0
        self.latitude_list = []
        self.longitude_list = []
        self.altitude_list = []
        self.altitude = 0
        self.longitude = 0
        self.latitude = 0
        self.last_pos_mode = 0
        self.ins_used = False
        self.last_plot_update_time = 0

        self.plot_data = ArrayPlotData(
            lat_spp=[],
            lng_spp=[],
            alt_spp=[],
            cur_lat_spp=[],
            cur_lng_spp=[],
            lat_dgnss=[],
            lng_dgnss=[],
            alt_dgnss=[],
            cur_lat_dgnss=[],
            cur_lng_dgnss=[],
            lat_float=[],
            lng_float=[],
            alt_float=[],
            cur_lat_float=[],
            cur_lng_float=[],
            lat_fixed=[],
            lng_fixed=[],
            alt_fixed=[],
            cur_lat_fixed=[],
            cur_lng_fixed=[],
            lat_sbas=[],
            lng_sbas=[],
            cur_lat_sbas=[],
            cur_lng_sbas=[],
            lng_dr=[],
            lat_dr=[],
            cur_lat_dr=[],
            cur_lng_dr=[]
        )
        self.plot = Plot(self.plot_data)

        # 1000 point buffer
        self.plot.plot(
            ('lng_spp', 'lat_spp'),
            type='line',
            line_width=0.1,
            name='',
            color=color_dict[SPP_MODE])
        self.plot.plot(
            ('lng_spp', 'lat_spp'),
            type='scatter',
            name='',
            color=color_dict[SPP_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        self.plot.plot(
            ('lng_dgnss', 'lat_dgnss'),
            type='line',
            line_width=0.1,
            name='',
            color=color_dict[DGNSS_MODE])
        self.plot.plot(
            ('lng_dgnss', 'lat_dgnss'),
            type='scatter',
            name='',
            color=color_dict[DGNSS_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        self.plot.plot(
            ('lng_float', 'lat_float'),
            type='line',
            line_width=0.1,
            name='',
            color=color_dict[FLOAT_MODE])
        self.plot.plot(
            ('lng_float', 'lat_float'),
            type='scatter',
            name='',
            color=color_dict[FLOAT_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        self.plot.plot(
            ('lng_fixed', 'lat_fixed'),
            type='line',
            line_width=0.1,
            name='',
            color=color_dict[FIXED_MODE])
        self.plot.plot(
            ('lng_fixed', 'lat_fixed'),
            type='scatter',
            name='',
            color=color_dict[FIXED_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        self.plot.plot(
            ('lng_sbas', 'lat_sbas'),
            type='line',
            line_width=0.1,
            name='',
            color=color_dict[SBAS_MODE])
        self.plot.plot(
            ('lng_sbas', 'lat_sbas'),
            type='scatter',
            name='',
            color=color_dict[SBAS_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        self.plot.plot(
            ('lng_dr', 'lat_dr'),
            type='line',
            line_width=0.1,
            name='',
            color=color_dict[DR_MODE])
        self.plot.plot(
            ('lng_dr', 'lat_dr'),
            type='scatter',
            color=color_dict[DR_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        # current values
        spp = self.plot.plot(
            ('cur_lng_spp', 'cur_lat_spp'),
            type='scatter',
            name=mode_dict[SPP_MODE],
            color=color_dict[SPP_MODE],
            marker='plus',
            line_width=1.5,
            marker_size=5.0)
        dgnss = self.plot.plot(
            ('cur_lng_dgnss', 'cur_lat_dgnss'),
            type='scatter',
            name=mode_dict[DGNSS_MODE],
            color=color_dict[DGNSS_MODE],
            marker='plus',
            line_width=1.5,
            marker_size=5.0)
        rtkfloat = self.plot.plot(
            ('cur_lng_float', 'cur_lat_float'),
            type='scatter',
            name=mode_dict[FLOAT_MODE],
            color=color_dict[FLOAT_MODE],
            marker='plus',
            line_width=1.5,
            marker_size=5.0)
        rtkfix = self.plot.plot(
            ('cur_lng_fixed', 'cur_lat_fixed'),
            type='scatter',
            name=mode_dict[FIXED_MODE],
            color=color_dict[FIXED_MODE],
            marker='plus',
            line_width=1.5,
            marker_size=5.0)
        sbas = self.plot.plot(
            ('cur_lng_sbas', 'cur_lat_sbas'),
            type='scatter',
            name=mode_dict[SBAS_MODE],
            color=color_dict[SBAS_MODE],
            marker='plus',
            line_width=1.5,
            marker_size=5.0)
        dr = self.plot.plot(
            ('cur_lng_dr', 'cur_lat_dr'),
            type='scatter',
            name=mode_dict[DR_MODE],
            color=color_dict[DR_MODE],
            marker='plus',
            line_width=1.5,
            marker_size=5.0)
        plot_labels = ['SPP', 'SBAS', 'DGPS', 'RTK float', 'RTK fixed', 'DR']
        plots_legend = dict(
            zip(plot_labels, [spp, sbas, dgnss, rtkfloat, rtkfix, dr]))
        self.plot.legend.plots = plots_legend
        self.plot.legend.labels = plot_labels  # sets order
        self.plot.legend.visible = True

        self.plot.index_axis.tick_label_position = 'inside'
        self.plot.index_axis.tick_label_color = 'gray'
        self.plot.index_axis.tick_color = 'gray'
        self.plot.index_axis.title = 'Longitude (degrees)'
        self.plot.index_axis.title_spacing = 5
        self.plot.value_axis.tick_label_position = 'inside'
        self.plot.value_axis.tick_label_color = 'gray'
        self.plot.value_axis.tick_color = 'gray'
        self.plot.value_axis.title = 'Latitude (degrees)'
        self.plot.value_axis.title_spacing = 5
        self.plot.padding = (25, 25, 25, 25)

        self.plot.tools.append(PanTool(self.plot))
        zt = ZoomTool(
            self.plot, zoom_factor=1.1, tool_mode="box", always_on=False)
        self.plot.overlays.append(zt)

        self.link = link
        self.link.add_callback(self.pos_llh_callback, [SBP_MSG_POS_LLH_DEP_A, SBP_MSG_POS_LLH])
        self.link.add_callback(self.vel_ned_callback, [SBP_MSG_VEL_NED_DEP_A, SBP_MSG_VEL_NED])
        self.link.add_callback(self.dops_callback, [SBP_MSG_DOPS_DEP_A, SBP_MSG_DOPS])
        self.link.add_callback(self.gps_time_callback, [SBP_MSG_GPS_TIME_DEP_A, SBP_MSG_GPS_TIME])
        self.link.add_callback(self.utc_time_callback, [SBP_MSG_UTC_TIME])
        self.link.add_callback(self.age_corrections_callback, SBP_MSG_AGE_CORRECTIONS)
        self.week = None
        self.utc_time = None
        self.age_corrections = None
        self.nsec = 0
        self.meters_per_lat = None
        self.meters_per_lon = None

        self.python_console_cmds = {
            'solution': self,
        }
Пример #7
0
class SolutionView(HasTraits):
    python_console_cmds = Dict()
    # we need to doubleup on Lists to store the psuedo absolutes separately
    # without rewriting everything
    """
  logging_v : toggle logging for velocity files
  directory_name_v : location and name of velocity files
  logging_p : toggle logging for position files
  directory_name_p : location and name of velocity files
  """
    plot_history_max = Int(1000)
    last_plot_update_time = Float()
    last_stale_update_time = Float()
    logging_v = Bool(False)
    display_units = Enum(["degrees", "meters"])
    directory_name_v = File

    logging_p = Bool(False)
    directory_name_p = File

    lats_psuedo_abs = List()
    lngs_psuedo_abs = List()
    alts_psuedo_abs = List()

    table = List()
    dops_table = List()
    pos_table = List()
    vel_table = List()

    rtk_pos_note = Str(
        "It is necessary to enter the \"Surveyed Position\" settings for the base station in order to view the RTK Positions in this tab."
    )

    plot = Instance(Plot)
    velocity_view = Instance(VelocityView)
    plot_data = Instance(ArrayPlotData)
    # Store plots we care about for legend

    running = Bool(True)
    zoomall = Bool(False)
    position_centered = Bool(False)

    clear_button = SVGButton(
        label='',
        tooltip='Clear',
        filename=resource_filename('console/images/iconic/x.svg'),
        width=16,
        height=16)
    zoomall_button = SVGButton(
        label='',
        tooltip='Zoom All',
        toggle=True,
        filename=resource_filename('console/images/iconic/fullscreen.svg'),
        width=16,
        height=16)
    center_button = SVGButton(
        label='',
        tooltip='Center on Solution',
        toggle=True,
        filename=resource_filename('console/images/iconic/target.svg'),
        width=16,
        height=16)
    paused_button = SVGButton(
        label='',
        tooltip='Pause',
        toggle_tooltip='Run',
        toggle=True,
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        width=16,
        height=16)

    traits_view = View(
        HSplit(
            VGroup(
                Item('table',
                     style='readonly',
                     editor=TabularEditor(adapter=SimpleAdapter()),
                     show_label=False,
                     width=0.3),
                Item('rtk_pos_note',
                     show_label=False,
                     resizable=True,
                     editor=MultilineTextEditor(TextEditor(multi_line=True)),
                     style='readonly',
                     width=0.3,
                     height=-40),
            ),
            Tabbed(
                VGroup(
                    HGroup(Item('paused_button', show_label=False),
                           Item('clear_button', show_label=False),
                           Item('zoomall_button', show_label=False),
                           Item('center_button', show_label=False),
                           Item('display_units', label="Display Units"),
                           padding=0),
                    Item('plot',
                         show_label=False,
                         editor=ComponentEditor(bgcolor=(0.8, 0.8, 0.8)),
                         label="Position"),
                    label='Position',
                ),
                Item('velocity_view',
                     show_label=False,
                     style='custom',
                     label="Velocity"))))

    def _zoomall_button_fired(self):
        self.zoomall = not self.zoomall

    def _center_button_fired(self):
        self.position_centered = not self.position_centered

    def _paused_button_fired(self):
        self.running = not self.running

    def _reset_remove_current(self):
        self.plot_data.update_data(self._get_update_current())

    def _get_update_current(self, current_dict={}):
        out_dict = {
            'cur_lat_spp': [],
            'cur_lng_spp': [],
            'cur_lat_dgnss': [],
            'cur_lng_dgnss': [],
            'cur_lat_float': [],
            'cur_lng_float': [],
            'cur_lat_fixed': [],
            'cur_lng_fixed': [],
            'cur_lat_sbas': [],
            'cur_lng_sbas': [],
            'cur_lat_dr': [],
            'cur_lng_dr': []
        }
        out_dict.update(current_dict)
        return out_dict

    def _synchronize_plot_data_by_mode(self,
                                       mode_string,
                                       update_current=False):
        # do all required plot_data updates for a single
        # new solution with mode defined by mode_string
        pending_update = {
            'lat_' + mode_string:
            [x for x in self.slns['lat_' + mode_string] if not np.isnan(x)],
            'lng_' + mode_string:
            [y for y in self.slns['lng_' + mode_string] if not np.isnan(y)]
        }
        if update_current:
            current = {}
            if len(pending_update['lat_' + mode_string]) != 0:
                current = {
                    'cur_lat_' + mode_string:
                    [pending_update['lat_' + mode_string][-1]],
                    'cur_lng_' + mode_string:
                    [pending_update['lng_' + mode_string][-1]]
                }
            else:
                current = {
                    'cur_lat_' + mode_string: [],
                    'cur_lng_' + mode_string: []
                }
            pending_update.update(self._get_update_current(current))
        self.plot_data.update_data(pending_update)

    def _append_empty_sln_data(self, exclude_mode=None):
        for each_mode in mode_string_dict.values():
            if exclude_mode is None or each_mode != exclude_mode:
                self.slns['lat_' + each_mode].append(np.nan)
                self.slns['lng_' + each_mode].append(np.nan)

    def _update_sln_data_by_mode(self, soln, mode_string):
        # do backend deque updates for a new solution of type
        # mode string
        self.scaling_lock.acquire()
        lat = (soln.lat - self.offset[0]) * self.sf[0]
        lng = (soln.lon - self.offset[1]) * self.sf[1]
        self.scaling_lock.release()
        self.slns['lat_' + mode_string].append(lat)
        self.slns['lng_' + mode_string].append(lng)
        # Rotate old data out by appending to deque
        self._append_empty_sln_data(exclude_mode=mode_string)

    def _clr_sln_data(self):
        for each in self.slns:
            self.slns[each].clear()

    def _clear_history(self):
        for each in self.slns:
            self.slns[each].clear()
        pending_update = {
            'lat_spp': [],
            'lng_spp': [],
            'alt_spp': [],
            'lat_dgnss': [],
            'lng_dgnss': [],
            'alt_dgnss': [],
            'lat_float': [],
            'lng_float': [],
            'alt_float': [],
            'lat_fixed': [],
            'lng_fixed': [],
            'alt_fixed': [],
            'lat_sbas': [],
            'lng_sbas': [],
            'alt_sbas': [],
            'lat_dr': [],
            'lng_dr': [],
            'alt_dr': []
        }
        pending_update.update(self._get_update_current())
        self.plot_data.update(pending_update)

    def _clear_button_fired(self):
        self._clear_history()

    def age_corrections_callback(self, sbp_msg, **metadata):
        age_msg = MsgAgeCorrections(sbp_msg)
        if age_msg.age != 0xFFFF:
            self.age_corrections = age_msg.age / 10.0
        else:
            self.age_corrections = None

    def update_table(self):
        self.table = self.pos_table + self.vel_table + self.dops_table

    def auto_survey(self):
        if len(self.lats) != 0:
            self.latitude = sum(self.lats) / len(self.lats)
            self.altitude = sum(self.alts) / len(self.alts)
            self.longitude = sum(self.lngs) / len(self.lngs)

    def pos_llh_callback(self, sbp_msg, **metadata):
        if sbp_msg.msg_type == SBP_MSG_POS_LLH_DEP_A:
            soln = MsgPosLLHDepA(sbp_msg)
        else:
            soln = MsgPosLLH(sbp_msg)

        self.last_pos_mode = get_mode(soln)
        if self.last_pos_mode != 0:
            self.last_soln = soln
            mode_string = mode_string_dict[self.last_pos_mode]
            if mode_string not in self.pending_draw_modes:
                # this list allows us to tell GUI thread which solutions to update
                # (if we decide not to update at full data rate)
                # we use short strings to identify each solution mode
                self.pending_draw_modes.append(mode_string)
            self.list_lock.acquire()
            self._update_sln_data_by_mode(soln, mode_string)
            self.list_lock.release()
        else:
            self.list_lock.acquire()
            self._append_empty_sln_data()
            self.list_lock.release()
        self.ins_used = ((soln.flags & 0x8) >> 3) == 1
        pos_table = []
        soln.h_accuracy *= 1e-3
        soln.v_accuracy *= 1e-3

        tow = soln.tow * 1e-3
        if self.nsec is not None:
            tow += self.nsec * 1e-9

        # Return the best estimate of my local and receiver time in convenient
        # format that allows changing precision of the seconds
        ((tloc, secloc), (tgps, secgps)) = log_time_strings(self.week, tow)
        if self.utc_time:
            ((tutc, secutc)) = datetime_2_str(self.utc_time)

        if (self.directory_name_p == ''):
            filepath_p = time.strftime("position_log_%Y%m%d-%H%M%S.csv")
        else:
            filepath_p = os.path.join(
                self.directory_name_p,
                time.strftime("position_log_%Y%m%d-%H%M%S.csv"))

        if not self.logging_p:
            self.log_file = None

        if self.logging_p:
            if self.log_file is None:
                self.log_file = sopen(filepath_p, 'w')
                self.log_file.write(
                    "pc_time,gps_time,tow(sec),latitude(degrees),longitude(degrees),altitude(meters),"
                    "h_accuracy(meters),v_accuracy(meters),n_sats,flags\n")
            log_str_gps = ""
            if tgps != "" and secgps != 0:
                log_str_gps = "{0}:{1:06.6f}".format(tgps, float(secgps))
            self.log_file.write(
                '%s,%s,%.3f,%.10f,%.10f,%.4f,%.4f,%.4f,%d,%d\n' %
                ("{0}:{1:06.6f}".format(tloc, float(secloc)), log_str_gps, tow,
                 soln.lat, soln.lon, soln.height, soln.h_accuracy,
                 soln.v_accuracy, soln.n_sats, soln.flags))
            self.log_file.flush()

        if self.last_pos_mode == 0:
            pos_table.append(('GPS Week', EMPTY_STR))
            pos_table.append(('GPS TOW', EMPTY_STR))
            pos_table.append(('GPS Time', EMPTY_STR))
            pos_table.append(('Num. Signals', EMPTY_STR))
            pos_table.append(('Lat', EMPTY_STR))
            pos_table.append(('Lng', EMPTY_STR))
            pos_table.append(('Height', EMPTY_STR))
            pos_table.append(('Horiz Acc', EMPTY_STR))
            pos_table.append(('Vert Acc', EMPTY_STR))
        else:
            self.last_stime_update = monotonic()

            if self.week is not None:
                pos_table.append(('GPS Week', str(self.week)))
            pos_table.append(('GPS TOW', "{:.3f}".format(tow)))

            if self.week is not None:
                pos_table.append(
                    ('GPS Time', "{0}:{1:06.3f}".format(tgps, float(secgps))))
            if self.utc_time is not None:
                pos_table.append(
                    ('UTC Time', "{0}:{1:06.3f}".format(tutc, float(secutc))))
                pos_table.append(('UTC Src', self.utc_source))
            if self.utc_time is None:
                pos_table.append(('UTC Time', EMPTY_STR))
                pos_table.append(('UTC Src', EMPTY_STR))

            pos_table.append(('Sats Used', soln.n_sats))
            pos_table.append(('Lat', "{:.12g}".format(soln.lat)))
            pos_table.append(('Lng', "{:.12g}".format(soln.lon)))
            pos_table.append(('Height', "{0:.3f}".format(soln.height)))
            pos_table.append(('Horiz Acc', "{:.12g}".format(soln.h_accuracy)))
            pos_table.append(('Vert Acc', "{:.12g}".format(soln.v_accuracy)))

        pos_table.append(('Pos Flags', '0x%03x' % soln.flags))
        pos_table.append(('INS Used', '{}'.format(self.ins_used)))
        pos_table.append(('Pos Fix Mode', mode_dict[self.last_pos_mode]))
        if self.age_corrections is not None:
            pos_table.append(('Corr. Age [s]', self.age_corrections))

        # only store valid solutions for auto survey and degrees to meter transformation
        if self.last_pos_mode != 0:
            self.lats.append(soln.lat)
            self.lngs.append(soln.lon)
            self.alts.append(soln.height)
            self.tows.append(soln.tow)
            self.modes.append(self.last_pos_mode)
        self.auto_survey()

        # set-up table variables
        self.pos_table = pos_table
        self.update_table()
        # setup_plot variables
        # Updating array plot data is not thread safe, so we have to fire an event
        # and have the GUI thread do it
        if monotonic() - self.last_plot_update_time > GUI_UPDATE_PERIOD:
            self.update_scheduler.schedule_update('_solution_draw',
                                                  self._solution_draw)

    def _display_units_changed(self):
        # we store current extents of plot and current scalefactlrs
        self.scaling_lock.acquire()
        self.recenter = True  # recenter flag tells _solution_draw to update view extents
        self.prev_extents = (self.plot.index_range.low_setting,
                             self.plot.index_range.high_setting,
                             self.plot.value_range.low_setting,
                             self.plot.value_range.high_setting)
        self.prev_offsets = (self.offset[0], self.offset[1])
        self.prev_sfs = (self.sf[0], self.sf[1])
        if self.display_units == "meters":
            self.offset = (
                np.mean(
                    np.array(self.lats)[~(np.equal(np.array(self.modes), 0))]),
                np.mean(
                    np.array(self.lngs)[~(np.equal(np.array(self.modes), 0))]),
                np.mean(
                    np.array(self.alts)[~(np.equal(np.array(self.modes), 0))]))
            (self.meters_per_lat, self.meters_per_lon) = meters_per_deg(
                np.mean(
                    np.array(self.lats)[~(np.equal(np.array(self.modes), 0))]))
            self.sf = (self.meters_per_lat, self.meters_per_lon)
            self.plot.value_axis.title = 'Latitude (meters)'
            self.plot.index_axis.title = 'Longitude (meters)'
        else:
            self.offset = (0, 0, 0)
            self.sf = (1, 1)
            self.plot.value_axis.title = 'Latitude (degrees)'
            self.plot.index_axis.title = 'Longitude (degrees)'
        self.scaling_lock.release()
        self.list_lock.acquire()
        # now we update the existing sln deques to go from meters back to degrees or vice versa
        for each_array in self.slns:
            index = 0 if 'lat' in str(each_array) else 1
            # going from degrees to meters; do scaling with new offset and sf
            if self.display_units == "meters":
                self.slns[each_array] = deque(
                    (np.array(self.slns[each_array]) - self.offset[index]) *
                    self.sf[index],
                    maxlen=PLOT_HISTORY_MAX)
            # going from degrees to meters; do inverse scaling with former offset and sf
            if self.display_units == "degrees":
                self.slns[each_array] = deque(
                    np.array(self.slns[each_array]) / self.prev_sfs[index] +
                    self.prev_offsets[index],
                    maxlen=PLOT_HISTORY_MAX)
        self.pending_draw_modes = list(mode_string_dict.values())
        self.list_lock.release()

    def rescale_for_units_change(self):
        # Chaco scales view automatically when 'auto' is stored
        if self.prev_extents[0] != 'auto':
            # Otherwise use has used mousewheel zoom and we need to transform
            if self.display_units == 'meters':
                new_scaling = (
                    (self.prev_extents[0] - self.offset[1]) * self.sf[1],
                    (self.prev_extents[1] - self.offset[1]) * self.sf[1],
                    (self.prev_extents[2] - self.offset[0]) * self.sf[0],
                    (self.prev_extents[3] - self.offset[0]) * self.sf[0])
            else:
                new_scaling = (self.prev_extents[0] / self.prev_sfs[1] +
                               self.prev_offsets[1],
                               self.prev_extents[1] / self.prev_sfs[1] +
                               self.prev_offsets[1],
                               self.prev_extents[2] / self.prev_sfs[0] +
                               self.prev_offsets[0],
                               self.prev_extents[3] / self.prev_sfs[0] +
                               self.prev_offsets[0])

            # set plot scaling accordingly
            self.plot.index_range.low_setting = new_scaling[0]
            self.plot.index_range.high_setting = new_scaling[1]
            self.plot.value_range.low_setting = new_scaling[2]
            self.plot.value_range.high_setting = new_scaling[3]

    def _solution_draw(self):
        self.list_lock.acquire()
        current_time = monotonic()
        self.last_plot_update_time = current_time
        pending_draw_modes = self.pending_draw_modes
        current_mode = pending_draw_modes[-1] if len(
            pending_draw_modes) > 0 else None
        # Periodically, we make sure to redraw older data to expire old plot data
        if current_time - self.last_stale_update_time > STALE_DATA_PERIOD:
            # we don't update old solution modes every timestep to try and save CPU
            pending_draw_modes = list(mode_string_dict.values())
            self.last_stale_update_time = current_time
        for mode_string in pending_draw_modes:
            if self.running:
                update_current = mode_string == current_mode if current_mode else True
                self._synchronize_plot_data_by_mode(
                    mode_string, update_current=update_current)
                if mode_string in self.pending_draw_modes:
                    self.pending_draw_modes.remove(mode_string)

        self.list_lock.release()
        if not self.zoomall and self.position_centered and self.running:
            d = (self.plot.index_range.high - self.plot.index_range.low) / 2.
            self.plot.index_range.set_bounds(
                (self.last_soln.lon - self.offset[1]) * self.sf[1] - d,
                (self.last_soln.lon - self.offset[1]) * self.sf[1] + d)
            d = (self.plot.value_range.high - self.plot.value_range.low) / 2.
            self.plot.value_range.set_bounds(
                (self.last_soln.lat - self.offset[0]) * self.sf[0] - d,
                (self.last_soln.lat - self.offset[0]) * self.sf[0] + d)
        if self.zoomall:
            self.recenter = False
            plot_square_axes(self.plot, ('lng_spp', 'lng_dgnss', 'lng_float',
                                         'lng_fixed', 'lng_sbas', 'lng_dr'),
                             ('lat_spp', 'lat_dgnss', 'lat_float', 'lat_fixed',
                              'lat_sbas', 'lat_dr'))
        if self.recenter:
            try:
                self.rescale_for_units_change()
                self.recenter = False
            except AttributeError:
                pass

    def dops_callback(self, sbp_msg, **metadata):
        flags = 0
        if sbp_msg.msg_type == SBP_MSG_DOPS_DEP_A:
            dops = MsgDopsDepA(sbp_msg)
            flags = 1
        else:
            dops = MsgDops(sbp_msg)
            flags = dops.flags
        if flags != 0:
            self.dops_table = [('PDOP', '%.1f' % (dops.pdop * 0.01)),
                               ('GDOP', '%.1f' % (dops.gdop * 0.01)),
                               ('TDOP', '%.1f' % (dops.tdop * 0.01)),
                               ('HDOP', '%.1f' % (dops.hdop * 0.01)),
                               ('VDOP', '%.1f' % (dops.vdop * 0.01))]
        else:
            self.dops_table = [('PDOP', EMPTY_STR), ('GDOP', EMPTY_STR),
                               ('TDOP', EMPTY_STR), ('HDOP', EMPTY_STR),
                               ('VDOP', EMPTY_STR)]

        self.dops_table.append(('DOPS Flags', '0x%03x' % flags))
        self.dops_table.append(
            ('INS Status', '0x{:0}'.format(self.ins_status_flags)))

    def ins_status_callback(self, sbp_msg, **metadata):
        status = MsgInsStatus(sbp_msg)
        self.ins_status_flags = status.flags
        self.last_ins_status_receipt_time = monotonic()

    def vel_ned_callback(self, sbp_msg, **metadata):
        flags = 0
        if sbp_msg.msg_type == SBP_MSG_VEL_NED_DEP_A:
            vel_ned = MsgVelNEDDepA(sbp_msg)
            flags = 1
        else:
            vel_ned = MsgVelNED(sbp_msg)
            flags = vel_ned.flags
        tow = vel_ned.tow * 1e-3
        if self.nsec is not None:
            tow += self.nsec * 1e-9

        ((tloc, secloc), (tgps, secgps)) = log_time_strings(self.week, tow)

        if self.directory_name_v == '':
            filepath_v = time.strftime("velocity_log_%Y%m%d-%H%M%S.csv")
        else:
            filepath_v = os.path.join(
                self.directory_name_v,
                time.strftime("velocity_log_%Y%m%d-%H%M%S.csv"))

        if not self.logging_v:
            self.vel_log_file = None

        if self.logging_v:
            if self.vel_log_file is None:
                self.vel_log_file = sopen(filepath_v, 'w')
                self.vel_log_file.write(
                    'pc_time,gps_time,tow(sec),north(m/s),east(m/s),down(m/s),speed(m/s),flags,num_signals\n'
                )
            log_str_gps = ''
            if tgps != "" and secgps != 0:
                log_str_gps = "{0}:{1:06.6f}".format(tgps, float(secgps))
            self.vel_log_file.write(
                '%s,%s,%.3f,%.6f,%.6f,%.6f,%.6f,%d,%d\n' %
                ("{0}:{1:06.6f}".format(tloc, float(secloc)), log_str_gps, tow,
                 vel_ned.n * 1e-3, vel_ned.e * 1e-3, vel_ned.d * 1e-3,
                 math.sqrt(vel_ned.n * vel_ned.n + vel_ned.e * vel_ned.e) *
                 1e-3, flags, vel_ned.n_sats))
            self.vel_log_file.flush()
        if (flags & 0x7) != 0:
            self.vel_table = [
                ('Vel. N', '% 8.4f' % (vel_ned.n * 1e-3)),
                ('Vel. E', '% 8.4f' % (vel_ned.e * 1e-3)),
                ('Vel. D', '% 8.4f' % (vel_ned.d * 1e-3)),
            ]
        else:
            self.vel_table = [
                ('Vel. N', EMPTY_STR),
                ('Vel. E', EMPTY_STR),
                ('Vel. D', EMPTY_STR),
            ]
        self.vel_table.append(('Vel Flags', '0x%03x' % flags))
        self.update_table()

    def gps_time_callback(self, sbp_msg, **metadata):
        if sbp_msg.msg_type == SBP_MSG_GPS_TIME_DEP_A:
            time_msg = MsgGPSTimeDepA(sbp_msg)
            flags = 1
        elif sbp_msg.msg_type == SBP_MSG_GPS_TIME:
            time_msg = MsgGPSTime(sbp_msg)
            flags = time_msg.flags
            if flags != 0:
                self.week = time_msg.wn
                self.nsec = time_msg.ns_residual

    def utc_time_callback(self, sbp_msg, **metadata):
        tmsg = MsgUtcTime(sbp_msg)
        microseconds = int(tmsg.ns / 1000.00)
        if tmsg.flags & 0x7 != 0:
            dt = datetime.datetime(tmsg.year, tmsg.month, tmsg.day, tmsg.hours,
                                   tmsg.minutes, tmsg.seconds, microseconds)
            self.utc_time = dt
            self.utc_time_flags = tmsg.flags
            if (tmsg.flags >> 3) & 0x3 == 0:
                self.utc_source = "Factory Default"
            elif (tmsg.flags >> 3) & 0x3 == 1:
                self.utc_source = "Non Volatile Memory"
            elif (tmsg.flags >> 3) & 0x3 == 2:
                self.utc_source = "Decoded this Session"
            else:
                self.utc_source = "Unknown"
        else:
            self.utc_time = None
            self.utc_source = None

    def __init__(self, link, dirname=''):
        super(SolutionView, self).__init__()
        self.velocity_view = VelocityView(link)
        self.ins_status_flags = 0
        self.pending_draw_modes = []
        self.recenter = False
        self.offset = (0, 0, 0)
        self.sf = (1, 1)
        self.list_lock = threading.Lock()
        self.scaling_lock = threading.Lock()
        self.slns = {
            'lat_spp': deque(maxlen=PLOT_HISTORY_MAX),
            'lng_spp': deque(maxlen=PLOT_HISTORY_MAX),
            'alt_spp': deque(maxlen=PLOT_HISTORY_MAX),
            'lat_dgnss': deque(maxlen=PLOT_HISTORY_MAX),
            'lng_dgnss': deque(maxlen=PLOT_HISTORY_MAX),
            'alt_dgnss': deque(maxlen=PLOT_HISTORY_MAX),
            'lat_float': deque(maxlen=PLOT_HISTORY_MAX),
            'lng_float': deque(maxlen=PLOT_HISTORY_MAX),
            'alt_float': deque(maxlen=PLOT_HISTORY_MAX),
            'lat_fixed': deque(maxlen=PLOT_HISTORY_MAX),
            'lng_fixed': deque(maxlen=PLOT_HISTORY_MAX),
            'alt_fixed': deque(maxlen=PLOT_HISTORY_MAX),
            'lat_sbas': deque(maxlen=PLOT_HISTORY_MAX),
            'lng_sbas': deque(maxlen=PLOT_HISTORY_MAX),
            'alt_sbas': deque(maxlen=PLOT_HISTORY_MAX),
            'lat_dr': deque(maxlen=PLOT_HISTORY_MAX),
            'lng_dr': deque(maxlen=PLOT_HISTORY_MAX),
            'alt_dr': deque(maxlen=PLOT_HISTORY_MAX)
        }
        self.lats = deque(maxlen=PLOT_HISTORY_MAX)
        self.lngs = deque(maxlen=PLOT_HISTORY_MAX)
        self.alts = deque(maxlen=PLOT_HISTORY_MAX)
        self.tows = deque(maxlen=PLOT_HISTORY_MAX)
        self.modes = deque(maxlen=PLOT_HISTORY_MAX)
        self.log_file = None
        self.directory_name_v = dirname
        self.directory_name_p = dirname
        self.vel_log_file = None
        self.last_stime_update = 0
        self.last_ins_status_receipt_time = 0
        self.last_soln = None

        self.altitude = 0
        self.longitude = 0
        self.latitude = 0
        self.last_pos_mode = 0
        self.ins_used = False
        self.last_plot_update_time = 0
        self.last_stale_update_time = 0

        self.plot_data = ArrayPlotData(lat_spp=[],
                                       lng_spp=[],
                                       alt_spp=[],
                                       cur_lat_spp=[],
                                       cur_lng_spp=[],
                                       lat_dgnss=[],
                                       lng_dgnss=[],
                                       alt_dgnss=[],
                                       cur_lat_dgnss=[],
                                       cur_lng_dgnss=[],
                                       lat_float=[],
                                       lng_float=[],
                                       alt_float=[],
                                       cur_lat_float=[],
                                       cur_lng_float=[],
                                       lat_fixed=[],
                                       lng_fixed=[],
                                       alt_fixed=[],
                                       cur_lat_fixed=[],
                                       cur_lng_fixed=[],
                                       lat_sbas=[],
                                       lng_sbas=[],
                                       cur_lat_sbas=[],
                                       cur_lng_sbas=[],
                                       lng_dr=[],
                                       lat_dr=[],
                                       cur_lat_dr=[],
                                       cur_lng_dr=[])
        self.plot = Plot(self.plot_data)

        # 1000 point buffer
        self.plot.plot(('lng_spp', 'lat_spp'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[SPP_MODE])
        self.plot.plot(('lng_spp', 'lat_spp'),
                       type='scatter',
                       name='',
                       color=color_dict[SPP_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_dgnss', 'lat_dgnss'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[DGNSS_MODE])
        self.plot.plot(('lng_dgnss', 'lat_dgnss'),
                       type='scatter',
                       name='',
                       color=color_dict[DGNSS_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_float', 'lat_float'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[FLOAT_MODE])
        self.plot.plot(('lng_float', 'lat_float'),
                       type='scatter',
                       name='',
                       color=color_dict[FLOAT_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_fixed', 'lat_fixed'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[FIXED_MODE])
        self.plot.plot(('lng_fixed', 'lat_fixed'),
                       type='scatter',
                       name='',
                       color=color_dict[FIXED_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_sbas', 'lat_sbas'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[SBAS_MODE])
        self.plot.plot(('lng_sbas', 'lat_sbas'),
                       type='scatter',
                       name='',
                       color=color_dict[SBAS_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_dr', 'lat_dr'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[DR_MODE])
        self.plot.plot(('lng_dr', 'lat_dr'),
                       type='scatter',
                       color=color_dict[DR_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        # current values
        spp = self.plot.plot(('cur_lng_spp', 'cur_lat_spp'),
                             type='scatter',
                             name=mode_dict[SPP_MODE],
                             color=color_dict[SPP_MODE],
                             marker='plus',
                             line_width=1.5,
                             marker_size=5.0)
        dgnss = self.plot.plot(('cur_lng_dgnss', 'cur_lat_dgnss'),
                               type='scatter',
                               name=mode_dict[DGNSS_MODE],
                               color=color_dict[DGNSS_MODE],
                               marker='plus',
                               line_width=1.5,
                               marker_size=5.0)
        rtkfloat = self.plot.plot(('cur_lng_float', 'cur_lat_float'),
                                  type='scatter',
                                  name=mode_dict[FLOAT_MODE],
                                  color=color_dict[FLOAT_MODE],
                                  marker='plus',
                                  line_width=1.5,
                                  marker_size=5.0)
        rtkfix = self.plot.plot(('cur_lng_fixed', 'cur_lat_fixed'),
                                type='scatter',
                                name=mode_dict[FIXED_MODE],
                                color=color_dict[FIXED_MODE],
                                marker='plus',
                                line_width=1.5,
                                marker_size=5.0)
        sbas = self.plot.plot(('cur_lng_sbas', 'cur_lat_sbas'),
                              type='scatter',
                              name=mode_dict[SBAS_MODE],
                              color=color_dict[SBAS_MODE],
                              marker='plus',
                              line_width=1.5,
                              marker_size=5.0)
        dr = self.plot.plot(('cur_lng_dr', 'cur_lat_dr'),
                            type='scatter',
                            name=mode_dict[DR_MODE],
                            color=color_dict[DR_MODE],
                            marker='plus',
                            line_width=1.5,
                            marker_size=5.0)
        plot_labels = ['SPP', 'SBAS', 'DGPS', 'RTK float', 'RTK fixed', 'DR']
        plots_legend = dict(
            zip(plot_labels, [spp, sbas, dgnss, rtkfloat, rtkfix, dr]))
        self.plot.legend.plots = plots_legend
        self.plot.legend.labels = plot_labels  # sets order
        self.plot.legend.visible = True

        self.plot.index_axis.tick_label_position = 'inside'
        self.plot.index_axis.tick_label_color = 'gray'
        self.plot.index_axis.tick_color = 'gray'
        self.plot.index_axis.title = 'Longitude (degrees)'
        self.plot.index_axis.title_spacing = 5
        self.plot.value_axis.tick_label_position = 'inside'
        self.plot.value_axis.tick_label_color = 'gray'
        self.plot.value_axis.tick_color = 'gray'
        self.plot.value_axis.title = 'Latitude (degrees)'
        self.plot.value_axis.title_spacing = 5
        self.plot.padding = (25, 25, 25, 25)

        self.plot.tools.append(PanTool(self.plot))
        zt = ZoomTool(self.plot,
                      zoom_factor=1.1,
                      tool_mode="box",
                      always_on=False)
        self.plot.overlays.append(zt)

        self.link = link
        self.link.add_callback(self.pos_llh_callback,
                               [SBP_MSG_POS_LLH_DEP_A, SBP_MSG_POS_LLH])
        self.link.add_callback(self.ins_status_callback, [SBP_MSG_INS_STATUS])
        self.link.add_callback(self.vel_ned_callback,
                               [SBP_MSG_VEL_NED_DEP_A, SBP_MSG_VEL_NED])
        self.link.add_callback(self.dops_callback,
                               [SBP_MSG_DOPS_DEP_A, SBP_MSG_DOPS])
        self.link.add_callback(self.gps_time_callback,
                               [SBP_MSG_GPS_TIME_DEP_A, SBP_MSG_GPS_TIME])
        self.link.add_callback(self.utc_time_callback, [SBP_MSG_UTC_TIME])
        self.link.add_callback(self.age_corrections_callback,
                               SBP_MSG_AGE_CORRECTIONS)
        self.week = None
        self.utc_time = None
        self.age_corrections = None
        self.nsec = 0
        self.meters_per_lat = None
        self.meters_per_lon = None
        self.python_console_cmds = {'solution': self}
        self.update_scheduler = UpdateScheduler()
Пример #8
0
class Setting(SettingBase):
    full_name = Str()
    section = Str()
    confirmed_set = Bool(True)
    readonly = Bool(False)
    traits_view = View(
        VGroup(
            Item('full_name', label='Name', style='readonly'),
            Item('value', editor=TextEditor(auto_set=False, enter_set=True),
                 visible_when='confirmed_set and not readonly'),
            Item('value', style='readonly',
                 visible_when='not confirmed_set or readonly', editor=TextEditor(readonly_allow_selection=True)),
            Item('units', style='readonly'),
            UItem('default_value',
                  style='readonly',
                  height=-1,
                  editor=MultilineTextEditor(TextEditor(multi_line=True)),
                  show_label=True,
                  resizable=True),
            UItem(
                'description',
                style='readonly',
                editor=MultilineTextEditor(TextEditor(multi_line=True)),
                show_label=True,
                resizable=True),
            UItem(
                'notes',
                label="Notes",
                height=-1,
                editor=MultilineTextEditor(TextEditor(multi_line=True)),
                style='readonly',
                show_label=True,
                resizable=True),
            show_border=True,
            label='Setting', ), )

    def __init__(self, name, section, value, ordering, settings):
        self.name = name
        self.section = section
        self.full_name = "%s.%s" % (section, name)
        self.value = value
        self.ordering = ordering
        self.settings = settings
        self.expert = settings.settings_yaml.get_field(section, name, 'expert')
        self.description = settings.settings_yaml.get_field(
            section, name, 'Description')
        self.units = settings.settings_yaml.get_field(section, name, 'units')
        self.notes = settings.settings_yaml.get_field(section, name, 'Notes')
        self.default_value = settings.settings_yaml.get_field(
            section, name, 'default value')
        readonly = settings.settings_yaml.get_field(section, name, 'readonly')
        # get_field returns empty string if field missing, so I need this check to assign bool to traits bool
        if readonly:
            self.readonly = True
        self.revert_in_progress = False
        self.timed_revert_thread = None
        self.confirmed_set = True
        # flag on each setting to indicate a write failure if the revert thread has run on the setting
        self.write_failure = False

    def revert_to_prior_value(self, name, old, new):
        '''Revert setting to old value in the case we can't confirm new value'''
        self.revert_in_progress = True
        self.value = old
        self.revert_in_progress = False
        # reset confirmed_set to make sure setting is editable again
        self.confirmed_set = True
        self.write_failure = True
        invalid_setting_prompt = prompt.CallbackPrompt(
            title="Settings Write Error",
            actions=[prompt.close_button], )
        invalid_setting_prompt.text = \
            ("\n   Unable to confirm that {0} was set to {1}.\n"
             "   Ensure the range and formatting of the entry are correct.\n"
             "   Ensure that the new setting value did not interrupt console communication.").format(self.name, new)
        invalid_setting_prompt.run()

    def _value_changed(self, name, old, new):
        '''When a user changes a value, kick off a timed revert thread to revert it in GUI if no confirmation
           that the change was successful is received. '''
        if (old != new and old is not Undefined and new is not Undefined):
            if type(self.value) == unicode:
                self.value = self.value.encode('ascii', 'replace')
            # Revert_in_progress is a guard to prevent this from running when the revert function changes the value
            if not self.revert_in_progress:
                self.confirmed_set = False
                self.timed_revert_thread = TimedDelayStoppableThread(SETTINGS_REVERT_TIMEOUT,
                                                                     target=self.revert_to_prior_value,
                                                                     args=(name, old, new))
                self.settings.set(self.section, self.name, self.value)
                self.timed_revert_thread.start()
Пример #9
0
class SbpRelayView(HasTraits):
    """
    SBP Relay view- Class allows user to specify port, IP address, and message set
    to relay over UDP and to configure a http connection
    """
    running = Bool(False)
    _network_info = List()
    configured = Bool(False)
    broadcasting = Bool(False)
    msg_enum = Enum('Observations', 'All')
    ip_ad = String(DEFAULT_UDP_ADDRESS)
    port = Int(DEFAULT_UDP_PORT)
    information = String(
        'UDP Streaming\n\nBroadcast SBP information received by'
        ' the console to other machines or processes over UDP. With the \'Observations\''
        ' radio button selected, the console will broadcast the necessary information'
        ' for a rover Piksi to acheive an RTK solution.'
        '\n\nThis can be used to stream observations to a remote Piksi through'
        ' aircraft telemetry via ground control software such as MAVProxy or'
        ' Mission Planner.')
    show_networking = Bool(False)
    http_information = String(
        'Experimental Piksi Networking\n\n'
        "Use this widget to connect Piksi receivers to http servers.\n\n")
    start = Button(label='Start', toggle=True, width=32)
    stop = Button(label='Stop', toggle=True, width=32)
    connected_rover = Bool(False)
    connect_rover = Button(label='Connect', toggle=True, width=32)
    disconnect_rover = Button(label='Disconnect', toggle=True, width=32)
    url = String()
    base_pragma = String()
    rover_pragma = String()
    base_device_uid = String()
    rover_device_uid = String()
    toggle = True
    network_refresh_button = SVGButton(
        label='Refresh Network Status',
        tooltip='Refresh Network Status',
        filename=resource_filename('console/images/fontawesome/refresh.svg'),
        width=16,
        height=16,
        aligment='center')
    cell_modem_view = Instance(CellModemView)
    view = View(
        VGroup(
            spring,
            HGroup(VGroup(
                Item('msg_enum',
                     label="Messages to broadcast",
                     style='custom',
                     enabled_when='not running'),
                Item('ip_ad', label='IP Address', enabled_when='not running'),
                Item('port', label="Port", enabled_when='not running'),
                HGroup(
                    spring,
                    UItem('start',
                          enabled_when='not running',
                          show_label=False),
                    UItem('stop', enabled_when='running', show_label=False),
                    spring)),
                   VGroup(
                       Item('information',
                            label="Notes",
                            height=10,
                            editor=MultilineTextEditor(
                                TextEditor(multi_line=True)),
                            style='readonly',
                            show_label=False,
                            resizable=True,
                            padding=15),
                       spring,
                   ),
                   visible_when='not show_networking'), spring,
            HGroup(VGroup(
                HGroup(
                    spring,
                    UItem('connect_rover',
                          enabled_when='not connected_rover',
                          show_label=False),
                    UItem('disconnect_rover',
                          enabled_when='connected_rover',
                          show_label=False), spring),
                HGroup(
                    Spring(springy=False, width=2),
                    Item('url',
                         enabled_when='not connected_rover',
                         show_label=True), Spring(springy=False, width=2)),
                HGroup(spring, Item('base_pragma', label='Base option '),
                       Item('base_device_uid', label='Base device '), spring),
                HGroup(spring, Item('rover_pragma', label='Rover option'),
                       Item('rover_device_uid', label='Rover device'), spring),
            ),
                   VGroup(
                       Item('http_information',
                            label="Notes",
                            height=10,
                            editor=MultilineTextEditor(
                                TextEditor(multi_line=True)),
                            style='readonly',
                            show_label=False,
                            resizable=True,
                            padding=15),
                       spring,
                   ),
                   visible_when='show_networking'),
            HGroup(
                Item('cell_modem_view', style='custom', show_label=False),
                VGroup(Item(
                    '_network_info',
                    style='readonly',
                    editor=TabularEditor(adapter=SimpleNetworkAdapter()),
                    show_label=False,
                ),
                       Item('network_refresh_button',
                            show_label=False,
                            width=0.50),
                       show_border=True,
                       label="Network"),
            )))

    def _network_callback(self, m, **metadata):
        txstr = sizeof_fmt(m.tx_bytes),
        rxstr = sizeof_fmt(m.rx_bytes)
        if m.interface_name.startswith(
                'ppp0'):  # Hack for ppp tx and rx which doesn't work
            txstr = "---"
            rxstr = "---"
        elif m.interface_name.startswith('lo') or m.interface_name.startswith(
                'sit0'):
            return
        table_row = ((m.interface_name, ip_bytes_to_string(m.ipv4_address),
                      ((m.flags & (1 << 6)) != 0), txstr, rxstr))
        exists = False
        for i, each in enumerate(self._network_info):
            if each[0][0] == table_row[0][0]:
                self._network_info[i] = table_row
                exists = True
        if not exists:
            self._network_info.append(table_row)

    def __init__(self,
                 link,
                 show_networking=False,
                 device_uid=None,
                 url='',
                 whitelist=None,
                 rover_pragma='',
                 base_pragma='',
                 rover_uuid='',
                 base_uuid='',
                 connect=False,
                 verbose=False):
        """
        Traits tab with UI for UDP broadcast of SBP.

        Parameters
        ----------
        link : sbp.client.handler.Handler
          Link for SBP transfer to/from Piksi.
        device_uid : str
          Piksi Device UUID (defaults to None)
        base : str
          HTTP endpoint
        whitelist : [int] | None
          Piksi Device UUID (defaults to None)

        """
        self.link = link
        # Whitelist used for UDP broadcast view
        self.cell_modem_view = CellModemView(link)
        self.msgs = OBS_MSGS
        # register a callback when the msg_enum trait changes
        self.on_trait_change(self.update_msgs, 'msg_enum')
        # Whitelist used for broadcasting
        self.whitelist = whitelist
        self.device_uid = None
        self.python_console_cmds = {'update': self}
        self.rover_pragma = rover_pragma
        self.base_pragma = base_pragma
        self.rover_device_uid = rover_uuid
        self.base_device_uid = base_uuid
        self.verbose = verbose
        self.http_watchdog_thread = None
        self.url = url
        self.show_networking = show_networking
        if connect:
            self.connect_when_uuid_received = True
        else:
            self.connect_when_uuid_received = False
        self.cellmodem_interface_name = "ppp0"
        self.link.add_callback(self._network_callback,
                               SBP_MSG_NETWORK_STATE_RESP)

    def update_msgs(self):
        """Updates the instance variable msgs which store the msgs that we
        will send over UDP.

        """
        if self.msg_enum == 'Observations':
            self.msgs = OBS_MSGS
        elif self.msg_enum == 'All':
            self.msgs = [None]
        else:
            raise NotImplementedError

    def set_route(self, uuid=None, serial_id=None, channel=CHANNEL_UUID):
        """Sets serial_id hash for HTTP headers.

        Parameters
        ----------
        uuid: str
          real uuid of device
        serial_id : int
          Piksi device ID
        channel : str
          UUID namespace for device UUID

        """
        if uuid:
            device_uid = uuid
        elif serial_id:
            device_uid = str(get_uuid(channel, serial_id % 1000))
        else:
            print(
                "Improper call of set_route, either a serial number or UUID should be passed"
            )
            device_uid = str(get_uuid(channel, 1234))
            print("Setting UUID to default value of {0}".format(device_uid))
        self.device_uid = device_uid

    def _prompt_setting_error(self, text):
        """Nonblocking prompt for a device setting error.

        Parameters
        ----------
        text : str
          Helpful error message for the user

        """
        prompt = CallbackPrompt(title="Setting Error", actions=[close_button])
        prompt.text = text
        prompt.run(block=False)

    def update_network_state(self):
        self._network_refresh_button_fired()

    def _network_refresh_button_fired(self):
        self._network_info = []
        self.link(MsgNetworkStateReq())

    def _disconnect_rover_fired(self):
        """Handle callback for HTTP rover disconnects.

        """
        try:
            if (isinstance(self.http_watchdog_thread, threading.Thread)
                    and not self.http_watchdog_thread.stopped()):
                self.http_watchdog_thread.stop()
            else:
                print(("Unable to disconnect: Http watchdog thread "
                       "inititalized at {0} and connected since {1} has "
                       "already been stopped").format(
                           self.http_watchdog_thread.get_init_time(),
                           self.http_watchdog_thread.get_connect_time()))
            self.connected_rover = False
        except:  # noqa
            self.connected_rover = False
            import traceback
            print(traceback.format_exc())

    def _connect_rover_fired(self):
        """Handle callback for HTTP rover connections.  Launches an instance of http_watchdog_thread.
        """
        if not self.device_uid:
            msg = "\nDevice ID not found!\n\nConnection requires a valid Piksi device ID."
            self._prompt_setting_error(msg)
            return
        try:
            _base_device_uid = self.base_device_uid or self.device_uid
            _rover_device_uid = self.rover_device_uid or self.device_uid
            config = HttpConsoleConnectConfig(self.link, self.device_uid,
                                              self.url, self.whitelist,
                                              self.rover_pragma,
                                              self.base_pragma,
                                              _rover_device_uid,
                                              _base_device_uid)
            self.http_watchdog_thread = HttpWatchdogThread(
                link=self.link,
                http_config=config,
                stopped_callback=self._disconnect_rover_fired,
                verbose=self.verbose)
            self.connected_rover = True
            self.http_watchdog_thread.start()
        except:  # noqa
            if (isinstance(self.http_watchdog_thread, threading.Thread)
                    and self.http_watchdog_thread.stopped()):
                self.http_watchdog_thread.stop()
            self.connected_rover = False
            import traceback
            print(traceback.format_exc())

    def _start_fired(self):
        """Handle start udp broadcast button. Registers callbacks on
        self.link for each of the self.msgs If self.msgs is None, it
        registers one generic callback for all messages.

        """
        self.running = True
        try:
            self.func = UdpLogger(self.ip_ad, self.port)
            self.link.add_callback(self.func, self.msgs)
        except:  # noqa
            import traceback
            print(traceback.format_exc())

    def _stop_fired(self):
        """Handle the stop udp broadcast button. It uses the self.funcs and
        self.msgs to remove the callbacks that were registered when the
        start button was pressed.

        """
        try:
            self.link.remove_callback(self.func, self.msgs)
            self.func.__exit__()
            self.func = None
            self.running = False
        except:  # noqa
            import traceback
            print(traceback.format_exc())
Пример #10
0
class Setting(SettingBase):
    full_name = Str()
    section = Str()
    confirmed_set = Bool(True)
    readonly = Bool(False)
    traits_view = View(
        VGroup(
            Item('full_name', label='Name', style='readonly'),
            Item('value',
                 editor=TextEditor(auto_set=False, enter_set=True),
                 visible_when='confirmed_set and not readonly'),
            Item('value',
                 style='readonly',
                 visible_when='not confirmed_set or readonly',
                 editor=TextEditor(readonly_allow_selection=True)),
            Item('units', style='readonly'),
            UItem('default_value',
                  style='readonly',
                  height=-1,
                  editor=MultilineTextEditor(TextEditor(multi_line=True)),
                  show_label=True,
                  resizable=True),
            UItem('description',
                  style='readonly',
                  editor=MultilineTextEditor(TextEditor(multi_line=True)),
                  show_label=True,
                  resizable=True),
            UItem('notes',
                  label="Notes",
                  height=-1,
                  editor=MultilineTextEditor(TextEditor(multi_line=True)),
                  style='readonly',
                  show_label=True,
                  resizable=True),
            show_border=True,
            label='Setting',
        ), )

    def __init__(self, name, section, value, ordering=0, settings=None):
        # _prevent_revert_thread attribute is a guard against starting any timed
        # revert thread. It should be True when .value changes due to initialization
        # or updating of GUI without any physical setting change commanded on device
        self._prevent_revert_thread = True
        self.name = name
        self.section = section
        self.full_name = "%s.%s" % (section, name)
        self.value = value
        self.ordering = ordering
        self.settings = settings
        self.timed_revert_thread = None
        self.confirmed_set = True
        # flag on each setting to indicate a write failure if the revert thread has run on the setting
        self.write_failure = False
        if settings:
            self.expert = settings.settings_yaml.get_field(
                section, name, 'expert')
            self.description = settings.settings_yaml.get_field(
                section, name, 'Description')
            self.units = settings.settings_yaml.get_field(
                section, name, 'units')
            self.notes = settings.settings_yaml.get_field(
                section, name, 'Notes')
            self.default_value = settings.settings_yaml.get_field(
                section, name, 'default value')
            readonly = settings.settings_yaml.get_field(
                section, name, 'readonly')
            # get_field returns empty string if field missing, so I need this check to assign bool to traits bool
            if readonly:
                self.readonly = True
        self._prevent_revert_thread = False

    def revert_to_prior_value(self,
                              name,
                              old,
                              new,
                              error_value=SBP_WRITE_STATUS.TIMED_OUT):
        '''Revert setting to old value in the case we can't confirm new value'''

        if self.readonly:
            return

        self._prevent_revert_thread = True
        self.value = old
        self._prevent_revert_thread = False
        # reset confirmed_set to make sure setting is editable again
        self.confirmed_set = True
        self.write_failure = True
        invalid_setting_prompt = prompt.CallbackPrompt(
            title="Settings Write Error",
            actions=[prompt.close_button],
        )
        if error_value == SBP_WRITE_STATUS.TIMED_OUT:
            invalid_setting_prompt.text = \
                ("\n   Unable to confirm that {0} was set to {1}.\n"
                 "   Message timed out.\n"
                 "   Ensure that the new setting value did not interrupt console communication.\n"
                 "   Error Value: {2}")
        else:
            invalid_setting_prompt.text = \
                ("\n   Unable to set {0} to {1}.\n")

        if error_value == SBP_WRITE_STATUS.VALUE_REJECTED:
            invalid_setting_prompt.text += \
                ("   Ensure the range and formatting of the entry are correct.\n"
                 "   Error Value: {2}")
        elif error_value == SBP_WRITE_STATUS.SETTING_REJECTED:
            invalid_setting_prompt.text += \
                ("   {0} is not a valid setting.\n"
                 "   Error Value: {2}")
        elif error_value == SBP_WRITE_STATUS.PARSE_FAILED:
            invalid_setting_prompt.text += \
                ("   Could not parse value: {1}.\n"
                 "   Error Value: {2}")
        elif error_value == SBP_WRITE_STATUS.READ_ONLY:
            invalid_setting_prompt.text += \
                ("   {0} is read-only.\n"
                 "   Error Value: {2}")
        elif error_value == SBP_WRITE_STATUS.MODIFY_DISABLED:
            invalid_setting_prompt.text += \
                ("   Modifying {0} is currently disabled.\n"
                 "   Error Value: {2}")
        elif error_value == SBP_WRITE_STATUS.SERVICE_FAILED:
            invalid_setting_prompt.text += \
                ("   Service failed while changing setting. See logs.\n"
                 "   Error Value: {2}")
        else:
            invalid_setting_prompt.text += \
                ("   Unknown Error.\n"
                 "   Error Value: {2}")
        invalid_setting_prompt.text = invalid_setting_prompt.text.format(
            self.name, new, error_value)
        invalid_setting_prompt.run()

    def _value_changed(self, name, old, new):
        '''When a user changes a value, kick off a timed revert thread to revert it in GUI if no confirmation
            that the change was successful is received.'''
        if not self._prevent_revert_thread:
            if getattr(self, 'settings', None):
                if (old != new and old is not Undefined
                        and new is not Undefined):
                    if type(self.value) == unicode:
                        self.value = self.value.encode('ascii', 'replace')
                    self.confirmed_set = False
                    self.timed_revert_thread = TimedDelayStoppableThread(
                        SETTINGS_REVERT_TIMEOUT,
                        target=self.revert_to_prior_value,
                        args=(name, old, new))
                    self.settings.set(self.section, self.name, self.value)
                    self.timed_revert_thread.start()
                # If we have toggled the Inertial Nav enable setting (currently "output mode")
                # we display some helpful hints for the user
                if (self.section == "ins" and self.name == "output_mode"
                        and old is not None and self.settings is not None):
                    if new in [
                            'GNSS and INS', 'INS Only', 'Loosely Coupled',
                            'LC + GNSS', 'Debug', 'debug'
                    ]:
                        hint_thread = threading.Thread(
                            target=self.settings._display_ins_settings_hint)
                        hint_thread.start()
                    # regardless of which way setting is going, a restart is required
                    else:
                        self.settings.display_ins_output_hint()
Пример #11
0
class Setting(SettingBase):
    full_name = Str()
    section = Str()
    confirmed_set = Bool(True)
    readonly = Bool(False)
    traits_view = View(
        VGroup(
            Item('full_name', label='Name', style='readonly'),
            Item('value',
                 editor=TextEditor(auto_set=False, enter_set=True),
                 visible_when='confirmed_set and not readonly'),
            Item('value',
                 style='readonly',
                 visible_when='not confirmed_set or readonly',
                 editor=TextEditor(readonly_allow_selection=True)),
            Item('units', style='readonly'),
            UItem('default_value',
                  style='readonly',
                  height=-1,
                  editor=MultilineTextEditor(TextEditor(multi_line=True)),
                  show_label=True,
                  resizable=True),
            UItem(
                'description',
                style='readonly',
                editor=MultilineTextEditor(TextEditor(multi_line=True)),
                show_label=True,
                resizable=True),
            UItem(
                'notes',
                label="Notes",
                height=-1,
                editor=MultilineTextEditor(TextEditor(multi_line=True)),
                style='readonly',
                show_label=True,
                resizable=True),
            show_border=True,
            label='Setting', ), )

    def __init__(self, name, section, value, ordering=0, settings=None):
        self.name = name
        self.section = section
        self.full_name = "%s.%s" % (section, name)
        self.value = value
        self.ordering = ordering
        self.settings = settings
        self.confirmed_set = True
        self.skip_write_req = False

        if settings is None:
            return

        self.expert = settings.settings_yaml.get_field(section, name, 'expert')
        self.description = settings.settings_yaml.get_field(
            section, name, 'Description')
        self.units = settings.settings_yaml.get_field(section, name, 'units')
        self.notes = settings.settings_yaml.get_field(section, name, 'Notes')
        self.default_value = settings.settings_yaml.get_field(
            section, name, 'default value')
        readonly = settings.settings_yaml.get_field(section, name, 'readonly')
        # get_field returns empty string if field missing, so I need this check to assign bool to traits bool
        if readonly:
            self.readonly = True

    def revert_to_prior_value(self, section, name, old, new, error_value):
        '''Revert setting to old value in the case we can't confirm new value'''

        if self.readonly:
            return

        self.skip_write_req = True
        self.value = old
        self.skip_write_req = False

        invalid_setting_prompt = prompt.CallbackPrompt(
            title="Settings Write Error: {}.{}".format(section, name),
            actions=[prompt.close_button], )
        if error_value == SettingsWriteResponseCodes.SETTINGS_WR_TIMEOUT:
            invalid_setting_prompt.text = \
                ("\n   Unable to confirm that {0} was set to {1}.\n"
                 "   Message timed out.\n"
                 "   Ensure that the new setting value did not interrupt console communication.\n"
                 "   Error Value: {2}")
        elif error_value == SettingsWriteResponseCodes.SETTINGS_WR_VALUE_REJECTED:
            invalid_setting_prompt.text += \
                ("   Ensure the range and formatting of the entry are correct.\n"
                 "   Error Value: {2}")
        elif error_value == SettingsWriteResponseCodes.SETTINGS_WR_SETTING_REJECTED:
            invalid_setting_prompt.text += \
                ("   {0} is not a valid setting.\n"
                 "   Error Value: {2}")
        elif error_value == SettingsWriteResponseCodes.SETTINGS_WR_PARSE_FAILED:
            invalid_setting_prompt.text += \
                ("   Could not parse value: {1}.\n"
                 "   Error Value: {2}")
        elif error_value == SettingsWriteResponseCodes.SETTINGS_WR_READ_ONLY:
            invalid_setting_prompt.text += \
                ("   {0} is read-only.\n"
                 "   Error Value: {2}")
        elif error_value == SettingsWriteResponseCodes.SETTINGS_WR_MODIFY_DISABLED:
            invalid_setting_prompt.text += \
                ("   Modifying {0} is currently disabled.\n"
                 "   Error Value: {2}")
        elif error_value == SettingsWriteResponseCodes.SETTINGS_WR_SERVICE_FAILED:
            invalid_setting_prompt.text += \
                ("   Service failed while changing setting. See logs.\n"
                 "   Error Value: {2}")
        else:
            invalid_setting_prompt.text += \
                ("   Unknown Error.\n"
                 "   Error Value: {2}")
        invalid_setting_prompt.text = invalid_setting_prompt.text.format(self.name, new, error_value)
        invalid_setting_prompt.run()

    def _write_value(self, old, new):
        if (old is not Undefined and new is not Undefined):
            self.confirmed_set = False
            (error, section, name, value) = self.settings.settings_api.write(self.section, self.name, new)
            if error == SettingsWriteResponseCodes.SETTINGS_WR_OK:
                self.value = new
            else:
                self.revert_to_prior_value(self.section, self.name, old, new, error)

            self.confirmed_set = True

        # If we have toggled the Inertial Nav enable setting (currently "output mode")
        # we display some helpful hints for the user
        if (self.section == "ins" and self.name == "output_mode" and
                old is not None and self.settings is not None):
            if new in ['GNSS and INS', 'INS Only', 'Loosely Coupled', 'LC + GNSS', 'Debug', 'debug']:
                hint_thread = threading.Thread(
                    target=self.settings._display_ins_settings_hint)
                hint_thread.start()
            # regardless of which way setting is going, a restart is required
            else:
                self.settings.display_ins_output_hint()

    def _value_changed(self, name, old, new):
        if getattr(self, 'settings', None) is None:
            return

        if self.skip_write_req or old == new:
            return

        self.settings.workqueue.put(self._write_value, old, new)
Пример #12
0
class Setting(SettingBase):
    full_name = Str()
    section = Str()

    traits_view = View(
        VGroup(
            Item('full_name', label='Name', style='readonly'),
            Item('value', editor=TextEditor(auto_set=False, enter_set=True)),
            Item('units', style='readonly'),
            Item('default_value', style='readonly'),
            UItem('description',
                  style='readonly',
                  editor=MultilineTextEditor(TextEditor(multi_line=True)),
                  show_label=True,
                  resizable=True),
            UItem('notes',
                  label="Notes",
                  height=-1,
                  editor=MultilineTextEditor(TextEditor(multi_line=True)),
                  style='readonly',
                  show_label=True,
                  resizable=True),
            show_border=True,
            label='Setting',
        ), )

    def __init__(self, name, section, value, ordering, settings):
        self.name = name
        self.section = section
        self.full_name = "%s.%s" % (section, name)
        self.value = value
        self.ordering = ordering
        self.settings = settings
        self.expert = settings.settings_yaml.get_field(section, name, 'expert')
        self.description = settings.settings_yaml.get_field(
            section, name, 'Description')
        self.units = settings.settings_yaml.get_field(section, name, 'units')
        self.notes = settings.settings_yaml.get_field(section, name, 'Notes')
        self.default_value = settings.settings_yaml.get_field(
            section, name, 'default value')
        self.revert_in_progress = False
        self.confirmed_set = False

    def revert_after_delay(self, delay, name, old, new, popop=True):
        '''Revert setting to old value after delay in seconds has elapsed'''
        time.sleep(delay)
        if self.confirmed_set != True:
            self.revert_in_progress = True
            self.value = old
            invalid_setting_prompt = prompt.CallbackPrompt(
                title="Settings Write Error",
                actions=[prompt.close_button],
            )
            invalid_setting_prompt.text = \
                   ("\n   Unable to confirm that {0} was set to {1}.\n"
                      "   Ensure the range and formatting of the entry are correct.\n"
                      "    Ensure that the new setting value did not interrupt console communication.").format(self.name, new)
            invalid_setting_prompt.run()

        self.confirmed_set = False

    def _value_changed(self, name, old, new):
        if (old != new and old is not Undefined and new is not Undefined):
            if type(self.value) == unicode:
                self.value = self.value.encode('ascii', 'replace')
            if not self.revert_in_progress:
                self.timed_revert_thread = threading.Thread(
                    target=self.revert_after_delay, args=(1, name, old, new))
                self.settings.set(self.section, self.name, self.value)
                self.timed_revert_thread.start()
            self.revert_in_progress = False
Пример #13
0
class SbpRelayView(HasTraits):
    """
  SBP Relay view- Class allows user to specify port, IP address, and message set
  to relay over UDP and to configure a skylark connection
  """
    running = Bool(False)
    configured = Bool(False)
    broadcasting = Bool(False)
    msg_enum = Enum('Observations', 'All')
    ip_ad = String(DEFAULT_UDP_ADDRESS)
    port = Int(DEFAULT_UDP_PORT)
    information = String(
        'UDP Streaming\n\nBroadcast SBP information received by'
        ' the console to other machines or processes over UDP. With the \'Observations\''
        ' radio button selected, the console will broadcast the necessary information'
        ' for a rover Piksi to acheive an RTK solution.'
        '\n\nThis can be used to stream observations to a remote Piksi through'
        ' aircraft telemetry via ground control software such as MAVProxy or'
        ' Mission Planner.')
    http_information = String(
        'Skylark - Experimental Piksi Networking\n\n'
        "Skylark is Swift Navigation's Internet service for connecting Piksi receivers without the use of a radio. To receive GPS observations from the closest nearby Piksi base station (within 5km), click Connect to Skylark.\n\n"
    )
    start = Button(label='Start', toggle=True, width=32)
    stop = Button(label='Stop', toggle=True, width=32)
    connected_rover = Bool(False)
    connect_rover = Button(label='Connect to Skylark', toggle=True, width=32)
    disconnect_rover = Button(label='Disconnect from Skylark',
                              toggle=True,
                              width=32)
    skylark_url = String()
    base_pragma = String()
    rover_pragma = String()
    base_device_uid = String()
    rover_device_uid = String()
    toggle = True
    view = View(
        VGroup(
            spring,
            HGroup(
                VGroup(
                    Item('running',
                         show_label=True,
                         style='readonly',
                         visible_when='running'),
                    Item('msg_enum',
                         label="Messages to broadcast",
                         style='custom',
                         enabled_when='not running'),
                    Item('ip_ad',
                         label='IP Address',
                         enabled_when='not running'),
                    Item('port', label="Port", enabled_when='not running'),
                    HGroup(
                        spring,
                        UItem('start',
                              enabled_when='not running',
                              show_label=False),
                        UItem('stop', enabled_when='running',
                              show_label=False), spring)),
                VGroup(
                    Item('information',
                         label="Notes",
                         height=10,
                         editor=MultilineTextEditor(
                             TextEditor(multi_line=True)),
                         style='readonly',
                         show_label=False,
                         resizable=True,
                         padding=15),
                    spring,
                ),
            ), spring,
            HGroup(
                VGroup(
                    HGroup(
                        spring,
                        UItem('connect_rover',
                              enabled_when='not connected_rover',
                              show_label=False),
                        UItem('disconnect_rover',
                              enabled_when='connected_rover',
                              show_label=False), spring),
                    HGroup(
                        Spring(springy=False, width=2),
                        Item('skylark_url',
                             enabled_when='not connected_rover',
                             show_label=True), Spring(springy=False, width=2)),
                    HGroup(spring, Item('base_pragma', label='Base option '),
                           Item('base_device_uid', label='Base device '),
                           spring),
                    HGroup(spring, Item('rover_pragma', label='Rover option'),
                           Item('rover_device_uid', label='Rover device'),
                           spring),
                ),
                VGroup(
                    Item('http_information',
                         label="Notes",
                         height=10,
                         editor=MultilineTextEditor(
                             TextEditor(multi_line=True)),
                         style='readonly',
                         show_label=False,
                         resizable=True,
                         padding=15),
                    spring,
                ),
            ), spring))

    def __init__(self,
                 link,
                 device_uid=None,
                 base=DEFAULT_BASE,
                 whitelist=None,
                 rover_pragma='',
                 base_pragma='',
                 rover_uuid='',
                 base_uuid='',
                 connect=False,
                 verbose=False):
        """
    Traits tab with UI for UDP broadcast of SBP.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    device_uid : str
      Piksi Device UUID (defaults to None)
    base : str
      HTTP endpoint
    whitelist : [int] | None
      Piksi Device UUID (defaults to None)

    """
        self.link = link
        # Whitelist used for UDP broadcast view
        self.msgs = OBS_MSGS
        # register a callback when the msg_enum trait changes
        self.on_trait_change(self.update_msgs, 'msg_enum')
        # Whitelist used for Skylark broadcasting
        self.whitelist = whitelist
        self.device_uid = None
        self.python_console_cmds = {'update': self}
        self.rover_pragma = rover_pragma
        self.base_pragma = base_pragma
        self.rover_device_uid = rover_uuid
        self.base_device_uid = base_uuid
        self.verbose = verbose
        self.skylark_watchdog_thread = None
        self.skylark_url = base
        if connect:
            self.connect_when_uuid_received = True
        else:
            self.connect_when_uuid_received = False

    def update_msgs(self):
        """Updates the instance variable msgs which store the msgs that we
    will send over UDP.

    """
        if self.msg_enum == 'Observations':
            self.msgs = OBS_MSGS
        elif self.msg_enum == 'All':
            self.msgs = [None]
        else:
            raise NotImplementedError

    def set_route(self, uuid=None, serial_id=None, channel=CHANNEL_UUID):
        """Sets serial_id hash for HTTP headers.

    Parameters
    ----------
    uuid: str
      real uuid of device
    serial_id : int
      Piksi device ID
    channel : str
      UUID namespace for device UUID

    """
        if uuid:
            device_uid = uuid
        elif serial_id:
            device_uid = str(get_uuid(channel, serial_id % 1000))
        else:
            print "Improper call of set_route, either a serial number or UUID should be passed"
            device_uid = str(get_uuid(channel, 1234))
            print "Setting UUID to default value of {0}".format(device_uid)
        self.device_uid = device_uid

    def _prompt_setting_error(self, text):
        """Nonblocking prompt for a device setting error.

    Parameters
    ----------
    text : str
      Helpful error message for the user

    """
        prompt = CallbackPrompt(title="Setting Error", actions=[close_button])
        prompt.text = text
        prompt.run(block=False)

    def _disconnect_rover_fired(self):
        """Handle callback for HTTP rover disconnects.

    """
        try:
            if isinstance(self.skylark_watchdog_thread, threading.Thread) and \
               not self.skylark_watchdog_thread.stopped():
                self.skylark_watchdog_thread.stop()
            else:
                print(
                    "Unable to disconnect: Skylark watchdog thread "
                    "inititalized at {0} and connected since {1} has "
                    "already been stopped").format(
                        self.skylark_watchdog_thread.get_init_time(),
                        self.skylark_watchdog_thread.get_connect_time())
            self.connected_rover = False
        except:
            self.connected_rover = False
            import traceback
            print traceback.format_exc()

    def _connect_rover_fired(self):
        """Handle callback for HTTP rover connections.  Launches an instance of skylark_watchdog_thread.
    """
        if not self.device_uid:
            msg = "\nDevice ID not found!\n\nConnection requires a valid Piksi device ID."
            self._prompt_setting_error(msg)
            return
        try:
            _base_device_uid = self.base_device_uid or self.device_uid
            _rover_device_uid = self.rover_device_uid or self.device_uid
            config = SkylarkConsoleConnectConfig(
                self.link, self.device_uid, self.skylark_url, self.whitelist,
                self.rover_pragma, self.base_pragma, _rover_device_uid,
                _base_device_uid)
            self.skylark_watchdog_thread = SkylarkWatchdogThread(
                link=self.link,
                skylark_config=config,
                stopped_callback=self._disconnect_rover_fired,
                verbose=self.verbose)
            self.connected_rover = True
            self.skylark_watchdog_thread.start()
        except:
            if isinstance(self.skylark_watchdog_thread, threading.Thread) \
               and self.skylark_watchdog_thread.stopped():
                self.skylark_watchdog_thread.stop()
            self.connected_rover = False
            import traceback
            print traceback.format_exc()

    def _start_fired(self):
        """Handle start udp broadcast button. Registers callbacks on
    self.link for each of the self.msgs If self.msgs is None, it
    registers one generic callback for all messages.

    """
        self.running = True
        try:
            self.func = UdpLogger(self.ip_ad, self.port)
            self.link.add_callback(self.func, self.msgs)
        except:
            import traceback
            print traceback.format_exc()

    def _stop_fired(self):
        """Handle the stop udp broadcast button. It uses the self.funcs and
    self.msgs to remove the callbacks that were registered when the
    start button was pressed.

    """
        try:
            self.link.remove_callback(self.func, self.msgs)
            self.func.__exit__()
            self.func = None
            self.running = False
        except:
            import traceback
            print traceback.format_exc()
Пример #14
0
class SbpRelayView(HasTraits):
    """
    Class allows user to specify port, IP address, and message set
    to relay over UDP.
    """
    running = Bool(False)
    _network_info = List()
    configured = Bool(False)
    broadcasting = Bool(False)
    msg_enum = Enum('Observations', 'All')
    ip_ad = String(DEFAULT_UDP_ADDRESS)
    port = Int(DEFAULT_UDP_PORT)
    information = String(
        'UDP Streaming\n\nBroadcast SBP information received by'
        ' the console to other machines or processes over UDP. With the \'Observations\''
        ' radio button selected, the console will broadcast the necessary information'
        ' for a rover Piksi to acheive an RTK solution.'
        '\n\nThis can be used to stream observations to a remote Piksi through'
        ' aircraft telemetry via ground control software such as MAVProxy or'
        ' Mission Planner.')
    start = Button(label='Start', toggle=True, width=32)
    stop = Button(label='Stop', toggle=True, width=32)
    network_refresh_button = SVGButton(
        label='Refresh Network Status',
        tooltip='Refresh Network Status',
        filename=resource_filename('console/images/fontawesome/refresh.svg'),
        width=16,
        height=16,
        aligment='center')
    cell_modem_view = Instance(CellModemView)
    view = View(
        VGroup(
            spring,
            HGroup(
                VGroup(
                    Item('msg_enum',
                         label="Messages to broadcast",
                         style='custom',
                         enabled_when='not running'),
                    Item('ip_ad',
                         label='IP Address',
                         enabled_when='not running'),
                    Item('port', label="Port", enabled_when='not running'),
                    HGroup(
                        spring,
                        UItem('start',
                              enabled_when='not running',
                              show_label=False),
                        UItem('stop', enabled_when='running',
                              show_label=False), spring)),
                VGroup(
                    Item('information',
                         label="Notes",
                         height=10,
                         editor=MultilineTextEditor(
                             TextEditor(multi_line=True)),
                         style='readonly',
                         show_label=False,
                         resizable=True,
                         padding=15),
                    spring,
                )), spring,
            HGroup(
                Item('cell_modem_view', style='custom', show_label=False),
                VGroup(Item(
                    '_network_info',
                    style='readonly',
                    editor=TabularEditor(adapter=SimpleNetworkAdapter()),
                    show_label=False,
                ),
                       Item('network_refresh_button',
                            show_label=False,
                            width=0.50),
                       show_border=True,
                       label="Network"),
            )))

    def _network_callback(self, m, **metadata):
        txstr = sizeof_fmt(m.tx_bytes),
        rxstr = sizeof_fmt(m.rx_bytes)
        if m.interface_name.startswith(
                b'ppp0'):  # Hack for ppp tx and rx which doesn't work
            txstr = "---"
            rxstr = "---"
        elif m.interface_name.startswith(b'lo') or m.interface_name.startswith(
                b'sit0'):
            return
        table_row = ((m.interface_name.decode('ascii'),
                      ip_bytes_to_string(m.ipv4_address),
                      ((m.flags & (1 << 6)) != 0), txstr, rxstr))
        exists = False
        for i, each in enumerate(self._network_info):
            if each[0][0] == table_row[0][0]:
                self._network_info[i] = table_row
                exists = True
        if not exists:
            self._network_info.append(table_row)

    def __init__(self, link):
        """
        Traits tab with UI for UDP broadcast of SBP.

        Parameters
        ----------
        link : sbp.client.handler.Handler
          Link for SBP transfer to/from Piksi.
        device_uid : str
          Piksi Device UUID (defaults to None)
        whitelist : [int] | None
          Piksi Device UUID (defaults to None)

        """
        self.link = link
        # Whitelist used for UDP broadcast view
        self.cell_modem_view = CellModemView(link)
        self.msgs = OBS_MSGS
        # register a callback when the msg_enum trait changes
        self.on_trait_change(self.update_msgs, 'msg_enum')
        self.python_console_cmds = {'update': self}
        self.cellmodem_interface_name = "ppp0"
        self.link.add_callback(self._network_callback,
                               SBP_MSG_NETWORK_STATE_RESP)

    def update_msgs(self):
        """Updates the instance variable msgs which store the msgs that we
        will send over UDP.

        """
        if self.msg_enum == 'Observations':
            self.msgs = OBS_MSGS
        elif self.msg_enum == 'All':
            self.msgs = [None]
        else:
            raise NotImplementedError

    def _prompt_setting_error(self, text):
        """Nonblocking prompt for a device setting error.

        Parameters
        ----------
        text : str
          Helpful error message for the user

        """
        prompt = CallbackPrompt(title="Setting Error", actions=[close_button])
        prompt.text = text
        prompt.run(block=False)

    def update_network_state(self):
        self._network_refresh_button_fired()

    def _network_refresh_button_fired(self):
        self._network_info = []
        self.link(MsgNetworkStateReq())

    def _start_fired(self):
        """Handle start udp broadcast button. Registers callbacks on
        self.link for each of the self.msgs If self.msgs is None, it
        registers one generic callback for all messages.

        """
        self.running = True
        try:
            self.func = UdpLogger(self.ip_ad, self.port)
            self.link.add_callback(self.func, self.msgs)
        except:  # noqa
            import traceback
            print(traceback.format_exc())

    def _stop_fired(self):
        """Handle the stop udp broadcast button. It uses the self.funcs and
        self.msgs to remove the callbacks that were registered when the
        start button was pressed.

        """
        try:
            self.link.remove_callback(self.func, self.msgs)
            self.func.__exit__()
            self.func = None
            self.running = False
        except:  # noqa
            import traceback
            print(traceback.format_exc())