Ejemplo n.º 1
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(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):
        # do all required plot_data updates for a single
        # new solution with mode defined by mode_string
        pending_update = {
            'lat_' + mode_string: self.slns['lat_' + mode_string],
            'lng_' + mode_string: self.slns['lng_' + mode_string]
        }
        current = {}
        if len(self.slns['lat_' + mode_string]) != 0:
            current = {
                'cur_lat_' + mode_string:
                [self.slns['lat_' + mode_string][-1]],
                'cur_lng_' + mode_string:
                [self.slns['lng_' + mode_string][-1]]
            }
        pending_update.update(self._get_update_current(current))
        self.plot_data.update_data(pending_update)

    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.slns['lat_' + mode_string].append(lat)
        self.slns['lng_' + mode_string].append(lng)
        self.scaling_lock.release()

    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.latitude_list) != 0:
            self.latitude = sum(self.lats) / len(self.lats)
            self.altitude = sum(self.alts) / len(self.alts)
            self.longitude = sum(self.lngs) / len(self.lng)

    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)
        # this list allows us to tell GUI thread which solutions to update
        # (if we decide not to update at full data rate)
        # we move into using short strings for each solution mode instead of
        # numbers for developer friendliness
        if self.last_pos_mode != 0:
            mode_string = mode_string_dict[self.last_pos_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()
        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))

        # 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 time.time() - self.last_plot_update_time > GUI_UPDATE_PERIOD:
            GUI.invoke_later(self._solution_draw)

    def _display_units_changed(self):
        self.recenter = True  # recenter flag tells _solution_draw to update view extents
        # we store current extents of plot and current scalefactlrs
        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])
        self.scaling_lock.acquire()
        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()
        # 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' 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)

    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.last_plot_update_time = time.time()
        self.list_lock.acquire()
        # update our "current solution" icon
        for mode_string in list(self.pending_draw_modes):
            self._synchronize_plot_data_by_mode(mode_string)
            self.pending_draw_modes.remove(mode_string)

        self.list_lock.release()
        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(
                (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))

    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.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_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,
        }
Ejemplo n.º 2
0
class SettingsView(HasTraits):
    """Traits-defined console settings view.

    link : object
      Serial driver object.
    read_finished_functions : list
      Callbacks to call on finishing a settings read.
    name_of_yaml_file : str
      Settings to read from (defaults to settings.yaml)
    expert : bool
      Show expert settings (defaults to False)
    gui_mode : bool
      ??? (defaults to True)
    skip : bool
      Skip reading of the settings (defaults to False). Intended for
      use when reading from network connections.
    """
    show_auto_survey = Bool(False)
    settings_yaml = list()
    auto_survey = SVGButton(
        label='Auto Survey',
        tooltip='Auto populate surveyed lat, lon and alt fields',
        filename='',
        width=16,
        height=20)
    settings_read_button = SVGButton(
        label='Reload',
        tooltip='Reload settings from Piksi',
        filename=os.path.join(determine_path(), 'images', 'fontawesome',
                              'refresh.svg'),
        width=16,
        height=20)
    settings_save_button = SVGButton(
        label='Save to Flash',
        tooltip='Save settings to Flash',
        filename=os.path.join(determine_path(), 'images', 'fontawesome',
                              'download.svg'),
        width=16,
        height=20)
    factory_default_button = SVGButton(
        label='Reset to Defaults',
        tooltip='Reset to Factory Defaults',
        filename=os.path.join(determine_path(), 'images', 'fontawesome',
                              'exclamation-triangle.svg'),
        width=16,
        height=20)
    settings_list = List(SettingBase)
    expert = Bool()
    selected_setting = Instance(SettingBase)
    traits_view = View(
        HSplit(
            Item(
                'settings_list',
                editor=TabularEditor(
                    adapter=SimpleAdapter(),
                    editable_labels=False,
                    auto_update=True,
                    editable=False,
                    selected='selected_setting'),
                show_label=False, ),
            VGroup(
                HGroup(
                    Item('settings_read_button', show_label=False),
                    Item('settings_save_button', show_label=False),
                    Item('factory_default_button', show_label=False),
                    Item(
                        'auto_survey',
                        show_label=False,
                        visible_when='show_auto_survey'), ),
                HGroup(
                    Item(
                        'expert',
                        label="Show Advanced Settings",
                        show_label=True)),
                Item('selected_setting', style='custom', show_label=False), ),
        ))

    def _selected_setting_changed(self):
        if self.selected_setting:
            if (self.selected_setting.name in [
                    'surveyed_position', 'broadcast', 'surveyed_lat',
                    'surveyed_lon', 'surveyed_alt'
            ] and self.lat != 0 and self.lon != 0):
                self.show_auto_survey = True
            else:
                self.show_auto_survey = False

    def _expert_changed(self, info):
        try:
            self.settings_display_setup(do_read_finished=False)
        except AttributeError:
            pass

    def _settings_read_button_fired(self):
        self.enumindex = 0
        self.ordering_counter = 0
        self.link(MsgSettingsReadByIndexReq(index=self.enumindex))

    def _settings_save_button_fired(self):
        self.link(MsgSettingsSave())

    def _factory_default_button_fired(self):
        confirm_prompt = prompt.CallbackPrompt(
            title="Reset to Factory Defaults?",
            actions=[prompt.close_button, prompt.reset_button],
            callback=self.reset_factory_defaults)
        confirm_prompt.text = "This will erase all settings and then reset the device.\n" \
            + "Are you sure you want to reset to factory defaults?"
        confirm_prompt.run(block=False)

    def reset_factory_defaults(self):
        # Reset the Piksi, with flag set to restore default settings
        self.link(MsgReset(flags=1))

    def _auto_survey_fired(self):
        confirm_prompt = prompt.CallbackPrompt(
            title="Auto populate surveyed position?",
            actions=[prompt.close_button, prompt.auto_survey_button],
            callback=self.auto_survey_fn)
        confirm_prompt.text = "\n" \
            + "This will set the Surveyed Position section to the \n" \
            + "mean position of the last 1000 position solutions.\n \n" \
            + "The fields that will be auto-populated are: \n" \
            + "Surveyed Lat \n" \
            + "Surveyed Lon \n" \
            + "Surveyed Alt \n \n" \
            + "The surveyed position will be an approximate value. \n" \
            + "This may affect the relative accuracy of Piksi. \n \n" \
            + "Are you sure you want to auto-populate the Surveyed Position section?"
        confirm_prompt.run(block=False)

    def auto_survey_fn(self):
        lat_value = str(self.lat)
        lon_value = str(self.lon)
        alt_value = str(self.alt)
        self.settings['surveyed_position']['surveyed_lat'].value = lat_value
        self.settings['surveyed_position']['surveyed_lon'].value = lon_value
        self.settings['surveyed_position']['surveyed_alt'].value = alt_value
        self.settings_display_setup(do_read_finished=False)

    # Callbacks for receiving messages
    def settings_display_setup(self, do_read_finished=True):
        self.settings_list = []
        sections = sorted(self.settings.keys())
        for sec in sections:
            this_section = []
            for name, setting in sorted(
                    self.settings[sec].iteritems(),
                    key=lambda n_s: n_s[1].ordering):
                if not setting.expert or (self.expert and setting.expert):
                    this_section.append(setting)
            if this_section:
                self.settings_list.append(SectionHeading(sec))
                self.settings_list += this_section
        # call read_finished_functions as needed
        if do_read_finished:
            for cb in self.read_finished_functions:
                if self.gui_mode:
                    GUI.invoke_later(cb)
                else:
                    cb()

    def settings_read_by_index_done_callback(self, sbp_msg, **metadata):
        self.settings_display_setup()

    def settings_read_resp_callback(self, sbp_msg, **metadata):
        confirmed_set = True
        settings_list = sbp_msg.setting.split("\0")
        if len(settings_list) <= 3:
            print("Received malformed settings read response {0}".format(
                sbp_msg))
            confirmed_set = False
        try:
            if self.settings[settings_list[0]][settings_list[1]].value != settings_list[2]:
                try:
                    float_val = float(self.settings[settings_list[0]][
                        settings_list[1]].value)
                    float_val2 = float(settings_list[2])
                    if abs(float_val - float_val2) > 0.000001:
                        confirmed_set = False
                except ValueError:
                    confirmed_set = False
            if not confirmed_set:
                pass
                # We pass if the new value doesn't match current console value.  It would be nice to update it, but that may cause side effects.
            self.settings[settings_list[0]][settings_list[
                1]].confirmed_set = confirmed_set
        except KeyError:
            return

    def settings_read_by_index_callback(self, sbp_msg, **metadata):
        section, setting, value, format_type = sbp_msg.payload[2:].split(
            '\0')[:4]
        self.ordering_counter += 1
        if format_type == '':
            format_type = None
        else:
            setting_type, setting_format = format_type.split(':')
        if section not in self.settings:
            self.settings[section] = {}
        if format_type is None:
            # Plain old setting, no format information
            self.settings[section][setting] = Setting(
                setting,
                section,
                value,
                ordering=self.ordering_counter,
                settings=self)
        else:
            if setting_type == 'enum':
                enum_values = setting_format.split(',')
                self.settings[section][setting] = EnumSetting(
                    setting,
                    section,
                    value,
                    ordering=self.ordering_counter,
                    values=enum_values,
                    settings=self)
            else:
                # Unknown type, just treat is as a string
                self.settings[section][setting] = Setting(
                    setting, section, value, settings=self)
        if self.enumindex == sbp_msg.index:
            self.enumindex += 1
            self.link(MsgSettingsReadByIndexReq(index=self.enumindex))

    def piksi_startup_callback(self, sbp_msg, **metadata):
        self.settings.clear()
        self._settings_read_button_fired()

    def set(self, section, name, value):
        self.link(
            MsgSettingsWrite(setting='%s\0%s\0%s\0' % (section, name, value)))

    def cleanup(self):
        """ Remove callbacks from serial link. """
        self.link.remove_callback(self.piksi_startup_callback, SBP_MSG_STARTUP)
        self.link.remove_callback(self.settings_read_by_index_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_REQ)
        self.link.remove_callback(self.settings_read_by_index_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_RESP)
        self.link.remove_callback(self.settings_read_by_index_done_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_DONE)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.cleanup()

    def __init__(self,
                 link,
                 read_finished_functions=[],
                 name_of_yaml_file="settings.yaml",
                 expert=False,
                 gui_mode=True,
                 skip=False):
        super(SettingsView, self).__init__()
        self.expert = expert
        self.show_auto_survey = False
        self.gui_mode = gui_mode
        self.enumindex = 0
        self.settings = {}
        self.link = link
        self.link.add_callback(self.piksi_startup_callback, SBP_MSG_STARTUP)
        self.link.add_callback(self.settings_read_by_index_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_REQ)
        self.link.add_callback(self.settings_read_by_index_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_RESP)
        self.link.add_callback(self.settings_read_by_index_done_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_DONE)
        self.link.add_callback(self.settings_read_resp_callback,
                               SBP_MSG_SETTINGS_READ_RESP)
        # Read in yaml file for setting metadata
        self.settings_yaml = SettingsList(name_of_yaml_file)
        # List of functions to be executed after all settings are read.
        # No support for arguments currently.
        self.read_finished_functions = read_finished_functions
        self.setting_detail = SettingBase()
        if not skip:
            try:
                self._settings_read_button_fired()
            except IOError:
                print(
                    "IOError in settings_view startup call of _settings_read_button_fired."
                )
                print("Verify that write permissions exist on the port.")
        self.python_console_cmds = {'settings': self}
Ejemplo n.º 3
0
class BaselineView(HasTraits):
    python_console_cmds = Dict()

    ns = List()
    es = List()
    ds = List()

    table = List()

    plot = Instance(Plot)
    plot_data = Instance(ArrayPlotData)

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

    clear_button = SVGButton(label='',
                             tooltip='Clear',
                             filename=os.path.join(os.path.dirname(__file__),
                                                   'images', 'iconic',
                                                   'x.svg'),
                             width=16,
                             height=16)
    zoomall_button = SVGButton(label='',
                               tooltip='Zoom All',
                               filename=os.path.join(os.path.dirname(__file__),
                                                     'images', 'iconic',
                                                     'fullscreen.svg'),
                               width=16,
                               height=16)
    center_button = SVGButton(label='',
                              tooltip='Center on Baseline',
                              toggle=True,
                              filename=os.path.join(os.path.dirname(__file__),
                                                    'images', 'iconic',
                                                    'target.svg'),
                              width=16,
                              height=16)
    paused_button = SVGButton(
        label='',
        tooltip='Pause',
        toggle_tooltip='Run',
        toggle=True,
        filename=os.path.join(os.path.dirname(__file__), 'images', 'iconic',
                              'pause.svg'),
        toggle_filename=os.path.join(os.path.dirname(__file__), 'images',
                                     'iconic', 'play.svg'),
        width=16,
        height=16)

    reset_button = Button(label='Reset Filters')
    reset_iar_button = Button(label='Reset IAR')
    init_base_button = Button(label='Init. with known baseline')

    traits_view = View(
        HSplit(
            Item('table',
                 style='readonly',
                 editor=TabularEditor(adapter=SimpleAdapter()),
                 show_label=False,
                 width=0.3),
            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('reset_button', show_label=False),
                    Item('reset_iar_button', show_label=False),
                    Item('init_base_button', show_label=False),
                ),
                Item(
                    'plot',
                    show_label=False,
                    editor=ComponentEditor(bgcolor=(0.8, 0.8, 0.8)),
                ))))

    def _zoomall_button_fired(self):
        self.plot.index_range.low_setting = 'auto'
        self.plot.index_range.high_setting = 'auto'
        self.plot.value_range.low_setting = 'auto'
        self.plot.value_range.high_setting = 'auto'

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

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

    def _reset_button_fired(self):
        self.link.send_message(sbp_messages.RESET_FILTERS, '\x00')

    def _reset_iar_button_fired(self):
        self.link.send_message(sbp_messages.RESET_FILTERS, '\x01')

    def _init_base_button_fired(self):
        self.link.send_message(sbp_messages.INIT_BASE, '')

    def _clear_button_fired(self):
        self.ns = []
        self.es = []
        self.ds = []
        self.plot_data.set_data('n', [])
        self.plot_data.set_data('e', [])
        self.plot_data.set_data('d', [])
        self.plot_data.set_data('t', [])

    def _baseline_callback_ecef(self, data):
        #Don't do anything for ECEF currently
        return

    def iar_state_callback(self, data):
        self.num_hyps = struct.unpack('<I', data)[0]

    def _baseline_callback_ned(self, data):
        # Updating an ArrayPlotData isn't thread safe (see chaco issue #9), so
        # actually perform the update in the UI thread.
        if self.running:
            GUI.invoke_later(self.baseline_callback, data)

    def update_table(self):
        self._table_list = self.table.items()

    def gps_time_callback(self, data):
        self.week = sbp_messages.GPSTime(data).wn
        self.nsec = sbp_messages.GPSTime(data).ns

    def baseline_callback(self, data):
        soln = sbp_messages.BaselineNED(data)
        table = []

        soln.n = soln.n * 1e-3
        soln.e = soln.e * 1e-3
        soln.d = soln.d * 1e-3

        dist = np.sqrt(soln.n**2 + soln.e**2 + soln.d**2)

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

        if self.week is not None:
            t = datetime.datetime(1980, 1, 6) + \
                datetime.timedelta(weeks=self.week) + \
                datetime.timedelta(seconds=tow)

            table.append(('GPS Time', t))
            table.append(('GPS Week', str(self.week)))

            if self.log_file is None:
                self.log_file = open(
                    time.strftime("baseline_log_%Y%m%d-%H%M%S.csv"), 'w')

            self.log_file.write('%s,%.4f,%.4f,%.4f,%.4f,%d,0x%02x,%d\n' %
                                (str(t), soln.n, soln.e, soln.d, dist,
                                 soln.n_sats, soln.flags, self.num_hyps))
            self.log_file.flush()

        table.append(('GPS ToW', tow))

        table.append(('N', soln.n))
        table.append(('E', soln.e))
        table.append(('D', soln.d))
        table.append(('Dist.', dist))
        table.append(('Num. Sats.', soln.n_sats))
        table.append(('Flags', '0x%02x' % soln.flags))
        if soln.flags & 1:
            table.append(('Mode', 'Fixed RTK'))
        else:
            table.append(('Mode', 'Float'))
        table.append(('IAR Num. Hyps.', self.num_hyps))

        self.ns.append(soln.n)
        self.es.append(soln.e)
        self.ds.append(soln.d)

        self.ns = self.ns[-1000:]
        self.es = self.es[-1000:]
        self.ds = self.ds[-1000:]

        self.plot_data.set_data('n', self.ns)
        self.plot_data.set_data('e', self.es)
        self.plot_data.set_data('d', self.ds)
        self.plot_data.set_data('ref_n', [0.0, soln.n])
        self.plot_data.set_data('ref_e', [0.0, soln.e])
        self.plot_data.set_data('ref_d', [0.0, soln.d])
        t = range(len(self.ns))
        self.plot_data.set_data('t', t)

        if self.position_centered:
            d = (self.plot.index_range.high - self.plot.index_range.low) / 2.
            self.plot.index_range.set_bounds(soln.n - d, soln.n + d)
            d = (self.plot.value_range.high - self.plot.value_range.low) / 2.
            self.plot.value_range.set_bounds(soln.e - d, soln.e + d)

        self.table = table

    def __init__(self, link):
        super(BaselineView, self).__init__()

        self.log_file = None

        self.num_hyps = 0

        self.plot_data = ArrayPlotData(n=[0.0],
                                       e=[0.0],
                                       d=[0.0],
                                       t=[0.0],
                                       ref_n=[0.0],
                                       ref_e=[0.0],
                                       ref_d=[0.0])
        self.plot = Plot(self.plot_data)

        self.plot.plot(('e', 'n'),
                       type='line',
                       name='line',
                       color=(0, 0, 0, 0.1))
        self.plot.plot(('e', 'n'),
                       type='scatter',
                       name='points',
                       color='blue',
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('ref_e', 'ref_n'),
                       type='scatter',
                       color='red',
                       marker='plus',
                       marker_size=5,
                       line_width=1.5)

        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.value_axis.tick_label_position = 'inside'
        self.plot.value_axis.tick_label_color = 'gray'
        self.plot.value_axis.tick_color = 'gray'
        self.plot.padding = (0, 1, 0, 1)

        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.week = None
        self.nsec = 0

        self.link = link
        self.link.add_callback(sbp_messages.SBP_BASELINE_NED,
                               self._baseline_callback_ned)
        self.link.add_callback(sbp_messages.SBP_BASELINE_ECEF,
                               self._baseline_callback_ecef)
        self.link.add_callback(sbp_messages.IAR_STATE, self.iar_state_callback)
        self.link.add_callback(sbp_messages.SBP_GPS_TIME,
                               self.gps_time_callback)

        self.python_console_cmds = {'baseline': self}
Ejemplo n.º 4
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())
Ejemplo n.º 5
0
class PCBaseControl(NoPlotControl):
    eq_axis = Bool(False)
    # vis_toggle = Button('Visibility')
    y_down = SVGButton(filename=pjoin(img_path, 'y_down.svg'),
                       width=32,
                       height=32)
    y_up = SVGButton(filename=pjoin(img_path, 'y_up.svg'), width=32, height=32)
    x_down = SVGButton(filename=pjoin(img_path, 'x_down.svg'),
                       width=32,
                       height=32)
    x_up = SVGButton(filename=pjoin(img_path, 'x_up.svg'), width=32, height=32)
    reset_xy = SVGButton(filename=pjoin(img_path, 'reset_xy.svg'),
                         width=32,
                         height=32)
    subset_groups = List()

    traits_view = View(
        Group(Item('model',
                   editor=ComponentEditor(bgcolor=bg_color),
                   show_label=False),
              Label('Scroll to zoom and drag to pan in plot.'),
              Include('plot_controllers'),
              orientation="vertical"))

    # @on_trait_change('vis_toggle')
    # def switch_visibility(self, obj, name, new):
    #     obj.model.show_points()

    @on_trait_change('eq_axis')
    def switch_axis(self, obj, name, new):
        obj.model.toggle_eq_axis(new)

    @on_trait_change('reset_xy')
    def pc_axis_reset(self, obj, name, new):
        obj.model.set_x_y_pc(1, 2)

    @on_trait_change('x_up')
    def pc_axis_x_up(self, obj, name, new):
        x, y, n = obj.model.get_x_y_status()
        if x < n:
            x += 1
        else:
            x = 1
        obj.model.set_x_y_pc(x, y)

    @on_trait_change('x_down')
    def pc_axis_x_down(self, obj, name, new):
        x, y, n = obj.model.get_x_y_status()
        if x > 1:
            x -= 1
        else:
            x = n
        obj.model.set_x_y_pc(x, y)

    @on_trait_change('y_up')
    def pc_axis_y_up(self, obj, name, new):
        x, y, n = obj.model.get_x_y_status()
        if y < n:
            y += 1
        else:
            y = 1
        obj.model.set_x_y_pc(x, y)

    @on_trait_change('y_down')
    def pc_axis_y_down(self, obj, name, new):
        x, y, n = obj.model.get_x_y_status()
        if y > 1:
            y -= 1
        else:
            y = n
        obj.model.set_x_y_pc(x, y)
