Skip to content

Commit

Permalink
Added support for sp3 version d
Browse files Browse the repository at this point in the history
rio: apply black formatting and py36 f-string

tests: distinguish names of sp3c from sp3d

doc: add sp3d
  • Loading branch information
fmeynadier authored and scivision committed Feb 21, 2020
1 parent 4765331 commit 2acbb6b
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 37 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ where ease of cross-platform install and correctness are primary goals.

![RINEX plot](tests/example_plot.png)


## Input data types

* RINEX 3.x or RINEX 2.x
Expand All @@ -31,7 +30,8 @@ where ease of cross-platform install and correctness are primary goals.
* `.zip`
* Hatanaka compressed RINEX (plain `.crx` or `.crx.gz` etc.)
* Python `io.StringIO` text stream RINEX'
* .sp3 SP3-c ephemeris
* .sp3 [SP3-c](ftp://igs.org/pub/data/format/sp3c.txt) ephemeris
* .sp3d [SP3-d](ftp://ftp.igs.org/pub/data/format/sp3d.pdf) extended ephemeris

## Output

Expand Down
48 changes: 28 additions & 20 deletions georinex/rio.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@


@contextmanager
def opener(fn: typing.Union[TextIO, Path],
header: bool = False) -> TextIO:
def opener(fn: typing.Union[TextIO, Path], header: bool = False) -> TextIO:
"""provides file handle for regular ASCII or gzip files transparently"""
if isinstance(fn, str):
fn = Path(fn).expanduser()
Expand All @@ -47,7 +46,9 @@ def opener(fn: typing.Union[TextIO, Path],
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())
f = io.StringIO(
io.TextIOWrapper(bf, encoding='ascii', errors='ignore').read()
)
yield f
elif fn.suffix == '.Z':
if unlzw is None:
Expand Down Expand Up @@ -79,7 +80,7 @@ def first_nonblank_line(f: TextIO, max_lines: int = 10) -> str:

line = ""
for _i in range(max_lines):
line = f.readline(80)
line = f.readline(81)
if line.strip():
break

Expand Down Expand Up @@ -115,8 +116,9 @@ def rinexinfo(f: typing.Union[Path, TextIO]) -> typing.Dict[str, typing.Any]:
line = first_nonblank_line(f) # don't choke on binary files

if line.startswith('#c'):
return {'version': 'c',
'rinextype': 'sp3'}
return {'version': 'c', 'rinextype': 'sp3'}
elif line.startswith('#d'):
return {'version': 'd', 'rinextype': 'sp3'}

version = rinex_version(line)[0]
file_type = line[20]
Expand All @@ -139,10 +141,12 @@ def rinexinfo(f: typing.Union[Path, TextIO]) -> typing.Dict[str, typing.Any]:
else:
rinex_type = line[20]

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

except (TypeError, AttributeError, ValueError) as e:
# keep ValueError for consistent user error handling
Expand All @@ -158,32 +162,36 @@ def rinex_version(s: str) -> typing.Tuple[typing.Union[float, str], bool]:
----------
s : str
first line of RINEX/CRINEX file
first line of RINEX/CRINEX/SP3 file
Results
-------
version : float
RINEX file version
RINEX/SP3 file version
is_crinex : bool
is it a Compressed RINEX CRINEX Hatanaka file
"""
if not isinstance(s, str):
raise TypeError('need first line of RINEX file as string')
raise TypeError('need first line of RINEX/SP3 file as string')
if len(s) < 2:
raise ValueError(f'cannot decode RINEX version from line:\n{s}')

if len(s) >= 80:
if s[60:80] not in ('RINEX VERSION / TYPE', 'CRINEX VERS / TYPE'):
raise ValueError('The first line of the RINEX file header is corrupted.')
raise ValueError(f'cannot decode RINEX/SP3 version from line:\n{s}')

# %% .sp3 file
if s[0] == '#':
if s[1] != 'c':
raise ValueError('Georinex only handles version C of SP3 files.')
supported_versions = ['c', 'd']
if s[1] not in supported_versions:
raise ValueError(
f"SP3 versions of SP3 files currently handled: {','.join(supported_versions)}"
)
return 'sp3' + s[1], False

# %% typical RINEX files
if len(s) >= 80:
if s[60:80] not in ('RINEX VERSION / TYPE', 'CRINEX VERS / TYPE'):
raise ValueError('The first line of the RINEX file header is corrupted.')

try:
vers = float(s[:9]) # %9.2f
except ValueError as err:
Expand Down
19 changes: 9 additions & 10 deletions georinex/sp3.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,18 @@ def load_sp3(fn: Path, outfn: Path) -> xarray.Dataset:
dat["agency"] = ln[56:60]

f.readline()

ln = f.readline()
assert ln[0] == "+", f"failed to read {fn} SV header"
Nsv = int(ln[4:6])
# version c : Nsv <= 85, int(ln[4:6])
# version d : Nsv <= 999, int(len[3:6])
# (see ftp://igs.org/pub/data/format/sp3d.pdf)
# So this should work for both versions
Nsv = int(ln[3:6])
svs = get_sv(ln, Nsv)
if Nsv > 17:
svs += get_sv(f.readline(), Nsv - 17)
if Nsv > 34:
svs += get_sv(f.readline(), Nsv - 34)
if Nsv > 51:
svs += get_sv(f.readline(), Nsv - 51)
if Nsv > 68:
svs += get_sv(f.readline(), Nsv - 68)
unread_sv = Nsv - 17
while unread_sv > 0:
svs += get_sv(f.readline(), unread_sv)
unread_sv -= 17
# let us know if you need these intermediate lines parsed
for ln in f:
if ln.startswith("*"):
Expand Down
5 changes: 3 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
[metadata]
name = georinex
version = 1.14.0
version = 1.14.1
author = Michael Hirsch, Ph.D.
author_email = [email protected]
description = Python RINEX 2/3 NAV/OBS reader with speed and simplicity.
url = https://github.com/scivision/georinex
keywords =
RINEX
sp3
HDF5
NetCDF
NetCDF4
classifiers =
Development Status :: 5 - Production/Stable
Environment :: Console
Expand Down
File renamed without changes.
144 changes: 144 additions & 0 deletions tests/data/minimal.sp3d
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#dP2020 1 24 0 0 0.00000000 288 u+U IGS14 FIT GFZ
## 1 432000.00000000 300.00000000 58872 0.0000000000000
+ 116 C01C02C03C04C05C06C07C08C09C10C11C12C13C14C16C18C19
+ C20C21C22C23C24C25C26C27C28C29C30C33C34C37C39C40C45
+ C46E01E02E03E04E05E07E08E09E11E12E13E14E15E18E19E21
+ E24E25E26E27E30E31E33E36G01G02G03G04G05G06G07G08G09
+ G10G11G12G13G14G15G16G17G18G19G20G21G22G23G24G25G26
+ G27G28G29G30G31G32J01J02J03J07R01R02R03R04R05R07R08
+ R09R11R12R13R14R15R16R17R18R19R20R21R22R23 00 00 00
++ 10 10 10 0 10 7 10 8 10 7 7 10 7 10 10 7 7
++ 8 9 7 8 10 7 8 9 5 7 0 10 0 0 0 0 0
++ 9 6 7 6 6 5 5 6 7 6 6 5 5 6 6 5 5
++ 5 5 6 7 5 7 6 10 8 5 6 6 5 7 7 5 8
++ 7 6 6 6 5 6 6 8 6 8 5 6 8 7 6 5 6
++ 5 7 6 6 5 7 10 10 10 10 7 6 10 7 0 6 6
++ 0 8 8 7 7 10 8 5 10 9 7 5 6 0 7 7 7
%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc
%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc
%f 1.2500000 1.025000000 0.00000000000 0.000000000000000
%f 0.0000000 0.000000000 0.00000000000 0.000000000000000
%i 0 0 0 0 0 0 0 0 0
%i 0 0 0 0 0 0 0 0 0
/* PCV:IGS14_2080 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN
/* GeoForschungsZentrum Potsdam
/* (manually truncated)
/*
* 2020 1 24 0 0 0.00000000
PC01 -32326.678246 27059.067017 -943.313529 116.924578
PC02 4418.028325 41926.253886 997.115101 591.335025
PC03 -14779.087533 39523.780866 78.676697 999999.999999
PC04 -39617.537229 14386.600714 -524.810291 -427.707329
PC05 21816.447161 36100.740384 1278.561603 -230.006748
PC06 -5931.749645 31517.533056 27927.583282 256.017123
PC07 -19819.367535 23615.259976 -28774.312923 70.911304
PC08 -19855.144501 36896.247360 2577.935608 -93.227872
PC09 4006.928825 38092.912646 18260.234725 282.954028
PC10 -4686.877182 25993.337451 -32723.844565 -589.981773
PC11 10084.204350 -17083.919906 19714.120842 -139.127616
PC12 15912.782038 3613.252845 22674.870975 311.808015
PC13 -10335.759303 38007.465278 14754.379570 223.902013
PC14 17216.334580 -8987.727258 -19994.739549 -805.256024
PC16 -3802.852272 32168.388316 27125.289266 -826.780071
PC18 7311.496144 41522.365116 -955.335648 66.900456
PC19 -7868.145691 14084.836330 22791.553153 306.202098
PC20 -23592.308126 2889.170281 14654.926218 -602.019042
PC21 25745.607921 10454.104408 2494.291618 -7.484447
PC22 12688.888393 17325.372427 17808.155988 -728.489514
PC23 -2186.471591 -17967.122431 21242.631189 -876.657402
PC24 23598.084540 -12298.953731 -8464.171282 -907.157829
PC25 14409.474116 -21977.594916 9414.468803 -491.646985
PC26 18568.788661 2930.864221 -20640.767896 545.087503
PC27 -15537.023738 -13894.225785 -18554.930865 265.909629
PC28 -7249.933176 -26723.009119 -3498.565392 256.858417
PC29 -4315.004166 24046.856673 -13489.450530 177.907992
PC30 -14523.066821 7262.865333 -22692.328873 260.896413
PC33 7860.054171 -14118.370339 -22747.803973 -819.454388
PC34 14470.984285 -7613.870830 22611.453760 -414.363668
PC37 -18074.001278 -3946.240601 20902.796935 -905.774733
PC39 -980.525220 32671.633922 26679.122074 8.162284
PC40 -11877.888108 20675.420368 -34811.197218 999999.999999
PC45 2145.079302 17890.690417 -21299.715715 999999.999999
PC46 -23469.954560 12631.424808 8208.654462 999999.999999
PE01 26898.080012 -11410.808392 4773.086786 -779.893211
PE02 -26850.654571 11449.865327 -4848.807646 107.836085
PE03 -18049.989431 5313.451325 22847.162648 -258.384182
PE04 1148.816065 -20068.982634 -21728.255828 -451.503859
PE05 -24545.997398 -12709.525399 10586.129668 -411.491367
PE07 16659.131301 23191.731213 7781.760816 -331.061180
PE08 -740.021159 20069.625419 21737.582114 6230.511300
PE09 -16914.302452 -23033.065260 -7745.708834 6179.158461
PE11 -5478.647706 -24240.603138 -16064.797734 931.442234
PE12 7939.382509 -28410.172101 2358.987428 5986.615945
PE13 5389.175093 24367.646037 15932.902597 397.689569
PE14 -21754.232715 868.930098 -24175.669456 -878.981588
PE15 -7649.309681 28516.820748 -2109.748127 879.794125
PE18 16188.041804 1675.889898 16879.566353 -975.846467
PE19 19301.548343 -3278.187206 -22203.978647 -1.832067
PE21 26228.497812 1498.630544 -13647.911806 -578.689388
PE24 -10037.293799 -13826.988183 24156.976921 5647.424658
PE25 -26168.572507 -1581.359286 13741.232815 -501.969576
PE26 15496.414820 5742.764220 24568.876204 3611.903469
PE27 10178.824398 13697.921333 -24184.083319 297.388914
PE30 -11998.979896 17780.839106 -20396.561624 4200.520462
PE31 11798.804258 -17818.678313 20490.329553 -471.417516
PE33 16232.325282 -15914.782589 18969.166498 -470.788601
PE36 -15274.457104 -5921.227577 -24639.679884 607.478604
PG01 14421.622181 -21978.632467 2017.797832 -272.082956
PG02 -17266.702497 5080.293639 -18862.008266 -391.555488
PG03 13193.736063 -13139.230909 -18961.924693 -78.673264
PG04 5163.174823 -18099.820603 -18726.041965 -38.668313
PG05 -26452.767558 2399.982684 2221.628727 -6.527516
PG06 -12205.234719 -8453.601301 -21968.559865 -190.184016
PG07 2245.723961 -22659.552025 13548.752876 -198.713411
PG08 12466.283308 -8418.413125 21915.635222 -21.264585
PG09 -4106.202393 -23380.694448 -11957.370785 -139.382120
PG10 16897.671904 11716.579576 17013.780937 -220.167160
PG11 11076.502818 -20426.889649 12623.430449 -381.544502
PG12 -17146.561533 10560.971003 -17452.134177 159.077551
PG13 -16047.364668 -736.311246 21040.672466 -17.888222
PG14 17466.336500 14543.469611 -13562.864339 -36.929991
PG15 -11885.249644 10429.094294 21018.519943 -256.522628
PG16 26701.849402 990.435118 1977.216427 -116.778515
PG17 -14374.839805 -21835.721740 -4428.650515 203.845861
PG18 16692.739163 -17928.788217 9643.839317 115.314455
PG19 -16686.988728 -16630.699358 -12548.556050 -208.395678
PG20 6105.794028 14916.861196 21040.918370 527.828406
PG21 1226.074266 22282.730938 15369.225644 -51.355055
PG22 20240.933498 -11201.488959 -12729.410690 -782.873550
PG23 3582.409337 -18907.095529 -17788.937797 -141.131733
PG24 -14432.400563 21582.814311 4391.569959 -7.985303
PG25 -3274.175776 15044.035642 -21842.817935 -15.325859
PG26 25142.487813 4650.925241 -7621.767452 133.605262
PG27 17489.005259 3803.028107 19614.437137 -190.101136
PG28 -16742.924625 -12773.331852 16694.715816 743.560056
PG29 2250.195924 23326.988032 -12512.525168 -15.099597
PG30 -6961.896101 -16219.426731 19820.171523 -136.292725
PG31 14278.499285 5863.841417 -21675.756883 -18.522858
PG32 16264.117742 20436.923241 -5151.636671 193.240258
PJ01 -27436.837036 22523.691853 27836.237339 -325.296824
PJ02 -36156.314455 20607.903202 -2362.252577 -0.909197
PJ03 -18745.232439 26042.036012 -22663.186293 -0.194434
PJ07 -25399.431174 33655.290986 14.474069 -0.028234
PR01 -9791.945067 7045.617530 22473.585154 56.048253
PR02 10809.402171 15495.991269 17202.289338 408.561797
PR03 21869.312676 13104.747475 1461.571051 8.770882
PR04 20327.839310 3309.704354 -15031.487623 36.374727
PR05 7284.704617 -8281.323766 -22995.963903 43.693390
PR07 -22000.844160 -12838.441603 -788.068983 -32.352237
PR08 -20355.706099 -3247.143411 15062.943923 12.913837
PR09 -1848.822532 -11740.377793 22575.771808 113.038275
PR11 -22647.768687 9730.621610 6652.315588 -11.034102
PR12 -17289.216074 15802.461169 -10041.530055 99.517221
PR13 -1482.561452 13043.953184 -21888.197206 -33.907456
PR14 14855.973808 2645.921308 -20554.892278 47.402612
PR15 22583.880704 -9207.451119 -7472.209258 105.756300
PR16 16783.958243 -16269.857979 10339.598694 -1.112912
PR17 8674.379228 14962.200090 18750.523961 294.950377
PR18 10424.755901 -3015.293420 23102.076518 22.615043
PR19 7189.952117 -19113.043891 15295.452630 -75.529967
PR20 -1021.383433 -25233.633673 -3406.630594 -402.261089
PR21 -8790.273829 -14258.050318 -19239.842680 -100.229406
PR22 -10130.008362 6316.264416 -22546.932490 -11.049888
PR23 -4425.741164 23101.252280 -9872.967273 281.775910
EOF
15 changes: 12 additions & 3 deletions tests/test_sp3.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
R = Path(__file__).parent / "data"


def test_data():
def test_sp3c():
dat = gr.load(R / "igs19362.sp3")

d0 = dat.sel(time="2017-02-14T00:15:00")
Expand All @@ -31,8 +31,8 @@ def test_header():
gr.load(R / "header.sp3")


def test_minimal():
dat = gr.load(R / "minimal.sp3")
def test_minimal_sp3c():
dat = gr.load(R / "minimal.sp3c")

d0 = dat.sel(time="2017-02-14T00:00:00")

Expand All @@ -44,6 +44,15 @@ def test_minimal():
assert G20.clock.item() == approx(459.944522)


def test_minimal_sp3d():
dat = gr.load(R / "minimal.sp3d")
d0 = dat.sel(time="2020-01-24T00:00:00")
assert len(d0.sv) == 116
E21 = d0.sel(sv="E21")
assert E21["position"].values == approx(
[26228.497812, 1498.630544, -13647.911806])
assert E21.clock.item() == approx(-578.689388)

# perhaps not a valid test?
# def test_truncated():
# dat = gr.load(R / "truncated.sp3")
Expand Down

0 comments on commit 2acbb6b

Please sign in to comment.