hi there
telkl mehow to visualise this code in airsim simulator
from pymavlink import mavutil
import math
import time



class Drone:

    def __init__(self, system_id, connection):  # Updated to use double underscores
        self.system_id = system_id
        self.connection = connection

    def set_mode(self, mode):
        self.connection.mav.set_mode_send(
            self.system_id,
            mavutil.mavlink.MAV_MODE_FLAG_CUSTOM_MODE_ENABLED,
            mode
        )

    def arm(self, arm=True):
        self.connection.mav.command_long_send(self.system_id, self.connection.target_component,
                                              mavutil.mavlink.MAV_CMD_COMPONENT_ARM_DISARM, 0, int(arm), 0, 0, 0, 0, 0,
                                              0)

    def takeoff(self, altitude):
        self.connection.mav.command_long_send(self.system_id, self.connection.target_component,
                                              mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, 0, 0, 0, 0, 0, 0, 0, altitude)

    def send_waypoint(self, wp):
        self.connection.mav.send(mavutil.mavlink.MAVLink_set_position_target_global_int_message(
            10,
            self.system_id,
            self.connection.target_component,
            mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
            int(0b110111111000),
            int(wp[0] * 10 ** 7),
            int(wp[1] * 10 ** 7),
            wp[2],
            0, 
            0, 
            0, 
            0, 
            0, 
            0, 
            0
        ))

    def get_position(self):
        self.connection.mav.request_data_stream_send(
            self.system_id, self.connection.target_component,
            mavutil.mavlink.MAV_DATA_STREAM_POSITION, 1, 1)

        while True:
            msg = self.connection.recv_match(type='GLOBAL_POSITION_INT', blocking=True)
            if msg.get_srcSystem() == self.system_id:
                return (msg.lat / 10 ** 7, msg.lon / 10 ** 7, msg.alt / 10 ** 3)



class PIDController:

    def __init__(self, kp, ki, kd, limit):  # Updated to use double underscores
        self.kp = kp
        self.ki = ki
        self.kd = kd
        self.limit = limit
        self.prev_error = 0
        self.integral = 0

    def update(self, error, dt):
        derivative = (error - self.prev_error) / dt
        self.integral += error * dt
        self.integral = max(min(self.integral, self.limit), -self.limit)  # Clamp the integral term
        output = self.kp * error + self.ki * self.integral + self.kd * derivative
        self.prev_error = error
        return output

distance = 5  # Distance in meters
angle = 60  # Angle in degrees
kp = 0.1
ki = 0.01
kd = 0.05
pid_limit = 0.0001
# Moved pid_lat and pid_lon initialization here with correct arguments
pid_lat = PIDController(kp, ki, kd, pid_limit)
pid_lon = PIDController(kp, ki, kd, pid_limit)

def calculate_follower_coordinates(wp, distance, angle):
    earth_radius = 6371000.0  # in meters
    latitude_change = (180 * distance * math.cos(math.radians(angle))) / (math.pi * earth_radius)
    longitude_change = (180 * distance * math.sin(math.radians(angle))) / (
            math.pi * earth_radius * math.cos(math.radians(wp[0])))
    new_latitude = wp[0] + latitude_change
    new_longitude = wp[1] + longitude_change
    return (new_latitude, new_longitude, wp[2])


waypoints = [
    (28.5861474, 77.3421320, 10),
    (28.5859040, 77.3420736, 10)
]

the_connection = mavutil.mavlink_connection('/dev/ttyUSB0', baud=57600)

master_drone = Drone(3, the_connection)
follower_drone = Drone(2, the_connection)

# Set mode to Guided and arm both drones
for drone in [master_drone, follower_drone]:
    drone.set_mode(4)
    drone.arm()
    drone.takeoff(10)


def abort():
    # Updated this function to check for user input and break the infinite loop
    print("Type 'abort' to return to Launch and disarm motors.")
    start_time = time.monotonic()
    while time.monotonic() - start_time < 7:
        user_input = input("Time left: {} seconds \n".format(int(7 - (time.monotonic() - start_time))))

        if user_input.lower() == "abort":
            print("Returning to Launch and disarming motors…")
            for drone in [master_drone, follower_drone]:
                drone.set_mode(6)  # RTL mode
                drone.arm(False)  # Disarm motors
            return True
    return False


mode = the_connection.mode_mapping()[the_connection.flightmode]
# mode = 4
print(f"{mode}")


time_start = time.time()
while mode == 4:
    # Abort drones if the abort command is given
    if abort():
        exit()

    if time.time() - time_start >= 1:  # Check if one second passed
        for master_wp in waypoints:
            master_drone.send_waypoint(master_wp)
            follower_position = master_drone.get_position()
            if follower_position is None:  # Check if position is None (timeout exceeded)
                break
            follower_wp = calculate_follower_coordinates(follower_position, distance, angle)

            # Call PID controller update for latitude and longitude
            dt = time.time() - time_start
            pid_lat_output = pid_lat.update(follower_wp[0] - follower_position[0], dt)
            pid_lon_output = pid_lon.update(follower_wp[1] - follower_position[1], dt)

            # Adjust waypoint position using PID controller output
            adjusted_follower_wp = (
                follower_wp[0] + pid_lat_output, follower_wp[1] + pid_lon_output, follower_wp[2])

            follower_drone.send_waypoint(adjusted_follower_wp)
        else:
            mode_mapping = the_connection.mode_mapping()
            current_mode = the_connection.flightmode
            mode = mode_mapping[current_mode]
            time_start = time.time()
            time.sleep(0.1)  # Reduce sleep time to avoid too much delay
            continue
        break  # Break out of the loop if position is None (timeout exceeded)

    # Set mode to RTL and disarm both drones
    for drone in [master_drone, follower_drone]:
        drone.set_mode(6)
        drone.arm(False)



the_connection.close()