Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • lisa-simulation/instrument
1 result
Show changes
Commits on Source (17)
node: $Format:%H$
node-date: $Format:%cI$
describe-name: $Format:%(describe:tags=true)$
ref-names: $Format:%D$
.gitarchive export-subst
tests/keplerian-orbits-1-0-2.h5 filter=lfs diff=lfs merge=lfs -text
tests/esa-orbits-1-0-2.h5 filter=lfs diff=lfs merge=lfs -text
tests/keplerian-orbits-2-0.h5 filter=lfs diff=lfs merge=lfs -text
......
......@@ -4,19 +4,64 @@ image: python:3.8
stages:
- test
- generate
- document
- build
- deploy
before_script:
- pip install -r requirements.txt
- pip install .
variables:
GIT_LFS_SKIP_SMUDGE: 1
cache:
paths:
- public
pylint:
script:
- pylint lisainstrument
pytest:
variables:
GIT_LFS_SKIP_SMUDGE: 0
script:
- pip install .
- python -m pytest -v --junitxml=report.xml
artifacts:
reports:
junit: report.xml
dist:
stage: build
script:
- python -m build
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+(?:\.\d+){0,2}$/'
artifacts:
paths:
- dist
tag-latest:
stage: deploy
image: datamachines/git-lfs
before_script:
- git config http.sslverify false
- git config user.email "${GITLAB_USER_EMAIL}"
- git config user.name "${GITLAB_USER_NAME}"
- git remote set-url origin https://gitlab-ci:${GITLAB_CI_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git
script:
- git tag --force latest
- git push --force origin latest
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+(?:\.\d+){0,2}$/'
pypi:
stage: deploy
script:
- twine check dist/*
- twine upload dist/*
dependencies:
- dist
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+(?:\.\d+){0,2}$/'
prune docs
prune tests
exclude .*
exclude requirements.txt
exclude zenodo.py
......@@ -2,9 +2,16 @@
# -*- coding: utf-8 -*-
"""LISA Instrument module."""
from .meta import __version__
from .meta import __author__
from .meta import __email__
import importlib_metadata
from .instrument import Instrument
from .hexagon import Hexagon
try:
metadata = importlib_metadata.metadata('lisainstrument').json
__version__ = importlib_metadata.version('lisainstrument')
__author__ = metadata['author']
__email__ = metadata['author_email']
except importlib_metadata.PackageNotFoundError:
pass
......@@ -12,6 +12,7 @@ import re
import logging
import numpy as np
import matplotlib.pyplot as plt
import importlib_metadata
from h5py import File
from scipy.signal import lfilter, kaiserord, firwin
......@@ -25,7 +26,6 @@ from lisaconstants import c
from .containers import ForEachSC
from .containers import ForEachMOSA
from . import meta
from . import dsp
from . import noises
......@@ -91,7 +91,8 @@ class Instrument:
sync_asds=0.42,
# Tilt-to-length (TTL)
ttl_coeffs='default',
sc_jitter_asds=(5E-9, 5E-9, 5E-9), mosa_jitter_asds=(2E-9, 1e-9), mosa_angles='default',
sc_jitter_asds=(5E-9, 5E-9, 5E-9), sc_jitter_fknees=(8E-4, 8E-4, 8E-4),
mosa_jitter_asds=(2E-9, 1e-9), mosa_jitter_fknees=(8E-4, 8E-4), mosa_angles='default',
dws_asds=7E-8/335,
# Pseudo-ranging
ranging_biases=0, ranging_asds=3E-9, prn_ambiguity=None,
......@@ -159,12 +160,17 @@ class Instrument:
oms_fknees: dictionary of cutoff frequencies for OMS noise
ttl_coeffs: tuple (local_phi, distant_phi, local_eta, distant_eta) of dictionaries of
tilt-to-length coefficients on each MOSA [m/rad], 'default' for a default set of
coefficients, or 'random' to randomly generate a set of coefficients in a uniform
distribution [-2.3, 2.3] mm/rad
coefficients, or 'random' to draw a set of coefficients from uniform distributions
(LISA-UKOB-INST-ML-0001-i2 LISA TTL STOP Model, summary table, 2.4 mm/rad and
2.2mm/rad for distant and local coefficients, respectively)
sc_jitter_asds: tuple of dictionaries of angular jitter amplitude spectral densities
for spacecraft, ordered as (yaw, pitch, roll) [rad/sqrt(Hz)]
sc_jitter_fknees: tuple of dictionaries of cutoff frequencies for spacecraft angular jitter,
ordered as (yaw, pitch, roll) [Hz]
mosa_jitter_asds: tuple of dictionaries of angular jitter amplitude spectral densities
for MOSA, ordered as (yaw, pitch) [rad/sqrt(Hz)]
mosa_jitter_fknees: tuple of dictionaries of cutoff frequencies for MOSA angular jitter,
ordered as (yaw, pitch) [Hz]
mosa_angles: dictionary of oriented MOSA opening angles [deg], or 'default'
dws_asds: dictionary of amplitude spectral densities for DWS measurement noise [rad/sqrt(Hz)]
ranging_biases: dictionary of ranging noise bias [s]
......@@ -176,7 +182,7 @@ class Instrument:
# pylint: disable=too-many-arguments,too-many-statements,too-many-locals,too-many-branches
logger.info("Initializing instrumental simulation")
self.git_url = 'https://gitlab.in2p3.fr/lisa-simulation/instrument'
self.version = meta.__version__
self.version = importlib_metadata.version('lisainstrument')
self.simulated = False
# Check orbit dataset
......@@ -236,6 +242,11 @@ class Instrument:
np.arange(self.physics_size + self.initial_telemetry_physics_size, dtype=np.float64) * self.physics_dt
self.physics_et_withinitial = self.physics_t_withinitial - self.t0
# Orbits, gravitational waves, glitches
self.init_orbits(orbits, orbit_dataset)
self.init_gws(gws)
self.init_glitches(glitches)
# Instrument topology
self.central_freq = float(central_freq)
self.init_lock(lock)
......@@ -337,29 +348,28 @@ class Instrument:
# Tilt-to-length
if ttl_coeffs == 'default':
# Default values drawn from uniform distribution [-2.3, 2.3] mm/rad
# for local and distant coeffs using `np.random.uniform()`
# Default values drawn from distributions set in 'random'
self.ttl_coeffs_local_phis = ForEachMOSA({
'12': -3.79868389e-04, '23': 1.27445665e-03, '31': -8.19115231e-04,
'13': -5.03792762e-05, '32': 8.24348407e-04, '21': 1.80772079e-03,
'12': 2.005835e-03, '23': 2.105403e-04, '31': -1.815399e-03,
'13': -2.865050e-04, '32': -1.986657e-03, '21': 9.368319e-04,
})
self.ttl_coeffs_distant_phis = ForEachMOSA({
'12': -1.24812926e-03, '23': 7.49010812e-04, '31': -6.50959027e-05,
'13': -1.33360362e-03, '32': 6.12997141e-04, '21': -2.53955346e-04,
'12': 1.623910e-03, '23': 1.522873e-04, '31': -1.842871e-03,
'13': -2.091585e-03, '32': 1.300866e-03, '21': -8.445374e-04,
})
self.ttl_coeffs_local_etas = ForEachMOSA({
'12': -6.26115149e-04, '23': -2.13028857e-04, '31': 6.62915985e-04,
'13': 5.46940002e-04, '32': -9.50450080e-04, '21': 2.72098575e-04,
'12': -1.670389e-03, '23': 1.460681e-03, '31': -1.039064e-03,
'13': 1.640473e-04, '32': 1.205353e-03, '21': -9.205764e-04,
})
self.ttl_coeffs_distant_etas = ForEachMOSA({
'12': 1.87659513e-03, '23': -1.26813952e-03, '31': 8.64052482e-04,
'13': -4.54433476e-04, '32': -1.46396776e-03, '21': -2.06925864e-04,
'12': -1.076470e-03, '23': 5.228848e-04, '31': -5.662766e-05,
'13': 1.960050e-03, '32': 9.021890e-04, '21': 1.908239e-03,
})
elif ttl_coeffs == 'random':
self.ttl_coeffs_local_phis = ForEachMOSA(lambda _: np.random.uniform(-2.3E-3, 2.3E-3))
self.ttl_coeffs_distant_phis = ForEachMOSA(lambda _: np.random.uniform(-2.3E-3, 2.3E-3))
self.ttl_coeffs_local_etas = ForEachMOSA(lambda _: np.random.uniform(-2.3E-3, 2.3E-3))
self.ttl_coeffs_distant_etas = ForEachMOSA(lambda _: np.random.uniform(-2.3E-3, 2.3E-3))
self.ttl_coeffs_local_phis = ForEachMOSA(lambda _: np.random.uniform(-2.2E-3, 2.2E-3))
self.ttl_coeffs_distant_phis = ForEachMOSA(lambda _: np.random.uniform(-2.4E-3, 2.4E-3))
self.ttl_coeffs_local_etas = ForEachMOSA(lambda _: np.random.uniform(-2.2E-3, 2.2E-3))
self.ttl_coeffs_distant_etas = ForEachMOSA(lambda _: np.random.uniform(-2.4E-3, 2.4E-3))
else:
self.ttl_coeffs_local_phis = ForEachMOSA(ttl_coeffs[0])
self.ttl_coeffs_distant_phis = ForEachMOSA(ttl_coeffs[1])
......@@ -368,8 +378,13 @@ class Instrument:
self.sc_jitter_phi_asds = ForEachSC(sc_jitter_asds[0])
self.sc_jitter_eta_asds = ForEachSC(sc_jitter_asds[1])
self.sc_jitter_theta_asds = ForEachSC(sc_jitter_asds[2])
self.sc_jitter_phi_fknees = ForEachSC(sc_jitter_fknees[0])
self.sc_jitter_eta_fknees = ForEachSC(sc_jitter_fknees[1])
self.sc_jitter_theta_fknees = ForEachSC(sc_jitter_fknees[2])
self.mosa_jitter_phi_asds = ForEachMOSA(mosa_jitter_asds[0])
self.mosa_jitter_eta_asds = ForEachMOSA(mosa_jitter_asds[1])
self.mosa_jitter_phi_fknees = ForEachMOSA(mosa_jitter_fknees[0])
self.mosa_jitter_eta_fknees = ForEachMOSA(mosa_jitter_fknees[1])
self.dws_asds = ForEachMOSA(dws_asds)
# MOSA opening angles
......@@ -382,11 +397,6 @@ class Instrument:
else:
self.mosa_angles = ForEachMOSA(mosa_angles)
# Orbits, gravitational waves, glitches
self.init_orbits(orbits, orbit_dataset)
self.init_gws(gws)
self.init_glitches(glitches)
# Interpolation and antialiasing filter
self.init_interpolation(interpolation)
self.init_aafilter(aafilter)
......@@ -534,7 +544,7 @@ class Instrument:
# Switch between various fplan file standards
if version in SpecifierSet('== 1.1.*', True):
logger.debug("Interpolating locking beatnote frequencies with piecewise linear functions")
times = self.t0 + np.arange(fplanf.attrs['size']) * fplanf.attrs['dt']
times = self.orbit_t0 + np.arange(fplanf.attrs['size']) * fplanf.attrs['dt']
interpolate = lambda x: InterpolatedUnivariateSpline(times, x, k=1, ext='raise')(self.physics_t)
lock_beatnotes = {}
# Go through all MOSAs and pick locking beatnotes
......@@ -571,6 +581,7 @@ class Instrument:
if orbits == 'static':
logger.info("Using default set of static proper pseudo-ranges")
self.orbit_file = None
self.orbit_t0 = self.t0
self.pprs = ForEachMOSA({
# Default PPRs based on first samples of Keplerian orbits (v2.0.dev)
'12': 8.33242295, '23': 8.30282196, '31': 8.33242298,
......@@ -599,6 +610,7 @@ class Instrument:
else:
logger.info("Using user-provided proper pseudo-ranges and derivatives")
self.orbit_file = None
self.orbit_t0 = self.t0
self.pprs = ForEachMOSA(orbits)
self.d_pprs = self.pprs.transformed(lambda _, x:
0 if np.isscalar(x) else np.gradient(x, self.physics_dt)
......@@ -641,6 +653,13 @@ class Instrument:
raise ValueError(f"invalid orbit dataset '{self.orbit_dataset}'")
try:
logger.debug("Reading orbit's t0")
if self.orbit_dataset == 'tcb/ltt':
self.orbit_t0 = orbitf['tcb']['t'][0]
elif self.orbit_dataset == 'tps/ppr':
self.orbit_t0 = orbitf['tps']['tau'][0]
else:
raise ValueError(f"invalid orbit dataset '{self.orbit_dataset}'")
logger.debug("Interpolating proper pseudo-ranges")
self.pprs = ForEachMOSA(lambda mosa: pprs(mosa)(self.physics_t))
logger.debug("Interpolating proper pseudo-range derivatives")
......@@ -663,6 +682,8 @@ class Instrument:
# Interpolate necessary orbital quantities,
# show a helpful error message if orbit file is too short
try:
logger.debug("Reading orbit's t0")
self.orbit_t0 = orbitf.attrs['t0']
logger.debug("Interpolating proper pseudo-ranges")
dataset = orbitf['tcb/ltt'] if self.orbit_dataset == 'tcb/ltt' else orbitf['tps/ppr']
self.pprs = ForEachMOSA(lambda mosa: interpolate(dataset[:, link_index[mosa]], self.physics_t))
......@@ -1513,22 +1534,27 @@ class Instrument:
logger.info("Generating spacecraft angular jitters")
self.sc_jitter_phis = ForEachSC(lambda sc:
noises.jitter(self.physics_fs, self.physics_size, self.sc_jitter_phi_asds[sc])
noises.jitter(self.physics_fs, self.physics_size,
self.sc_jitter_phi_asds[sc], self.sc_jitter_phi_fknees[sc])
)
self.sc_jitter_etas = ForEachSC(lambda sc:
noises.jitter(self.physics_fs, self.physics_size, self.sc_jitter_eta_asds[sc])
noises.jitter(self.physics_fs, self.physics_size,
self.sc_jitter_eta_asds[sc], self.sc_jitter_eta_fknees[sc])
)
self.sc_jitter_thetas = ForEachSC(lambda sc:
noises.jitter(self.physics_fs, self.physics_size, self.sc_jitter_theta_asds[sc])
noises.jitter(self.physics_fs, self.physics_size,
self.sc_jitter_theta_asds[sc], self.sc_jitter_theta_fknees[sc])
)
logger.info("Generating MOSA angular jitters")
self.mosa_jitter_phis = ForEachMOSA(lambda mosa:
noises.jitter(self.physics_fs, self.physics_size, self.mosa_jitter_phi_asds[mosa])
noises.jitter(self.physics_fs, self.physics_size,
self.mosa_jitter_phi_asds[mosa], self.mosa_jitter_phi_fknees[mosa])
)
self.mosa_jitter_etas = ForEachMOSA(lambda mosa:
noises.jitter(self.physics_fs, self.physics_size, self.mosa_jitter_eta_asds[mosa])
noises.jitter(self.physics_fs, self.physics_size,
self.mosa_jitter_eta_asds[mosa], self.mosa_jitter_eta_fknees[mosa])
)
logger.info("Computing MOSA total angular jitters")
......@@ -1783,7 +1809,7 @@ class Instrument:
'sc_jitter_phi_asds', 'sc_jitter_eta_asds',
'sc_jitter_theta_asds', 'mosa_jitter_phi_asds',
'dws_asds', 'mosa_angles',
'orbit_file', 'orbit_dataset',
'orbit_file', 'orbit_dataset', 'orbit_t0',
'gw_file', 'gw_group',
'glitch_file',
'interpolation_order',
......
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# pylint: disable=missing-module-docstring
__version__ = '1.2.dev'
__author__ = 'Jean-Baptiste Bayle'
__email__ = 'j2b.bayle@gmail.com'
......@@ -266,7 +266,7 @@ def oms(fs, size, asd, fknee):
Multiplying by (2π f / c)^2 to express it as fractional frequency deviations,
S_oms(f) [ffd] = (2π asd / c)^2 [ f^2 + (fknee^4 / f^2) ]
= (2π asd / c)^2 f^2 + (2π asd fknee^2 / c)^2 f^(-2).
= (2π asd / c)^2 f^2 + (2π asd fknee^2 / c)^2 f^(-2).
Note that the level of this noise depends on the interferometer and the type of beatnote.
......@@ -283,22 +283,26 @@ def oms(fs, size, asd, fknee):
return violet(fs, size, 2 * pi * asd / c) \
+ red(fs, size, 2 * pi * asd * fknee**2 / c)
def jitter(fs, size, asd):
def jitter(fs, size, asd, fknee):
"""Generate jitter for one angular degree of freedom.
The power spectral density in angle is given by
S_jitter(f) [rad] = asd^2,
S_jitter(f) [rad] = asd^2 [ 1 + (fknee / f)^4 ],
which is converted to angular velocity by mutliplying by (2π f)^2,
S_jitter(f) [rad/s] = (2π asd)^2 f^2.
S_jitter(f) [rad/s] = (2π asd)^2 [ f^2 + (fknee^4 / f^2) ]
= (2π asd)^2 f^2 + (2π asd fknee^2)^2 f^(-2).
Args:
asd: amplitude spectral density [rad/sqrt(Hz)]
fknee: cutoff frequency [Hz]
"""
logger.debug("Generating jitter (fs=%s Hz, size=%s, asd=%s rad/sqrt(Hz))", fs, size, asd)
return violet(fs, size, 2 * pi * asd)
logger.debug("Generating jitter (fs=%s Hz, size=%s, asd=%s rad/sqrt(Hz), fknee=%s Hz)",
fs, size, asd, fknee)
return violet(fs, size, 2 * pi * asd) \
+ red(fs, size, 2 * pi * asd * fknee**2)
def dws(fs, size, asd):
"""Generate DWS measurement noise.
......
......@@ -13,6 +13,7 @@ bleach==5.0.0
certifi==2021.10.8
cffi==1.15.0
charset-normalizer==2.0.12
commonmark==0.9.1
cycler==0.11.0
debugpy==1.6.0
decorator==5.1.1
......@@ -23,7 +24,7 @@ entrypoints==0.4
executing==0.8.3
fastjsonschema==2.15.3
fonttools==4.31.2
h5py==3.6.0
h5py==3.7.0
healpy==1.15.2
idna==3.3
imagesize==1.3.0
......@@ -33,12 +34,14 @@ ipykernel==6.11.0
ipython==8.2.0
ipython-genutils==0.2.0
isort==5.10.1
jaraco.classes==3.2.3
jedi==0.18.1
Jinja2==3.1.1
jsonschema==4.6.0
jupyter-client==7.2.1
jupyter-core==4.9.2
jupyterlab-pygments==0.2.2
keyring==23.9.3
kiwisolver==1.4.1
lazy-object-proxy==1.7.1
lisaconstants==1.3
......@@ -50,6 +53,7 @@ mccabe==0.7.0
mdit-py-plugins==0.3.0
mdurl==0.1.0
mistune==0.8.4
more-itertools==9.0.0
mypy==0.961
mypy-extensions==0.4.3
myst-parser==0.17.0
......@@ -58,13 +62,14 @@ nbconvert==6.5.0
nbformat==5.4.0
nest-asyncio==1.5.4
notebook==6.4.12
numpy==1.22.3
numpy==1.23.4
packaging==21.3
pandocfilters==1.5.0
parso==0.8.3
pexpect==4.8.0
pickleshare==0.7.5
Pillow==9.0.1
pkginfo==1.8.3
platformdirs==2.5.1
pluggy==1.0.0
prometheus-client==0.14.1
......@@ -84,9 +89,14 @@ python-dateutil==2.8.2
pytz==2022.1
PyYAML==6.0
pyzmq==22.3.0
readme-renderer==37.2
requests==2.27.1
requests-toolbelt==0.10.0
rfc3986==2.0.0
rich==12.6.0
scipy==1.8.0
Send2Trash==1.8.0
setuptools-scm==7.0.5
six==1.16.0
snowballstemmer==2.2.0
soupsieve==2.3.2.post1
......@@ -105,6 +115,7 @@ tomli==2.0.1
tomlkit==0.11.0
tornado==6.1
traitlets==5.2.2.post1
twine==4.0.1
typing_extensions==4.1.1
urllib3==1.26.9
wcwidth==0.2.5
......
......@@ -2,26 +2,23 @@
# -*- coding: utf-8 -*-
# pylint: disable=line-too-long,missing-module-docstring,exec-used
from typing import Dict
import setuptools
with open("README.md", 'r', encoding='utf-8') as fh:
long_description = fh.read()
META: Dict[str, str] = {}
with open("lisainstrument/meta.py", 'r', encoding='utf-8') as file:
exec(file.read(), META)
setuptools.setup(
name='lisainstrument',
version=META['__version__'],
author=META['__author__'],
author_email=META['__email__'],
use_scm_version=True,
author='Jean-Baptiste Bayle',
author_email='j2b.bayle@gmail.com',
description='LISA Instrument simulates instrumental noises, propagates laser beams, generates measurements and the on-board processing to deliver simulated telemetry data.',
long_description=long_description,
long_description_content_type="text/markdown",
url="https://gitlab.in2p3.fr/lisa-simulation/instrument",
license='BSD-3-Clause',
packages=setuptools.find_packages(),
install_requires=[
'h5py',
......@@ -30,7 +27,9 @@ setuptools.setup(
'matplotlib',
'lisaconstants',
'packaging',
'importlib_metadata',
],
setup_requires=['setuptools_scm'],
tests_require=['pytest'],
python_requires='>=3.7',
)