Beispiel #1
0
    def pc_calibration_measurement(self, calibration_settings):

        null_pulse = LightPulse(calibration_settings)

        self.add_to_queue(null_pulse.complete_waveform, calibration_settings)

        return self.single_measurement()
Beispiel #2
0
    def load_settings(self, event):
        """
        Loads settings from the forms
        """
        # TODO: change so just fills form out
        # and then reads into data structure when press perform measurement

        self.view1.clear_experiment_form()

        self.measurement_handler.clear_queue()
        file_dir, file_name = self.view1.ask_user_for_filename(
            defaultDir=self.app_dir,
            message='Choose a file',
            wildcard='*.*',
            style=wx.OPEN)
        if file_dir is not None:
            # try:
            config_dict = load_metadata(file_name, file_dir)
            settings = self._parse_config(config_dict)
            for setting in settings["experiment_settings"]:
                self.measurement_handler.add_to_queue(
                    LightPulse(setting).create_waveform(), setting)
            self.temperature_settings = settings["temp_settings"]
            # except Exception as e:
            #     print("An Exception occurred: {0}".format(e))
        self.view1.set_experiment_form(self.measurement_handler.as_list())

        self.view1.set_temperature_form(self.temperature_settings.as_dict())
        self.measurement_handler.clear_queue()
Beispiel #3
0
    def upload(self, event):
        """
        Bulk upload settings from a text file
        """
        self.measurement_handler.clear_queue()
        file_dir, file_name = self.view1.ask_user_for_filename(
            defaultDir=self.app_dir,
            style=wx.OPEN,
            message='Choose a file',
            wildcard='*.*')
        if file_dir is not None:
            config_dict = load_metadata(file_name, file_dir)
            settings = self._parse_config(config_dict)
            for setting in settings["experiment_settings"]:
                self.measurement_handler.add_to_queue(
                    LightPulse(setting).create_waveform(), setting)

            self.temperature_settings = settings["temp_settings"]

            self.view1.set_temperature_form(
                self.temperature_settings.as_dict())
            self.view1.disable_all_settings_inputs()
            self.view1.show_info_modal(
                "{0} experiment settings uploaded successfully!".format(
                    len(self.measurement_handler._queue)))
            self.uploaded = True
Beispiel #4
0
    def perform_measurement(self, event):
        """
        Perform the queued measurements
        """

        try:
            if not self.uploaded:
                # try to retrieve settings from the various forms
                config_dict = {}
                config_dict["temperature_settings"] = (
                    self.view1.get_temperature_form())
                config_dict["wafer_settings"] = self.view1.get_wafer_form()
                config_dict["experiment_settings"] = (
                    self.view1.get_experiment_form())
                try:
                    settings = self._parse_config(config_dict)
                except KeyError as e:
                    self.view1.show_error_modal(str(e))

                for setting in settings["experiment_settings"]:
                    self.measurement_handler.add_to_queue(
                        LightPulse(setting).create_waveform(), setting)
                self.wafer_settings = settings["wafer_settings"]
                self.temperature_settings = settings["temp_settings"]

            if self.data_dir is not None:
                save_metadata(self.wafer_settings.as_dict(), self.data_dir,
                              self.wafer_settings.id)

            if self.measurement_handler.is_queue_empty():
                raise (PVInputError("No measurements loaded."))

            # Do the actual measurements
            # TODO: refactor in separate method

            dataset_list = []
            total_measurements = 0
            self.PlotModal = PlotModal(self.app)
            self.PlotModal.Show()
            while not self.measurement_handler.is_queue_empty():

                single_dataset = self.measurement_handler.single_measurement()
                dataset_list.append(single_dataset)
                total_measurements = total_measurements + 1
                ts = int(time.time())
                dataset_name = (str(total_measurements) +
                                self.wafer_settings.id + str(ts))
                save_data(single_dataset, dataset_name, self.data_dir)

                # print single_dataset.shape
                self.PlotModal.plot_data([x for x in single_dataset[:, 1:].T])

        except PVInputError as e:
            self.view1.show_error_modal(str(e))
