Esempio n. 1
0
def evaluate_start(result: FlightResult, task: Task, tp: FlightPointer):
    from pilot.notification import Notification
    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 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'''
        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'''
        if max_jump_the_gun > 0 and result.real_start_time < result.SSS_time:
            diff = result.SSS_time - result.real_start_time
            penalty = diff * jtg_penalty_per_sec
            # check
            comment = f"Jump the gun: {diff} seconds. Penalty: {penalty} points"
            result.notifications.append(
                Notification(notification_type='jtg',
                             flat_penalty=penalty,
                             comment=comment))
Esempio n. 2
0
def evaluate_ess(result: FlightResult, task: Task):
    if any(e.name == 'ESS' for e in result.waypoints_achieved):
        result.ESS_time, result.ESS_altitude = min(
            [(x.rawtime, x.altitude)
             for x in result.waypoints_achieved if x.name == 'ESS'],
            key=lambda t: t[0])
        result.speed = (task.SS_distance / 1000) / (result.ss_time / 3600)
Esempio n. 3
0
def calculate_time_points_reduction(t):
    from pilot.flightresult import FlightResult
    p = FlightResult()
    p.distance_flown = t.opt_dist
    p.SSS_time = t.max_ss_time
    p.ESS_time = t.stop_time
    return pilot_speed(t, p)
