diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..71b07f0151d15e5c8a07ed31816ccc98bcad65fa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+# folder with images
+/Bilder/*
+
+# Python cache-files
+*.pyc
\ No newline at end of file
diff --git a/Klassendiagramm.png b/Klassendiagramm.png
new file mode 100644
index 0000000000000000000000000000000000000000..3dcc27a5c2cf26f14d8b891ae4180a41a76fbda6
Binary files /dev/null and b/Klassendiagramm.png differ
diff --git a/Lokalisierung_LEGO/.vscode/extensions.json b/Lokalisierung_LEGO/.vscode/extensions.json
new file mode 100644
index 0000000000000000000000000000000000000000..f8f1a4401370ad4181562d98b9cbf1795831c631
--- /dev/null
+++ b/Lokalisierung_LEGO/.vscode/extensions.json
@@ -0,0 +1,13 @@
+{
+	// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
+	// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
+
+	// List of extensions which should be recommended for users of this workspace.
+	"recommendations": [
+		"lego-education.ev3-micropython"
+	],
+	// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
+	"unwantedRecommendations": [
+		"ms-python.python"
+	]
+}
\ No newline at end of file
diff --git a/Lokalisierung_LEGO/.vscode/launch.json b/Lokalisierung_LEGO/.vscode/launch.json
new file mode 100644
index 0000000000000000000000000000000000000000..d933aeb1fa42c27e31946454216860b6d7e83f9d
--- /dev/null
+++ b/Lokalisierung_LEGO/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+	// Use IntelliSense to learn about possible attributes.
+	// Hover to view descriptions of existing attributes.
+	// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+	"version": "0.2.0",
+	"configurations": [
+		{
+			"name": "Download and Run",
+			"type": "ev3devBrowser",
+			"request": "launch",
+			"program": "/home/robot/${workspaceRootFolderName}/main.py",
+			"interactiveTerminal": false
+		}
+	]
+}
diff --git a/Lokalisierung_LEGO/.vscode/settings.json b/Lokalisierung_LEGO/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..343aaf987bc5720f3b0327c5ea28d21f56cd6d94
--- /dev/null
+++ b/Lokalisierung_LEGO/.vscode/settings.json
@@ -0,0 +1,7 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+	"files.eol": "\n",
+	"debug.openDebug": "openOnSessionStart",
+	"python.linting.enabled": false,
+	"python.languageServer": "None"
+}
diff --git a/Lokalisierung_LEGO/main.py b/Lokalisierung_LEGO/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a730ee9e5193737e3d4c662b7d553e3ea21d1e9
--- /dev/null
+++ b/Lokalisierung_LEGO/main.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Tue May  9 12:16:54 2023
+
+@author: Jessica Dreyer, Sebastian Böttger
+
+Hi I'm Sir Ludwig. I'm a self driving robot.
+My job is it to follow a white line.
+I love my job!
+"""
+
+#%% imports
+import socket
+from datetime import datetime
+from ev3dev2.port import LegoPort
+from ev3dev2.sensor import INPUT_4, INPUT_1
+from ev3dev2.sensor.lego import ColorSensor
+from ev3dev2.motor import OUTPUT_A, OUTPUT_B, MoveTank, SpeedPercent
+from ev3dev2.sensor.lego import GyroSensor
+#import sys
+
+STUD_MM = 8
+use_gyro = 0 # 0 = ohne Gyrosensor, 1 = mit Gyrosensor
+
+#HOST = "10.128.41.108"
+#HOST = "10.128.41.90"
+#HOST = "192.168.178.81"
+HOST = "192.168.0.100"
+PORT = 50007
+
+#%% main programm
+
+print("Hello! My name is Sir Ludwig and I love to follow the line <3")
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.connect((HOST,PORT))
+
+tank_drive = MoveTank(OUTPUT_A, OUTPUT_B)
+max_speed = 170 / 60 # rps
+d = 120 # in mm
+radumfang = (3.141592 * 54) # in mm
+white_value = 54
+black_value = 13
+desired_light = (white_value + black_value) / 2
+t = 4.5 # in ms
+
+cs = ColorSensor(INPUT_4)
+
+gs = GyroSensor(INPUT_1)
+gs.mode = 'GYRO-RATE' # Rotationsgeschwindigkeit
+
+fehler = 0
+fehler_alt = 0
+s_r = 0
+s_r_alt = 0
+s_l = 0
+s_l_alt = 0
+
+counter_links = 0
+counter_rechts = 0
+counter_geradeaus = 0
+counter = 0
+w = 0
+v = 0
+
+time_last_send = datetime.utcnow()
+
+while (1):
+    sensorwert = cs.value()
+    fehler_alt = fehler
+    fehler = desired_light - sensorwert
+
+    ableitung = (fehler - fehler_alt) / t
+
+    turning = (0.4 * fehler) + (0.08 * ableitung)
+
+    right_motor = 30 - turning
+    left_motor = 30 + turning
+
+    tank_drive.on(SpeedPercent(left_motor), SpeedPercent(right_motor))
+    
+    right_speed =  tank_drive.right_motor.speed / 360
+    left_speed =  tank_drive.left_motor.speed / 360
+
+    v_r = right_speed * radumfang # in mm / s       
+    v_l = left_speed * radumfang # in mm / s        
+
+    v += (v_r + v_l) / 2
+
+    if (use_gyro == 0):
+        w += (v_r - v_l) / (2*d)
+    else:
+        w += (gs.value() / 180) * -3.141592
+
+    counter += 1
+    if(counter == 6):
+        w /= counter
+        v /= counter
+        v_str = str(format((round(v, 5)), '.5f')).zfill(9)
+        w_str = format((round(w, 5)), '.5f')
+        if (w >= 0):
+            w_str = "+" + w_str
+        time_delta = datetime.utcnow() - time_last_send
+        t_str = format((round(time_delta.total_seconds(), 6)), '.6f')
+        stringSend = "w=" + w_str + "v=" + v_str +"t=" + t_str
+        s.sendall(stringSend.encode())
+        time_last_send = datetime.utcnow()
+        counter = 0
+        w = 0
+        v = 0
\ No newline at end of file
diff --git a/README.md b/README.md
index 6ac5304aac302d7346fa0d75ff382cb7631d55ff..329a07f97b005f10f7fd59619938d209a17cd43a 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,16 @@
-# localization  VROB
+# Toolkit for locating a vehicle on the race track
 This project deals with the localization of a vehicle on a race track. 
 This is a student project from Bochum University of Applied Sciences.
 
-## Name
-localization of a vehicle on a race track
+The main approach of localization is based on locating the vehicle based on the current and already travelled section types.
+A section type can be a right turn, a left turn and straight.
 
 ## Robot
-The used Robot is the LEGO EV3 Brick.
+The used Robot is the LEGO EV3 Brick as a line follower to test the algorithm.
+
+The robot use MicroPython.
+How to install MicroPython and set up your Visual Studio Code to connect your pc to the robot is documented in this file:
+https://assets.education.lego.com/v3/assets/blt293eea581807678a/bltb470b9ea6e38f8d4/5f8802fc4376310c19e33714/getting-started-with-micropython-v2_enus.pdf?locale=de-de
 
 Important notes regarding the robot:
 * The robot has differential drive.
@@ -16,7 +20,7 @@ Important notes regarding the robot:
     * GyroSensor: INPUT 1
 
 ## Getting started
-How to run MicroPython programs on LEGO EV3: 
+### How to run MicroPython programs on LEGO EV3: 
 1. Download and flash the EV3 MicroPython image onto a micro SD card
 2. Insert the micro SD card into the SD card slot on the EV3 Brick
 3. Download, install, and launch Visual Studio Code on your computer
@@ -25,7 +29,7 @@ How to run MicroPython programs on LEGO EV3:
     * After installing the extension, a device can be connected in Visual Studio Code (at the bottom, left).
     * The LEGO EV3 Brick should be listed and can be selected in the list.
 
-The following Python-packages must be installed on the computer to run the algorithm:
+### The following Python-packages must be installed on the computer to run the algorithm:
 * numpy
 * pandas
 * matplotlib
@@ -33,6 +37,28 @@ The following Python-packages must be installed on the computer to run the algor
 * socket
 * datetime
 
+### Preparations before run
+Clone this git. Connect the EV3 robot to the same network as your executing device.
+
+The following adjustments might be necessary, before the algorithm is executable:
+* change ip-address in Lokalisierung_LEGO/main.py to the ip-address of the executing device
+* insert the sections of the race track in localization.py (get_set_of_section_*())
+	* structure of a section: [name, section_type, previous_sections_type]
+* if the track plotter is used, insert also a set of track parts in the file grafic (get_set_of_trackparts_*()).
+    * structure of a section: [URL, name, start position(x,y), start orientation)]
+    * Images of the race tracks are not included in this git. If interested, please contact the authors.
+
+### Run
+1. Connect the robot via Visual Studio Code.
+2. Start the python file race_management.py
+    * Now some windows should pop up.
+    * You can't move this windows until the UDP-connection with the robot is connected.
+    * __When you want to stop the script you have to restart the python kernel.__
+3. Start the main.py file on the robot.
+    * To do this, select the file in the Lego-EV window in the lower left corner of Visual Studio Code. Then right click and click on "Run"
+4. Now the robot should drive and send data to the pc which plots in the console the current detected section and plot stuff.
+5. To stop the robot/program press the left upper button on the robot, or close the dashboard window.
+
 ## Description
 The main branch contains the following files:
 * localization.py:      localization algorithm
@@ -41,10 +67,6 @@ The main branch contains the following files:
 * race_management.py:   main program
 * Lokalisierung_LEGO (folder), main.py: programming of the robot LEGO EV3
 
-The following adjustments might be necessary, before the algorithm is executable:
-* change ip-address in main.py
-* entering the route in localization.py (get_set_of_section_*())
-
 The following measured values are stored and must be adjusted if necessary (in main.py):
 * wheel circumference (radumfang)
 * wheel spacing (d)
@@ -59,12 +81,16 @@ Notes on constant values (in race_management.py):
 * S = 0, R = 1, L = 2: value for a straight part, a right turn, a left turn of the track
 **These values should not be changed in order to maintain the functionality of the algorithm.**
 
+## Code overview
+![Alt text](Klassendiagramm.png)
+
 ## Authors and acknowledgment
 Jessica Dreyer, Sebastian Böttger
 
 ## License
-Project is licensed with MIT.
-See at LICENSE
+This project is licensed under the MIT License - see the LICENSE file for details.
 
 ## Project status
-Work in progress.
+Project closed.
+
+Cordial invitation to further develop this project.
\ No newline at end of file
diff --git a/driving.py b/driving.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d952dc92fa70edcd7ee6528f48fb4c1904a2618
--- /dev/null
+++ b/driving.py
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Tue May  9 12:16:54 2023
+
+@author: Jessica Dreyer, Sebastian Böttger
+"""
+#%% Imports
+import numpy as np
+
+#%% Constans
+S = 0 # Value for a straight part of the track
+R = 1 # Value for a right turn
+L = 2 # Value for a left turn
+#%% Classes
+"""
+# This class contains some functions to analyze data from the LEGO robot EV3.
+# It detects the section type depending on the rotationspeed of the robot.
+# It calculates the odometrie for x-position, y-position and direction of the robot.
+# It calculates the driven distance on a section.
+"""
+class Driving_Analyser:
+    
+    """
+    # Constructor of the driving analyser.
+    # Needs threshold values of rotationspeed for left and right turn
+    # and a block size which represents the number of iterations to analyze the section type
+    """
+    def __init__(self, threshold_left, threshold_right, block_size):
+        self.threshold_left = threshold_left
+        self.threshold_right = threshold_right
+        self.block_size = block_size
+        self.counter_left = 0;
+        self.counter_right = 0;
+        self.counter_straight = 0;
+        self.x_old = 0
+        self.y_old = 0
+        self.theta_old = 0
+        self.driven_distance_on_section = 0
+    
+    """
+    # This function detects the section type.
+    # For the detection the given threshold for left and right turns 
+    # and the block size to sum up the values are used.
+    # The function decides on the basis of the threshold values
+    # whether the current rotation speed is a left or a right turn
+    # and sums up these types in the amount of the block size to make the decision for the section type.
+    # If the function is called and the sum of values is lower than the block size it returns None
+    # If the detection of the section is completed, the function will return an integer value.
+    # 0 # Value for a straight part of the track
+    # 1 # Value for a right turn
+    # 2 # Value for a left turn
+    # The entire return statemant is a tuple with the section type, the counte for left and right turns and a counter for straight
+    # return:
+    # section_type, counter_left, counter_right, counter_straight
+    """
+    def detect_section_type(self, w):
+        section_type = None
+        if (w >= self.threshold_left):      # left turn
+            self.counter_left += 1
+        elif (w <= self.threshold_right):   # right turn
+            self.counter_right += 1
+        else:                               # straight
+            self.counter_straight += 1
+        if ((self.counter_left + self.counter_right + self.counter_straight) == self.block_size):
+            if (self.counter_left > self.counter_right and self.counter_left > self.counter_straight):
+                section_type = L
+            elif (self.counter_right > self.counter_left and self.counter_right > self.counter_straight):
+                section_type = R
+            else:
+                section_type = S
+            self.counter_left = self.counter_right = self.counter_straight = 0
+            #print(self.__get_section_type(section_type))
+        return (section_type, self.counter_left, self.counter_right, self.counter_straight)
+    
+    """
+    # This function calcuates the continues odometrie which adds the new positon to the old one.
+    # The calculation of the position depends on the speed, rotationspeed and the time span.
+    """
+    def calculate_odometrie_continues (self, w, v, time_delta):
+        
+        if w != 0: #or v != 0:
+            theta = self.theta_old + (w * time_delta)
+            x = self.x_old + (v * ((np.sin(theta) - np.sin(self.theta_old)) / w))
+            y = self.y_old + (v * (-(np.cos(theta) - np.cos(self.theta_old)) / w))
+
+        else:
+            theta = self.theta_old
+            x = self.x_old + (v * np.cos(theta) * time_delta)
+            y = self.y_old + (v * np.sin(theta) * time_delta)
+
+
+        self.theta_old = theta
+        self.y_old = round(y, 5)
+        self.x_old = round(x, 5)
+        
+        return (self.x_old, self.y_old, self.theta_old)
+    
+    """
+    # This function calcuates the delta odometrie which returns only the delta position of the given time span.
+    # The calculation of the position depends on the speed, rotationspeed and the time span.
+    """
+    def calculate_odometrie_delta (self, w, v, time_delta):
+
+        if w != 0:
+            theta_delta = (w * time_delta)
+            x_delta = (v * (np.sin(theta_delta) / w))
+            y_delta = (v * (-(np.cos(theta_delta) - np.cos(0)) / w))
+
+        else:
+            theta_delta = 0
+            x_delta = v * (np.cos(0))
+            y_delta = v * (np.sin(0))
+        
+        return (x_delta, y_delta, theta_delta)
+    
+    """
+    # This function returns the drivien distance on the section.
+    """
+    def calculate_driven_distance_on_section (self, v,time_delta):
+        self.driven_distance_on_section += v*time_delta
+        return self.driven_distance_on_section
+    
+    """
+    # This function resets the drivien distance.
+    """
+    def reset_driven_distance_on_section (self):
+        self.driven_distance_on_section = 0
+    
+    """
+    # This function is for internal use and printing only.
+    # It converts the integer values for the section type to the correct discription of the section type.
+    """
+    def __get_section_type(self, section_type_int):
+        section_type_string = ""
+        if section_type_int == S:
+            section_type_string = "straight"
+        elif section_type_int == R:
+            section_type_string = "right"
+        elif section_type_int == L:
+            section_type_string = "left"
+        return section_type_string
\ No newline at end of file
diff --git a/grafic.py b/grafic.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ab2c7849d7d3f2b1287acfb3fe1503907325191
--- /dev/null
+++ b/grafic.py
@@ -0,0 +1,449 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Sat May  6 13:26:28 2023
+
+@author: Jessica Dreyer, Sebastian Böttger
+"""
+#%% imports
+import numpy as np
+import pandas as pd
+import matplotlib.pyplot as plt
+import matplotlib.patches as patches
+from matplotlib.patches import Circle
+import matplotlib.image as img
+import tkinter as tk
+
+# not importend imports
+import time
+import random
+
+
+#%% class
+"""
+# This class can plot values as a consistant graph.
+# With the method update you can insert a new value to the graph and the grafic will refresh automaticly.
+"""
+class Line_Plotter:
+    """
+    # The constructor needs a title, for the diagramm as well as a lable for the x and y axses.
+    # Optional you can set the number of values that are plottet on the x-axses.
+    """
+    def __init__(self, title, x_lable, y_lable ,x_scale = 100):
+        self.counter = 0
+        self.fig = plt.figure()
+        self.x = np.array(range(-x_scale + 1,1))
+        self.y = np.zeros(x_scale)
+        self.ax = self.fig.add_subplot(111)
+        self.line, = self.ax.plot(self.x, self.y)
+        self.ax.set_xlim(self.x[0],self.x[self.x.size -1])
+        self.ax.set_ylim(0, 10)
+        self.ax.set_title(title)
+        self.ax.set_xlabel(x_lable)
+        self.ax.set_ylabel(y_lable)
+        self.fig.canvas.draw()
+        self.fig.canvas.flush_events()
+        
+    """
+    # The method update insert a new value to the graph an refresh the graph automaticly.
+    # It needs only the value as a parameter.
+    """
+    def update(self, value):
+        self.y = np.append(self.y, value)
+        self.y = self.y[1:]
+        self.x = np.append(self.x, self.counter)
+        self.x = self.x[1:]
+        
+        self.line._x = self.x
+        self.line.set_ydata(self.y)
+        self.fig.canvas.draw()
+        self.fig.canvas.flush_events()
+        
+        self.ax.set_xlim(self.x[0],self.x[self.x.size -1])
+        self.ax.set_ylim(self.y.min(), self.y.max())
+        
+        self.counter = self.counter + 1
+    
+    """
+    # The method close ensures that the window is closed.
+    """
+    def close(self):
+        plt.close(self.fig)
+
+"""
+# This class can plot values as a continues graph.
+# It can plot the values with a given timedelta to the last known value.
+"""
+class Line_Plotter_Time:
+    """
+    # The constructor needs a title, for the diagramm as well as a lable for the x and y axses.
+    # Optional you can set the number of values that are plottet on the x-axses.
+    """
+    def __init__(self, title, x_lable, y_lable ,x_scale = 100):
+        self.x_scale = x_scale
+        self.fig = plt.figure()
+        self.x = np.array(range(0,1))
+        self.y = np.zeros(1)
+        self.ax = self.fig.add_subplot(111)
+        self.line, = self.ax.plot(self.x, self.y)
+        self.ax.set_xlim(0,10)
+        self.ax.set_ylim(0, 10)
+        self.ax.set_title(title)
+        self.ax.set_xlabel(x_lable)
+        self.ax.set_ylabel(y_lable)
+        
+    """
+    # The method update insert a new value to the graph on the timestamp delte which is given an refresh the graph automaticly.
+    # It needs the value and a given time delta to the last value as a parameter.
+    # The timedelta will be summed to the hight time of a datapoint in the digramm and will plot at this time the new value.
+    """
+    def update(self, value, time_delta):
+        self.y = np.append(self.y, value)
+        self.x = np.append(self.x, self.x[self.x.size -1] + time_delta)
+        if (self.x.size > self.x_scale):
+            self.y = self.y[1:]
+            self.x = self.x[1:]
+        
+        self.line._x = self.x
+        self.line.set_ydata(self.y)
+        self.fig.canvas.draw()
+        self.fig.canvas.flush_events()
+        
+        self.ax.set_xlim(self.x[0],self.x[self.x.size -1])
+        self.ax.set_ylim(self.y.min(), self.y.max())
+    
+    """
+    # The method close ensures that the window is closed.
+    """
+    def close(self):
+        plt.close(self.fig)
+
+"""
+# This class can plot the racetrack and highlighte the current section.
+# It should also display a little robot an update the position, but unfortunately, it's not function properly.
+"""
+class Track_Plotter:
+    """
+    # This constructor needs a set of trackparts which contains a URL to the backgroundimage for the plot.
+    """
+    def __init__(self, background_url, track, ratio = 1):
+        self.background = img.imread(background_url)
+        self.track = track
+        self.ratio = ratio
+        self.fig = plt.figure()
+        self.ax = self.fig.add_subplot(111)
+        self.live_image = self.ax.imshow(self.background, extent=[0,1000,750,0])
+        self.robot_is_active = False
+        self.robot_position = self.track.iloc[0].start_position
+        self.robot_direction = self.track.iloc[0].start_direction
+        self.ax.set_xlim(0,1000)
+        self.ax.set_ylim(750,0)
+        plt.show(block=False)
+        plt.pause(0.1)
+    
+    """
+    # Wich this funtion you get a set of trackparts. The URLs are not includet in this git.
+    """
+    def get_set_of_trackparts_3():
+        # Track plotting
+        trackparts = []
+        trackparts.append(['./Bilder/Strecke_03_S1.PNG', 'S1', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S2.PNG', 'S2', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S3.PNG', 'S3', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S4.PNG', 'S4', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S5.PNG', 'S5', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S6.PNG', 'S6', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S7.PNG', 'S7', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S8.PNG', 'S8', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S9.PNG', 'S9', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S10.PNG', 'S10', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S11.PNG', 'S11', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S12.PNG', 'S12', (0,0), 0])
+        trackparts.append(['./Bilder/Strecke_03_S13.PNG', 'S13', (0,0), 0])
+        
+        # Create DataFrame
+        track = pd.DataFrame(trackparts,columns=['image_url', 'name', 'start_position', 'start_direction'])
+        background = './Bilder/Strecke_03.png'
+        
+        return (background, track)
+    
+    """
+    # This method is internal used to create the robot. 
+    """
+    def __create_triangle(self, position, direction):
+        # Definiere die Position der Ecke des Dreiecks
+        x = position[0]
+        y = position[1]
+        
+        # Definiere die Länge der Kante, die in Richtung des Winkels theta zeigt
+        length = 100
+        
+        # Definiere den Winkel theta in Grad
+        theta_deg = direction
+        
+        # Berechne den Winkel theta in Bogenmaß
+        theta = np.deg2rad(theta_deg)
+        
+        # Berechne die Koordinaten des Dreiecks
+        x1 = x
+        y1 = y
+        
+        x2 = x + length * np.cos(theta -np.deg2rad(135))
+        y2 = y + length * np.sin(theta -np.deg2rad(135))
+        x3 = x + length * np.cos(theta -np.deg2rad(135) + np.pi/2)
+        y3 = y + length * np.sin(theta -np.deg2rad(135) + np.pi/2)
+        
+        
+        # Erstelle das Dreieck
+        self.__triangle = patches.Polygon([(x1, y1), (x2, y2), (x3, y3)], closed=True, fill=True, facecolor='green', alpha=0.5)
+        return self.__triangle
+    
+    """
+    # This function is used to select a section and set up that backgorundimage
+    """
+    def select_trackpart(self, trackpart_name):
+        trackpart = self.track[self.track.name == trackpart_name]
+        image = img.imread(trackpart.iloc[0].image_url)
+        self.live_image.set_data(image)
+        self.fig.canvas.draw()
+        self.fig.canvas.flush_events()
+    
+    """
+    # This funktion is used to set the robot to the startposition of a trackpart.
+    # Currently the robot simulation is not usable!
+    """
+    def set_robot_to_trackpart(self, trackpart_name):
+        trackpart = self.track[self.track.name == trackpart_name]
+        self.robot_position = trackpart.iloc[0].start_position
+        self.robot_direction = trackpart.iloc[0].start_direction
+        self.update_robot()
+    
+    """
+    # This funktion is used to move the robot forward.
+    # Currently the robot simulation is not usable!
+    """
+    def move_robot_forward(self, distance):
+        x,y = self.robot_position
+        theta = np.deg2rad(self.robot_direction)
+        x_new = x + distance*self.ratio * np.sin(theta)
+        y_new = y - distance*self.ratio * np.cos(theta)
+        self.robot_position = (x_new, y_new)
+        self.update_robot()
+    
+    """
+    # This funktion is used to set the robot to a specific position on the plot.
+    # Currently the robot simulation is not usable!
+    """
+    def move_robot_to_position(self, position):
+        self.robot_position += position
+        self.update_robot()
+    
+    """
+    # This funktion is used to rotate the robot.
+    # Currently the robot simulation is not usable!
+    """
+    def rotate_robot(self, rotation):
+        self.robot_direction += rotation
+        self.update_robot()
+    
+    """
+    # This funktion enable the plotting of the robot.
+    # Currently the robot simulation is not usable!
+    """
+    def activate_robot(self):
+        print("Do not use this method!!! - activate_robot")
+        self.robot_is_active = True
+        self.__circle = Circle(self.robot_position, 10, facecolor='green', edgecolor='green')
+        self.__circle = self.ax.add_artist(self.__circle)
+        self.__triangle = self.__create_triangle(self.robot_position, self.robot_direction)
+        self.__triangle = self.ax.add_artist(self.__triangle)
+        self.fig.canvas.draw_idle()
+        self.fig.canvas.flush_events()
+    
+    """
+    # This funktion is used to update the position and orientation of the robot on the plot.
+    # Currently the robot simulation is not usable!
+    """
+    def update_robot(self):
+        print("Do not use this method!!! - update_robot")
+        if self.robot_is_active:
+            try:
+                self.__circle.remove()
+                self.__triangle.remove()
+            except NotImplementedError:
+                pass
+            self.__triangle = self.triangle = self.__create_triangle(self.robot_position, self.robot_direction)
+            self.__triangle = self.ax.add_artist(self.__triangle)
+            self.__circle = Circle(self.robot_position, 15, facecolor='green', edgecolor='green')
+            self.__circle = self.ax.add_artist(self.__circle)
+            self.fig.canvas.draw_idle()
+            self.fig.canvas.flush_events()
+            
+            # self.ax.draw_artist(self.__triangle)
+            # self.ax.draw_artist(self.__circle)
+            # self.fig.canvas.blit(self.fig.bbox)
+            # self.ax.figure.canvas.flush_events()
+            
+            # try:
+            #     self.ax.draw_artist(self.__triangle)
+            #     self.ax.draw_artist(self.__circle)
+                
+            # except AttributeError:
+            #     pass
+            # self.__circle.set_center(self.robot_position)
+            # self.ax.draw_artist(self.__circle)
+            # self.fig.canvas.blit(self.fig.bbox)
+            # self.fig.canvas.flush_events()
+            
+    """
+    # This funktion close the window.
+    """
+    def close(self):
+        plt.close(self.fig)
+
+"""
+# This class creates a little Dashborad with the most important information from the robot and the localization.
+"""
+class Dashboard:
+    """
+    # This constructor needs nothing and set up all values for the dashboard.
+    """
+    def __init__(self):
+        self.current_section_type = ""
+        self.last_sections_type = ""
+        self.enter_new_section = ""
+        self.last_known_section = ""
+        self.count_fail_loc = 0
+        self.x_position = 0
+        self.y_position = 0
+        self.driven_distance_on_section = 0
+        
+        self.window = tk.Tk()
+        self.window.title("Dashboard")
+        
+        self.label_current_section_type = tk.Label(self.window, text="current section type:")
+        self.label_last_sections_type = tk.Label(self.window, text="last sections type:")
+        self.label_enter_new_section = tk.Label(self.window, text="enter new section:")
+        self.label_last_known_section = tk.Label(self.window, text="last known section:")
+        self.label_count_fail_loc = tk.Label(self.window, text="count fail loc:")
+        self.label_x_position = tk.Label(self.window, text="x position:")
+        self.label_y_position = tk.Label(self.window, text="y position:")
+        self.label_driven_distance_on_section = tk.Label(self.window, text="driven distance on section:")
+        
+        self.label_current_section_type.pack()
+        self.label_last_sections_type.pack()
+        self.label_enter_new_section.pack()
+        self.label_last_known_section.pack()
+        self.label_count_fail_loc.pack()
+        self.label_x_position.pack()
+        self.label_y_position.pack()
+        self.label_driven_distance_on_section.pack()
+        
+        self.window.protocol("WM_DELETE_WINDOW", print(""))
+        self.window.update_idletasks()
+        self.window.update()
+
+    """
+    # This function sets the current section type to the dashboard
+    """
+    def set_current_section_type(self, section_type):
+        self.current_section_type = section_type
+        self.label_current_section_type.config(text="current section type: " + self.current_section_type)
+        self.update_window()
+    
+    """
+    # This function sets the last sections type to the dashboard
+    """
+    def set_last_sections_type(self, last_sections_type):
+        self.last_sections_type = last_sections_type
+        self.label_last_sections_type.config(text="last sections type: " + self.last_sections_type)
+        self.update_window()
+    
+    """
+    # This function sets the value enter new section to the dashboard
+    """
+    def set_enter_new_section(self, enter_new_section):
+        self.enter_new_section = enter_new_section
+        self.label_enter_new_section.config(text="enter new section: " + self.enter_new_section)
+        self.update_window()
+    
+    """
+    # This function sets the last known section to the dashboard
+    """
+    def set_last_known_section(self, last_known_section):
+        self.last_known_section = last_known_section
+        self.label_last_known_section.config(text="last known section: " + self.last_known_section)
+        self.update_window()
+    
+    """
+    # This function sets the counter fail localization to the dashboard
+    """
+    def set_count_fail_loc(self, count_fail_loc):
+        self.count_fail_loc = count_fail_loc
+        self.label_count_fail_loc.config(text="count fail loc: " + str(self.count_fail_loc))
+        self.update_window()
+    
+    """
+    # This function sets the x position of the robot to the dashboard
+    """
+    def set_x_position(self, x_position):
+        self.x_position = x_position/1000
+        self.label_x_position.config(text="x position: " + str(self.x_position) + " m")
+        self.update_window()
+    
+    """
+    # This function sets the y position of the robot to the dashboard
+    """  
+    def set_y_position(self, y_position):
+        self.y_position = y_position/1000
+        self.label_y_position.config(text="y position: " + str(self.y_position) + " m")
+        self.update_window()
+    
+    """
+    # This function sets the driven distance on the section to the dashboard
+    """
+    def set_driven_distance_on_section(self, driven_distance_on_section):
+        self.driven_distance_on_section = driven_distance_on_section/1000
+        self.label_driven_distance_on_section.config(text="driven distance on section: " + str(self.driven_distance_on_section) + " m")
+        self.update_window()
+    
+    """
+    # This function update the dashboard it's self. without this method, the dashboard will not refresh.
+    """
+    def update_window(self):
+        self.window.update_idletasks()
+        self.window.update()
+
+#%% Main Skript
+
+if __name__ == '__main__':
+    # Line plotter without timing
+    #ln_plotter_1 = Line_Plotter("Linieargeschwindigkeit", "Zeitschritte", "mm/s")
+    #ln_plotter_2 = Line_Plotter("Winkelgeschwindigkeit", "Zeitschritte", "rad/s")
+    #for i in range(200):
+        #ln_plotter_1.update(random.random() * 5)
+        #ln_plotter_2.update(random.random() * 5)
+        #time.sleep(0.05)
+    
+    # Line plotter with timing
+    # ln_plotter_1 = Line_Plotter_Time("Linieargeschwindigkeit", "s", "mm/s")
+    # ln_plotter_2 = Line_Plotter_Time("Winkelgeschwindigkeit", "s", "rad/s")
+    # for i in range(200):
+    #     ln_plotter_1.update(random.random() * 5, 0.05)
+    #     ln_plotter_2.update(random.random() * 5, 0.05)
+    #     #time.sleep(0.05)
+    
+    # Track plotting
+    background, track = Track_Plotter.get_set_of_trackparts_3()
+    
+    tr_plotter = Track_Plotter(background, track)
+    #tr_plotter.activate_robot()
+    #tr_plotter.update_robot()
+    #tr_plotter.set_robot_to_trackpart('S1')
+    for i in range(3):
+        if i == 0:
+            tr_plotter.select_trackpart('S1')
+        if i ==  1:
+            tr_plotter.select_trackpart('S5')
+        if i == 2:
+            tr_plotter.select_trackpart('S9')
+        time.sleep(3)
\ No newline at end of file
diff --git a/localization.py b/localization.py
new file mode 100644
index 0000000000000000000000000000000000000000..c02cce718a24b8ffa0e94a30383c5efa3c2c6e24
--- /dev/null
+++ b/localization.py
@@ -0,0 +1,236 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Mon May  1 14:48:45 2023
+
+@author: Jessica Dreyer, Sebastian Böttger
+"""
+#%% imports
+import numpy as np
+import pandas as pd
+
+#%% constants
+S = 0 # Value for a straight part of the track
+R = 1 # Value for a right turn
+L = 2 # Value for a left turn
+
+
+#%% Classes
+"""
+# This class provides methods to localize a robot on a given race track.
+# The race track is given during the creation of the object and contains the following structure:
+    [name of the section, section type, array of the last section types]
+    The name of the section is a string and can be choosen individually.
+    The sectiontyp is an int and can be one of the following states:
+        0 = straight; 1 = right turn; 2 = left turn
+        These three values are saved in the const-variables S, R and L.
+    The array of the last section types is a simple array which contains a set of ints.
+        Each int stands for one of the three states S, R or L.
+"""
+class Localizer:
+    
+    """
+    # The constructor gets a set of sections and a boolean value for debugging.
+    """
+    def __init__(self, sections, debug = False):
+        self.sections = sections
+        self.current_section_type = None
+        self.last_sections_type = [None, None, None, None, None]
+        self.enter_new_section = False
+        self.last_known_section = None
+        self.count_fail_loc = 0
+        self.debug = debug
+    
+    """
+    # This function returns a set of sections
+    """
+    def get_set_of_section_1():
+        track = []
+        track.append(["S1", S, [L,S,L,S,L]])
+        track.append(["S2", L, [S,L,S,L,S]])
+        track.append(["S3", S, [L,S,L,S,L]])
+        track.append(["S4", L, [S,L,S,L,S]])
+        track.append(["S5", S, [L,S,L,S,L]])
+        track.append(["S6", R, [S,L,S,L,S]])
+        track.append(["S7", S, [R,S,L,S,L]])
+        track.append(["S8", L, [S,R,S,L,S]])
+        track.append(["S9", S, [L,S,R,S,L]])
+        track.append(["S10", R, [S,L,S,R,S]])
+        track.append(["S11", L, [R,S,L,S,R]])
+        track.append(["S12", S, [L,R,S,L,S]])
+        track.append(["S13", L, [S,L,R,S,L]])
+        track.append(["S14", S, [L,S,L,R,S]])
+        track.append(["S15", L, [S,L,S,L,R]])
+
+        # DataFrame erstellen
+        track = pd.DataFrame(track, columns=['s_name', 'type', 'previous'])
+        return track
+    
+    """
+    # This function returns a set of sections
+    """
+    def get_set_of_section_2():
+        track = []
+        track.append(["S1", S, [L,S,L,S,L]])
+        track.append(["S2", L, [S,L,S,L,S]])
+        track.append(["S3", S, [L,S,L,S,L]])
+        track.append(["S4", L, [S,L,S,L,S]])
+        track.append(["S5", S, [L,S,L,S,L]])
+        track.append(["S6", R, [S,L,S,L,S]])
+        track.append(["S7", S, [R,S,L,S,L]])
+        track.append(["S8", L, [S,R,S,L,S]])
+        track.append(["S9", S, [L,S,R,S,L]])
+        track.append(["S10", R, [S,L,S,R,S]])
+        track.append(["S11", S, [R,S,L,S,R]])
+        track.append(["S12", L, [S,R,S,L,S]])
+        track.append(["S13", S, [L,S,R,S,L]])
+        track.append(["S14", L, [S,L,S,R,S]])
+        track.append(["S15", S, [L,S,L,S,R]])
+        track.append(["S16", L, [S,L,S,L,S]])
+
+        # DataFrame erstellen
+        track = pd.DataFrame(track, columns=['s_name', 'type', 'previous'])
+        return track
+    
+    """
+    # This function returns a set of sections
+    """
+    def get_set_of_section_3():
+        track = []
+        track.append(["S1", S, [L,S,L,R,S]])
+        track.append(["S2", L, [S,L,S,L,R]])
+        track.append(["S3", S, [L,S,L,S,L]])
+        track.append(["S4", L, [S,L,S,L,S]])
+        track.append(["S5", S, [L,S,L,S,L]])
+        track.append(["S6", R, [S,L,S,L,S]])
+        track.append(["S7", S, [R,S,L,S,L]])
+        track.append(["S8", L, [S,R,S,L,S]])
+        track.append(["S9", S, [L,S,R,S,L]])
+        track.append(["S10", R, [S,L,S,R,S]])
+        track.append(["S11", L, [R,S,L,S,R]])
+        track.append(["S12", S, [L,R,S,L,S]])
+        track.append(["S13", L, [S,L,R,S,L]])
+
+        # DataFrame erstellen
+        track = pd.DataFrame(track, columns=['s_name', 'type', 'previous'])
+        return track
+    
+    """
+    # This function updates the object values depending on the section type which is provided.
+    If the section type is the same as before nothing changes.
+    If the section type has changed, all object variables are updated and the variable "enter_new_section" is set to true.
+    """
+    def update_position(self, section_type):
+        if self.current_section_type is not section_type:
+            self.enter_new_section = True
+            self.last_sections_type = self.last_sections_type[:4]
+            self.last_sections_type.insert(0, self.current_section_type)
+            self.current_section_type = section_type
+        else:
+            self.enter_new_section = False
+    
+    """
+    # This function calculates the section where the robot is located with the highest probability
+    # The calculation is based on a given set of sections, the last five recognized sections and the current recognized section.
+    """
+    def localize(self):
+        if(self.debug):
+            print("Current: ", self.__get_section_type(self.current_section_type), " Last: ", self.last_sections_type, " Last know section:" , self.last_known_section)
+        
+        # get all sections which have the same sectiontype as the current sectiontype
+        possible_sections = self.sections[self.sections.type == self.current_section_type]
+        previous_count = 0
+        
+        # iterate the this sections and compare the previous sectiontyps of the possible section with the last know sectiontypes
+        while(len(possible_sections) > 1 and previous_count < len(self.last_sections_type)):
+            # brock up if the last known sectiontype is not filled
+            if(self.last_sections_type[previous_count] == None):
+                previous_count = 5
+            else:
+                previous_types = pd.DataFrame(possible_sections.previous.array)
+                possible_sections = possible_sections[(previous_types[previous_count] == self.last_sections_type[previous_count]).array]
+                previous_count += 1
+        # check if more than one section is possible
+        if(len(possible_sections) > 1):
+            if(self.debug):
+                print("More than one part of the track is possible for localization.")
+            if (self.last_known_section is not None):
+                
+                # Calculate distance from last known_section to all possible sections
+                index_last_known_section = self.sections[self.sections.s_name == self.last_known_section.s_name].index[0]
+                if(self.debug):
+                    print("Index of Last Section: ", index_last_known_section)
+                
+                help_sections = self.sections.copy()
+                help_sections.index += self.sections.shape[0]
+                help_sections = self.sections.copy().append(help_sections)
+                
+                filtert_section = pd.DataFrame()
+                for possible_section_s_name in np.array(possible_sections.s_name):
+                    filtert_section = filtert_section.append(help_sections[help_sections.s_name == possible_section_s_name])
+                
+                
+                indexs = abs(filtert_section.index - index_last_known_section)
+                if(self.debug):
+                    print("Distance from indexs: ", indexs)
+                
+                # Get index of entry with the smallest distance
+                min_index = np.argmin(indexs)
+                
+                # Check if there is more than one entry with the same distance like min_index
+                all_min_indices = np.where(indexs == indexs[min_index])[0]
+                
+                # If there is more than on index, localization failed
+                if(len(all_min_indices) > 1):
+                    min_index = all_min_indices[1]
+                    if(self.debug):
+                        print("No localization possible")
+                if(self.debug):
+                    print(possible_sections)
+                s_name_possible_section = filtert_section.iloc[min_index].s_name
+                possible_sections = possible_sections[possible_sections.s_name == s_name_possible_section]
+                if(self.debug):
+                    print ("Am nahliegenste Section:", possible_sections)
+            else:
+                if(self.debug):
+                    print("No localization possible - More than one part of the track can fit")
+                    print(possible_sections)
+                
+        elif(len(possible_sections) < 1):
+            self.count_fail_loc += 1
+            if(self.last_known_section is not None and self.last_sections_type[4] is not None):
+                self.current_section_type = self.last_known_section.type
+                self.last_sections_type = self.last_known_section.previous
+            if(self.debug):
+                print("No possible section was found. No localization possible.")
+            if (self.count_fail_loc == 3):
+                self.last_sections_type = [None, None, None, None, None]
+                self.last_known_section = None
+                self.count_fail_loc = 0
+        
+        if(len(possible_sections) == 1):
+            if(self.debug):
+                print(possible_sections)
+            self.last_known_section = possible_sections.iloc[0]
+            self.count_fail_loc = 0
+        
+        return possible_sections
+    
+    """
+    # This is an intern function which translates the int values to a string.
+    It is only used for debugging or printing to the consol.
+    """
+    def __get_section_type(self, section_type_int):
+        section_type_string = ""
+        if section_type_int == S:
+            section_type_string = "straight"
+        elif section_type_int == R:
+            section_type_string = "right"
+        elif section_type_int == L:
+            section_type_string = "left"
+        return section_type_string
+#%% Main
+if __name__ == '__main__':
+    lk = Localizer(Localizer.get_set_of_section_1())
+    lk.update_position(L)
+    lk.update_position(R)
+    print(lk.localize())
\ No newline at end of file
diff --git a/race_management.py b/race_management.py
new file mode 100644
index 0000000000000000000000000000000000000000..2db72bbb9816b9685e81b800c73a39e28b4bc6b1
--- /dev/null
+++ b/race_management.py
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Tue May  2 15:01:44 2023
+
+@author: Jessica Dreyer, Sebastian Böttger
+"""
+#%% imports
+import socket
+from datetime import datetime
+from localization import Localizer
+from driving import Driving_Analyser
+from grafic import Line_Plotter, Line_Plotter_Time, Track_Plotter, Dashboard
+
+#%% constant values
+HOST = ''                   # Symbolic name meaning all available interfaces
+PORT = 50007                # Arbitrary non-privileged port
+CHAR_RECIVING = 31          # Number of chars one UDP-message contains
+THRESHOLD_LEFT = 0.05       # Value to detect a left turn of the robot
+THRESHOLD_RIGHT = -0.05     # Value to detect a right turn of the robot
+BLOCK_SIZE_DA = 10          # Number of iterations to analyze the section type
+S = 0 # Value for a straight part of the track
+R = 1 # Value for a right turn
+L = 2 # Value for a left turn
+
+#%% Functions
+def main_loop():
+    print("TO EXIT THE PROGRAMM, CLOSE THE DASHBOARDWINDOW!!!")
+    try:
+        # create objektes
+        driving_analyser = Driving_Analyser(THRESHOLD_LEFT, THRESHOLD_RIGHT, BLOCK_SIZE_DA)
+        localizer = Localizer(Localizer.get_set_of_section_3(), False)
+        
+        # line plotter with time
+        lp_v = Line_Plotter_Time("Linear speed", "s", "mm/s", 1000)
+        lp_w = Line_Plotter_Time("Rotation speed", "s", "rad/s", 1000)
+        #lp_section_type = Line_Plotter_Time("Filtered rotation speed", "s", "current section type", 1000)
+        
+        # trackplotter
+        background, track = Track_Plotter.get_set_of_trackparts_3()
+        tp = Track_Plotter(background, track)
+        
+        # dashboard
+        dashboard = Dashboard()
+        dashboard.update_window()
+        
+        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+            s.bind((HOST, PORT))
+            s.listen(1)
+            conn, addr = s.accept()
+            with conn:
+                print('Connected by', addr)
+                while True:
+                    dashboard.update_window()
+                    # reciving data
+                    data = conn.recv(CHAR_RECIVING)
+                    
+                    # check if data countions something
+                    if len(data) > 0:
+                        
+                        # pars data to v w and t
+                        v,w,t = pars_data(data)
+                        
+                        # update odometrie
+                        driving_analyser.calculate_odometrie_continues(w, v, t)
+                        driving_analyser.calculate_driven_distance_on_section(v, t)
+                        
+                        # update line plotter
+                        lp_v.update(v,t)
+                        lp_w.update(w,t)
+                        #lp_section_type.update(localizer.current_section_type if localizer.current_section_type is not None else 0, t)
+                        
+                        # get section type
+                        section_type,_,_,_ = driving_analyser.detect_section_type(w)
+                        
+                        # if section type is None driving_analyser still count to decide which type it is
+                        if(section_type is not None):
+                            #print(get_section_type_formated(section_type))
+                            # update localizer position
+                            localizer.update_position(section_type)
+                            update_dashboard(dashboard, localizer, driving_analyser)
+                            
+                            if(localizer.enter_new_section):
+                                driving_analyser.reset_driven_distance_on_section()
+                                section = localizer.localize()
+                                #print(section)
+                                if len(section) == 1:
+                                    print(section.iloc[0].s_name)
+                                    tp.select_trackpart(section.iloc[0].s_name)
+
+    except Exception as e:
+        print("An error occured")
+        print(e)
+    finally:
+        try:
+            dashboard.window.destroy()
+        except Exception:
+            pass 
+        lp_w.close()
+        lp_v.close()
+        #lp_section_type.close();
+        tp.close()
+        
+
+def pars_data(data):
+    data = data.decode("utf-8")
+    w = float(data[3:10])
+    if data[2] == "-":
+        w *= -1
+    v = float(data[12:21])
+    t = float(data[23:])
+    return (v,w,t)
+
+def get_section_type_formated(section_type_int):
+    section_type_string = ""
+    if section_type_int == S:
+        section_type_string = "straight"
+    elif section_type_int == R:
+        section_type_string = "right"
+    elif section_type_int == L:
+        section_type_string = "left"
+    return section_type_string
+
+def get_last_sections_type_formated(last_sections_type_int):
+    last_sections_type = ""
+    for section_type in last_sections_type_int:
+        last_sections_type += get_section_type_formated(section_type) + ', '
+    return last_sections_type
+
+def update_dashboard(dashboard, localizer, driving_analyser):
+    dashboard.set_current_section_type(get_section_type_formated(localizer.current_section_type))
+    dashboard.set_last_sections_type(get_last_sections_type_formated(localizer.last_sections_type))
+    dashboard.set_enter_new_section("Yes" if localizer.enter_new_section else "No")
+    dashboard.set_last_known_section("None" if localizer.last_known_section is None else localizer.last_known_section.s_name)
+    dashboard.set_count_fail_loc(localizer.count_fail_loc)
+    dashboard.set_x_position(driving_analyser.x_old)
+    dashboard.set_y_position(driving_analyser.y_old)
+    dashboard.set_driven_distance_on_section(driving_analyser.driven_distance_on_section)
+    #dashboard.update_window()
+
+#%% main script for race controle
+if __name__ == '__main__':
+    main_loop()
\ No newline at end of file