From 00c4e3f439007ec36f1ac516ff8c7ce94fff2352 Mon Sep 17 00:00:00 2001
From: abeinder <abeinder@iastate.edu>
Date: Wed, 26 Apr 2023 11:30:07 -0500
Subject: [PATCH] added rest of documentation

---
 pycrocart/.idea/workspace.xml         | 36 +++++++++----
 pycrocart/CrazyflieProtoConnection.py | 78 ++++++++++++++++++++++++---
 pycrocart/LoggingConfigTab.py         | 38 ++++++++++++-
 pycrocart/LoggingSelectionMenu.py     | 23 +++++++-
 pycrocart/ParameterTab.py             | 45 ++++++++++++++--
 pycrocart/PlottingWindow.py           | 38 ++++++++-----
 pycrocart/PyCroCart.py                |  2 +-
 pycrocart/PyLine.py                   |  8 ++-
 pycrocart/SetpointHandler.py          | 76 +++++++++++++++++++-------
 pycrocart/SetpointMenu.py             |  4 +-
 10 files changed, 287 insertions(+), 61 deletions(-)

diff --git a/pycrocart/.idea/workspace.xml b/pycrocart/.idea/workspace.xml
index 20ebf6909..d2d50689a 100644
--- a/pycrocart/.idea/workspace.xml
+++ b/pycrocart/.idea/workspace.xml
@@ -1,17 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="ChangeListManager">
-    <list default="true" id="279c12a8-b571-4465-a118-8f46f6730b05" name="Changes" comment="add working gamepad control and ReadMe.md">
+    <list default="true" id="279c12a8-b571-4465-a118-8f46f6730b05" name="Changes" comment="added comments">
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/ControlWindow.py" beforeDir="false" afterPath="$PROJECT_DIR$/ControlTab.py" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/Examples/animated_graph_example.py" beforeDir="false" afterPath="$PROJECT_DIR$/Examples/animated_graph_example.py" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/GamepadWizard.py" beforeDir="false" afterPath="$PROJECT_DIR$/GamepadWizardTab.py" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/LoggingConfigWindow.py" beforeDir="false" afterPath="$PROJECT_DIR$/LoggingConfigTab.py" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/ParameterWindow.py" beforeDir="false" afterPath="$PROJECT_DIR$/ParameterTab.py" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/CrazyflieProtoConnection.py" beforeDir="false" afterPath="$PROJECT_DIR$/CrazyflieProtoConnection.py" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/LoggingConfigTab.py" beforeDir="false" afterPath="$PROJECT_DIR$/LoggingConfigTab.py" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/LoggingSelectionMenu.py" beforeDir="false" afterPath="$PROJECT_DIR$/LoggingSelectionMenu.py" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/ParameterTab.py" beforeDir="false" afterPath="$PROJECT_DIR$/ParameterTab.py" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/PlottingWindow.py" beforeDir="false" afterPath="$PROJECT_DIR$/PlottingWindow.py" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/PyCroCart.py" beforeDir="false" afterPath="$PROJECT_DIR$/PyCroCart.py" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/PyLine.py" beforeDir="false" afterPath="$PROJECT_DIR$/PyLine.py" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/SetpointHandler.py" beforeDir="false" afterPath="$PROJECT_DIR$/SetpointHandler.py" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/SetpointMenu.py" beforeDir="false" afterPath="$PROJECT_DIR$/SetpointMenu.py" afterDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -33,6 +32,14 @@
     </option>
     <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
   </component>
+  <component name="GitSEFilterConfiguration">
+    <file-type-list>
+      <filtered-out-file-type name="LOCAL_BRANCH" />
+      <filtered-out-file-type name="REMOTE_BRANCH" />
+      <filtered-out-file-type name="TAG" />
+      <filtered-out-file-type name="COMMIT_BY_MESSAGE" />
+    </file-type-list>
+  </component>
   <component name="MarkdownSettingsMigration">
     <option name="stateVersion" value="1" />
   </component>
@@ -256,7 +263,8 @@
       <workItem from="1682362425838" duration="5156000" />
       <workItem from="1682441377494" duration="2792000" />
       <workItem from="1682445089720" duration="8546000" />
-      <workItem from="1682476048756" duration="8000" />
+      <workItem from="1682476048756" duration="137000" />
+      <workItem from="1682521516348" duration="4679000" />
     </task>
     <task id="LOCAL-00001" summary="initial push">
       <created>1681663716970</created>
@@ -300,7 +308,14 @@
       <option name="project" value="LOCAL" />
       <updated>1682454191112</updated>
     </task>
