lisainstrument 1.3__tar.gz → 1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {lisainstrument-1.3 → lisainstrument-1.4}/PKG-INFO +1 -1
  2. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/instrument.py +54 -41
  3. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/noises.py +15 -7
  4. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument.egg-info/PKG-INFO +1 -1
  5. {lisainstrument-1.3 → lisainstrument-1.4}/report.py +6 -6
  6. {lisainstrument-1.3 → lisainstrument-1.4}/MANIFEST.in +0 -0
  7. {lisainstrument-1.3 → lisainstrument-1.4}/README.md +0 -0
  8. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/__init__.py +0 -0
  9. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/containers.py +0 -0
  10. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/dsp.py +0 -0
  11. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/hexagon.py +0 -0
  12. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/pyplnoise/LICENSE +0 -0
  13. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/pyplnoise/__init__.py +0 -0
  14. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument/pyplnoise/pyplnoise.py +0 -0
  15. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument.egg-info/SOURCES.txt +0 -0
  16. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument.egg-info/dependency_links.txt +0 -0
  17. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument.egg-info/requires.txt +0 -0
  18. {lisainstrument-1.3 → lisainstrument-1.4}/lisainstrument.egg-info/top_level.txt +0 -0
  19. {lisainstrument-1.3 → lisainstrument-1.4}/setup.cfg +0 -0
  20. {lisainstrument-1.3 → lisainstrument-1.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lisainstrument
3
- Version: 1.3
3
+ Version: 1.4
4
4
  Summary: LISA Instrument simulates instrumental noises, propagates laser beams, generates measurements and the on-board processing to deliver simulated telemetry data.
5
5
  Home-page: https://gitlab.in2p3.fr/lisa-simulation/instrument
6
6
  Author: Jean-Baptiste Bayle
@@ -9,6 +9,7 @@ Authors:
9
9
  """
10
10
 
11
11
  import re
12
+ import math
12
13
  import logging
13
14
  import numpy as np
14
15
  import matplotlib.pyplot as plt
@@ -144,11 +145,11 @@ class Instrument:
144
145
  modulation_freqs: dictionary of modulation frequencies [Hz], or 'default'
145
146
  tdir_modulations: dictionary of callable generators of TDIR assistance modulations
146
147
  that take an array of TPS times as argument, or None
147
- clock_asds: dictionary of clock noise amplitude spectral densities
148
+ clock_asds: dictionary of clock noise amplitude spectral densities [/sqrt(Hz)]
148
149
  clock_offsets: dictionary of clock offsets from TPS [s]
149
- clock_freqoffsets: dictionary of clock frequency offsets [s^-1], or 'default'
150
- clock_freqlindrifts: dictionary of clock frequency linear drifts [s^-2], or 'default'
151
- clock_freqquaddrifts: dictionary of clock frequency quadratic drifts [s^-3], or 'default'
150
+ clock_freqoffsets: dictionary of clock frequency offsets [s/s], or 'default'
151
+ clock_freqlindrifts: dictionary of clock frequency linear drifts [s/s^2], or 'default'
152
+ clock_freqquaddrifts: dictionary of clock frequency quadratic drifts [s/s^3], or 'default'
152
153
  clockinv_tolerance: convergence tolerance for clock noise inversion [s]
153
154
  clockinv_maxiter: maximum number of iterations for clock noise inversion
154
155
  backlink_asds: dictionary of amplitude spectral densities for backlink noise [m/sqrt(Hz)]
@@ -225,27 +226,33 @@ class Instrument:
225
226
  self.physics_dt = self.dt / self.physics_upsampling
226
227
  self.physics_fs = self.fs * self.physics_upsampling
227
228
  logger.info("Computing physics time vector (size=%s, dt=%s)", self.physics_size, self.physics_dt)
228
- self.physics_t = self.t0 + np.arange(self.physics_size, dtype=np.float64) * self.physics_dt
229
- self.physics_et = self.physics_t - self.t0 # elapsed time
229
+ self.physics_et = np.arange(self.physics_size, dtype=np.float64) * self.physics_dt # elapsed time
230
+ self.physics_t = self.t0 + self.physics_et
230
231
 
231
232
  # Telemetry sampling
232
233
  self.telemetry_downsampling = int(telemetry_downsampling)
233
- self.initial_telemetry_size = int(initial_telemetry_size)
234
- self.initial_telemetry_measurement_size = \
235
- self.initial_telemetry_size * self.telemetry_downsampling
236
- self.initial_telemetry_physics_size = \
237
- self.initial_telemetry_measurement_size * self.physics_upsampling
238
- self.telemetry_size = self.initial_telemetry_size \
239
- + int(np.ceil(self.size / self.telemetry_downsampling))
240
234
  self.telemetry_dt = self.dt * self.telemetry_downsampling
241
235
  self.telemetry_fs = self.fs / self.telemetry_downsampling
236
+ # Extra telemetry samples before t0
237
+ self.initial_telemetry_size = int(initial_telemetry_size)
242
238
  self.telemetry_t0 = self.t0 - self.initial_telemetry_size * self.telemetry_dt
239
+ # Total telemetry size, includes initial telemetry samples
240
+ # plus telemetry samples covering the entire measurement time vector,
241
+ # hence the use of ``math.ceil`` -- the +1 is for the sample at t0
242
+ self.telemetry_size = self.initial_telemetry_size + 1 \
243
+ + math.ceil(self.size / self.telemetry_downsampling)
243
244
  logger.info("Computing telemetry time vector (size=%s, dt=%s)", self.telemetry_size, self.telemetry_dt)
244
- self.telemetry_t = self.telemetry_t0 + np.arange(self.telemetry_size, dtype=np.float64) * self.telemetry_dt
245
-
246
- self.physics_t_withinitial = self.telemetry_t0 + \
247
- np.arange(self.physics_size + self.initial_telemetry_physics_size, dtype=np.float64) * self.physics_dt
248
- self.physics_et_withinitial = self.physics_t_withinitial - self.t0
245
+ self.telemetry_t = self.telemetry_t0 \
246
+ + np.arange(self.telemetry_size, dtype=np.float64) * self.telemetry_dt
247
+ # Physics time vector covering telemetry samples
248
+ self.telemetry_to_physics_dt = self.telemetry_downsampling * self.physics_upsampling
249
+ self.physics_size_covering_telemetry = self.telemetry_size * self.telemetry_to_physics_dt
250
+ self.physics_et_covering_telemetry = self.telemetry_t0 - self.t0 + \
251
+ np.arange(self.physics_size_covering_telemetry, dtype=np.float64) * self.physics_dt
252
+ self.physics_t_covering_telemetry = self.t0 + self.physics_et_covering_telemetry
253
+ # How to cut physics data covering telemetry to regular size
254
+ first_sample = self.initial_telemetry_size * self.telemetry_to_physics_dt
255
+ self.telemetry_physics_slice = slice(first_sample, first_sample + self.physics_size)
249
256
 
250
257
  # Orbits, gravitational waves, glitches
251
258
  self.init_orbits(orbits, orbit_dataset)
@@ -653,7 +660,7 @@ class Instrument:
653
660
  logger.debug("Interpolating proper pseudo-range derivatives")
654
661
  self.d_pprs = ForEachMOSA(lambda mosa: pprs(mosa).derivative()(self.physics_t))
655
662
  logger.debug("Interpolating TPSs with respect to TCB")
656
- self.tps_wrt_tcb = ForEachSC(lambda sc: tps_wrt_tcb(sc)(self.physics_t_withinitial))
663
+ self.tps_wrt_tcb = ForEachSC(lambda sc: tps_wrt_tcb(sc)(self.physics_t_covering_telemetry))
657
664
  except ValueError as error:
658
665
  logger.error("Missing orbit information at \n%s", self.physics_t)
659
666
  raise ValueError("missing orbit information, use longer orbit file or adjust sampling") from error
@@ -677,14 +684,19 @@ class Instrument:
677
684
  self.pprs = ForEachMOSA(lambda mosa: interpolate(dataset[:, link_index[mosa]], self.physics_t))
678
685
  logger.debug("Interpolating proper pseudo-range derivatives")
679
686
  self.d_pprs = ForEachMOSA(lambda mosa: interpolate(dataset[:, link_index[mosa]], self.physics_t, nu=1))
687
+ except ValueError as error:
688
+ logger.error("Missing orbit information at \n%s", self.physics_t)
689
+ raise ValueError("missing orbit information, use longer orbit file or adjust sampling") from error
690
+ try:
680
691
  logger.debug("Interpolating TPSs with respect to TCB")
681
692
  if self.orbit_dataset == 'tcb/ltt':
682
693
  self.tps_wrt_tcb = ForEachSC(lambda sc: 0)
683
694
  else:
684
695
  dataset = orbitf['tcb/delta_tau']
685
- self.tps_wrt_tcb = ForEachSC(lambda sc: interpolate(dataset[:, sc_index[sc]], self.physics_t))
696
+ self.tps_wrt_tcb = ForEachSC(lambda sc:
697
+ interpolate(dataset[:, sc_index[sc]], self.physics_t_covering_telemetry))
686
698
  except ValueError as error:
687
- logger.error("Missing orbit information at \n%s", self.physics_t)
699
+ logger.error("Missing orbit information at \n%s", self.physics_t_covering_telemetry)
688
700
  raise ValueError("missing orbit information, use longer orbit file or adjust sampling") from error
689
701
 
690
702
  def init_gws(self, gws):
@@ -957,18 +969,18 @@ class Instrument:
957
969
  + self.integrated_clock_noise_fluctuations
958
970
 
959
971
  logger.debug("Computing THE with respect to TCB")
960
- t = self.physics_et_withinitial
972
+ t = self.physics_et_covering_telemetry
961
973
  self.the_wrt_tcb_withinitial = \
962
974
  self.tps_wrt_tcb \
963
975
  + self.clock_offsets \
964
976
  + self.clock_freqoffsets * (t + self.tps_wrt_tcb) \
965
977
  + self.clock_freqlindrifts * (t + self.tps_wrt_tcb)**2 / 2 \
966
978
  + self.clock_freqquaddrifts * (t + self.tps_wrt_tcb)**3 / 3 \
967
- + self.tps_wrt_tcb * self.clock_noise_fluctuations_withinitial \
968
- + self.integrated_clock_noise_fluctuations_withinitial
979
+ + self.tps_wrt_tcb * self.clock_noise_fluctuations_covering_telemetry \
980
+ + self.integrated_clock_noise_fluctuations_covering_telemetry
969
981
 
970
982
  logger.debug("Computing MOC time correlations")
971
- physics_to_telemetry = lambda _, x: x[::self.physics_upsampling * self.telemetry_downsampling]
983
+ physics_to_telemetry = lambda _, x: x[::self.telemetry_to_physics_dt]
972
984
  self.moc_time_correlations = self.moc_time_correlation_noises \
973
985
  + self.the_wrt_tcb_withinitial.transformed(physics_to_telemetry)
974
986
 
@@ -1516,35 +1528,36 @@ class Instrument:
1516
1528
  logger.debug("Generating clock noise fluctuations")
1517
1529
 
1518
1530
  # Include initial telemetry time period
1519
- self.clock_noise_fluctuations_withinitial = ForEachSC(lambda sc:
1531
+ self.clock_noise_fluctuations_covering_telemetry = ForEachSC(lambda sc:
1520
1532
  noises.clock(
1521
1533
  self.physics_fs,
1522
- self.physics_size + self.initial_telemetry_physics_size,
1534
+ self.physics_size_covering_telemetry,
1523
1535
  self.clock_asds[sc]),
1524
1536
  concurrent=self.concurrent
1525
1537
  )
1526
1538
 
1527
1539
  # Slice to only select physics period
1528
- self.clock_noise_fluctuations = self.clock_noise_fluctuations_withinitial.transformed(
1529
- lambda _, x: x if np.isscalar(x) else x[self.initial_telemetry_physics_size:]
1530
- )
1540
+ self.clock_noise_fluctuations = \
1541
+ self.clock_noise_fluctuations_covering_telemetry.transformed(
1542
+ lambda _, x: x if np.isscalar(x) else x[self.telemetry_physics_slice]
1543
+ )
1531
1544
 
1532
1545
  logger.debug("Integrating clock noise fluctuations")
1533
1546
 
1534
1547
  # Include initial telemetry time period
1535
- self.integrated_clock_noise_fluctuations_withinitial = \
1548
+ self.integrated_clock_noise_fluctuations_covering_telemetry = \
1536
1549
  ForEachSC(lambda sc:
1537
1550
  cumulative_trapezoid(np.broadcast_to(
1538
- self.clock_noise_fluctuations_withinitial[sc],
1539
- self.physics_size + self.initial_telemetry_physics_size),
1551
+ self.clock_noise_fluctuations_covering_telemetry[sc],
1552
+ self.physics_size_covering_telemetry),
1540
1553
  dx=self.physics_dt, initial=0),
1541
1554
  concurrent=self.concurrent
1542
1555
  )
1543
1556
 
1544
1557
  # Slice to only select physics period
1545
1558
  self.integrated_clock_noise_fluctuations = \
1546
- self.integrated_clock_noise_fluctuations_withinitial.transformed(
1547
- lambda _, x: x if np.isscalar(x) else x[self.initial_telemetry_physics_size:]
1559
+ self.integrated_clock_noise_fluctuations_covering_telemetry.transformed(
1560
+ lambda _, x: x if np.isscalar(x) else x[self.telemetry_physics_slice]
1548
1561
  )
1549
1562
 
1550
1563
  ## Modulation noise
@@ -1911,7 +1924,7 @@ class Instrument:
1911
1924
  'git_url', 'version', 'concurrent',
1912
1925
  'dt', 't0', 'size', 'fs', 'duration',
1913
1926
  'initial_telemetry_size', 'telemetry_downsampling', 'telemetry_fs',
1914
- 'telemetry_size', 'telemetry_t0',
1927
+ 'telemetry_dt', 'telemetry_size', 'telemetry_t0',
1915
1928
  'physics_upsampling', 'physics_size', 'physics_dt', 'physics_fs',
1916
1929
  'central_freq', 'lock_config', 'lock',
1917
1930
  'fplan_file', 'fplan',
@@ -1981,9 +1994,9 @@ class Instrument:
1981
1994
  logger.debug("Writing clock noise to '%s'", output)
1982
1995
  self.clock_noise_offsets.write(hdf5, 'clock_noise_offsets')
1983
1996
  self.clock_noise_fluctuations.write(hdf5, 'clock_noise_fluctuations')
1984
- self.clock_noise_fluctuations_withinitial.write(
1997
+ self.clock_noise_fluctuations_covering_telemetry.write(
1985
1998
  hdf5, 'clock_noise_fluctuations_withinitial')
1986
- self.integrated_clock_noise_fluctuations_withinitial.write(
1999
+ self.integrated_clock_noise_fluctuations_covering_telemetry.write(
1987
2000
  hdf5, 'integrated_clock_noise_fluctuations_withinitial')
1988
2001
  self.integrated_clock_noise_fluctuations.write(
1989
2002
  hdf5, 'integrated_clock_noise_fluctuations')
@@ -2035,9 +2048,6 @@ class Instrument:
2035
2048
  logger.debug("Writing THE with respect to TCB to '%s'", output)
2036
2049
  self.the_wrt_tcb_withinitial.write(hdf5, 'the_wrt_tcb_withinitial')
2037
2050
 
2038
- logger.debug("Writing MOC time correlations to '%s'", output)
2039
- self.moc_time_correlations.write(hdf5, 'moc_time_correlations')
2040
-
2041
2051
  logger.debug("Writing tilt-to-length couplings to '%s'", output)
2042
2052
  self.local_ttls.write(hdf5, 'local_ttls')
2043
2053
  self.distant_ttls.write(hdf5, 'distant_ttls')
@@ -2223,6 +2233,9 @@ class Instrument:
2223
2233
  self.rfi_carriers.write(hdf5, 'rfi_carriers')
2224
2234
  self.rfi_usbs.write(hdf5, 'rfi_usbs')
2225
2235
 
2236
+ logger.debug("Writing MOC time correlations to '%s'", output)
2237
+ self.moc_time_correlations.write(hdf5, 'moc_time_correlations')
2238
+
2226
2239
  logger.info("Closing measurement file '%s'", output)
2227
2240
 
2228
2241
  def plot_fluctuations(self, output=None, skip=0):
@@ -80,7 +80,8 @@ def violet(fs, size, asd):
80
80
  Args:
81
81
  fs: sampling frequency [Hz]
82
82
  size: number of samples [samples]
83
- asd: amplitude spectral density [/sqrt(Hz)]"""
83
+ asd: amplitude spectral density [/sqrt(Hz)]
84
+ """
84
85
  logger.debug("Generating violet noise (fs=%s Hz, size=%s, asd=%s)", fs, size, asd)
85
86
  if not asd:
86
87
  logger.debug("Vanishing power spectral density, bypassing noise generation")
@@ -89,18 +90,20 @@ def violet(fs, size, asd):
89
90
  return np.gradient(white_noise, 1 / fs) / (2 * pi)
90
91
 
91
92
 
92
- def pink(fs, size, asd):
93
+ def pink(fs, size, asd, fmin=None):
93
94
  """Generate a pink noise in f^(-1/2) in amplitude.
94
95
 
95
96
  Args:
96
97
  fs: sampling frequency [Hz]
97
98
  size: number of samples [samples]
98
- asd: amplitude spectral density [/sqrt(Hz)]"""
99
+ asd: amplitude spectral density [/sqrt(Hz)]
100
+ fmin: saturation frequency (default to fs / size) [Hz]
101
+ """
99
102
  logger.debug("Generating pink noise (fs=%s Hz, size=%s, asd=%s)", fs, size, asd)
100
103
  if not asd:
101
104
  logger.debug("Vanishing power spectral density, bypassing noise generation")
102
105
  return 0
103
- generator = pyplnoise.PinkNoise(fs, fs / size, fs / 2)
106
+ generator = pyplnoise.PinkNoise(fs, fmin or fs / size, fs / 2)
104
107
  return asd / sqrt(2) * generator.get_series(size)
105
108
 
106
109
 
@@ -110,7 +113,8 @@ def red(fs, size, asd):
110
113
  Args:
111
114
  fs: sampling frequency [Hz]
112
115
  size: number of samples [samples]
113
- asd: amplitude spectral density [/sqrt(Hz)]"""
116
+ asd: amplitude spectral density [/sqrt(Hz)]
117
+ """
114
118
  logger.debug("Generating red noise (fs=%s Hz, size=%s, asd=%s)", fs, size, asd)
115
119
  if not asd:
116
120
  logger.debug("Vanishing power spectral density, bypassing noise generation")
@@ -125,7 +129,8 @@ def infrared(fs, size, asd):
125
129
  Args:
126
130
  fs: sampling frequency [Hz]
127
131
  size: number of samples [samples]
128
- asd: amplitude spectral density [/sqrt(Hz)]"""
132
+ asd: amplitude spectral density [/sqrt(Hz)]
133
+ """
129
134
  logger.debug("Generating infrared noise (fs=%s Hz, size=%s, asd=%s)", fs, size, asd)
130
135
  if not asd:
131
136
  logger.debug("Vanishing power spectral density, bypassing noise generation")
@@ -173,11 +178,14 @@ def clock(fs, size, asd):
173
178
 
174
179
  S_q(f) [ffd] = (asd)^2 f^(-1)
175
180
 
181
+ Clock noise saturates below 1E-5 Hz, as the low-frequency part is modeled by
182
+ deterministing clock drifts.
183
+
176
184
  Args:
177
185
  asd: amplitude spectral density [/sqrt(Hz)]
178
186
  """
179
187
  logger.debug("Generating clock noise fluctuations (fs=%s Hz, size=%s, asd=%s /sqrt(Hz))", fs, size, asd)
180
- return pink(fs, size, asd)
188
+ return pink(fs, size, asd, fmin=1E-5)
181
189
 
182
190
 
183
191
  def modulation(fs, size, asd):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lisainstrument
3
- Version: 1.3
3
+ Version: 1.4
4
4
  Summary: LISA Instrument simulates instrumental noises, propagates laser beams, generates measurements and the on-board processing to deliver simulated telemetry data.
5
5
  Home-page: https://gitlab.in2p3.fr/lisa-simulation/instrument
6
6
  Author: Jean-Baptiste Bayle
@@ -550,7 +550,7 @@ def moc_time_correlation():
550
550
 
551
551
  plt.figure(figsize=(16, 8))
552
552
  for sc in instru.SCS:
553
- plot_tseries(instru.physics_t, instru.moc_time_correlations[sc], label=sc)
553
+ plot_tseries(instru.telemetry_t, instru.moc_time_correlations[sc], label=sc)
554
554
  plt.ylabel('Time [s]')
555
555
  plt.title('MOC time correlation noise')
556
556
  plt.legend()
@@ -559,7 +559,7 @@ def moc_time_correlation():
559
559
 
560
560
  plt.figure(figsize=(16, 8))
561
561
  for sc in instru.SCS:
562
- plot_asd(instru.physics_t, instru.moc_time_correlations[sc], label=sc)
562
+ plot_asd(instru.telemetry_t, instru.moc_time_correlations[sc], label=sc)
563
563
  plt.ylabel('ASD [s/sqrt(Hz)]')
564
564
  plt.title('MOC time correlation noise')
565
565
  plt.legend()
@@ -742,12 +742,12 @@ def plot_measurements(instru, pdf):
742
742
  pdf.savefig()
743
743
  plt.close()
744
744
 
745
- # Time-domain TCB timer deviations
745
+ # Time-domain MOC time correlations
746
746
  plt.figure(figsize=(16, 8))
747
747
  for sc in instru.SCS:
748
- plot_tseries(instru.t, instru.tcb_timer_deviations[sc], skip=0, label=sc)
749
- plt.ylabel('Timer deviation [s]')
750
- plt.title('Timer deviations from TCB')
748
+ plot_tseries(instru.telemetry_t, instru.moc_time_correlations[sc], skip=0, label=sc)
749
+ plt.ylabel('Deviation [s]')
750
+ plt.title('MOC time correlations')
751
751
  plt.legend()
752
752
  pdf.savefig()
753
753
  plt.close()
File without changes
File without changes
File without changes
File without changes