diff --git a/examples/main.py b/examples/main.py
index 902aa55c54300c74b7bd67c61a41b4e87afd0a17..963bcc6b233562022328cdd957b1f955df4f00ef 100644
--- a/examples/main.py
+++ b/examples/main.py
@@ -11,6 +11,9 @@ from rich.logging import RichHandler
 import CaptDeviceControl as CaptDevice
 
 
+
+#logging.disable(logging.INFO)
+
 if __name__ == "__main__":
 
     def setup_logging():
@@ -35,7 +38,7 @@ if __name__ == "__main__":
 
 
     model = CaptDevice.Model(conf)
-    controller = CaptDevice.Controller(model)
+    controller = CaptDevice.Controller(model, None)
     window = CaptDevice.View(model, controller)
 
     window.show()
diff --git a/src/CaptDeviceControl/CaptDeviceConfig.py b/src/CaptDeviceControl/CaptDeviceConfig.py
index 273afe6d7326646355ec6e11e2345959f0a80e02..fdc0a46616cf6bad4c4c52ef7f3695641bade73f 100644
--- a/src/CaptDeviceControl/CaptDeviceConfig.py
+++ b/src/CaptDeviceControl/CaptDeviceConfig.py
@@ -11,7 +11,7 @@ class CaptDeviceConfig(cfg.ConfigNode):
 
     def __init__(self) -> None:
         super().__init__()