Ejemplo n.º 6
0
class BaselineView(HasTraits):

    # This mapping should match the flag definitions in libsbp for
    # the MsgBaselineNED message. While this isn't strictly necessary
    # it helps avoid confusion

    python_console_cmds = Dict()
    last_plot_update_time = Float()
    last_stale_update_time = Float()

    table = List()

    logging_b = Bool(False)
    directory_name_b = File

    plot = Instance(Plot)
    plot_data = Instance(ArrayPlotData)

    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 Baseline',
        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)

    reset_button = Button(label='Reset Filters')

    traits_view = View(
        HSplit(
            Item('table',
                 style='readonly',
                 editor=TabularEditor(adapter=SimpleAdapter()),
                 show_label=False,
                 width=0.3),
            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('reset_button', show_label=False),
                ),
                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_button_fired(self):
        self.link(MsgResetFilters(filter=0))

    def _get_update_current(self, current_dict={}):
        out_dict = {
            'cur_n_fixed': [],
            'cur_e_fixed': [],
            'cur_d_fixed': [],
            'cur_n_float': [],
            'cur_e_float': [],
            'cur_d_float': [],
            'cur_n_dgnss': [],
            'cur_e_dgnss': [],
            'cur_d_dgnss': []
        }
        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 = {
            'n_' + mode_string:
            [n for n in self.slns['n_' + mode_string] if not np.isnan(n)],
            'e_' + mode_string:
            [e for e in self.slns['e_' + mode_string] if not np.isnan(e)]
        }
        if update_current:
            current = {}
            if len(pending_update['n_' + mode_string]) != 0:
                current = {
                    'cur_n_' + mode_string:
                    [pending_update['n_' + mode_string][-1]],
                    'cur_e_' + mode_string:
                    [pending_update['e_' + mode_string][-1]]
                }
            else:
                current = {
                    'cur_n_' + mode_string: [],
                    'cur_e_' + 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['n_' + each_mode].append(np.nan)
                self.slns['e_' + 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.slns['n_' + mode_string].append(soln.n)
        self.slns['e_' + mode_string].append(soln.e)
        # 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 _reset_remove_current(self):
        self.plot_data.update_data(self._get_update_current())

    def _clear_history(self):
        self._clr_sln_data()
        pending_update = {
            'n_fixed': [],
            'e_fixed': [],
            'd_fixed': [],
            'n_float': [],
            'e_float': [],
            'd_float': [],
            'n_dgnss': [],
            'e_dgnss': [],
            'd_dgnss': []
        }
        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 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 & 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 baseline_heading_callback(self, sbp_msg, **metadata):
        headingMsg = MsgBaselineHeading(sbp_msg)
        if headingMsg.flags & 0x7 != 0:
            self.heading = headingMsg.heading * 1e-3
        else:
            self.heading = "---"

    def baseline_callback(self, sbp_msg, **metadata):
        soln = MsgBaselineNEDDepA(sbp_msg)
        table = []

        soln.n = soln.n * 1e-3
        soln.e = soln.e * 1e-3
        soln.d = soln.d * 1e-3
        soln.h_accuracy = soln.h_accuracy * 1e-3
        soln.v_accuracy = soln.v_accuracy * 1e-3

        dist = np.sqrt(soln.n**2 + soln.e**2 + soln.d**2)

        tow = soln.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.utc_time is not None:
            ((tutc, secutc)) = datetime_2_str(self.utc_time)

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

        if not self.logging_b:
            self.log_file = None

        if self.logging_b:
            if self.log_file is None:
                self.log_file = sopen(filepath, 'w')
                self.log_file.write(
                    'pc_time,gps_time,tow(sec),north(meters),east(meters),down(meters),h_accuracy(meters),v_accuracy(meters),'
                    'distance(meters),num_sats,flags,num_hypothesis\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,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%d,%d,%d\n' %
                ("{0}:{1:06.6f}".format(tloc, float(secloc)), log_str_gps, tow,
                 soln.n, soln.e, soln.d, soln.h_accuracy, soln.v_accuracy,
                 dist, soln.n_sats, soln.flags, self.num_hyps))
            self.log_file.flush()

        self.last_mode = get_mode(soln)

        if self.last_mode < 1:
            table.append(('GPS Week', EMPTY_STR))
            table.append(('GPS TOW', EMPTY_STR))
            table.append(('GPS Time', EMPTY_STR))
            table.append(('UTC Time', EMPTY_STR))
            table.append(('UTC Src', EMPTY_STR))
            table.append(('N', EMPTY_STR))
            table.append(('E', EMPTY_STR))
            table.append(('D', EMPTY_STR))
            table.append(('Horiz Acc', EMPTY_STR))
            table.append(('Vert Acc', EMPTY_STR))
            table.append(('Dist.', EMPTY_STR))
            table.append(('Sats Used', EMPTY_STR))
            table.append(('Flags', EMPTY_STR))
            table.append(('Mode', EMPTY_STR))
            table.append(('Heading', EMPTY_STR))
            table.append(('Corr. Age [s]', EMPTY_STR))
        else:
            self.last_btime_update = monotonic()
            if self.week is not None:
                table.append(('GPS Week', str(self.week)))
            table.append(('GPS TOW', "{:.3f}".format(tow)))

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

            table.append(('N', "{:.12g}".format(soln.n)))
            table.append(('E', "{:.12g}".format(soln.e)))
            table.append(('D', "{:.12g}".format(soln.d)))
            table.append(('Horiz Acc', "{:.12g}".format(soln.h_accuracy)))
            table.append(('Vert Acc', "{:.12g}".format(soln.v_accuracy)))
            table.append(('Dist.', "{0:.3f}".format(dist)))

            table.append(('Sats Used', soln.n_sats))

            table.append(('Flags', '0x%02x' % soln.flags))
            table.append(('Mode', mode_dict[self.last_mode]))
            if self.heading is not None:
                table.append(('Heading', self.heading))
            if self.age_corrections is not None:
                table.append(('Corr. Age [s]', self.age_corrections))
            else:
                table.append(('Corr. Age [s]', EMPTY_STR))
        self.table = table

        if self.last_mode != 0:
            self.last_soln = soln
            mode_string = mode_string_dict[self.last_mode]
            if mode_string not in self.pending_draw_modes:
                # if we don't already have a pending upate for that 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(soln)
            self.list_lock.release()

        if monotonic() - self.last_plot_update_time > GUI_UPDATE_PERIOD:
            self.update_scheduler.schedule_update('_solution_draw',
                                                  self._solution_draw)

    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)
                if mode_string in self.pending_draw_modes:
                    self.pending_draw_modes.remove(mode_string)

        self.list_lock.release()
        # make the zoomall win over the position centered button
        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.e - d,
                                             self.last_soln.e + d)
            d = (self.plot.value_range.high - self.plot.value_range.low) / 2.
            self.plot.value_range.set_bounds(self.last_soln.n - d,
                                             self.last_soln.n + d)

        if self.zoomall:
            plot_square_axes(self.plot, ('e_fixed', 'e_float', 'e_dgnss'),
                             ('n_fixed', 'n_float', 'n_dgnss'))

    def __init__(self, link, plot_history_max=1000, dirname=''):
        super(BaselineView, self).__init__()
        self.pending_draw_modes = []
        self.log_file = None
        self.directory_name_b = dirname
        self.num_hyps = 0
        self.last_hyp_update = 0
        self.last_btime_update = 0
        self.last_soln = None
        self.last_mode = 0
        self.last_plot_update_time = 0
        self.last_stale_update_time = 0
        self.slns = {
            'n_fixed': deque(maxlen=PLOT_HISTORY_MAX),
            'e_fixed': deque(maxlen=PLOT_HISTORY_MAX),
            'd_fixed': deque(maxlen=PLOT_HISTORY_MAX),
            'n_float': deque(maxlen=PLOT_HISTORY_MAX),
            'e_float': deque(maxlen=PLOT_HISTORY_MAX),
            'd_float': deque(maxlen=PLOT_HISTORY_MAX),
            'n_dgnss': deque(maxlen=PLOT_HISTORY_MAX),
            'e_dgnss': deque(maxlen=PLOT_HISTORY_MAX),
            'd_dgnss': deque(maxlen=PLOT_HISTORY_MAX)
        }
        self.plot_data = ArrayPlotData(n_fixed=[],
                                       e_fixed=[],
                                       n_float=[],
                                       e_float=[],
                                       n_dgnss=[],
                                       e_dgnss=[],
                                       t=[0.0],
                                       ref_n=[0.0],
                                       ref_e=[0.0],
                                       cur_e_fixed=[],
                                       cur_n_fixed=[],
                                       cur_e_float=[],
                                       cur_n_float=[],
                                       cur_e_dgnss=[],
                                       cur_n_dgnss=[])

        self.list_lock = threading.Lock()
        self.plot = Plot(self.plot_data)
        pts_float = self.plot.plot(  # noqa: F841
            ('e_float', 'n_float'),
            type='scatter',
            color=color_dict[FLOAT_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        pts_fixed = self.plot.plot(  # noqa: F841
            ('e_fixed', 'n_fixed'),
            type='scatter',
            color=color_dict[FIXED_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        pts_dgnss = self.plot.plot(  # noqa: F841
            ('e_dgnss', 'n_dgnss'),
            type='scatter',
            color=color_dict[DGNSS_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        ref = self.plot.plot(('ref_e', 'ref_n'),
                             type='scatter',
                             color='red',
                             marker='plus',
                             marker_size=5,
                             line_width=1.5)
        cur_fixed = self.plot.plot(('cur_e_fixed', 'cur_n_fixed'),
                                   type='scatter',
                                   color=color_dict[FIXED_MODE],
                                   marker='plus',
                                   marker_size=5,
                                   line_width=1.5)
        cur_float = self.plot.plot(('cur_e_float', 'cur_n_float'),
                                   type='scatter',
                                   color=color_dict[FLOAT_MODE],
                                   marker='plus',
                                   marker_size=5,
                                   line_width=1.5)
        cur_dgnss = self.plot.plot(('cur_e_dgnss', 'cur_n_dgnss'),
                                   type='scatter',
                                   color=color_dict[DGNSS_MODE],
                                   marker='plus',
                                   line_width=1.5,
                                   marker_size=5)
        plot_labels = [' Base Position', 'DGPS', 'RTK Float', 'RTK Fixed']
        plots_legend = dict(
            zip(plot_labels, [ref, cur_dgnss, cur_float, cur_fixed]))
        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 = 'E (meters)'
        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 = 'N (meters)'
        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.week = None
        self.utc_time = None
        self.age_corrections = None
        self.heading = "---"
        self.nsec = 0

        self.link = link
        self.link.add_callback(
            self.baseline_callback,
            [SBP_MSG_BASELINE_NED, SBP_MSG_BASELINE_NED_DEP_A])
        self.link.add_callback(self.baseline_heading_callback,
                               [SBP_MSG_BASELINE_HEADING])
        self.link.add_callback(self.gps_time_callback,
                               [SBP_MSG_GPS_TIME, SBP_MSG_GPS_TIME_DEP_A])
        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.python_console_cmds = {'baseline': self}
        self.update_scheduler = UpdateScheduler()
Ejemplo n.º 7
0
class SolutionView(HasTraits):
    python_console_cmds = Dict()
    # we need to doubleup on Lists to store the psuedo absolutes separately
    # without rewriting everything
    lats = List()
    lngs = List()
    alts = List()
    """
  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
  """

    logging_v = Bool(False)
    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_spp = List()
    table_psuedo_abs = List()
    dops_table = List()
    pos_table_spp = 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 psuedo-absolute 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=os.path.join(determine_path(), 'images',
                                                   'iconic', 'x.svg'),
                             width=16,
                             height=16)
    zoomall_button = SVGButton(label='',
                               tooltip='Zoom All',
                               toggle=True,
                               filename=os.path.join(determine_path(),
                                                     'images', 'iconic',
                                                     'fullscreen.svg'),
                               width=16,
                               height=16)
    center_button = SVGButton(label='',
                              tooltip='Center on Solution',
                              toggle=True,
                              filename=os.path.join(determine_path(), 'images',
                                                    'iconic', 'target.svg'),
                              width=16,
                              height=16)
    paused_button = SVGButton(label='',
                              tooltip='Pause',
                              toggle_tooltip='Run',
                              toggle=True,
                              filename=os.path.join(determine_path(), 'images',
                                                    'iconic', 'pause.svg'),
                              toggle_filename=os.path.join(
                                  determine_path(), 'images', 'iconic',
                                  'play.svg'),
                              width=16,
                              height=16)

    traits_view = View(
        HSplit(
            Tabbed(
                VGroup(Item('',
                            label='Single Point Position (SPP)',
                            emphasized=True),
                       Item('table_spp',
                            style='readonly',
                            editor=TabularEditor(adapter=SimpleAdapter()),
                            show_label=False,
                            width=0.3),
                       label='Single Point Position'),
                VGroup(Item('', label='RTK Position', emphasized=True),
                       Item('table_psuedo_abs',
                            style='readonly',
                            editor=TabularEditor(adapter=SimpleAdapter()),
                            show_label=False,
                            width=0.3,
                            height=0.9),
                       Item('rtk_pos_note',
                            show_label=False,
                            resizable=True,
                            editor=MultilineTextEditor(
                                TextEditor(multi_line=True)),
                            style='readonly',
                            width=0.3,
                            height=-40),
                       label='RTK Position')),
            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('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 _clear_button_fired(self):
        self.lats = []
        self.lngs = []
        self.alts = []
        self.lats_psuedo_abs = []
        self.lngs_psuedo_abs = []
        self.alts_psuedo_abs = []
        self.plot_data.set_data('lat', [])
        self.plot_data.set_data('lng', [])
        self.plot_data.set_data('alt', [])
        self.plot_data.set_data('t', [])
        self.plot_data.set_data('lat_ps', [])
        self.plot_data.set_data('lng_ps', [])
        self.plot_data.set_data('alt_ps', [])
        self.plot_data.set_data('t_ps', [])

    def _pos_llh_callback(self, sbp_msg, **metadata):
        # Updating an ArrayPlotData isn't thread safe (see chaco issue #9), so
        # actually perform the update in the UI thread.
        if self.running:
            GUI.invoke_later(self.pos_llh_callback, sbp_msg)

    def mode_string(self, msg):
        if msg:
            if (msg.flags & 0xff) == 0:
                return 'SPP (single point position)'
            elif (msg.flags & 0xff) == 1:
                return 'Fixed RTK'
            elif (msg.flags & 0xff) == 2:
                return 'Float RTK'
        return 'None'

    def update_table(self):
        self._table_list = self.table_spp.items()

    def auto_survey(self):
        if self.counter < 1000:
            self.counter = self.counter + 1
        self.latitude_list.append(self.last_soln.lat)
        self.longitude_list.append(self.last_soln.lon)
        self.altitude_list.append(self.last_soln.height)
        self.latitude_list = self.latitude_list[-1000:]
        self.longitude_list = self.longitude_list[-1000:]
        self.altitude_list = self.altitude_list[-1000:]
        self.latitude = (sum(self.latitude_list)) / self.counter
        self.altitude = (sum(self.altitude_list)) / self.counter
        self.longitude = (sum(self.longitude_list)) / self.counter

    def pos_llh_callback(self, sbp_msg, **metadata):
        self.last_stime_update = time.time()
        soln = MsgPosLLH(sbp_msg)
        self.last_soln = soln
        masked_flag = soln.flags & 0x7
        if masked_flag == 0:
            psuedo_absolutes = False
        else:
            psuedo_absolutes = True
        pos_table = []

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

        if self.week is not None:
            t = datetime.datetime(1980, 1, 6) + \
                datetime.timedelta(weeks=self.week) + \
                datetime.timedelta(seconds=tow)
            pos_table.append(('GPS Time', t))
            pos_table.append(('GPS Week', str(self.week)))

            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 self.logging_p == False:
                self.log_file = None

            if self.logging_p:
                if self.log_file is None:
                    self.log_file = open(filepath_p, 'w')
                    self.log_file.write(
                        "time,latitude(degrees),longitude(degrees),altitude(meters),n_sats,flags\n"
                    )

                self.log_file.write('%s,%.10f,%.10f,%.4f,%d,%d\n' %
                                    (str(t), soln.lat, soln.lon, soln.height,
                                     soln.n_sats, soln.flags))
                self.log_file.flush()

        pos_table.append(('GPS ToW', tow))

        pos_table.append(('Num. sats', soln.n_sats))

        pos_table.append(('Lat', soln.lat))
        pos_table.append(('Lng', soln.lon))
        pos_table.append(('Alt', soln.height))
        pos_table.append(('Flags', '0x%02x' % soln.flags))

        pos_table.append(('Mode', self.mode_string(soln)))

        self.auto_survey()

        if psuedo_absolutes:
            # setup_plot variables
            self.lats_psuedo_abs.append(soln.lat)
            self.lngs_psuedo_abs.append(soln.lon)
            self.alts_psuedo_abs.append(soln.height)

            self.lats_psuedo_abs = self.lats_psuedo_abs[-1000:]
            self.lngs_psuedo_abs = self.lngs_psuedo_abs[-1000:]
            self.alts_psuedo_abs = self.alts_psuedo_abs[-1000:]

            self.plot_data.set_data('lat_ps', self.lats_psuedo_abs)
            self.plot_data.set_data('lng_ps', self.lngs_psuedo_abs)
            self.plot_data.set_data('alt_ps', self.alts_psuedo_abs)
            self.plot_data.set_data('cur_lat_ps', [soln.lat])
            self.plot_data.set_data('cur_lng_ps', [soln.lon])
            t_psuedo_abs = range(len(self.lats))
            if t is not None:
                self.plot_data.set_data('t', t)
            self.plot_data.set_data('t_ps', t_psuedo_abs)
            # set-up table variables
            self.table_psuedo_abs = pos_table

        else:
            # setup_plot variables
            self.lats.append(soln.lat)
            self.lngs.append(soln.lon)
            self.alts.append(soln.height)

            self.lats = self.lats[-1000:]
            self.lngs = self.lngs[-1000:]
            self.alts = self.alts[-1000:]

            self.plot_data.set_data('lat', self.lats)
            self.plot_data.set_data('lng', self.lngs)
            self.plot_data.set_data('alt', self.alts)
            self.plot_data.set_data('cur_lat', [soln.lat])
            self.plot_data.set_data('cur_lng', [soln.lon])
            t = range(len(self.lats))
            self.plot_data.set_data('t', t)

            # set-up table variables
            self.pos_table_spp = pos_table
            self.table_spp = self.pos_table_spp + self.vel_table + self.dops_table
            # TODO: figure out how to center the graph now that we have two separate messages
            # when we selectivtely send only SPP, the centering function won't work anymore
            if self.position_centered:
                d = (self.plot.index_range.high -
                     self.plot.index_range.low) / 2.
                self.plot.index_range.set_bounds(soln.lon - d, soln.lon + d)
                d = (self.plot.value_range.high -
                     self.plot.value_range.low) / 2.
                self.plot.value_range.set_bounds(soln.lat - d, soln.lat + d)
        if self.zoomall:
            plot_square_axes(self.plot, 'lng', 'lat')

    def dops_callback(self, sbp_msg, **metadata):
        dops = MsgDops(sbp_msg)
        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))]
        self.table_spp = self.pos_table_spp + self.vel_table + self.dops_table

    def vel_ned_callback(self, sbp_msg, **metadata):
        vel_ned = MsgVelNED(sbp_msg)

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

        if self.week is not None:
            t = datetime.datetime(1980, 1, 6) + \
                datetime.timedelta(weeks=self.week) + \
                datetime.timedelta(seconds=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 self.logging_v == False:
                self.vel_log_file = None

            if self.logging_v:

                if self.vel_log_file is None:
                    self.vel_log_file = open(filepath_v, 'w')
                    self.vel_log_file.write(
                        'time,north(m/s),east(m/s),down(m/s),speed(m/s),num_sats\n'
                    )

                self.vel_log_file.write(
                    '%s,%.6f,%.6f,%.6f,%.6f,%d\n' %
                    (str(t), 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, vel_ned.n_sats))
                self.vel_log_file.flush()

        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)),
        ]
        self.table_spp = self.pos_table_spp + self.vel_table + self.dops_table

    def gps_time_callback(self, sbp_msg, **metadata):
        self.week = MsgGPSTime(sbp_msg).wn
        self.nsec = MsgGPSTime(sbp_msg).ns

    def __init__(self, link, dirname=''):
        super(SolutionView, self).__init__()

        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.plot_data = ArrayPlotData(lat=[],
                                       lng=[],
                                       alt=[],
                                       t=[],
                                       cur_lat=[],
                                       cur_lng=[],
                                       cur_lat_ps=[],
                                       cur_lng_ps=[],
                                       lat_ps=[],
                                       lng_ps=[],
                                       alt_ps=[],
                                       t_ps=[])
        self.plot = Plot(self.plot_data)

        # 1000 point buffer
        self.plot.plot(('lng', 'lat'),
                       type='line',
                       name='',
                       color=(0, 0, 0.9, 0.1))
        self.plot.plot(('lng', 'lat'),
                       type='scatter',
                       name='',
                       color='blue',
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_ps', 'lat_ps'),
                       type='line',
                       name='',
                       color=(1, 0.4, 0, 0.1))
        self.plot.plot(('lng_ps', 'lat_ps'),
                       type='scatter',
                       name='',
                       color='orange',
                       marker='diamond',
                       line_width=0.0,
                       marker_size=1.0)
        # current values
        spp = self.plot.plot(('cur_lng', 'cur_lat'),
                             type='scatter',
                             name='SPP',
                             color='blue',
                             marker='plus',
                             line_width=1.5,
                             marker_size=5.0)
        rtk = self.plot.plot(('cur_lng_ps', 'cur_lat_ps'),
                             type='scatter',
                             name='RTK',
                             color='orange',
                             marker='plus',
                             line_width=1.5,
                             marker_size=5.0)
        plot_labels = ['SPP', 'RTK']
        plots_legend = dict(zip(plot_labels, [spp, rtk]))
        self.plot.legend.plots = plots_legend
        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)
        self.link.add_callback(self.vel_ned_callback, SBP_MSG_VEL_NED)
        self.link.add_callback(self.dops_callback, SBP_MSG_DOPS)
        self.link.add_callback(self.gps_time_callback, SBP_MSG_GPS_TIME)

        self.week = None
        self.nsec = 0

        self.python_console_cmds = {
            'solution': self,
        }
Ejemplo n.º 8
0
class ObservationView(HasTraits):
  python_console_cmds = Dict()

  _obs_table_list = List()
  obs = Dict()

  name = 'Rover'

  recording = Bool(False)

  record_button = SVGButton(
    label='Record', tooltip='Record Raw Observations',
    toggle_tooltip='Stop Recording', toggle=True,
    filename=os.path.join(os.path.dirname(__file__), 'images', 'fontawesome', 'floppy-o.svg'),
    toggle_filename=os.path.join(os.path.dirname(__file__), 'images', 'fontawesome', 'stop.svg'),
    width=16, height=16
  )

  def trait_view(self, view):
    return View(
      HGroup(
        Item('_obs_table_list', style = 'readonly', editor = TabularEditor(adapter=SimpleAdapter()), show_label=False),
        VGroup(
          Item('record_button', show_label=False),
        ),
        label = self.name,
        show_border = True
      )
    )

  def _record_button_fired(self):
    self.recording = not self.recording
    if not self.recording:
      if self.rinex_file is not None:
        self.rinex_file.close()
      self.rinex_file = None

  def update_obs(self):
    self._obs_table_list = [(prn + 1,) + obs for prn, obs in sorted(self.obs.items(), key=lambda x: x[0])]

  def obs_callback(self, data, sender=None):
    if (sender is not None and
        (self.relay ^ (sender == 0))):
      return

    if self.rinex_file is None and self.recording:
      self.rinex_file = open(self.name+self.t.strftime("-%Y%m%d-%H%M%S.obs"),  'w')
      header = """     2.11           OBSERVATION DATA    G (GPS)             RINEX VERSION / TYPE
pyNEX                                   %s UTC PGM / RUN BY / DATE 
                                                            MARKER NAME         
                                                            OBSERVER / AGENCY   
                                                            REC # / TYPE / VERS 
                                                            ANT # / TYPE        
   808673.9171 -4086658.5368  4115497.9775                  APPROX POSITION XYZ 
        0.0000        0.0000        0.0000                  ANTENNA: DELTA H/E/N
     1     0                                                WAVELENGTH FACT L1/2
     4    C1    L1    S1                                    # / TYPES OF OBSERV 
%s%13.7f     GPS         TIME OF FIRST OBS   
                                                            END OF HEADER       
""" % (
          datetime.datetime.utcnow().strftime("%Y%m%d %H%M%S"),
          self.t.strftime("  %Y    %m    %d    %H    %M"), self.t.second + self.t.microsecond * 1e-6,
      )
      self.rinex_file.write(header)
      self.rinex_file.flush()


    hdr_fmt = "<dH"
    hdr_size = struct.calcsize(hdr_fmt)
    tow, wn = struct.unpack("<dH", data[:hdr_size])
    self.gps_tow = tow
    self.gps_week = wn
    self.t = datetime.datetime(1980, 1, 5) + \
             datetime.timedelta(weeks=self.gps_week) + \
             datetime.timedelta(seconds=self.gps_tow)

    obs_fmt = '<ddfB'
    """
  double P;      /**< Pseudorange (m) */
  double L;      /**< Carrier-phase (cycles) */
  float snr;     /**< Signal-to-Noise ratio */
  u8 prn;        /**< Satellite number. */
    """

    obs_size = struct.calcsize(obs_fmt)
    self.n_obs = (len(data) - hdr_size) / obs_size
    obs_data = data[hdr_size:]

    self.obs = {}
    for i in range(self.n_obs):
      P, L, snr, prn = struct.unpack(obs_fmt, obs_data[:obs_size])
      obs_data = obs_data[obs_size:]
      self.obs[prn] = (P, L, snr)

    if self.recording:
        prns = list(self.obs.iterkeys())
        self.rinex_file.write("%s %10.7f  0 %2d" % (self.t.strftime(" %y %m %d %H %M"),
                                                    self.t.second + self.t.microsecond*1e-6,
                                                    len(prns)))
        while len(prns) > 0:
            prns_ = prns[:12]
            prns = prns[12:]
            for prn in prns_:
                self.rinex_file.write('G%2d' % (prn+1))
            self.rinex_file.write('   ' * (12 - len(prns_)))
            self.rinex_file.write('\n')

        for prn in list(self.obs.iterkeys()):
            # G    3 C1C L1C D1C
            self.rinex_file.write("%14.3f  " % self.obs[prn][0])
            self.rinex_file.write("%14.3f  " % self.obs[prn][1])
            self.rinex_file.write("%14.3f  \n" % self.obs[prn][2])

        self.rinex_file.flush()

    self.update_obs()

  def __init__(self, link, name='Rover', relay=False):
    super(ObservationView, self).__init__()

    self.obs_count = 0
    self.n_obs = 1

    self.relay = relay
    self.name = name

    self.rinex_file = None

    self.link = link
    self.link.add_callback(ids.NEW_OBS, self.obs_callback)

    self.python_console_cmds = {
      'obs': self
    }
Ejemplo n.º 9
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)
    logging_v = Bool(False)
    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=os.path.join(determine_path(), 'images',
                                                   'iconic', 'x.svg'),
                             width=16,
                             height=16)
    zoomall_button = SVGButton(label='',
                               tooltip='Zoom All',
                               toggle=True,
                               filename=os.path.join(determine_path(),
                                                     'images', 'iconic',
                                                     'fullscreen.svg'),
                               width=16,
                               height=16)
    center_button = SVGButton(label='',
                              tooltip='Center on Solution',
                              toggle=True,
                              filename=os.path.join(determine_path(), 'images',
                                                    'iconic', 'target.svg'),
                              width=16,
                              height=16)
    paused_button = SVGButton(label='',
                              tooltip='Pause',
                              toggle_tooltip='Run',
                              toggle=True,
                              filename=os.path.join(determine_path(), 'images',
                                                    'iconic', 'pause.svg'),
                              toggle_filename=os.path.join(
                                  determine_path(), '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('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.set_data('cur_lat_spp', [])
        self.plot_data.set_data('cur_lng_spp', [])
        self.plot_data.set_data('cur_alt_spp', [])
        self.plot_data.set_data('cur_lat_dgnss', [])
        self.plot_data.set_data('cur_lng_dgnss', [])
        self.plot_data.set_data('cur_alt_dgnss', [])
        self.plot_data.set_data('cur_lat_float', [])
        self.plot_data.set_data('cur_lng_float', [])
        self.plot_data.set_data('cur_alt_float', [])
        self.plot_data.set_data('cur_lat_fixed', [])
        self.plot_data.set_data('cur_lng_fixed', [])
        self.plot_data.set_data('cur_alt_fixed', [])

    def _clear_button_fired(self):
        self.tows = np.empty(self.plot_history_max)
        self.lats = np.empty(self.plot_history_max)
        self.lngs = np.empty(self.plot_history_max)
        self.alts = np.empty(self.plot_history_max)
        self.modes = np.empty(self.plot_history_max)
        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._reset_remove_current()

    def _pos_llh_callback(self, sbp_msg, **metadata):
        # Updating an ArrayPlotData isn't thread safe (see chaco issue #9), so
        # actually perform the update in the UI thread.
        if self.running:
            GUI.invoke_later(self.pos_llh_callback, sbp_msg)

    def update_table(self):
        self._table_list = self.table_spp.items()

    def auto_survey(self):
        if self.counter < 1000:
            self.counter = self.counter + 1
        self.latitude_list.append(self.last_soln.lat)
        self.longitude_list.append(self.last_soln.lon)
        self.altitude_list.append(self.last_soln.height)
        self.latitude_list = self.latitude_list[-1000:]
        self.longitude_list = self.longitude_list[-1000:]
        self.altitude_list = self.altitude_list[-1000:]
        self.latitude = (sum(self.latitude_list)) / self.counter
        self.altitude = (sum(self.altitude_list)) / self.counter
        self.longitude = (sum(self.longitude_list)) / self.counter

    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)
        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

        if self.week is not None:
            t = datetime.datetime(1980, 1, 6) + \
                datetime.timedelta(weeks=self.week) + \
                datetime.timedelta(seconds=tow)
            tstr = t.strftime('%Y-%m-%d %H:%M')
            secs = t.strftime('%S.%f')

            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 self.logging_p == False:
                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(
                        "time,latitude(degrees),longitude(degrees),altitude(meters),"
                        "h_accuracy(meters),v_accuracy(meters),n_sats,flags\n")
                self.log_file.write(
                    '%s,%.10f,%.10f,%.4f,%.4f,%.4f,%d,%d\n' %
                    ("{0}:{1:06.6f}".format(tstr, float(secs)), 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 Time', EMPTY_STR))
            pos_table.append(('GPS Week', EMPTY_STR))
            pos_table.append(('GPS TOW', 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(('h_accuracy', EMPTY_STR))
            pos_table.append(('v_accuracy', EMPTY_STR))
        else:
            self.last_stime_update = time.time()
            if self.week is not None:
                pos_table.append(
                    ('GPS Time', "{0}:{1:06.3f}".format(tstr, float(secs))))
                pos_table.append(('GPS Week', str(self.week)))
            pos_table.append(('GPS TOW', "{:.3f}".format(tow)))
            pos_table.append(('Num. Sats', soln.n_sats))
            pos_table.append(('Lat', soln.lat))
            pos_table.append(('Lng', soln.lon))
            pos_table.append(('Height', soln.height))
            pos_table.append(('h_accuracy', soln.h_accuracy))
            pos_table.append(('v_accuracy', soln.v_accuracy))

        pos_table.append(('Pos Flags', '0x%03x' % soln.flags))
        pos_table.append(('Pos Fix Mode', mode_dict[self.last_pos_mode]))

        self.auto_survey()

        # setup_plot variables
        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:]

        # SPP
        spp_indexer, dgnss_indexer, float_indexer, fixed_indexer = None, None, None, None
        if np.any(self.modes):
            spp_indexer = (self.modes == SPP_MODE)
            dgnss_indexer = (self.modes == DGNSS_MODE)
            float_indexer = (self.modes == FLOAT_MODE)
            fixed_indexer = (self.modes == FIXED_MODE)

            # make sure that there is at least one true in indexer before setting
            if any(spp_indexer):
                self.plot_data.set_data('lat_spp', self.lats[spp_indexer])
                self.plot_data.set_data('lng_spp', self.lngs[spp_indexer])
                self.plot_data.set_data('alt_spp', self.alts[spp_indexer])
            if any(dgnss_indexer):
                self.plot_data.set_data('lat_dgnss', self.lats[dgnss_indexer])
                self.plot_data.set_data('lng_dgnss', self.lngs[dgnss_indexer])
                self.plot_data.set_data('alt_dgnss', self.alts[dgnss_indexer])
            if any(float_indexer):
                self.plot_data.set_data('lat_float', self.lats[float_indexer])
                self.plot_data.set_data('lng_float', self.lngs[float_indexer])
                self.plot_data.set_data('alt_float', self.alts[float_indexer])
            if any(fixed_indexer):
                self.plot_data.set_data('lat_fixed', self.lats[fixed_indexer])
                self.plot_data.set_data('lng_fixed', self.lngs[fixed_indexer])
                self.plot_data.set_data('alt_fixed', self.alts[fixed_indexer])

            # update our "current solution" icon
            if self.last_pos_mode == SPP_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_spp', [soln.lat])
                self.plot_data.set_data('cur_lng_spp', [soln.lon])
            elif self.last_pos_mode == DGNSS_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_dgnss', [soln.lat])
                self.plot_data.set_data('cur_lng_dgnss', [soln.lon])
            elif self.last_pos_mode == FLOAT_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_float', [soln.lat])
                self.plot_data.set_data('cur_lng_float', [soln.lon])
            elif self.last_pos_mode == FIXED_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_fixed', [soln.lat])
                self.plot_data.set_data('cur_lng_fixed', [soln.lon])
            else:
                pass

        # set-up table variables
        self.pos_table = pos_table
        self.table = self.pos_table + self.vel_table + self.dops_table

        # TODO: figure out how to center the graph now that we have two separate messages
        # when we selectively send only SPP, the centering function won't work anymore

        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 - d, soln.lon + d)
            d = (self.plot.value_range.high - self.plot.value_range.low) / 2.
            self.plot.value_range.set_bounds(soln.lat - d, soln.lat + d)
        if self.zoomall:
            plot_square_axes(
                self.plot, ('lng_spp', 'lng_dgnss', 'lng_float', 'lng_fixed'),
                ('lat_spp', 'lat_dgnss', 'lat_float', 'lat_fixed'))

    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.table = self.pos_table + self.vel_table + self.dops_table

    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

        if self.week is not None:
            t = datetime.datetime(1980, 1, 6) + \
                datetime.timedelta(weeks=self.week) + \
                datetime.timedelta(seconds=tow)
            tstr = t.strftime('%Y-%m-%d %H:%M')
            secs = t.strftime('%S.%f')

            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 self.logging_v == False:
                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(
                        'time,north(m/s),east(m/s),down(m/s),speed(m/s),flags,num_signals\n'
                    )
                self.vel_log_file.write(
                    '%s,%.6f,%.6f,%.6f,%.6f,%d,%d\n' %
                    ("{0}:{1:06.6f}".format(tstr, float(secs)),
                     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.table = self.pos_table + self.vel_table + self.dops_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

    def __init__(self, link, dirname=''):
        super(SolutionView, self).__init__()

        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.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=[])
        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)
        # 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)
        plot_labels = ['SPP', 'DGPS', "RTK float", "RTK fixed"]
        plots_legend = dict(zip(plot_labels, [spp, dgnss, rtkfloat, rtkfix]))
        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.week = None
        self.nsec = 0

        self.python_console_cmds = {
            'solution': self,
        }
Ejemplo n.º 10
0
class BaselineView(HasTraits):
    python_console_cmds = Dict()

    table = List()

    logging_b = Bool(False)
    directory_name_b = File

    plot = Instance(Plot)
    plot_data = Instance(ArrayPlotData)

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

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

    reset_button = Button(label='Reset Filters')
    reset_iar_button = Button(label='Reset IAR')
    init_base_button = Button(label='Init. with known baseline')

    traits_view = View(
        HSplit(
            Item('table',
                 style='readonly',
                 editor=TabularEditor(adapter=SimpleAdapter()),
                 show_label=False,
                 width=0.3),
            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('reset_button', show_label=False),
                    Item('reset_iar_button', show_label=False),
                    Item('init_base_button', show_label=False),
                ),
                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_button_fired(self):
        self.link(MsgResetFilters(filter=0))

    def _reset_iar_button_fired(self):
        self.link(MsgResetFilters(filter=1))

    def _init_base_button_fired(self):
        self.link(MsgInitBase())

    def _clear_button_fired(self):
        self.neds[:] = np.NAN
        self.fixeds[:] = False
        self.plot_data.set_data('n_fixed', [])
        self.plot_data.set_data('e_fixed', [])
        self.plot_data.set_data('d_fixed', [])
        self.plot_data.set_data('n_float', [])
        self.plot_data.set_data('e_float', [])
        self.plot_data.set_data('d_float', [])
        self.plot_data.set_data('t', [])
        self.plot_data.set_data('cur_fixed_n', [])
        self.plot_data.set_data('cur_fixed_e', [])
        self.plot_data.set_data('cur_fixed_d', [])
        self.plot_data.set_data('cur_float_n', [])
        self.plot_data.set_data('cur_float_e', [])
        self.plot_data.set_data('cur_float_d', [])

    def iar_state_callback(self, sbp_msg, **metadata):
        self.num_hyps = sbp_msg.num_hyps
        self.last_hyp_update = time.time()

    def _baseline_callback_ned(self, sbp_msg, **metadata):
        # Updating an ArrayPlotData isn't thread safe (see chaco issue #9), so
        # actually perform the update in the UI thread.
        if self.running:
            GUI.invoke_later(self.baseline_callback, sbp_msg)

    def update_table(self):
        self._table_list = self.table.items()

    def gps_time_callback(self, sbp_msg, **metadata):
        self.week = MsgGPSTime(sbp_msg).wn
        self.nsec = MsgGPSTime(sbp_msg).ns

    def mode_string(self, msg):
        if msg:
            self.fixed = (msg.flags & 1) == 1
            if self.fixed:
                return 'Fixed RTK'
            else:
                return 'Float'
        return 'None'

    def baseline_callback(self, sbp_msg):
        self.last_btime_update = time.time()
        soln = MsgBaselineNED(sbp_msg)
        self.last_soln = soln
        table = []

        soln.n = soln.n * 1e-3
        soln.e = soln.e * 1e-3
        soln.d = soln.d * 1e-3

        dist = np.sqrt(soln.n**2 + soln.e**2 + soln.d**2)

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

        if self.week is not None:
            t = datetime.datetime(1980, 1, 6) + \
                datetime.timedelta(weeks=self.week) + \
                datetime.timedelta(seconds=tow)

            table.append(('GPS Time', t))
            table.append(('GPS Week', str(self.week)))

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

            if self.logging_b == False:
                self.log_file = None

            if self.logging_b:
                if self.log_file is None:
                    self.log_file = open(filepath, 'w')

                    self.log_file.write(
                        'time,north(meters),east(meters),down(meters),distance(meters),num_sats,flags,num_hypothesis\n'
                    )

                self.log_file.write('%s,%.4f,%.4f,%.4f,%.4f,%d,0x%02x,%d\n' %
                                    (str(t), soln.n, soln.e, soln.d, dist,
                                     soln.n_sats, soln.flags, self.num_hyps))
                self.log_file.flush()

        table.append(('GPS ToW', tow))

        table.append(('N', soln.n))
        table.append(('E', soln.e))
        table.append(('D', soln.d))
        table.append(('Dist.', dist))
        table.append(('Num. Sats.', soln.n_sats))
        table.append(('Flags', '0x%02x' % soln.flags))
        table.append(('Mode', self.mode_string(soln)))
        if time.time() - self.last_hyp_update < 10 and self.num_hyps != 1:
            table.append(('IAR Num. Hyps.', self.num_hyps))
        else:
            table.append(('IAR Num. Hyps.', "None"))

        # Rotate array, deleting oldest entries to maintain
        # no more than N in plot
        self.neds[1:] = self.neds[:-1]
        self.fixeds[1:] = self.fixeds[:-1]

        # Insert latest position
        self.neds[0][:] = [soln.n, soln.e, soln.d]
        self.fixeds[0] = self.fixed

        neds_fixed = self.neds[self.fixeds]
        neds_float = self.neds[np.logical_not(self.fixeds)]

        if not all(map(any, np.isnan(neds_fixed))):
            self.plot_data.set_data('n_fixed', neds_fixed.T[0])
            self.plot_data.set_data('e_fixed', neds_fixed.T[1])
            self.plot_data.set_data('d_fixed', neds_fixed.T[2])
        if not all(map(any, np.isnan(neds_float))):
            self.plot_data.set_data('n_float', neds_float.T[0])
            self.plot_data.set_data('e_float', neds_float.T[1])
            self.plot_data.set_data('d_float', neds_float.T[2])

        if self.fixed:
            self.plot_data.set_data('cur_fixed_n', [soln.n])
            self.plot_data.set_data('cur_fixed_e', [soln.e])
            self.plot_data.set_data('cur_fixed_d', [soln.d])
            self.plot_data.set_data('cur_float_n', [])
            self.plot_data.set_data('cur_float_e', [])
            self.plot_data.set_data('cur_float_d', [])
        else:
            self.plot_data.set_data('cur_float_n', [soln.n])
            self.plot_data.set_data('cur_float_e', [soln.e])
            self.plot_data.set_data('cur_float_d', [soln.d])
            self.plot_data.set_data('cur_fixed_n', [])
            self.plot_data.set_data('cur_fixed_e', [])
            self.plot_data.set_data('cur_fixed_d', [])

        self.plot_data.set_data('ref_n', [0.0])
        self.plot_data.set_data('ref_e', [0.0])
        self.plot_data.set_data('ref_d', [0.0])

        if self.position_centered:
            d = (self.plot.index_range.high - self.plot.index_range.low) / 2.
            self.plot.index_range.set_bounds(soln.e - d, soln.e + d)
            d = (self.plot.value_range.high - self.plot.value_range.low) / 2.
            self.plot.value_range.set_bounds(soln.n - d, soln.n + d)

        if self.zoomall:
            plot_square_axes(self.plot, ('e_fixed', 'e_float'),
                             ('n_fixed', 'n_float'))
        self.table = table

    def __init__(self, link, plot_history_max=1000, dirname=''):
        super(BaselineView, self).__init__()
        self.log_file = None
        self.directory_name_b = dirname
        self.num_hyps = 0
        self.last_hyp_update = 0
        self.last_btime_update = 0
        self.last_soln = None
        self.plot_data = ArrayPlotData(n_fixed=[0.0],
                                       e_fixed=[0.0],
                                       d_fixed=[0.0],
                                       n_float=[0.0],
                                       e_float=[0.0],
                                       d_float=[0.0],
                                       t=[0.0],
                                       ref_n=[0.0],
                                       ref_e=[0.0],
                                       ref_d=[0.0],
                                       cur_fixed_e=[],
                                       cur_fixed_n=[],
                                       cur_fixed_d=[],
                                       cur_float_e=[],
                                       cur_float_n=[],
                                       cur_float_d=[])
        self.plot_history_max = plot_history_max

        self.neds = np.empty((plot_history_max, 3))
        self.neds[:] = np.NAN
        self.fixeds = np.zeros(plot_history_max, dtype=bool)

        self.plot = Plot(self.plot_data)
        color_float = (0.5, 0.5, 1.0)
        color_fixed = 'orange'
        pts_float = self.plot.plot(('e_float', 'n_float'),
                                   type='scatter',
                                   color=color_float,
                                   marker='dot',
                                   line_width=0.0,
                                   marker_size=1.0)
        pts_fixed = self.plot.plot(('e_fixed', 'n_fixed'),
                                   type='scatter',
                                   color=color_fixed,
                                   marker='dot',
                                   line_width=0.0,
                                   marker_size=1.0)
        lin = self.plot.plot(('e_fixed', 'n_fixed'),
                             type='line',
                             color=(1, 0.65, 0, 0.1))
        ref = self.plot.plot(('ref_e', 'ref_n'),
                             type='scatter',
                             color='red',
                             marker='plus',
                             marker_size=5,
                             line_width=1.5)
        cur_fixed = self.plot.plot(('cur_fixed_e', 'cur_fixed_n'),
                                   type='scatter',
                                   color=color_fixed,
                                   marker='plus',
                                   marker_size=5,
                                   line_width=1.5)
        cur_float = self.plot.plot(('cur_float_e', 'cur_float_n'),
                                   type='scatter',
                                   color=color_float,
                                   marker='plus',
                                   marker_size=5,
                                   line_width=1.5)
        plot_labels = ['Base Position', 'RTK Fixed', 'RTK Float']
        plots_legend = dict(zip(plot_labels, [ref, cur_fixed, cur_float]))
        self.plot.legend.plots = plots_legend
        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 = 'E (meters)'
        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 = 'N (meters)'
        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.week = None
        self.nsec = 0

        self.link = link
        self.link.add_callback(self._baseline_callback_ned,
                               SBP_MSG_BASELINE_NED)
        self.link.add_callback(self.iar_state_callback, SBP_MSG_IAR_STATE)
        self.link.add_callback(self.gps_time_callback, SBP_MSG_GPS_TIME)

        self.python_console_cmds = {'baseline': self}
Ejemplo n.º 11
0
class SystemMonitorView(HasTraits):
    python_console_cmds = Dict()

    _threads_table_list = List()
    threads = List()

    uart_a_crc_error_count = Int(0)
    uart_a_io_error_count = Int(0)
    uart_a_rx_buffer = Float(0)
    uart_a_tx_buffer = Float(0)
    uart_a_tx_KBps = Float(0)
    uart_a_rx_KBps = Float(0)

    uart_b_crc_error_count = Int(0)
    uart_b_io_error_count = Int(0)
    uart_b_rx_buffer = Float(0)
    uart_b_tx_buffer = Float(0)
    uart_b_tx_KBps = Float(0)
    uart_b_rx_KBps = Float(0)

    ftdi_crc_error_count = Int(0)
    ftdi_io_error_count = Int(0)
    ftdi_rx_buffer = Float(0)
    ftdi_tx_buffer = Float(0)
    ftdi_tx_KBps = Float(0)
    ftdi_rx_KBps = Float(0)

    msg_obs_avg_latency_ms = Int(0)
    msg_obs_min_latency_ms = Int(0)
    msg_obs_max_latency_ms = Int(0)
    msg_obs_window_latency_ms = Int(0)

    piksi_reset_button = SVGButton(label='Reset Piksi',
                                   tooltip='Reset Piksi',
                                   filename=os.path.join(
                                       os.path.dirname(__file__), 'images',
                                       'fontawesome', 'power27.svg'),
                                   width=16,
                                   height=16,
                                   aligment='center')

    traits_view = View(
        VGroup(
            Item(
                '_threads_table_list',
                style='readonly',
                editor=TabularEditor(adapter=SimpleAdapter()),
                show_label=False,
                width=0.85,
            ),
            HGroup(
                VGroup(
                    VGroup(
                        Item('msg_obs_window_latency_ms',
                             label='Obs Latency',
                             style='readonly',
                             format_str='%dms'),
                        Item('msg_obs_avg_latency_ms',
                             label='Obs Latency (Avg ms)',
                             style='readonly',
                             format_str='%dms'),
                        Item('msg_obs_min_latency_ms',
                             label='Obs Latency (Min ms)',
                             style='readonly',
                             format_str='%dms'),
                        Item('msg_obs_max_latency_ms',
                             label='Obs Latency (Max ms)',
                             style='readonly',
                             format_str='%dms'),
                        label='Connection Monitor',
                        show_border=True,
                    ),
                    HGroup(
                        Spring(width=50, springy=False),
                        Item('piksi_reset_button',
                             show_label=False,
                             width=0.50),
                    ),
                ),
                VGroup(
                    Item('uart_a_crc_error_count',
                         label='CRC Errors',
                         style='readonly'),
                    Item('uart_a_io_error_count',
                         label='IO Errors',
                         style='readonly'),
                    Item('uart_a_tx_buffer',
                         label='TX Buffer %',
                         style='readonly',
                         format_str='%.1f'),
                    Item('uart_a_rx_buffer',
                         label='RX Buffer %',
                         style='readonly',
                         format_str='%.1f'),
                    Item('uart_a_tx_KBps',
                         label='TX KBytes/s',
                         style='readonly',
                         format_str='%.2f'),
                    Item('uart_a_rx_KBps',
                         label='RX KBytes/s',
                         style='readonly',
                         format_str='%.2f'),
                    label='UART A',
                    show_border=True,
                ),
                VGroup(
                    Item('uart_b_crc_error_count',
                         label='CRC Errors',
                         style='readonly'),
                    Item('uart_b_io_error_count',
                         label='IO Errors',
                         style='readonly'),
                    Item('uart_b_tx_buffer',
                         label='TX Buffer %',
                         style='readonly',
                         format_str='%.1f'),
                    Item('uart_b_rx_buffer',
                         label='RX Buffer %',
                         style='readonly',
                         format_str='%.1f'),
                    Item('uart_b_tx_KBps',
                         label='TX KBytes/s',
                         style='readonly',
                         format_str='%.2f'),
                    Item('uart_b_rx_KBps',
                         label='RX KBytes/s',
                         style='readonly',
                         format_str='%.2f'),
                    label='UART B',
                    show_border=True,
                ),
                VGroup(
                    Item('ftdi_crc_error_count',
                         label='CRC Errors',
                         style='readonly'),
                    Item('ftdi_io_error_count',
                         label='IO Errors',
                         style='readonly'),
                    Item('ftdi_tx_buffer',
                         label='TX Buffer %',
                         style='readonly',
                         format_str='%.1f'),
                    Item('ftdi_rx_buffer',
                         label='RX Buffer %',
                         style='readonly',
                         format_str='%.1f'),
                    Item('ftdi_tx_KBps',
                         label='TX KBytes/s',
                         style='readonly',
                         format_str='%.2f'),
                    Item('ftdi_rx_KBps',
                         label='RX KBytes/s',
                         style='readonly',
                         format_str='%.2f'),
                    label='USB UART',
                    show_border=True,
                ),
            ),
        ), )

    def update_threads(self):
        self._threads_table_list = [
            (thread_name, state.cpu, state.stack_free)
            for thread_name, state in sorted(
                self.threads, key=lambda x: x[1].cpu, reverse=True)
        ]

    def heartbeat_callback(self, sbp_msg, **metadata):
        self.update_threads()
        self.threads = []

    def thread_state_callback(self, sbp_msg, **metadata):
        if sbp_msg.name == '':
            sbp_msg.name = '(no name)'
        sbp_msg.cpu /= 10.
        self.threads.append((sbp_msg.name, sbp_msg))

    def _piksi_reset_button_fired(self):
        self.link(MsgReset())

    def uart_state_callback(self, m, **metadata):
        self.uart_a_tx_KBps = m.uart_a.tx_throughput
        self.uart_a_rx_KBps = m.uart_a.rx_throughput
        self.uart_a_crc_error_count = m.uart_a.crc_error_count
        self.uart_a_io_error_count = m.uart_a.io_error_count
        self.uart_a_tx_buffer = 100 * m.uart_a.tx_buffer_level / 255.0
        self.uart_a_rx_buffer = 100 * m.uart_a.rx_buffer_level / 255.0

        self.uart_b_tx_KBps = m.uart_b.tx_throughput
        self.uart_b_rx_KBps = m.uart_b.rx_throughput
        self.uart_b_crc_error_count = m.uart_b.crc_error_count
        self.uart_b_io_error_count = m.uart_b.io_error_count
        self.uart_b_tx_buffer = 100 * m.uart_b.tx_buffer_level / 255.0
        self.uart_b_rx_buffer = 100 * m.uart_b.rx_buffer_level / 255.0

        self.uart_ftdi_tx_KBps = m.uart_ftdi.tx_throughput
        self.uart_ftdi_rx_KBps = m.uart_ftdi.rx_throughput
        self.uart_ftdi_crc_error_count = m.uart_ftdi.crc_error_count
        self.uart_ftdi_io_error_count = m.uart_ftdi.io_error_count
        self.uart_ftdi_tx_buffer = 100 * m.uart_ftdi.tx_buffer_level / 255.0
        self.uart_ftdi_rx_buffer = 100 * m.uart_ftdi.rx_buffer_level / 255.0

        self.msg_obs_avg_latency_ms = m.latency.avg
        self.msg_obs_min_latency_ms = m.latency.lmin
        self.msg_obs_max_latency_ms = m.latency.lmax
        self.msg_obs_window_latency_ms = m.latency.current

    def __init__(self, link):
        super(SystemMonitorView, self).__init__()

        self.link = link
        self.link.add_callback(self.heartbeat_callback, SBP_MSG_HEARTBEAT)
        self.link.add_callback(self.thread_state_callback,
                               SBP_MSG_THREAD_STATE)
        self.link.add_callback(self.uart_state_callback, SBP_MSG_UART_STATE)

        self.python_console_cmds = {'mon': self}
Ejemplo n.º 12
0
class ObservationView(HasTraits):
  python_console_cmds = Dict()

  _obs_table_list = List()
  obs = Dict()

  name = Str('Rover')

  recording = Bool(False)

  record_button = SVGButton(
    label='Record', tooltip='Record Raw Observations',
    toggle_tooltip='Stop Recording', toggle=True,
    filename=os.path.join(determine_path(),
                          'images', 'fontawesome', 'floppy-o.svg'),
    toggle_filename=os.path.join(determine_path(),
                                 'images', 'fontawesome', 'stop.svg'),
    width=16, height=16
  )

  def trait_view(self, view):
    return View(
      HGroup(
        Item('_obs_table_list', style='readonly',
             editor=TabularEditor(adapter=SimpleAdapter()), show_label=False),
        VGroup(
          Item('record_button', show_label=False),
        ),
        label=self.name,
        show_border=True
      )
    )

  def _record_button_fired(self):
    self.recording = not self.recording
    if not self.recording:
      if self.rinex_file is not None:
        self.rinex_file.close()
      self.rinex_file = None

  def rinex_save(self):
    if self.recording:
      if self.rinex_file is None:
        # If the file is being opened for the first time, write the RINEX header
        self.rinex_file = open(self.name + self.t.strftime(
                               "-%Y%m%d-%H%M%S.obs"), 'w')
        header = '     ' +\
"""2.11           OBSERVATION DATA    G (GPS)             RINEX VERSION / TYPE
pyNEX                                   %s UTC PGM / RUN BY / DATE
                                                            MARKER NAME
                                                            OBSERVER / AGENCY
                                                            REC # / TYPE / VERS
                                                            ANT # / TYPE
   808673.9171 -4086658.5368  4115497.9775                  APPROX POSITION XYZ
        0.0000        0.0000        0.0000                  ANTENNA: DELTA H/E/N
     1     0                                                WAVELENGTH FACT L1/2
     3    C1    L1    S1                                    # / TYPES OF OBSERV
%s%13.7f     GPS         TIME OF FIRST OBS
                                                            END OF HEADER
""" % (
            datetime.datetime.utcnow().strftime("%Y%m%d %H%M%S"),
            self.t.strftime("  %Y    %m    %d    %H    %M"),
                            self.t.second + self.t.microsecond * 1e-6)
        self.rinex_file.write(header)

      # L1CA only
      copy_prns = prns = [s for s in self.obs.iterkeys() if L1CA in s]
      self.rinex_file.write("%s %10.7f  0 %2d" %
                            (self.t.strftime(" %y %m %d %H %M"),
                             self.t.second + self.t.microsecond * 1e-6,
                             len(prns)))

      while len(copy_prns) > 0:
        prns_ = copy_prns[:12]
        copy_prns = copy_prns[12:]
        for prn in prns_:
          # take only the leading prn number
          self.rinex_file.write('G%2d' % (int(prn.split()[0])))
        self.rinex_file.write('   ' * (12 - len(prns_)))
        self.rinex_file.write('\n')

      for prn in prns:
        # G    3 C1C L1C D1C
        self.rinex_file.write("%14.3f  " % self.obs[prn][0])
        self.rinex_file.write("%14.3f  " % self.obs[prn][1])
        self.rinex_file.write("%14.3f  \n" % self.obs[prn][2])

      self.rinex_file.flush()

  def update_obs(self):
    self._obs_table_list =\
      [(prn,) + obs for prn, obs in sorted(self.obs.items(),
                                           key=lambda x: x[0])]

  def obs_packed_callback(self, sbp_msg, **metadata):
    if (sbp_msg.sender is not None and
        (self.relay ^ (sbp_msg.sender == 0))):
      return
    tow = sbp_msg.header.t.tow
    wn = sbp_msg.header.t.wn
    seq = sbp_msg.header.n_obs

    tow = float(tow) / 1000.0

    total = seq >> 4
    count = seq & ((1 << 4) - 1)

    # Confirm this packet is good.
    # Assumes no out-of-order packets
    if count == 0:
      self.old_tow = self.gps_tow
      self.gps_tow = tow
      self.gps_week = wn
      self.prev_obs_total = total
      self.prev_obs_count = 0
      self.old_obs = copy.deepcopy(self.obs)
      self.obs = {}
    elif self.gps_tow != tow or\
         self.gps_week != wn or\
         self.prev_obs_count + 1 != count or\
         self.prev_obs_total != total:
      print "We dropped a packet. Skipping this observation sequence"
      self.prev_obs_count = -1
      return
    else:
      self.prev_obs_count = count

    # Save this packet
    # See sbp_piksi.h for format
    for o in sbp_msg.obs:
      prn = o.sid.sat
      # compute time difference of carrier phase for display
      cp = float(o.L.i) + float(o.L.f) / (1 << 8)
      if o.sid.code == 0 or o.sid.code == 1:
        prn += 1
      prn = '{} ({})'.format(prn, code_to_str(o.sid.code))
      if sbp_msg.msg_type == SBP_MSG_OBS_DEP_A or\
         sbp_msg.msg_type == SBP_MSG_OBS_DEP_B:
        divisor = 1e2
      else:
        divisor = 5e1
      try:
        ocp = self.old_obs[prn][1]
      except:
        ocp = 0
      cf = (cp - ocp) / (self.gps_tow - self.old_tow)
      self.obs[prn] = (float(o.P) / divisor,
                       float(o.L.i) + float(o.L.f) / (1 << 8),
                       float(o.cn0) / 4,
                       cf)
    if (count == total - 1):
      self.t = datetime.datetime(1980, 1, 6) + \
               datetime.timedelta(weeks=self.gps_week) + \
               datetime.timedelta(seconds=self.gps_tow)

      self.update_obs()
      self.rinex_save()

    return

  def ephemeris_callback(self, m, **metadata):
    prn = m.sid.sat
    if m.sid.code == 0 or m.sid.code == 1:
      prn += 1
    if self.recording:
      if self.eph_file is None:
        self.eph_file = open(self.name + self.t.strftime("-%Y%m%d-%H%M%S.eph"),
                             'w')
        header = "time, " \
               + "tgd, " \
               + "crs, crc, cuc, cus, cic, cis, " \
               + "dn, m0, ecc, sqrta, omega0, omegadot, w, inc, inc_dot, " \
               + "af0, af1, af2, " \
               + "toe_tow, toe_wn, toc_tow, toc_wn, " \
               + "valid, " \
               + "healthy, " \
               + "prn\n"
        self.eph_file.write(header)

      strout = "%s %10.7f" % (self.t.strftime(" %y %m %d %H %M"),
                              self.t.second + self.t.microsecond * 1e-6)
      strout += "," + str([m.tgd,
                           m.c_rs, m.c_rc, m.c_uc, m.c_us, m.c_ic, m.c_is,
                           m.dn, m.m0, m.ecc, m.sqrta, m.omega0, m.omegadot,
                           m.w, m.inc, m.inc_dot,
                           m.af0, m.af1, m.af2,
                           m.toe_tow, m.toe_wn, m.toc_tow, m.toc_wn,
                           m.valid,
                           m.healthy,
                           prn])[1: -1] + "\n"
      self.eph_file.write(strout)
      self.eph_file.flush()

  def __init__(self, link, name='Rover', relay=False):
    super(ObservationView, self).__init__()
    self.obs_count = 0
    self.gps_tow = 0.0
    self.gps_week = 0
    self.relay = relay
    self.name = name
    self.rinex_file = None
    self.eph_file = None
    self.link = link
    self.link.add_callback(self.obs_packed_callback,
                           [SBP_MSG_OBS, SBP_MSG_OBS_DEP_B])
    self.link.add_callback(self.ephemeris_callback, SBP_MSG_EPHEMERIS)
    self.python_console_cmds = {'obs': self}
Ejemplo n.º 13
0
class SwiftConsole(HasTraits):
    """Traits-defined Swift Console.

    link : object
      Serial driver
    update : bool
      Update the firmware
    log_level_filter : str
      Syslog string, one of "ERROR", "WARNING", "INFO", "DEBUG".
    """

    link = Instance(sbpc.Handler)
    console_output = Instance(OutputList())
    python_console_env = Dict
    device_serial = Str('')
    dev_id = Str('')
    tracking_view = Instance(TrackingView)
    solution_view = Instance(SolutionView)
    baseline_view = Instance(BaselineView)
    skyplot_view = Instance(SkyplotView)
    observation_view = Instance(ObservationView)
    networking_view = Instance(SbpRelayView)
    observation_view_base = Instance(ObservationView)
    system_monitor_view = Instance(SystemMonitorView)
    settings_view = Instance(SettingsView)
    update_view = Instance(UpdateView)
    ins_view = Instance(INSView)
    mag_view = Instance(MagView)
    spectrum_analyzer_view = Instance(SpectrumAnalyzerView)
    log_level_filter = Enum(list(SYSLOG_LEVELS.values()))
    """"
  mode : baseline and solution view - SPP, Fixed or Float
  num_sat : baseline and solution view - number of satellites
  port : which port is Swift Device is connected to
  directory_name : location of logged files
  json_logging : enable JSON logging
  csv_logging : enable CSV logging

  """
    pos_mode = Str('')
    rtk_mode = Str('')
    ins_status_string = Str('')
    num_sats_str = Str('')
    cnx_desc = Str('')
    age_of_corrections = Str('')
    uuid = Str('')
    directory_name = Directory
    json_logging = Bool(True)
    csv_logging = Bool(False)
    show_csv_log = Bool(False)
    cnx_icon = Str('')
    heartbeat_count = Int()
    last_timer_heartbeat = Int()
    driver_data_rate = Str()
    solid_connection = Bool(False)

    csv_logging_button = SVGButton(
        toggle=True,
        label='CSV log',
        tooltip='start CSV logging',
        toggle_tooltip='stop CSV logging',
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        orientation='vertical',
        width=2,
        height=2,
    )
    json_logging_button = SVGButton(
        toggle=True,
        label='JSON log',
        tooltip='start JSON logging',
        toggle_tooltip='stop JSON logging',
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        orientation='vertical',
        width=2,
        height=2,
    )
    paused_button = SVGButton(
        label='',
        tooltip='Pause console update',
        toggle_tooltip='Resume console update',
        toggle=True,
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        width=8,
        height=8)
    clear_button = SVGButton(
        label='',
        tooltip='Clear console buffer',
        filename=resource_filename('console/images/iconic/x.svg'),
        width=8,
        height=8)

    view = View(VSplit(
        Tabbed(Tabbed(Item('tracking_view',
                           style='custom',
                           label='Signals',
                           show_label=False),
                      Item('skyplot_view',
                           style='custom',
                           label='Sky Plot',
                           show_label=False),
                      label="Tracking"),
               Item('solution_view', style='custom', label='Solution'),
               Item('baseline_view', style='custom', label='Baseline'),
               VSplit(
                   Item('observation_view', style='custom', show_label=False),
                   Item('observation_view_base',
                        style='custom',
                        show_label=False),
                   label='Observations',
               ),
               Item('settings_view', style='custom', label='Settings'),
               Item('update_view', style='custom', label='Update'),
               Tabbed(Item('system_monitor_view',
                           style='custom',
                           label='System Monitor'),
                      Item('ins_view', style='custom', label='INS'),
                      Item('mag_view', style='custom', label='Magnetometer'),
                      Item('networking_view',
                           label='Networking',
                           style='custom',
                           show_label=False),
                      Item('spectrum_analyzer_view',
                           label='Spectrum Analyzer',
                           style='custom'),
                      label='Advanced',
                      show_labels=False),
               show_labels=False),
        VGroup(
            VGroup(
                HGroup(
                    Spring(width=4, springy=False),
                    Item('paused_button',
                         show_label=False,
                         padding=0,
                         width=8,
                         height=8),
                    Item('clear_button', show_label=False, width=8, height=8),
                    Item('', label='Console Log', emphasized=True),
                    Item('csv_logging_button',
                         emphasized=True,
                         show_label=False,
                         visible_when='show_csv_log',
                         width=12,
                         height=-30,
                         padding=0),
                    Item('json_logging_button',
                         emphasized=True,
                         show_label=False,
                         width=12,
                         height=-30,
                         padding=0),
                    Item(
                        'directory_name',
                        show_label=False,
                        springy=True,
                        tooltip=
                        'Choose location for file logs. Default is home/SwiftNav.',
                        height=-25,
                        enabled_when='not(json_logging or csv_logging)',
                        editor_args={'auto_set': True}),
                    UItem(
                        'log_level_filter',
                        style='simple',
                        padding=0,
                        height=8,
                        show_label=True,
                        tooltip=
                        'Show log levels up to and including the selected level of severity.\nThe CONSOLE log level is always visible.'
                    ),
                ),
                Item('console_output',
                     style='custom',
                     editor=InstanceEditor(),
                     height=125,
                     show_label=False,
                     full_size=True),
            ),
            HGroup(
                Spring(width=4, springy=False),
                Item('',
                     label='Port:',
                     emphasized=True,
                     tooltip='Interface for communicating with Swift device'),
                Item('cnx_desc', show_label=False, style='readonly'),
                Item('',
                     label='Pos:',
                     emphasized=True,
                     tooltip='Device Position Mode: SPS, DGNSS, or RTK'),
                Item('pos_mode', show_label=False, style='readonly'),
                Item('',
                     label='RTK:',
                     emphasized=True,
                     tooltip='Device RTK Mode: Float or Fixed'),
                Item('rtk_mode', show_label=False, style='readonly'),
                Item('',
                     label='Sats:',
                     emphasized=True,
                     tooltip='Number of satellites used in solution'),
                Item('num_sats_str',
                     padding=2,
                     show_label=False,
                     style='readonly'),
                Item('',
                     label='Corr Age:',
                     emphasized=True,
                     tooltip=
                     'Age of corrections (-- means invalid / not present)'),
                Item('age_of_corrections',
                     padding=2,
                     show_label=False,
                     style='readonly'),
                Item('',
                     label='INS:',
                     emphasized=True,
                     tooltip='INS Status String'),
                Item('ins_status_string',
                     padding=2,
                     show_label=False,
                     style='readonly',
                     width=6),
                Spring(springy=True),
                Item('driver_data_rate', style='readonly', show_label=False),
                Item('cnx_icon',
                     show_label=False,
                     padding=0,
                     width=8,
                     height=8,
                     visible_when='solid_connection',
                     springy=False,
                     editor=ImageEditor(
                         allow_clipping=False,
                         image=ImageResource(
                             resource_filename(
                                 'console/images/iconic/arrows_blue.png')))),
                Item('cnx_icon',
                     show_label=False,
                     padding=0,
                     width=8,
                     height=8,
                     visible_when='not solid_connection',
                     springy=False,
                     editor=ImageEditor(
                         allow_clipping=False,
                         image=ImageResource(
                             resource_filename(
                                 'console/images/iconic/arrows_grey.png')))),
                Spring(width=4, height=-2, springy=False),
            ),
            Spring(height=1, springy=False),
        ),
    ),
                icon=icon,
                resizable=True,
                width=800,
                height=600,
                handler=ConsoleHandler(),
                title=CONSOLE_TITLE)

    def print_message_callback(self, sbp_msg, **metadata):
        try:
            encoded = sbp_msg.payload.encode('ascii', 'ignore')
            for eachline in reversed(encoded.split('\n')):
                self.console_output.write_level(
                    eachline, str_to_log_level(eachline.split(':')[0]))
        except UnicodeDecodeError as e:
            print("Error encoding msg_print: {}".format(e))

    def log_message_callback(self, sbp_msg, **metadata):
        encoded = sbp_msg.text.decode('utf8')
        for eachline in reversed(encoded.split('\n')):
            self.console_output.write_level(eachline, sbp_msg.level)

    def ext_event_callback(self, sbp_msg, **metadata):
        e = MsgExtEvent(sbp_msg)
        print(
            'External event: %s edge on pin %d at wn=%d, tow=%d, time qual=%s'
            % ("Rising" if
               (e.flags &
                (1 << 0)) else "Falling", e.pin, e.wn, e.tow, "good" if
               (e.flags & (1 << 1)) else "unknown"))

    def cmd_resp_callback(self, sbp_msg, **metadata):
        r = MsgCommandResp(sbp_msg)
        print("Received a command response message with code {0}".format(
            r.code))

    def _paused_button_fired(self):
        self.console_output.paused = not self.console_output.paused

    def _log_level_filter_changed(self):
        """
        Takes log level enum and translates into the mapped integer.
        Integer stores the current filter value inside OutputList.
        """
        self.console_output.log_level_filter = str_to_log_level(
            self.log_level_filter)

    def _clear_button_fired(self):
        self.console_output.clear()

    def _directory_name_changed(self):
        if self.baseline_view and self.solution_view:
            self.baseline_view.directory_name_b = self.directory_name
            self.solution_view.directory_name_p = self.directory_name
            self.solution_view.directory_name_v = self.directory_name
        if self.observation_view and self.observation_view_base:
            self.observation_view.dirname = self.directory_name
            self.observation_view_base.dirname = self.directory_name

    def update_on_heartbeat(self, sbp_msg, **metadata):
        self.heartbeat_count += 1

    def check_heartbeat(self):
        # if our heartbeat hasn't changed since the last timer interval the connection must have dropped
        if self.heartbeat_count == self.last_timer_heartbeat and self.heartbeat_count != 0:
            self.solid_connection = False
            self.ins_status_string = "None"
            self.pos_mode = "None"
            self.ins_mode = "None"
            self.num_sats_str = EMPTY_STR
            self.last_timer_heartbeat = self.heartbeat_count
        else:
            self.solid_connection = True
        self.last_timer_heartbeat = self.heartbeat_count

        bytes_diff = self.driver.bytes_read_since(self.last_driver_bytes_read)
        # 1024 bytes per KiloByte
        self.driver_data_rate = "{0:.2f} KB/s".format(
            (bytes_diff) / (HEARTBEAT_CHECK_PERIOD_SECONDS * 1024))
        self.last_driver_bytes_read = self.driver.total_bytes_read

        # if we aren't getting heartbeats and we have at some point gotten heartbeats, we don't have
        # a good connection and we should age out the data from the views
        if not self.solid_connection or self.driver_data_rate == 0:
            return
        # -- grab current time to use in logic
        current_time = monotonic()
        # --- determining which mode, llh or baseline, to show in the status bar ---
        llh_display_mode = "None"
        llh_num_sats = 0
        llh_is_rtk = False

        baseline_display_mode = "None"

        ins_status_string = EMPTY_STR

        # determine the latest llh solution mode
        if self.solution_view and (current_time -
                                   self.solution_view.last_stime_update) < 1:
            llh_solution_mode = self.solution_view.last_pos_mode
            llh_display_mode = pos_mode_dict.get(llh_solution_mode, EMPTY_STR)
            if llh_solution_mode > 0 and self.solution_view.last_soln:
                llh_num_sats = self.solution_view.last_soln.n_sats
            llh_is_rtk = (llh_solution_mode in RTK_MODES)
            if getattr(self.solution_view, 'ins_used',
                       False) and llh_solution_mode != DR_MODE:
                llh_display_mode += "+INS"

        # determine the latest baseline solution mode
        if (self.baseline_view and self.settings_view
                and self.settings_view.dgnss_enabled
                and (current_time - self.baseline_view.last_btime_update) < 1):
            baseline_solution_mode = self.baseline_view.last_mode
            baseline_display_mode = rtk_mode_dict.get(baseline_solution_mode,
                                                      EMPTY_STR)

        # if baseline solution mode is empty and POS mode is RTK, get the RTK mode from pos
        if baseline_display_mode not in rtk_mode_dict.values() and llh_is_rtk:
            baseline_display_mode = rtk_mode_dict.get(llh_solution_mode)

        # determine the latest INS mode
        if self.solution_view and (
                current_time -
                self.solution_view.last_ins_status_receipt_time) < 1:
            ins_flags = self.solution_view.ins_status_flags
            ins_mode = ins_flags & 0x7
            ins_type = (ins_flags >> 29) & 0x7
            odo_status = (ins_flags >> 8) & 0x3
            # ins_status has bug in odo.
            # If it says no odo, check if we have had tics in last 10 seconds from INS_UPDATES
            if odo_status != 1:
                if (current_time -
                        self.solution_view.last_odo_update_time) < 10:
                    odo_status = 1
            ins_error = (ins_flags >> 4) & 0xF
            if ins_error != 0:
                ins_status_string = ins_error_dict.get(ins_error, "Unk Error")
            else:
                ins_status_string = ins_type_dict.get(ins_type, "unk") + "-"
                ins_status_string += ins_mode_dict.get(ins_mode, "unk")
                if odo_status == 1:
                    ins_status_string += "+Odo"

        # get age of corrections from baseline view
        if self.baseline_view:
            if (self.baseline_view.age_corrections is not None
                    and (current_time -
                         self.baseline_view.last_age_corr_receipt_time) < 1):
                self.age_of_corrections = "{0} s".format(
                    self.baseline_view.age_corrections)
            else:
                self.age_of_corrections = EMPTY_STR

        # populate modes and #sats on status bar
            self.ins_status_string = ins_status_string
            self.rtk_mode = baseline_display_mode
            self.pos_mode = llh_display_mode
            self.num_sats_str = "{}".format(llh_num_sats)

        # --- end of status bar mode determination section ---

        if self.settings_view:  # for auto populating surveyed fields
            self.settings_view.lat = self.solution_view.latitude
            self.settings_view.lon = self.solution_view.longitude
            self.settings_view.alt = self.solution_view.altitude

    def _csv_logging_button_action(self):
        if self.csv_logging and self.baseline_view.logging_b and self.solution_view.logging_p and self.solution_view.logging_v:
            print("Stopped CSV logging")
            self.csv_logging = False
            self.baseline_view.logging_b = False
            self.solution_view.logging_p = False
            self.solution_view.logging_v = False

        else:
            print("Started CSV logging at %s" % self.directory_name)
            self.csv_logging = True
            self.baseline_view.logging_b = True
            self.solution_view.logging_p = True
            self.solution_view.logging_v = True

    def _start_json_logging(self, override_filename=None):
        if override_filename:
            filename = override_filename
        else:
            filename = time.strftime("swift-gnss-%Y%m%d-%H%M%S.sbp.json",
                                     time.localtime())
            filename = os.path.normpath(
                os.path.join(self.directory_name, filename))
        self.logger = s.get_logger(True, filename, self.expand_json)
        self.forwarder = sbpc.Forwarder(self.link, self.logger)
        self.forwarder.start()
        if self.settings_view:
            self.settings_view._settings_read_all()

    def _stop_json_logging(self):
        fwd = self.forwarder
        fwd.stop()
        self.logger.flush()
        self.logger.close()

    def _json_logging_button_action(self):
        if self.first_json_press and self.json_logging:
            print(
                "JSON Logging initiated via CMD line.  Please press button again to stop logging"
            )
        elif self.json_logging:
            self._stop_json_logging()
            self.json_logging = False
            print("Stopped JSON logging")
        else:
            self._start_json_logging()
            self.json_logging = True
        self.first_json_press = False

    def _json_logging_button_fired(self):
        if not os.path.exists(self.directory_name) and not self.json_logging:
            print(
                "The selected logging directory does not exist and will be created."
            )
        self._json_logging_button_action()

    def _csv_logging_button_fired(self):
        if not os.path.exists(self.directory_name) and not self.csv_logging:
            print(
                "The selected logging directory does not exist and will be created."
            )
        self._csv_logging_button_action()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.console_output.close()

    def __init__(self,
                 link,
                 driver,
                 update,
                 log_level_filter,
                 error=False,
                 cnx_desc=None,
                 json_logging=False,
                 show_csv_log=False,
                 log_dirname=None,
                 override_filename=None,
                 log_console=False,
                 connection_info=None,
                 expand_json=False,
                 hide_legend=False):
        self.error = error
        self.cnx_desc = cnx_desc
        self.connection_info = connection_info
        self.show_csv_log = show_csv_log
        self.dev_id = cnx_desc
        self.num_sats_str = EMPTY_STR
        self.mode = ''
        self.ins_status_string = "None"
        self.forwarder = None
        self.age_of_corrections = '--'
        self.expand_json = expand_json
        # if we have passed a logfile, we set our directory to it
        override_filename = override_filename
        self.last_status_update_time = 0
        self.last_driver_bytes_read = 0
        self.driver = driver

        if log_dirname:
            self.directory_name = log_dirname
            if override_filename:
                override_filename = os.path.join(log_dirname,
                                                 override_filename)
        else:
            self.directory_name = swift_path

        # Start swallowing sys.stdout and sys.stderr
        self.console_output = OutputList(tfile=log_console,
                                         outdir=self.directory_name)
        sys.stdout = self.console_output
        self.console_output.write("Console: " + CONSOLE_VERSION +
                                  " starting...")
        if not error:
            sys.stderr = self.console_output

        self.log_level_filter = log_level_filter
        self.console_output.log_level_filter = str_to_log_level(
            log_level_filter)
        try:
            self.link = link
            self.link.add_callback(self.print_message_callback,
                                   SBP_MSG_PRINT_DEP)
            self.link.add_callback(self.log_message_callback, SBP_MSG_LOG)
            self.link.add_callback(self.ext_event_callback, SBP_MSG_EXT_EVENT)
            self.link.add_callback(self.cmd_resp_callback,
                                   SBP_MSG_COMMAND_RESP)
            self.link.add_callback(self.update_on_heartbeat, SBP_MSG_HEARTBEAT)
            self.dep_handler = DeprecatedMessageHandler(link)
            settings_read_finished_functions = []
            self.tracking_view = TrackingView(self.link,
                                              legend_visible=(not hide_legend))
            self.solution_view = SolutionView(self.link,
                                              dirname=self.directory_name)
            self.baseline_view = BaselineView(self.link,
                                              dirname=self.directory_name)
            self.skyplot_view = SkyplotView(self.link, self.tracking_view)
            self.observation_view = ObservationView(
                self.link,
                name='Local',
                relay=False,
                dirname=self.directory_name,
                tracking_view=self.tracking_view)
            self.observation_view_base = ObservationView(
                self.link,
                name='Remote',
                relay=True,
                dirname=self.directory_name)
            self.system_monitor_view = SystemMonitorView(self.link)
            self.update_view = UpdateView(self.link,
                                          download_dir=swift_path,
                                          prompt=update,
                                          connection_info=self.connection_info)
            self.ins_view = INSView(self.link)
            self.mag_view = MagView(self.link)
            self.spectrum_analyzer_view = SpectrumAnalyzerView(self.link)
            settings_read_finished_functions.append(
                self.update_view.compare_versions)
            self.networking_view = SbpRelayView(self.link)
            self.json_logging = json_logging
            self.csv_logging = False
            self.first_json_press = True
            if json_logging:
                self._start_json_logging(override_filename)
                self.json_logging = True
            # we set timer interval to 1200 milliseconds because we expect a heartbeat each second
            self.timer_cancel = call_repeatedly(HEARTBEAT_CHECK_PERIOD_SECONDS,
                                                self.check_heartbeat)

            # Once we have received the settings, update device_serial with
            # the Swift serial number which will be displayed in the window
            # title. This callback will also update the header route as used
            # by the networking view.

            def update_serial():
                mfg_id = None
                try:
                    self.uuid = self.settings_view.settings['system_info'][
                        'uuid'].value
                    mfg_id = self.settings_view.settings['system_info'][
                        'serial_number'].value
                except KeyError:
                    pass
                if mfg_id:
                    self.device_serial = 'PK' + str(mfg_id)

            skip_settings_read = False
            if 'mode' in self.connection_info:
                if self.connection_info['mode'] == 'file':
                    skip_settings_read = True

            settings_read_finished_functions.append(update_serial)
            self.settings_view = SettingsView(self.link,
                                              settings_read_finished_functions,
                                              skip_read=skip_settings_read)
            self.update_view.settings = self.settings_view.settings
            self.python_console_env = {
                'send_message': self.link,
                'link': self.link,
            }
            self.python_console_env.update(
                self.tracking_view.python_console_cmds)
            self.python_console_env.update(
                self.solution_view.python_console_cmds)
            self.python_console_env.update(
                self.baseline_view.python_console_cmds)
            self.python_console_env.update(
                self.skyplot_view.python_console_cmds)
            self.python_console_env.update(
                self.observation_view.python_console_cmds)
            self.python_console_env.update(
                self.networking_view.python_console_cmds)
            self.python_console_env.update(
                self.system_monitor_view.python_console_cmds)
            self.python_console_env.update(
                self.update_view.python_console_cmds)
            self.python_console_env.update(self.ins_view.python_console_cmds)
            self.python_console_env.update(self.mag_view.python_console_cmds)
            self.python_console_env.update(
                self.settings_view.python_console_cmds)
            self.python_console_env.update(
                self.spectrum_analyzer_view.python_console_cmds)

        except:  # noqa
            import traceback
            traceback.print_exc()
            if self.error:
                os._exit(1)
Ejemplo n.º 14
0
class SettingsView(HasTraits):

  settings_yaml = list()

  settings_read_button = SVGButton(
    label='Reload', tooltip='Reload settings from Piksi',
    filename=os.path.join(os.path.dirname(__file__), 'images', 'fontawesome', 'refresh.svg'),
    width=16, height=16
  )

  settings_save_button = SVGButton(
    label='Save to Flash', tooltip='Save settings to Flash',
    filename=os.path.join(os.path.dirname(__file__), 'images', 'fontawesome', 'download.svg'),
    width=16, height=16
  )

  factory_default_button = SVGButton(
    label='Reset to Defaults', tooltip='Reset to Factory Defaults',
    filename=os.path.join(os.path.dirname(__file__), 'images', 'fontawesome', 'exclamation-triangle.svg'),
    width=16, height=16
  )

  settings_list = List(SettingBase)
  selected_setting = Instance(SettingBase)

  traits_view = View(
    HSplit(
      Item('settings_list',
        editor = TabularEditor(
          adapter=SimpleAdapter(),
          editable_labels=False,
          auto_update=True,
          selected='selected_setting'
        ),
        show_label=False,
      ),
      VGroup(
        HGroup(
          Item('settings_read_button', show_label=False),
          Item('settings_save_button', show_label=False),
          Item('factory_default_button', show_label=False),
        ),
        Item('selected_setting', style='custom', show_label=False),
      ),
    )
  )

  def _settings_read_button_fired(self):
    self.enumindex = 0
    self.ordering_counter = 0
    self.link(MsgSettingsReadByIndexReq(index=self.enumindex))

  def _settings_save_button_fired(self):
    self.link(MsgSettingsSave())

  def _factory_default_button_fired(self):
    confirm_prompt = prompt.CallbackPrompt(
                          title="Reset to Factory Defaults?",
                          actions=[prompt.close_button, prompt.reset_button],
                          callback=self.reset_factory_defaults
                         )
    confirm_prompt.text = "This will erase all settings and then reset the device.\n" \
                        + "Are you sure you want to reset to factory defaults?"
    confirm_prompt.run(block=False)

  def reset_factory_defaults(self):
    # Delete settings file
    fio = FileIO(self.link)
    fio.remove('config')
    # Reset the Piksi
    self.link(MsgReset())

  ##Callbacks for receiving messages

  def settings_display_setup(self):
    self.settings_list = []

    sections = sorted(self.settings.keys())

    for sec in sections:
      this_section = []
      for name, setting in sorted(self.settings[sec].iteritems(),
        key=lambda (n, s): s.ordering):
        if not (self.hide_expert and setting.expert):
          this_section.append(setting)
      if this_section:
        self.settings_list.append(SectionHeading(sec))
        self.settings_list += this_section
    # call read_finished_functions as needed
    for cb in self.read_finished_functions:
      if self.gui_mode:
        GUI.invoke_later(cb)
      else:
        cb()
    return



  def settings_read_by_index_done_callback(self, sbp_msg, **metadata):
    self.settings_display_setup()
    return

  def settings_read_by_index_callback(self, sbp_msg, **metadata):
    if not sbp_msg.payload:
      # Settings output from Piksi is terminated by an empty message.
      # Bundle up our list and display it.
      self.settings_display_setup()
      return

    section, setting, value, format_type = sbp_msg.payload[2:].split('\0')[:4]
    self.ordering_counter += 1

    if format_type == '':
      format_type = None
    else:
      setting_type, setting_format = format_type.split(':')

    if not self.settings.has_key(section):
      self.settings[section] = {}

    if format_type is None:
      # Plain old setting, no format information
      self.settings[section][setting] = Setting(setting, section, value,
                                                ordering=self.ordering_counter,
                                                settings=self
                                               )
    else:
      if setting_type == 'enum':
        enum_values = setting_format.split(',')
        self.settings[section][setting] = EnumSetting(setting, section, value,
                                                      ordering=self.ordering_counter,
                                                      values=enum_values,
                                                      settings=self
                                                     )
      else:
        # Unknown type, just treat is as a string
        self.settings[section][setting] = Setting(setting, section, value,
                                                  settings=self
                                                 )

    self.enumindex += 1
    self.link(MsgSettingsReadByIndexReq(index=self.enumindex))

  def piksi_startup_callback(self, sbp_msg, **metadata):
    self._settings_read_button_fired()

  def set(self, section, name, value):
    self.link(MsgSettingsWrite(setting='%s\0%s\0%s\0' % (section, name, value)))

  def cleanup(self):
    """ Remove callbacks from serial link. """
    self.link.remove_callback(self.piksi_startup_callback, SBP_MSG_STARTUP)
    self.link.remove_callback(self.settings_read_by_index_callback, SBP_MSG_SETTINGS_READ_BY_INDEX_REQ)
    self.link.remove_callback(self.settings_read_by_index_callback, SBP_MSG_SETTINGS_READ_BY_INDEX_RESP)
    self.link.remove_callback(self.settings_read_by_index_done_callback, SBP_MSG_SETTINGS_READ_BY_INDEX_DONE)

  def __enter__(self):
    return self

  def __exit__(self, *args):
    self.cleanup()

  def __init__(self, link, read_finished_functions=[], name_of_yaml_file="settings.yaml", hide_expert=False, gui_mode=True):
    super(SettingsView, self).__init__()

    self.hide_expert = hide_expert
    self.gui_mode = gui_mode
    self.enumindex = 0
    self.settings = {}
    self.link = link
    self.link.add_callback(self.piksi_startup_callback, SBP_MSG_STARTUP)
    self.link.add_callback(self.settings_read_by_index_callback, SBP_MSG_SETTINGS_READ_BY_INDEX_REQ)
    self.link.add_callback(self.settings_read_by_index_callback, SBP_MSG_SETTINGS_READ_BY_INDEX_RESP)
    self.link.add_callback(self.settings_read_by_index_done_callback, SBP_MSG_SETTINGS_READ_BY_INDEX_DONE)

    # Read in yaml file for setting metadata
    self.settings_yaml = SettingsList(name_of_yaml_file)

    # List of functions to be executed after all settings are read.
    # No support for arguments currently.
    self.read_finished_functions = read_finished_functions

    self.setting_detail = SettingBase()

    self._settings_read_button_fired()

    self.python_console_cmds = {
      'settings': self
    }
Ejemplo n.º 15
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())
Ejemplo n.º 16
0
class SettingsView(HasTraits):
    """Traits-defined console settings view.

    link : object
      Serial driver object.
    read_finished_functions : list
      Callbacks to call on finishing a settings read.
    name_of_yaml_file : str
      Settings to read from (defaults to settings.yaml)
    expert : bool
      Show expert settings (defaults to False)
    gui_mode : bool
      ??? (defaults to True)
    skip : bool
      Skip reading of the settings (defaults to False). Intended for
      use when reading from network connections.
    """
    show_auto_survey = Bool(False)
    settings_yaml = list()
    auto_survey = SVGButton(
        label='Auto\nSurvey',
        tooltip='Auto populate surveyed lat, lon and alt fields',
        filename='',
        width=20,
        height=20)
    settings_read_button = SVGButton(
        tooltip='Reload settings from Piksi',
        filename=resource_filename('console/images/fontawesome/refresh.svg'),
        allow_clipping=False,
        width_padding=4,
        height_padding=4)
    settings_save_button = SVGButton(
        label='Save to\nDevice',
        tooltip='Save settings to persistent storage on device.',
        filename=resource_filename('console/images/fontawesome/floppy-o.svg'),
        width=20,
        height=20)
    settings_export_to_file_button = SVGButton(
        label='Export to\nFile',
        tooltip='Export settings from device to a file on this PC.',
        filename=resource_filename('console/images/fontawesome/download.svg'),
        width=20,
        height=20)
    settings_import_from_file_button = SVGButton(
        label='Import\nfrom File',
        tooltip='Import settings to device from a file on this PC.',
        filename=resource_filename('console/images/fontawesome/upload.svg'),
        width=20,
        height=20)
    factory_default_button = SVGButton(
        label='Reset to\nDefaults',
        tooltip='Reset to Factory Defaults',
        filename=resource_filename(
            'console/images/fontawesome/exclamation-triangle.svg'),
        width=20,
        height=20)
    settings_list = List(SettingBase)
    expert = Bool()
    selected_setting = Instance(SettingBase)
    traits_view = View(
        HSplit(
            Item(
                'settings_list',
                editor=TabularEditor(adapter=SimpleAdapter(),
                                     editable_labels=False,
                                     auto_update=True,
                                     editable=False,
                                     selected='selected_setting'),
                show_label=False,
            ),
            VGroup(
                HGroup(
                    Item('settings_save_button', show_label=False),
                    Item('settings_export_to_file_button', show_label=False),
                    Item('settings_import_from_file_button', show_label=False),
                    Item('factory_default_button', show_label=False),
                    Item('auto_survey',
                         show_label=False,
                         visible_when='show_auto_survey'),
                ),
                HGroup(
                    Item('settings_read_button',
                         show_label=False,
                         padding=0,
                         height=-20,
                         width=-20),
                    Item('', label="Refresh settings\nfrom device", padding=0),
                    Item('expert', show_label=False),
                    Item('', label="Show Advanced\nSettings", padding=0),
                ),
                Item('selected_setting', style='custom', show_label=False),
            ),
        ))

    def _selected_setting_changed(self):
        if self.selected_setting:
            if (self.selected_setting.name in [
                    'surveyed_position', 'broadcast', 'surveyed_lat',
                    'surveyed_lon', 'surveyed_alt'
            ] and self.lat != 0 and self.lon != 0):
                self.show_auto_survey = True
            else:
                self.show_auto_survey = False

    def _expert_changed(self, info):
        try:
            self.settings_display_setup(do_read_finished=False)
        except AttributeError:
            pass

    def update_required_smoothpose_settings(self):
        """
        Update any recommended settings for smoothpose
        """
        list = self._determine_smoothpose_recommended_settings()
        for each_setting in list:
            self.settings[each_setting.section][
                each_setting.name].value = each_setting.rec_value

    def _determine_smoothpose_recommended_settings(self):
        """
        Returns a list of settings that should change for smoothpose
        """

        recommended_settings = {
            'imu_raw_output': Setting('imu_raw_output', 'imu', 'True'),
            'gyro_range': Setting('gyro_range', 'imu', '1000'),
            'acc_range': Setting('acc_range', 'imu', '8g'),
            'imu_rate': Setting('imu_rate', 'imu', '100')
        }
        settings_wrong_list = []
        for each_key in recommended_settings.keys():
            if recommended_settings[each_key].value != self.settings['imu'][
                    each_key].value:
                self.settings['imu'][
                    each_key].rec_value = recommended_settings[each_key].value
                settings_wrong_list.append(self.settings['imu'][each_key])
        return settings_wrong_list

    def _save_and_reset(self):
        self._settings_save_button_fired()
        self.link(MsgReset(flags=0))

    def _display_ins_settings_hint(self):
        """
        Display helpful hint messages to help a user set up inertial product
        """
        settings_list = self._determine_smoothpose_recommended_settings()
        if len(settings_list) > 0:
            confirm_prompt = prompt.CallbackPrompt(
                title="Update Recommended Inertial Navigation Settings?",
                actions=[prompt.close_button, prompt.update_button],
                callback=self.update_required_smoothpose_settings)
            confirm_prompt.settings_list = settings_list
            confirm_prompt.text = "\n\n" \
                                  "    In order to enable INS output, it is necessary to enable and configure the imu.    \n" \
                                  "    Your current settings indicate that your imu raw ouptut is disabled and/or improperly configured.    \n\n" \
                                  "    Choose \"Update\" to allow the console to change the following settings on your device to help enable INS output.    \n" \
                                  "    Choose \"Close\" to ignore this recommendation and not update any device settings.    \n\n"
            # from objbrowser import browse
            # browse(confirm_prompt)
            confirm_prompt.view.content.content[0].content.append(
                Item(
                    "settings_list",
                    editor=TabularEditor(adapter=SimpleChangeAdapter(),
                                         editable_labels=False,
                                         auto_update=True,
                                         editable=False),
                    # Only make pop-up as tall as necessary
                    height=-(len(confirm_prompt.settings_list) * 25 + 40),
                    label='Recommended Settings'))
            confirm_prompt.run(block=False)
            while (confirm_prompt.thread.is_alive()):
                # Wait until first popup is closed before opening second popup
                time.sleep(1)
        # even if we didn't need to change any settings, we still have to save settings and restart
        self.display_ins_output_hint()

    def display_ins_output_hint(self):
        confirm_prompt2 = prompt.CallbackPrompt(
            title="Restart Device?",
            actions=[prompt.close_button, prompt.ok_button],
            callback=self._save_and_reset)

        confirm_prompt2.text = "\n\n" \
                               "    In order for the \"Ins Output Mode\" setting to take effect, it is necessary to save the    \n" \
                               "    current settings to device flash and then power cycle your device.    \n\n" \
                               "    Choose \"OK\" to immediately save settings to device flash and send the software reset command.    \n" \
                               "    The software reset will temporarily interrupt the console's connection to the device but it   \n" \
                               "    will recover on its own.    \n\n"

        confirm_prompt2.run(block=False)

    def _send_pending_settings_by_index(self):
        for eachindex in self.pending_settings:
            self.link(MsgSettingsReadByIndexReq(index=eachindex))

    def _restart_retry_thread(self):
        if self.retry_pending_read_index_thread:
            self.retry_pending_read_index_thread.stop()
        self.retry_pending_read_index_thread = TimedDelayStoppableThread(
            SETTINGS_RETRY_TIMEOUT,
            target=self._send_pending_settings_by_index,
            args=[])
        self.retry_pending_read_index_thread.start()

    def _settings_read_by_index(self):
        self.enumindex = 0  # next index to ask for
        self.pending_settings = []  # list of settings idices we've asked for
        self.ordering_counter = 0  # helps make deterministic order of settings
        self.setup_pending = True  # guards against receipt of multiple "done" msgs
        # queue up BATCH_WINDOW settings indices to read
        self.pending_settings = range(self.enumindex,
                                      self.enumindex + BATCH_WINDOW)
        self.enumindex += BATCH_WINDOW
        self._send_pending_settings_by_index()
        # start a thread that will resend any read indexes that haven't come
        self._restart_retry_thread()

    def _settings_read_button_fired(self):
        self.settings.clear()
        self._settings_read_by_index()

    def _settings_save_button_fired(self):
        self.link(MsgSettingsSave())

    def _factory_default_button_fired(self):
        confirm_prompt = prompt.CallbackPrompt(
            title="Reset to Factory Defaults?",
            actions=[prompt.close_button, prompt.reset_button],
            callback=self.reset_factory_defaults)
        confirm_prompt.text = "This will erase all settings and then reset the device.\n" \
                              + "Are you sure you want to reset to factory defaults?"
        confirm_prompt.run(block=False)

    def reset_factory_defaults(self):
        # Reset the Piksi, with flag set to restore default settings
        self.link(MsgReset(flags=1))

    def _settings_export_to_file_button_fired(self):
        """Exports current gui settings to INI file. Prompts user for file name and location.
        Should handle all expected error cases.  Defaults to file "config.ini" in the swift_path
        """
        # Prompt user for location and name of file
        file = FileDialog(action='save as',
                          default_directory=swift_path,
                          default_filename='config.ini',
                          wildcard='*.ini')
        is_ok = file.open()
        if is_ok == OK:
            print('Exporting settings to local path {0}'.format(file.path))
            # copy settings so we can modify dict in place to write for configparser
            settings_out = {}
            # iterate over nested dict and set inner value to a bare string rather than dict
            for section in self.settings:
                settings_out[section] = {}
                for setting, inner_dict in self.settings[section].iteritems():
                    settings_out[section][setting] = str(inner_dict.value)
            # write out with config parser
            parser = configparser.RawConfigParser()
            # the optionxform is needed to handle case sensitive settings
            parser.optionxform = str
            parser.read_dict(settings_out)
            # write to ini file
            try:
                with open(file.path, "w") as f:
                    parser.write(f)
            except IOError as e:
                print('Unable to export settings to file due to IOError: {}'.
                      format(e))
        else:  # No error message because user pressed cancel and didn't choose a file
            pass

    def _settings_import_from_file_button_fired(self):
        """Imports settings from INI file and sends settings write to device for each entry.
        Prompts user for input file.  Should handle all expected error cases.
        """
        # Prompt user for file
        file = FileDialog(action='open',
                          default_directory=swift_path,
                          default_filename='config.ini',
                          wildcard='*.ini')
        is_ok = file.open()
        if is_ok == OK:  # file chosen successfully
            print('Importing settings from local path {} to device.'.format(
                file.path))
            parser = configparser.ConfigParser()
            # the optionxform is needed to handle case sensitive settings
            parser.optionxform = str
            try:
                with open(file.path, 'r') as f:
                    parser.read_file(f)
            except configparser.ParsingError as e:  # file formatted incorrectly
                print(
                    'Unable to parse ini file due to ParsingError: {}.'.format(
                        e))
                print('Unable to import settings to device.')
                return
            except IOError as e:  # IOError (likely a file permission issue)
                print('Unable to read ini file due to IOError: {}'.format(e))
                print('Unable to import settings to device.')
                return

            # Iterate over each setting and set in the GUI.
            # Use the same mechanism as GUI to do settings write to device

            for section, settings in parser.items():
                this_section = self.settings.get(section, None)
                for setting, value in settings.items():
                    if this_section:
                        this_setting = this_section.get(setting, None)
                        if this_setting:
                            this_setting.value = value
                        else:
                            print((
                                "Unable to import settings from file. Setting \"{0}\" in section \"{1}\""
                                " has not been sent from device.").format(
                                    setting, section))
                            return
                    else:
                        print((
                            "Unable to import settings from file."
                            " Setting section \"{0}\" has not been sent from device."
                        ).format(section))
                        return

            # Double check that no settings had a write failure.  All settings should exist if we get to this point.
            a = TimedDelayStoppableThread(
                SETTINGS_REVERT_TIMEOUT + 0.1,
                target=self._wait_for_any_write_failures,
                args=(dict(parser)))
            a.start()
        else:
            pass  # No error message here because user likely pressed cancel when choosing file

    def _auto_survey_fired(self):
        confirm_prompt = prompt.CallbackPrompt(
            title="Auto populate surveyed position?",
            actions=[prompt.close_button, prompt.auto_survey_button],
            callback=self.auto_survey_fn)
        confirm_prompt.text = "\n" \
                              + "This will set the Surveyed Position section to the \n" \
                              + "mean position of the last 1000 position solutions.\n \n" \
                              + "The fields that will be auto-populated are: \n" \
                              + "Surveyed Lat \n" \
                              + "Surveyed Lon \n" \
                              + "Surveyed Alt \n \n" \
                              + "The surveyed position will be an approximate value. \n" \
                              + "This may affect the relative accuracy of Piksi. \n \n" \
                              + "Are you sure you want to auto-populate the Surveyed Position section?"
        confirm_prompt.run(block=False)

    def auto_survey_fn(self):
        lat_value = str(self.lat)
        lon_value = str(self.lon)
        alt_value = str(self.alt)
        self.settings['surveyed_position']['surveyed_lat'].value = lat_value
        self.settings['surveyed_position']['surveyed_lon'].value = lon_value
        self.settings['surveyed_position']['surveyed_alt'].value = alt_value
        self.settings_display_setup(do_read_finished=False)

    def _wait_for_any_write_failures(self, parser):
        """Checks for any settings write failures for which a successful write is expected.
        If no failures have occurred, we prompt the user whether to save settings to the device's flash.

           Args:
                parser (dict): A dict of dicts with setting sections then names for keys
        """
        write_failures = 0
        for section, settings in parser.items():
            for setting, _ in settings.items():
                if self.settings[section][setting].write_failure:
                    write_failures += 1
                    self.settings[section][setting].write_failure = False
        if write_failures == 0:
            print("Successfully imported settings from file.")
            confirm_prompt = prompt.CallbackPrompt(
                title="Save to device flash?",
                actions=[prompt.close_button, prompt.ok_button],
                callback=self._settings_save_button_fired)
            confirm_prompt.text = "\n" \
                                  "  Settings import from file complete.  Click OK to save the settings  \n" \
                                  "  to the device's persistent storage.  \n"
            confirm_prompt.run(block=False)
        else:
            print(
                "Unable to import settings from file: {0} settings write failures occurred."
                .format(write_failures))

    # Callbacks for receiving messages
    def settings_display_setup(self, do_read_finished=True):
        self.settings_list = []
        sections = sorted(self.settings.keys())
        for sec in sections:
            this_section = []
            for name, setting in sorted(self.settings[sec].iteritems(),
                                        key=lambda n_s: n_s[1].ordering):
                if not setting.expert or (self.expert and setting.expert):
                    this_section.append(setting)
            if this_section:
                self.settings_list.append(SectionHeading(sec))
                self.settings_list += this_section
        # call read_finished_functions as needed
        if do_read_finished:
            for cb in self.read_finished_functions:
                if self.gui_mode:
                    GUI.invoke_later(cb)
                else:
                    cb()

    def settings_read_by_index_done_callback(self, sbp_msg, **metadata):
        if self.retry_pending_read_index_thread:
            self.retry_pending_read_index_thread.stop()
        # we should only setup the display once per iteration to avoid races
        if self.setup_pending:
            self.settings_display_setup()
            self.setup_pending = False

    def settings_read_resp_callback(self, sbp_msg, **metadata):
        confirmed_set = True
        settings_list = sbp_msg.setting.split("\0")
        if len(settings_list) <= 3:
            print("Received malformed settings read response {0}".format(
                sbp_msg))
            confirmed_set = False
        try:
            if self.settings[settings_list[0]][
                    settings_list[1]].value != settings_list[2]:
                try:
                    float_val = float(self.settings[settings_list[0]][
                        settings_list[1]].value)
                    float_val2 = float(settings_list[2])
                    if abs(float_val - float_val2) > 0.000001:
                        confirmed_set = False
                except ValueError:
                    confirmed_set = False
            if confirmed_set:
                # If we verify the new values matches our expectation, we cancel the revert thread
                if self.settings[settings_list[0]][
                        settings_list[1]].timed_revert_thread:
                    self.settings[settings_list[0]][
                        settings_list[1]].timed_revert_thread.stop()
                self.settings[settings_list[0]][
                    settings_list[1]].confirmed_set = True
        except KeyError:
            return

    def settings_write_resp_callback(self, sbp_msg, **metadata):
        if sbp_msg.status == 2:
            # Setting was rejected.  This shouldn't happen because we'll only
            # send requests for settings enumerated using read by index.
            return
        settings_list = sbp_msg.setting.split("\0")
        if len(settings_list) <= 3:
            print("Received malformed settings write response {0}".format(
                sbp_msg))
            return
        try:
            setting = self.settings[settings_list[0]][settings_list[1]]
        except KeyError:
            return
        if setting.timed_revert_thread:
            setting.timed_revert_thread.stop()
        if sbp_msg.status == 1:
            # Value was rejected.  Inform the user and revert display to the
            # old value.
            new = setting.value
            old = settings_list[2]
            setting.revert_to_prior_value(setting.name, old, new)
            return
        # Write accepted.  Use confirmed value in display without sending settings write.
        setting._prevent_revert_thread = True
        setting.value = settings_list[2]
        setting._prevent_revert_thread = False
        setting.confirmed_set = True

    def settings_read_by_index_callback(self, sbp_msg, **metadata):
        section, setting, value, format_type = sbp_msg.payload[2:].split(
            '\0')[:4]
        self.ordering_counter += 1
        if format_type == '':
            format_type = None
        else:
            setting_type, setting_format = format_type.split(':')
        if section not in self.settings:
            self.settings[section] = {}
        # setting exists, we won't reinitilize it but rather update existing setting
        dict_setting = self.settings[section].get(setting, False)
        if dict_setting:
            dict_setting._prevent_revert_thread = True
            dict_setting.value = value
            dict_setting._prevent_revert_thread = False
            dict_setting.ordering = self.ordering_counter
            if format_type is not None and setting_type == 'enum':
                enum_values = setting_format.split(',')
                dict_setting.enum_values = enum_values
        else:
            if format_type is None:
                # Plain old setting, no format information
                self.settings[section][setting] = Setting(
                    setting,
                    section,
                    value,
                    ordering=self.ordering_counter,
                    settings=self)
            else:
                if setting_type == 'enum':
                    enum_values = setting_format.split(',')
                    self.settings[section][setting] = EnumSetting(
                        setting,
                        section,
                        value,
                        enum_values,
                        ordering=self.ordering_counter,
                        settings=self)
                else:
                    # Unknown type, just treat is as a string
                    self.settings[section][setting] = Setting(
                        setting,
                        section,
                        value,
                        settings=self,
                        ordering=self.ordering_counter)
        # remove index from list of pending items
        if sbp_msg.index in self.pending_settings:
            self.pending_settings.remove(sbp_msg.index)
        if len(self.pending_settings) == 0:
            self.pending_settings = range(self.enumindex,
                                          self.enumindex + BATCH_WINDOW)
            self.enumindex += BATCH_WINDOW
            self._send_pending_settings_by_index()
            self._restart_retry_thread()

    def piksi_startup_callback(self, sbp_msg, **metadata):
        self._settings_read_by_index()

    def set(self, section, name, value):
        self.link(
            MsgSettingsWrite(setting='%s\0%s\0%s\0' % (section, name, value)))

    def cleanup(self):
        """ Remove callbacks from serial link. """
        self.link.remove_callback(self.piksi_startup_callback, SBP_MSG_STARTUP)
        self.link.remove_callback(self.settings_read_by_index_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_REQ)
        self.link.remove_callback(self.settings_read_by_index_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_RESP)
        self.link.remove_callback(self.settings_read_by_index_done_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_DONE)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.cleanup()

    def __init__(self,
                 link,
                 read_finished_functions=[],
                 name_of_yaml_file="settings.yaml",
                 expert=False,
                 gui_mode=True,
                 skip=False):
        super(SettingsView, self).__init__()
        self.ordering_counter = 0
        self.expert = expert
        self.show_auto_survey = False
        self.gui_mode = gui_mode
        self.enumindex = 0
        self.settings = {}
        self.link = link
        self.link.add_callback(self.piksi_startup_callback, SBP_MSG_STARTUP)
        self.link.add_callback(self.settings_read_by_index_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_REQ)
        self.link.add_callback(self.settings_read_by_index_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_RESP)
        self.link.add_callback(self.settings_read_by_index_done_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_DONE)
        self.link.add_callback(self.settings_read_resp_callback,
                               SBP_MSG_SETTINGS_READ_RESP)
        self.link.add_callback(self.settings_write_resp_callback,
                               SBP_MSG_SETTINGS_WRITE_RESP)
        # Read in yaml file for setting metadata
        self.settings_yaml = SettingsList(name_of_yaml_file)
        # List of functions to be executed after all settings are read.
        # No support for arguments currently.
        self.read_finished_functions = read_finished_functions
        self.setting_detail = SettingBase()
        self.pending_settings = []
        self.retry_pending_read_index_thread = None
        self.setup_pending = False
        if not skip:
            try:
                self._settings_read_by_index()
            except IOError:
                print(
                    "IOError in settings_view startup call of _settings_read_by_index."
                )
                print("Verify that write permissions exist on the port.")
        self.python_console_cmds = {'settings': self}
Ejemplo n.º 17
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)
    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.set_data('cur_lat_spp', [])
        self.plot_data.set_data('cur_lng_spp', [])
        self.plot_data.set_data('cur_alt_spp', [])
        self.plot_data.set_data('cur_lat_dgnss', [])
        self.plot_data.set_data('cur_lng_dgnss', [])
        self.plot_data.set_data('cur_alt_dgnss', [])
        self.plot_data.set_data('cur_lat_float', [])
        self.plot_data.set_data('cur_lng_float', [])
        self.plot_data.set_data('cur_alt_float', [])
        self.plot_data.set_data('cur_lat_fixed', [])
        self.plot_data.set_data('cur_lng_fixed', [])
        self.plot_data.set_data('cur_alt_fixed', [])
        self.plot_data.set_data('cur_lat_sbas', [])
        self.plot_data.set_data('cur_lng_sbas', [])
        self.plot_data.set_data('cur_alt_sbas', [])

    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', [])

    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 _pos_llh_callback(self, sbp_msg, **metadata):
        # Updating an ArrayPlotData isn't thread safe (see chaco issue #9), so
        # actually perform the update in the UI thread.
        if self.running:
            GUI.invoke_later(self.pos_llh_callback, sbp_msg)

    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)
        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(('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.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:]

    def solution_draw(self):
        if self.running:
            GUI.invoke_later(self._solution_draw)

    def _solution_draw(self):
        spp_indexer, dgnss_indexer, float_indexer, fixed_indexer, sbas_indexer = None, None, None, None, None
        self._clear_history()
        soln = self.last_soln
        if np.any(self.modes):
            if self.display_units == "meters":
                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(soln.lat)
                sf = (self.meters_per_lat, self.meters_per_lon)
                self.plot.value_axis.title = 'Latitude (meters)'
                self.plot.index_axis.title = 'Longitude (meters)'
            else:
                offset = (0, 0, 0)
                sf = (1, 1)
                self.plot.value_axis.title = 'Latitude (degrees)'
                self.plot.index_axis.title = 'Longitude (degrees)'
            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)

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

                # update our "current solution" icon
            if self.last_pos_mode == SPP_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_spp',
                                        [(soln.lat - offset[0]) * sf[0]])
                self.plot_data.set_data('cur_lng_spp',
                                        [(soln.lon - offset[1]) * sf[1]])
            elif self.last_pos_mode == DGNSS_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_dgnss',
                                        [(soln.lat - offset[0]) * sf[0]])
                self.plot_data.set_data('cur_lng_dgnss',
                                        [(soln.lon - offset[1]) * sf[1]])
            elif self.last_pos_mode == FLOAT_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_float',
                                        [(soln.lat - offset[0]) * sf[0]])
                self.plot_data.set_data('cur_lng_float',
                                        [(soln.lon - offset[1]) * sf[1]])
            elif self.last_pos_mode == FIXED_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_fixed',
                                        [(soln.lat - offset[0]) * sf[0]])
                self.plot_data.set_data('cur_lng_fixed',
                                        [(soln.lon - offset[1]) * sf[1]])
            elif self.last_pos_mode == SBAS_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_sbas',
                                        [(soln.lat - offset[0]) * sf[0]])
                self.plot_data.set_data('cur_lng_sbas',
                                        [(soln.lon - offset[1]) * 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 - offset[1]) * sf[1] - d,
                    (soln.lon - offset[1]) * sf[1] + d)
                d = (self.plot.value_range.high -
                     self.plot.value_range.low) / 2.
                self.plot.value_range.set_bounds(
                    (soln.lat - offset[0]) * sf[0] - d,
                    (soln.lat - offset[0]) * sf[0] + d)
            if self.zoomall:
                plot_square_axes(self.plot,
                                 ('lng_spp', 'lng_dgnss', 'lng_float',
                                  'lng_fixed', 'lng_sbas'),
                                 ('lat_spp', 'lat_dgnss', 'lat_float',
                                  'lat_fixed', 'lat_sbas'))

    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.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.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=[])
        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='scatter',
                       name='',
                       color=color_dict[SBAS_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)
        plot_labels = ['SPP', 'SBAS', 'DGPS', 'RTK float', 'RTK fixed']
        plots_legend = dict(
            zip(plot_labels, [spp, sbas, dgnss, rtkfloat, rtkfix]))
        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)
        call_repeatedly(0.2, self.solution_draw)

        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,
        }