-    <option name="localTasksCounter" value="7" />
+    <task id="LOCAL-00007" summary="added comments">
+      <created>1682479884093</created>
+      <option name="number" value="00007" />
+      <option name="presentableId" value="LOCAL-00007" />
+      <option name="project" value="LOCAL" />
+      <updated>1682479884093</updated>
+    </task>
+    <option name="localTasksCounter" value="8" />
     <servers />
   </component>
   <component name="TypeScriptGeneratedFilesManager">
@@ -323,7 +338,8 @@
     <MESSAGE value="removed a ton of warnings" />
     <MESSAGE value="current working version" />
     <MESSAGE value="add working gamepad control and ReadMe.md" />
-    <option name="LAST_COMMIT_MESSAGE" value="add working gamepad control and ReadMe.md" />
+    <MESSAGE value="added comments" />
+    <option name="LAST_COMMIT_MESSAGE" value="added comments" />
   </component>
   <component name="XDebuggerManager">
     <breakpoint-manager>
diff --git a/pycrocart/CrazyflieProtoConnection.py b/pycrocart/CrazyflieProtoConnection.py
index 0b1098c73..cdb898145 100644
--- a/pycrocart/CrazyflieProtoConnection.py
+++ b/pycrocart/CrazyflieProtoConnection.py
@@ -1,3 +1,17 @@
+"""
+Crazyflie Proto Connection handles the actual interaction with the crazyflie.
+The only reason it exists is to serve as an intermediary between the frontend
+and the crazyflie itself so that it can handle all interactions with the
+cflib library and that the GUI doesn't have to.
+
+If one wanted to slot this GUI into the existing microcart infrastructure,
+CrazyflieProtoConnection could be rewritten to interface to frontend
+commands, and to connect to the backend, and the crazyflie adapter,
+and crazyflie groundstation.
+
+"""
+
+
 from time import time
 from typing import List
 import time
@@ -11,8 +25,20 @@ from cflib.crazyflie.log import LogConfig
 
 
 class CrazyflieProtoConnection:
+    """
+    Handles all interactions with cflib.
+    """
+
     def __init__(self):
-        # Set up the timer to update the plot
+        """
+        Initialize the start time, the logging queue which is given to the
+        plotting window, and set the synchronous crazyflie connection by
+        default to None. This should be set to the crazyflie when connecting
+        to one.
+        """
+
+        # Get the start time, so that the timestamp returned to the user will
+        # be in seconds since startup.
         self.start_time = time.time()
 
         self.logging_queue = Queue()
@@ -23,6 +49,8 @@ class CrazyflieProtoConnection:
         self.logging_configs = []
 
     def logging_callback(self, _timestamp, data, _logconf):
+        """ Whenever data comes in from the logging, it is sent here,
+        which routes it into our specific format for the logging queue. """
 
         timestamp1 = time.time() - self.start_time
 
@@ -32,6 +60,8 @@ class CrazyflieProtoConnection:
             self.logging_queue.put(value_pair)
 
     def param_set_value(self, group: str, name: str, value: float):
+        """ Set a crazyflie parameter value. """
+
         try:
             if self.scf.is_link_open():
                 full_name = group + "." + name
@@ -50,36 +80,45 @@ class CrazyflieProtoConnection:
             print("Nothing connected")
 
     def done_setting_param_value(self, *_args):
+        """ Callback when done setting a parameter value. """
         print("Done setting param")
         self.param_callback_count += 1
 
     def param_get_value(self, group: str, name: str):
-
+        """ Retrieve parameter value from crazyflie toc. """
         try:
             if self.scf.is_link_open():
                 return self.scf.cf.param.values[group][name]
         except AttributeError:
             pass
-        return -1.234567890
+        return -1.234567890  # 1234567890 should be pretty obvious that
+        # something has gone wrong.
 
     def get_logging_toc(self):
+        """ Retrieve entire logging table of contents. Used in order to
+        display list in logging tab. """
+
         try:
             if self.scf.is_link_open():
                 tocFull = self.scf.cf.log.toc.toc
                 toc = []
                 for key in tocFull.keys():
                     for inner_key in tocFull[key].keys():
+                        # concatenate group name with parameter name.
                         full_name = key + "." + inner_key
                         toc.append(full_name)
 
                 return toc
             else:
-                return {}
+                return []
         except AttributeError:
             pass
-        return {}
+        return []
 
     def get_param_toc(self):
+        """ Get the names of all groups available for parameters on the
+        crazyflie. Used to populate parameter group list on parameter tab. """
+
         try:
             if self.scf.is_link_open():
                 toc = self.scf.cf.param.values
@@ -90,6 +129,10 @@ class CrazyflieProtoConnection:
         return {}
 
     def add_log_config(self, name: str, period: int, variables: List[str]):
