Exemplo n.º 1
0
def _fast_alloc(fn: T.TextIO | Path, Nl_sv: int) -> int:
    """
    prescan first N lines of file to see if it truncates to less than 80 bytes

    Picking N:  N > Nobs+4 or so.
      100 seemed a good start.
    """
    if isinstance(fn, Path):
        assert fn.is_file(), "need freshly opened file"
    elif isinstance(fn, io.StringIO):
        fn.seek(0)
    else:
        raise TypeError(f"Unknown filetype {type(fn)}")

    ln = ""  # in case of truncated file, don't crash
    with opener(fn) as f:
        _skip_header(f)
        # %% find the first line with time (sometimes a blank line or two after header)
        for ln in f:
            try:
                t = _timeobs(ln)
            except ValueError:
                continue

            if isinstance(t, datetime):
                break

        try:
            _getsvind(f, ln)
        except ValueError as e:
            logging.debug(e)
            return 0

        raw = [f.readline() for _ in range(Nl_sv)]

    lens = list(map(len, raw))
    if max(lens) < 79:  # oddly formatted file, no prediction
        return 0

    shorts = sum(ln < 79 for ln in lens)

    return len(lens) - shorts
Exemplo n.º 2
0
def rinexheader(fn: T.TextIO | Path) -> dict[str, T.Any]:
    """
    retrieve RINEX 2/3 or CRINEX 1/3 header as unparsed dict()
    """
    if isinstance(fn, (str, Path)):
        fn = Path(fn).expanduser()

    if isinstance(fn, Path) and fn.suffix == ".nc":
        return rinexinfo(fn)
    elif isinstance(fn, Path):
        with opener(fn, header=True) as f:
            return rinexheader(f)
    elif isinstance(fn, io.StringIO):
        fn.seek(0)
    elif isinstance(fn, io.TextIOWrapper):
        pass
    else:
        raise TypeError(f"unknown RINEX filetype {type(fn)}")

    info = rinexinfo(fn)

    if int(info["version"]) in (1, 2):
        if info["rinextype"] == "obs":
            hdr = obsheader2(fn)
        elif info["rinextype"] == "nav":
            hdr = navheader2(fn)
        else:
            raise ValueError(f"Unknown rinex type {info} in {fn}")
    elif int(info["version"]) == 3:
        if info["rinextype"] == "obs":
            hdr = obsheader3(fn)
        elif info["rinextype"] == "nav":
            hdr = navheader3(fn)
        else:
            raise ValueError(f"Unknown rinex type {info} in {fn}")
    else:
        raise ValueError(f"unknown RINEX {info}  {fn}")

    return hdr
Exemplo n.º 3
0
def opener(fn: T.TextIO | Path, header: bool = False) -> T.Iterator[T.TextIO]:
    """provides file handle for regular ASCII or gzip files transparently"""
    if isinstance(fn, str):
        fn = Path(fn).expanduser()

    if isinstance(fn, io.StringIO):
        fn.seek(0)
        yield fn
    elif isinstance(fn, Path):
        # need to have this check for Windows
        if not fn.is_file():
            raise FileNotFoundError(fn)

        finf = fn.stat()
        if finf.st_size > 100e6:
            logging.info(f"opening {finf.st_size/1e6} MByte {fn.name}")

        suffix = fn.suffix.lower()

        if suffix == ".gz":
            with gzip.open(fn, "rt") as f:
                _, is_crinex = rinex_version(first_nonblank_line(f))
                f.seek(0)

                if is_crinex and not header:
                    # Conversion to string is necessary because of a quirk where gzip.open()
                    # even with 'rt' doesn't decompress until read.
                    f = io.StringIO(crx2rnx(f.read()))
                yield f
        elif suffix == ".bz2":
            # this is for plain bzip2 files, NOT tar.bz2, which requires f.seek(512)
            with bz2.open(fn, "rt") as f:
                _, is_crinex = rinex_version(first_nonblank_line(f))
                f.seek(0)

                if is_crinex and not header:
                    f = io.StringIO(crx2rnx(f.read()))
                yield f
        elif suffix == ".zip":
            with zipfile.ZipFile(fn, "r") as z:
                flist = z.namelist()
                for rinexfn in flist:
                    with z.open(rinexfn, "r") as bf:
                        f = io.StringIO(
                            io.TextIOWrapper(
                                bf, encoding="ascii",
                                errors="ignore").read()  # type: ignore
                        )
                        yield f
        elif suffix == ".z":
            if unlzw is None:
                raise ImportError("pip install unlzw3")
            with fn.open("rb") as zu:
                with io.StringIO(unlzw(zu.read()).decode("ascii")) as f:
                    yield f
        else:  # assume not compressed (or Hatanaka)
            with fn.open("r", encoding="ascii", errors="ignore") as f:
                _, is_crinex = rinex_version(first_nonblank_line(f))
                f.seek(0)

                if is_crinex and not header:
                    f = io.StringIO(crx2rnx(f))
                yield f
    else:
        raise OSError(f"Unsure what to do with input of type: {type(fn)}")
Exemplo n.º 4
0
def rinexinfo(f: T.TextIO | Path) -> dict[str, T.Any]:
    """verify RINEX version"""

    if isinstance(f, (str, Path)):
        fn = Path(f).expanduser()

        if fn.suffix == ".nc":
            attrs: dict[str, T.Any] = {"rinextype": []}
            for g in ("OBS", "NAV"):
                try:
                    dat = xarray.open_dataset(fn, group=g)
                    attrs["rinextype"].append(g.lower())
                except OSError:
                    continue
                attrs.update(dat.attrs)
            return attrs

        with opener(fn, header=True) as f:
            return rinexinfo(f)

    f.seek(0)

    try:
        line = first_nonblank_line(f)  # don't choke on binary files

        if line.startswith(("#a", "#c", "#d")):
            return {"version": line[1], "rinextype": "sp3"}

        version = rinex_version(line)[0]
        file_type = line[20]
        if int(version) == 2:
            if file_type == "N":
                system = "G"
            elif file_type == "G":
                system = "R"
            elif file_type == "E":
                system = "E"
            else:
                system = line[40]
        else:
            system = line[40]

        if line[20] in ("O", "C"):
            rinex_type = "obs"
        elif line[20] == "N" or "NAV" in line[20:40]:
            rinex_type = "nav"
        else:
            rinex_type = line[20]

        info = {
            "version": version,
            "filetype": file_type,
            "rinextype": rinex_type,
            "systems": system,
        }

    except (TypeError, AttributeError, ValueError) as e:
        # keep ValueError for consistent user error handling
        raise ValueError(f"not a known/valid RINEX file.  {e}")

    return info
Exemplo n.º 5
0
def obsheader2(
    f: T.TextIO | Path, useindicators: bool = False, meas: list[str] = None
) -> dict[str, T.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

        hd = ln[60:80].strip()
        c = ln[:60]
        # %% measurement types
        if "# / TYPES OF OBSERV" in hd:
            if Nobs == 0:
                Nobs = int(c[:6])
                hdr[hd] = c[6:].split()
            else:
                hdr[hd] += c[6:].split()
        elif hd not in hdr:  # Header label
            hdr[hd] = c  # string with info
        else:  # concatenate
            hdr[hd] += " " + 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: T.Any = np.zeros(len(hdr["fields"]), dtype=bool)
            for m in meas:
                for i, field in enumerate(hdr["fields"]):
                    if field.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