Beispiel #5
0
    def __init__(self, parent):

        # models
        self.metadata = ExperimentSettings()

        self.Data = ExperimentData(metadata=self.metadata)

        self.light_pulse = LightPulse(self.metadata)

        # setup file data
        self.dirname = os.getcwd()
        self.data_file = "untitled.dat"
        self.metadata_file = "untitled.inf"

        self.__InitUI(parent)
        self.__InitValidators()
Beispiel #6
0
class WaveformThreadTest(unittest.TestCase):
    # np.set_printoptions(threshold='nan')
    def setUp(self):
        self.settings = ExperimentSettings()
        self.lp = LightPulse(self.settings)

    def test_init(self, mock_waveform_thread):
        WaveformThread(
            waveform=self.lp.create_waveform(),
            Channel='ao1',
            Time=np.float64(1.011),
            input_voltage_range=10.0,
            output_voltage_range=self.settings.output_voltage_range,
            input_sample_rate=self.settings.sample_rate,
            output_sample_rate=self.settings.output_sample_rate
        )
Beispiel #7
0
    def display(self, event):
        """
        Plot waveforms in modal
        """
        self.PlotModal = PlotModal(self.app)

        waveform_list = []
        settings_dict = {}

        try:
            settings_dict[
                "experiment_settings"] = self.view1.get_experiment_form()
            experiment_settings = self._parse_experiment_settings(
                settings_dict)
            for setting in experiment_settings:
                waveform_list.append(-LightPulse(setting).create_waveform())

            self.PlotModal.Show()
            self.PlotModal.plot_data(waveform_list)
        except PVInputError as e:
            self.view1.show_error_modal(str(e))
Beispiel #8
0
    def single_measurement(self):
        '''
        This runs a single measurements:
            1. Sets the femto gain
            2. Generatres a waveform
            3. Sends command to DAQ
            4. Does next item
        '''
        # get measurement from list
        measurement_settings = self._queue.popleft()
        print 'The measurement settings are', type(measurement_settings)
        print measurement_settings.ref_gain
        print measurement_settings.pl_gain

        # set preamp gains
        self.preamps.configure(measurement_settings)

        # gets the voltage pulse to control the waveform
        waveform_array = LightPulse(measurement_settings).create_waveform()
        # plt.figure()
        # plt.plot(waveform_array)
        # plt.show()

        # initalises the DAQ ready for a measurement
        thread = self.daq._int_thread(waveform_array, measurement_settings)

        # lets get the averaging
        averaging = measurement_settings.averaging

        # set the variables before they are created?
        thread_time = None
        measurement_data = []

        # perform a measurement
        measurement_data, thread_time = self.daq.run_thread(thread)

        # make sure the length makes sense
        data_len = measurement_data.shape[0] / self.daq.NUM_CHANNELS
        # print type(data_len), data_len
        # print
        assert (data_len.is_integer(),
                "No of points per channel={0}".format(data_len))
        data_len = int(data_len)

        # if more averages were requested
        if averaging > 1:
            for i in range(averaging - 1):
                thread_data, thread_time = self.daq.run_thread(thread)
                # stack the data
                measurement_data = np.vstack((thread_data, measurement_data))
                # average the data, weighting it towards
                # running total
                measurement_data = np.average(measurement_data,
                                              axis=0,
                                              weights=(1, i + 1))

        # convert into columns, putting the right data
        # in the right place
        measurement_data = measurement_data[:data_len *
                                            int(self.daq.NUM_CHANNELS)]
        measurement_data = measurement_data.reshape(
            (data_len, int(self.daq.NUM_CHANNELS)), order='F')

        assert measurement_data.shape[0] == thread_time.shape[
            0], 'data Length differ {0} {1}'.format(measurement_data.shape,
                                                    thread_time.shape[0])

        measurement_data = np.vstack((thread_time, measurement_data.T)).T
        measurement_data = bin_data(measurement_data,
                                    measurement_settings.binning)

        return measurement_data