+        """ Add a logging config. Used from logging tab when refreshing
+        logging variables. Add callback to route logged data to logging
+        queue. """
+
         print("Name: " + name + ", period: " + str(period) + ", variables: "
               + str(variables))
         logging_group = LogConfig(name=name, period_in_ms=period)
@@ -103,40 +146,61 @@ class CrazyflieProtoConnection:
         self.scf.cf.log.add_config(self.logging_configs[-1])
 
     def clear_logging_configs(self):
+        """ Stop logging and clear configuration. Used when refreshing
+        logging to stop anything that has configured to be logged from
+        logging. """
+
         self.stop_logging()
         self.logging_configs = []
+        # done refreshing toc is a callback function that is triggered when
+        # refresh toc is done executing.
         self.scf.cf.log.refresh_toc(
             self.done_refreshing_toc, self.scf.cf.log._toc_cache)
+        # Blocks until toc is done refreshing.
         while self.param_callback_count < 1:
             time.sleep(0.01)
         self.param_callback_count = 0
 
+        # grabs new toc values
         self.scf.wait_for_params()
 
-    def done_refreshing_toc(self, *args):
-
+    def done_refreshing_toc(self, *_args):
+        """ Callback for flow control, increments param callback count to
+        allow exit of while loop. """
         self.param_callback_count = 1
 
     def start_logging(self):
+        """ Begins logging all configured logging blocks. This is used from
+        the controls tab when hitting begin logging. """
         for i in range(0, len(self.logging_configs)):
             self.logging_configs[i].start()
 
     def stop_logging(self):
+        """ Stops logging all configured logging blocks. This is used from
+        the controls tab when hitting pause logging. """
         for i in range(0, len(self.logging_configs)):
             self.logging_configs[i].stop()
 
     def connect(self, uri: str):
+        """
+        Handles connecting to a crazyflie. Bitcraze has excellent
+        documentation on how to use the synchronous crazyflie object in order
+        to send setpoints, set parameters or retrieve logging.
+        :param uri: Radio channel
+        """
         cflib.crtp.init_drivers()
         self.scf = SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache'))
         self.scf.open_link()
         self.scf.wait_for_params()
 
     def disconnect(self):
+        """ Disconnect from crazyflie. """
         if self.is_connected:
             self.scf.close_link()
 
     @staticmethod
     def list_available_crazyflies():
+        """ Lists crazyflies that are on and within range. """
         cflib.crtp.init_drivers()  # run this again just in case you plug the
         # dongle in
         return cflib.crtp.scan_interfaces()
diff --git a/pycrocart/LoggingConfigTab.py b/pycrocart/LoggingConfigTab.py
index 19845fd87..e3a76e084 100644
--- a/pycrocart/LoggingConfigTab.py
+++ b/pycrocart/LoggingConfigTab.py
@@ -7,8 +7,24 @@ from CrazyflieProtoConnection import CrazyflieProtoConnection
 
 
 class LoggingConfigTab(QWidget):
+    """
+    LoggingConfigTab is the class that configures the logging config tab
+    inside the gui. Holds the logging table of contents, and a couple buttons to
+    configure the logging blocks, refresh the logging, and stop it completely.
+    """
+
     def __init__(self, cf: CrazyflieProtoConnection,
                  logging_selection_menu_update_function):
+        """
+        Initialize all widgets in the logging config tab window.
+
+        :param cf: CrazyflieProtoConnection in order to interface with
+        drone.
+        :param logging_selection_menu_update_function: This function is
+        called whenever the logging blocks are refreshed. The purpose is to
+        allow the logging selection menu to know that the logging config has
+        been refreshed and to look for new signals.
+        """
         super().__init__()
 
         layout = QHBoxLayout()
@@ -88,26 +104,37 @@ class LoggingConfigTab(QWidget):
 
     @staticmethod
     def open_log_file(_self):
+        """ Open logging file using default json file editor, probably
+        notepad. """
         os.startfile('logging_variables.json')
 
     def on_refresh(self):
-        print("refresh")
+        """ Whenever the refresh button is clicked, call this function. """
+
         self.cf.clear_logging_configs()
         self.update_logging_values()
 
         self.set_logging_block_from_file()
 
     def on_stop(self):
+        """ Whenever the stop logging button is clicked, call this function.
+        Arguably unneccesary given the pause button on the controls page. """
+
         self.cf.stop_logging()
 
     def update_logging_values(self):
+        """ Update the logging parameter values available in the left-hand
+        menu. """
 
+        # Clear away the current values
         for item in self.items:
             self.list_widget.takeItem(self.list_widget.row(item))
 
