diff --git a/lisainstrument/lisa.py b/lisainstrument/lisa.py index 80ad1b9bfb08b5d125b6a1baa66d22f57062deea..d0fd9804a4fd5b45f1b79e8d22b243434c0d3603 100644 --- a/lisainstrument/lisa.py +++ b/lisainstrument/lisa.py @@ -26,20 +26,20 @@ class Instrument: MOSAS = ['12', '23', '31', '13', '32', '21'] def __init__(self, size=2592000, dt=0.3, t0=0, - # Inter-spacecraft propagation - orbits=None, pprs=None, d_pprs=None, gws=None, interpolation=('lagrange', 31), - # Lasers - laser_asds=28.2, modulation_asds=1E-14, modulation_fknees=1.5E-2, - central_freq=2.816E14, modulation_freqs=None, offsets_freqs=None, three_lasers=False, - # Clocks - clock_asds=6.32E-14, clock_offsets=None, clock_freqoffsets=None, clock_freqlindrifts=None, - clock_freqquaddrifts=None, clockinv_tolerance=1E-10, clockinv_maxiter=5, - # Optical pathlength noises - backlink_asds=3E-12, backlink_fknees=2E-3, testmass_asds=2.4E-15, testmass_fknees=0.4E-3, - # Pseudo-ranging - ranging_biases=0, ranging_asds=3E-9, - # Physics simulation sampling and filtering - physics_upsampling=10, aafilter=('kaiser', 240, 0.3, 1.35)): + # Inter-spacecraft propagation + orbits=None, pprs=None, d_pprs=None, gws=None, interpolation=('lagrange', 31), + # Lasers + laser_asds=28.2, modulation_asds=1E-14, modulation_fknees=1.5E-2, + central_freq=2.816E14, modulation_freqs=None, offsets_freqs=None, three_lasers=False, + # Clocks + clock_asds=6.32E-14, clock_offsets=None, clock_freqoffsets=None, clock_freqlindrifts=None, + clock_freqquaddrifts=None, clockinv_tolerance=1E-10, clockinv_maxiter=5, + # Optical pathlength noises + backlink_asds=3E-12, backlink_fknees=2E-3, testmass_asds=2.4E-15, testmass_fknees=0.4E-3, + # Pseudo-ranging + ranging_biases=0, ranging_asds=3E-9, + # Physics simulation sampling and filtering + physics_upsampling=10, aafilter=('kaiser', 240, 0.3, 1.35)): """Initialize an instrumental simulation. Args: @@ -78,24 +78,57 @@ class Instrument: ('kaiser', attenuation [dB], f1 [Hz], f2 [Hz]) with attenuation the attenuation in the stopband, and f1 < f2 the frequencies defining the transition band; use `None` for no filter """ - # pylint: disable=too-many-arguments,too-many-locals + # pylint: disable=too-many-arguments self.git_url = 'https://gitlab.in2p3.fr/lisa-simulation/instrument' self.version = meta.__version__ logging.info("Initializing instrumental simulation (dt=%.2f, t0=%.2f, size=%d)", dt, t0, size) + # Measurement sampling self.size = int(size) self.dt = float(dt) self.t0 = float(t0) self.fs = 1 / self.dt - self.laser_asds = self.mosa_dict(laser_asds) - self.modulation_asds = self.mosa_dict(modulation_asds) - self.modulation_fknees = self.mosa_dict(modulation_fknees) - self.central_freq = float(central_freq) + # Physics sampling + self.physics_upsampling = int(physics_upsampling) + self.physics_size = self.size * self.physics_upsampling + self.physics_dt = self.dt / self.physics_upsampling + self.physics_fs = self.fs * self.physics_upsampling + + # Noise parameters + self.init_noises(laser_asds, modulation_asds, modulation_fknees, central_freq, modulation_freqs, + offsets_freqs, three_lasers, clock_asds, clock_offsets, clock_freqoffsets, clock_freqlindrifts, + clock_freqquaddrifts, backlink_asds, backlink_fknees, testmass_asds, testmass_fknees, + ranging_biases, ranging_asds) + + # Clock-noise inversion self.clockinv_tolerance = float(clockinv_tolerance) self.clockinv_maxiter = int(clockinv_maxiter) + + # Interpolation, antialiasing filter and orbits + self.init_interpolation(interpolation) + self.init_aafilter(aafilter) + self.init_orbits(orbits, pprs, d_pprs) + + # Orbits, gravitational waves + self.gws = self.mosa_dict(gws, default=0) + + def init_noises(self, laser_asds, modulation_asds, modulation_fknees, central_freq, modulation_freqs, + offsets_freqs, three_lasers, clock_asds, clock_offsets, clock_freqoffsets, clock_freqlindrifts, + clock_freqquaddrifts, backlink_asds, backlink_fknees, testmass_asds, testmass_fknees, + ranging_biases, ranging_asds): + """Initialize noise parameters. + + See docstring of `__init__()` for more details. + """ + # pylint: disable=too-many-arguments,too-many-locals + self.central_freq = float(central_freq) self.three_lasers = bool(three_lasers) + self.laser_asds = self.mosa_dict(laser_asds) + self.modulation_asds = self.mosa_dict(modulation_asds) + self.modulation_fknees = self.mosa_dict(modulation_fknees) + self.clock_asds = self.sc_dict(clock_asds) self.clock_offsets = self.sc_dict(clock_offsets, default=0) self.clock_freqoffsets = self.sc_dict(clock_freqoffsets, default={ @@ -119,11 +152,19 @@ class Instrument: self.testmass_asds = self.mosa_dict(testmass_asds) self.testmass_fknees = self.mosa_dict(testmass_fknees) - self.physics_upsampling = int(physics_upsampling) - self.physics_size = self.size * self.physics_upsampling - self.physics_dt = self.dt / self.physics_upsampling - self.physics_fs = self.fs * self.physics_upsampling + self.modulation_freqs = self.mosa_dict(modulation_freqs, default={ + # Default based on mission baseline of 2.4 MHz and 2.401 MHz for left and right MOSAs respect + '12': 2.4E9, '23': 2.4E9, '31': 2.4E9, + '13': 2.401E9, '32': 2.401E9, '21': 2.401E9, + }) + + self.offsets_freqs = self.mosa_dict(offsets_freqs, default={ + # Default based on default for LISANode + '12': 8.1E6, '23': 9.2E6, '31': 10.3E6, '13': 1.4E6, '32': -11.6E6, '21': -9.5E6, + }) + def init_interpolation(self, interpolation): + """Initialize interpolation.""" if interpolation is None: logging.info("Disabling interpolation") designed_interpolation = lambda x: x @@ -135,6 +176,8 @@ class Instrument: designed_interpolation = Instrument.design_interpolation(interpolation) self.interpolate = lambda x, shift: x if numpy.isscalar(x) else designed_interpolation(x, shift) + def init_aafilter(self, aafilter): + """Initialize antialiasing filter and downsampling.""" if aafilter is None: logging.info("Disabling antialiasing filter") designed_aafilter = lambda x: x @@ -148,6 +191,8 @@ class Instrument: self.downsampled = lambda x: x if numpy.isscalar(x) else x[::self.physics_upsampling] + def init_orbits(self, orbits, pprs, d_pprs): + """Initialize orbits.""" if orbits is not None: self.orbits_path = str(orbits) self.orbits = h5py.File(self.orbits_path, 'r') @@ -167,20 +212,6 @@ class Instrument: '13': -3.1977E-9, '32': -5.0252E-9, '21': 3.1977E-9, }) - # No gravitational-wave signal by default - self.gws = self.mosa_dict(gws, default=0) - - # Default based on mission baseline of 2.4 MHz and 2.401 MHz for left and right MOSAs respect. - self.modulation_freqs = self.mosa_dict(modulation_freqs, default={ - '12': 2.4E9, '23': 2.4E9, '31': 2.4E9, - '13': 2.401E9, '32': 2.401E9, '21': 2.401E9, - }) - - # Default based on default for LISANode - self.offsets_freqs = self.mosa_dict(offsets_freqs, default={ - '12': 8.1E6, '23': 9.2E6, '31': 10.3E6, '13': 1.4E6, '32': -11.6E6, '21': -9.5E6, - }) - @classmethod def mosa_dict(cls, value, default=None): """Make sure that value is turned into a dictionary with the MOSAs as keys.