Skip to content

Commit

Permalink
enhancement in igc process to detect restarts
Browse files Browse the repository at this point in the history
  • Loading branch information
biuti committed Jul 19, 2023
1 parent 0245c60 commit 657edcc
Showing 1 changed file with 88 additions and 57 deletions.
145 changes: 88 additions & 57 deletions airscore/core/pilot/track.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
class Track(Flight):

def __init__(self, *args, **kwargs):
self.start_time = kwargs.pop('start_time', None)
super().__init__(*args, **kwargs)

def _check_altitudes(self):
Expand Down Expand Up @@ -123,6 +124,45 @@ def _check_altitudes(self):
self.press_alt_valid = press_alt_ok
self.gnss_alt_valid = gnss_alt_ok

def _compute_takeoff_landing(self):
"""Finds the takeoff and landing fixes in the log.
Takeoff fix is the first fix in the flying mode. Landing fix
is the next fix after the last fix in the flying mode or the
last fix in the file.
"""
takeoff_fix = None
landing_fix = None
was_flying = False
for fix in self.fixes:
if fix.flying and takeoff_fix is None:
takeoff_fix = fix
elif fix.flying and landing_fix and self.start_time:
# pilot restarts within start window
takeoff_fix = fix
landing_fix = None
if not fix.flying and was_flying:
landing_fix = fix
if self.start_time and fix.rawtime < self.start_time:
# continue checking if pilot restarts within start window
pass
elif self._config.which_flight_to_pick == "first":
# User requested to select just the first flight in the log,
# terminate now.
break
was_flying = fix.flying

if takeoff_fix is None:
# No takeoff found.
return

if landing_fix is None:
# Landing on the last fix
landing_fix = self.fixes[-1]

self.takeoff_fix = takeoff_fix
self.landing_fix = landing_fix

@staticmethod
def create_from_file(filename: Path, config: FlightParsingConfig = FlightParsingConfig()):
"""Creates an instance of Flight from a given file.
Expand All @@ -135,34 +175,9 @@ def create_from_file(filename: Path, config: FlightParsingConfig = FlightParsing
An instance of Flight built from the supplied IGC file.
"""

fixes = []
a_records = []
i_records = []
h_records = []
abs_filename = Path(filename).expanduser().absolute()
with abs_filename.open('r', encoding="ISO-8859-1") as flight_file:
for line in flight_file:
line = line.replace('\n', '').replace('\r', '')
if not line:
continue
if line[0] == 'A':
a_records.append(line)
elif line[0] == 'B':
fix = GNSSFix.build_from_B_record(line, index=len(fixes))
if fix is not None:
if fixes and math.fabs(fix.rawtime - fixes[-1].rawtime) < 1e-5:
# The time did not change since the previous fix.
# Ignore this fix.
pass
else:
fixes.append(fix)
elif line[0] == 'I':
i_records.append(line)
elif line[0] == 'H':
h_records.append(line)
else:
# Do not parse any other types of IGC records
pass
fixes, a_records, i_records, h_records = parse_igc_file(flight_file)
return Track(fixes, a_records, h_records, i_records, config)

@staticmethod
Expand All @@ -184,42 +199,58 @@ def process(
An instance of Track(igc_lib Flight) built from the supplied IGC file.
"""

fixes = []
a_records = []
i_records = []
h_records = []
abs_filename = Path(filename).expanduser().absolute()
with abs_filename.open('r', encoding="ISO-8859-1") as flight_file:
for line in flight_file:
line = line.replace('\n', '').replace('\r', '')
if not line:
continue
if line[0] == 'A':
a_records.append(line)
elif line[0] == 'B':
fix = GNSSFix.build_from_B_record(line, index=len(fixes))
if fix is not None:
if fixes and math.fabs(fix.rawtime - fixes[-1].rawtime) < 1e-5:
# The time did not change since the previous fix.
# Ignore this fix.
pass
elif task and not (task.window_open_time - 1 <= fix.rawtime <= task.task_deadline + 1):
# We are out of task time.
# Ignore this fix.
pass
else:
fixes.append(fix)
elif line[0] == 'I':
i_records.append(line)
elif line[0] == 'H':
h_records.append(line)
else:
# Do not parse any other types of IGC records
pass
fixes, a_records, i_records, h_records = parse_igc_file(flight_file, task)
try:
return Track(fixes, a_records, h_records, i_records, config)
return Track(fixes, a_records, h_records, i_records, config, start_time=task.window_close_time or task.start_time)
except (IndexError, AttributeError) as e:
print(f"Error creating Track from {filename.name}: {e}")
return None
# return flight


def parse_igc_file(lines: list, task: (Task or None) = None) -> tuple:
fixes = []
a_records = []
i_records = []
h_records = []
for line in lines:
line = line.replace('\n', '').replace('\r', '')
if not line:
continue
if line[0] == 'A':
# Manufacturer and unique ID for FR
a_records.append(line)
elif line[0] == 'H':
# Header record
h_records.append(line)
elif line[0] == 'I':
# FXA extension (mandatory)
i_records.append(line)
elif line[0] == 'E' and line[-3:] == 'PEV':
# trying to catch pilot-initiated events (PEV code) that seem to indicate
# detected launch in some GPS instruments
# this should avoid to consider aborted launch and other events as first flight
# NOTE: disabled for now, seems not necessary with relaunch detecting code
# print(f"found a PEV event: {line}")
# fixes.clear()
continue
elif line[0] == 'B':
fix = GNSSFix.build_from_B_record(line, index=len(fixes))
if fix is not None:
if fixes and math.fabs(fix.rawtime - fixes[-1].rawtime) < 1e-5:
# The time did not change since the previous fix.
# Ignore this fix.
pass
elif task and not (task.window_open_time - 1 <= fix.rawtime <= task.task_deadline + 1):
# We are out of task time.
# Ignore this fix.
pass
else:
fixes.append(fix)
else:
# Do not parse any other types of IGC records
pass

return fixes, a_records, i_records, h_records

0 comments on commit 657edcc

Please sign in to comment.