-        self.sample_rate = cfg.Field(50000, friendly_name="Sample rate",
+        self.sample_rate = cfg.Field(500, friendly_name="Sample rate",
                                      description="Sample rate of the device")
         self.streaming_rate = cfg.Field(500, friendly_name="Streaming rate",
                                      description="Streaming rate in Hz (should be below 1kHz)")
diff --git a/src/CaptDeviceControl/__init__.py b/src/CaptDeviceControl/__init__.py
index 65ab4a9424daabd37b081ffb246d53c7ce4a46a8..eb96e39a703665131ee83f15829b3b6f0cdb3b8b 100644
--- a/src/CaptDeviceControl/__init__.py
+++ b/src/CaptDeviceControl/__init__.py
@@ -6,6 +6,6 @@ Package Version:
 """
 import sys
 from .CaptDeviceConfig import CaptDeviceConfig as Config
-from .controller.AD2CaptDeviceController import AD2CaptDeviceController as Controller
+from .controller.BaseAD2CaptDevice import BaseAD2CaptDevice as Controller
 from .model.AD2CaptDeviceModel import AD2CaptDeviceModel as Model
 from .view.AD2CaptDeviceView import ControlWindow as View
\ No newline at end of file
diff --git a/src/CaptDeviceControl/controller/AD2CaptDeviceController.py b/src/CaptDeviceControl/controller/AD2CaptDeviceController.py
deleted file mode 100644
index 24bff829e27a76bb3f409dfb2c98f4404d8358e1..0000000000000000000000000000000000000000
--- a/src/CaptDeviceControl/controller/AD2CaptDeviceController.py
+++ /dev/null
@@ -1,290 +0,0 @@
-#!/.venv/Scripts/python
-
-from ctypes import c_int, byref, create_string_buffer, cdll, c_int32, c_uint, c_double
-
-from CaptDeviceControl.controller.BaseAD2CaptDevice import BaseAD2CaptDevice
-from CaptDeviceControl.model.AD2CaptDeviceModel import AD2CaptDeviceModel
-#from .controller.BaseAD2CaptDevice import BaseAD2CaptDevice
-#from .model.AD2CaptDeviceModel import AD2CaptDeviceModel
-
-from CaptDeviceControl.constants.dwfconstants import enumfilterUSB, enumfilterType, enumfilterDemo
-from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDeviceControl import MPCaptDeviceControl
-
-
-class AD2CaptDeviceController(BaseAD2CaptDevice):
-
-    def __init__(self, ad2capt_model: AD2CaptDeviceModel):
-        self.dwf = cdll.dwf
-        super().__init__(ad2capt_model)
-
-
-
-        # This is required for acquiring the data
-
-
-
-    def read_hardware_config(self, iDevice):
-        hw_info_dict = {}
-        hdwf = c_int()
-        int0 = c_int()
-        int1 = c_int()
-        uint0 = c_uint()
-        dbl0 = c_double()
-        dbl1 = c_double()
-        dbl2 = c_double()
-        self.dwf.FDwfDeviceConfigOpen(c_int(iDevice), c_int(0), byref(hdwf))
-        if hdwf.value == 0:
-            szerr = create_string_buffer(512)
-            self.dwf.FDwfGetLastErrorMsg(szerr)
-            raise Exception(str(szerr.value))
-
-        self.dwf.FDwfAnalogInChannelCount(hdwf, byref(int0))
-        hw_info_dict["analog_in_channels"] = int(int0.value)
-
-        self.dwf.FDwfAnalogIOChannelCount(hdwf, byref(int0))
-        hw_info_dict["analog_io_channels"] = int(int0.value)
-
-        self.dwf.FDwfAnalogInBufferSizeInfo(hdwf, 0, byref(int0))
-        hw_info_dict["buffer_size"] = int(int0.value)
-
-        self.dwf.FDwfAnalogInBitsInfo(hdwf, byref(int0))
-        hw_info_dict["adc_bits"] = int(int0.value)
-
-        self.dwf.FDwfAnalogInChannelRangeInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
-        hw_info_dict["range"] = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
-
-        self.dwf.FDwfAnalogInChannelOffsetInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
-        hw_info_dict["offset"] = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
-
-        return hw_info_dict
-
-
-    def discover_connected_devices(self):
-        pass
-        #self.mpcaptdevicecontrol.discover_connected_devices()
-
-
-
-    # def _open_device(self, device_index):
-    #     devicename = create_string_buffer(64)
-    #     serialnum = create_string_buffer(16)
-    #
-    #     self.dwf.FDwfEnumDeviceName(c_int(device_index), devicename)
-    #     self.dwf.FDwfEnumSN(c_int(device_index), serialnum)
-    #
-    #     self.model.device_name = devicename
-    #     self.model.device_serial_number = serialnum
-    #     # open device
-    #     self.logger.info(f"[{self.pref} Task] Opening device #{device_index}...")
-    #
-    #     # Opens a device identified by the enumeration index and retrieves a handle. To automatically
-    #     # enumerate all connected devices and open the first discovered device, use index -1.
-    #     self.dwf.FDwfDeviceOpen(c_int(device_index), byref(self.model.hdwf))
-    #
-    #     if self.model.hdwf.value == hdwfNone.value:
-    #         szerr = create_string_buffer(512)
-    #         self.dwf.FDwfGetLastErrorMsg(szerr)
-    #         # print(str(szerr.value))
-    #         self.model.connected = False
-    #         raise Exception(f"Failed to open device: {szerr.value}")
-    #     else:
-    #         self.model.connected = True
-    #     self.get_analog_in_status()
-    #     self.logger.info(f"[{self.pref} Task] Device connected!")
-    #
-    # def _setup_acquisition(self):
-    #     # set up acquisition
-    #     self.get_analog_in_status()
-    #     self.logger.debug(f"[{self.pref} Task] Setup for acquisition. Wait 2 seconds for the offset to stabilize.")
-    #     self.dwf.FDwfAnalogInChannelEnableSet(self.model.hdwf, c_int(self.model.analog_in_channel), c_int(1))
-    #     self.dwf.FDwfAnalogInChannelRangeSet(self.model.hdwf, c_int(self.model.analog_in_channel), c_double(5))
-    #     self.dwf.FDwfAnalogInAcquisitionModeSet(self.model.hdwf, acqmodeRecord)
-    #     self.dwf.FDwfAnalogInFrequencySet(self.model.hdwf, c_double(self.model.hz_acquisition))
-    #     self.dwf.FDwfAnalogInRecordLengthSet(self.model.hdwf, 0)  # -1 infinite record length
-    #     self.get_analog_in_status()
-    #     # wait at least 2 seconds for the offset to stabilize
-    #     time.sleep(2)
-    #     self.logger.info(f"[{self.pref} Task] Setup for acquisition done.")
-    #     return True
-    #
-    # # # ==================================================================================================================
-    # # #   Acquisition
-    # # # ==================================================================================================================
-    # # @Slot()
-    # # def start_capture(self, capture):
-    # #     if not self.model.connected:
-    # #         self.logger.warning(f"[{self.pref} Task] No device connected. Connecting to first device.")
-    # #         self.connect_device(0)
-    # #         return False
-    # #
-    # #     if capture:
-    # #         self.logger.info(f"[{self.pref} Task] Setting up device for capturing.")
-    # #         self.gen_sine()
-    # #         self._setup_acquisition()
-    # #         # if self._setup_acquisition():
-    # #         #    self.logger.info(f"[{self.pref} Task] Started capturing thread")
-    # #         self.set_ad2_acq_status(True)
-    # #         return self.thread_manager.start(self._capture)
-    # #     else:
-    # #         self.set_ad2_acq_status(False)
-    # #     # return self._capture()
-    # #
-    # # def _capture(self):
-    # #     self.model.capturing_finished = False
-    # #     self.model.device_capturing = False
-    # #     cAvailable = c_int()
-    # #     cLost = c_int()
-    # #     cCorrupted = c_int()
-    # #
-    # #     self.logger.info(f"[{self.pref} Report] Capturing started. "
-    # #                      f"Waiting for start command: "
-    # #                      f"{self.model.start_recording}<->{self.model.stop_recording}")
-    # #
-    # #     # Configures the instrument and start or stop the acquisition. To reset the Auto trigger timeout, set
-    # #     # fReconfigure to TRUE.
-    # #     # print("Starting oscilloscope")
-    # #     self.dwf.FDwfAnalogInConfigure(self.model.hdwf, c_int(0), c_int(1))
-    # #
-    # #     t0 = -1
-    # #
-    # #     cSamples = 0
-    # #     self.model.device_ready = True
-    # #     self.logger.info(f"[{self.pref} Report] Capturing device is ready.")
-    # #     while True:
-    # #
-    # #         if self.model.start_recording and not self.model.stop_recording:
-    # #             if t0 < 0:
-    # #                 self.logger.info(f"[{self.pref} Report] Start command received.")
-    # #                 self.model.capturing_finished = False
-    # #                 self.model.device_capturing = True
-    # #                 self.model.current_recorded_samples = []
-    # #                 timestamp = datetime.now()
-    # #                 t0 = time.time()
-    # #             # print(f"Start ({cSamples})")
-    # #             sts = self.get_analog_in_status()
-    # #
-    # #             if cSamples == 0 and (
-    # #                     sts == DwfStateConfig or
-    # #                     sts == DwfStatePrefill or
-    # #                     sts == DwfStateArmed):
-    # #                 print('idle')
-    # #                 continue  # Acquisition not yet started.
-    # #
-    # #             # Retrieves information about the recording process. The data loss occurs when the device acquisition
-    # #             # is faster than the read process to PC. In this case, the device recording buffer is filled and data
-    # #             # samples are overwritten. Corrupt samples indicate that the samples have been overwritten by the
-    # #             # acquisition process during the previous read. In this case, try optimizing the loop process for faster
-    # #             # execution or reduce the acquisition frequency or record length to be less than or equal to the device
-    # #             # buffer size (record length <= buffer size/frequency).
-    # #             self.dwf.FDwfAnalogInStatusRecord(self.model.hdwf,  # Interface handle
-    # #                                               byref(cAvailable),
-    # #                                               byref(cLost),
-    # #                                               byref(cCorrupted))
-    # #
-    # #             cSamples += cLost.value
-    # #
-    # #             if cLost.value:
-    # #                 self.logger.warning(f"[{self.pref} Report] - Sample(s) lost ({cLost.value})")
-    # #                 self.model.fLost += int(cLost.value)
-    # #             if cCorrupted.value:
-    # #                 self.logger.warning(f"[{self.pref} Report] - Samples(s) corrupted ({cCorrupted.value})")
-    # #                 self.model.fCorrupted += int(cCorrupted.value)
-    # #
-    # #             # self.dwf.FDwfAnalogInStatusSamplesValid(self.hdwf, byref(self.cValid))
-    # #             if cAvailable.value == 0:
-    # #                 # print(f"Nothing available {cAvailable.value}")
-    # #                 continue
-    # #             else:
-    # #                 # print(f"Available: {cAvailable.value}")
-    # #
-    # #                 # if cSamples + cAvailable.value > self.ad2capt_model.n_samples:
-    # #                 #    cAvailable = c_int(self.ad2capt_model.n_samples - cSamples)
-    # #                 rgdSamples = (c_double * cAvailable.value)()
-    # #                 # Retrieves the acquired data samples from the specified idxChannel on the AnalogIn instrument. It
-    # #                 # copies the data samples to the provided buffer.
-    # #                 self.dwf.FDwfAnalogInStatusData(self.model.hdwf,
-    # #                                                 c_int(self.model.analog_in_channel),
-    # #                                                 byref(rgdSamples),
-    # #                                                 cAvailable)  # get channel 1 data
-    # #                 for s in rgdSamples:
-    # #                     self.model.current_recorded_samples.append(float(s))
-    # #
-    # #                 cSamples += cAvailable.value
-    # #
-    # #         elif not self.model.start_recording and self.model.stop_recording:
-    # #             t1 = time.time()
-    # #             self.model.measurement_time = t1 - t0
-    # #             self.get_analog_in_status()
-    # #             self.model.capturing_finished = True
-    # #             self.model.device_capturing = False
-    # #             self.logger.info(f"Finished Thread. Acquisition took {self.model.measurement_time} s. "
-    # #                              f"Process captured {len(self.model.current_recorded_samples)} samples.")
-    # #             # 1. Assign the current captured samples to a dict
-    # #             self.model.all_recorded_samples.append({'timestamp': timestamp,
-    # #                                                     'measurement_time': self.model.measurement_time,
-    # #                                                     'num_samples': len(
-    # #                                                         self.model.current_recorded_samples),
-    # #                                                     'acqRate': self.model.hz_acquisition,
-    # #                                                     'samples': self.model.current_recorded_samples})
-    # #             # Reset status bits
-    # #             try:
-    # #                 time.sleep(1)
-    # #                 self.close_device()
-    # #             except Exception as e:
-    # #                 print(e)
-    # #             return
-    # #             # return self.model.current_recorded_samples, self.model.measurement_time
-    # #         else:
-    # #             self.model.device_capturing = False
-    #
-    # # # ==================================================================================================================
-    # # #
-    # # # ==================================================================================================================
-    # # def gen_sine(self, channel=0, frequency=1):
-    # #     self.logger.debug(f"[{self.pref} Task] Generating sine wave on output 0...")
-    # #     self.dwf.FDwfAnalogOutNodeEnableSet(self.model.hdwf, c_int(channel), AnalogOutNodeCarrier, c_int(1))
-    # #     self.dwf.FDwfAnalogOutNodeFunctionSet(self.model.hdwf, c_int(channel), AnalogOutNodeCarrier,
-    # #                                           funcTrapezium)  # sine
-    # #     self.dwf.FDwfAnalogOutNodeFrequencySet(self.model.hdwf, c_int(channel), AnalogOutNodeCarrier,
-    # #                                            c_double(frequency))  # 1Hz
-    # #     self.dwf.FDwfAnalogOutNodeAmplitudeSet(self.model.hdwf, c_int(channel), AnalogOutNodeCarrier, c_double(2))
-    # #     self.dwf.FDwfAnalogOutConfigure(self.model.hdwf, c_int(channel), c_int(1))
-    # #     return self.model.hdwf
-    #
-    # # ==================================================================================================================
-    # #
-    # # ==================================================================================================================
-    # def close_device(self):
-    #     # Resets and configures (by default, having auto configure enabled) all AnalogOut instrument
-    #     # parameters to default values for the specified channel. To reset instrument parameters across all
-    #     # channels, set idxChannel to -1.
-    #     self.model.fLost = 0
-    #     self.model.fCorrupted = 0
-    #     self.model.start_recording = True
-    #     self.model.stop_recording = False
-    #     self.model.capturing_finished = False
-    #     self.model.device_capturing = False
-    #     self.model.connected = False
-    #     self.model.device_state = 0
-    #     self.model.dwf_version = "Unknown"
-    #     self.model.device_serial_number = "Unknown"
-    #     self.model.device_name = "Unknown"
-    #     self.model.analog_in_channel = -1
-    #
-    #     self.dwf.FDwfAnalogOutReset(self.model.hdwf, c_int(self.model.analog_in_channel))
-    #     self.dwf.FDwfDeviceCloseAll()
-    #     self.logger.info(f"[{self.pref} Task] Device closed.")
-
-    # def get_analog_in_status(self):
-    #     sts: c_byte = c_byte()
-    #     # Checks the state of the acquisition. To read the data from the device, set fReadData to TRUE. For
-    #     # single acquisition mode, the data will be read only when the acquisition is finished.
-    #     self.dwf.FDwfAnalogInStatus(self.model.hdwf,  # Interface handle.
-    #                                 c_int(1),  # True, if data should be read
-    #                                 byref(sts))  # Variable to receive the acquisition state
-    #     self.model.device_state = sts.value
-    #     return sts
-
-    # ==================================================================================================================
-    #
-    # ==================================================================================================================
diff --git a/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py b/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py
index 20cb325d7566c34ece9b1b15081ed978842952db..dfad80f216eb4822ec681df3616467047821ca05 100644
--- a/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py
+++ b/src/CaptDeviceControl/controller/BaseAD2CaptDevice.py
@@ -1,9 +1,12 @@
 import logging
+import os
 import time
 from abc import abstractmethod
 from collections import deque
 
 from PySide6.QtCore import QObject, QThreadPool
+from numpy import ndarray
+from rich.logging import RichHandler
 
 from CaptDeviceControl.controller.mp_AD2Capture.AD2StateMPSetter import AD2State
 from CaptDeviceControl.controller.mp_AD2Capture.MPDeviceControl import mp_capture
@@ -16,12 +19,19 @@ from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDeviceControl import MPCap
 
 class BaseAD2CaptDevice(QObject):
 
-    def __init__(self, ad2capt_model: AD2CaptDeviceModel):
+    def __init__(self, ad2capt_model: AD2CaptDeviceModel, start_capture_flag: Value):
         super().__init__()
         self.model = ad2capt_model
 
         self.pref = "AD2CaptDev"
-        self.logger = logging.getLogger(f"AD2 Device")
+
+        self.handler = RichHandler(rich_tracebacks=True)
+        self.logger = logging.getLogger(f"AD2Controller({os.getpid()})")
+        self.logger.handlers = [self.handler]
+        self.logger.setLevel(logging.DEBUG)
+        formatter = logging.Formatter('%(name)s %(message)s')
+        self.handler.setFormatter(formatter)
+
 
         self.signals = AD2CaptDeviceSignals()
 
@@ -32,13 +42,17 @@ class BaseAD2CaptDevice(QObject):
         # self.thread_manager.setThreadPriority(QThread.HighestPriority)
 
         self.lock = Lock()
-        self.proc = None
+        #self.proc = None
         self.stream_data_queue = Queue()
         self.capture_data_queue = Queue()
-        self.state_queue = Queue()
+        #self.state_queue = Queue()
 
-        self.start_capture_flag = Value('i', 0, lock=self.lock)
-        self.end_process_flag = Value('i', False, lock=self.lock)
+        if start_capture_flag is None:
+            self.start_capture_flag = Value('i', 0, lock=self.lock)
+        else:
+            self.start_capture_flag = start_capture_flag
+        self.kill_capture_flag = Value('i', 0, lock=self.lock)
+        #self.end_process_flag = Value('i', False, lock=self.lock)
 
         # Number of sa
         self.streaming_data_dqueue: deque = None # a dqueue, initialize later
@@ -50,31 +64,33 @@ class BaseAD2CaptDevice(QObject):
                                                        self.stream_data_queue,
                                                        self.capture_data_queue,
                                                        self.start_capture_flag,
-                                                       enable_logging=True)
+                                                       self.kill_capture_flag,
+                                                       enable_internal_logging=False)
 
         #self.mpcaptdevicecontrol.discover_connected_devices_finished.connect(
         #    lambda x: type(self.model).connected_devices.fset(self.model, x))
 
 
-        self.mpcaptdevicecontrol.connected_devices()
-
-    def get_analog_in_informatio(self):
-        self.mpcaptdevicecontrol.ain_channels(self.model.selected_device)
+        #self.mpcaptdevicecontrol.connected_devices()
 
+    def device_selected_index_changed(self):
+        print(self.model.device_information.device_index)
+        self.mpcaptdevicecontrol.ain_channels(self.model.device_information.device_index)
+        #self.mpcaptdevicecontrol
     def connect_device(self, device_id):
         self.mpcaptdevicecontrol.open_device(device_id)
         self.mpcaptdevicecontrol.start_capture(
             self.model.sample_rate,
-            self.model.selected_ain_channel)
+            self.model.device_information.device_index)
         self.start_device_process()
         return True
 
     def close_device(self):
         self.mpcaptdevicecontrol.close_device()
 
-    @abstractmethod
+
     def discover_connected_devices(self):
-        raise NotImplementedError
+        self.mpcaptdevicecontrol.connected_devices()
 
     @abstractmethod
     def update_device_information(self):
@@ -169,35 +185,36 @@ class BaseAD2CaptDevice(QObject):
         #self.proc.start()
 
         # self.thread_manager.moveToThread(())
-        #self.thread_manager.start(self.qt_consume_data)
+        self.thread_manager.start(self.qt_consume_data)
         self.thread_manager.start(self.qt_stream_data)
         #self.thread_manager.start(self.qt_get_state)
     
     def qt_consume_data(self):
-        """Consume data from the queue and plot it. This is a QThread."""
-        while not self.kill_thread and not bool(self.end_process_flag.value):
-            while self.capture_data_queue.qsize() > 0:
-                self.model.unconsumed_capture_data = self.capture_data_queue.qsize()
-                d, s = self.capture_data_queue.get()
-                [self.model.recorded_samples.append(e) for e in d]
-                # self.model.samples_captured = len(self.model.recorded_samples)
-                self.status_dqueue.append(s)
-            #time.sleep(0.01)
+        while True:
+            try:
+                capt_data = self.capture_data_queue.get()
+                if isinstance(capt_data, ndarray):
+                    print(f"Capt data queue size {self.capture_data_queue.qsize()}")
+                    # for d in stream_data:
+                         # self.model.recorded_samples.append(d)
+            except Exception as e:
+                self.logger.info(f"Error while consuming data {e}")
         self.logger.info("Capture Data consume thread ended")
 
     def qt_stream_data(self):
-        nth_cnt = 1
-        nth = 2
-        while not self.kill_thread and not bool(self.end_process_flag.value):
-            while True:
-                #self.stream_data_queue.qsize() > 0:
-                #self.model.unconsumed_stream_samples = self.stream_data_queue.qsize()
-                for d in self.stream_data_queue.get()[0]:
-                    #if nth_cnt == nth:
-                    self.streaming_data_dqueue.append(d)
-                    #    nth_cnt = 0
-                    #nth_cnt += 1
-            #time.sleep(0.01)
+        while True:
+            t = time.time()
+            try:
+                stream_data = self.stream_data_queue.get(block=True)
+                if isinstance(stream_data, ndarray):
+                    #print(f"Stream data queue size {self.stream_data_queue.qsize()}")
+                    for d in stream_data:
+                        self.streaming_data_dqueue.append(d)
+                t_end = time.time()
+                #print(f"Time to get data {t_end-t}")
+            except Exception as e:
+                self.logger.info(f"Timeout reached. No data in queue {self.stream_data_queue.qsize()} or"
+                                 f"{e}")
         self.logger.info("Streaming data consume thread ended")
 
     def qt_get_state(self):
@@ -246,13 +263,13 @@ class BaseAD2CaptDevice(QObject):
     # Destructor
     # ==================================================================================================================
     def stop_process(self):
-        self.end_process_flag.value = True
-            
+        #self.end_process_flag.value = True
+
         time_start = time.time()
-        while self.proc.is_alive():
-            time.sleep(0.1)
+        #while self.proc.is_alive():
+        #    time.sleep(0.1)
         self.logger.warning(f"AD2 process exited after {time.time()-time_start}s")
-        self.kill_thread = True    
+        self.kill_thread = True
 
     def __del__(self):
         self.logger.info("Exiting AD2 controller")
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/AnalogOutChannel.py b/src/CaptDeviceControl/controller/DeviceInformation/AnalogOutChannel.py
new file mode 100644
index 0000000000000000000000000000000000000000..97f6cd2bc906a85821fdd1322ce21c3bc559aeed
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/AnalogOutChannel.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version: 
+"""
+from ctypes import CDLL, c_int, byref
+
+from controller.DeviceInformation.HWDeviceBase import HWDeviceBase
+
+
+class AnalogOutChannel(HWDeviceBase):
+    def __init__(self, dwf: CDLL, hwdf: c_int, device_idx, channel):
+        super().__init__(dwf, hwdf)
+
+        self._channel: int = channel
+        self._node: int = 0
+        self._buffer_size: int = 0
+        self._amplitude: tuple = (0, 0)
+        self._offset: tuple = (0, 0)
+        self._frequency: tuple = (0, 0)
+
+    def reinit(self, fields: dict):
+        for k, v in fields.items():
+            setattr(self, k, v)
+
+    @property
+    def channel(self) -> int:
+        """
+            Returns the channel number of the AnalogOut channel.
+            :return: The channel number of the AnalogOut channel.
+        """
+        return self._channel
+
+    @property
+    def node(self) -> int:
+        """
+            Returns the node number of the AnalogOut channel.
+            :return: The node number of the AnalogOut channel.
+        """
+        return self._node
+
+    @property
+    def buffer_size(self) -> int:
+        """
+            Reads the number of AnalogIn channels of the device. The oscilloscope channel settings are
+            identical across all channels.
+            Calls WaveForms API Function 'FDwfAnalogInChannelCount(HDWF hdwf, int *pcChannel)'
+            :return: The number of analog in channels.
+        """
+        self._check_device_connection()
+        int0 = c_int()
+        self.dwf.FDwfAnalogInChannelCount(self.hdwf, byref(int0))
+        self._analog_in_channels = int(int0.value)
+        return self._analog_in_channels
+
+
+class AnalogOutChannelSetter():
+    pass
\ No newline at end of file
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/HWConnectedDeviceInformation.py b/src/CaptDeviceControl/controller/DeviceInformation/HWConnectedDeviceInformation.py
new file mode 100644
index 0000000000000000000000000000000000000000..39300949a20b859072a73c7323bd6b56478a8270
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/HWConnectedDeviceInformation.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version: 
+"""
+from ctypes import CDLL
+
+from controller.DeviceInformation.dataclasses.AnalogInChannelInfo import AnalogInChannels
+from controller.DeviceInformation.AnalogOutChannel import AnalogOutChannels
+
+
+class HWConnectedDeviceInformation:
+    """ Class for storing information about the connected device."""
+
+    def __init__(self, dwf: CDLL, device_idx, type="USB"):
+        self.dwf = dwf
+        self._type: str = type
+
+        self._device_id: int = device_idx
+        self._device_name: str = ""
+        self._serial_number: str = ""
+
+        self.analog_in_channels = list[AnalogInChannels]
+        self.analog_out_channels = list[AnalogOutChannels]
+
+    @property
+    def type(self) -> str:
+        """
+        Returns the type of the device (USB or Simulator)
+        :return: Type of the device
+        """
+        return self._type
+
+    @property
+    def device_id(self) -> int:
+        """
+        The device id of the enumerated device.
+        :return: The device id of the enumerated device.
+        """
+        return self._device_id
+
+    @property
+    def device_name(self) -> str:
+        """
+        The device name of the enumerated device.
+        :return: The device name of the enumerated device.
+        """
+        return self._device_name
+
+    @property
+    def serial_number(self) -> str:
+        """
+        The serial number of the enumerated device.
+        :return: The serial number of the enumerated device.
+        """
+        return self._serial_number
+
+    def __repr__(self):
+        return f"HW({self._device_name}, {self._serial_number}, {self._type})"
\ No newline at end of file
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceBase.py b/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceBase.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1cfda7b0d2960fc55fb18de67aad4292a4493b1
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceBase.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version: 
+"""
+from ctypes import CDLL, c_int, create_string_buffer, c_int32, byref
+
+from constants.dwfconstants import enumfilterType, enumfilterUSB, enumfilterDemo
+
+
+class HWDeviceBase:
+    """Base class for retrieving information from the hardware."""
+
+    def __int__(self, dwf: CDLL, hwdf: c_int):
+        self.dwf: CDLL = dwf
+        self.hdwf: hwdf = hwdf
+
+    def _check_device_connection(self):
+        if self.hdwf.value == 0:
+            szerr = create_string_buffer(512)
+            self.dwf.FDwfGetLastErrorMsg(szerr)
+            raise Exception(str(szerr.value))
+
+
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceInformation.py b/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceInformation.py
new file mode 100644
index 0000000000000000000000000000000000000000..59d679900dd55ff7590d97c9bdc95a889902e852
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/HWDeviceInformation.py
@@ -0,0 +1,200 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version: 
+"""
+from ctypes import c_int, create_string_buffer, CDLL, byref
+
+from controller.DeviceInformation.WaveFormsAPI import WFAPIDeviceEnumeration, WFAPIDeviceControl, WFAPIChannels
+from controller.DeviceInformation.dataclasses.AnalogInChannelInfo import AnalogInChannelInfo, AnalogInChannelRetriever
+
+
+class HWDeviceInformation:
+    """ Class for storing information about the connected device."""
+
+    def __init__(self, device_idx, type="USB"):
+        self._type: str = type
+
+        self._device_id: int = device_idx
+        self._device_name: str = ""
+        self._serial_number: str = ""
+
+        self._analog_in_channels: list[AnalogInChannelInfo] = []
+        self._analog_out_channels: list[AnalogInChannelInfo] = []
+
+        self._analog_in_channel_count: int = 0
+        self._analog_out_channel_count: int = 0
+        self._analog_io_channel_count: int = 0
+
+        self._digital_in_channel_count: int = 0
+        self._digital_out_channel_count: int = 0
+        self._digital_io_channel_count: int = 0
+
+        self._analog_in_buffer_size: int = 0
+        self._analog_out_buffer_size: int = 0
+
+        self._digital_in_buffer_size: int = 0
+        self._digital_out_buffer_size: int = 0
+        # self._reload()
+
+    @property
+    def type(self) -> str:
+        """
+        Returns the type of the device (USB or Simulator)
+        :return: Type of the device
+        """
+        return self._type
+
+    @property
+    def device_id(self) -> int:
+        """
+        The device id of the enumerated device.
+        :return: The device id of the enumerated device.
+        """
+        return self._device_id
+
+    @property
+    def device_name(self) -> str:
+        """
+        The device name of the enumerated device.
+        :return: The device name of the enumerated device.
+        """
+        return self._device_name
+
+    @property
+    def serial_number(self) -> str:
+        """
+        The serial number of the enumerated device.
+        :return: The serial number of the enumerated device.
+        """
+        return self._serial_number
+
+    @property
+    def analog_in_channels(self) -> list[AnalogInChannelInfo]:
+        """
+        The analog in channels of the device without opening it.
+        :return: The analog in channels.
+        """
+        return self._analog_in_channels
+
+    @property
+    def analog_out_channels(self) -> list[AnalogInChannelInfo]:
+        """
+        The analog out channels of the device without opening it.
+        :return: The analog out channels.
+        """
+        return self._analog_out_channels
+
+    @property
+    def analog_in_channel_count(self) -> int:
+        """
+        The number of AnalogIn channels of the device without opening it.
+        :return: The number of analog in channels.
+        """
+        return self._analog_in_channel_count
+
+    @property
+    def analog_out_channel_count(self) -> int:
+        """
+        The number of AnalogOut channels of the device without opening it.
+        :return: The number of analog out channels.
+        """
+        return self._analog_out_channel_count
+
+    @property
+    def analog_io_channel_count(self) -> int:
+        """
+        The number of AnalogIO channels of the device without opening it.
+        :return: The number of analog io channels.
+        """
+        return self._analog_io_channel_count
+
+    @property
+    def digital_in_channel_count(self) -> int:
+        """
+        The number of DigitalIn channels of the device without opening it.
+        :return: The number of digital in channels.
+        """
+        return self._digital_in_channel_count
+
+    @property
+    def digital_out_channel_count(self) -> int:
+        """
+        The number of DigitalOut channels of the device without opening it.
+        :return: The number of digital out channels.
+        """
+        return self._digital_out_channel_count
+
+    @property
+    def digital_io_channel_count(self) -> int:
+        """
+        The number of DigitalIO channels of the device without opening it.
+        :return: The number of digital io channels.
+        """
+        return self._digital_io_channel_count
+
+    @property
+    def analog_in_buffer_size(self) -> int:
+        """
+        The buffer size of the AnalogIn channels of the device without opening it.
+        :return: The buffer size of the analog in channels.
+        """
+        return self._analog_in_buffer_size
+
+    @property
+    def analog_out_buffer_size(self) -> int:
+        """
+        The buffer size of the AnalogOut channels of the device without opening it.
+        :return: The buffer size of the analog out channels.
+        """
+        return self._analog_out_buffer_size
+
+    @property
+    def digital_in_buffer_size(self) -> int:
+        """
+        The buffer size of the DigitalIn channels of the device without opening it.
+        :return: The buffer size of the digital in channels.
+        """
+        return self._digital_in_buffer_size
+
+    @property
+    def digital_out_buffer_size(self) -> int:
+        """
+        The buffer size of the DigitalOut channels of the device without opening it.
+        :return: The buffer size of the digital out channels.
+        """
+        return self._digital_out_buffer_size
+
+    def __repr__(self):
+        return f"HW({self._device_name}, {self._serial_number}, {self._type})"
+
+
+class HWDeviceInformationRetriever(HWDeviceInformation):
+    def __init__(self, dwf: CDLL, device_idx, con_type="USB"):
+        super().__init__(device_idx, con_type)
+        self._device_name = WFAPIDeviceEnumeration.device_name(dwf, self.device_id)
+        self._serial_number = WFAPIDeviceEnumeration.serial_number(dwf, self.device_id)
+
+        # Top get all settings, we need to open the device
+        try:
+            self.hwdf = WFAPIDeviceControl.device_open(dwf, self.device_id)
+        except Exception as ex:
+            print(f"Error opening device: {ex}")
+            self.hwdf = None
+
+        if self.hwdf is not None:
+            # Get the number of ain channels
+            channel_count = WFAPIChannels().analog_in_channels_count(dwf,  self.hwdf)
+            #print(channel_count)
+            # Create a list of channels
+            for channel in range(channel_count):
+                o = AnalogInChannelRetriever(dwf, self.hwdf, channel).simple()
+                self._analog_in_channels.append(o)
+                print(type(o))
+
+            self._analog_in_channel_count = len(self._analog_in_channels)
+
+
+    def simple(self):
+        return super()
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/WaveFormsAPI.py b/src/CaptDeviceControl/controller/DeviceInformation/WaveFormsAPI.py
new file mode 100644
index 0000000000000000000000000000000000000000..02bb60588e5c1b2a038f083d5d61651b48870731
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/WaveFormsAPI.py
@@ -0,0 +1,509 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version: 
+"""
+import logging
+import sys
+from abc import abstractmethod
+from ctypes import c_int, byref, c_double, CDLL, create_string_buffer, cdll, c_int32
+
+from constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB
+
+
+class WFAPI:
+
+    @staticmethod
+    def _check_device_connection(dwf: CDLL, hwdf: c_int):
+        if hwdf.value == 0:
+            szerr = create_string_buffer(512)
+            dwf.FDwfGetLastErrorMsg(szerr)
+            raise Exception(str(szerr.value))
+
+
+class WFAPIChannels(WFAPI):
+
+    def __init__(self):
+        super().__init__()
+
+    @staticmethod
+    def analog_in_channels_count(dwf: CDLL, hdwf: c_int) -> int:
+        """
+        Reads the number of AnalogIn channels of the device. The oscilloscope channel settings are
+        identical across all channels.
+        Calls WaveForms API Function 'FDwfAnalogInChannelCount(HDWF hdwf, int *pcChannel)'
+        :return: The number of analog in channels.
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        int0 = c_int()
+        dwf.FDwfAnalogInChannelCount(hdwf, byref(int0))
+        _analog_in_channels = int(int0.value)
+        return _analog_in_channels
+
+    @staticmethod
+    def analog_on_channel_enable_set(dwf: CDLL, hdwf: c_int, channel: int, enable: bool):
+        """
+        Not implemented.
+        Enables or disables the specified AnalogIn channel.
+        Calls WaveForms API Function 'FDwfAnalogInChannelEnableSet(HDWF hdwf, int idxChannel, int fEnable)'
+        :param channel: The channel to enable or disable.
+        :param enable: True to enable the channel, False to disable it.
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        raise NotImplementedError()
+
+    @staticmethod
+    def analog_in_channel_enable_get(dwf: CDLL, hdwf: c_int, channel: int) -> bool:
+        """
+        Not implemented.
+        Gets the current enable/disable status of the specified AnalogIn channel.
+        Calls WaveForms API Function 'FDwfAnalogInChannelEnableGet(HDWF hdwf, int idxChannel, int *pfEnable)'
+        :param channel: Index of channel to get the enable/disable status of.
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        raise NotImplementedError()
+
+    @staticmethod
+    def analog_in_channel_filter_info(dwf: CDLL, hdwf: c_int, filter_number: int):
+        """
+        Not implemented.
+        Returns the supported sampling modes. They are returned (by reference) as a bit field. This bit field
+        can be parsed using the IsBitSet Macro. Individual bits are defined using the FILTER constants in dwf.h.
+        When the acquisition frequency (FDwfAnalogInFrequencySet) is less than the ADC frequency (maximum
+        acquisition frequency).
+
+        Calls the WaveForms API Function 'FDwfAnalogInChannelFilterInfo(HDWF hdwf, int *pfsfilter)'
+
+        :param filter_number:
+            - filterDecimate: 0
+              Store every Nth ADC conversion, where N = ADC frequency /acquisition frequency.
+            - filterAverage: 1
+              Store the average of N ADC conversions.
+            - filterMinMax: 2
+              Store interleaved, the minimum and maximum values, of 2xN conversions.
+            - filterAverageFit: 3
+              The stored samples match the specified range instead of the device input range options.
+              This can improve the vertical resolution of the samples.
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        raise NotImplementedError()
+
+    @staticmethod
+    def analog_in_channel_filter_set(dwf: CDLL, hdwf: c_int, channel: int, filter_number: int):
+        """
+        Not implemented.
+        Sets the acquisition filter for each AnalogIn channel. With channel index -1, each enabled AnalogIn
+        channel filter will be configured to use the same, new option.
+
+        Calls the WaveForms API Function 'FDwfAnalogInChannelFilterSet(HDWF hdwf, int idxChannel, FILTER filter)'
+
+        :param channel: Channel index
+        :param filter_number: Acquisition sample filter to set.
+            - filterDecimate: 0
+              Store every Nth ADC conversion, where N = ADC frequency /acquisition frequency.
+            - filterAverage: 1
+              Store the average of N ADC conversions.
+            - filterMinMax: 2
+              Store interleaved, the minimum and maximum values, of 2xN conversions.
+            - filterAverageFit: 3
+              The stored samples match the specified range instead of the device input range options.
+              This can improve the vertical resolution of the samples.
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        raise NotImplementedError()
+
+    @staticmethod
+    def analog_in_channel_filter_get(dwf: CDLL, hdwf: c_int, channel: int) -> int:
+        """
+        Not implemented.
+        Returns the configured acquisition filter.
+
+        Calls the WaveForms API Function 'FDwfAnalogInChannelFilterGet(HDWF hdwf, int idxChannel, FILTER *pfilter)'
+
+        :param channel: Channel index
+        :return: filter type (int)
+            - filterDecimate: 0
+              Store every Nth ADC conversion, where N = ADC frequency /acquisition frequency.
+            - filterAverage: 1
+              Store the average of N ADC conversions.
+            - filterMinMax: 2
+              Store interleaved, the minimum and maximum values, of 2xN conversions.
+            - filterAverageFit: 3
+              The stored samples match the specified range instead of the device input range options.
+              This can improve the vertical resolution of the samples.
+        """
+
+    @staticmethod
+    def analog_in_channel_range_info(dwf: CDLL, hdwf: c_int) -> tuple:
+        """
+        Returns the minimum and maximum range, peak to peak values, and the number of adjustable steps.
+        The oscilloscope channel settings are identical across all channels.
+        Calls WaveForms API Function
+        'FDwfAnalogInChannelRangeInfo(HDWF hdwf, double *pvoltsMin, double *pvoltsMax, double *pnSteps)'
+        :return: The minimum and maximum range, peak to peak values, and the number of adjustable steps as a tuple
+        (min, max, steps).
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        dbl0 = c_double()
+        dbl1 = c_double()
+        dbl2 = c_double()
+        dwf.FDwfAnalogInChannelRangeInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
+        _range = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
+        return _range
+
+    @staticmethod
+    def analog_in_channel_range_steps(dwf: CDLL, hdwf: c_int):
+        """
+        Not implemented.
+        Reads the range of steps supported by the device. For instance: 1, 2, 5, 10, etc.
+
+        Calls WaveForms API Function
+        'FDwfAnalogInChannelRangeSteps(HDWF hdwf, double rgVoltsStep[32], int *pnSteps)'
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        raise NotImplementedError()
+
+    @staticmethod
+    def analog_in_channel_range_set(dwf: CDLL, hdwf: c_int, channel: int, range: float):
+        """
+        Not implemented.
+        Configures the range for each channel. With channel index -1, each enabled Analog In channel range
+        will be configured to the same, new value.
+
+        Calls WaveForms API Function
+        'FDwfAnalogInChannelRangeSet(HDWF hdwf, int idxChannel, double voltsRange)'
+        :param channel: Channel index
+        :param range: The range to set.
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        raise NotImplementedError()
+
+    @staticmethod
+    def analog_in_channel_range_get(dwf: CDLL, hdwf: c_int, channel: int) -> float:
+        """
+        Not implemented.
+        Returns the real range value for the given channel.
+
+        Calls WaveForms API Function
+        'FDwfAnalogInChannelRangeGet(HDWF hdwf, int idxChannel, double *pvoltsRange)'
+
+        :param channel: Channel index
+        :return: The real range value for the given channel.
+        """
+
+    @staticmethod
+    def analog_in_channel_offset_info(dwf: CDLL, hdwf: c_int) -> tuple:
+        """
+        Returns the minimum and maximum offset levels supported, and the number of adjustable steps. The oscilloscope
+        channel settings are identical across all channels.
+        Calls WaveForms API Function
+        'FDwfAnalogInChannelOffsetInfo(HDWF hdwf, double *pvoltsMin, double *pvoltsMax, double *pnSteps)'
+        :return: The minimum and maximum offset levels supported, and the number of adjustable steps as
+        a tuple (min, max, steps).
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        dbl0 = c_double()
+        dbl1 = c_double()
+        dbl2 = c_double()
+        dwf.FDwfAnalogInChannelOffsetInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
+        _offset = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
+        return _offset
+
+    @staticmethod
+    def analog_in_buffer_size(dwf: CDLL, hdwf: c_int) -> tuple:
+        """
+        Returns the minimum and maximum allowable buffer sizes for the instrument. The oscilloscope
+        channel settings are identical across all channels.
+        Calls WaveForms API Function 'FDwfAnalogInBufferSizeInfo(HDWF hdwf, int *pnSizeMin, int *pnSizeMax)'
+        :return: The minimum and maximum allowable buffer sizes for the instrument as a tuple (min, max).
+        """
+        WFAPI._check_device_connection(dwf, hdwf)
+        int0 = c_int()
+        int1 = c_int()
+        dwf.FDwfAnalogInBufferSizeInfo(hdwf, byref(int0), byref(int1))
+        _buffer_size = (int(int0.value), int(int0.value))
+        return _buffer_size
+
+    @staticmethod
+    def analog_in_bits(dwf: CDLL, hdwf: c_int) -> int:
+        """
+        Retrieves the number bits used by the AnalogIn ADC. The oscilloscope channel settings are identical
+        across all channels.
+        Calls WaveForms API Function 'FDwfAnalogInBitsInfo(HDWF hdwf, int *pnBits)'
+        :return: The number bits used by the AnalogIn ADC.
+        """
+        int0 = c_int()
+        WFAPI._check_device_connection(dwf, hdwf)
+        dwf.FDwfAnalogInBitsInfo(hdwf, byref(int0))
+        _adc_bits = int(int0.value)
+        return _adc_bits
+
+
+class WFAPIDeviceEnumeration(WFAPI):
+
+    def __init__(self):
+        super().__init__()
+
+    @staticmethod
+    def device_name(dwf: CDLL, device_id: int) -> str:
+        """
+        Retrieves the device name of the enumerated device.
+        Calls WaveForms API Function 'FDwfEnumDeviceName(int idxDevice, char szDeviceName[32])'
+        :return: The device name of the enumerated device
+        """
+        devicename = create_string_buffer(64)
+        dwf.FDwfEnumDeviceName(c_int(device_id), devicename)
+        return str(devicename.value.decode("utf-8"))
+
+    @staticmethod
+    def serial_number(dwf: CDLL, device_id: int) -> str:
+        """
+        Retrieves the serial number of the enumerated device.
+        :return:
+        """
+        serialnum = create_string_buffer(16)
+        dwf.FDwfEnumSN(c_int(device_id), serialnum)
+        return str(serialnum.value.decode("utf-8"))
+
+    @staticmethod
+    def analog_in_channel_count(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the number of AnalogIn channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 1.
+        :return: The number of analog in channels.
+        """
+        cInfo = c_int()
+        # c_int(1): DECIAnalogInChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(1), byref(cInfo))
+        return int(cInfo.value)
+
+    @staticmethod
+    def analog_out_channel_count(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the number of AnalogOut channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 2.
+        :return: The number of analog out channels.
+        """
+        cInfo = c_int()
+        # c_int(2): DECIAnalogOutChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(2), byref(cInfo))
+        return int(cInfo.value)
+
+    @staticmethod
+    def analog_io_channel_count(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the number of AnalogIO channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 3.
+        :return: The number of analog io channels.
+        """
+        cInfo = c_int()
+        # c_int(3): DECIAnalogIOChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(3), byref(cInfo))
+        return int(cInfo.value)
+
+    @staticmethod
+    def digital_in_channel_count(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the number of DigitalIn channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 4.
+        :return: The number of digital in channels.
+        """
+        cInfo = c_int()
+        # c_int(4): DECIDigitalInChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(4), byref(cInfo))
+
+        return int(cInfo.value)
+
+    @staticmethod
+    def digital_out_channel_count(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the number of DigitalOut channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 5.
+        :return: The number of digital out channels.
+        """
+        cInfo = c_int()
+        # c_int(5): DECIDigitalOutChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(5), byref(cInfo))
+
+        return int(cInfo.value)
+
+    @staticmethod
+    def digital_io_channel_count(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the number of DigitalIO channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 6.
+        :return: The number of digital io channels.
+        """
+        cInfo = c_int()
+        # c_int(6): DECIDigitalIOChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(6), byref(cInfo))
+        return int(cInfo.value)
+
+    @staticmethod
+    def analog_in_buffer_size(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the buffer size of the AnalogIn channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 7.
+        :return: The buffer size of the analog in channels.
+        """
+        cInfo = c_int()
+        # c_int(6): DECIDigitalIOChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(7), byref(cInfo))
+        return int(cInfo.value)
+
+    @staticmethod
+    def analog_out_buffer_size(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the buffer size of the AnalogOut channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 8.
+        :return: The buffer size of the analog out channels.
+        """
+        cInfo = c_int()
+        # c_int(6): DECIDigitalIOChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(8), byref(cInfo))
+        return int(cInfo.value)
+
+    @staticmethod
+    def digital_in_buffer_size(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the buffer size of the DigitalIn channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 9.
+        :return: The buffer size of the digital in channels.
+        """
+        cInfo = c_int()
+        # c_int(6): DECIDigitalIOChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(9), byref(cInfo))
+        return int(cInfo.value)
+
+    @staticmethod
+    def digital_out_buffer_size(dwf: CDLL, device_id: int) -> int:
+        """
+        Returns the buffer size of the DigitalOut channels of the device without opening it.
+        Note: These are intended for preliminary information before opening a device. Further information are available
+        with various the FDwf#Info functions.
+
+        Calls WaveForms API Function 'FDwfEnumConfigInfo(int idxConfig, DwfEnumConfigInfo info, int *pValue)'
+        with parameter info = 10.
+        :return: The buffer size of the digital out channels.
+        """
+        cInfo = c_int()
+        # c_int(6): DECIDigitalIOChannelCount
+        dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(10), byref(cInfo))
+        return int(cInfo.value)
+
+
+class WFAPIDeviceControl(WFAPI):
+
+    def __init__(self):
+        super().__init__()
+
+    @staticmethod
+    def device_open(dwf: CDLL, device_id: int) -> c_int:
+        """
+        Opens a device identified by the enumeration index and retrieves a handle.
+        To automatically enumerate all connected devices and open the first discovered device, use index -1.
+
+        Calls WaveForms API Function 'FDwfDeviceOpen(int idxDevice, HDWF *phdwf)'
+
+        :param dwf: Shared library handle.
+        :param device_id: The device id of the device to open.
+        :return: The handle (hdwf) of the opened device.
+        """
+        hdwf = c_int()
+        dwf.FDwfDeviceOpen(c_int(device_id), byref(hdwf))
+        return hdwf
+
+
+class WaveFormsAPI:
+
+    def __init__(self, parent_logger: logging.Logger):
+        self._logger = parent_logger
+        self.channels = WFAPIChannels()
+        self.device_enumeration = WFAPIDeviceEnumeration()
+        self.device_control = WFAPIDeviceControl()
+
+        self._dwf_init = False
+        self._dwf = None
+
+    def init_dwf(self) -> CDLL:
+        if not self._dwf_init:
+            if sys.platform.startswith("win"):
+                self._dwf = cdll.dwf
+            elif sys.platform.startswith("darwin"):
+                self._dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf")
+            else:
+                self._dwf = cdll.LoadLibrary("libdwf.so")
+            self._dwf_init = True
+        return self._dwf
+
+
+    def get_connected_devices(self, dwf, discover_simulators: bool = False):
+        self._logger.info(f"Discovering connected devices...")
+        # enumerate connected devices
+        connected_devices = []
+        # for filter_type in [(c_int32(enumfilterType.value | enumfilterUSB.value), 'USB'),
+        #                     (c_int32(enumfilterType.value | enumfilterNetwork.value), 'Network'),
+        #                     (c_int32(enumfilterType.value | enumfilterAXI.value), 'AXI'),
+        #                     (c_int32(enumfilterType.value | enumfilterRemote.value), 'Remote'),
+        #                     (c_int32(enumfilterType.value | enumfilterAudio.value), 'Audio'),
+        #                     (c_int32(enumfilterType.value | enumfilterDemo.value), 'Demo')]:
+        cDevice = c_int()
+        filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value | enumfilterUSB.value), 'USB')
+        # filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value), 'DEMO')
+        self._logger.debug(f"Filtering {type} devices...")
+        self.dwf.FDwfEnum(filter, byref(cDevice))
+        num_of_connected_devices = cDevice
+
+        devicename = create_string_buffer(64)
+        serialnum = create_string_buffer(16)
+
+        for iDevice in range(0, cDevice.value):
+            self.dwf.FDwfEnumDeviceName(c_int(iDevice), devicename)
+            self.dwf.FDwfEnumSN(c_int(iDevice), serialnum)
+            connected_devices.append({
+                'type': type,
+                'device_id': int(iDevice),
+                'device_name': str(devicename.value.decode('UTF-8')),
+                'serial_number': str(serialnum.value.decode('UTF-8'))
+            })
+            # _mp_log_debug(f"Found {type} device: {devicename.value.decode('UTF-8')} ({serialnum.value.decode('UTF-8')})")
+        # print(connected_devices)
+        # print(f"Discoverd {len(self.model.connected_devices)} devices.")
+        return connected_devices
diff --git a/src/CaptDeviceControl/controller/DeviceInformation/dataclasses/AnalogInChannelInfo.py b/src/CaptDeviceControl/controller/DeviceInformation/dataclasses/AnalogInChannelInfo.py
new file mode 100644
index 0000000000000000000000000000000000000000..96d637bd65f7ea9337aded5de2e637575bcdedd8
--- /dev/null
+++ b/src/CaptDeviceControl/controller/DeviceInformation/dataclasses/AnalogInChannelInfo.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version: 
+"""
+from ctypes import CDLL, c_int, byref, c_double
+from dataclasses import dataclass
+
+from controller.DeviceInformation.HWDeviceBase import HWDeviceBase
+from controller.DeviceInformation.WaveFormsAPI import WFAPIChannels
+
+
+@dataclass
+class AnalogInChannelInfo:
+    """ Class for storing the information about the channels"""
+
+    def __init__(self):
+        self._channel = 0
+        self._buffer_size: tuple = (0, 0)
+        self._adc_bits: int = 0
+        self._range: tuple = (0, 0, 0)
+        self._offset: tuple = (0, 0, 0)
+
+    @property
+    def buffer_size(self) -> tuple:
+        """ Returns the minimum and maximum allowable buffer sizes for the instrument"""
+        return self._buffer_size
+
+    @property
+    def adc_bits(self) -> int:
+        """ Returns the number bits used by the AnalogIn ADC"""
+        return self._adc_bits
+
+    @property
+    def range(self) -> tuple:
+        """ Returns the minimum and maximum range, peak to peak values, and the number of adjustable steps"""
+        return self._range
+
+    @property
+    def offset(self) -> tuple:
+        """ Returns the minimum and maximum offset levels supported, and the number of adjustable steps"""
+        return self._offset
+
+
+class AnalogInChannelRetriever(AnalogInChannelInfo):
+
+    def __init__(self, dwf: CDLL, hwdf: c_int, channel: int):
+        super().__init__()
+        self._channel = channel
+        self._buffer_size: tuple = WFAPIChannels().analog_in_buffer_size(dwf, hwdf)
+        self._adc_bits: int = WFAPIChannels().analog_in_bits(dwf, hwdf)
+        self._range: tuple = WFAPIChannels().analog_in_channel_range_info(dwf, hwdf)
+        self._offset: tuple = WFAPIChannels().analog_in_channel_offset_info(dwf, hwdf)
+
+    def simple(self):
+        # TODO: Does nto work
+        return super(AnalogInChannelInfo, self)
\ No newline at end of file
diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/AD2StateMPSetter.py b/src/CaptDeviceControl/controller/mp_AD2Capture/AD2StateMPSetter.py
index b92f8ddf457581f7361ffbe8d585792f8b1d4bdc..f1f0827cdfc2f5fe380b01e0fe847fe6cb281604 100644
--- a/src/CaptDeviceControl/controller/mp_AD2Capture/AD2StateMPSetter.py
+++ b/src/CaptDeviceControl/controller/mp_AD2Capture/AD2StateMPSetter.py
@@ -6,6 +6,7 @@ class AD2State:
     def __init__(self):
         # Multiprocessing Information
         self._pid = None
+        self._ppid = None
 
         # WaveForms Runtime (DWF) Information
         self._dwf_version: str = "Unknown"
@@ -31,7 +32,11 @@ class AD2State:
 
         # Acquired Signal Information
         self._acquisition_state: int = AD2Constants.CapturingState.STOPPED()
+
+        self._time_capture_started: float = -1  # The time the capturing has started
+        self._time_capture_ended: float = -1  # The time the capturing has ended
         self._recording_time: float = -1
+
         self._samples_captured: int = 0
         self._samples_lost: int = -1
         self._samples_corrupted: int = -1
@@ -50,6 +55,10 @@ class AD2State:
     def pid(self):
         return self._pid
 
+    @property
+    def ppid(self):
+        return self._ppid
+
     # =========== WaveForms Runtime (DWF) Information
     @property
     def dwf_version(self):
@@ -108,6 +117,16 @@ class AD2State:
     def acquisition_state(self):
         return self._acquisition_state
 
+    @property
+    def time_capture_started(self) -> float:
+        """The time the capturing has started"""
+        return self._time_capture_started
+
+    @property
+    def time_capture_ended(self) -> float:
+        """The time the capturing has ended"""
+        return self._time_capture_ended
+
     @property
     def recording_time(self):
         return self._recording_time
@@ -146,6 +165,11 @@ class AD2StateMPSetter(AD2State):
         self._pid = value
         self._state_queue.put(self.to_simple_class())
 
+    @AD2State.ppid.setter
+    def ppid(self, value):
+        self._ppid = value
+        self._state_queue.put(self.to_simple_class())
+
     # =========== WaveForms Runtime (DWF) Information
     @AD2State.dwf_version.setter
     def dwf_version(self, value):
@@ -218,6 +242,17 @@ class AD2StateMPSetter(AD2State):
         self._acquisition_state = value
         self._state_queue.put(self.to_simple_class())
 
+    @AD2State.time_capture_ended.setter
+    def time_capture_started(self, value: float):
+        """The time the capturing has started"""
+        self._time_capture_started = value
+        self._state_queue.put(self.to_simple_class())
+
+    @AD2State.time_capture_ended.setter
+    def time_capture_ended(self, value: float):
+        self._time_capture_ended = value
+        self._state_queue.put(self.to_simple_class())
+
     @AD2State.recording_time.setter
     def recording_time(self, value):
         self._recording_time = value
diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py
index d88356356d06c97f9e8e5863a37cecd6673ae886..fa38f2e22c62df592a697951ddbd98ce3cbd8961 100644
--- a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py
+++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDevice.py
@@ -1,24 +1,44 @@
+import ctypes
 import os
 import sys
 import time
-from ctypes import c_int, c_int32, byref, create_string_buffer, cdll, c_double, c_byte
+from ctypes import c_int, c_int32, byref, create_string_buffer, cdll, c_double, c_byte, CDLL
 from multiprocessing import Queue, Value
 
 import cmp
+import numpy as np
 
 from constants.dwfconstants import enumfilterType, enumfilterDemo, enumfilterUSB, acqmodeRecord, DwfStateConfig, \
     DwfStatePrefill, DwfStateArmed
 
 
 class MPCaptDevice(cmp.CProcess):
+    @staticmethod
+    def timeit(func):
+        def wrapper(self, *args, **kwargs):
+            time_start = time.time()
+            res = func(self, *args, **kwargs)
+            time_stop = time.time()
+            print(f"Function {func.__name__} took {time_stop - time_start} seconds.")
+            return res #time_stop - time_start
+
+        return wrapper
+
     def __init__(self, state_queue, cmd_queue,
-                 streaming_data_queue: Queue,
-                 capture_data_queue: Queue,
+                 streaming_data_queue: Queue, capture_data_queue: Queue,
                  start_capture_flag: Value,
-                 enable_logging):
-        super().__init__(state_queue, cmd_queue, enable_logging=enable_logging)
+                 kill_capture_flag: Value,
+                 enable_internal_logging):
+        super().__init__(state_queue, cmd_queue, enable_internal_logging=enable_internal_logging)
+
+        self._c_samples = None
+        self._c_corrupted = None
+        self._c_lost = None
+        self._c_available = None
 
         self.start_capture_flag: Value = start_capture_flag
+        self.kill_capture_flag: Value = kill_capture_flag
+
         self.stream_data_queue = streaming_data_queue
         self.capture_data_queue = capture_data_queue
 
@@ -27,28 +47,49 @@ class MPCaptDevice(cmp.CProcess):
         self.dwf = None
         self.hdwf = None
 
+        # Capture data counters
+
         self._dwf_version = None
         self._device_serial_number: str = ""
-        self._device_name: str = "None"
-        self._connected = self.connected()
+        self._device_name: str = ""
+        self._connected = False
 
         self._samples_lost = 0
         self._samples_corrupted = 0
 
-
-
     def postrun_init(self):
         self.logger, self.ha = self.create_new_logger(f"{self.__class__.__name__}/({os.getpid()})")
+
         if sys.platform.startswith("win"):
             self.dwf = cdll.dwf
         elif sys.platform.startswith("darwin"):
             self.dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf")
         else:
             self.dwf = cdll.LoadLibrary("libdwf.so")
-
+        self._connected = self.connected()
         self.hdwf = c_int()
         self._ain_device_state: c_byte = c_byte()
 
+        self._c_available = c_int()
+        self._c_lost = c_int()
+        self._c_corrupted = c_int()
+        self._c_samples = 0
+
+    @cmp.CProcess.register_for_signal('_changed')
+    def device_capturing(self, capturing: bool):
+        self.logger.info(f"Device capturing: {capturing}")
+        return capturing
+
+    @cmp.CProcess.register_for_signal('_changed')
+    def dwf_version(self):
+        self.logger.debug(f"Getting DWF version information...")
+        version = create_string_buffer(16)
+        self.dwf.FDwfGetVersion(version)
+        return version.value.decode("utf-8")
+
+    # ==================================================================================================================
+    # Device Enumeration without connecting to the device
+    # ==================================================================================================================
     @cmp.CProcess.register_for_signal('_changed')
     def connected_devices(self):
         self.logger.info(f"Discovering connected devices...")
@@ -66,30 +107,34 @@ class MPCaptDevice(cmp.CProcess):
         self.logger.debug(f"Filtering {type} devices...")
         self.dwf.FDwfEnum(filter, byref(cDevice))
         num_of_connected_devices = cDevice
-
-        devicename = create_string_buffer(64)
-        serialnum = create_string_buffer(16)
+        self.logger.debug(f"Found {cDevice.value} {type} devices.")
 
         for iDevice in range(0, cDevice.value):
-            self.dwf.FDwfEnumDeviceName(c_int(iDevice), devicename)
-            self.dwf.FDwfEnumSN(c_int(iDevice), serialnum)
             connected_devices.append({
                 'type': type,
                 'device_id': int(iDevice),
-                'device_name': str(devicename.value.decode('UTF-8')),
-                'serial_number': str(serialnum.value.decode('UTF-8'))
+                'device_name': self.device_name(iDevice),
+                'serial_number': self.device_serial_number(iDevice)
             })
             # _mp_log_debug(f"Found {type} device: {devicename.value.decode('UTF-8')} ({serialnum.value.decode('UTF-8')})")
         # print(connected_devices)
         # print(f"Discoverd {len(self.model.connected_devices)} devices.")
+        self.logger.info(f"Found {len(connected_devices)} devices.")
         return connected_devices
 
     @cmp.CProcess.register_for_signal('_changed')
     def ain_channels(self, device_id) -> list:
-        print(f">>> Reading available analog input channels for device {device_id}.")
         cInfo = c_int()
         self.dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(1), byref(cInfo))
-        return list(range(0, cInfo.value))
+        ain_channels = cInfo.value
+        if ain_channels == 0:
+            # Sometimes, the device reports a wrong number of ain channels
+            # so we can try to connect to the device first and retrieve the information
+            self.open_device(device_id)
+            ain_channels = self.analog_in_channels_count()
+            self.close_device()
+        self.logger.debug(f"Device {device_id} has {ain_channels} analog input channels.")
+        return list(range(0, ain_channels))
 
     @cmp.CProcess.register_for_signal('_changed')
     def ain_buffer_size(self, device_id) -> int:
@@ -97,69 +142,178 @@ class MPCaptDevice(cmp.CProcess):
         self.dwf.FDwfEnumConfigInfo(c_int(device_id), c_int(7), byref(cInfo))
         return cInfo.value
 
-    @cmp.CProcess.register_for_signal('_changed')
-    def dwf_version(self):
-        self.logger.debug(f"Getting DWF version information...")
-        version = create_string_buffer(16)
-        self.dwf.FDwfGetVersion(version)
-        return version.value.decode("utf-8")
-
     @cmp.CProcess.register_for_signal('_changed')
     def device_name(self, device_index: int) -> str:
-        devicename = create_string_buffer(64)
-        self.dwf.FDwfEnumDeviceName(c_int(device_index), devicename)
-        return str(devicename.value.decode("utf-8"))
+        try:
+            devicename = create_string_buffer(64)
+            self.dwf.FDwfEnumDeviceName(c_int(device_index), devicename)
+            return str(devicename.value.decode("utf-8"))
+        except Exception as e:
+            self.logger.error(f"Error while reading device name: {e}")
+            raise Exception(f"Error while reading device name: {e}")
 
     @cmp.CProcess.register_for_signal('_changed')
     def device_serial_number(self, device_index: int) -> str:
-        serialnum = create_string_buffer(16)
-        self.dwf.FDwfEnumSN(c_int(device_index), serialnum)
-        return str(serialnum.value.decode("utf-8")).replace("SN:", "")
-
+        try:
+            serialnum = create_string_buffer(16)
+            self.dwf.FDwfEnumSN(c_int(device_index), serialnum)
+            return str(serialnum.value.decode("utf-8")).replace("SN:", "")
+        except Exception as e:
+            self.logger.error(f"Error while reading device serial number: {e}")
+            raise Exception(f"Error while reading device serial number: {e}")
+
+    # ==================================================================================================================
+    # Device connection status
+    # ==================================================================================================================
     @cmp.CProcess.register_for_signal('_changed')
     def connected(self) -> bool:
         if self.hdwf is None or self.hdwf.value == 0:
+            szerr = create_string_buffer(512)
+            self.dwf.FDwfGetLastErrorMsg(szerr)
+            self.logger.error(str(szerr.value))
             return False
         else:
+            self.logger.debug(f"Device connected: {self._device_name} ({self._device_serial_number})")
             return True
 
+    # ==================================================================================================================
+    # Analog Input Channel Information
+    # ==================================================================================================================
+    def analog_in_channels_count(self) -> int:
+        """
+        Reads the number of AnalogIn channels of the device. The oscilloscope channel settings are
+        identical across all channels.
+        Calls WaveForms API Function 'FDwfAnalogInChannelCount(HDWF hdwf, int *pcChannel)'
+        :return: The number of analog in channels.
+        """
+        if self.connected():
+            try:
+                int0 = c_int()
+                self.dwf.FDwfAnalogInChannelCount(self.hdwf, byref(int0))
+                _analog_in_channels = int(int0.value)
+                return _analog_in_channels
+            except Exception as e:
+                self.logger.error(f"Can not read the AnalogIn Channel Count. {e}")
+                raise Exception(f"Can not read the AnalogIn Channel Count. {e}")
+        else:
+            self.logger.error(f"Can not read the AnalogIn Channel Count. Device not connected.")
+            raise Exception(f"Can not read the AnalogIn Channel Count. Device not connected.")
+
+    @cmp.CProcess.register_for_signal('_changed')
+    def analog_in_bits(self) -> int:
+        """
+        Retrieves the number bits used by the AnalogIn ADC. The oscilloscope channel settings are identical
+        across all channels.
+        Calls WaveForms API Function 'FDwfAnalogInBitsInfo(HDWF hdwf, int *pnBits)'
+        :return: The number bits used by the AnalogIn ADC.
+        """
+        int0 = c_int()
+        if self.connected():
+            self.dwf.FDwfAnalogInBitsInfo(self.hdwf, byref(int0))
+            _adc_bits = int(int0.value)
+            return _adc_bits
+        else:
+            self.logger.error(f"Can not read the AnalogIn Bits. Device not connected.")
+            raise Exception(f"Can not read the AnalogIn Bits. Device not connected.")
+
+    @cmp.CProcess.register_for_signal('_changed')
+    def analog_in_buffer_size(self) -> tuple:
+        """
+        Returns the minimum and maximum allowable buffer sizes for the instrument. The oscilloscope
+        channel settings are identical across all channels.
+        Calls WaveForms API Function 'FDwfAnalogInBufferSizeInfo(HDWF hdwf, int *pnSizeMin, int *pnSizeMax)'
+        :return: The minimum and maximum allowable buffer sizes for the instrument as a tuple (min, max).
+        """
+        if self.connected():
+            int0 = c_int()
+            int1 = c_int()
+            self.dwf.FDwfAnalogInBufferSizeInfo(self.hdwf, byref(int0), byref(int1))
+            _buffer_size = (int(int0.value), int(int0.value))
+            return _buffer_size
+        else:
+            self.logger.error(f"Can not read the AnalogIn Buffer Size. Device not connected.")
+            raise Exception(f"Can not read the AnalogIn Buffer Size. Device not connected.")
+
+    @cmp.CProcess.register_for_signal('_changed')
+    def analog_in_channel_range_info(self) -> tuple:
+        """
+        Returns the minimum and maximum range, peak to peak values, and the number of adjustable steps.
+        The oscilloscope channel settings are identical across all channels.
+        Calls WaveForms API Function
+        'FDwfAnalogInChannelRangeInfo(HDWF hdwf, double *pvoltsMin, double *pvoltsMax, double *pnSteps)'
+        :return: The minimum and maximum range, peak to peak values, and the number of adjustable steps as a tuple
+        (min, max, steps).
+        """
+        if self.connected:
+            dbl0 = c_double()
+            dbl1 = c_double()
+            dbl2 = c_double()
+            self.dwf.FDwfAnalogInChannelRangeInfo(self.hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
+            _range = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
+            return _range
+        else:
+            self.logger.error(f"Can not read the AnalogIn Channel Range. Device not connected.")
+            raise Exception(f"Can not read the AnalogIn Channel Range. Device not connected.")
+
+    @cmp.CProcess.register_for_signal('_changed')
+    def analog_in_offset(self) -> tuple:
+        """ Returns the minimum and maximum offset levels supported, and the number of adjustable steps"""
+        if self.connected():
+            dbl0 = c_double()
+            dbl1 = c_double()
+            dbl2 = c_double()
+            self.dwf.FDwfAnalogInChannelOffsetInfo(self.hdwf, byref(dbl0), byref(dbl1), byref(dbl2))
+            _offset = (int(dbl0.value), int(dbl1.value), int(dbl2.value))
+            return _offset
+        else:
+            self.logger.error(f"Can not read the AnalogIn Offset. Device not connected.")
+            raise Exception(f"Can not read the AnalogIn Offset. Device not connected.")
+
+    # ==================================================================================================================
+    # Functions for opening and closing the device
+    # ==================================================================================================================
     @cmp.CProcess.register_for_signal()
     def open_device(self, device_index):
         """
         Opens the device and returns the handle.
         :return: Device handle.
         """
+        self.logger.debug(f"Opening device {device_index}...")
         self._dwf_version = self.dwf_version()
-        self._device_name = self.device_name(device_index)
-        self._device_serial_number = self.device_serial_number(device_index)
 
         # Opens the device specified by idxDevice. The device handle is returned in hdwf. If idxDevice is -1, the
         # first available device is opened.
-        self.logger.info(f"[Task] Opening device #{device_index}...")
         self.dwf.FDwfDeviceOpen(c_int(device_index), byref(self.hdwf))
 
+        self._device_name = self.device_name(device_index)
+        self._device_serial_number = self.device_serial_number(device_index)
+
         if self.hdwf.value == 0:
             szerr = create_string_buffer(512)
             self.dwf.FDwfGetLastErrorMsg(szerr)
-            self.logger.error(f"Failed to open device: {szerr.value}")
+            err = szerr.value.decode("utf-8")
+            self.logger.error(f"Failed to open device: {err}")
             # ad2_state.connected = False
-            raise Exception(f"Failed to open device: {szerr.value}")
+            raise Exception(f"Failed to open device: {err}")
         else:
             self.logger.info(f"Device opened: {self._device_name} "
                              f"({self._device_serial_number})")
         self._connected = self.connected()
 
     def close_device(self):
-        #self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(channel))
+        # self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(channel))
         self.logger.info(f"[Task] Closing device...")
         self.dwf.FDwfDeviceClose(self.hdwf)
         self.hdwf.value = 0
         self._connected = self.connected()
         self.logger.info(f"[Task] Device closed.")
 
-    def setup_aquisition(self, sample_rate: float, ain_channel: int):
+    # ==================================================================================================================
+    # Function for setting up the acquisition
+    # ==================================================================================================================
+    def setup_acquisition(self, sample_rate: float, ain_channel: int):
         self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1),
-                               byref(self._ain_device_state))  # Variable to receive the acquisition state
+                                    byref(self._ain_device_state))  # Variable to receive the acquisition state
         self.logger.info(f"[Task] Setup for acquisition on channel {ain_channel} with rate {sample_rate} Hz.")
         self.dwf.FDwfAnalogInChannelEnableSet(self.hdwf, c_int(ain_channel), c_int(1))
         self.dwf.FDwfAnalogInChannelRangeSet(self.hdwf, c_int(ain_channel), c_double(5))
@@ -168,177 +322,205 @@ class MPCaptDevice(cmp.CProcess):
         self.dwf.FDwfAnalogInRecordLengthSet(self.hdwf, 0)  # -1 infinite record length
 
         # Variable to receive the acquisition state
-        self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_state))
-        self.logger.info(f"[Task] Wait 2 seconds for the offset to stabilize.")
+        #self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_state))
+        #self.logger.info(f"[Task] Wait 2 seconds for the offset to stabilize.")
         # wait at least 2 seconds for the offset to stabilize
-        time.sleep(2)
-        self.logger.info(f"[Task] Setup for acquisition done.")
+        #time.sleep(2)
+        #self.logger.info(f"[Task] Setup for acquisition done.")
+
+    # ==================================================================================================================
+    # Python wrapper for WaveForms API Functions
+    # ==================================================================================================================
+    @timeit
+    def _dwf_analog_in_status(self, hdwf, read_data, ptr_device_state):
+        try:
+            _read_data_cint = c_int(int(read_data))
+            self.dwf.FDwfAnalogInStatus(hdwf, _read_data_cint, ptr_device_state)
+        except Exception as e:
+            self.logger.error(f"Error while getting data from device: {e}")
+            raise Exception(f"Error while getting data from device: {e}")
+        return ptr_device_state
+
+    @timeit
+    def _dwf_analog_in_status_record(self, hdwf, ptr_c_available, ptr_c_lost, ptr_c_corrupted):
+        """
+        Retrieves information about the recording process. The data loss occurs when the device acquisition
+        is faster than the read process to PC. In this case, the device recording buffer is filled and data
+        samples are overwritten. Corrupt samples indicate that the samples have been overwritten by the
+        acquisition process during the previous read.
+        :param hdwf: Interface handle
+        :param c_available: Pointer to variable to receive the available number of samples.
+        :param c_lost: Pointer to variable to receive the lost samples after the last check.
+        :param c_corrupted:Pointer to variable to receive the number of samples that could be corrupt.
+        :return:
+        """
+        try:
+            self.dwf.FDwfAnalogInStatusRecord(hdwf, ptr_c_available, ptr_c_lost, ptr_c_corrupted)
+        except Exception as e:
+            self.logger.error(f"Error while getting data from device: {e}")
+            raise Exception(f"Error while getting data from device: {e}")
+        return ptr_c_available, ptr_c_lost, ptr_c_corrupted
+
+    @timeit
+    def _dwf_analog_in_status_data(self, hdwf, channel, ptr_rgd_samples, c_available):
+        """
+        Retrieves the acquired data samples from the specified idxChannel on the AnalogIn instrument. It
+        copies the data samples to the provided buffer.
+        :param hdwf: Interface handle
+        :param channel: Channel index
+        :param rgd_samples: Pointer to allocated buffer to copy the acquisition data.
+        :return:
+        """
+        try:
+            self.dwf.FDwfAnalogInStatusData(hdwf, c_int(channel), ptr_rgd_samples, c_available)  # get channel data
+        except Exception as e:
+            self.logger.error(f"Error while getting data from device: {e}")
+            raise Exception(f"Error while getting data from device: {e}")
+        return ptr_rgd_samples, c_available
 
     def start_capture(self, sample_rate: float, ain_channel: int):
         """
         Captures data from the device and puts it into a queue.
-        :param capture_data_queue:
-        :param state_queue:
-        :param start_capture:
-        :param end_process:
-        :param device_id:
+        :param ain_channel:
         :param sample_rate:
-        :param stream_data_queue: Queue to put the data into.
-        :param channel: Channel to capture data from.
         :return: None
         """
-        # Streaming the data should only be set to 1000Hz, otherwise the UI will freeze. The capturing however should
-        # stay at the given sample rate.
-        # Using the modulo operation allow us to determine the variable stream_n that is required
-        # to scale down the streaming rate.
-        stream_rate = 1000  # Hz
-        # stream_n = sample_rate / stream_rate
-        # stream_sample_cnt = 0
-
-        time_capture_started = 0
-        capturing_notified = False
-        # Print pid and ppid
-        self.logger.info(f"Starting capture thread, pid={os.getpid()}")
-        #ad2_state = AD2StateMPSetter(state_queue)
-
-        #ad2_state.pid = os.getpid()
-
-        #ad2_state.selected_ain_channel = channel
-        #ad2_state.sample_rate = sample_rate
-        #self.logger.debug(f"Setting up device {device_id} with "
-        #              f"channel {ad2_state.selected_ain_channel} and "
-        #              f"acquisition rate {ad2_state.sample_rate} Hz")
-
-        #dwf, hdwf = _mp_open_device(device_id, ad2_state)
-
-        # acquisition_state = c_byte()
-
-        cAvailable = c_int()
-        cLost = c_int()
-        cCorrupted = c_int()
-
         # FDwfAnalogInStatus(HDWF hdwf, BOOL fReadData, DwfState* psts)
-        self.setup_aquisition(ain_channel=ain_channel, sample_rate=sample_rate)
-        #_t_setup_sine_wave(dwf, hdwf, ad2_state)
+        self.setup_acquisition(ain_channel=ain_channel, sample_rate=sample_rate)
 
+        # Creates a Sin Wave on the Analog Out Channel 0
+        self.setup_sine_wave(channel=0)
         self.logger.info("Configuring acquisition. Starting oscilloscope.")
-        # FDwfAnalogInConfigure(HDWF hdwf, int fReconfigure, int fStart)
+
         # Configures the instrument and start or stop the acquisition. To reset the Auto trigger timeout, set
-        # fReconfigure to TRUE.
-        # hdwf – Interface handle.
-        # fReconfigure – Configure the device.
-        # fStart – Start the acquisition.
         self.dwf.FDwfAnalogInConfigure(self.hdwf, c_int(0), c_int(1))
-
         self.logger.info("Device configured. Starting acquisition.")
 
-        cSamples = 0
+        time_capture_started = 0
         capture_samples = 0
-        #print(end_process.value)
-        # Checks the state of the acquisition. To read the data from the device, set fReadData to TRUE. For
-        # single acquisition mode, the data will be read only when the acquisition is finished
-        time_FDwfAnalogInStatus_start = time.time()
-        self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_state))
-        time_FDwfAnalogInStatus_stop = time.time()
-        time_FDwfAnalogInStatus = time_FDwfAnalogInStatus_stop - time_FDwfAnalogInStatus_start
-        print(f"FDwfAnalogInStatus took {time_FDwfAnalogInStatus} seconds")
-
-        while True:
-            time_start = time.time()
-            print("New iteration")
-
-
-
-            time_cSamples_start = time.time()
-            if cSamples == 0 and (
-                    self._ain_device_state == DwfStateConfig or
-                    self._ain_device_state == DwfStatePrefill or
-                    self._ain_device_state == DwfStateArmed):
-                self.logger.info("Device in idle state. Waiting for acquisition to start.")
-                continue  # Acquisition not yet started.
-            time_cSamples_stop = time.time()
-            time_cSamples = time_cSamples_stop - time_cSamples_start
-            print(f"cSamples took {time_cSamples} seconds")
-
-
-            time_FDwfAnalogInStatusRecord_start = time.time()
-            self.dwf.FDwfAnalogInStatusRecord(self.hdwf, byref(cAvailable), byref(cLost), byref(cCorrupted))
-            cSamples += cLost.value
-            time_FDwfAnalogInStatusRecord_stop = time.time()
-            time_FDwfAnalogInStatusRecord = time_FDwfAnalogInStatusRecord_stop - time_FDwfAnalogInStatusRecord_start
-            print(f"FDwfAnalogInStatusRecord took {time_FDwfAnalogInStatusRecord} seconds")
-
-            time_samples_l_c_start = time.time()
-            if cLost.value:
-                self._samples_lost += cLost.value
-            if cCorrupted.value:
-                self._samples_corrupted += cCorrupted.value
-            time_samples_l_c_stop = time.time()
-            time_samples_l_c = time_samples_l_c_stop - time_samples_l_c_start
-            print(f"time_samples_l_c took {time_samples_l_c} seconds")
-
-            # self.dwf.FDwfAnalogInStatusSamplesValid(self.hdwf, byref(self.cValid))
-            if cAvailable.value == 0:
-                print(cAvailable.value)
-                continue
-            else:
-                # print(f"Available: {cAvailable.value}")
-                # if cSamples + cAvailable.value > self.ad2capt_model.n_samples:
-                #    cAvailable = c_int(self.ad2capt_model.n_samples - cSamples)
-                time_rgdsamples_start = time.time()
-                rgdSamples = (c_double * cAvailable.value)()
-                time_rgdsamples_stop = time.time()
-                time_rgdsamples = time_rgdsamples_stop - time_rgdsamples_start
-                print(f"rgdSamples took {time_rgdsamples} seconds")
-
-                time_FDwfAnalogInStatusData_start = time.time()
-                self.dwf.FDwfAnalogInStatusData(self.hdwf, c_int(ain_channel), byref(rgdSamples), cAvailable)  # get channel data
-                time_FDwfAnalogInStatusData_stop = time.time()
-                time_FDwfAnalogInStatusData = time_FDwfAnalogInStatusData_stop - time_FDwfAnalogInStatusData_start
-                print(f"FDwfAnalogInStatusData took {time_FDwfAnalogInStatusData} seconds")
-
-                # Print how many samples are available
-                status = {"available": cAvailable.value, 'captured': len(rgdSamples), 'lost': cLost.value,
-                          'corrupted': cCorrupted.value, "time": time.time()}
-                self.logger.debug(status)
-                print(f"took: {time.time() - time_start} seconds")
-                #self.stream_data_queue.put(
-                #    ([(float(s)) for s in rgdSamples], status)
-                #)
-
-                # if self.start_capture.value == int(True):
-                #     if not capturing_notified:
-                #         time_capture_started = time.time()
-                #         capture_samples = 0
-                #         _mp_log_info("Starting command recieved. Acquisition started.")
-                #         ad2_state.acquisition_state = AD2Constants.CapturingState.RUNNING()
-                #         capturing_notified = True
-                #     capture_samples = capture_samples + len(rgdSamples)
-                #     status = {
-                #         "available": cAvailable.value,
-                #         "captured": capture_samples,
-                #         "lost": cLost.value,
-                #         "corrupted": cCorrupted.value,
-                #         "recording_time": time.time() - time_capture_started}
-                #     capture_data_queue.put(([float(s) for s in rgdSamples], status))
-                #     # capture_data_queue.put([float(s) for s in rgdSamples])
-                # elif start_capture.value == 0:
-                #     if capturing_notified:
-                #         ad2_state.acquisition_state = AD2Constants.CapturingState.STOPPED()
-                #         time_capture_stopped = time.time()
-                #         time_captured = time_capture_stopped - time_capture_started
-                #         ad2_state.recording_time = time_captured
-                #         _mp_log_info(f"Acquisition stopped after {time_captured} seconds. Captured {capture_samples} "
-                #                      f"samples. Resulting in a time of {capture_samples / ad2_state.sample_rate} s.")
-                #         status = {
-                #             "available": cAvailable.value,
-                #             "captured": capture_samples,
-                #             "lost": cLost.value,
-                #             "corrupted": cCorrupted.value,
-                #             "recording_time": time.time() - time_capture_started}
-                #         capture_data_queue.put(([float(s) for s in rgdSamples], status))
-                #
-                #         capturing_notified = False
-                cSamples += cAvailable.value
-
+        capture_started = False
+        capture_ended = False
+
+        n_samples = int((sample_rate*2))
+        rgd_samples = (c_double * n_samples)()
+        #num_sent_samples = 0
+        try:
+            self.dwf.FDwfAnalogOutReset(self.hdwf, c_int(0))
+
+
+            while self.kill_capture_flag.value == int(False):
+                self._c_samples = 0
+
+
+                time_start = time.time()
+
+                # Checks the state of the acquisition. To read the data from the device, set fReadData to TRUE. For
+                # single acquisition mode, the data will be read only when the acquisition is finished
+                self.dwf.FDwfAnalogInStatus(self.hdwf, c_int(1), byref(self._ain_device_state))
+
+                if self._c_samples == 0 and (
+                        self._ain_device_state == DwfStateConfig or
+                        self._ain_device_state == DwfStatePrefill or
+                        self._ain_device_state == DwfStateArmed):
+                    # self.logger.info("Device in idle state. Waiting for acquisition to start.")
+                    continue  # Acquisition not yet started.
+
+                self.dwf.FDwfAnalogInStatusRecord(self.hdwf,
+                                                  byref(self._c_available),
+                                                  byref(self._c_lost), byref(self._c_corrupted)
+                                                  )
+                self._c_samples += self._c_lost.value
+                #if self._c_lost.value:
+                #    self._samples_lost += self._c_lost.value
+                #if self._c_corrupted.value:
+                #    self._samples_corrupted += self._c_corrupted.value
+
+                # self.dwf.FDwfAnalogInStatusSamplesValid(self.hdwf, byref(self.cValid))
+                if self._c_available.value == 0:
+                    pass
+                    #print("Nothing available")
+                    #continue
+
+                else:
+                    if self._c_samples + self._c_available.value > n_samples:
+                        self._c_available = c_int(n_samples - self._c_samples)
+
+                    # print(f"Available: {self._c_available.value}")
+                    # if cSamples + cAvailable.value > self.ad2capt_model.n_samples:
+                    #    cAvailable = c_int(self.ad2capt_model.n_samples - cSamples)
+                    # time_rgdsamples_start = time.time()
+
+                    # arr = [None] * cAvailable.value
+                    # time_rgdsamples_stop = time.time()
+                    # time_rgdsamples = time_rgdsamples_stop - time_rgdsamples_start
+                    # print(f"rgd_samples took {time_rgdsamples} seconds")
+                    rgd_samples = (c_double * self._c_available.value)()
+                    # Get the data from the device and store it in rgd_samples
+                    self.dwf.FDwfAnalogInStatusData(self.hdwf,
+                                                    c_int(ain_channel),
+                                                    rgd_samples,
+                                                    self._c_available)
+                    #print(f"Got data from device: {self._c_available.value}")
+                    self._c_samples += self._c_available.value
+                    iteration_time = time.time() - time_start
+
+                # Convert the data to a numpy array and put it into the queue
+                # self.logger.info("Convert data to numpy array and put it into the queue.")
+
+                #    num_sent_samples = 0
+                arr = np.array(rgd_samples)
+                #print(f"I send {len(arr)} samples to the queue.")
+                self.stream_data_queue.put(arr)
+
+                if self.start_capture_flag.value == int(True):
+                    if not capture_started:
+                        self.device_capturing(True)
+                        time_capture_started = time.time()
+                        self.logger.info(
+                            "**************************** Starting command received. Acquisition started.")
+                        capture_started = True
+                        capture_ended = False
+                        capture_samples = 0
+                    #capture_samples = capture_samples + len(arr)
+                    #self.capture_data_queue.put(arr)
+                elif self.start_capture_flag.value == int(False):
+
+                    if not capture_ended and capture_started:
+                        self.device_capturing(False)
+                        time_capture_stopped = time.time()
+                        time_captured = time_capture_stopped - time_capture_started
+                        self.logger.info(
+                            "**************************** Stopping command received. Acquisition stopped.")
+                        self.logger.info(
+                            f"Acquisition stopped after {time_captured} seconds. Captured {capture_samples} "
+                            f"samples. Resulting in a time of {capture_samples / sample_rate} s.")
+                        capture_ended = True
+                        capture_started = False
+                        #self.capture_data_queue.put(arr)
+                # self._c_samples += self._c_available.value
+
+
+
+        except Exception as e:
+            self.logger.error(f"Error while capturing data from device: {e}")
+            raise Exception(f"Error while capturing data from device: {e}")
+        self.logger.info("Capture thread ended.")
         self.close_device()
+
+    # ==================================================================================================================
+    # Others
+    # ==================================================================================================================
+    def setup_sine_wave(self, channel: int = 0):
+        self.logger.debug("Generating AM sine wave...")
+        self.dwf.FDwfAnalogOutNodeEnableSet(self.hdwf, c_int(0), c_int(0), c_int(1))  # carrier
+        self.dwf.FDwfAnalogOutNodeFunctionSet(self.hdwf, c_int(0), c_int(0), c_int(1))  # sine
+        self.dwf.FDwfAnalogOutNodeFrequencySet(self.hdwf, c_int(0), c_int(0), c_double(0.1))
+        self.dwf.FDwfAnalogOutNodeAmplitudeSet(self.hdwf, c_int(0), c_int(0), c_double(1))
+        # dwf.FDwfAnalogOutNodeOffsetSet(hdwf, c_int(0), c_int(0), c_double(0.5))
+        # dwf.FDwfAnalogOutNodeEnableSet(hdwf, c_int(0), c_int(2), c_int(1))  # AM
+        # dwf.FDwfAnalogOutNodeFunctionSet(hdwf, c_int(0), c_int(2), c_int(3))  # triangle
+        # dwf.FDwfAnalogOutNodeFrequencySet(hdwf, c_int(0), c_int(2), c_double(0.1))
+        # dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, c_int(0), c_int(2), c_double(50))
+        self.dwf.FDwfAnalogOutConfigure(self.hdwf, c_int(channel), c_int(1))
+        time.sleep(1)
+        self.logger.debug(f"Sine wave on output channel {channel} configured.")
diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py
index e5fc099b8365e8812f8d52ace0f60538322aea07..5b73a813bf1cdb3f0fb0847cac6d007ec6e8ab63 100644
--- a/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py
+++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPCaptDeviceControl.py
@@ -1,8 +1,11 @@
+import os
+
 import cmp
 from PySide6.QtCore import Signal
 
 from CaptDeviceControl.controller.mp_AD2Capture.MPCaptDevice import MPCaptDevice
 from model.AD2CaptDeviceModel import AD2CaptDeviceSignals, AD2CaptDeviceModel
+from model.AD2Constants import AD2Constants
 
 
 class MPCaptDeviceControl(cmp.CProcessControl):
@@ -17,45 +20,75 @@ class MPCaptDeviceControl(cmp.CProcessControl):
     open_device_finished = Signal()
     close_device_finished = Signal()
 
+    analog_in_bits_changed = Signal(int)
+    analog_in_buffer_size_changed = Signal(int)
+    analog_in_channel_range_changed = Signal(tuple)
+    analog_in_offset_changed = Signal(tuple)
+
+    device_capturing_changed = Signal(bool)
+
     def __init__(self,
                  model: AD2CaptDeviceModel,
                  streaming_data_queue,
                  capturing_data_queue,
                  start_capture_flag,
+                 kill_capture_flag,
                  parent=None,
-                 enable_logging=False):
-        super().__init__(parent, enable_logging=enable_logging)
+                 enable_internal_logging=False):
+        super().__init__(parent, enable_internal_logging=enable_internal_logging)
+        self.model = model
         self.register_child_process(
             MPCaptDevice(self.state_queue, self.cmd_queue,
                          streaming_data_queue,
                          capturing_data_queue,
                          start_capture_flag,
-                         enable_logging=enable_logging))
+                         kill_capture_flag,
+                         enable_internal_logging=enable_internal_logging))
+
+        self.logger, self.logger_handler = self.create_new_logger(f"{self.__class__.__name__}({os.getpid()})")
+
+        self.connected_devices_changed.connect(
+            lambda x: type(model.device_information).connected_devices.fset(model.device_information, x))
+        self.dwf_version_changed.connect(
+            lambda x: type(model).dwf_version.fset(model, x))
+        self.device_name_changed.connect(
+            lambda x: type(model.device_information).device_name.fset(model.device_information, x))
+        self.device_serial_number_changed.connect(
+            lambda x: type(model.device_information).device_serial_number.fset(model.device_information, x))
+        self.connected_changed.connect(
+            lambda x: type(model.device_information).device_connected.fset(model.device_information, x))
+
+        # Analog In Information
+        self.ain_channels_changed.connect(lambda x: type(model.analog_in).ain_channels.fset(model.analog_in, x))
+        self.ain_buffer_size_changed.connect(lambda x: type(model.analog_in).ain_buffer_size.fset(model.analog_in, x))
+        self.analog_in_bits_changed.connect(lambda x: type(model.analog_in).ain_bits.fset(model.analog_in, x))
+        self.analog_in_buffer_size_changed.connect(lambda x: type(model.analog_in).ain_buffer_size.fset(model.analog_in, x))
+        self.analog_in_channel_range_changed.connect(lambda x: type(model.analog_in).ai.fset(model.analog_in, x))
+        self.analog_in_offset_changed.connect(lambda x: type(model.analog_in).ain_offset.fset(model.analog_in, x))
 
-        self.connected_devices_changed.connect(lambda x: type(model).connected_devices.fset(model, x))
-        self.ain_channels_changed.connect(lambda x: type(model).ain_channels.fset(model, x))
-        self.ain_buffer_size_changed.connect(lambda x: type(model).ain_buffer_size.fset(model, x))
-        self.dwf_version_changed.connect(lambda x: type(model).dwf_version.fset(model, x))
-        self.device_name_changed.connect(lambda x: type(model).device_name.fset(model, x))
-        self.device_serial_number_changed.connect(lambda x: type(model).device_serial_number.fset(model, x))
-        self.connected_changed.connect(lambda x: type(model).connected.fset(model, x))
+        self.device_capturing_changed.connect(self.on_capturing_state_changed)
 
+    def on_capturing_state_changed(self, capturing: bool):
+        if capturing:
+            self.model.device_capturing_state = AD2Constants.CapturingState.RUNNING()
+        else:
+            self.model.device_capturing_state = AD2Constants.CapturingState.STOPPED()
 
     @cmp.CProcessControl.register_function()
     def connected_devices(self):
-        self._internal_logger.info("Discovering connected devices.")
+        self.logger.info("Discovering connected devices.")
 
     @cmp.CProcessControl.register_function()
     def ain_channels(self, device_id):
-        self._internal_logger.info(f"Reading available analog input channels for device {device_id}.")
+        self.logger.info(f"Reading available analog input channels for device {device_id}.")
 
     @cmp.CProcessControl.register_function()
     def open_device(self, device_index):
-        self._internal_logger.info(f"Opening device {device_index}.")
+        self.logger.info(f"Opening device {device_index}.")
 
     @cmp.CProcessControl.register_function()
     def close_device(self):
-        self._internal_logger.info(f"Closing device device.")
+        self.logger.info(f"Closing device device.")
 
     @cmp.CProcessControl.register_function()
     def start_capture(self, sample_rate, ain_channel):
diff --git a/src/CaptDeviceControl/controller/mp_AD2Capture/MPDeviceControl.py b/src/CaptDeviceControl/controller/mp_AD2Capture/MPDeviceControl.py
index 9aadab180388d634ac475dbb4d7fe239ad2ee1fd..e6692352a63dd679fd140a890b982315c9211b2d 100644
--- a/src/CaptDeviceControl/controller/mp_AD2Capture/MPDeviceControl.py
+++ b/src/CaptDeviceControl/controller/mp_AD2Capture/MPDeviceControl.py
@@ -9,7 +9,7 @@ import os
 import random
 import sys
 import time
-from ctypes import c_int, byref, c_double, cdll, create_string_buffer, c_int32
+from ctypes import c_int, byref, c_double, cdll, create_string_buffer, c_int32, CDLL
 
 from CaptDeviceControl.controller.mp_AD2Capture.AD2StateMPSetter import AD2StateMPSetter
 from CaptDeviceControl.model.AD2Constants import AD2Constants
@@ -54,25 +54,20 @@ def mp_capture(stream_data_queue, capture_data_queue, state_queue,
     :param channel: Channel to capture data from.
     :return: None
     """
-    # Streaming the data should only be set to 1000Hz, otherwise the UI will freeze. The capturing however should
-    # stay at the given sample rate.
-    # Using the modulo operation allow us to determine the variable stream_n that is required
-    # to scale down the streaming rate.
-    stream_rate = 1000  # Hz
-    #stream_n = sample_rate / stream_rate
-    #stream_sample_cnt = 0
 
     time_capture_started = 0
     capturing_notified = False
-    # Print pid and ppid
-    _mp_log_info(f"Starting capture thread, pid={os.getpid()}, ppid={os.getppid()}")
-    ad2_state = AD2StateMPSetter(state_queue)
 
+    ad2_state = AD2StateMPSetter(state_queue)
     ad2_state.pid = os.getpid()
+    ad2_state.ppid = os.getppid()
+    # Print pid and ppid
+    _mp_log_info(f"Starting capture thread, pid={ad2_state.pid}, ppid={ad2_state.ppid}")
 
     ad2_state.selected_ain_channel = channel
     ad2_state.sample_rate = sample_rate
-    _mp_log_debug(f"Setting up device {device_id} with "
+    ad2_state.device_index = device_id
+    _mp_log_debug(f"Setting up device {ad2_state.device_index} with "
                   f"channel {ad2_state.selected_ain_channel} and "
                   f"acquisition rate {ad2_state.sample_rate} Hz")
 
@@ -250,25 +245,12 @@ def _t_setup_aquisition(dwf, hdwf, ad2_state: AD2StateMPSetter):
 
 def _mp_discover_connected_devices(dwf):
     _mp_log_info(f"Discovering connected devices...")
-    # enumerate connected devices
-    connected_devices = []
-    # for filter_type in [(c_int32(enumfilterType.value | enumfilterUSB.value), 'USB'),
-    #                     (c_int32(enumfilterType.value | enumfilterNetwork.value), 'Network'),
-    #                     (c_int32(enumfilterType.value | enumfilterAXI.value), 'AXI'),
-    #                     (c_int32(enumfilterType.value | enumfilterRemote.value), 'Remote'),
-    #                     (c_int32(enumfilterType.value | enumfilterAudio.value), 'Audio'),
-    #                     (c_int32(enumfilterType.value | enumfilterDemo.value), 'Demo')]:
-    cDevice = c_int()
-    filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value| enumfilterUSB.value), 'USB')
-    # filter, type = (c_int32(enumfilterType.value | enumfilterDemo.value), 'DEMO')
-    _mp_log_debug(f"Filtering {type} devices...")
-    dwf.FDwfEnum(filter, byref(cDevice))
-    num_of_connected_devices = cDevice
+    num_of_connected_devices = 0
 
     devicename = create_string_buffer(64)
     serialnum = create_string_buffer(16)
 
-    for iDevice in range(0, cDevice.value):
+    for iDevice in enumerate_devices(dwf, show_demo_devices=True):
         dwf.FDwfEnumDeviceName(c_int(iDevice), devicename)
         dwf.FDwfEnumSN(c_int(iDevice), serialnum)
         connected_devices.append({
@@ -282,8 +264,25 @@ def _mp_discover_connected_devices(dwf):
     # print(f"Discoverd {len(self.model.connected_devices)} devices.")
     return connected_devices
 
+def enumerate_devices(dwf: CDLL, show_demo_devices=False) -> list:
+    """
+    Enumerates all connected devices. Function is used to discover all connected, compatible devices.
+    Builds an internal list of detected devices filtered by the enumfilter parameter. It must be called
+    before using other FDwfEnum functions because they obtain information about enumerated devices
+    from this list identified by the device index.
+    :param show_demo_devices: Specify if demo devices should be shown.
+    :return: A list from 0 to n devices.
+    """
+    if show_demo_devices:
+        enum_filter = c_int32(enumfilterType.value | enumfilterUSB.value | enumfilterDemo.value)
+    else:
+        enum_filter = c_int32(enumfilterType.value | enumfilterUSB.value)
+
+    cDevice = c_int()
+    dwf.FDwfEnum(enum_filter, byref(cDevice))
+    return list(range(0, cDevice.value))
 
-def _mp_open_device(device_index, ad2_state: AD2StateMPSetter):
+def _mp_open_device(ad2_state: AD2StateMPSetter):
     """
     Opens the device and returns the handle.
     :return: Device handle.
@@ -303,14 +302,14 @@ def _mp_open_device(device_index, ad2_state: AD2StateMPSetter):
 
     # Opens the device specified by idxDevice. The device handle is returned in hdwf. If idxDevice is -1, the
     # first available device is opened.
-    _mp_log_info(f"[Task] Opening device #{device_index}...")
-    dwf.FDwfDeviceOpen(c_int(device_index), byref(hdwf))
+    _mp_log_info(f"[Task] Opening device #{ad2_state.device_index}...")
+    dwf.FDwfDeviceOpen(c_int(ad2_state.device_index), byref(hdwf))
 
     devicename = create_string_buffer(64)
     serialnum = create_string_buffer(16)
 
-    dwf.FDwfEnumDeviceName(c_int(device_index), devicename)
-    dwf.FDwfEnumSN(c_int(device_index), serialnum)
+    dwf.FDwfEnumDeviceName(c_int(ad2_state.device_index), devicename)
+    dwf.FDwfEnumSN(c_int(ad2_state.device_index), serialnum)
 
     ad2_state.device_name = str(devicename.value.decode("utf-8"))
     ad2_state.device_serial_number = str(serialnum.value.decode("utf-8")).replace("SN:", "")
diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceAnalogInModel.py b/src/CaptDeviceControl/model/AD2CaptDeviceAnalogInModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..c22edf93a6eae905a01fa5d2b1222f8c499685b5
--- /dev/null
+++ b/src/CaptDeviceControl/model/AD2CaptDeviceAnalogInModel.py
@@ -0,0 +1,86 @@
+from ctypes import c_int, c_byte
+
+from PySide6.QtCore import QObject, Signal
+from CaptDeviceControl.CaptDeviceConfig import CaptDeviceConfig
+class AD2CaptDeviceAnalogInSignals(QObject):
+    def __init__(self, parent=None):
+        super().__init__(parent)
+
+    # Analog In Information
+    selected_ain_channel_changed = Signal(int)
+    ain_channels_changed = Signal(list)
+    ain_bits_changed = Signal(int)
+    ain_buffer_size_changed = Signal(int)
+    ain_channel_range_changed = Signal(tuple)
+    ain_offset_changed = Signal(tuple)
+    ain_device_state_changed = Signal(int)
+
+
+class AD2CaptDeviceAnalogInModel:
+
+    def __init__(self, config: CaptDeviceConfig):
+        self.signals = AD2CaptDeviceAnalogInSignals()
+        self.config = config
+        # Analog In Information
+        self._ain_channels: list = []
+        self._ain_bits: int = -1
+        self._ain_buffer_size: tuple = (-1, -1)
+        self._ain_channel_range: tuple = (-1, -1, -1)
+        self._ain_offset: tuple = (-1, -1, -1)
+        self._ain_device_state: int = -1
+
+    # ==================================================================================================================
+    # Analog In Information
+    # ==================================================================================================================
+    @property
+    def selected_ain_channel(self) -> int:
+        return self.config.ain_channel.get()
+
+    @selected_ain_channel.setter
+    def selected_ain_channel(self, value: int | c_int):
+        if isinstance(value, c_int):
+            value = int(value.value)
+        else:
+            value = int(value)
+        self.config.ain_channel.set(value)
+        self.signals.selected_ain_channel_changed.emit(self.selected_ain_channel)
+
+
+    @property
+    def ain_channels(self) -> list:
+        return self._ain_channels
+
+    @ain_channels.setter
+    def ain_channels(self, value: list):
+        self._ain_channels = value
+        self.signals.ain_channels_changed.emit(self.ain_channels)
+
+    @property
+    def ain_buffer_size(self) -> tuple:
+        return self._ain_buffer_size
+
+    @ain_buffer_size.setter
+    def ain_buffer_size(self, value: float):
+        self._ain_buffer_size = value
+        self.signals.ain_buffer_size_changed.emit(self.ain_buffer_size)
+
+    @property
+    def ain_bits(self) -> int:
+        return self._ain_bits
+
+    @ain_bits.setter
+    def ain_bits(self, value: int):
+        self._ain_bits = value
+        self.signals.ain_bits_changed.emit(self.ain_bits)
+
+    @property
+    def ain_device_state(self) -> int:
+        return self._ain_device_state
+
+    @ain_device_state.setter
+    def ain_device_state(self, value: c_int | int | c_byte):
+        if isinstance(value, c_int) or isinstance(value, c_byte):
+            self._ain_device_state = int(value.value)
+        else:
+            self._ain_device_state = int(value)
+        self.signals.ain_device_state_changed.emit(self.ain_device_state)
\ No newline at end of file
diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py b/src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e0734369f20745e1b067661b5cc2b75cc969280
--- /dev/null
+++ b/src/CaptDeviceControl/model/AD2CaptDeviceInformationModel.py
@@ -0,0 +1,117 @@
+from ctypes import c_int, Array
+
+from PySide6.QtCore import QObject, Signal
+
+
+class AD2CaptDeviceInformationSignals(QObject):
+    def __init__(self, parent=None):
+        super().__init__(parent)
+
+    # Connected Device Information
+    num_of_connected_devices_changed = Signal(int)
+    connected_devices_changed = Signal(list)
+    selected_device_index_changed = Signal(int)
+
+    # Device information
+    device_connected_changed = Signal(bool)
+    device_name_changed = Signal(str)
+    device_serial_number_changed = Signal(str)
+    device_index_changed = Signal(int)
+
+
+class AD2CaptDeviceInformationModel:
+    def __init__(self):
+        self.signals = AD2CaptDeviceInformationSignals()
+
+        # Connected Device Information
+        self._num_of_connected_devices: int = 0
+        self._connected_devices: list = []
+
+        # Device Information
+        self._selected_device_index: int = 0
+        self._device_connected: bool = False
+        self._device_name: str = "Unknown"
+        self._device_serial_number: str = "Unknown"
+
+    # ==================================================================================================================
+    # Connected Device Information
+    # ==================================================================================================================
+    @property
+    def num_of_connected_devices(self) -> int:
+        return self._num_of_connected_devices
+
+    @num_of_connected_devices.setter
+    def num_of_connected_devices(self, value: c_int | int):
+        if isinstance(value, c_int):
+            self._num_of_connected_devices = int(value.value)
+        else:
+            self._num_of_connected_devices = int(value)
+        self.signals.num_of_connected_devices_changed.emit(self._num_of_connected_devices)
+
+    @property
+    def connected_devices(self) -> list:
+        return self._connected_devices
+
+    @connected_devices.setter
+    def connected_devices(self, value: list):
+        self._connected_devices = value
+        self.signals.connected_devices_changed.emit(self.connected_devices)
+
+    @property
+    def selected_device_index(self) -> int:
+        return self._selected_device_index
+
+    @selected_device_index.setter
+    def selected_device_index(self, value: int):
+        self._selected_device_index = value
+        self.signals.selected_device_index_changed.emit(self.selected_device_index)
+
+    # ==================================================================================================================
+    # Device Information
+    # ==================================================================================================================
+    @property
+    def device_connected(self):
+        return self._device_connected
+
+    @device_connected.setter
+    def device_connected(self, value):
+        self._device_connected = value
+        self.signals.device_connected_changed.emit(self._device_connected)
+
+    @property
+    def device_name(self) -> str:
+        return self._device_name
+
+    @device_name.setter
+    def device_name(self, value: Array | str):
+        if not isinstance(value, str):
+            self._device_name = str(value.value.decode('UTF-8'))
+        else:
+            self._device_name = str(value)
+        self.signals.device_name_changed.emit(self.device_name)
+
+    @property
+    def device_serial_number(self) -> str:
+        return self._device_serial_number
+
+    @device_serial_number.setter
+    def device_serial_number(self, value: Array | str):
+        if not isinstance(value, str):
+            self._device_serial_number = str(value.value.decode('UTF-8'))
+        else:
+            self._device_serial_number = str(value)
+        (self.signals.
+
+         device_serial_number_changed.emit(self.device_serial_number))
+
+    @property
+    def device_index(self) -> int:
+        return self._device_index
+
+    @device_index.setter
+    def device_index(self, value: c_int | int):
+        if isinstance(value, c_int):
+            self._device_index = int(value.value)
+        else:
+            self._device_index = int(value)
+        self.signals.device_serial_number_changed.emit(self.device_index)
diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceModel.py b/src/CaptDeviceControl/model/AD2CaptDeviceModel.py
index 3b9f0dc82c3100e03ba4d0e9f295e1729032441a..5a9c6bc1d3c47d8072ac0f2d993f9cc3b8f57433 100644
--- a/src/CaptDeviceControl/model/AD2CaptDeviceModel.py
+++ b/src/CaptDeviceControl/model/AD2CaptDeviceModel.py
@@ -4,11 +4,16 @@ from PySide6.QtCore import QObject, Signal
 
 from CaptDeviceControl.model.AD2Constants import AD2Constants
 from CaptDeviceControl.CaptDeviceConfig import CaptDeviceConfig as Config
-#from MeasurementData.Properties.AD2CaptDeviceProperties import AD2CaptDeviceProperties
+from CaptDeviceControl.model.AD2CaptDeviceAnalogInModel import AD2CaptDeviceAnalogInModel
+from CaptDeviceControl.model.AD2CaptDeviceInformationModel import AD2CaptDeviceInformationSignals, \
+    AD2CaptDeviceInformationModel
+
+
+# from MeasurementData.Properties.AD2CaptDeviceProperties import AD2CaptDeviceProperties
 
 
 class AD2CaptDeviceSignals(QObject):
-    def __init__(self, parent = None):
+    def __init__(self, parent=None):
         super().__init__(parent)
 
     ad2captdev_config_changed = Signal(Config)
@@ -16,29 +21,11 @@ class AD2CaptDeviceSignals(QObject):
     # WaveForms Runtime (DWF) Information
     dwf_version_changed = Signal(str)
 
-    # Connected Device Information
-    num_of_connected_devices_changed = Signal(int)
-    connected_devices_changed = Signal(list)
-    selected_device_changed = Signal(int)
-
-    # Device information
-    connected_changed = Signal(bool)
-    device_name_changed = Signal(str)
-    device_serial_number_changed = Signal(str)
-    device_index_changed = Signal(int)
-
     # Acquisition Settings
     sample_rate_changed = Signal(int)
     streaming_rate_changed = Signal(int)
-    selected_ain_channel_changed = Signal(int)
-    duration_streaming_history_changed = Signal(int)
-
-    # Analog In Information
-    ain_channels_changed = Signal(list)
-    ain_buffer_size_changed = Signal(int)
-    ain_bits_changed = Signal(int)
-    ain_device_state_changed = Signal(int)
 
+    duration_streaming_history_changed = Signal(int)
 
     # Analog Out Information
     aout_channels_changed = Signal(list)
@@ -63,17 +50,13 @@ class AD2CaptDeviceSignals(QObject):
     # Multiprocessing Information
     pid_changed = Signal(int)
 
-
     # ==================================================================================================================
     # Delete later
     # Signals for device information
     hwdf_changed = Signal(int)
 
-
     device_ready_changed = Signal(bool)
 
-
-
     # Signals for reporting if samples were lost or corrupted
     fLost_changed = Signal(int)
     fCorrupted_changed = Signal(int)
@@ -83,13 +66,11 @@ class AD2CaptDeviceSignals(QObject):
 
     # Recording settings for starting, stopping and pausing
 
-
     # Signal if new samples have been aquired
 
     all_recorded_samples_changed = Signal(list)
     num_of_current_recorded_samples_changed = Signal(int)
     measurement_time_changed = Signal(float)
-    
 
     ad2_settings = Signal(dict)
     error = Signal(str)
@@ -99,8 +80,6 @@ class AD2CaptDeviceSignals(QObject):
     ad2_captured_value = Signal(list)
 
 
-
-
 class AD2CaptDeviceModel:
 
     def __init__(self, ad2captdev_config: Config):
@@ -110,31 +89,15 @@ class AD2CaptDeviceModel:
         # WaveForms Runtime (DWF) Information
         self._dwf_version: str = "Unknown"
 
-        # Connected Device Information
-        self._num_of_connected_devices: int = 0
-        self._connected_devices: list = []
-        self._selected_device: int = 0
-
-        # Device Information
-        self._connected: bool = False
-        self._device_name: str = "Unknown"
-        self._device_serial_number: str = "Unknown"
-        self._device_index: int = -1
+        self.device_information = AD2CaptDeviceInformationModel()
+        self.analog_in = AD2CaptDeviceAnalogInModel(self.ad2captdev_config)
 
         # Acquisition Settings
         self._sample_rate: int = self.ad2captdev_config.sample_rate.value
         self._streaming_rate: int = self.ad2captdev_config.streaming_rate.value
 
-        self._selected_ain_channel: int = 0
         self._duration_streaming_history: float = 0
 
-
-        # Analog In Information
-        self._ain_channels: list = []
-        self._ain_buffer_size: float = -1
-        self._ain_device_state: int = -1
-        self._ain_bits: int = -1
-
         # Analog Out Information
         self.aout_channels: list = []
 
@@ -150,7 +113,6 @@ class AD2CaptDeviceModel:
         self._unconsumed_stream_samples: int = 0
         self._unconsumed_capture_samples: int = 0
 
-
         # Recording Flags (starting, stopping and pausing)
         self._device_capturing_state: AD2Constants.CapturingState = AD2Constants.CapturingState.STOPPED()
         self._start_recording = False
@@ -181,8 +143,6 @@ class AD2CaptDeviceModel:
         self._num_of_current_recorded_samples: int = 0
         self._n_samples: int = 0
 
-
-
     @property
     def ad2captdev_config(self) -> Config:
         return self._ad2captdev_config
@@ -207,89 +167,6 @@ class AD2CaptDeviceModel:
             self._dwf_version = value
         self.signals.dwf_version_changed.emit(self.dwf_version)
 
-    # ==================================================================================================================
-    # Connected Device Information
-    # ==================================================================================================================
-    @property
-    def num_of_connected_devices(self) -> int:
-        return self._num_of_connected_devices
-
-    @num_of_connected_devices.setter
-    def num_of_connected_devices(self, value: c_int | int):
-        if isinstance(value, c_int):
-            self._num_of_connected_devices = int(value.value)
-        else:
-            self._num_of_connected_devices = int(value)
-        self.signals.num_of_connected_devices_changed.emit(self._num_of_connected_devices)
-
-    @property
-    def connected_devices(self) -> list:
-        return self._connected_devices
-
-    @connected_devices.setter
-    def connected_devices(self, value: list):
-        self._connected_devices = value
-        self.signals.connected_devices_changed.emit(self.connected_devices)
-
-    @property
-    def selected_device(self) -> int:
-        return self._selected_device
-
-    @selected_device.setter
-    def selected_device(self, value: int):
-        self._selected_device = value
-        self.signals.selected_device_changed.emit(self.selected_device)
-
-
-    # ==================================================================================================================
-    # Device Information
-    # ==================================================================================================================
-    @property
-    def connected(self):
-        return self._connected
-
-    @connected.setter
-    def connected(self, value):
-        self._connected = value
-        self.signals.connected_changed.emit(self._connected)
-
-    @property
-    def device_name(self) -> str:
-        return self._device_name
-
-    @device_name.setter
-    def device_name(self, value: Array | str):
-        if not isinstance(value, str):
-            self._device_name = str(value.value.decode('UTF-8'))
-        else:
-            self._device_name = str(value)
-        self.signals.device_name_changed.emit(self.device_name)
-
-    @property
-    def device_serial_number(self) -> str:
-        return self._device_serial_number
-
-    @device_serial_number.setter
-    def device_serial_number(self, value: Array | str):
-        if not isinstance(value, str):
-            self._device_serial_number = str(value.value.decode('UTF-8'))
-        else:
-            self._device_serial_number = str(value)
-        self.signals.device_serial_number_changed.emit(self.device_serial_number)
-
-    @property
-    def device_index(self) -> int:
-        return self._device_index
-
-    @device_index.setter
-    def device_index(self, value: c_int | int):
-        if isinstance(value, c_int):
-            self._device_index = int(value.value)
-        else:
-            self._device_index = int(value)
-        self.signals.device_serial_number_changed.emit(self.device_index)
-
-
     # ==================================================================================================================
     # Acquisition Settings
     # ==================================================================================================================
@@ -313,19 +190,6 @@ class AD2CaptDeviceModel:
         self.ad2captdev_config.streaming_rate.set(self._streaming_rate)
         self.signals.streaming_rate_changed.emit(self._streaming_rate)
 
-    @property
-    def selected_ain_channel(self) -> int:
-        return self._selected_ain_channel
-
-    @selected_ain_channel.setter
-    def selected_ain_channel(self, value: int | c_int):
-        if isinstance(value, c_int):
-            self._selected_ain_channel = int(value.value)
-        else:
-            self._selected_ain_channel = int(value)
-        self.ad2captdev_config.ain_channel.set(self._selected_ain_channel)
-        self.signals.selected_ain_channel_changed.emit(self.selected_ain_channel)
-
     @property
     def duration_streaming_history(self) -> float:
         return self._duration_streaming_history
@@ -335,48 +199,6 @@ class AD2CaptDeviceModel:
         self._duration_streaming_history = value
         self.signals.duration_streaming_history_changed.emit(self.duration_streaming_history)
 
-    # ==================================================================================================================
-    # Analog In Information
-    # ==================================================================================================================
-    @property
-    def ain_channels(self) -> list:
-        return self._ain_channels
-
-    @ain_channels.setter
-    def ain_channels(self, value: list):
-        self._ain_channels = value
-        self.signals.ain_channels_changed.emit(self.ain_channels)
-
-    @property
-    def ain_buffer_size(self) -> float:
-        return self._ain_buffer_size
-
-    @ain_buffer_size.setter
-    def ain_buffer_size(self, value: float):
-        self._ain_buffer_size = value
-        self.signals.ain_buffer_size_changed.emit(self.ain_buffer_size)
-
-    @property
-    def ain_bits(self) -> int:
-        return self._ain_bits
-
-    @ain_bits.setter
-    def ain_bits(self, value: int):
-        self._ain_bits = value
-        self.signals.ain_bits_changed.emit(self.ain_bits)
-
-    @property
-    def ain_device_state(self) -> int:
-        return self._ain_device_state
-
-    @ain_device_state.setter
-    def ain_device_state(self, value: c_int | int | c_byte):
-        if isinstance(value, c_int) or isinstance(value, c_byte):
-            self._ain_device_state = int(value.value)
-        else:
-            self._ain_device_state = int(value)
-        self.signals.ain_device_state_changed.emit(self.ain_device_state)
-
     # ==================================================================================================================
     # Analog Out Information
     # ==================================================================================================================
@@ -389,7 +211,6 @@ class AD2CaptDeviceModel:
         self._aout_channels = value
         self.signals.aout_channels_changed.emit(self.aout_channels)
 
-
     # ==================================================================================================================
     # Acquired Signal Information
     # ==================================================================================================================
@@ -401,7 +222,7 @@ class AD2CaptDeviceModel:
     def recorded_samples(self, value: list):
         self._recorded_samples = value
         self.samples_captured = len(self._recorded_samples)
-        #self.signals.num_of_current_recorded_samples_changed.emit(self.num_of_current_recorded_samples)
+        # self.signals.num_of_current_recorded_samples_changed.emit(self.num_of_current_recorded_samples)
         self.signals.recorded_samples_changed.emit(self.recorded_samples)
 
     @property
@@ -440,17 +261,15 @@ class AD2CaptDeviceModel:
         self._samples_corrupted = value
         self.signals.samples_corrupted_changed.emit(self.samples_corrupted)
 
-
     @property
     def capturing_finished(self) -> bool:
         return self._capturing_finished
-    
+
     @capturing_finished.setter
     def capturing_finished(self, value: bool):
         self._capturing_finished = value
-        print(f"Set _capturing_finished to { self._capturing_finished}")
+        print(f"Set _capturing_finished to {self._capturing_finished}")
         self.signals.capturing_finished_changed.emit(self._capturing_finished)
-        
 
     # ==================================================================================================================
     # Recording Flags (starting, stopping and pausing)
@@ -491,7 +310,6 @@ class AD2CaptDeviceModel:
         self._reset_recording = value
         self.signals.reset_recording_changed.emit(self._reset_recording)
 
-
     # ==================================================================================================================
     # Multiprocessing Flags
     # ==================================================================================================================
@@ -522,8 +340,6 @@ class AD2CaptDeviceModel:
         self._unconsumed_capture_samples = value
         self.signals.unconsumed_capture_samples_changed.emit(self.unconsumed_capture_samples)
 
-
-
     # ==================================================================================================================
     # ==================================================================================================================
     # DELETE LATER
@@ -538,7 +354,7 @@ class AD2CaptDeviceModel:
         self._auto_connect = value
 
     @property
-    #def ad2_properties(self) -> AD2CaptDeviceProperties:
+    # def ad2_properties(self) -> AD2CaptDeviceProperties:
     #    return AD2CaptDeviceProperties(self._fLost, self._fCorrupted,
     #                                   self._sample_rate, self._n_samples,
     #                                   self._measurement_time)
@@ -593,7 +409,6 @@ class AD2CaptDeviceModel:
         self._all_recorded_samples = value
         self.signals.all_recorded_samples_changed.emit(self.all_recorded_samples)
 
-
     @property
     def n_samples(self):
         return self._n_samples
@@ -620,5 +435,3 @@ class AD2CaptDeviceModel:
     def fLost(self, value):
         self._fLost = value
         self.signals.fLost_changed.emit(self._fLost)
-
-
diff --git a/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py b/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py
new file mode 100644
index 0000000000000000000000000000000000000000..81f119f75525d46bf37bfaddff45d65e97a7399b
--- /dev/null
+++ b/src/CaptDeviceControl/model/AD2CaptDeviceSignals.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+"""
+Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
+Created: 2023-10-19 12:35
+Package Version: 
+"""
+from PySide6.QtCore import QObject, Signal
+
+from CaptDeviceConfig import CaptDeviceConfig as Config
+from controller.DeviceInformation.HWDeviceInformation import HWDeviceInformation
+from model.AD2Constants import AD2Constants
+
+
+class AD2CaptDeviceSignals(QObject):
+    def __init__(self, parent=None):
+        super().__init__(parent)
+
+    ad2captdev_config_changed = Signal(Config)
+
+    # WaveForms Runtime (DWF) Information
+    dwf_version_changed = Signal(str)
+
+    # Connected Device Information
+    num_of_discovered_devices_changed = Signal(int)
+    discovered_devices_changed = Signal(list)
+    selected_devices_changed = Signal(HWDeviceInformation)
+
+    # Device information
+    connected_changed = Signal(bool)
+    device_name_changed = Signal(str)
+    serial_number_changed = Signal(str)
+    device_index_changed = Signal(int)
+
+    # Acquisition Settings
+    sample_rate_changed = Signal(int)
+    streaming_rate_changed = Signal(int)
+    selected_ain_channel_changed = Signal(int)
+    duration_streaming_history_changed = Signal(int)
+
+    # Analog In Information
+    ain_channels_changed = Signal(list)
+    ain_buffer_size_changed = Signal(int)
+    ain_bits_changed = Signal(int)
+    ain_device_state_changed = Signal(int)
+
+    # Analog Out Information
+    aout_channels_changed = Signal(list)
+
+    # Acquired Signal Information
+    recorded_samples_changed = Signal(list)
+    recording_time_changed = Signal(float)
+    samples_captured_changed = Signal(int)
+    samples_lost_changed = Signal(int)
+    samples_corrupted_changed = Signal(int)
+    # Actually for the worker, these are the samples that have not been consumed yet by the UI thread.
+    unconsumed_stream_samples_changed = Signal(int)
+    unconsumed_capture_samples_changed = Signal(int)
+
+    # Recording Flags (starting, stopping and pausing)
+    device_capturing_state_changed = Signal(AD2Constants.CapturingState)
+    start_recording_changed = Signal(bool)
+    stop_recording_changed = Signal(bool)
+    reset_recording_changed = Signal(bool)
+    capturing_finished_changed = Signal(bool)
+
+    # Multiprocessing Information
+    pid_changed = Signal(int)
+
+    # ==================================================================================================================
+    # Delete later
+    # Signals for device information
+    hwdf_changed = Signal(int)
+
+    device_ready_changed = Signal(bool)
+
+    # Signals for reporting if samples were lost or corrupted
+    fLost_changed = Signal(int)
+    fCorrupted_changed = Signal(int)
+    # Acquisition information
+
+    n_samples_changed = Signal(int)
+
+    # Recording settings for starting, stopping and pausing
+
+    # Signal if new samples have been aquired
+
+    all_recorded_samples_changed = Signal(list)
+    num_of_current_recorded_samples_changed = Signal(int)
+    measurement_time_changed = Signal(float)
+
+    ad2_settings = Signal(dict)
+    error = Signal(str)
+    ad2_is_capturing = Signal(bool)
+
+    ad2_set_acquisition = Signal(bool)
+    ad2_captured_value = Signal(list)
diff --git a/src/CaptDeviceControl/view/AD2CaptDeviceView.py b/src/CaptDeviceControl/view/AD2CaptDeviceView.py
index cdb348a5a6cca2066d56354169b63e54c1380a5a..93aa47b51004a0c12b335db918af26768b71222c 100644
--- a/src/CaptDeviceControl/view/AD2CaptDeviceView.py
+++ b/src/CaptDeviceControl/view/AD2CaptDeviceView.py
@@ -1,4 +1,5 @@
 import logging
+import os
 from collections import deque
 
 import numpy as np
@@ -9,8 +10,9 @@ from PySide6.QtWidgets import QMainWindow, QStatusBar
 from pyqtgraph.dockarea import DockArea, Dock
 
 import pyqtgraph as pg
+from rich.logging import RichHandler
 
-from CaptDeviceControl.controller.AD2CaptDeviceController import AD2CaptDeviceController
+from CaptDeviceControl.controller.BaseAD2CaptDevice import BaseAD2CaptDevice
 from CaptDeviceControl.model.AD2CaptDeviceModel import AD2CaptDeviceModel
 from CaptDeviceControl.model.AD2Constants import AD2Constants
 from CaptDeviceControl.view.Ui_AD2ControlWindow import Ui_AD2ControlWindow
@@ -23,9 +25,14 @@ from fswidgets import PlayPushButton
 
 class ControlWindow(QMainWindow):
 
-    def __init__(self, model: AD2CaptDeviceModel, controller: AD2CaptDeviceController):
+    def __init__(self, model: AD2CaptDeviceModel, controller: BaseAD2CaptDevice):
         super().__init__()
-        self.logger = logging.getLogger("AD2ControlWindow")
+        self.handler = RichHandler(rich_tracebacks=True)
+        self.logger = logging.getLogger(f"AD2Window({os.getpid()})")
+        self.logger.handlers = [self.handler]
+        self.logger.setLevel(logging.DEBUG)
+        formatter = logging.Formatter('%(name)s %(message)s')
+        self.handler.setFormatter(formatter)
 
         self.controller = controller
         self.model = model
@@ -51,10 +58,10 @@ class ControlWindow(QMainWindow):
         # Timer for periodically updating the plot
         self.capture_update_timer = QTimer()
         self.capture_update_timer.setInterval(10)
-        self.capture_update_timer.timeout.connect(self._on_capture_update_plot)
+        #self.capture_update_timer.timeout.connect(self._on_capture_update_plot)
 
         self.stream_update_timer = QTimer()
-        self.stream_update_timer.setInterval(10)
+        self.stream_update_timer.setInterval(40)
         self.stream_update_timer.timeout.connect(self._on_stream_update_timer_timeout)
 
         self.stream_samples_frequency = 1000
@@ -95,33 +102,33 @@ class ControlWindow(QMainWindow):
         self.model.signals.dwf_version_changed.connect(self._on_dwf_version_changed)
 
         # Connected Device Information
-        self.model.signals.num_of_connected_devices_changed.connect(self._on_num_of_connected_devices_changed)
-        self.model.signals.connected_devices_changed.connect(self._on_connected_devices_changed)
+        self.model.device_information.signals.num_of_connected_devices_changed.connect(self._on_num_of_connected_devices_changed)
+        self.model.device_information.signals.connected_devices_changed.connect(self._on_connected_devices_changed)
 
         # Device information
-        self.model.signals.connected_changed.connect(self._on_connected_changed)
-        self.model.signals.device_name_changed.connect(self._on_device_name_changed)
-        self.model.signals.device_serial_number_changed.connect(self._on_device_serial_number_changed)
-        self.model.signals.device_index_changed.connect(self._on_device_index_changed)
+        self.model.device_information.signals.device_connected_changed.connect(self._on_connected_changed)
+        self.model.device_information.signals.device_name_changed.connect(self._on_device_name_changed)
+        self.model.device_information.signals.device_serial_number_changed.connect(self._on_device_serial_number_changed)
+        self.model.device_information.signals.device_index_changed.connect(self._on_device_index_changed)
 
         # Acquisition Settings
         self.model.signals.sample_rate_changed.connect(self._model_on_sample_rate_changed)
-        self.model.signals.selected_ain_channel_changed.connect(self._model_on_selected_ain_changed)
 
         # Analog In Information
-        self.model.signals.ain_channels_changed.connect(self._on_ain_channels_changed)
-        self.model.signals.ain_buffer_size_changed.connect(self._on_ain_buffer_size_changed)
-        self.model.signals.ain_bits_changed.connect(self._on_ain_bits_changed)
-        self.model.signals.ain_device_state_changed.connect(self._on_ain_device_state_changed)
+        self.model.analog_in.signals.selected_ain_channel_changed.connect(self._model_on_selected_ain_changed)
+        self.model.analog_in.signals.ain_channels_changed.connect(self._on_ain_channels_changed)
+        self.model.analog_in.signals.ain_buffer_size_changed.connect(self._on_ain_buffer_size_changed)
+        self.model.analog_in.signals.ain_bits_changed.connect(self._on_ain_bits_changed)
+        self.model.analog_in.signals.ain_device_state_changed.connect(self._on_ain_device_state_changed)
 
 
         # Analog Out Information
         self.model.signals.aout_channels_changed.connect(self._on_aout_channels_changed)
 
         # Acquired Signal Information
-        self.model.signals.recorded_samples_changed.connect(self._on_recorded_samples_changed)
-        self.model.signals.recording_time_changed.connect(self._on_recording_time_changed)
-        self.model.signals.samples_captured_changed.connect(self._on_samples_captured_changed)
+        #self.model.signals.recorded_samples_changed.connect(self._on_recorded_samples_changed)
+        #self.model.signals.recording_time_changed.connect(self._on_recording_time_changed)
+        #self.model.signals.samples_captured_changed.connect(self._on_samples_captured_changed)
         self.model.signals.samples_lost_changed.connect(self._on_samples_lost_changed)
         self.model.signals.samples_corrupted_changed.connect(self._on_samples_corrupted_changed)
 
@@ -179,8 +186,8 @@ class ControlWindow(QMainWindow):
     # Slots for Model
     # ==================================================================================================================
     def _on_device_selected_changed(self, index):
-        self.model.selected_device = index
-        self.controller.get_analog_in_informatio()
+        self.model.device_information.device_index = index
+        self.controller.device_selected_index_changed()
         # First populate the AIn box+
         #m: dict = self.model.connected_devices[index]
         #self.model.ain_channels = list(range(0, int(m['analog_in_channels'])))
@@ -201,6 +208,7 @@ class ControlWindow(QMainWindow):
             dev: dict
             #  'type': type, 'device_id', 'device_name', 'serial_number'
             self._ui.cb_device_select.addItem(f"{it}: {dev['type']}{dev['device_id']} - {dev['device_name']}")
+        self._ui.cb_device_select.setCurrentIndex(0)
 
     # ============== Device information
     def _on_connected_changed(self, connected):
@@ -396,7 +404,7 @@ class ControlWindow(QMainWindow):
         self.scope_original.clear()
         # print(self.ad2device.recorded_samples)
         self.scope_original.plot(
-            list(self.controller.streaming_data_dqueue),#[::self.stream_n],
+            np.array(self.controller.streaming_data_dqueue)[::100],
             pen=pg.mkPen(width=1))
         self._ui.lcd_unconsumed_stream.display(self.model.unconsumed_stream_samples)
 
@@ -405,8 +413,8 @@ class ControlWindow(QMainWindow):
     #
     # ==================================================================================================================
     def on_btn_connect_to_device_clicked(self):
-        print(self.model.connected)
-        if self.model.connected:
+        print(self.model.device_information.device_connected)
+        if self.model.device_information.device_connected:
             self.controller.close_device()
             self._ui.btn_connect.setText("Connect")
         else:
diff --git a/tests/testing_mp_pyside.py b/tests/testing_mp_pyside.py
new file mode 100644
index 0000000000000000000000000000000000000000..14a8cbb4949b99c614a7942f73d612594968c1a9
--- /dev/null
+++ b/tests/testing_mp_pyside.py
@@ -0,0 +1,42 @@
+import sys
+import multiprocessing
+from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
+from PySide6.QtCore import Signal, QObject, Qt
+from time import sleep
+
+class ChildProcess(QObject):
+    finished = Signal(str)
+
+    def run(self):
+        app = QApplication(sys.argv)
+        main_window = MainWindow()
+        main_window.show()
+        sys.exit(app.exec_())
+
+class MainWindow(QMainWindow):
+    def __init__(self):
+        super().__init__()
+
+        self.setWindowTitle("Multiprocessing with PySide6")
+        self.setGeometry(100, 100, 400, 200)
+
+        self.central_widget = QWidget(self)
+        self.setCentralWidget(self.central_widget)
+
+        self.layout = QVBoxLayout(self.central_widget)
+
+        self.start_button = QPushButton("Start Child Process", self)
+        self.start_button.clicked.connect(self.start_child_process)
+        self.layout.addWidget(self.start_button)
+
+        self.child_process = None
+
+    def start_child_process(self):
+        if not self.child_process or not self.child_process.is_alive():
+            self.child_process = multiprocessing.Process(target=self.run_child_process)
+            self.child_process.start()
+            self.start_button.setEnabled(False)
+
+    def run_child_process(self):
+        child = ChildProcess()
+        child.finished.connect(self.handle_child_signal)