Ejemplo n.º 18
0
class SwiftConsole(HasTraits):
    """Traits-defined Swift Console.

    link : object
      Serial driver
    update : bool
      Update the firmware
    log_level_filter : str
      Syslog string, one of "ERROR", "WARNING", "INFO", "DEBUG".
    skip_settings : bool
      Don't read the device settings. Set to False when the console is reading
      from a network connection only.

    """

    link = Instance(sbpc.Handler)
    console_output = Instance(OutputList())
    python_console_env = Dict
    device_serial = Str('')
    dev_id = Str('')
    tracking_view = Instance(TrackingView)
    solution_view = Instance(SolutionView)
    baseline_view = Instance(BaselineView)
    observation_view = Instance(ObservationView)
    networking_view = Instance(SbpRelayView)
    observation_view_base = Instance(ObservationView)
    system_monitor_view = Instance(SystemMonitorView)
    settings_view = Instance(SettingsView)
    update_view = Instance(UpdateView)
    imu_view = Instance(IMUView)
    mag_view = Instance(MagView)
    spectrum_analyzer_view = Instance(SpectrumAnalyzerView)
    skylark_view = Instance(SkylarkView)
    log_level_filter = Enum(list(SYSLOG_LEVELS.itervalues()))
    """"
  mode : baseline and solution view - SPP, Fixed or Float
  num_sat : baseline and solution view - number of satellites
  port : which port is Swift Device is connected to
  directory_name : location of logged files
  json_logging : enable JSON logging
  csv_logging : enable CSV logging

  """

    mode = Str('')
    num_sats = Int(0)
    cnx_desc = Str('')
    latency = Str('')
    uuid = Str('')
    directory_name = Directory
    json_logging = Bool(True)
    csv_logging = Bool(False)
    cnx_icon = Str('')
    heartbeat_count = Int()
    last_timer_heartbeat = Int()
    solid_connection = Bool(False)

    csv_logging_button = SVGButton(
        toggle=True,
        label='CSV log',
        tooltip='start CSV logging',
        toggle_tooltip='stop CSV logging',
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        orientation='vertical',
        width=2,
        height=2,
    )
    json_logging_button = SVGButton(
        toggle=True,
        label='JSON log',
        tooltip='start JSON logging',
        toggle_tooltip='stop JSON logging',
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        orientation='vertical',
        width=2,
        height=2,
    )
    paused_button = SVGButton(
        label='',
        tooltip='Pause console update',
        toggle_tooltip='Resume console update',
        toggle=True,
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        width=8,
        height=8)
    clear_button = SVGButton(
        label='',
        tooltip='Clear console buffer',
        filename=resource_filename('console/images/iconic/x.svg'),
        width=8,
        height=8)

    view = View(VSplit(
        Tabbed(Item('tracking_view', style='custom', label='Tracking'),
               Item('solution_view', style='custom', label='Solution'),
               Item('baseline_view', style='custom', label='Baseline'),
               VSplit(
                   Item('observation_view', style='custom', show_label=False),
                   Item('observation_view_base',
                        style='custom',
                        show_label=False),
                   label='Observations',
               ),
               Item('settings_view', style='custom', label='Settings'),
               Item('update_view', style='custom', label='Update'),
               Tabbed(Item('system_monitor_view',
                           style='custom',
                           label='System Monitor'),
                      Item('imu_view', style='custom', label='IMU'),
                      Item('mag_view', style='custom', label='Magnetometer'),
                      Item('networking_view',
                           label='Networking',
                           style='custom',
                           show_label=False),
                      Item('spectrum_analyzer_view',
                           label='Spectrum Analyzer',
                           style='custom'),
                      label='Advanced',
                      show_labels=False),
               Item('skylark_view', style='custom', label='Skylark'),
               show_labels=False),
        VGroup(
            VGroup(
                HGroup(
                    Spring(width=4, springy=False),
                    Item('paused_button',
                         show_label=False,
                         padding=0,
                         width=8,
                         height=8),
                    Item('clear_button', show_label=False, width=8, height=8),
                    Item('', label='Console Log', emphasized=True),
                    Item('csv_logging_button',
                         emphasized=True,
                         show_label=False,
                         width=12,
                         height=-30,
                         padding=0),
                    Item('json_logging_button',
                         emphasized=True,
                         show_label=False,
                         width=12,
                         height=-30,
                         padding=0),
                    Item(
                        'directory_name',
                        show_label=False,
                        springy=True,
                        tooltip=
                        'Choose location for file logs. Default is home/SwiftNav.',
                        height=-25,
                        enabled_when='not(json_logging or csv_logging)',
                        editor_args={'auto_set': True}),
                    UItem(
                        'log_level_filter',
                        style='simple',
                        padding=0,
                        height=8,
                        show_label=True,
                        tooltip=
                        'Show log levels up to and including the selected level of severity.\nThe CONSOLE log level is always visible.'
                    ),
                ),
                Item('console_output',
                     style='custom',
                     editor=InstanceEditor(),
                     height=125,
                     show_label=False,
                     full_size=True),
            ),
            HGroup(
                Spring(width=4, springy=False),
                Item('',
                     label='Interface:',
                     emphasized=True,
                     tooltip='Interface for communicating with Swift device'),
                Item('cnx_desc', show_label=False, style='readonly'),
                Item('',
                     label='FIX TYPE:',
                     emphasized=True,
                     tooltip='Device Mode: SPS, Float RTK, Fixed RTK'),
                Item('mode', show_label=False, style='readonly'),
                Item('',
                     label='#Sats:',
                     emphasized=True,
                     tooltip='Number of satellites used in solution'),
                Item('num_sats', padding=2, show_label=False,
                     style='readonly'),
                Item('',
                     label='Base Latency:',
                     emphasized=True,
                     tooltip='Corrections latency (-1 means no corrections)'),
                Item('latency', padding=2, show_label=False, style='readonly'),
                Item('',
                     label='Device UUID:',
                     emphasized=True,
                     tooltip='Universally Unique Device Identifier (UUID)'),
                Item('uuid',
                     padding=2,
                     show_label=False,
                     style='readonly',
                     width=6),
                Spring(springy=True),
                Item('cnx_icon',
                     show_label=False,
                     padding=0,
                     width=8,
                     height=8,
                     visible_when='solid_connection',
                     springy=False,
                     editor=ImageEditor(
                         allow_clipping=False,
                         image=ImageResource(
                             resource_filename(
                                 'console/images/iconic/arrows_blue.png')))),
                Item('cnx_icon',
                     show_label=False,
                     padding=0,
                     width=8,
                     height=8,
                     visible_when='not solid_connection',
                     springy=False,
                     editor=ImageEditor(
                         allow_clipping=False,
                         image=ImageResource(
                             resource_filename(
                                 'console/images/iconic/arrows_grey.png')))),
                Spring(width=4, height=-2, springy=False),
            ),
            Spring(height=1, springy=False),
        ),
    ),
                icon=icon,
                resizable=True,
                width=800,
                height=600,
                handler=ConsoleHandler(),
                title=CONSOLE_TITLE)

    def print_message_callback(self, sbp_msg, **metadata):
        try:
            encoded = sbp_msg.payload.encode('ascii', 'ignore')
            for eachline in reversed(encoded.split('\n')):
                self.console_output.write_level(
                    eachline, str_to_log_level(eachline.split(':')[0]))
        except UnicodeDecodeError:
            print("Critical Error encoding the serial stream as ascii.")

    def log_message_callback(self, sbp_msg, **metadata):
        try:
            encoded = sbp_msg.text.encode('ascii', 'ignore')
            for eachline in reversed(encoded.split('\n')):
                self.console_output.write_level(eachline, sbp_msg.level)
        except UnicodeDecodeError:
            print("Critical Error encoding the serial stream as ascii.")

    def ext_event_callback(self, sbp_msg, **metadata):
        e = MsgExtEvent(sbp_msg)
        print(
            'External event: %s edge on pin %d at wn=%d, tow=%d, time qual=%s'
            % ("Rising" if
               (e.flags &
                (1 << 0)) else "Falling", e.pin, e.wn, e.tow, "good" if
               (e.flags & (1 << 1)) else "unknown"))

    def cmd_resp_callback(self, sbp_msg, **metadata):
        r = MsgCommandResp(sbp_msg)
        print("Received a command response message with code {0}".format(
            r.code))

    def _paused_button_fired(self):
        self.console_output.paused = not self.console_output.paused

    def _log_level_filter_changed(self):
        """
        Takes log level enum and translates into the mapped integer.
        Integer stores the current filter value inside OutputList.
        """
        self.console_output.log_level_filter = str_to_log_level(
            self.log_level_filter)

    def _clear_button_fired(self):
        self.console_output.clear()

    def _directory_name_changed(self):
        if self.baseline_view and self.solution_view:
            self.baseline_view.directory_name_b = self.directory_name
            self.solution_view.directory_name_p = self.directory_name
            self.solution_view.directory_name_v = self.directory_name
        if self.observation_view and self.observation_view_base:
            self.observation_view.dirname = self.directory_name
            self.observation_view_base.dirname = self.directory_name

    def check_heartbeat(self):
        # if our heartbeat hasn't changed since the last timer interval the connection must have dropped
        if self.heartbeat_count == self.last_timer_heartbeat:
            self.solid_connection = False
        else:
            self.solid_connection = True
        self.last_timer_heartbeat = self.heartbeat_count

    def update_on_heartbeat(self, sbp_msg, **metadata):
        self.heartbeat_count += 1
        # First initialize the state to nothing, if we can't update, it will be none
        temp_mode = "None"
        temp_num_sats = 0
        view = None
        if self.baseline_view and self.solution_view:
            # If we have a recent baseline update, we use the baseline info
            if time.time() - self.baseline_view.last_btime_update < 10:
                view = self.baseline_view
            # Otherwise, if we have a recent SPP update, we use the SPP
            elif time.time() - self.solution_view.last_stime_update < 10:
                view = self.solution_view
            if view:
                if view.last_soln:
                    # if all is well we update state
                    temp_mode = mode_dict.get(get_mode(view.last_soln),
                                              EMPTY_STR)
                    temp_num_sats = view.last_soln.n_sats

        self.mode = temp_mode
        self.num_sats = temp_num_sats

        if self.settings_view:  # for auto populating surveyed fields
            self.settings_view.lat = self.solution_view.latitude
            self.settings_view.lon = self.solution_view.longitude
            self.settings_view.alt = self.solution_view.altitude
        if self.system_monitor_view:
            if self.system_monitor_view.msg_obs_window_latency_ms != -1:
                self.latency = "{0} ms".format(
                    self.system_monitor_view.msg_obs_window_latency_ms)
            else:
                self.latency = EMPTY_STR

    def _csv_logging_button_action(self):
        if self.csv_logging and self.baseline_view.logging_b and self.solution_view.logging_p and self.solution_view.logging_v:
            print("Stopped CSV logging")
            self.csv_logging = False
            self.baseline_view.logging_b = False
            self.solution_view.logging_p = False
            self.solution_view.logging_v = False

        else:
            print("Started CSV logging at %s" % self.directory_name)
            self.csv_logging = True
            self.baseline_view.logging_b = True
            self.solution_view.logging_p = True
            self.solution_view.logging_v = True

    def _start_json_logging(self, override_filename=None):
        if override_filename:
            filename = override_filename
        else:
            filename = time.strftime("swift-gnss-%Y%m%d-%H%M%S.sbp.json")
            filename = os.path.normpath(
                os.path.join(self.directory_name, filename))
        self.logger = s.get_logger(True, filename, self.expand_json)
        self.forwarder = sbpc.Forwarder(self.link, self.logger)
        self.forwarder.start()
        if self.settings_view:
            self.settings_view._settings_read_button_fired()

    def _stop_json_logging(self):
        fwd = self.forwarder
        fwd.stop()
        self.logger.flush()
        self.logger.close()

    def _json_logging_button_action(self):
        if self.first_json_press and self.json_logging:
            print(
                "JSON Logging initiated via CMD line.  Please press button again to stop logging"
            )
        elif self.json_logging:
            self._stop_json_logging()
            self.json_logging = False
            print("Stopped JSON logging")
        else:
            self._start_json_logging()
            self.json_logging = True
        self.first_json_press = False

    def _json_logging_button_fired(self):
        if not os.path.exists(self.directory_name) and not self.json_logging:
            confirm_prompt = CallbackPrompt(
                title="Logging directory creation",
                actions=[ok_button],
                callback=self._json_logging_button_action)
            confirm_prompt.text = "\nThe selected logging directory does not exist and will be created."
            confirm_prompt.run(block=False)
        else:
            self._json_logging_button_action()

    def _csv_logging_button_fired(self):
        if not os.path.exists(self.directory_name) and not self.csv_logging:
            confirm_prompt = CallbackPrompt(
                title="Logging directory creation",
                actions=[ok_button],
                callback=self._csv_logging_button_action)
            confirm_prompt.text = "\nThe selected logging directory does not exist and will be created."
            confirm_prompt.run(block=False)
        else:
            self._csv_logging_button_action()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.console_output.close()

    def __init__(self,
                 link,
                 update,
                 log_level_filter,
                 skip_settings=False,
                 error=False,
                 cnx_desc=None,
                 json_logging=False,
                 log_dirname=None,
                 override_filename=None,
                 log_console=False,
                 networking=None,
                 connection_info=None,
                 expand_json=False):
        self.error = error
        self.cnx_desc = cnx_desc
        self.connection_info = connection_info
        self.dev_id = cnx_desc
        self.num_sats = 0
        self.mode = ''
        self.forwarder = None
        self.latency = '--'
        self.expand_json = expand_json
        # if we have passed a logfile, we set our directory to it
        override_filename = override_filename

        if log_dirname:
            self.directory_name = log_dirname
            if override_filename:
                override_filename = os.path.join(log_dirname,
                                                 override_filename)
        else:
            self.directory_name = swift_path

        # Start swallowing sys.stdout and sys.stderr
        self.console_output = OutputList(tfile=log_console,
                                         outdir=self.directory_name)
        sys.stdout = self.console_output
        self.console_output.write("Console: " + CONSOLE_VERSION +
                                  " starting...")
        if not error:
            sys.stderr = self.console_output

        self.log_level_filter = log_level_filter
        self.console_output.log_level_filter = str_to_log_level(
            log_level_filter)
        try:
            self.link = link
            self.link.add_callback(self.print_message_callback,
                                   SBP_MSG_PRINT_DEP)
            self.link.add_callback(self.log_message_callback, SBP_MSG_LOG)
            self.link.add_callback(self.ext_event_callback, SBP_MSG_EXT_EVENT)
            self.link.add_callback(self.cmd_resp_callback,
                                   SBP_MSG_COMMAND_RESP)
            self.link.add_callback(self.update_on_heartbeat, SBP_MSG_HEARTBEAT)
            self.dep_handler = DeprecatedMessageHandler(link)
            settings_read_finished_functions = []
            self.tracking_view = TrackingView(self.link)
            self.solution_view = SolutionView(self.link,
                                              dirname=self.directory_name)
            self.baseline_view = BaselineView(self.link,
                                              dirname=self.directory_name)
            self.observation_view = ObservationView(
                self.link,
                name='Local',
                relay=False,
                dirname=self.directory_name)
            self.observation_view_base = ObservationView(
                self.link,
                name='Remote',
                relay=True,
                dirname=self.directory_name)
            self.system_monitor_view = SystemMonitorView(self.link)
            self.update_view = UpdateView(self.link,
                                          download_dir=swift_path,
                                          prompt=update,
                                          connection_info=self.connection_info)
            self.imu_view = IMUView(self.link)
            self.mag_view = MagView(self.link)
            self.spectrum_analyzer_view = SpectrumAnalyzerView(self.link)
            settings_read_finished_functions.append(
                self.update_view.compare_versions)
            if networking:
                from ruamel.yaml import YAML
                yaml = YAML(typ='safe')
                try:
                    networking_dict = yaml.load(networking)
                    networking_dict.update({'show_networking': True})
                except yaml.YAMLError:
                    print(
                        "Unable to interpret networking cmdline argument.  It will be ignored."
                    )
                    import traceback
                    print(traceback.format_exc())
                    networking_dict = {'show_networking': True}
            else:
                networking_dict = {}
            networking_dict.update(
                {'whitelist': [SBP_MSG_POS_LLH, SBP_MSG_HEARTBEAT]})
            self.networking_view = SbpRelayView(self.link, **networking_dict)
            self.skylark_view = SkylarkView()
            self.json_logging = json_logging
            self.csv_logging = False
            self.first_json_press = True
            if json_logging:
                self._start_json_logging(override_filename)
                self.json_logging = True
            # we set timer interval to 1200 milliseconds because we expect a heartbeat each second
            self.timer_cancel = call_repeatedly(1.2, self.check_heartbeat)

            # Once we have received the settings, update device_serial with
            # the Swift serial number which will be displayed in the window
            # title. This callback will also update the header route as used
            # by the networking view.

            def update_serial():
                mfg_id = None
                try:
                    self.uuid = self.settings_view.settings['system_info'][
                        'uuid'].value
                    mfg_id = self.settings_view.settings['system_info'][
                        'serial_number'].value
                except KeyError:
                    pass
                if mfg_id:
                    self.device_serial = 'PK' + str(mfg_id)
                self.skylark_view.set_uuid(self.uuid)
                self.networking_view.set_route(uuid=self.uuid,
                                               serial_id=mfg_id)
                if self.networking_view.connect_when_uuid_received:
                    self.networking_view._connect_rover_fired()

            settings_read_finished_functions.append(update_serial)
            self.settings_view = SettingsView(self.link,
                                              settings_read_finished_functions,
                                              skip=skip_settings)
            self.update_view.settings = self.settings_view.settings
            self.python_console_env = {
                'send_message': self.link,
                'link': self.link,
            }
            self.python_console_env.update(
                self.tracking_view.python_console_cmds)
            self.python_console_env.update(
                self.solution_view.python_console_cmds)
            self.python_console_env.update(
                self.baseline_view.python_console_cmds)
            self.python_console_env.update(
                self.observation_view.python_console_cmds)
            self.python_console_env.update(
                self.networking_view.python_console_cmds)
            self.python_console_env.update(
                self.system_monitor_view.python_console_cmds)
            self.python_console_env.update(
                self.update_view.python_console_cmds)
            self.python_console_env.update(self.imu_view.python_console_cmds)
            self.python_console_env.update(self.mag_view.python_console_cmds)
            self.python_console_env.update(
                self.settings_view.python_console_cmds)
            self.python_console_env.update(
                self.spectrum_analyzer_view.python_console_cmds)

        except:  # noqa
            import traceback
            traceback.print_exc()
            if self.error:
                sys.exit(1)