+        # Get table of contents.
         logging_vars_toc = self.cf.get_logging_toc()
         self.list_widget.addItems(logging_vars_toc)
 
+        # Update items list so, we can remove items next time we try.
         self.items = []
         self.items_text = []
         for x in range(self.list_widget.count()):
@@ -115,9 +142,12 @@ class LoggingConfigTab(QWidget):
             self.items_text.append(self.list_widget.item(x).text())
 
     def set_logging_block_from_file(self):
+        """ Read logging variables json. Offer feedback to the user if
+        formatting is incorrect. """
 
         with open('./logging_variables.json', 'r') as f:
             contents = json.load(f)
+        # todo offer feedback if file is missing
 
         blocks_valid = True
         blocks = []
@@ -171,6 +201,8 @@ class LoggingConfigTab(QWidget):
                     blocks_valid = False
                     break
 
+                # This is where good variables are actually handled, but will
+                # raise errors if they are bad.
                 for var in variables:
                     if var not in self.items_text:
                         bad_var = var
@@ -200,14 +232,18 @@ class LoggingConfigTab(QWidget):
                     "<span style='color: red;'>" + error_text + "</span>")
                 blocks_valid = False
                 break
+            # todo add error for if there are over 4 logging groups
 
+            # current logging blocks
             blocks.append({'name': name, 'period': period, 'vars': variables})
 
         if blocks_valid:
             self.error_label.setText("")
             variables = []
             for block in blocks:
+                # add logging block to crazyflie proto connection
                 self.cf.add_log_config(block['name'], block['period'],
                                        block['vars'])
                 variables.append(block['vars'])
+            # call function for menu update on controls page
             self.logging_selection_menu_update_function(variables)
diff --git a/pycrocart/LoggingSelectionMenu.py b/pycrocart/LoggingSelectionMenu.py
index 72657f28b..4d19a48ac 100644
--- a/pycrocart/LoggingSelectionMenu.py
+++ b/pycrocart/LoggingSelectionMenu.py
@@ -2,6 +2,11 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QComboBox
 
 
 class LoggingSelectionMenu(QWidget):
+    """
+    Logging selection menu sits within the controls tab, handles selecting
+    valid logging signals through a series of combo boxes.
+    """
+
     def __init__(self):
         super().__init__()
 
@@ -13,28 +18,40 @@ class LoggingSelectionMenu(QWidget):
         self.cbs = [QComboBox(), QComboBox(), QComboBox(), QComboBox(),
                     QComboBox()]
 
+        # Default logging variables to Logging Variable 1,2,3,4,5
         for i in range(0, len(self.cbs)):
             lst = ["Logging Variable " + str(i+1)]
             lst.extend(self.available_logging_variables)
             self.cbs[i].addItems(lst)
             layout.addWidget(self.cbs[i], i+1)
+            # add callback for when new selection happens
             self.cbs[i].currentIndexChanged.connect(
                 self.selection_change_callback)
+        # todo add coloring to each combo box text that matches the plotting
+        #  window line color associated with that logging variable
 
     def get_axis_of_signal(self, signal_name: str):
-
+        """ Used for determining which signal is which in plotting window. """
         for i in range(0, len(self.cbs)):
             if signal_name == self.cbs[i].currentText():
                 return i+1
 
     def update_available_logging_variables(self, variables_list: list):
+        """ This function is called by the logging config tab upon refreshing
+        the logging variables. If anything changes, the variables list will
+        be updated. """
         self.available_logging_variables = []
         for variable in variables_list:
             for sub_variable in variable:
                 self.available_logging_variables.append(sub_variable)
         self.selection_change_callback()
 
+        # todo handle variables that used to be in the list and no longer are
+        # todo always pause or set back to Logging Variable # whenever the
+        #  old variable selected is no longer valid
+
     def selection_change_callback(self):
