Example #1
0
def calculate_final_results(result: FlightResult,
                            task: Task,
                            tp: FlightPointer,
                            lead_coeff: LeadCoeff,
                            airspace: AirspaceCheck = None,
                            deadline: int = None,
                            print=print):
    """"""
    '''final results'''
    print("100|% complete")

    if tp.start_done:
        evaluate_start(result, task, tp)
        '''ESS Time'''
        if tp.ess_done:
            evaluate_ess(result, task)

            if tp.made_all:
                evaluate_goal(result, task)

    if result.result_type != 'goal':
        print(f"Pilot landed after {result.distance_flown / 1000:.2f}km")

    result.best_waypoint_achieved = str(
        result.waypoints_achieved[-1].name
    ) if result.waypoints_achieved else None

    if lead_coeff:
        result.fixed_LC = lead_coeff.summing

    if task.airspace_check:
        infringements, notifications, penalty = airspace.get_infringements_result(
            result.infringements)
        result.infringements = infringements
        result.notifications.extend(notifications)
Example #2
0
def process_igc(task_id: int, par_id: int, tracklog):
    from pilot.track import create_igc_filename, igc_parsing_config_from_yaml
    from calcUtils import epoch_to_date
    from airspace import AirspaceCheck
    from igc_lib import Flight
    from task import Task
    from pilot.flightresult import FlightResult, save_track

    pilot = FlightResult.read(par_id, task_id)
    if pilot.name:
        task = Task.read(task_id)
        fullname = create_igc_filename(task.file_path, task.date, pilot.name)
        tracklog.save(fullname)
        pilot.track_file = Path(fullname).name
    else:
        return None, None
    """import track"""
    # track = Track(track_file=fullname, par_id=pilot.par_id)
    FlightParsingConfig = igc_parsing_config_from_yaml(task.igc_config_file)
    flight = Flight.create_from_file(fullname,
                                     config_class=FlightParsingConfig)
    """check result"""
    if not flight:
        error = f"for {pilot.name} - Track is not a valid track file"
        return None, error
    elif not epoch_to_date(flight.date_timestamp) == task.date:
        error = f"for {pilot.name} - Track has a different date from task date"
        return None, error
    else:
        print(
            f"pilot {pilot.par_id} associated with track {pilot.track_file} \n"
        )
        """checking track against task"""
        if task.airspace_check:
            airspace = AirspaceCheck.from_task(task)
        else:
            airspace = None
        pilot.check_flight(flight, task, airspace_obj=airspace)
        print(f"track verified with task {task.task_id}\n")
        '''create map file'''
        pilot.save_tracklog_map_file(task, flight)
        """adding track to db"""
        # pilot.to_db()
        save_track(pilot, task.id)
        time = ''
        data = {'par_id': pilot.par_id, 'track_id': pilot.track_id}
        if pilot.goal_time:
            time = sec_to_time(pilot.ss_time)
        if pilot.result_type == 'goal':
            data['Result'] = f'Goal {time}'
        elif pilot.result_type == 'lo':
            data['Result'] = f"LO {round(pilot.distance / 1000, 2)}"
        if pilot.track_id:  # if there is a track, make the result a link to the map
            # trackid = data['track_id']
            parid = data['par_id']
            result = data['Result']
            data[
                'Result'] = f'<a href="/map/{parid}-{task.task_id}">{result}</a>'
    return data, None
Example #3
0
def create_tracklog_map_result_file(par_id: int, task_id: int):
    from pilot import flightresult
    from task import Task
    from igc_lib import Flight
    from airspace import AirspaceCheck
    task = Task.read(task_id)
    airspace = None if not task.airspace_check else AirspaceCheck.from_task(task)
    pilot = flightresult.FlightResult.read(par_id, task_id)
    file = Path(task.file_path, pilot.track_file)
    '''load track file'''
    flight = Flight.create_from_file(file)
    pilot.check_flight(flight, task, airspace)
    pilot.save_tracklog_map_file(task, flight)
Example #4
0
 def read(task_id):
     task = Task.read(task_id)
     if task.turnpoints:
         if not task.distance:
             task.calculate_task_length()
         if not task.optimised_turnpoints:
             task.calculate_optimised_task_length()
     test = task.date < datetime.today().date()
     task.get_pilots()
     airspace = AirspaceCheck.from_task(task)
     livetrack = LiveTracking(task, airspace, test)
     livetrack.create_result()
     return livetrack
Example #5
0
def check_livetrack(result: FlightResult,
                    task: Task,
                    airspace: AirspaceCheck = None):
    """ Checks a Flight object against the task.
        Args:
               result:   a FlightResult object
               task:     a Task object
               airspace: a AirspaceCheck object
        Returns:
                a list of GNSSFixes of when turnpoints were achieved.
    """
    from flightcheck.flightpointer import FlightPointer
    from flightcheck import flightcheck
    from formulas.libs.leadcoeff import LeadCoeff
    '''initialize'''
    fixes = result.livetrack
    '''Turnpoint managing'''
    tp = FlightPointer(task)
    tp.pointer = result.waypoints_made + 1
    '''leadout coefficient'''
    if task.formula.formula_departure == 'leadout':
        lead_coeff = LeadCoeff(task)
        # print(f"{result.name} - cycle init Lead Coeff: {lead_coeff.summing}, fixed LC: {result.fixed_LC}")
        if tp.start_done:
            lead_coeff.best_dist_to_ess = [
                lead_coeff.opt_dist_to_ess - result.distance_flown / 1000
            ]
    else:
        lead_coeff = None
    '''Airspace check managing'''
    if task.airspace_check:
        if task.airspace_check and not airspace:
            print(f'We should not create airspace here')
            airspace = AirspaceCheck.from_task(task)
    real_start_time, already_ess, previous_achieved = result.real_start_time, result.ESS_time, result.waypoints_achieved
    flightcheck.check_fixes(result,
                            fixes,
                            task,
                            tp,
                            lead_coeff,
                            airspace,
                            livetracking=True,
                            igc_parsing_config=config)

    calculate_incremental_results(result, task, tp, lead_coeff, airspace,
                                  real_start_time, already_ess,
                                  previous_achieved)
Example #6
0
def verify_and_import_track(result: FlightResult, track, task, print=print):
    from airspace import AirspaceCheck
    from pilot.flightresult import save_track

    if task.airspace_check:
        airspace = AirspaceCheck.from_task(task)
    else:
        airspace = None
    '''check flight against task'''
    result.check_flight(track.flight, task, airspace_obj=airspace, print=print)
    '''create map file'''
    result.save_tracklog_map_file(task, track.flight)
    '''save to database'''
    save_track(result, task.id)
    if result.notifications:
        print(str(result.notifications))
    print('***************END****************')
    return result
