def rinex_obs(**parser_args: Any) -> parsers.RinexParser: """Dispatch to correct subclass based on version in Rinex file""" # Import parsers locally to avoid circular imports from midgard.parsers import wip_rinex2_obs as rinex2_obs, wip_rinex3_obs as rinex3_obs _PARSERS = { "2": rinex2_obs.Rinex2ObsParser, "3": rinex3_obs.Rinex3ObsParser } # Run a basic rinex parser to info from file parser = parsers.RinexParser(**parser_args) rinex_info = parser.get_rinex_version_type() if rinex_info["file_type"] != "O": raise exceptions.ParserError( f"File {parser.file_path} is a {rinex_info['file_type']!r} file, expected 'O'" ) version = rinex_info["rinex_version"][0] # Major version try: return _PARSERS[version](**parser_args) except KeyError: raise exceptions.ParserError( f"File {parser.file_path} is version {rinex_info['rinex_version']}, " f"which is not supported")
def read_data(self) -> None: """Dispatch to correct subclass based on version in Rinex file Need to make sure the dispatch only happens for RinexObsParsers, not for subclasses. """ if self.__class__.__name__ == "RinexObsParser": Parser: Type[parsers.RinexParser] # Find correct parser subclass version = self.get_rinex_version() if version.startswith("3"): from midgard.parsers.rinex3_obs import Rinex3ObsParser Parser = Rinex3ObsParser elif version.startswith("2"): from midgard.parsers.rinex2_obs import Rinex2ObsParser Parser = Rinex2ObsParser else: raise exceptions.ParserError( f"Unknown version {version!r} for Rinex observation files") # Read data with correct parser parser = Parser(**self.meta["__kwargs__"]) parser.read_data() # Copy data to self self.header.update(parser.header) self.data.update(parser.data) else: super().read_data()
def rinex(**parser_args: Any) -> parsers.RinexParser: """Dispatch to correct subclass based on Rinex file type""" parser = parsers.RinexParser(**parser_args) rinex_info = parser.get_rinex_version_type() if rinex_info["file_type"] not in _PARSERS.keys(): raise exceptions.ParserError( f"File {parser.file_path} is a {rinex_info['file_type']!r} file, " f"expected {' '.join(_PARSERS.keys())}") file_type = rinex_info["file_type"] try: return _PARSERS[file_type](**parser_args) except KeyError: raise exceptions.ParserError( f"File {parser.file_path} has file type {rinex_info['file_type']}, which is not supported" )
def get_rinex_version(self) -> str: """Get version of Rinex file""" version = self.rinex_version__type with open(self.file_path, mode="r", encoding=self.file_encoding) as fid: for line in fid: marker = line[60:80].strip() if marker != version.marker: self.error(f"Wrong marker {marker!r} before version information") continue return line[slice(*version.fields["rinex_version"])].strip() raise exceptions.ParserError(f"No information about Rinex version found in {self.file_path}")
def get_rinex_version_type(self) -> Dict[str, str]: """Get version and type of Rinex file""" header_def = self.rinex_version__type with open(self.file_path, mode="r", encoding=self.file_encoding) as fid: for line in fid: marker = line[60:80].strip() if marker != header_def.marker: self.error(f"Wrong marker {marker!r} before version information") continue return {k: line[slice(*v)].strip() for k, v in header_def.fields.items()} raise exceptions.ParserError(f"No information about Rinex version found in {self.file_path}")
def _raise_error(self, text: str) -> None: """Raise a parser error""" raise exceptions.ParserError(text)
def save_correction(self, line: Dict[str, str], cache: Dict[str, Any]) -> None: """Save antenna correction in data structures. The antenna corrections are saved after reading of corrections for one frequency. Antenna correction data are saved in following data structure, whereby satellite antenna corrections are time dependent: self.data = { <prn> : { <valid from>: { cospar_id: <value>, sat_code: <value>, sat_type: <value>, valid_until: <value>, azimuth: <list with azimuth values>, elevation: <list with elevation values>, <frequency>: { azi: [<list with azimuth-elevation dependent corrections>], neu: [north, east, up], noazi: [<list with elevation dependent corrections>] }}}, <receiver antenna> : { azimuth: <list with azimuth values>, elevation: <list with elevation values>, <frequency>: { azi: [<array with azimuth-elevation dependent corrections>], neu: [north, east, up], noazi: [<list with elevation dependent corrections>] }} } """ ant = cache["antenna_code"] if cache["sat_code"] else cache[ "antenna_type"] self.data.setdefault(ant, dict()) freq = cache["frequency_code"] if "valid_from" in cache: dt = cache["valid_from"] tmp: Dict[str, Any] = dict( ) # Temporary dictionary, where antenna correction for one frequency is saved. tmp[freq] = dict() # Save general information of ANTEX antenna section (NOTE: Has to be done only once.) if cache["num_freq_counter"] == 0: if cache["sat_code"]: # only necessary for satellites if dt in self.data[ant]: valid_from = "{:4d}-{:02d}-{:02d}".format( dt.year, dt.month, dt.day) raise exceptions.ParserError( f"Antenna correction for satellite PRN {ant} (SVN {cache['sat_code']}) valid " f"from {valid_from} is not unique.") tmp["cospar_id"] = cache["cospar_id"] tmp["sat_code"] = cache["sat_code"] tmp["sat_type"] = cache["antenna_type"] if "valid_until" in cache: tmp["valid_until"] = cache["valid_until"] else: tmp["valid_until"] = datetime.datetime.now() # Determine elevation list if cache["dzen"] != 0.0: tmp["elevation"] = np.arange( 90.0 - cache["zen1"], 90.0 - (cache["zen2"] + cache["dzen"]), -cache["dzen"]) tmp["elevation"] = np.radians(tmp["elevation"]) # Determine azimuth list if cache["dazi"] != 0.0: tmp["azimuth"] = np.arange(0, 360 + cache["dazi"], cache["dazi"]) tmp["azimuth"] = np.radians(tmp["azimuth"]) # Save frequency dependent antenna corrections tmp[freq]["neu"] = [ cache["north"] * Unit.millimeter2meter, cache["east"] * Unit.millimeter2meter, cache["up"] * Unit.millimeter2meter, ] tmp[freq]["noazi"] = np.array(cache["noazi"]) if "azi" in cache: tmp[freq]["azi"] = np.array(cache["azi"]) # Save satellite antenna correction in data structure if cache["sat_code"]: self.data[ant].setdefault(dt, dict()) if freq in self.data[ant][dt]: valid_from = "{:4d}-{:02d}-{:02d}".format( dt.year, dt.month, dt.day) raise exceptions.ParserError( f"Frequency {freq} antenna corrections for satellite PRN {ant} valid from {valid_from} is " f"not unique in file {self.file_path}.") self.data[ant][dt].update(tmp) # Save receiver antenna correction in data structure else: if freq in self.data[ant]: raise exceptions.ParserError( f"Frequency {freq} antenna corrections for receiver antenna {ant} is not unique " f"in file {self.file_path}.") self.data[ant].update(tmp) cache["num_freq_counter"] = cache["num_freq_counter"] + 1