Source code for astroforge.coordinates.iers._iers_utils
# Copyright (c) 2024 Massachusetts Institute of Technology
# SPDX-License-Identifier: MIT
import os
import sys
from datetime import datetime
import numpy as np
import requests
from . import (
CURRENT_IERS_FILENAME,
IERS_URL,
LEAPSECOND_FILENAME,
POLARMOTION_FILENAME,
UT1_UTC_FILENAME,
_datadir,
)
[docs]
def download_iers():
"""Download and save the IERS Bulletin A file.
The Bulletin A file can be found `here <https://datacenter.iers.org/data/9/finals2000A.all>`_.
"""
print(f"downloading IERS file from \n\t{IERS_URL}")
# format filename
dt = datetime.now()
newfname = _datadir / f"finalsAll_{dt.year:4d}-{dt.month:02d}-{dt.day:02d}.txt"
print(f"Saving to {newfname}")
if not os.path.exists(_datadir):
os.makedirs(_datadir)
# download the IERS file
try:
with open(newfname, "wb") as f:
response = requests.get(IERS_URL, stream=True)
total_length = response.headers.get("content-length")
if total_length is None: # no content length header
f.write(response.content)
else:
dl = 0
total_length = int(total_length)
for data in response.iter_content(chunk_size=4096):
dl += len(data)
f.write(data)
done = int(100 * dl / total_length)
sys.stdout.write("\r[%s%s]" % ("=" * done, " " * (100 - done)))
sys.stdout.flush()
print()
except:
raise Exception("Failed to download IERS file")
# link the downloaded file to be the "current" file
try:
fname = CURRENT_IERS_FILENAME
print(f"Making symbolic link to {fname}")
if os.path.exists(fname) and os.path.islink(fname):
# NOTE: I understand there is technically a race condition here
# (it is possible that another process creates a link to the file
# between the time I unlink it and relink it). That's not likely,
# so I'm not worrying about it
os.unlink(fname)
os.symlink(newfname, fname)
except:
raise Exception("Failed to create symlink")
[docs]
def parse_iers():
"""Parse the IERS file into more readily usable intermediate files.
This function saves three files with
1) polar motion data
2) UT1-UTC data
3) leap second data
"""
# parse the MJD, pm-x, pm-y, ut1-utc data from the file
with open(CURRENT_IERS_FILENAME, "r") as f:
lines = f.readlines()
N = len(lines)
mjd = np.nan * np.ones((N,))
xp = np.nan * np.ones((N,))
yp = np.nan * np.ones((N,))
ut1utc = np.nan * np.ones((N,))
for n, line in enumerate(lines):
try:
mjd[n] = float(line[6:16])
xp[n] = float(line[18:28])
yp[n] = float(line[37:47])
ut1utc[n] = float(line[58:68])
except:
pass
# determine leap seconds
with np.errstate(invalid="ignore"):
leapseconds = 12 + ut1utc - np.unwrap(ut1utc * 2 * np.pi) / 2 / np.pi
# save to file
np.savez(
POLARMOTION_FILENAME,
mjd=mjd[~np.isnan(xp)],
xp=xp[~np.isnan(xp)],
yp=yp[~np.isnan(yp)],
)
np.savez(
UT1_UTC_FILENAME, mjd=mjd[~np.isnan(ut1utc)], ut1utc=ut1utc[~np.isnan(ut1utc)]
)
np.savez(
LEAPSECOND_FILENAME,
mjd=mjd[~np.isnan(leapseconds)],
leapseconds=leapseconds[~np.isnan(leapseconds)].astype(int),
)
[docs]
def setup_iers(download=True):
"""Utility function for setting up the IERS utilities.
Parameters
----------
download : bool, optional
Flag indicating whether or not to download the IERS bulletin A file, by default True
Raises
------
FileNotFoundError
Raised if there is not a local copy of the IERS file.
"""
if download:
download_iers()
if not os.path.exists(CURRENT_IERS_FILENAME):
raise FileNotFoundError(
(
f"Trying to access the *current* IERS file at\n"
f" {CURRENT_IERS_FILENAME}\n"
f"but that file does not exist"
)
)
parse_iers()