+        """ Called whenever a signal selection is changed. """
 
         selected_signals = [
             self.cbs[0].currentText(), self.cbs[1].currentText(),
@@ -44,12 +61,14 @@ class LoggingSelectionMenu(QWidget):
 
         modified_selection_signals = []
 
+        # find any signals that are different from before.
         for i in range(0, len(self.available_logging_variables)):
             if not (self.available_logging_variables[i] in selected_signals):
                 modified_selection_signals.append(
                     self.available_logging_variables[i])
 
-        # cb1
+        # update logging variable options in each menu, don't show options
+        # that are selected elsewhere.
         for i in range(0, len(self.cbs)):
 
             self.cbs[i].currentIndexChanged.disconnect(
diff --git a/pycrocart/ParameterTab.py b/pycrocart/ParameterTab.py
index 86de41fd5..ab80f04fe 100644
--- a/pycrocart/ParameterTab.py
+++ b/pycrocart/ParameterTab.py
@@ -22,7 +22,7 @@ class ParameterTab(QWidget):
 
         layout = QGridLayout()
         self.setLayout(layout)
-        self.cf = cf  # Crayzflie proto connection
+        self.cf = cf  # Crazyflie proto connection
 
         self.sending = False
         self.sending_queue = Queue()
@@ -166,14 +166,19 @@ class ParameterTab(QWidget):
         self.progress_bar.setMaximumWidth(200)
         layout.addWidget(self.progress_bar, 12, 1, alignment=Qt.AlignHCenter)
 
+        # for connecting during runtime this would need to be moved
         self.on_connect()
 
     def on_connect(self):
-        """ Whenever connecting to the crazyflie,  """
+        """ Whenever connecting to the crazyflie, grab the parameter table of
+        contents. This is how we actually get what parameters and groups are
+        available. """
         self.toc = self.cf.get_param_toc()
         self.populate_group_menu_options()
 
     def populate_group_menu_options(self):
+        """ Remove any current entries from parameter boxes and add the
+        options for the parameter groups. """
 
         self.set_param_group_cbox.clear()
         self.get_param_group_cbox.clear()
@@ -182,22 +187,37 @@ class ParameterTab(QWidget):
         self.get_param_group_cbox.addItems(self.toc.keys())
 
     def on_get_param_group_changed(self):
+        """ Whenever a parameter group is selected, populate the options for
+        the specific entries available in each group. """
+
         self.get_param_cbox.clear()
         group = self.get_param_group_cbox.currentText()
         self.get_param_cbox.addItems(self.toc[group])
 
     def on_set_param_group_changed(self):
+        """ Whenever a parameter group is selected, populate the options for
+        the specific entries available in each group. """
+
         self.set_param_cbox.clear()
         group = self.set_param_group_cbox.currentText()
         self.set_param_cbox.addItems(self.toc[group])
 
     def get_param(self):
+        """ Retrieve parameter value from crazyflie proto connection. All
+        parameter values are always available from it, so this is actually
+        just retrieving data from the object not from the physical crazyflie
+        itself. """
+
         group = self.get_param_group_cbox.currentText()
         entry = self.get_param_cbox.currentText()
         value = self.cf.param_get_value(group, entry)
         self.value_value_label.setText(value)
 
     def set_param(self):
+        """ Set parameter value. If the value isn't a number offer the user
+        feedback that it is an unacceptable value. Otherwise, set the
+        parameter utilizing the crazyflie proto connection. """
+
         group = self.set_param_group_cbox.currentText()
         entry = self.set_param_cbox.currentText()
         value = self.set_param_value.text()
@@ -216,10 +236,14 @@ class ParameterTab(QWidget):
 
     @staticmethod
     def on_edit(_self):
+        """ When editing the mp4params file, launch it in notepad or default
+        text editor. """
         os.startfile('mp4params.json')
 
     def on_send(self):
+        """ Send mp4params file to CrazyflieProtoConnection. """
 
+        # Don't send if you are already sending
         if not self.sending:
 
             try:
@@ -230,22 +254,33 @@ class ParameterTab(QWidget):
                 self.complete_label.setEnabled(False)
                 self.complete_label.setText("Complete ✓")
 
+                # add all the parameters in the file to a sending queue
                 for key in contents:
                     for sub_key in contents[key]:
                         self.num_to_send += 1
 
-                        self.sending_queue.put({'key': key, 'sub_key': sub_key,
-                                                'value': contents[key][sub_key]})
+                        self.sending_queue.put(
+                            {'key': key, 'sub_key': sub_key,
+                             'value': contents[key][sub_key]})
 
+                # every 300 ms, send the next parameter. Uses a timer to
+                # retain reactivity of UI. Uses 300ms because it was more
+                # stable than 100ms. Relatively often at 100ms parameters
+                # would not get set correctly
                 self.timer.timeout.connect(self.send_callback)
                 self.timer.start(300)
             except json.decoder.JSONDecodeError:
                 error_text = "Malformed Json"
+                # If your json loading fails for some reason this is the
+                # error to be provided to the user.
                 self.complete_label.setText(
                     "<span style='color: red;'>" + error_text + "</span>")
+            # todo add error for if file isn't present
 
     def send_callback(self):
-
+        """ This function get called whenever a parameter is ready to be
+        sent from the file, and once all parameters are set it will
+        disconnect itself from being called. Also updates the progress bar. """
         try:
             vals = self.sending_queue.get(block=False)
             key = vals['key']
diff --git a/pycrocart/PlottingWindow.py b/pycrocart/PlottingWindow.py
index 74a369437..520c40c8a 100644
--- a/pycrocart/PlottingWindow.py
+++ b/pycrocart/PlottingWindow.py
@@ -3,11 +3,27 @@ import pyqtgraph as pg
 
 
 class PlottingWindow(pg.PlotWidget):
+    """
+    Plotting window utilized a pyqtgraph plot widget in order to graph the
+    logging variables that have been selected by the logging selection menu
+    from the available logging config.
+    """
+
     def __init__(self, parent=None, num_axes=5, width=5, height=4, dpi=100):
+        """
+        Initialize plotting window, axes, labels, and geometry.
+
+        :param parent: Parent widget
+        :param num_axes: Number of data axes
+        :param width: Default width in pixels
+        :param height: Default height in pixels
+        :param dpi:
+        """
+
         super().__init__(parent=parent, width=width, height=height, dpi=dpi)
         self.setLabel('bottom', 'Time (s)')
         self.setLabel('left', 'Amplitude')
-        self.setLabel('top', 'Sine Wave Plot')
+        self.setLabel('top', 'Logging Variables Plot')
         self._num_axes = num_axes
 
         self.current_iter = 0
@@ -17,26 +33,21 @@ class PlottingWindow(pg.PlotWidget):
 
         if num_axes > 0:
             # Set up the sine wave data and plot
+            # store 1000 data points for each axis
             self.plot_data_ys = np.zeros((1000, num_axes))
             self.plot_data_xs = np.zeros((1000, num_axes))
 
             self.plot_curves = \
                 [self.plot(self.plot_data_xs[:, i], self.plot_data_ys[:, i],
-                           pen=pg.mkPen(pg.intColor(i), width=2)) for i in range(num_axes)]
-
-    def update_num_axes(self):
-        num_axes = 0
-        if num_axes < 0:
-            raise ValueError("num_axes must be greater than 0!")
-
-        if num_axes != self._num_axes:
-            if num_axes == 0:  # turn it all off
-                self.clear()
-            else:
-                pass
+                           pen=pg.mkPen(pg.intColor(i), width=2))
+                 for i in range(num_axes)]
 
     def update_plot(self, input_data: float, input_timestamp: float,
                     input_axis: int):