Ejemplo n.º 19
0
class SystemMonitorView(HasTraits):
    python_console_cmds = Dict()

    _threads_table_list = List()
    _csac_telem_list = List()
    _csac_received = Bool(False)
    threads = List()

    msg_obs_avg_latency_ms = Int(0)
    msg_obs_min_latency_ms = Int(0)
    msg_obs_max_latency_ms = Int(0)
    msg_obs_window_latency_ms = Int(0)

    msg_obs_avg_period_ms = Int(0)
    msg_obs_min_period_ms = Int(0)
    msg_obs_max_period_ms = Int(0)
    msg_obs_window_period_ms = Float(0)

    zynq_temp = Float(0)
    fe_temp = Float(0)

    piksi_reset_button = SVGButton(
        label='Reset Device',
        tooltip='Reset Device',
        filename=resource_filename('console/images/fontawesome/power27.svg'),
        width=16,
        height=16,
        aligment='center')

    traits_view = View(
        VGroup(
            Item(
                '_threads_table_list',
                style='readonly',
                editor=TabularEditor(adapter=SimpleAdapter()),
                show_label=False,
                width=0.85,
            ),
            HGroup(
                VGroup(
                    HGroup(VGroup(Item('msg_obs_window_latency_ms',
                                       label='Curr',
                                       style='readonly',
                                       format_str='%dms'),
                                  Item('msg_obs_avg_latency_ms',
                                       label='Avg',
                                       style='readonly',
                                       format_str='%dms'),
                                  Item('msg_obs_min_latency_ms',
                                       label='Min',
                                       style='readonly',
                                       format_str='%dms'),
                                  Item('msg_obs_max_latency_ms',
                                       label='Max',
                                       style='readonly',
                                       format_str='%dms'),
                                  label='Latency',
                                  show_border=True),
                           VGroup(Item('msg_obs_window_period_ms',
                                       label='Curr',
                                       style='readonly',
                                       format_str='%dms'),
                                  Item('msg_obs_avg_period_ms',
                                       label='Avg',
                                       style='readonly',
                                       format_str='%dms'),
                                  Item('msg_obs_min_period_ms',
                                       label='Min',
                                       style='readonly',
                                       format_str='%dms'),
                                  Item('msg_obs_max_period_ms',
                                       label='Max',
                                       style='readonly',
                                       format_str='%dms'),
                                  label='Period',
                                  show_border=True),
                           show_border=True,
                           label="Observation Connection Monitor"),
                    Item('piksi_reset_button', show_label=False, width=0.50)),
                VGroup(
                    Item('zynq_temp',
                         label='Zynq CPU Temp',
                         style='readonly',
                         format_str='%.1fC'),
                    Item('fe_temp',
                         label='RF Frontend Temp',
                         style='readonly',
                         format_str='%.1fC'),
                    show_border=True,
                    label="Device Monitor",
                ),
                VGroup(Item('_csac_telem_list',
                            style='readonly',
                            editor=TabularEditor(adapter=SimpleCSACAdapter()),
                            show_label=False),
                       show_border=True,
                       label="Metrics",
                       visible_when='_csac_received'))), )

    def update_threads(self):
        self._threads_table_list = [
            (thread_name, state.cpu, state.stack_free)
            for thread_name, state in sorted(
                self.threads, key=lambda x: x[1].cpu, reverse=True)
        ]

    def update_network_state(self):
        self._network_refresh_button_fired()

    def heartbeat_callback(self, sbp_msg, **metadata):
        if self.threads != []:
            self.update_threads()
            self.threads = []

    def device_callback(self, sbp_msg, **metadata):
        self.zynq_temp = float(sbp_msg.cpu_temperature) / 100.
        self.fe_temp = float(sbp_msg.fe_temperature) / 100.

    def thread_state_callback(self, sbp_msg, **metadata):
        if sbp_msg.name == '':
            sbp_msg.name = '(no name)'
        sbp_msg.cpu /= 10.
        self.threads.append((sbp_msg.name, sbp_msg))

    def csac_header_callback(self, sbp_msg, **metadata):
        self.headers = sbp_msg.telemetry_labels.split(',')
        self.telem_header_index = sbp_msg.id

    def csac_telem_callback(self, sbp_msg, **metadata):
        self._csac_telem_list = []
        if self.telem_header_index is not None:
            if sbp_msg.id == self.telem_header_index:
                self._csac_received = True
                metrics_of_interest = [
                    'Status', 'Alarm', 'Mode', 'Phase', 'DiscOK'
                ]
                telems = sbp_msg.telemetry.split(',')
                for i, each in enumerate(self.headers):
                    if each in metrics_of_interest:
                        self._csac_telem_list.append((each, telems[i]))

    def _piksi_reset_button_fired(self):
        self.link(MsgReset(flags=0))

    def uart_state_callback(self, m, **metadata):
        self.msg_obs_avg_latency_ms = m.latency.avg
        self.msg_obs_min_latency_ms = m.latency.lmin
        self.msg_obs_max_latency_ms = m.latency.lmax
        self.msg_obs_window_latency_ms = m.latency.current
        if m.msg_type == SBP_MSG_UART_STATE:
            self.msg_obs_avg_period_ms = m.obs_period.avg
            self.msg_obs_min_period_ms = m.obs_period.pmin
            self.msg_obs_max_period_ms = m.obs_period.pmax
            self.msg_obs_window_period_ms = m.obs_period.current

    def __init__(self, link):
        super(SystemMonitorView, self).__init__()
        self.link = link
        self.telem_header_index = None
        self.link.add_callback(self.heartbeat_callback, SBP_MSG_HEARTBEAT)
        self.link.add_callback(self.device_callback, SBP_MSG_DEVICE_MONITOR)
        self.link.add_callback(self.thread_state_callback,
                               SBP_MSG_THREAD_STATE)
        self.link.add_callback(self.uart_state_callback,
                               [SBP_MSG_UART_STATE, SBP_MSG_UART_STATE_DEPA])
        self.link.add_callback(self.csac_telem_callback,
                               SBP_MSG_CSAC_TELEMETRY)
        self.link.add_callback(self.csac_header_callback,
                               SBP_MSG_CSAC_TELEMETRY_LABELS)

        self.python_console_cmds = {'mon': self}
