def obsheader2(f: TextIO, useindicators: bool = False, meas: Sequence[str] = None) -> Dict[str, Any]: """ End users should use rinexheader() """ if isinstance(f, (str, Path)): with opener(f, header=True) as h: return obsheader2(h, useindicators, meas) f.seek(0) # %% selection if isinstance(meas, str): meas = [meas] if not meas or not meas[0].strip(): meas = None hdr = rinexinfo(f) Nobs = 0 # not None due to type checking for ln in f: if "END OF HEADER" in ln: break h = ln[60:80].strip() c = ln[:60] # %% measurement types if '# / TYPES OF OBSERV' in h: if Nobs == 0: Nobs = int(c[:6]) hdr[h] = c[6:].split() else: hdr[h] += c[6:].split() elif h not in hdr: # Header label hdr[h] = c # string with info else: # concatenate hdr[h] += " " + c # %% useful values try: hdr['systems'] = hdr['RINEX VERSION / TYPE'][40] except KeyError: pass hdr['Nobs'] = Nobs # 5 observations per line (incorporating LLI, SSI) hdr['Nl_sv'] = ceil(hdr['Nobs'] / 5) # %% list with receiver location in x,y,z cartesian ECEF (OPTIONAL) try: hdr['position'] = [float(j) for j in hdr['APPROX POSITION XYZ'].split()] if ecef2geodetic is not None: hdr['position_geodetic'] = ecef2geodetic(*hdr['position']) except (KeyError, ValueError): pass # %% observation types try: hdr['fields'] = hdr['# / TYPES OF OBSERV'] if Nobs != len(hdr['fields']): raise ValueError(f'{f.name} header read incorrectly') if isinstance(meas, (tuple, list, np.ndarray)): ind = np.zeros(len(hdr['fields']), dtype=bool) for m in meas: for i, f in enumerate(hdr['fields']): if f.startswith(m): ind[i] = True hdr['fields_ind'] = np.nonzero(ind)[0] else: ind = slice(None) hdr['fields_ind'] = np.arange(Nobs) hdr['fields'] = np.array(hdr['fields'])[ind].tolist() except KeyError: pass hdr['Nobsused'] = hdr['Nobs'] if useindicators: hdr['Nobsused'] *= 3 # %% try: hdr['# OF SATELLITES'] = int(hdr['# OF SATELLITES'][:6]) except (KeyError, ValueError): pass # %% time try: hdr['t0'] = _timehdr(hdr['TIME OF FIRST OBS']) except (KeyError, ValueError): pass try: hdr['t1'] = _timehdr(hdr['TIME OF LAST OBS']) except (KeyError, ValueError): pass try: # This key is OPTIONAL hdr['interval'] = float(hdr['INTERVAL'][:10]) except (KeyError, ValueError): pass return hdr
def obsheader2(f: TextIO, useindicators: bool = False, meas: Sequence[str] = None) -> Dict[str, Any]: """ End users should use rinexheader() """ if isinstance(f, (str, Path)): with opener(f, header=True) as h: return obsheader2(h, useindicators, meas) f.seek(0) # %% selection if isinstance(meas, str): meas = [meas] if not meas or not meas[0].strip(): meas = None hdr = rinexinfo(f) Nobs = 0 # not None due to type checking for ln in f: if "END OF HEADER" in ln: break h = ln[60:80].strip() c = ln[:60] # %% measurement types if "# / TYPES OF OBSERV" in h: if Nobs == 0: Nobs = int(c[:6]) hdr[h] = c[6:].split() else: hdr[h] += c[6:].split() elif h not in hdr: # Header label hdr[h] = c # string with info else: # concatenate hdr[h] += " " + c # %% useful values try: hdr["systems"] = hdr["RINEX VERSION / TYPE"][40] except KeyError: pass hdr["Nobs"] = Nobs # 5 observations per line (incorporating LLI, SSI) hdr["Nl_sv"] = ceil(hdr["Nobs"] / 5) # %% list with receiver location in x,y,z cartesian ECEF (OPTIONAL) try: hdr["position"] = [ float(j) for j in hdr["APPROX POSITION XYZ"].split() ] if ecef2geodetic is not None: hdr["position_geodetic"] = ecef2geodetic(*hdr["position"]) except (KeyError, ValueError): pass # %% observation types try: hdr["fields"] = hdr["# / TYPES OF OBSERV"] if hdr["Nobs"] != len(hdr["fields"]): logging.error( f"{f.name} number of observations declared in header does not match fields" ) hdr["Nobs"] = len(hdr["fields"]) if isinstance(meas, (tuple, list, np.ndarray)): ind = np.zeros(len(hdr["fields"]), dtype=bool) for m in meas: for i, f in enumerate(hdr["fields"]): if f.startswith(m): ind[i] = True hdr["fields_ind"] = np.nonzero(ind)[0] else: ind = np.s_[:] hdr["fields_ind"] = np.arange(hdr["Nobs"]) hdr["fields"] = np.array(hdr["fields"])[ind].tolist() except KeyError: pass hdr["Nobsused"] = hdr["Nobs"] if useindicators: hdr["Nobsused"] *= 3 # %% try: hdr["# OF SATELLITES"] = int(hdr["# OF SATELLITES"][:6]) except (KeyError, ValueError): pass # %% time try: hdr["t0"] = _timehdr(hdr["TIME OF FIRST OBS"]) except (KeyError, ValueError): pass try: hdr["t1"] = _timehdr(hdr["TIME OF LAST OBS"]) except (KeyError, ValueError): pass try: # This key is OPTIONAL hdr["interval"] = float(hdr["INTERVAL"][:10]) except (KeyError, ValueError): pass try: s = " " hdr["rxmodel"] = s.join(hdr["REC # / TYPE / VERS"].split()[1:-1]) except (KeyError, ValueError): pass return hdr
def obsheader3(f: TextIO, use: Sequence[str] = None, meas: Sequence[str] = None) -> Dict[str, Any]: """ get RINEX 3 OBS types, for each system type optionally, select system type and/or measurement type to greatly speed reading and save memory (RAM, disk) """ if isinstance(f, (str, Path)): with opener(f, header=True) as h: return obsheader3(h, use, meas) fields = {} Fmax = 0 # %% first line hdr = rinexinfo(f) for ln in f: if "END OF HEADER" in ln: break h = ln[60:80] c = ln[:60] if 'SYS / # / OBS TYPES' in h: k = c[0] fields[k] = c[6:60].split() N = int(c[3:6]) # %% maximum number of fields in a file, to allow fast Numpy parse. Fmax = max(N, Fmax) n = N-13 while n > 0: # Rinex 3.03, pg. A6, A7 ln = f.readline() assert 'SYS / # / OBS TYPES' in ln[60:] fields[k] += ln[6:60].split() n -= 13 assert len(fields[k]) == N continue if h.strip() not in hdr: # Header label hdr[h.strip()] = c # don't strip for fixed-width parsers # string with info else: # concatenate to the existing string hdr[h.strip()] += " " + c # %% list with x,y,z cartesian (OPTIONAL) try: hdr['position'] = [float(j) for j in hdr['APPROX POSITION XYZ'].split()] if ecef2geodetic is not None: hdr['position_geodetic'] = ecef2geodetic(*hdr['position']) except (KeyError, ValueError): pass # %% time try: t0s = hdr['TIME OF FIRST OBS'] # NOTE: must do second=int(float()) due to non-conforming files hdr['t0'] = datetime(year=int(t0s[:6]), month=int(t0s[6:12]), day=int(t0s[12:18]), hour=int(t0s[18:24]), minute=int(t0s[24:30]), second=int(float(t0s[30:36])), microsecond=int(float(t0s[30:43]) % 1 * 1000000)) except (KeyError, ValueError): pass try: hdr['interval'] = float(hdr['INTERVAL'][:10]) except (KeyError, ValueError): pass # %% select specific satellite systems only (optional) if use is not None: if not set(fields.keys()).intersection(use): raise KeyError(f'system type {use} not found in RINEX file') fields = {k: fields[k] for k in use if k in fields} # perhaps this could be done more efficiently, but it's probably low impact on overall program. # simple set and frozenset operations do NOT preserve order, which would completely mess up reading! sysind = {} if isinstance(meas, (tuple, list, np.ndarray)): for sk in fields: # iterate over each system # ind = np.isin(fields[sk], meas) # boolean vector ind = np.zeros(len(fields[sk]), dtype=bool) for m in meas: for i, f in enumerate(fields[sk]): if f.startswith(m): ind[i] = True fields[sk] = np.array(fields[sk])[ind].tolist() sysind[sk] = np.empty(Fmax*3, dtype=bool) # *3 due to LLI, SSI for j, i in enumerate(ind): sysind[sk][j*3:j*3+3] = i else: sysind = {k: slice(None) for k in fields} hdr['fields'] = fields hdr['fields_ind'] = sysind hdr['Fmax'] = Fmax return hdr
def obsheader3(f: TextIO, use: Sequence[str] = None, meas: Sequence[str] = None) -> Dict[str, Any]: """ get RINEX 3 OBS types, for each system type optionally, select system type and/or measurement type to greatly speed reading and save memory (RAM, disk) """ if isinstance(f, (str, Path)): with opener(f, header=True) as h: return obsheader3(h, use, meas) fields = {} Fmax = 0 # %% first line hdr = rinexinfo(f) for ln in f: if "END OF HEADER" in ln: break h = ln[60:80] c = ln[:60] if 'SYS / # / OBS TYPES' in h: k = c[0] fields[k] = c[6:60].split() N = int(c[3:6]) # %% maximum number of fields in a file, to allow fast Numpy parse. Fmax = max(N, Fmax) n = N - 13 while n > 0: # Rinex 3.03, pg. A6, A7 ln = f.readline() assert 'SYS / # / OBS TYPES' in ln[60:] fields[k] += ln[6:60].split() n -= 13 assert len(fields[k]) == N continue if h.strip() not in hdr: # Header label hdr[h.strip()] = c # don't strip for fixed-width parsers # string with info else: # concatenate to the existing string hdr[h.strip()] += " " + c # %% list with x,y,z cartesian (OPTIONAL) try: hdr['position'] = [ float(j) for j in hdr['APPROX POSITION XYZ'].split() ] if ecef2geodetic is not None: hdr['position_geodetic'] = ecef2geodetic(*hdr['position']) except (KeyError, ValueError): pass # %% time try: t0s = hdr['TIME OF FIRST OBS'] # NOTE: must do second=int(float()) due to non-conforming files hdr['t0'] = datetime(year=int(t0s[:6]), month=int(t0s[6:12]), day=int(t0s[12:18]), hour=int(t0s[18:24]), minute=int(t0s[24:30]), second=int(float(t0s[30:36])), microsecond=int(float(t0s[30:43]) % 1 * 1000000)) except (KeyError, ValueError): pass try: hdr['interval'] = float(hdr['INTERVAL'][:10]) except (KeyError, ValueError): pass # %% select specific satellite systems only (optional) if use is not None: if not set(fields.keys()).intersection(use): raise KeyError(f'system type {use} not found in RINEX file') fields = {k: fields[k] for k in use if k in fields} # perhaps this could be done more efficiently, but it's probably low impact on overall program. # simple set and frozenset operations do NOT preserve order, which would completely mess up reading! sysind = {} if isinstance(meas, (tuple, list, np.ndarray)): for sk in fields: # iterate over each system # ind = np.isin(fields[sk], meas) # boolean vector ind = np.zeros(len(fields[sk]), dtype=bool) for m in meas: for i, f in enumerate(fields[sk]): if f.startswith(m): ind[i] = True fields[sk] = np.array(fields[sk])[ind].tolist() sysind[sk] = np.empty(Fmax * 3, dtype=bool) # *3 due to LLI, SSI for j, i in enumerate(ind): sysind[sk][j * 3:j * 3 + 3] = i else: sysind = {k: slice(None) for k in fields} hdr['fields'] = fields hdr['fields_ind'] = sysind hdr['Fmax'] = Fmax return hdr