Beispiel #9
0
class GUIController(FrameSkeleton):
    """
    Controller to handle interface with wx UI

    Attributes
    ----------


    MyFrame1:
    Fig1
    Data

    LoadPath
    Path
    measurement_type


    """

    def __init__(self, parent):

        # models
        self.metadata = ExperimentSettings()

        self.Data = ExperimentData(metadata=self.metadata)

        self.light_pulse = LightPulse(self.metadata)

        # setup file data
        self.dirname = os.getcwd()
        self.data_file = "untitled.dat"
        self.metadata_file = "untitled.inf"

        self.__InitUI(parent)
        self.__InitValidators()

    def __InitUI(self, parent):
        # initialize parent class
        FrameSkeleton.__init__(self, parent)

        # setup Matplotlib Canvas panel
        self.Fig1 = CanvasPanel(self.Figure1_Panel)

        # make status bars
        m_statusBar = wx.StatusBar(self)

        self.SetStatusBar(m_statusBar)
        # self.m_statusBar.SetStatusText("Ready to go!")

        self.data_panel = DataProcessingPanel(self.m_notebook1)

        self.m_notebook1.AddPage(
            self.data_panel,
            u"Data Processing",
            # True
        )

        # Setup the Menu
        menu_bar = wx.MenuBar()

        # File Menu
        filem = wx.Menu()
        filem.Append(wx.ID_OPEN, "Open\tCtrl+O")
        filem.Append(wx.ID_ANY, "Run\tCtrl+R")
        filem.Append(wx.ID_ABOUT, "About")
        filem.Append(wx.ID_EXIT, "Exit\tCtrl+X")

        # TODO: add and bind event handlers
        menu_bar.Append(filem, "&File")

        self.SetMenuBar(menu_bar)

        # initialise view with model parameters
        self.m_voltageRange.AppendItems(INPUT_VOLTAGE_RANGE_STR)
        self.m_Waveform.AppendItems(WAVEFORMS)
        self.m_Output.AppendItems(OUTPUTS)

        self.setWaveformParameters()
        self.setCollectionParameters()

    def __InitValidators(self):

        # Waveform validators
        self.m_Intensity.SetValidator(NumRangeValidator(numeric_type='int'))
        self.m_Threshold.SetValidator(NumRangeValidator(numeric_type='int'))
        self.m_Period.SetValidator(NumRangeValidator(numeric_type='float'))
        self.m_Offset_Before.SetValidator(NumRangeValidator(numeric_type='float'))
        self.m_Offset_After.SetValidator(NumRangeValidator(numeric_type='float'))

        # Data collection validators
        self.m_samplingFreq.SetValidator(NumRangeValidator(numeric_type='float'))
        self.m_Averaging.SetValidator(NumRangeValidator(numeric_type='int'))
        self.m_Binning.SetValidator(NumRangeValidator(numeric_type='int'))

    def setPCCalibrationMean(self):
        self.m_pcCalibrationMean.SetValue('{0:3.3f}'.format(
            self.metadata.pc_calibration_mean
        ))

    def setPCCalibrationStd(self):
        self.m_pcCalibrationStd.SetValue('{0:3.3f}'.format(
            self.metadata.pc_calibration_std
        ))

    def setSampleDataPoints(self, sample_data_points):
        self.m_DataPoint.SetValue('{0:.2e}'.format(sample_data_points))

    def setFrequency(self, frequence_val):
        self.m_Frequency.SetValue('{0:3.3f}'.format(frequence_val))

    def setWaveformParameters(self):
        self.m_Intensity.SetValue(str(self.metadata.amplitude))
        self.m_Period.SetValue(str(self.metadata.duration))
        self.m_Offset_Before.SetValue(str(self.metadata.offset_before))
        self.m_Offset_After.SetValue(str(self.metadata.offset_after))
        self.setFrequency(self.metadata.get_frequency())

    def setCollectionParameters(self):
        self.m_Binning.SetValue(str(self.metadata.binning))
        self.m_Averaging.SetValue(str(self.metadata.averaging))
        self.m_Threshold.SetValue(str(self.metadata.threshold))
        self.m_samplingFreq.SetValue(str(self.metadata.sample_rate))

        self.setSampleDataPoints(self.metadata.get_total_data_points())


    #################################
    # Event Handlers for Measurements
    def Perform_Measurement(self, event):

        print("GUIController: Perform_Measurement")

        # Using that instance we then run the lights,
        # and measure the outputs
        self.measurement_handler = MeasurementHandler()
        measurement_handler.add_to_queue(
            self.light_pulse.complete_waveform,
            self.metadata
        )

        raw_data = self.measurement_handler.single_measurement()
        self.Data.updateRawData(raw_data)
        self.Data.Data = utils.bin_data(raw_data, self.metadata.binning)
        # We then plot the datas, this has to be changed if the plots want
        # to be updated on the fly.

        pub.sendMessage('update.plot')


    def fftHandler(self):
        channel = self.data_panel.m_fftChoice.GetStringSelection()
        freq_data = self.Data.fftOperator(channel, self.metadata.get_total_time())
        self.PlotData(freq_data, title=['FFT of Raw data', 'Frequency (hz)', 'Voltage (V)'])


    def onWaveformParameters(self, event):
        self.metadata.A = float(self.m_Intensity.GetValue())
        self.metadata.Duration = float(self.m_Period.GetValue())
        self.metadata.Offset_Before = float(self.m_Offset_Before.GetValue())
        self.metadata.Offset_After = float(self.m_Offset_After.GetValue())
        self.metadata.Waveform = self.m_Waveform.GetStringSelection()

        pub.sendMessage('waveform.change')


    def onCollectionParameters(self, event):
        # TODO: refactor so it obeys DRY

        self.metadata.binning = int(self.m_Binning.GetValue())
        self.metadata.averaging = int(self.m_Averaging.GetValue())
        self.metadata.channel = self.m_Output.GetStringSelection()
        self.metadata.threshold = float(self.m_Threshold.GetValue())
        self.metadata.sample_rate = float(self.m_samplingFreq.GetValue())
        self.metadata.sample_data_points = self.metadata.get_total_data_points()

        pub.sendMessage('collection.change')

    ##########################
    # Plot Handlers
    #

    def plotHandler(self):
        self.PlotData(self.Data.Data)

    def PlotData(self, data, data_labels=['Reference', 'PC', 'PL'],
                 title=['Raw Data', 'Time (s)', 'Voltage (V)'], e=None):

        self.Fig1.clear()
        labels = data_labels
        colours = ['b', 'r', 'g']

        # this is done not to clog up the plot with many points
        if data.shape[0] > 1000:
            num = data.shape[0] // 1000
        else:
            num = 1

        # This plots the figure
        for i, label, colour in zip(data[:, 1:].T, labels, colours):

            self.Fig1.draw_points(
                data[::num, 0],
                i[::num],
                '.',
                Color=colour,
                Label=label
            )

        self.Fig1.legend()
        self.Fig1.labels(title[0], title[1], title[2])
        self.Fig1.update()
        if e is not None:
            e.skip()

    #################
    # Helper methods:
    def defaultFileDialogOptions(self):
        """
        Return a dictionary with file dialog options that can be
        used in both the save file dialog as well as in the open
        file dialog.
        """
        return dict(message='Choose a file', defaultDir=self.dirname, wildcard='*.*')

    def askUserForFilename(self, **dialogOptions):
        dialog = wx.FileDialog(self, **dialogOptions)
        if dialog.ShowModal() == wx.ID_OK:
            userProvidedFilename = True
            self.data_file = dialog.GetFilename()
            self.dirname = dialog.GetDirectory()
        else:
            userProvidedFilename = False
        dialog.Destroy()
        return userProvidedFilename

    def ShowCalibrationModal(self):
        msg_text = (
            "Please remove sample from measurement area\n"
            "Only one PC calibration is necessary per experimental session"
        )
        wx.MessageBox(msg_text, 'Info', wx.OK | wx.ICON_INFORMATION)

    ##########################
    # App state Event Handlers
    #
    def onSave(self, event):
        """
        Method to handle dialogue window and saving data to file
        """

        dialog = wx.FileDialog(
            None,
            'Save measurement data and metadata',
            self.dirname,
            '',
            r'DAT and INF files (*.dat;*.inf)|*.inf;*.dat',
            wx.FD_SAVE
        )

        if dialog.ShowModal() == wx.ID_OK:
            dialog_path = dialog.GetPath()
            self.dirname = os.path.dirname(dialog_path)
            self.SaveName = os.path.splitext(os.path.basename(dialog_path))[0]

            experiment_settings = self.metadata.get_settings_as_dict()
            waveform_settings = self.light_pulse.get_settings_as_dict()
            metadata_dict = experiment_settings.copy()

            utils.save_data(self.Data.Data, self.SaveName, self.dirname)
            utils.save_metadata(
                metadata_dict,
                self.SaveName,
                self.dirname
            )

        else:
            print('Canceled save')
        dialog.Destroy()

        event.Skip()

    def onSaveData(self, event):
        print("onSaveData")
        if self.askUserForFilename(style=wx.SAVE,
                                   **self.defaultFileDialogOptions()):
            utils.save_data(self.Data.Data, self.data_file, self.dirname)

        # print(filename)
        # fullpath = os.path.join(self.dirname, self.data_file)
        # self.Data.updateRawData(utils.load_data(fullpath))


    def onLoad(self, event):
        """
        Method to handle load metadata dialog window and update metadata state
        """

        dialog = wx.FileDialog(
            None,
            'Select a metadata file',
            self.dirname,
            '',
            r'*.inf',
            wx.FD_OPEN
        )

        if dialog.ShowModal() == wx.ID_OK:

            metadata_dict = utils.load_metadata(dialog.GetPath())
            metadata_stringified = dict(
                [a, str(x)] for a, x in metadata_dict.iteritems()
            )
            print(metadata_stringified)

            # experimental data
            self.m_Output.SetStringSelection(metadata_stringified[u'Channel'])
            self.m_Averaging.SetValue(metadata_stringified[u'Averaging'])
            try:
                self.m_Binning.SetValue(metadata_stringified[u'Measurement_Binning'])
            except:
                self.m_Binning.SetValue(metadata_stringified[u'Binning'])
            self.m_Threshold.SetValue(metadata_stringified[u'Threshold_mA'])


            # waveform data
            self.m_Intensity.SetValue(metadata_stringified[u'Intensity_v'])
            self.m_Waveform.SetStringSelection(metadata_stringified[u'Waveform'])

            self.m_Offset_Before.SetValue(metadata_stringified[u'Offset_Before_ms'])
            self.m_Offset_After.SetValue(metadata_stringified[u'Offset_After_ms'])
            self.m_Period.SetValue(metadata_stringified[u'Peroid_s'])

        dialog.Destroy()
        event.Skip()

    def onLoadData(self, event):
        """
        Handlers loading of new data set into Frame
        """
        print("start data loading")
        if self.askUserForFilename(style=wx.OPEN,
                                   **self.defaultFileDialogOptions()):
            fullpath = os.path.join(self.dirname, self.data_file)
            self.Data.updateRawData(utils.load_data(fullpath))

            print(self.Data.Data)
            pub.sendMessage('update.plot')
            self.onWaveformParameters(self, event)
            self.onCollectionParameters(self, event)

    def onExit(self, event):
        self.Close()

    def OnAbout(self, event):
        dialog = wx.MessageDialog(
            self,
            'An PV experimental assistant in wxPython',
            'About PVapp',
            wx.OK
        )
        dialog.ShowModal()
        dialog.Destroy()


    def onStatusUpdate(self, status, is_error=False):

        self.SetStatusText(status, 0)
        if is_error:
            self.m_statusBar.SetBackgroundColour('RED')
            self.m_statusBar.SetStatusText(status)
        else:
            self.m_statusBar.SetStatusText(status)