+        """ Add new data to the plot from the right side. Utilizes a shift
+        buffer to display data. Constrains the X range to the current time
+        window. """
+
         # Shift the data and add a new point
         self.plot_data_ys[:-1, input_axis] = self.plot_data_ys[1:, input_axis]
         self.plot_data_ys[-1, input_axis] = input_data
@@ -47,6 +58,7 @@ class PlottingWindow(pg.PlotWidget):
         self.plot_curves[input_axis].setData(self.plot_data_xs[:, input_axis],
                                              self.plot_data_ys[:, input_axis])
 
+        # don't scroll at first. Display 10 seconds of data.
         if max(self.plot_data_xs[-1]) < 10:
             self.setXRange(0, 10)  # had padding=0 on both these ranges in
             # case that's needed
diff --git a/pycrocart/PyCroCart.py b/pycrocart/PyCroCart.py
index 07af7b577..5ea6ea8ec 100644
--- a/pycrocart/PyCroCart.py
+++ b/pycrocart/PyCroCart.py
@@ -66,7 +66,7 @@ if __name__ == '__main__':
     cf1.scf.cf.commander = uCartCommander.Commander(cf1.scf.cf)
 
     time.sleep(1)
-    setpoint_handler1 = SetpointHandler(cf1.scf.cf.commander, cf1)
+    setpoint_handler1 = SetpointHandler(cf1.scf.cf.commander)
 
     window = PyCroCart(cf1, setpoint_handler1)
     window.tab4.on_refresh()
diff --git a/pycrocart/PyLine.py b/pycrocart/PyLine.py
index 94ad9b0af..75508e5d4 100644
--- a/pycrocart/PyLine.py
+++ b/pycrocart/PyLine.py
@@ -1,3 +1,8 @@
+"""
+This implements either a horizontal line, or a vertical line. Useful for
+adding visible separation in windows.
+"""
+
 from PyQt5 import QtWidgets
 
 
@@ -22,5 +27,6 @@ class QVSeparationLine(QtWidgets.QFrame):
         self.setMinimumHeight(1)
         self.setFrameShape(QtWidgets.QFrame.VLine)
         self.setFrameShadow(QtWidgets.QFrame.Sunken)
-        self.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
+        self.setSizePolicy(QtWidgets.QSizePolicy.Minimum,
+                           QtWidgets.QSizePolicy.Preferred)
         return