Example #7
0
 def read(task_id):
     task = Task.read(task_id)
     if task.turnpoints:
         if not task.distance:
             task.calculate_task_length()
         if not task.optimised_turnpoints:
             task.calculate_optimised_task_length()
     test = task.date < datetime.today().date()
     task.get_pilots()
     airspace = AirspaceCheck.from_task(task)
     livetrack = LiveTracking(task, airspace, test)
     for p in livetrack.pilots:
         create_igc_file(p, task)
         p.result_type = 'lo'
         # p.saved_to_db = False
         p.suspect_landing_fix = None
     livetrack.create_result()
     return livetrack
Example #8
0
    def check_flight(self,
                     flight,
                     task,
                     airspace_obj=None,
                     deadline=None,
                     print=print):
        """ Checks a Flight object against the task.
            Args:
                   :param flight: a Flight object
                   :param task: a Task
                   :param airspace_obj: airspace object to check flight against
                   :param deadline: in multiple start or elapsed time, I need to check again track using Min_flight_time
                                as deadline
                   :param print: function to overide print() function. defaults to print() i.e. no override. Intended for
                                 sending progress to front end
            Returns:
                    a list of GNSSFixes of when turnpoints were achieved.
        """
        from flightcheck.flightpointer import FlightPointer
        from flightcheck.flightcheck import check_fixes, calculate_final_results
        '''initialize'''
        if not self.result_type == 'nyp':
            self.reset()
        self.result_type = 'lo'

        if not task.optimised_turnpoints:
            # this should not happen
            task.calculate_optimised_task_length()
        ''' Altitude Source: '''
        alt_source = 'GPS' if task.formula.scoring_altitude is None else task.formula.scoring_altitude
        alt_compensation = 0 if alt_source == 'GPS' or task.QNH == 1013.25 else task.alt_compensation
        '''leadout coefficient'''
        if task.formula.formula_departure == 'leadout':
            lead_coeff = LeadCoeff(task)
        else:
            lead_coeff = None
        '''flight origin'''
        self.first_time = flight.fixes[0].rawtime if not hasattr(
            flight, 'takeoff_fix') else flight.takeoff_fix.rawtime
        '''flight end'''
        self.landing_time = flight.landing_fix.rawtime
        self.landing_altitude = (flight.landing_fix.gnss_alt if alt_source
                                 == 'GPS' else flight.landing_fix.press_alt +
                                 alt_compensation)
        '''Turnpoint managing'''
        tp = FlightPointer(task)
        '''Airspace check managing'''
        if task.airspace_check:
            if not airspace_obj and not deadline:
                print(f'We should not create airspace here')
                airspace_obj = AirspaceCheck.from_task(task)

        check_fixes(self,
                    flight.fixes,
                    task,
                    tp,
                    lead_coeff,
                    airspace_obj,
                    deadline=deadline,
                    print=print)

        calculate_final_results(self,
                                task,
                                tp,
                                lead_coeff,
                                airspace_obj,
                                deadline=deadline,
                                print=print)