Beispiel #10
0
 def setUp(self):
     self.settings = ExperimentSettings()
     self.lp = LightPulse(self.settings)
Beispiel #11
0
class MeasurementHandlerTest(unittest.TestCase):
    # np.set_printoptions(threshold='nan')
    def setUp(self):
        self.settings = ExperimentSettings()

        self.lp = LightPulse(self.settings)

    def test__run_thread(self, mock_waveform_thread):

        mock_waveform_thread.return_value.time = 3
        mock_waveform_thread.return_value.Read_Data = np.array([])

        handler = MeasurementHandler()
        handler.add_to_queue(self.lp.create_waveform(), self.settings)
        # handler._run_thread()

    def test_single_measurement_no_averaging(self, mock_waveform_thread):

        handler = MeasurementHandler()
        handler.add_to_queue(self.lp.create_waveform(), self.settings)
        return_tuple = (np.array([1, 2, 3, 4, 5, 6]), np.array([0, 1]))

        with patch.object(handler, '_run_thread',
                          return_value=return_tuple) as method:
            test_dataset = handler.single_measurement()
            self.assertEqual(1, method.call_count)
            np.testing.assert_array_equal(
                test_dataset,
                np.array([[0, 1, 3, 5], [1, 2, 4, 6]])
            )

        self.assertEqual(len(handler._queue), 0)

    def test_single_measurement_with_averaging(self, mock_waveform_thread):
        # setup
        settings = ExperimentSettings()
        settings.averaging = 2
        handler = MeasurementHandler()
        return_tuple = [
            (np.array([2, 3, 4, 5, 6, 7]), np.array([0, 1])),
            (np.array([3, 4, 5, 6, 7, 8]), np.array([0, 1])),
        ]

        handler.add_to_queue(self.lp.create_waveform(), settings)

        with patch.object(handler, '_run_thread', side_effect=return_tuple) as method:
            # perform
            test_dataset = handler.single_measurement()

            # assert
            self.assertEqual(2, method.call_count)
            np.testing.assert_array_equal(
                test_dataset,
                np.array([[0, 2.5, 4.5, 6.5], [1, 3.5, 5.5, 7.5]])
            )

        self.assertEqual(len(handler._queue), 0)


    def test_add_to_queue(self, mock_waveform_thread):
        handler = MeasurementHandler()
        handler.add_to_queue(self.lp.create_waveform(), self.settings)
        self.assertEqual(len(handler._queue), 1)

    @log_capture()
    def test_series_measurement_empty_queue(self, mock_waveform_thread,
                                            log_checker):

        handler = MeasurementHandler()

        observed_data_list = handler.series_measurement()
        self.assertEqual(
            len(observed_data_list), 0)

        log_checker.check(
            ('root', 'INFO', 'Total: 0 measurements performed'),
        )

    @log_capture()
    def test_series_measurement_one_in_queue(self, mock_waveform_thread,
                                             log_checker):

        # setup
        handler = MeasurementHandler()
        print(handler._queue)

        handler.add_to_queue(self.lp.create_waveform(), self.settings)
        side_effect_array = [
            np.array([[0, 2.5, 4.5, 6.5], [1, 3.5, 5.5, 7.5]])
        ]

        with patch.object(handler, 'single_measurement',
                          side_effect=side_effect_array) as method:
            # perform
            observed_data_list = handler.series_measurement()

        # assert
        for observed_data in observed_data_list:
            np.testing.assert_array_equal(
                observed_data,
                np.array([[0, 2.5, 4.5, 6.5], [1, 3.5, 5.5, 7.5]])
            )

        log_checker.check(
            ('root', 'INFO', 'Measurement #1 complete'),
            ('root', 'INFO', 'Total: 1 measurements performed'),
        )

    @log_capture()
    def test_series_measurement_multiple_queue(self, mock_waveform_thread,
                                               log_checker):

        side_effect_array = [
            np.array([[0, 2.5, 4.5, 6.5], [1, 3.5, 5.5, 7.5]]),
            np.array([[0, 2.5, 4.5, 6.5], [1, 3.5, 5.5, 7.5]]),
            np.array([[0, 2.5, 4.5, 6.5], [1, 3.5, 5.5, 7.5]])
        ]

        handler = MeasurementHandler()

        handler.add_to_queue(self.lp.create_waveform(), self.settings)
        handler.add_to_queue(self.lp.create_waveform(), self.settings)
        handler.add_to_queue(self.lp.create_waveform(), self.settings)

        with patch.object(handler, 'single_measurement',
                          side_effect=side_effect_array) as method:
            observed_data_list = handler.series_measurement()

        for observed_data in observed_data_list:
            np.testing.assert_array_equal(
                observed_data,
                np.array([[0, 2.5, 4.5, 6.5], [1, 3.5, 5.5, 7.5]])
            )
        log_checker.check(
            ('root', 'INFO', 'Measurement #1 complete'),
            ('root', 'INFO', 'Measurement #2 complete'),
            ('root', 'INFO', 'Measurement #3 complete'),
            ('root', 'INFO', 'Total: 3 measurements performed'),
        )