diff --git a/pycrocart/SetpointHandler.py b/pycrocart/SetpointHandler.py
index 79dcba6ee..7e6e8f8ac 100644
--- a/pycrocart/SetpointHandler.py
+++ b/pycrocart/SetpointHandler.py
@@ -1,12 +1,18 @@
-from enum import Enum
+"""
+Setpoint handler holds classes used to send setpoints to the crazyflie proto
+connection in an organized fashion. It is used by the setpoint menu found on
+the controls tab.
 
-from PyQt5.QtCore import QTimer
+"""
 
+from enum import Enum
+from PyQt5.QtCore import QTimer
 from uCartCommander import Commander
 from threading import Semaphore
 
 
 class FlightMode(Enum):
+    """ Available flight modes found in crazyflie firmware. """
     TYPE_STOP: int = 0
     TYPE_VELOCITY_WORLD: int = 1
     TYPE_ZDISTANCE: int = 2
@@ -14,14 +20,14 @@ class FlightMode(Enum):
     TYPE_HOVER: int = 5
     FULL_STATE_TYPE: int = 6
     TYPE_POSITION: int = 7
-    # Custom modes -----------
+    # ------ Custom modes added to firmware by uCart -----------
     ATTITUDE_RATE_TYPE: int = 8
     ATTITUDE_TYPE: int = 9
     MIXED_ATTITUDE_TYPE: int = 10
 
 
 class Setpoint:
-
+    """ Used in order to hold setpoint easily. """
     def __init__(self):
         self.yaw: float = 0
         self.pitch: float = 0
@@ -30,48 +36,73 @@ class Setpoint:
 
 
 class SetpointHandler:
+    """
+    Setpoint handler is used to send setpoints to the crazyflie proto
+    connection in an organized manner. It holds direct access to the
+    crazyflie's commander, which means that whenever connecting to a
+    crazyflie, that commander must be configured in the setpoint handler.
+    This commander is intended to by the custom uCartCommander which is a
+    modified low level setpoint commander. Currently, no modified version of
+    the high level commander exists.
 
-    def __init__(self, commander: Commander, cf):
+    """
+
+    def __init__(self, commander: Commander):
+        """
+        Initialize timer,
+
+        :param commander: uCartCommander taken directly from the synchronous
+        crazyflie.
+        """
 
         self.setpoint = Setpoint()
         self.commander = commander
-        self.cf = cf
 
         self.setpoint_semaphore = Semaphore(1)
         self._flight_mode = FlightMode.TYPE_STOP
 
-        # Set up the timer to update the plot
+        # Send setpoints to crazyflie every 20 ms.
         self.timer = QTimer()
         self.timer.timeout.connect(self.update)
-        self.timer.start(500)
+        self.timer.start(20)
 
     def update(self):
+        """ If the flight mode is not stopped, send the current setpoint. """
         if self._flight_mode != FlightMode.TYPE_STOP:
             self.sendSetpoint()
 
     def getFlightMode(self):
+        """ Returns current flight mode. """
         return self._flight_mode
 
     def setAttitudeMode(self):
+        """ Safely set to attitude mode. Uses semaphore in case a callback
+                happens in the middle of the function. """
         self.setpoint_semaphore.acquire()
         self._flight_mode = FlightMode.ATTITUDE_TYPE
         self.setpoint_semaphore.release()
 
     def setMixedAttitudeMode(self):
+        """ Safely set to mixed attitude mode. Uses semaphore in case a
+                callback happens in the middle of the function. """
         self.setpoint_semaphore.acquire()
         self._flight_mode = FlightMode.MIXED_ATTITUDE_TYPE
         self.setpoint_semaphore.release()
 
     def setRateMode(self):
+        """ Safely set to rate mode. Uses semaphore in case a callback
+                happens in the middle of the function. """
         self.setpoint_semaphore.acquire()
         self._flight_mode = FlightMode.ATTITUDE_RATE_TYPE
         self.setpoint_semaphore.release()
 
     def startFlying(self):
+        """ Sends an all 0's setpoint which is the convention to tell the
+            crazyflie to listen for setpoints. """
         self.commander.start_flying()
 
     def stopFlying(self):
-
+        """ Tells the crazyflie to stop flying. """
         self.setpoint_semaphore.acquire()
         self._flight_mode = FlightMode.TYPE_STOP
 
@@ -80,7 +111,8 @@ class SetpointHandler:
         print("Stop flying")
 
     def setSetpoint(self, yaw: float, pitch: float, roll: float, thrust: float):
-
+        """ Safely sets the crazyflie setpoint. Utilizes semaphore to avoid
+                reading and writing at the same time due to callbacks. """
         self.setpoint_semaphore.acquire()
 
         self.setpoint.yaw = yaw