Ejemplo n.º 20
0
class PortChooser(HasTraits):
    port = Str(None)
    ports = List()
    mode = Enum(cnx_type_list)
    flow_control = Enum(flow_control_options_list)
    ip_port = Int(55555)
    ip_address = Str('192.168.0.222')
    choose_baud = Bool(True)
    baudrate = Int()
    refresh_ports_button = SVGButton(
        label='',
        tooltip='Refresh Port List',
        filename=resource_filename(
            'console/images/fontawesome/refresh_blue.svg'),
        allow_clipping=False,
        width_padding=4,
        height_padding=4)

    traits_view = View(
        VGroup(
            Spring(height=8),
            HGroup(
                Spring(width=-2, springy=False),
                Item('mode',
                     style='custom',
                     editor=EnumEditor(values=cnx_type_list,
                                       cols=2,
                                       format_str='%s'),
                     show_label=False)),
            HGroup(VGroup(
                Label('Serial Device:'),
                HGroup(
                    Item('port',
                         editor=EnumEditor(name='ports'),
                         show_label=False,
                         springy=True),
                    Item('refresh_ports_button',
                         show_label=False,
                         padding=0,
                         height=-20,
                         width=-20),
                ),
            ),
                   VGroup(
                       Label('Baudrate:'),
                       Item('baudrate',
                            editor=EnumEditor(values=BAUD_LIST),
                            show_label=False,
                            visible_when='choose_baud'),
                       Item('baudrate',
                            show_label=False,
                            visible_when='not choose_baud',
                            style='readonly'),
                   ),
                   VGroup(
                       Label('Flow Control:'),
                       Item('flow_control',
                            editor=EnumEditor(values=flow_control_options_list,
                                              format_str='%s'),
                            show_label=False),
                   ),
                   visible_when="mode==\'Serial/USB\'"),
            HGroup(VGroup(
                Label('IP Address:'),
                Item('ip_address',
                     label="IP Address",
                     style='simple',
                     show_label=False,
                     height=-24),
            ),
                   VGroup(
                       Label('IP Port:'),
                       Item('ip_port',
                            label="IP Port",
                            style='simple',
                            show_label=False,
                            height=-24),
                   ),
                   Spring(),
                   visible_when="mode==\'TCP/IP\'"),
        ),
        buttons=['OK', 'Cancel'],
        default_button='OK',
        close_result=False,
        icon=icon,
        width=460,
        title='Swift Console v{0} - Select Interface'.format(CONSOLE_VERSION))

    def refresh_ports(self):
        """
        This method refreshes the port list
        """
        try:
            self.ports = [p for p, _, _ in s.get_ports()]
        except TypeError:
            pass

    def _refresh_ports_button_fired(self):
        self.refresh_ports()

    def __init__(self, baudrate=None):
        self.refresh_ports()
        # As default value, use the first city in the list:
        try:
            self.port = self.ports[0]
        except IndexError:
            pass
        if baudrate not in BAUD_LIST:
            self.choose_baud = False
        self.baudrate = baudrate