Beispiel #12
0
class LightPulseTest(unittest.TestCase):
    # np.set_printoptions(threshold='nan')
    def setUp(self):
        self.lp = LightPulse(ExperimentSettings())

    # def test_init_

    def test_can_create_pulse_array(self):

        pulse_array = self.lp.create_waveform()
        self.assertTrue(np.any(pulse_array))

        # make value
        self.assertTrue(np.any(np.amax(pulse_array) < 150.0))

        self.assertTrue(self.lp.A > 0)

        initial_offset = int(1.2)
        np.testing.assert_array_equal(np.zeros(initial_offset),
                                      pulse_array[:initial_offset])

        final_offset = int(12.0)
        np.testing.assert_array_equal(np.zeros(final_offset),
                                      pulse_array[-final_offset:])

        self.assertEqual(len(pulse_array[initial_offset:-final_offset]), 1200)

    def test_scale_to_threshold(self):
        # setup tests
        self.lp.Voltage_Threshold = 0.08152173913043478
        voltage_waveform = 6 * np.ones(10)
        test_array = np.array([
            5.83695652174, 5.83695652174, 5.83695652174, 5.83695652174,
            5.83695652174, 5.83695652174, 5.83695652174, 5.83695652174,
            5.83695652174, 5.83695652174
        ])

        #
        scaled_array = self.lp.scale_to_threshold(voltage_waveform)

        # assertions
        np.testing.assert_almost_equal(scaled_array,
                                       test_array,
                                       decimal=11,
                                       verbose=True)

    def test_final_pulse_array_function(self):
        example_array = np.loadtxt(
            "test/data/sample_sin_waveformthread_write_data.csv")
        self.lp.Waveform = "Sin"
        pulse_array = self.lp.create_waveform()
        np.savetxt('test_sine_array', pulse_array)
        np.testing.assert_almost_equal(pulse_array, example_array)

    def test_sin_function(self):
        pulse_array = self.lp.Sin(np.linspace(0, 1, 1200), 0.5)
        t = np.linspace(0, 1, 1200)
        amp = 0.5
        pi = np.pi
        np.testing.assert_array_equal(pulse_array,
                                      -(amp) * np.abs(np.sin(pi * t / t[-1])))

    def test_square_function(self):
        pulse_array = self.lp.Square(np.linspace(0, 5, 5), -6)
        self.assertTrue(np.all(pulse_array == (6 * np.ones(5))))

    # def test_cos_function(self):
    #     pass

    def test_triangle_function(self):
        pulse_array = self.lp.Triangle(np.arange(5), -4)
        self.assertTrue(np.all(pulse_array == np.array([0, 2, 4, 2, 0])))