@@ -92,12 +124,14 @@ class SetpointHandler:
         self.setpoint_semaphore.release()
 
     def sendSetpoint(self):
-
+        """ Uses commander to send setpoints to crazyflie depending upon the
+                current flight mode. """
         self.setpoint_semaphore.acquire()
 
         if self._flight_mode == FlightMode.ATTITUDE_TYPE:
 
-            thrust = self.setpoint.thrust * 65000 / 1000
+            # scales thrust from 100 for slider control.
+            thrust = self.setpoint.thrust * 65000 / 100
             print(f"Set attitude: {self.setpoint.yaw}, {self.setpoint.pitch}, "
                   f"{self.setpoint.roll}, {thrust}")
 
@@ -107,7 +141,7 @@ class SetpointHandler:
 
         elif self._flight_mode == FlightMode.ATTITUDE_RATE_TYPE:
 
-            thrust = self.setpoint.thrust * 65000 / 1000
+            thrust = self.setpoint.thrust * 65000 / 100
             print(f"Set attitude rate: {self.setpoint.yaw},"
                   f" {self.setpoint.pitch}, "
                   f"{self.setpoint.roll}, {thrust}")
@@ -118,21 +152,25 @@ class SetpointHandler:
 
         elif self._flight_mode == FlightMode.MIXED_ATTITUDE_TYPE:
 
+            # scales thrust from 1000 for more fine-grained gamepad control.
             thrust = self.setpoint.thrust * 65000 / 1000
             print(f"Set mixed attitude: {self.setpoint.yaw},"
                   f" {self.setpoint.pitch}, "
                   f"{self.setpoint.roll}, {thrust}")
 
-            self.commander.send_mixed_attitude_setpoint(self.setpoint.yaw,
-                self.setpoint.pitch, self.setpoint.roll, thrust)
+            self.commander.send_mixed_attitude_setpoint(
+                self.setpoint.yaw, self.setpoint.pitch, self.setpoint.roll,
+                thrust)
 
         self.setpoint_semaphore.release()
 
     def sendSetpointUnsafe(self):
+        """ Exactly the same as send setpoint but no semaphore is used. """
+
         print("Unsafe mode activate :)")
         if self._flight_mode == FlightMode.ATTITUDE_TYPE:
-
-            thrust = self.setpoint.thrust * 65000 / 1000
+            # scales thrust from 100 for slider control.
+            thrust = self.setpoint.thrust * 65000 / 100
             print(f"Set attitude: {self.setpoint.yaw}, {self.setpoint.pitch}, "
                   f"{self.setpoint.roll}, {self.setpoint.thrust}")
 
@@ -142,7 +180,7 @@ class SetpointHandler:
 
         elif self._flight_mode == FlightMode.ATTITUDE_RATE_TYPE:
 
-            thrust = self.setpoint.thrust * 65000 / 1000
+            thrust = self.setpoint.thrust * 65000 / 100
             print(f"Set attitude rate: {self.setpoint.yaw},"
                   f" {self.setpoint.pitch}, "
                   f"{self.setpoint.roll}, {thrust}")
@@ -152,7 +190,7 @@ class SetpointHandler:
                 thrust)
 
         elif self._flight_mode == FlightMode.MIXED_ATTITUDE_TYPE:
-
+            # scales thrust from 1000 for more fine-grained gamepad control.
             thrust = self.setpoint.thrust * 65000 / 1000
             print(f"Set mixed attitude: {self.setpoint.yaw},"
                   f" {self.setpoint.pitch}, "
diff --git a/pycrocart/SetpointMenu.py b/pycrocart/SetpointMenu.py
index 7123ebeb1..a4a3580e8 100644
--- a/pycrocart/SetpointMenu.py
+++ b/pycrocart/SetpointMenu.py
@@ -83,7 +83,7 @@ class SetpointMenu(QWidget):
 
         # Thrust is normally between 0 and 100. Scaled correctly to crazyflie
         # in the setpoint handler. For flypi, you only want it between 24% and
-        # 34%, and so instead everything is on a 1000 scale and you are adding
+        # 34%, and so instead everything is on a 1000 scale, and you are adding
         # the gamepad input to 240 up to 340.
         self.thrust_slider.setMinimum(0)
         self.thrust_slider.setMaximum(100)
@@ -136,7 +136,7 @@ class SetpointMenu(QWidget):
         """
         This is a function on a timer whenever we are attempting to send
         setpoints. Grabs the setpoints from the setpoint boxes and thrust
-        slider. It also checks whether or not we are in gamepad mixed
+        slider. It also checks whether we are in gamepad mixed
         attitude control mode, rate mode, or attitude mode.
 
         """
-- 
GitLab