Ejemplo n.º 21
0
class SolutionView(HasTraits):
  python_console_cmds = Dict()

  lats = List()
  lngs = List()
  alts = List()

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

  plot = Instance(Plot)
  plot_data = Instance(ArrayPlotData)

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

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

  traits_view = View(
    HSplit(
      Item('table', style = 'readonly', editor = TabularEditor(adapter=SimpleAdapter()), show_label=False, width=0.3),
      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(
          'plot',
          show_label = False,
          editor = ComponentEditor(bgcolor = (0.8,0.8,0.8)),
        )
      )
    )
  )

  def _zoomall_button_fired(self):
    self.plot.index_range.low_setting = 'auto'
    self.plot.index_range.high_setting = 'auto'
    self.plot.value_range.low_setting = 'auto'
    self.plot.value_range.high_setting = 'auto'

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

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

  def _clear_button_fired(self):
    self.lats = []
    self.lngs = []
    self.alts = []
    self.plot_data.set_data('lat', [])
    self.plot_data.set_data('lng', [])
    self.plot_data.set_data('alt', [])
    self.plot_data.set_data('t', [])

  def _pos_llh_callback(self, data):
    # Updating an ArrayPlotData isn't thread safe (see chaco issue #9), so
    # actually perform the update in the UI thread.
    if self.running:
      GUI.invoke_later(self.pos_llh_callback, data)

  def update_table(self):
    self._table_list = self.table.items()

  def pos_llh_callback(self, data):
    soln = sbp_messages.PosLLH(data)
    self.pos_table = []

    if self.log_file is None:
      self.log_file = open(time.strftime("position_log_%Y%m%d-%H%M%S.csv"), 'w')

    self.log_file.write('%.2f,%.4f,%.4f,%.4f,%d\n' % (soln.tow * 1e3, soln.lat, soln.lon, soln.height, soln.n_sats))
    self.log_file.flush()

    if self.week is not None:
      t = datetime.datetime(1980, 1, 6) + \
          datetime.timedelta(weeks=self.week) + \
          datetime.timedelta(seconds=soln.tow/1e3)
      self.pos_table.append(('GPS Time', t))
      self.pos_table.append(('GPS Week', str(self.week)))

    tow = soln.tow*1e-3 + self.nsec*1e-9
    if self.nsec is not None:
      tow += self.nsec*1e-9
    self.pos_table.append(('GPS ToW', tow))

    self.pos_table.append(('Num. sats', soln.n_sats))

    self.pos_table.append(('Lat', soln.lat))
    self.pos_table.append(('Lng', soln.lon))
    self.pos_table.append(('Alt', soln.height))

    self.lats.append(soln.lat)
    self.lngs.append(soln.lon)
    self.alts.append(soln.height)

    self.lats = self.lats[-1000:]
    self.lngs = self.lngs[-1000:]
    self.alts = self.alts[-1000:]

    self.plot_data.set_data('lat', self.lats)
    self.plot_data.set_data('lng', self.lngs)
    self.plot_data.set_data('alt', self.alts)
    t = range(len(self.lats))
    self.plot_data.set_data('t', t)

    self.table = self.pos_table + self.vel_table + self.dops_table

    if self.position_centered:
      d = (self.plot.index_range.high - self.plot.index_range.low) / 2.
      self.plot.index_range.set_bounds(soln.pos_llh[0] - d, soln.pos_llh[0] + d)
      d = (self.plot.value_range.high - self.plot.value_range.low) / 2.
      self.plot.value_range.set_bounds(soln.pos_llh[1] - d, soln.pos_llh[1] + d)

  def dops_callback(self, data):
    dops = sbp_messages.Dops(data)
    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))
    ]
    self.table = self.pos_table + self.vel_table + self.dops_table

  def vel_ned_callback(self, data):
    vel_ned = sbp_messages.VelNED(data)

    if self.vel_log_file is None:
      self.vel_log_file = open(time.strftime("velocity_log_%Y%m%d-%H%M%S.csv"), 'w')

    self.vel_log_file.write('%.2f,%.4f,%.4f,%.4f,%.4f,%d\n' % (vel_ned.tow * 1e3, vel_ned.n, vel_ned.e, vel_ned.d, math.sqrt(vel_ned.n*vel_ned.n + vel_ned.e*vel_ned.e),vel_ned.n_sats))
    self.vel_log_file.flush()

    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)),
    ]
    self.table = self.pos_table + self.vel_table + self.dops_table

  def gps_time_callback(self, data):
    self.week = sbp_messages.GPSTime(data).wn
    self.nsec = sbp_messages.GPSTime(data).ns

  def __init__(self, link):
    super(SolutionView, self).__init__()

    self.log_file = None
    self.vel_log_file = None

    self.plot_data = ArrayPlotData(lat=[0.0], lng=[0.0], alt=[0.0], t=[0.0], ref_lat=[0.0], ref_lng=[0.0], region_lat=[0.0], region_lng=[0.0])
    self.plot = Plot(self.plot_data)

    self.plot.plot(('lng', 'lat'), type='line', name='line', color=(0, 0, 0, 0.1))
    self.plot.plot(('lng', 'lat'), type='scatter', name='points', color='blue', marker='dot', line_width=0.0, marker_size=1.0)

    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.value_axis.tick_label_position = 'inside'
    self.plot.value_axis.tick_label_color = 'gray'
    self.plot.value_axis.tick_color = 'gray'
    self.plot.padding = (0, 1, 0, 1)

    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(sbp_messages.SBP_POS_LLH, self._pos_llh_callback)
    self.link.add_callback(sbp_messages.SBP_VEL_NED, self.vel_ned_callback)
    self.link.add_callback(sbp_messages.SBP_DOPS, self.dops_callback)
    self.link.add_callback(sbp_messages.SBP_GPS_TIME, self.gps_time_callback)

    self.week = None
    self.nsec = 0

    self.python_console_cmds = {
      'solution': self
    }