Esempio n. 4
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)
Esempio n. 5
0
def evaluate_goal(result: FlightResult, task: Task):
    """ ?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 result.waypoints_achieved):
        result.distance_flown = task.opt_dist
        result.goal_time, result.goal_altitude = min(
            [(x.rawtime, x.altitude)
             for x in result.waypoints_achieved if x.name == 'Goal'],
            key=lambda t: t[0])
        result.result_type = 'goal'
Esempio n. 6
0
    def read(cls, fp, short_name=None, keep_task_path=False, from_CIVL=False):
        """ A XML reader to read FSDB files
            Unfortunately the fsdb format isn't published so much of this is simply an
            exercise in reverse engineering.

            Input:
                - fp:           STR: filepath
                - from_CIVL:    BOOL: look for pilot on CIVL database
        """
        """read the fsdb file"""
        try:
            tree = ET.parse(fp)
            root = tree.getroot()
        except ET.Error:
            print("FSDB Read Error.")
            return None

        pilots = []
        tasks = []
        """Comp Info"""
        print("Getting Comp Info...")
        fs_comp = root.find('FsCompetition')
        comp = Comp.from_fsdb(fs_comp, short_name)
        """Formula"""
        comp.formula = Formula.from_fsdb(fs_comp)
        comp.formula.comp_class = comp.comp_class
        """Pilots"""
        print("Getting Pilots Info...")
        if from_CIVL:
            print('*** get from CIVL database')
        p = root.find('FsCompetition').find('FsParticipants')
        for pil in p.iter('FsParticipant'):
            pilot = Participant.from_fsdb(pil, from_CIVL=from_CIVL)
            # pp(pilot.as_dict())
            pilots.append(pilot)

        comp.participants = pilots
        """Tasks"""
        print("Getting Tasks Info...")
        t = root.find('FsCompetition').find('FsTasks')
        for tas in t.iter('FsTask'):
            '''create task obj'''
            task = Task.from_fsdb(tas, comp.time_offset, keep_task_path)
            '''check if task was valid'''
            if task is not None:
                if not task.task_path:
                    task.create_path()
                # task.time_offset = int(comp.time_offset)
                """Task Results"""
                node = tas.find('FsParticipants')
                if node is not None:
                    task.pilots = []
                    print("Getting Results Info...")
                    for res in node.iter('FsParticipant'):
                        '''pilots results'''
                        pilot = FlightResult.from_fsdb(res, task)
                        task.pilots.append(pilot)
                tasks.append(task)

        return cls(comp, tasks, fp)
Esempio n. 7
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
Esempio n. 8
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
Esempio n. 9
0
def dump_flight(track, task):
    # TODO check if file already exists otherwise create and save it
    from pilot.flightresult import FlightResult
    from mapUtils import get_bbox
    lib = task.formula.get_lib()
    task_result = FlightResult.check_flight(track.flight, task)  # check flight against task
    geojson_file = task_result.to_geojson_result(track, task)
    bbox = get_bbox(track.flight)
    return geojson_file, bbox
Esempio n. 10
0
def create_igc_file(result: FlightResult, task: Task):
    """ Flymaster IGC initialize format:

        AXFMSFP Flymaster Live, V1.0, S/N 618839
        HFFXA010
        HFPLTPILOT:Alessandro Ploner
        HFGTYGLIDERTYPE:
        HFGIDGLIDERID:
        HFDTM100GPSDATUM:WGS-1984
        HFCIDCOMPETITIONID:72
        HFCCLCOMPETITIONCLASS:
        HOSITSITE:Meduno - Monte Valinis-IT
        HFGPS:UBLOXNEO6
        HFPRSPRESSALTSENSOR:NA
        HFRFWFIRMWAREVERSION:202g
        HFRHWHARDWAREVERSION:1.0R2
        HFFTYFRTYPE:FLYMASTER,LIVE
        HFDTE150719
        B1132494613837N01248410EA0006900991
        B1132504613834N01248408EA0006900990
    """
    if result.track_file and Path(task.file_path, result.track_file).is_file():
        print(f"File already exists")
        return
    """check if directory already exists"""
    if not Path(task.file_path).is_dir():
        Path(task.file_path).mkdir(mode=0o755)
    '''create filename'''
    file = create_igc_filename(task.file_path, task.date, result.name,
                               result.ID)
    result.track_file = file.name
    '''create IGC header'''
    name = result.name
    glider = result.glider
    ID = result.ID
    site = task.turnpoints[0].description
    date = task.date.strftime("%d%m%y")
    header = f"AXFMSFP Flymaster Live, V1.0\n"
    header += f"HFFXA010\n"
    header += f"HFPLTPILOT:{name}\n"
    header += f"HFGTYGLIDERTYPE:{glider}\n"
    header += f"HFGIDGLIDERID:\n"
    header += f"HFDTM100GPSDATUM:WGS-1984\n"
    header += f"HFCIDCOMPETITIONID:{ID}\n"
    header += f"HFCCLCOMPETITIONCLASS:\n"
    header += f"HOSITSITE:{site}\n"
    header += f"HFGPS:UBLOXNEO6\n"
    header += f"HFPRSPRESSALTSENSOR:NA\n"
    header += f"HFRFWFIRMWAREVERSION:202g\n"
    header += f"HFRHWHARDWAREVERSION:1.0R2\n"
    header += f"HFFTYFRTYPE:FLYMASTER,LIVE\n"
    header += f"HFDTE{date}\n"
    '''create file'''
    f = open(file, "w+")
    f.write(header)
    f.close()
Esempio n. 11
0
def test_track_flight_check():
    test_track = Track.read_file('/app/tests/data/test_igc_2.igc', par_id=1)
    test_result = FlightResult()
    test_result.check_flight(flight=test_track.flight, task=test_task)
    assert int(test_result.distance_flown) == 64360
    assert test_result.best_waypoint_achieved == 'Goal'
    assert len(test_result.waypoints_achieved) == test_result.waypoints_made
    assert test_result.SSS_time == 41400
    assert test_result.ESS_time == 50555
    assert test_result.ESS_altitude == 880.0
    assert test_result.real_start_time == 41428
    assert test_result.flight_time == 12183.0
    achieved = test_result.waypoints_achieved[1]
    assert achieved.name == 'TP01'
    assert achieved.rawtime == 43947
    assert achieved.altitude == 1445.0
    assert math.isclose(float(achieved.lat), 45.8145667,
                        abs_tol=0.0000001)  # ~0.01 meters
    assert math.isclose(float(achieved.lon), 9.7707167,
                        abs_tol=0.0000001)  # ~0.01 meters
Esempio n. 12
0
def save_livetrack_result(p: FlightResult,
                          task: Task,
                          airspace: AirspaceCheck = None):
    from igc_lib import Flight
    from pilot.flightresult import save_track
    try:
        flight = Flight.create_from_file(Path(task.file_path, p.track_file))
        if flight.valid:
            print(
                f"flight valid. Livetracking LC: {p.fixed_LC} distance: {p.distance_flown} time: {p.ss_time}"
            )
            # test = FlightResult()
            # test.check_flight(flight, task, airspace)
            # print(f"Calculated LC: {test.fixed_LC} distance: {test.distance_flown} time: {test.ss_time}")
            # print(f"Difference %: {(test.fixed_LC - p.fixed_LC) / p.fixed_LC * 100}")
            save_track(p, task.id)
            p.save_tracklog_map_file(task, flight)
        else:
            print(f"{p.track_file} is not a valid igc. Result not saved.")
    except:
        print(f"{p.track_file} Error trying to save result.")
Esempio n. 13
0
def calculate_incremental_results(result: FlightResult, task: Task, tp,
                                  lead_coeff, airspace, real_start_time,
                                  already_ess, previous_achieved):
    """Incremental result update function"""
    from flightcheck import flightcheck
    if tp.start_done and not real_start_time == result.real_start_time:
        '''pilot has started or restarted'''
        result.notifications = [
            n for n in result.notifications if not n.notification_type == 'jtg'
        ]
        flightcheck.evaluate_start(result, task, tp)
    if tp.ess_done and not already_ess:
        '''pilot made ESS'''
        flightcheck.evaluate_ess(result, task)
    if tp.made_all:
        '''pilot made goal'''
        flightcheck.evaluate_goal(result, task)

    if len(result.waypoints_achieved) > len(previous_achieved):
        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
        # print(f"{result.name} - cycle end Lead Coeff: {lead_coeff.summing}, fixed LC: {result.fixed_LC}")

    if task.airspace_check:
        _, notifications, penalty = airspace.get_infringements_result(
            result.infringements)
        # result.infringements.extend([el for el in infringements if el not in result.infringements])
        result.notifications = [
            el for el in result.notifications
            if not el.notification_type == 'airspace'
        ]
        result.notifications.extend(notifications)
Esempio n. 14
0
def get_unscored_pilots(task_id: int, track_source=None):
    """ Gets list of registered pilots that still do not have a result
        Input:  task_id INT task database ID
        Output: list of Pilot obj."""
    from pilot.flightresult import FlightResult
    from db.tables import UnscoredPilotView as U
    pilot_list = []
    with db_session() as db:
        results = db.query(U).filter_by(task_id=task_id)
        if track_source == 'xcontest':
            results = results.filter(U.xcontest_id.isnot(None))
        elif track_source == 'flymaster':
            results = results.filter(U.live_id.isnot(None))
        results = results.all()
        for p in results:
            pilot = FlightResult()
            p.populate(pilot)
            pilot_list.append(pilot)
    return pilot_list
Esempio n. 15
0
def save_igc_background(task_id: int,
                        par_id: int,
                        tracklog,
                        user,
                        check_g_record=False):
    from task import Task
    from pilot.track import create_igc_filename, validate_G_record
    from pilot.flightresult import FlightResult

    pilot = FlightResult.read(par_id, task_id)
    print = partial(print_to_sse, id=par_id, channel=user)
    if pilot.name:
        task = Task.read(task_id)
        fullname = create_igc_filename(task.file_path, task.date, pilot.name)
        tracklog.save(fullname)
        print('|open_modal')
        print('***************START*******************')
        if check_g_record:
            print('Checking G-Record...')
            validation = validate_G_record(fullname)
            if validation == 'FAILED':
                print('G-Record not valid')
                data = {
                    'par_id': pilot.par_id,
                    'track_id': pilot.track_id,
                    'Result': ''
                }
                print(json.dumps(data) + '|g_record_fail')
                return None, None
            if validation == 'ERROR':
                print('Error trying to validate G-Record')
                return None, None
            if validation == 'PASSED':
                print('G-Record is valid')
        print(f'IGC file saved: {fullname.name}')
    else:
        return None, None
    return fullname
Esempio n. 16
0
def get_unscored_pilots(task_id: int, track_source=None):
    """ Gets list of registered pilots that still do not have a result
        Input:  task_id INT task database ID
        Output: list of Pilot obj."""
    from pilot.flightresult import FlightResult
    from db.tables import UnscoredPilotView as U
    pilot_list = []
    with db_session() as db:
        # results = db.query(U.par_id, U.comp_id, U.ID, U.name, U.nat, U.sex, U.civl_id,
        #                    U.live_id, U.glider, U.glider_cert, U.sponsor, U.xcontest_id,
        #                    U.team, U.nat_team).filter_by(task_id=task_id)
        results = db.query(U).filter_by(task_id=task_id)
        if track_source == 'xcontest':
            results = results.filter(U.xcontest_id.isnot(None))
        elif track_source == 'flymaster':
            results = results.filter(U.live_id.isnot(None))
        results = results.all()
        for p in results:
            # pilot = FlightResult.from_dict(p._asdict())
            pilot = FlightResult()
            p.populate(pilot)
            pilot_list.append(pilot)
    return pilot_list
Esempio n. 17
0
def assign_and_import_tracks(files,
                             task,
                             track_source=None,
                             user=None,
                             check_g_record=False,
                             print=print):
    """Find pilots to associate with tracks"""
    from compUtils import get_registration
    from pilot.track import Track, validate_G_record, igc_parsing_config_from_yaml
    from functools import partial
    from frontendUtils import print_to_sse
    import json
    import importlib

    pilot_list = []
    task_id = task.id
    comp_id = task.comp_id
    """checking if comp requires a regisration.
    Then we create a list of registered pilots to check against tracks filename.
    This should be much faster than checking against all pilots in database through a query"""
    registration = get_registration(comp_id)
    if registration:
        """We add tracks for the registered pilots not yet scored"""
        print(
            "Comp with registration: files will be checked against registered pilots not yet scored"
        )
        pilot_list = get_unscored_pilots(task_id, track_source)
        if len(pilot_list) == 0:
            print(f"Pilot list is empty")
            return
        print(
            f"We have {len(pilot_list)} pilots to find tracks for, and {len(files)} tracks"
        )
    else:
        print(
            f"No registration required, we have {len(files)} tracks to associate"
        )

    task_date = task.date
    track_counter = 0
    track_path = task.file_path
    FlightParsingConfig = igc_parsing_config_from_yaml(task.igc_config_file)

    # print("found {} tracks \n".format(len(files)))
    for file in files:
        mytrack = None
        filename = file.name
        print(f'filename {filename}, {type(filename)}')
        if registration:
            # print(f"checking {filename} against {len(pilot_list)} pilots...")
            """check filenames to find pilots"""
            if track_source:
                # print(f'Source: {track_source}')
                ''' Use Live server filename format to get pilot '''
                lib = importlib.import_module('.'.join(
                    ['sources', track_source]))
                pilot = lib.get_pilot_from_list(filename, pilot_list)
            else:
                pilot = get_pilot_from_list(filename, pilot_list)

            if pilot:
                """found a pilot for the track file.
                dropping pilot from list and creating track obj"""
                # print(f"Found a pilot to associate with file. dropping {pilot.name} from non scored list")
                pilot_list[:] = [
                    d for d in pilot_list if d.par_id != pilot.par_id
                ]
                mytrack = Track.read_file(filename=file,
                                          config=FlightParsingConfig,
                                          print=print)
        else:
            """We add track if we find a pilot in database
            that has not yet been scored"""
            mytrack = Track.read_file(filename=file,
                                      config=FlightParsingConfig,
                                      print=print)
            if get_pil_track(mytrack.par_id, task_id):
                """pilot has already been scored"""
                print(
                    f"Pilot with ID {mytrack.par_id} has already a valid track for task with ID {task_id}"
                )
                continue
            pilot = FlightResult.read(par_id=mytrack.par_id, task_id=task_id)
        """check result"""
        if not mytrack:
            print(
                f"Track {filename} is not a valid track file, pilot not found in competition or pilot "
                f"already has a track")
            continue
        elif not mytrack.date == task_date:
            print(f"track {filename} has a different date from task")
            continue
        """pilot is registered and has no valid track yet
        moving file to correct folder and adding to the list of valid tracks"""
        track_counter += 1
        print(f"Track {track_counter}|counter")
        mytrack.task_id = task_id
        filename_and_path = mytrack.copy_track_file(task_path=track_path,
                                                    pname=pilot.name)
        # print(f"pilot {mytrack.par_id} associated with track {mytrack.filename}")
        pilot.track_file = filename_and_path.name
        print(f"processing {pilot.ID} {pilot.name}:")
        if user:
            new_print = partial(print_to_sse, id=mytrack.par_id, channel=user)
            print('***************START*******************')
        else:
            new_print = print
        if check_g_record:
            print('Checking G-Record...')
            validation = validate_G_record(filename_and_path)
            if validation == 'FAILED':
                print('G-Record not valid')
                data = {
                    'par_id': pilot.par_id,
                    'track_id': pilot.track_id,
                    'Result': ''
                }
                print(json.dumps(data) + '|g_record_fail')
                continue
            if validation == 'ERROR':
                print('Error trying to validate G-Record')
                continue
            if validation == 'PASSED':
                print('G-Record is valid')
        verify_and_import_track(pilot, mytrack, task, print=new_print)
    print("*******************processed all tracks**********************")
Esempio n. 18
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
Esempio n. 19
0
def calculate_min_dist_score(t):
    from pilot.flightresult import FlightResult
    p = FlightResult()
    p.distance_flown = t.formula.min_dist
    return pilot_distance(t, p)
Esempio n. 20
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)