Example #9
0
def check_fixes(result: FlightResult,
                fixes: list,
                task: Task,
                tp: FlightPointer,
                lead_coeff: LeadCoeff = None,
                airspace: AirspaceCheck = None,
                livetracking: bool = False,
                igc_parsing_config: FlightParsingConfig = None,
                deadline: int = None,
                print=print):
    """"""
    '''initialize'''
    total_fixes = len(fixes)
    tolerance = task.formula.tolerance or 0
    min_tol_m = task.formula.min_tolerance or 0
    max_jump_the_gun = task.formula.max_JTG or 0  # seconds
    jtg_penalty_per_sec = 0 if max_jump_the_gun == 0 else task.formula.JTG_penalty_per_sec
    distances2go = task.distances_to_go  # Total task Opt. Distance, in legs list
    ''' Altitude Source: '''
    alt_source = 'GPS' if task.formula.scoring_altitude is None else task.formula.scoring_altitude
    alt_compensation = 0 if alt_source == 'GPS' or task.QNH == 1013.25 else task.alt_compensation
    '''Stopped task managing'''
    if task.stopped_time:
        if not deadline:
            '''Using stop_time (stopped_time - score_back_time)'''
            deadline = task.stop_time
        goal_altitude = task.goal_altitude or 0
        glide_ratio = task.formula.glide_bonus or 0
        stopped_distance = 0
        stopped_altitude = 0
        total_distance = 0

    if not livetracking:
        percentage_complete = 0
    else:
        '''get if pilot already started in previous track slices'''
        already_started = tp.start_done
        '''get if pilot already made ESS in previous track slices'''
        already_ESS = any(e.name == 'ESS' for e in result.waypoints_achieved)

    for i in range(total_fixes - 1):
        # report percentage progress
        if not livetracking and int(
                i / len(fixes) * 100) > percentage_complete:
            percentage_complete = int(i / len(fixes) * 100)
            print(f"{percentage_complete}|% complete")
        '''Get two consecutive trackpoints as needed to use FAI / CIVL rules logic
        '''
        # start_time = tt.time()
        my_fix = fixes[i]
        next_fix = fixes[i + 1]
        if livetracking:
            alt = next_fix.alt
            '''check coherence'''
            if next_fix.rawtime - my_fix.rawtime < 1:
                continue
            alt_rate = abs(next_fix.alt - my_fix.alt) / (next_fix.rawtime -
                                                         my_fix.rawtime)
            if (alt_rate > igc_parsing_config.max_alt_change_rate
                    or not igc_parsing_config.min_alt < alt <
                    igc_parsing_config.max_alt):
                continue
            '''check flying'''
            speed = next_fix.speed  # km/h
            if not result.first_time:
                '''not launched yet'''
                launch = next(x for x in tp.turnpoints if x.type == 'launch')
                if (abs(launch.altitude - alt) >
                        igc_parsing_config.min_alt_difference
                        and speed > igc_parsing_config.min_gsp_flight):
                    '''pilot launched'''
                    result.first_time = next_fix.rawtime
                    result.live_comment = 'flying'
                else:
                    '''still on launch'''
                    continue

            else:
                '''check if pilot landed'''
                if speed < igc_parsing_config.min_gsp_flight:
                    if not result.suspect_landing_fix:
                        result.suspect_landing_fix = next_fix
                        # suspect_landing_alt = alt
                    else:
                        time_diff = next_fix.rawtime - result.suspect_landing_fix.rawtime
                        alt_diff = abs(alt - result.suspect_landing_fix.alt)
                        if (time_diff > igc_parsing_config.max_still_seconds
                                and alt_diff <
                                igc_parsing_config.min_alt_difference):
                            '''assuming pilot landed'''
                            result.landing_time = next_fix.rawtime
                            result.landing_altitude = alt
                            result.live_comment = 'landed'
                            break
                elif result.suspect_landing_fix is not None:
                    result.suspect_landing_fix = None
        else:
            alt = next_fix.gnss_alt if alt_source == 'GPS' else next_fix.press_alt + alt_compensation

            if next_fix.rawtime < result.first_time:
                '''skip'''
                continue
            if result.landing_time and next_fix.rawtime > result.landing_time:
                '''pilot landed out'''
                # print(f'fix {i}: landed out - {next_fix.rawtime} - {alt}')
                break
        '''max altitude'''
        if alt > result.max_altitude:
            result.max_altitude = alt
        '''handle stopped task
        Pilots who were at a position between ESS and goal at the task stop time will be scored for their 
        complete flight, including the portion flown after the task stop time. 
        This is to remove any discontinuity between pilots just before goal and pilots who had just reached goal 
        at task stop time.
        '''
        if task.stopped_time and next_fix.rawtime > deadline and not tp.ess_done:
            result.still_flying_at_deadline = True
            result.stopped_distance = stopped_distance
            result.stopped_altitude = stopped_altitude
            result.total_distance = total_distance
            break
        '''check if task deadline has passed'''
        if task.task_deadline < next_fix.rawtime:
            # Task has ended
            result.still_flying_at_deadline = True
            break
        '''check if pilot has arrived in goal (last turnpoint) so we can stop.'''
        if tp.made_all:
            break
        '''check if start closing time passed and pilot did not start'''
        if task.start_close_time and task.start_close_time < my_fix.rawtime and not tp.start_done:
            # start closed
            break
        '''check tp type is known'''
        if tp.next.type not in ('launch', 'speed', 'waypoint', 'endspeed',
                                'goal'):
            assert False, f"Unknown turnpoint type: {tp.type}"
        '''check window is open'''
        if task.window_open_time > next_fix.rawtime:
            continue
        '''launch turnpoint managing'''
        if tp.type == "launch":
            if task.check_launch == 'on':
                # Set radius to check to 200m (in the task def it will be 0)
                # could set this in the DB or even formula if needed..???
                tp.next.radius = 200  # meters
                if tp.next.in_radius(my_fix, tolerance, min_tol_m):
                    result.waypoints_achieved.append(
                        create_waypoint_achieved(my_fix, tp, my_fix.rawtime,
                                                 alt))
                    tp.move_to_next()
            else:
                tp.move_to_next()

        # to do check for restarts for elapsed time tasks and those that allow jump the gun
        # if started and task.task_type != 'race' or result.jump_the_gun is not None:
        '''start turnpoint managing'''
        '''given all n crossings for a turnpoint cylinder, sorted in ascending order by their crossing time,
        the time when the cylinder was reached is determined.
        turnpoint[i] = SSS : reachingTime[i] = crossing[n].time
        turnpoint[i] =? SSS : reachingTime[i] = crossing[0].time

        We need to check start in 3 cases:
        - pilot has not started yet
        - race has multiple starts
        - task is elapsed time
        '''
        if pilot_can_start(task, tp, my_fix):
            # print(f'time: {my_fix.rawtime}, start: {task.start_time} | Interval: {task.SS_interval} | my start: {result.real_start_time} | better_start: {pilot_get_better_start(task, my_fix.rawtime, result.SSS_time)} | can start: {pilot_can_start(task, tp, my_fix)} can restart: {pilot_can_restart(task, tp, my_fix, result)} | tp: {tp.name}')
            if start_made_civl(my_fix, next_fix, tp.next, tolerance,
                               min_tol_m):
                time = int(round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                result.waypoints_achieved.append(
                    create_waypoint_achieved(my_fix, tp, time,
                                             alt))  # pilot has started
                result.real_start_time = time
                if not livetracking:
                    print(
                        f"Pilot started SS at {sec_to_time(result.real_start_time)}"
                    )
                result.best_distance_time = time
                tp.move_to_next()

        elif pilot_can_restart(task, tp, my_fix, result):
            # print(f'time: {my_fix.rawtime}, start: {task.start_time} | Interval: {task.SS_interval} | my start: {result.real_start_time} | better_start: {pilot_get_better_start(task, my_fix.rawtime, result.SSS_time)} | can start: {pilot_can_start(task, tp, my_fix)} can restart: {pilot_can_restart(task, tp, my_fix, result)} | tp: {tp.name}')
            if start_made_civl(my_fix, next_fix, tp.last_made, tolerance,
                               min_tol_m):
                tp.pointer -= 1
                time = int(round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                result.waypoints_achieved.pop()
                result.waypoints_achieved.append(
                    create_waypoint_achieved(my_fix, tp, time,
                                             alt))  # pilot has started again
                result.real_start_time = time
                result.best_distance_time = time
                if not livetracking:
                    print(
                        f"Pilot restarted SS at {sec_to_time(result.real_start_time)}"
                    )
                if lead_coeff:
                    lead_coeff.reset()
                tp.move_to_next()

        if tp.start_done:
            '''Turnpoint managing'''
            if (tp.next.shape == 'circle'
                    and tp.next.type in ('endspeed', 'waypoint')):
                if tp_made_civl(my_fix, next_fix, tp.next, tolerance,
                                min_tol_m):
                    time = int(
                        round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                    result.waypoints_achieved.append(
                        create_waypoint_achieved(
                            my_fix, tp, time,
                            alt))  # pilot has achieved turnpoint
                    if not livetracking:
                        print(
                            f"Pilot took {tp.name} at {sec_to_time(time)} at {alt}m"
                        )
                    tp.move_to_next()

            if tp.ess_done and tp.type == 'goal':
                if ((tp.next.shape == 'circle' and tp_made_civl(
                        my_fix, next_fix, tp.next, tolerance, min_tol_m))
                        or (tp.next.shape == 'line' and
                            (in_goal_sector(task, next_fix)))):
                    result.waypoints_achieved.append(
                        create_waypoint_achieved(
                            next_fix, tp, next_fix.rawtime,
                            alt))  # pilot has achieved goal
                    result.best_distance_time = next_fix.rawtime
                    if not livetracking:
                        print(f"Goal at {sec_to_time(next_fix.rawtime)}")
                    tp.done()
                    break
        '''update result data
        Once launched, distance flown should be max result among:
        - previous value;
        - optimized dist. to last turnpoint made;
        - total optimized distance minus opt. distance from next wpt to goal minus dist. to next wpt;
        '''
        if tp.pointer > 0:
            if tp.start_done and not tp.ess_done:
                '''optimized distance calculation each fix'''
                fix_dist_flown = task.opt_dist - get_shortest_path(
                    task, next_fix, tp.pointer)
                # print(f'time: {next_fix.rawtime} | fix: {tp.name} | Optimized Distance used')
            else:
                '''simplified and faster distance calculation'''
                fix_dist_flown = distance_flown(next_fix, tp.pointer,
                                                task.optimised_turnpoints,
                                                task.turnpoints[tp.pointer],
                                                distances2go)
                # print(f'time: {next_fix.rawtime} | fix: {tp.name} | Simplified Distance used')

            if fix_dist_flown > result.distance_flown:
                '''time of trackpoint with shortest distance to ESS'''
                result.best_distance_time = next_fix.rawtime
                '''updating best distance flown'''
                result.distance_flown = fix_dist_flown
            '''stopped task
            ∀p : p ∈ PilotsLandedBeforeGoal :
                bestDistance p = max(minimumDistance, 
                                     taskDistance − min(∀trackp.pointi : shortestDistanceToGoal(trackp.pointi )−(trackp .pointi .altitude−GoalAltitude)*GlideRatio)) 
            ∀p :p ∈ PilotsReachedGoal : bestDistance p = taskDistance
            '''
            if task.stopped_time and glide_ratio and total_distance < task.opt_dist:
                alt_over_goal = max(0, alt - goal_altitude)
                if fix_dist_flown + glide_ratio * alt_over_goal > total_distance:
                    '''calculate total distance with glide bonus'''
                    stopped_distance = fix_dist_flown
                    stopped_altitude = alt
                    total_distance = min(
                        fix_dist_flown + glide_ratio * alt_over_goal,
                        task.opt_dist)
        '''Leading coefficient
        LC = taskTime(i)*(bestDistToESS(i-1)^2 - bestDistToESS(i)^2 )
        i : i ? TrackPoints In SS'''
        if lead_coeff and tp.start_done and not tp.ess_done:
            lead_coeff.update(result, my_fix, next_fix)
        '''Airspace Check'''
        if task.airspace_check and airspace:
            # map_fix = [next_fix.rawtime, next_fix.lat, next_fix.lon, alt]
            plot, penalty = airspace.check_fix(next_fix, alt)
            if plot:
                # map_fix.extend(plot)
                '''Airspace Infringement: check if we already have a worse one'''
                airspace_name = plot[2]
                infringement_type = plot[3]
                dist = plot[4]
                separation = plot[5]
                result.infringements.append([
                    next_fix, alt, airspace_name, infringement_type, dist,
                    penalty, separation
                ])
                # print([next_fix, alt, airspace_name, infringement_type, dist, penalty])

    result.last_altitude = 0 if 'alt' not in locals() else alt
    result.last_time = 0 if 'next_fix' not in locals() else next_fix.rawtime
    if livetracking:
        result.height = (0 if not result.first_time or result.landing_time
                         or 'next_fix' not in locals() else next_fix.height)
Example #10
0
    def check_flight(self, flight, task, airspace_obj=None, deadline=None, print=print):
        """ Checks a Flight object against the task.
            Args:
                   :param flight: a Flight object
                   :param task: a Task
                   :param airspace_obj: airspace object to check flight against
                   :param deadline: in multiple start or elapsed time, I need to check again track using Min_flight_time
                                as deadline
                   :param print: function to overide print() function. defaults to print() i.e. no override. Intended for
                                 sending progress to front end
            Returns:
                    a list of GNSSFixes of when turnpoints were achieved.
        """
        from .flightpointer import FlightPointer

        ''' Altitude Source: '''
        alt_source = 'GPS' if task.formula.scoring_altitude is None else task.formula.scoring_altitude
        alt_compensation = 0 if alt_source == 'GPS' or task.QNH == 1013.25 else task.alt_compensation

        '''initialize'''
        if not self.result_type == 'nyp':
            self.reset()
        self.result_type = 'lo'
        tolerance = task.formula.tolerance or 0
        min_tol_m = task.formula.min_tolerance or 0
        max_jump_the_gun = task.formula.max_JTG or 0  # seconds
        jtg_penalty_per_sec = 0 if max_jump_the_gun == 0 else task.formula.JTG_penalty_per_sec
        max_altitude = 0
        percentage_complete = 0

        if not task.optimised_turnpoints:
            # this should not happen
            task.calculate_optimised_task_length()
        distances2go = task.distances_to_go  # Total task Opt. Distance, in legs list

        '''leadout coefficient'''
        if task.formula.formula_departure == 'leadout':
            lead_coeff = LeadCoeff(task)
        else:
            lead_coeff = None

        '''flight origin'''
        self.first_time = flight.fixes[0].rawtime if not hasattr(flight, 'takeoff_fix') else flight.takeoff_fix.rawtime
        '''flight end'''
        self.landing_time = flight.landing_fix.rawtime
        self.landing_altitude = (flight.landing_fix.gnss_alt if alt_source == 'GPS'
                                 else flight.landing_fix.press_alt + alt_compensation)

        '''Stopped task managing'''
        if task.stopped_time:
            if not deadline:
                '''Using stop_time (stopped_time - score_back_time)'''
                deadline = task.stop_time
            goal_altitude = task.goal_altitude or 0
            glide_ratio = task.formula.glide_bonus or 0
            stopped_distance = 0
            stopped_altitude = 0
            total_distance = 0

        '''Turnpoint managing'''
        tp = FlightPointer(task)

        '''Airspace check managing'''
        airspace_plot = []
        infringements_list = []
        airspace_penalty = 0
        if task.airspace_check:
            if not airspace_obj and not deadline:
                print(f'We should not create airspace here')
                airspace_obj = AirspaceCheck.from_task(task)
        total_fixes = len(flight.fixes)
        for i in range(total_fixes - 1):
            # report percentage progress
            if int(i / len(flight.fixes) * 100) > percentage_complete:
                percentage_complete = int(i / len(flight.fixes) * 100)
                print(f"{percentage_complete}|% complete")

            '''Get two consecutive trackpoints as needed to use FAI / CIVL rules logic
            '''
            # start_time = tt.time()
            my_fix = flight.fixes[i]
            next_fix = flight.fixes[i + 1]
            alt = next_fix.gnss_alt if alt_source == 'GPS' else next_fix.press_alt + alt_compensation

            if alt > max_altitude:
                max_altitude = alt

            '''pilot flying'''
            if next_fix.rawtime < self.first_time:
                continue
            if self.landing_time and next_fix.rawtime > self.landing_time:
                '''pilot landed out'''
                # print(f'fix {i}: landed out - {next_fix.rawtime} - {alt}')
                break

            '''handle stopped task
            Pilots who were at a position between ESS and goal at the task stop time will be scored for their 
            complete flight, including the portion flown after the task stop time. 
            This is to remove any discontinuity between pilots just before goal and pilots who had just reached goal 
            at task stop time.
            '''
            if task.stopped_time and next_fix.rawtime > deadline and not tp.ess_done:
                self.still_flying_at_deadline = True
                break

            '''check if task deadline has passed'''
            if task.task_deadline < next_fix.rawtime:
                # Task has ended
                self.still_flying_at_deadline = True
                break

            '''check if pilot has arrived in goal (last turnpoint) so we can stop.'''
            if tp.made_all:
                break

            '''check if start closing time passed and pilot did not start'''
            if task.start_close_time and task.start_close_time < my_fix.rawtime and not tp.start_done:
                # start closed
                break

            '''check tp type is known'''
            if tp.next.type not in ('launch', 'speed', 'waypoint', 'endspeed', 'goal'):
                assert False, f"Unknown turnpoint type: {tp.type}"

            '''check window is open'''
            if task.window_open_time > next_fix.rawtime:
                continue

            '''launch turnpoint managing'''
            if tp.type == "launch":
                if task.check_launch == 'on':
                    # Set radius to check to 200m (in the task def it will be 0)
                    # could set this in the DB or even formula if needed..???
                    tp.next.radius = 200  # meters
                    if tp.next.in_radius(my_fix, tolerance, min_tol_m):
                        self.waypoints_achieved.append(create_waypoint_achieved(my_fix, tp, my_fix.rawtime, alt))
                        tp.move_to_next()
                else:
                    tp.move_to_next()

            # to do check for restarts for elapsed time tasks and those that allow jump the gun
            # if started and task.task_type != 'race' or result.jump_the_gun is not None:

            '''start turnpoint managing'''
            '''given all n crossings for a turnpoint cylinder, sorted in ascending order by their crossing time,
            the time when the cylinder was reached is determined.
            turnpoint[i] = SSS : reachingTime[i] = crossing[n].time
            turnpoint[i] =? SSS : reachingTime[i] = crossing[0].time

            We need to check start in 3 cases:
            - pilot has not started yet
            - race has multiple starts
            - task is elapsed time
            '''
            if pilot_can_start(task, tp, my_fix):
                # print(f'time: {my_fix.rawtime}, start: {task.start_time} | Interval: {task.SS_interval} | my start: {self.real_start_time} | better_start: {pilot_get_better_start(task, my_fix.rawtime, self.SSS_time)} | can start: {pilot_can_start(task, tp, my_fix)} can restart: {pilot_can_restart(task, tp, my_fix, self)} | tp: {tp.name}')
                if start_made_civl(my_fix, next_fix, tp.next, tolerance, min_tol_m):
                    time = int(round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                    self.waypoints_achieved.append(
                        create_waypoint_achieved(my_fix, tp, time, alt))  # pilot has started
                    self.real_start_time = time
                    print(f"Pilot started SS at {sec_to_time(self.real_start_time)}")
                    self.best_distance_time = time
                    tp.move_to_next()

            elif pilot_can_restart(task, tp, my_fix, self):
                # print(f'time: {my_fix.rawtime}, start: {task.start_time} | Interval: {task.SS_interval} | my start: {self.real_start_time} | better_start: {pilot_get_better_start(task, my_fix.rawtime, self.SSS_time)} | can start: {pilot_can_start(task, tp, my_fix)} can restart: {pilot_can_restart(task, tp, my_fix, self)} | tp: {tp.name}')
                if start_made_civl(my_fix, next_fix, tp.last_made, tolerance, min_tol_m):
                    tp.pointer -= 1
                    time = int(round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                    self.waypoints_achieved.pop()
                    self.waypoints_achieved.append(
                        create_waypoint_achieved(my_fix, tp, time, alt))  # pilot has started again
                    self.real_start_time = time
                    self.best_distance_time = time
                    print(f"Pilot restarted SS at {sec_to_time(self.real_start_time)}")
                    if lead_coeff:
                        lead_coeff.reset()
                    tp.move_to_next()

            if tp.start_done:
                '''Turnpoint managing'''
                if (tp.next.shape == 'circle'
                        and tp.next.type in ('endspeed', 'waypoint')):
                    if tp_made_civl(my_fix, next_fix, tp.next, tolerance, min_tol_m):
                        time = int(round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                        self.waypoints_achieved.append(
                            create_waypoint_achieved(my_fix, tp, time, alt))  # pilot has achieved turnpoint
                        print(f"Pilot took {tp.name} at {sec_to_time(time)} at {alt}m")
                        tp.move_to_next()

                if tp.ess_done and tp.type == 'goal':
                    if ((tp.next.shape == 'circle' and tp_made_civl(my_fix, next_fix, tp.next, tolerance, min_tol_m))
                            or
                            (tp.next.shape == 'line' and (in_goal_sector(task, next_fix)))):
                        self.waypoints_achieved.append(
                            create_waypoint_achieved(next_fix, tp, next_fix.rawtime, alt))  # pilot has achieved goal
                        self.best_distance_time = next_fix.rawtime
                        print(f"Goal at {sec_to_time(next_fix.rawtime)}")
                        break

            '''update result data
            Once launched, distance flown should be max result among:
            - previous value;
            - optimized dist. to last turnpoint made;
            - total optimized distance minus opt. distance from next wpt to goal minus dist. to next wpt;
            '''
            if tp.pointer > 0:
                if tp.start_done and not tp.ess_done:
                    '''optimized distance calculation each fix'''
                    fix_dist_flown = task.opt_dist - get_shortest_path(task, next_fix, tp.pointer)
                    # print(f'time: {next_fix.rawtime} | fix: {tp.name} | Optimized Distance used')
                else:
                    '''simplified and faster distance calculation'''
                    fix_dist_flown = distance_flown(next_fix, tp.pointer, task.optimised_turnpoints,
                                                    task.turnpoints[tp.pointer], distances2go)
                    # print(f'time: {next_fix.rawtime} | fix: {tp.name} | Simplified Distance used')

                if fix_dist_flown > self.distance_flown:
                    '''time of trackpoint with shortest distance to ESS'''
                    self.best_distance_time = next_fix.rawtime
                    '''updating best distance flown'''
                    # self.distance_flown = max(fix_dist_flown,
                    #                             task.partial_distance[tp.last_made_index])  # old approach
                    self.distance_flown = fix_dist_flown

                '''stopped task
                ∀p : p ∈ PilotsLandedBeforeGoal :
                    bestDistance p = max(minimumDistance, 
                                         taskDistance − min(∀trackp.pointi : shortestDistanceToGoal(trackp.pointi )−(trackp .pointi .altitude−GoalAltitude)*GlideRatio)) 
                ∀p :p ∈ PilotsReachedGoal : bestDistance p = taskDistance
                '''
                if task.stopped_time and glide_ratio and total_distance < task.opt_dist:
                    alt_over_goal = max(0, alt - goal_altitude)
                    if fix_dist_flown + glide_ratio * alt_over_goal > total_distance:
                        '''calculate total distance with glide bonus'''
                        stopped_distance = fix_dist_flown
                        stopped_altitude = alt
                        total_distance = min(fix_dist_flown + glide_ratio * alt_over_goal, task.opt_dist)

            '''Leading coefficient
            LC = taskTime(i)*(bestDistToESS(i-1)^2 - bestDistToESS(i)^2 )
            i : i ? TrackPoints In SS'''
            if lead_coeff and tp.start_done and not tp.ess_done:
                lead_coeff.update(self, my_fix, next_fix)

            '''Airspace Check'''
            if task.airspace_check and airspace_obj:
                # map_fix = [next_fix.rawtime, next_fix.lat, next_fix.lon, alt]
                plot, penalty = airspace_obj.check_fix(next_fix, alt)
                if plot:
                    # map_fix.extend(plot)
                    '''Airspace Infringement: check if we already have a worse one'''
                    airspace_name = plot[2]
                    infringement_type = plot[3]
                    dist = plot[4]
                    separation = plot[5]
                    infringements_list.append([next_fix, alt, airspace_name, infringement_type,
                                               dist, penalty, separation])
                    # print([next_fix, alt, airspace_name, infringement_type, dist, penalty])
                else:
                    ''''''
                    # map_fix.extend([None, None, None, None, None])
                # airspace_plot.append(map_fix)

        '''final results'''
        print("100|% complete")
        self.max_altitude = max_altitude
        self.last_altitude = 0 if 'alt' not in locals() else alt
        self.last_time = 0 if 'next_fix' not in locals() else next_fix.rawtime

        '''manage stopped tasks'''
        if task.stopped_time and self.still_flying_at_deadline:
            self.stopped_distance = stopped_distance
            self.stopped_altitude = stopped_altitude
            self.total_distance = total_distance

        if tp.start_done:
            '''
            start time
            if race, the first times
            if multistart, the first time of the last gate pilot made
            if elapsed time, the time of last fix on start
            SS Time: the gate time'''
            self.SSS_time = task.start_time

            if task.task_type == 'RACE' and task.SS_interval:
                self.SSS_time += max(0, (start_number_at_time(task, self.real_start_time) - 1) * task.SS_interval)

            elif task.task_type == 'ELAPSED TIME':
                self.SSS_time = self.real_start_time

            '''manage jump the gun'''
            # print(f'wayponts made: {self.waypoints_achieved}')
            if max_jump_the_gun > 0 and self.real_start_time < self.SSS_time:
                diff = self.SSS_time - self.real_start_time
                penalty = diff * jtg_penalty_per_sec
                # check
                print(f'jump the gun: {diff} - valid: {diff <= max_jump_the_gun} - penalty: {penalty}')
                comment = f"Jump the gun: {diff} seconds. Penalty: {penalty} points"
                self.notifications.append(Notification(notification_type='jtg', flat_penalty=penalty, comment=comment))

            '''ESS Time'''
            if any(e.name == 'ESS' for e in self.waypoints_achieved):
                # self.ESS_time, ess_altitude = min([e[1] for e in self.waypoints_achieved if e[0] == 'ESS'])
                self.ESS_time, self.ESS_altitude = min([(x.rawtime, x.altitude) for x in self.waypoints_achieved
                                                        if x.name == 'ESS'], key=lambda t: t[0])
                self.speed = (task.SS_distance / 1000) / (self.ss_time / 3600)

                '''Distance flown'''
                ''' ?p:p?PilotsLandingBeforeGoal:bestDistancep = max(minimumDistance, taskDistance-min(?trackp.pointi shortestDistanceToGoal(trackp.pointi)))
                    ?p:p?PilotsReachingGoal:bestDistancep = taskDistance
                '''
                if any(e.name == 'Goal' for e in self.waypoints_achieved):
                    # self.distance_flown = distances2go[0]
                    self.distance_flown = task.opt_dist
                    self.goal_time, self.goal_altitude = min([(x.rawtime, x.altitude)
                                                              for x in self.waypoints_achieved
                                                              if x.name == 'Goal'], key=lambda t: t[0])
                    self.result_type = 'goal'
        if self.result_type != 'goal':
            print(f"Pilot landed after {self.distance_flown / 1000:.2f}km")

        self.best_waypoint_achieved = str(self.waypoints_achieved[-1].name) if self.waypoints_achieved else None

        if lead_coeff:
            self.fixed_LC = lead_coeff.summing

        if task.airspace_check:
            infringements, notifications, penalty = airspace_obj.get_infringements_result(infringements_list)
            self.infringements = infringements
            self.notifications.extend(notifications)
Example #11
0
def process_igc_background(task_id: int, par_id: int, file: Path, user: str):
    from pilot.track import igc_parsing_config_from_yaml
    from calcUtils import epoch_to_date
    from pilot.flightresult import FlightResult, save_track
    from airspace import AirspaceCheck
    from igc_lib import Flight
    from task import Task
    import json
    pilot = FlightResult.read(par_id, task_id)
    task = Task.read(task_id)
    print = partial(print_to_sse, id=par_id, channel=user)
    """import track"""
    # pilot.track = Track(track_file=filename, par_id=pilot.par_id)
    FlightParsingConfig = igc_parsing_config_from_yaml(task.igc_config_file)
    flight = Flight.create_from_file(file, config_class=FlightParsingConfig)
    data = {
        'par_id': pilot.par_id,
        'track_id': pilot.track_id,
        'Result': 'Not Yet Processed'
    }
    """check result"""
    if not flight:
        print(f"for {pilot.name} - Track is not a valid track file")
        print(json.dumps(data) + '|result')
        return None
    if not flight.valid:
        print(
            f'IGC does not meet quality standard set by igc parsing config. Notes:{pilot.flight.notes}'
        )
        print(json.dumps(data) + '|result')
        return None
    elif not epoch_to_date(flight.date_timestamp) == task.date:
        print(f"for {pilot.name} - Track has a different date from task date")
        print(json.dumps(data) + '|result')
        return None
    else:
        print(f"pilot {pilot.par_id} associated with track {file.name} \n")
        pilot.track_file = file.name
        """checking track against task"""
        if task.airspace_check:
            airspace = AirspaceCheck.from_task(task)
        else:
            airspace = None
        pilot.check_flight(flight, task, airspace_obj=airspace, print=print)
        print(f"track verified with task {task.task_id}\n")
        '''create map file'''
        pilot.save_tracklog_map_file(task, flight)
        """adding track to db"""
        # pilot.to_db()
        save_track(pilot, task.id)
        data['track_id'] = pilot.track_id
        time = ''

        if pilot.goal_time:
            time = sec_to_time(pilot.ESS_time - pilot.SSS_time)
        if pilot.result_type == 'goal':
            data['Result'] = f'Goal {time}'
        elif pilot.result_type == 'lo':
            data['Result'] = f"LO {round(pilot.distance / 1000, 2)}"
        if pilot.track_id:  # if there is a track, make the result a link to the map
            # trackid = data['track_id']
            parid = data['par_id']
            result = data['Result']
            data[
                'Result'] = f'<a href="/map/{parid}-{task.task_id}">{result}</a>'
        print(data['Result'])
        print(json.dumps(data) + '|result')
        print('***************END****************')
    return None
Example #12
0
def check_livetrack(pilot, task, airspace_obj=None):
    """ Checks a Flight object against the task.
        Args:
               pilot:  a Pilot object
               task:    a Task object
               airspace_obj: a AirspaceCheck object
        Returns:
                a list of GNSSFixes of when turnpoints were achieved.
    """
    from pilot.flightresult import Tp, pilot_can_start, pilot_can_restart, start_number_at_time
    from route import in_semicircle, start_made_civl, tp_made_civl, distance, \
        tp_time_civl, get_shortest_path, distance_flown
    from airspace import AirspaceCheck
    from formulas.libs.leadcoeff import LeadCoeff
    from pilot.notification import Notification
    '''initialize'''
    tolerance = task.formula.tolerance or 0
    min_tol_m = task.formula.min_tolerance or 0
    max_jump_the_gun = task.formula.max_JTG or 0  # seconds
    jtg_penalty_per_sec = 0 if max_jump_the_gun == 0 else task.formula.JTG_penalty_per_sec

    # if not task.optimised_turnpoints:
    #     task.calculate_optimised_task_length()
    distances2go = task.distances_to_go  # Total task Opt. Distance, in legs list

    result = pilot.result
    fixes = pilot.livetrack
    '''leadout coefficient'''
    if task.formula.formula_departure == 'leadout':
        lead_coeff = LeadCoeff(task)
        lead_coeff.summing = result.fixed_LC or 0.0
    else:
        lead_coeff = None
    '''Turnpoint managing'''
    tp = Tp(task)
    tp.pointer = result.waypoints_made + 1
    '''get if pilot already started in previous track slices'''
    already_started = tp.start_done
    restarted = False
    '''get if pilot already made ESS in previous track slices'''
    already_ESS = any(e[0] == 'ESS' for e in result.waypoints_achieved)
    '''Airspace check managing'''
    infringements_list = []
    if task.airspace_check:
        if task.airspace_check and not airspace_obj:
            print(f'We should not create airspace here')
            airspace_obj = AirspaceCheck.from_task(task)

    alt = 0
    suspect_landing_fix = None
    next_fix = None

    for i in range(len(fixes) - 1):
        '''Get two consecutive trackpoints as needed to use FAI / CIVL rules logic
        '''
        # start_time = tt.time()
        my_fix = fixes[i]
        next_fix = fixes[i + 1]
        result.last_time = next_fix.rawtime
        alt = next_fix.alt
        '''check coherence'''
        if next_fix.rawtime - my_fix.rawtime < 1:
            continue
        alt_rate = abs(next_fix.alt - my_fix.alt) / (next_fix.rawtime -
                                                     my_fix.rawtime)
        if alt_rate > max_alt_rate or not (min_alt < alt < max_alt):
            continue
        '''check flying'''
        speed = next_fix.speed  # km/h
        if not result.first_time:
            '''not launched yet'''
            launch = next(x for x in tp.turnpoints if x.type == 'launch')
            if distance(next_fix, launch) < 400:
                '''still on launch'''
                continue
            if abs(launch.altitude -
                   alt) > min_alt_difference and speed > min_flight_speed:
                '''pilot launched'''
                result.first_time = next_fix.rawtime
                result.live_comment = 'flying'
        else:
            '''check if pilot landed'''
            if speed < min_flight_speed:
                if not suspect_landing_fix:
                    suspect_landing_fix = next_fix
                    # suspect_landing_alt = alt
                else:
                    time_diff = next_fix.rawtime - suspect_landing_fix.rawtime
                    alt_diff = abs(alt - suspect_landing_fix.alt)
                    if time_diff > max_still_seconds and alt_diff < min_alt_difference:
                        '''assuming pilot landed'''
                        result.landing_time = next_fix.rawtime
                        result.landing_altitude = alt
                        result.live_comment = 'landed'
                        break
            elif suspect_landing_fix is not None:
                suspect_landing_fix = None

        # if alt > result.max_altitude:
        #     result.max_altitude = alt

        # '''handle stopped task'''
        # if task.stopped_time and next_fix.rawtime > deadline:
        #     result.last_altitude = alt  # check the rules on this point..which alt to
        #     break
        '''check if pilot has arrived in goal (last turnpoint) so we can stop.'''
        if tp.made_all:
            break
        '''check if task deadline has passed'''
        if task.task_deadline < next_fix.rawtime:
            # Task has ended
            result.live_comment = 'flying past deadline'
            break
        '''check if start closing time passed and pilot did not start'''
        if task.start_close_time and task.start_close_time < my_fix.rawtime and not tp.start_done:
            # start closed
            result.live_comment = 'did not start before start closing time'
            break

        if result.landing_time and next_fix.rawtime > result.landing_time:
            '''pilot already landed'''
            # this should not happen as landed pilot are filtered
            break
        '''check tp type is known'''
        if tp.next.type not in ('launch', 'speed', 'waypoint', 'endspeed',
                                'goal'):
            assert False, f"Unknown turnpoint type: {tp.type}"
        '''check window is open'''
        if task.window_open_time > next_fix.rawtime:
            continue
        '''start turnpoint managing'''
        '''given all n crossings for a turnpoint cylinder, sorted in ascending order by their crossing time,
        the time when the cylinder was reached is determined.
        turnpoint[i] = SSS : reachingTime[i] = crossing[n].time
        turnpoint[i] =? SSS : reachingTime[i] = crossing[0].time

        We need to check start in 3 cases:
        - pilot has not started yet
        - race has multiple starts
        - task is elapsed time
        '''
        if pilot_can_start(task, tp, my_fix):
            # print(f'time: {my_fix.rawtime}, start: {task.start_time} | Interval: {task.SS_interval} | my start: {result.real_start_time} | better_start: {pilot_get_better_start(task, my_fix.rawtime, result.SSS_time)} | can start: {pilot_can_start(task, tp, my_fix)} can restart: {pilot_can_restart(task, tp, my_fix, result)} | tp: {tp.name}')
            if start_made_civl(my_fix, next_fix, tp.next, tolerance,
                               min_tol_m):
                t = int(round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                result.waypoints_achieved.append([tp.name, t,
                                                  alt])  # pilot has started
                result.real_start_time = t
                tp.move_to_next()

        elif pilot_can_restart(task, tp, my_fix, result):
            # print(f'time: {my_fix.rawtime}, start: {task.start_time} | Interval: {task.SS_interval} | my start: {result.real_start_time} | better_start: {pilot_get_better_start(task, my_fix.rawtime, result.SSS_time)} | can start: {pilot_can_start(task, tp, my_fix)} can restart: {pilot_can_restart(task, tp, my_fix, result)} | tp: {tp.name}')
            if start_made_civl(my_fix, next_fix, tp.last_made, tolerance,
                               min_tol_m):
                tp.pointer -= 1
                t = int(round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                result.waypoints_achieved.pop()
                result.waypoints_achieved.append([tp.name, t, alt
                                                  ])  # pilot has started again
                result.real_start_time = t
                if lead_coeff:
                    lead_coeff.reset()
                tp.move_to_next()
                restarted = True

        if tp.start_done:
            '''Turnpoint managing'''
            if (tp.next.shape == 'circle'
                    and tp.next.type in ('endspeed', 'waypoint')):
                if tp_made_civl(my_fix, next_fix, tp.next, tolerance,
                                min_tol_m):
                    t = int(round(tp_time_civl(my_fix, next_fix, tp.next), 0))
                    result.waypoints_achieved.append(
                        [tp.name, t, alt])  # pilot has achieved turnpoint
                    tp.move_to_next()

            if tp.next.type == 'goal':
                if ((tp.next.shape == 'circle' and tp_made_civl(
                        my_fix, next_fix, tp.next, tolerance, min_tol_m)) or
                    (tp.next.shape == 'line' and
                     (in_semicircle(task, task.turnpoints, tp.pointer, my_fix)
                      or in_semicircle(task, task.turnpoints, tp.pointer,
                                       next_fix)))):
                    result.waypoints_achieved.append(
                        [tp.name, next_fix.rawtime,
                         alt])  # pilot has achieved turnpoint
                    break
        '''update result data
        Once launched, distance flown should be max result among:
        - previous value;
        - optimized dist. to last turnpoint made;
        - total optimized distance minus opt. distance from next wpt to goal minus dist. to next wpt;
        '''
        if tp.pointer > 0:
            if tp.start_done and not tp.ess_done:
                '''optimized distance calculation each fix'''
                fix_dist_flown = task.opt_dist - get_shortest_path(
                    task, next_fix, tp.pointer)
                # print(f'time: {next_fix.rawtime} | fix: {tp.name} | Optimized Distance used')
            else:
                '''simplified and faster distance calculation'''
                fix_dist_flown = distance_flown(next_fix, tp.pointer,
                                                task.optimised_turnpoints,
                                                task.turnpoints[tp.pointer],
                                                distances2go)
                # print(f'time: {next_fix.rawtime} | fix: {tp.name} | Simplified Distance used')

            result.distance_flown = max(result.distance_flown, fix_dist_flown)
        '''Leading coefficient
        LC = taskTime(i)*(bestDistToESS(i-1)^2 - bestDistToESS(i)^2 )
        i : i ? TrackPoints In SS'''
        if lead_coeff and tp.start_done and not tp.ess_done:
            lead_coeff.update(result, my_fix, next_fix)
        '''Airspace Check'''
        if task.airspace_check and airspace_obj:
            map_fix = [next_fix.rawtime, next_fix.lat, next_fix.lon, alt]
            plot, penalty = airspace_obj.check_fix(next_fix, alt)
            if plot:
                map_fix.extend(plot)
                '''Airspace Infringement: check if we already have a worse one'''
                airspace_name = plot[2]
                infringement_type = plot[3]
                dist = plot[4]
                infringements_list.append([
                    next_fix, airspace_name, infringement_type, dist, penalty
                ])
            else:
                map_fix.extend([None, None, None, None, None])
            # airspace_plot.append(map_fix)
    '''final results'''
    result.last_altitude = alt
    result.height = 0 if not result.first_time or result.landing_time or not next_fix else next_fix.height

    # print(f'start indev: {tp.start_index}')
    # print(f'start done: {tp.start_done}')
    # print(f'pointer: {tp.pointer}')
    if tp.start_done:
        if not already_started or restarted:
            '''
            start time
            if race, the first times
            if multistart, the first time of the last gate pilot made
            if elapsed time, the time of last fix on start
            SS Time: the gate time'''
            result.SSS_time = task.start_time

            if task.task_type == 'RACE' and task.SS_interval:
                result.SSS_time += max(
                    0,
                    (start_number_at_time(task, result.real_start_time) - 1) *
                    task.SS_interval)

            elif task.task_type == 'ELAPSED TIME':
                result.SSS_time = result.real_start_time
            '''manage jump the gun'''
            # print(f'wayponts made: {result.waypoints_achieved}')
            if max_jump_the_gun > 0:
                if result.real_start_time < result.SSS_time:
                    diff = result.SSS_time - result.real_start_time
                    penalty = diff * jtg_penalty_per_sec
                    # check
                    print(
                        f'jump the gun: {diff} - valid: {diff <= max_jump_the_gun} - penalty: {penalty}'
                    )
                    comment = f"Jump the gun: {diff} seconds. Penalty: {penalty} points"
                    result.notifications.append(
                        Notification(notification_type='jtg',
                                     flat_penalty=penalty,
                                     comment=comment))
        '''ESS Time'''
        if any(e[0] == 'ESS'
               for e in result.waypoints_achieved) and not already_ESS:
            # result.ESS_time, ess_altitude = min([e[1] for e in result.waypoints_achieved if e[0] == 'ESS'])
            result.ESS_time, result.ESS_altitude = min(
                [(x[1], x[2])
                 for x in result.waypoints_achieved if x[0] == 'ESS'],
                key=lambda t: t[0])
            result.speed = (task.SS_distance / 1000) / (result.ss_time / 3600)
        '''Distance flown'''
        ''' ?p:p?PilotsLandingBeforeGoal:bestDistancep = max(minimumDistance, taskDistance-min(?trackp.pointi shortestDistanceToGoal(trackp.pointi)))
            ?p:p?PilotsReachingGoal:bestDistancep = taskDistance
        '''
        if any(e[0] == 'Goal' for e in result.waypoints_achieved):
            result.distance_flown = task.opt_dist
            result.goal_time, result.goal_altitude = min(
                [(x[1], x[2])
                 for x in result.waypoints_achieved if x[0] == 'Goal'],
                key=lambda t: t[0])
            result.result_type = 'goal'
            result.live_comment = 'Goal!'

    result.best_waypoint_achieved = str(
        result.waypoints_achieved[-1]
        [0]) if result.waypoints_achieved else None

    if lead_coeff:
        result.fixed_LC = lead_coeff.summing

    if task.airspace_check:
        infringements, notifications, penalty = airspace_obj.get_infringements_result(
            infringements_list)
        result.notifications.extend(notifications)
        result.notifications = clear_notifications(task, result)
    return result