Ejemplo n.º 22
0
class BaselineView(HasTraits):

    # This mapping should match the flag definitions in libsbp for
    # the MsgBaselineNED message. While this isn't strictly necessary
    # it helps avoid confusion

    python_console_cmds = Dict()

    table = List()

    logging_b = Bool(False)
    directory_name_b = File

    plot = Instance(Plot)
    plot_data = Instance(ArrayPlotData)

    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 Baseline',
        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)

    reset_button = Button(label='Reset Filters')

    traits_view = View(
        HSplit(
            Item(
                'table',
                style='readonly',
                editor=TabularEditor(adapter=SimpleAdapter()),
                show_label=False,
                width=0.3),
            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('reset_button', show_label=False), ),
                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_button_fired(self):
        self.link(MsgResetFilters(filter=0))

    def _reset_remove_current(self):
        self.plot_data.set_data('cur_fixed_n', [])
        self.plot_data.set_data('cur_fixed_e', [])
        self.plot_data.set_data('cur_fixed_d', [])
        self.plot_data.set_data('cur_float_n', [])
        self.plot_data.set_data('cur_float_e', [])
        self.plot_data.set_data('cur_float_d', [])
        self.plot_data.set_data('cur_dgnss_n', [])
        self.plot_data.set_data('cur_dgnss_e', [])
        self.plot_data.set_data('cur_dgnss_d', [])

    def _clear_history(self):
        self.plot_data.set_data('n_fixed', [])
        self.plot_data.set_data('e_fixed', [])
        self.plot_data.set_data('d_fixed', [])
        self.plot_data.set_data('n_float', [])
        self.plot_data.set_data('e_float', [])
        self.plot_data.set_data('d_float', [])
        self.plot_data.set_data('n_dgnss', [])
        self.plot_data.set_data('e_dgnss', [])
        self.plot_data.set_data('d_dgnss', [])

    def _clear_button_fired(self):
        self.n[:] = np.NAN
        self.e[:] = np.NAN
        self.d[:] = np.NAN
        self.mode[:] = np.NAN
        self.plot_data.set_data('t', [])
        self._clear_history()
        self._reset_remove_current()

    def iar_state_callback(self, sbp_msg, **metadata):
        self.num_hyps = sbp_msg.num_hyps
        self.last_hyp_update = time.time()

    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 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 baseline_heading_callback(self, sbp_msg, **metadata):
        headingMsg = MsgBaselineHeading(sbp_msg)
        if headingMsg.flags & 0x7 != 0:
            self.heading = headingMsg.heading * 1e-3
        else:
            self.heading = "---"

    def baseline_callback(self, sbp_msg, **metadata):
        soln = MsgBaselineNEDDepA(sbp_msg)
        self.last_soln = soln
        table = []

        soln.n = soln.n * 1e-3
        soln.e = soln.e * 1e-3
        soln.d = soln.d * 1e-3
        soln.h_accuracy = soln.h_accuracy * 1e-3
        soln.v_accuracy = soln.v_accuracy * 1e-3

        dist = np.sqrt(soln.n**2 + soln.e**2 + soln.d**2)

        tow = soln.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.utc_time is not None:
            ((tutc, secutc)) = datetime_2_str(self.utc_time)

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

        if not self.logging_b:
            self.log_file = None

        if self.logging_b:
            if self.log_file is None:
                self.log_file = sopen(filepath, 'w')
                self.log_file.write(
                    'pc_time,gps_time,tow(sec),north(meters),east(meters),down(meters),h_accuracy(meters),v_accuracy(meters),'
                    'distance(meters),num_sats,flags,num_hypothesis\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,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%d,%d,%d\n' %
                ("{0}:{1:06.6f}".format(tloc, float(secloc)), log_str_gps, tow,
                 soln.n, soln.e, soln.d, soln.h_accuracy, soln.v_accuracy,
                 dist, soln.n_sats, soln.flags, self.num_hyps))
            self.log_file.flush()

        self.last_mode = get_mode(soln)
        if time.time() - self.last_plot_update_time > GUI_UPDATE_PERIOD:
            self.solution_draw()

        if self.last_mode < 1:
            table.append(('GPS Week', EMPTY_STR))
            table.append(('GPS TOW', EMPTY_STR))
            table.append(('GPS Time', EMPTY_STR))
            table.append(('UTC Time', EMPTY_STR))
            table.append(('UTC Src', EMPTY_STR))
            table.append(('N', EMPTY_STR))
            table.append(('E', EMPTY_STR))
            table.append(('D', EMPTY_STR))
            table.append(('Horiz Acc', EMPTY_STR))
            table.append(('Vert Acc', EMPTY_STR))
            table.append(('Dist.', EMPTY_STR))
            table.append(('Sats Used', EMPTY_STR))
            table.append(('Flags', EMPTY_STR))
            table.append(('Mode', EMPTY_STR))
        else:
            self.last_btime_update = time.time()
            if self.week is not None:
                table.append(('GPS Week', str(self.week)))
            table.append(('GPS TOW', "{:.3f}".format(tow)))

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

            table.append(('N', soln.n))
            table.append(('E', soln.e))
            table.append(('D', soln.d))
            table.append(('Horiz Acc', soln.h_accuracy))
            table.append(('Vert Acc', soln.v_accuracy))
            table.append(('Dist.', "{0:.3f}".format(dist)))

            table.append(('Sats Used', soln.n_sats))

        table.append(('Flags', '0x%02x' % soln.flags))
        table.append(('Mode', mode_dict[self.last_mode]))
        if self.heading is not None:
            table.append(('Heading', self.heading))
        if self.age_corrections is not None:
            table.append(('Corr. Age [s]', self.age_corrections))
        self.table = table
        # Rotate array, deleting oldest entries to maintain
        # no more than N in plot
        self.n[1:] = self.n[:-1]
        self.e[1:] = self.e[:-1]
        self.d[1:] = self.d[:-1]
        self.mode[1:] = self.mode[:-1]

        # Insert latest position
        if self.last_mode > 1:
            self.n[0], self.e[0], self.d[0] = soln.n, soln.e, soln.d
        else:
            self.n[0], self.e[0], self.d[0] = [np.NAN, np.NAN, np.NAN]
        self.mode[0] = self.last_mode

    def solution_draw(self):
        if self.running:
            GUI.invoke_later(self._solution_draw)

    def _solution_draw(self):
        self._clear_history()
        soln = self.last_soln
        self.last_plot_update_time = time.time()
        if np.any(self.mode):
            float_indexer = (self.mode == FLOAT_MODE)
            fixed_indexer = (self.mode == FIXED_MODE)
            dgnss_indexer = (self.mode == DGNSS_MODE)

            if np.any(fixed_indexer):
                self.plot_data.set_data('n_fixed', self.n[fixed_indexer])
                self.plot_data.set_data('e_fixed', self.e[fixed_indexer])
                self.plot_data.set_data('d_fixed', self.d[fixed_indexer])
            if np.any(float_indexer):
                self.plot_data.set_data('n_float', self.n[float_indexer])
                self.plot_data.set_data('e_float', self.e[float_indexer])
                self.plot_data.set_data('d_float', self.d[float_indexer])
            if np.any(dgnss_indexer):
                self.plot_data.set_data('n_dgnss', self.n[dgnss_indexer])
                self.plot_data.set_data('e_dgnss', self.e[dgnss_indexer])
                self.plot_data.set_data('d_dgnss', self.d[dgnss_indexer])

            # Update our last solution icon
            if self.last_mode == FIXED_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_fixed_n', [soln.n])
                self.plot_data.set_data('cur_fixed_e', [soln.e])
                self.plot_data.set_data('cur_fixed_d', [soln.d])
            elif self.last_mode == FLOAT_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_float_n', [soln.n])
                self.plot_data.set_data('cur_float_e', [soln.e])
                self.plot_data.set_data('cur_float_d', [soln.d])
            elif self.last_mode == DGNSS_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_dgnss_n', [soln.n])
                self.plot_data.set_data('cur_dgnss_e', [soln.e])
                self.plot_data.set_data('cur_dgnss_d', [soln.d])
            else:
                pass
        # make the zoomall win over the position centered button
        # position centered button has no effect when zoom all enabled

        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.e - d, soln.e + d)
            d = (self.plot.value_range.high - self.plot.value_range.low) / 2.
            self.plot.value_range.set_bounds(soln.n - d, soln.n + d)

        if self.zoomall:
            plot_square_axes(self.plot, ('e_fixed', 'e_float', 'e_dgnss'),
                             ('n_fixed', 'n_float', 'n_dgnss'))

    def __init__(self, link, plot_history_max=1000, dirname=''):
        super(BaselineView, self).__init__()
        self.log_file = None
        self.directory_name_b = dirname
        self.num_hyps = 0
        self.last_hyp_update = 0
        self.last_btime_update = 0
        self.last_soln = None
        self.last_mode = 0
        self.plot_data = ArrayPlotData(
            n_fixed=[0.0],
            e_fixed=[0.0],
            d_fixed=[0.0],
            n_float=[0.0],
            e_float=[0.0],
            d_float=[0.0],
            n_dgnss=[0.0],
            e_dgnss=[0.0],
            d_dgnss=[0.0],
            t=[0.0],
            ref_n=[0.0],
            ref_e=[0.0],
            ref_d=[0.0],
            cur_fixed_e=[],
            cur_fixed_n=[],
            cur_fixed_d=[],
            cur_float_e=[],
            cur_float_n=[],
            cur_float_d=[],
            cur_dgnss_e=[],
            cur_dgnss_n=[],
            cur_dgnss_d=[])

        self.plot_history_max = plot_history_max
        self.n = np.zeros(plot_history_max)
        self.e = np.zeros(plot_history_max)
        self.d = np.zeros(plot_history_max)
        self.mode = np.zeros(plot_history_max)
        self.last_plot_update_time = 0

        self.plot = Plot(self.plot_data)
        pts_float = self.plot.plot(
            ('e_float', 'n_float'),
            type='scatter',
            color=color_dict[FLOAT_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        pts_fixed = self.plot.plot(  # noqa: F841
            ('e_fixed', 'n_fixed'),
            type='scatter',
            color=color_dict[FIXED_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        pts_dgnss = self.plot.plot(  # noqa: F841
            ('e_dgnss', 'n_dgnss'),
            type='scatter',
            color=color_dict[DGNSS_MODE],
            marker='dot',
            line_width=0.0,
            marker_size=1.0)
        ref = self.plot.plot(
            ('ref_e', 'ref_n'),
            type='scatter',
            color='red',
            marker='plus',
            marker_size=5,
            line_width=1.5)
        cur_fixed = self.plot.plot(
            ('cur_fixed_e', 'cur_fixed_n'),
            type='scatter',
            color=color_dict[FIXED_MODE],
            marker='plus',
            marker_size=5,
            line_width=1.5)
        cur_float = self.plot.plot(
            ('cur_float_e', 'cur_float_n'),
            type='scatter',
            color=color_dict[FLOAT_MODE],
            marker='plus',
            marker_size=5,
            line_width=1.5)
        cur_dgnss = self.plot.plot(
            ('cur_dgnss_e', 'cur_dgnss_n'),
            type='scatter',
            color=color_dict[DGNSS_MODE],
            marker='plus',
            line_width=1.5,
            marker_size=5)
        plot_labels = [' Base Position', 'DGPS', 'RTK Float', 'RTK Fixed']
        plots_legend = dict(
            zip(plot_labels, [ref, cur_dgnss, cur_float, cur_fixed]))
        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 = 'E (meters)'
        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 = 'N (meters)'
        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.week = None
        self.utc_time = None
        self.age_corrections = None
        self.heading = "---"
        self.nsec = 0

        self.link = link
        self.link.add_callback(self.baseline_callback, [
            SBP_MSG_BASELINE_NED, SBP_MSG_BASELINE_NED_DEP_A
        ])
        self.link.add_callback(self.baseline_heading_callback,
                               [SBP_MSG_BASELINE_HEADING])
        self.link.add_callback(self.iar_state_callback, SBP_MSG_IAR_STATE)
        self.link.add_callback(self.gps_time_callback,
                               [SBP_MSG_GPS_TIME, SBP_MSG_GPS_TIME_DEP_A])
        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.python_console_cmds = {'baseline': self}