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 (7)
......@@ -86,7 +86,9 @@ class Instrument:
# Clock inversion
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,
backlink_asds=3E-12, backlink_fknees=2E-3,
testmass_asds=2.4E-15, testmass_fknees=0.4E-3, testmass_fbreak=8E-3,
testmass_shape='original', testmass_frelax=0.8E-4,
oms_asds=(6.35E-12, 1.25E-11, 1.42E-12, 3.38E-12, 3.32E-12, 7.90E-12), oms_fknees=2E-3,
# MOC time correlation
moc_time_correlation_asds=0.42,
......@@ -155,7 +157,10 @@ class Instrument:
backlink_asds: dictionary of amplitude spectral densities for backlink noise [m/sqrt(Hz)]
backlink_fknees: dictionary of cutoff frequencied for backlink noise [Hz]
testmass_asds: dictionary of amplitude spectral densities for test-mass noise [ms^(-2)/sqrt(Hz)]
testmass_fknees: dictionary of cutoff frequencies for test-mass noise [Hz]
testmass_fknees: dictionary of low-frequency cutoff frequencies for test-mass noise [Hz]
testmass_fbreak: dictionary of high-frequency break frequencies for test-mass noise [Hz]
testmass_shape: test-mass noise spectral shape, either 'original' or 'lowfreq-relax'
testmass_frelax: dictionary of low-frequency relaxation frequencies for test-mass noise [Hz]
oms_asds: tuple of dictionaries of amplitude spectral densities for OMS noise [m/sqrt(Hz)],
ordered as (isi_carrier, isi_usb, tmi_carrier, tmi_usb, rfi_carrier, rfi_usb)
moc_time_correlation_asds: dictionary of amplitude spectral densities for MOC time
......@@ -342,8 +347,6 @@ class Instrument:
# Backlink, OMS and test-mass acceleration noise
self.backlink_asds = ForEachMOSA(backlink_asds)
self.backlink_fknees = ForEachMOSA(backlink_fknees)
self.testmass_asds = ForEachMOSA(testmass_asds)
self.testmass_fknees = ForEachMOSA(testmass_fknees)
self.oms_isi_carrier_asds = ForEachMOSA(oms_asds[0])
self.oms_isi_usb_asds = ForEachMOSA(oms_asds[1])
self.oms_tmi_carrier_asds = ForEachMOSA(oms_asds[2])
......@@ -352,6 +355,15 @@ class Instrument:
self.oms_rfi_usb_asds = ForEachMOSA(oms_asds[5])
self.oms_fknees = ForEachMOSA(oms_fknees)
# Test-mass noise
if testmass_shape not in ['original', 'lowfreq-relax']:
raise ValueError(f"invalid test-mass noise spectral shape '{testmass_shape}'")
self.testmass_shape = testmass_shape
self.testmass_asds = ForEachMOSA(testmass_asds)
self.testmass_fknees = ForEachMOSA(testmass_fknees)
self.testmass_fbreak = ForEachMOSA(testmass_fbreak)
self.testmass_frelax = ForEachMOSA(testmass_frelax)
# Tilt-to-length
if ttl_coeffs == 'default':
# Default values drawn from distributions set in 'random'
......@@ -977,18 +989,18 @@ class Instrument:
## MOC time correlations
logger.debug("Computing local THE with respect to TPS")
logger.debug("Computing local SCET with respect to TPS")
t = self.physics_et
self.the_wrt_tps_local = \
self.scet_wrt_tps_local = \
self.clock_offsets \
+ self.clock_freqoffsets * t \
+ self.clock_freqlindrifts * t**2 / 2 \
+ self.clock_freqquaddrifts * t**3 / 3 \
+ self.integrated_clock_noise_fluctuations
logger.debug("Computing THE with respect to TCB")
logger.debug("Computing SCET with respect to TCB")
t = self.physics_et_covering_telemetry
self.the_wrt_tcb_withinitial = \
self.scet_wrt_tcb_withinitial = \
self.tps_wrt_tcb \
+ self.clock_offsets \
+ self.clock_freqoffsets * (t + self.tps_wrt_tcb) \
......@@ -1000,13 +1012,13 @@ class Instrument:
logger.debug("Computing MOC time correlations")
physics_to_telemetry = lambda _, x: x[::self.telemetry_to_physics_dt]
self.moc_time_correlations = self.moc_time_correlation_noises \
+ self.the_wrt_tcb_withinitial.transformed(physics_to_telemetry)
+ self.scet_wrt_tcb_withinitial.transformed(physics_to_telemetry)
## TDIR modulations
logger.debug("Forming TDIR assistance modulations")
self.tdir_modulations_tseries = ForEachMOSA(lambda mosa:
self.tdir_modulations[mosa](self.physics_et + self.the_wrt_tps_local[mosa[0]])
self.tdir_modulations[mosa](self.physics_et + self.scet_wrt_tps_local[mosa[0]])
)
## Local beams
......@@ -1067,9 +1079,9 @@ class Instrument:
- (self.central_freq + delayed_distant_usb_offsets) * self.gws \
- (self.central_freq + delayed_distant_usb_offsets) * self.local_ttls / c
logger.debug("Propagating local THEs with respect to TPS to distant MOSAs")
self.the_wrt_tps_distant = \
self.the_wrt_tps_local.for_each_mosa().distant() \
logger.debug("Propagating local SCETs with respect to TPS to distant MOSAs")
self.scet_wrt_tps_distant = \
self.scet_wrt_tps_local.for_each_mosa().distant() \
.transformed(lambda mosa, x: self.interpolate(x, -self.pprs[mosa])
- self.pprs[mosa]
)
......@@ -1159,7 +1171,7 @@ class Instrument:
## Measured pseudo-ranging on TPS grid (high-frequency)
logger.info("Computing measured pseudo-ranges on TPS")
self.tps_mprs = self.the_wrt_tps_local - self.the_wrt_tps_distant \
self.tps_mprs = self.scet_wrt_tps_local - self.scet_wrt_tps_distant \
+ self.ranging_noises
......@@ -1281,95 +1293,95 @@ class Instrument:
+ self.central_freq * self.oms_rfi_usb_noises \
+ self.glitch_readout_rfi_usbs
## Sampling beatnotes, DWS measurements, and measured pseudo-ranges to THE grid
## Sampling beatnotes, DWS measurements, and measured pseudo-ranges to SCET grid
logger.info("Inverting THE with respect to TPS")
self.tps_wrt_the = self.the_wrt_tps_local \
.transformed(lambda sc, x: self.invert_the_wrt_tps(x, sc), concurrent=self.concurrent)
logger.info("Inverting SCET with respect to TPS")
self.tps_wrt_scet = self.scet_wrt_tps_local \
.transformed(lambda sc, x: self.invert_scet_wrt_tps(x, sc), concurrent=self.concurrent)
self.timestamped = \
lambda mosa, x: self.interpolate(x, -self.tps_wrt_the.for_each_mosa()[mosa])
lambda mosa, x: self.interpolate(x, -self.tps_wrt_scet.for_each_mosa()[mosa])
logger.info("Sampling inter-spacecraft beatnotes to THE grid")
logger.info("Sampling inter-spacecraft beatnotes to SCET grid")
logger.debug("Sampling inter-spacecraft carrier beatnote fluctuations to THE grid")
self.the_isi_carrier_offsets = (
logger.debug("Sampling inter-spacecraft carrier beatnote fluctuations to SCET grid")
self.scet_isi_carrier_offsets = (
self.tps_isi_carrier_offsets / (1 + self.clock_noise_offsets)
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling inter-spacecraft carrier beatnote fluctuations to THE grid")
self.the_isi_carrier_fluctuations = (
logger.debug("Sampling inter-spacecraft carrier beatnote fluctuations to SCET grid")
self.scet_isi_carrier_fluctuations = (
self.tps_isi_carrier_fluctuations / (1 + self.clock_noise_offsets)
- self.tps_isi_carrier_offsets * self.clock_noise_fluctuations
/ (1 + self.clock_noise_offsets)**2
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling inter-spacecraft upper sideband beatnote offsets to THE grid")
self.the_isi_usb_offsets = (
logger.debug("Sampling inter-spacecraft upper sideband beatnote offsets to SCET grid")
self.scet_isi_usb_offsets = (
self.tps_isi_usb_offsets / (1 + self.clock_noise_offsets)
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling inter-spacecraft upper sideband beatnote fluctuations to THE grid")
self.the_isi_usb_fluctuations = (
logger.debug("Sampling inter-spacecraft upper sideband beatnote fluctuations to SCET grid")
self.scet_isi_usb_fluctuations = (
self.tps_isi_usb_fluctuations / (1 + self.clock_noise_offsets)
- self.tps_isi_usb_offsets * self.clock_noise_fluctuations
/ (1 + self.clock_noise_offsets)**2
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling inter-spacecraft DWS measurements to THE grid")
self.the_isi_dws_phis = self.tps_isi_dws_phis.transformed(self.timestamped, concurrent=self.concurrent)
self.the_isi_dws_etas = self.tps_isi_dws_etas.transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling inter-spacecraft DWS measurements to SCET grid")
self.scet_isi_dws_phis = self.tps_isi_dws_phis.transformed(self.timestamped, concurrent=self.concurrent)
self.scet_isi_dws_etas = self.tps_isi_dws_etas.transformed(self.timestamped, concurrent=self.concurrent)
logger.info("Sampling measured pseudo-ranges to THE grid")
self.the_mprs = self.tps_mprs.transformed(self.timestamped, concurrent=self.concurrent)
logger.info("Sampling measured pseudo-ranges to SCET grid")
self.scet_mprs = self.tps_mprs.transformed(self.timestamped, concurrent=self.concurrent)
logger.info("Sampling test-mass beatnotes to THE grid")
logger.info("Sampling test-mass beatnotes to SCET grid")
logger.debug("Sampling test-mass carrier beatnote offsets to THE grid")
self.the_tmi_carrier_offsets = (
logger.debug("Sampling test-mass carrier beatnote offsets to SCET grid")
self.scet_tmi_carrier_offsets = (
self.tps_tmi_carrier_offsets / (1 + self.clock_noise_offsets)
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling test-mass carrier beatnote fluctuations to THE grid")
self.the_tmi_carrier_fluctuations = (
logger.debug("Sampling test-mass carrier beatnote fluctuations to SCET grid")
self.scet_tmi_carrier_fluctuations = (
self.tps_tmi_carrier_fluctuations / (1 + self.clock_noise_offsets)
- self.tps_tmi_carrier_offsets * self.clock_noise_fluctuations
/ (1 + self.clock_noise_offsets)**2
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling test-mass upper sideband beatnote offsets to THE grid")
self.the_tmi_usb_offsets = (
logger.debug("Sampling test-mass upper sideband beatnote offsets to SCET grid")
self.scet_tmi_usb_offsets = (
self.tps_tmi_usb_offsets / (1 + self.clock_noise_offsets)
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling test-mass upper sideband beatnote fluctuations to THE grid")
self.the_tmi_usb_fluctuations = (
logger.debug("Sampling test-mass upper sideband beatnote fluctuations to SCET grid")
self.scet_tmi_usb_fluctuations = (
self.tps_tmi_usb_fluctuations / (1 + self.clock_noise_offsets)
- self.tps_tmi_usb_offsets * self.clock_noise_fluctuations
/ (1 + self.clock_noise_offsets)**2
).transformed(self.timestamped, concurrent=self.concurrent)
logger.info("Sampling reference beatnotes to THE grid")
logger.info("Sampling reference beatnotes to SCET grid")
logger.debug("Sampling reference carrier beatnote offsets to THE grid")
self.the_rfi_carrier_offsets = (
logger.debug("Sampling reference carrier beatnote offsets to SCET grid")
self.scet_rfi_carrier_offsets = (
self.tps_rfi_carrier_offsets / (1 + self.clock_noise_offsets)
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling reference carrier beatnote fluctuations to THE grid")
self.the_rfi_carrier_fluctuations = (
logger.debug("Sampling reference carrier beatnote fluctuations to SCET grid")
self.scet_rfi_carrier_fluctuations = (
self.tps_rfi_carrier_fluctuations / (1 + self.clock_noise_offsets)
- self.tps_rfi_carrier_offsets * self.clock_noise_fluctuations
/ (1 + self.clock_noise_offsets)**2
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling reference upper sideband beatnote offsets to THE grid")
self.the_rfi_usb_offsets = (
logger.debug("Sampling reference upper sideband beatnote offsets to SCET grid")
self.scet_rfi_usb_offsets = (
self.tps_rfi_usb_offsets / (1 + self.clock_noise_offsets)
).transformed(self.timestamped, concurrent=self.concurrent)
logger.debug("Sampling reference upper sideband beatnote fluctuations to THE grid")
self.the_rfi_usb_fluctuations = (
logger.debug("Sampling reference upper sideband beatnote fluctuations to SCET grid")
self.scet_rfi_usb_fluctuations = (
self.tps_rfi_usb_fluctuations / (1 + self.clock_noise_offsets)
- self.tps_rfi_usb_offsets * self.clock_noise_fluctuations
/ (1 + self.clock_noise_offsets)**2
......@@ -1383,33 +1395,33 @@ class Instrument:
self.electro_rfi = lambda mosa, x: self.interpolate(x, -self.electro_delays_rfis[mosa])
logger.debug("Applying electronic delays to inter-spacecraft beatnotes")
self.electro_isi_carrier_offsets = self.the_isi_carrier_offsets \
self.electro_isi_carrier_offsets = self.scet_isi_carrier_offsets \
.transformed(self.electro_isi, concurrent=self.concurrent)
self.electro_isi_carrier_fluctuations = self.the_isi_carrier_fluctuations \
self.electro_isi_carrier_fluctuations = self.scet_isi_carrier_fluctuations \
.transformed(self.electro_isi, concurrent=self.concurrent)
self.electro_isi_usb_offsets = self.the_isi_usb_offsets \
self.electro_isi_usb_offsets = self.scet_isi_usb_offsets \
.transformed(self.electro_isi, concurrent=self.concurrent)
self.electro_isi_usb_fluctuations = self.the_isi_usb_fluctuations \
self.electro_isi_usb_fluctuations = self.scet_isi_usb_fluctuations \
.transformed(self.electro_isi, concurrent=self.concurrent)
logger.debug("Applying electronic delays to test-mass beatnotes")
self.electro_tmi_carrier_offsets = self.the_tmi_carrier_offsets \
self.electro_tmi_carrier_offsets = self.scet_tmi_carrier_offsets \
.transformed(self.electro_tmi, concurrent=self.concurrent)
self.electro_tmi_carrier_fluctuations = self.the_tmi_carrier_fluctuations \
self.electro_tmi_carrier_fluctuations = self.scet_tmi_carrier_fluctuations \
.transformed(self.electro_tmi, concurrent=self.concurrent)
self.electro_tmi_usb_offsets = self.the_tmi_usb_offsets \
self.electro_tmi_usb_offsets = self.scet_tmi_usb_offsets \
.transformed(self.electro_tmi, concurrent=self.concurrent)
self.electro_tmi_usb_fluctuations = self.the_tmi_usb_fluctuations \
self.electro_tmi_usb_fluctuations = self.scet_tmi_usb_fluctuations \
.transformed(self.electro_tmi, concurrent=self.concurrent)
logger.debug("Applying electronic delays to reference beatnotes")
self.electro_rfi_carrier_offsets = self.the_rfi_carrier_offsets \
self.electro_rfi_carrier_offsets = self.scet_rfi_carrier_offsets \
.transformed(self.electro_rfi, concurrent=self.concurrent)
self.electro_rfi_carrier_fluctuations = self.the_rfi_carrier_fluctuations \
self.electro_rfi_carrier_fluctuations = self.scet_rfi_carrier_fluctuations \
.transformed(self.electro_rfi, concurrent=self.concurrent)
self.electro_rfi_usb_offsets = self.the_rfi_usb_offsets \
self.electro_rfi_usb_offsets = self.scet_rfi_usb_offsets \
.transformed(self.electro_rfi, concurrent=self.concurrent)
self.electro_rfi_usb_fluctuations = self.the_rfi_usb_fluctuations \
self.electro_rfi_usb_fluctuations = self.scet_rfi_usb_fluctuations \
.transformed(self.electro_rfi, concurrent=self.concurrent)
## Antialiasing filtering
......@@ -1427,13 +1439,13 @@ class Instrument:
.transformed(self.aafilter, concurrent=self.concurrent)
logger.debug("Filtering inter-spacecraft DWS measurements")
self.filtered_isi_dws_phis = self.the_isi_dws_phis \
self.filtered_isi_dws_phis = self.scet_isi_dws_phis \
.transformed(self.aafilter, concurrent=self.concurrent)
self.filtered_isi_dws_etas = self.the_isi_dws_etas \
self.filtered_isi_dws_etas = self.scet_isi_dws_etas \
.transformed(self.aafilter, concurrent=self.concurrent)
logger.debug("Filtering measured pseudo-ranges")
self.filtered_mprs = self.the_mprs \
self.filtered_mprs = self.scet_mprs \
.transformed(self.aafilter, concurrent=self.concurrent)
logger.debug("Filtering test-mass beatnotes")
......@@ -1601,8 +1613,15 @@ class Instrument:
logger.info("Generating test-mass acceleration noise")
self.testmass_noises = ForEachMOSA(lambda mosa:
noises.testmass(self.physics_fs, self.physics_size,
self.testmass_asds[mosa], self.testmass_fknees[mosa]),
noises.testmass(
self.physics_fs,
self.physics_size,
self.testmass_asds[mosa],
self.testmass_fknees[mosa],
self.testmass_fbreak[mosa],
self.testmass_frelax[mosa],
self.testmass_shape,
),
concurrent=self.concurrent
)
......@@ -1867,37 +1886,37 @@ class Instrument:
if not just_locked:
raise RuntimeError(f"cannot apply locking conditions to remaining lasers '{list(dependencies.keys())}'")
def invert_the_wrt_tps(self, the_wrt_tps, sc):
"""Invert THE with respect to TPS of a given spacecraft.
def invert_scet_wrt_tps(self, scet_wrt_tps, sc):
"""Invert SCET with respect to TPS of a given spacecraft.
We recursively solve the implicit equation dtau(tau) = dtau_hat(tau - dtau(tau)) until the
convergence criteria (tolerance) is met, or we exceed the maximum number of iterations.
Args:
the_wrt_tps: array of THEs with respect to TPS
scet_wrt_tps: array of SCETs with respect to TPS
sc: spacecraft index
"""
logger.debug("Inverting THE with respect to TPS for spacecraft %s", sc)
logger.debug("Inverting SCET with respect to TPS for spacecraft %s", sc)
logger.debug("Solving iteratively (tolerance=%s s, maxiter=%s)",
self.clockinv_tolerance, self.clockinv_maxiter)
# Drop samples at the edges to compute error
edge = min(100, len(the_wrt_tps) // 2 - 1)
edge = min(100, len(scet_wrt_tps) // 2 - 1)
error = 0
niter = 0
next_inverse = the_wrt_tps
next_inverse = scet_wrt_tps
while not niter or error > self.clockinv_tolerance:
if niter >= self.clockinv_maxiter:
logger.warning("Maximum number of iterations '%s' reached for SC %s (error=%.2E)", niter, sc, error)
break
logger.debug("Starting iteration #%s", niter)
inverse = next_inverse
next_inverse = self.interpolate(the_wrt_tps, -inverse)
next_inverse = self.interpolate(scet_wrt_tps, -inverse)
error = np.max(np.abs((inverse - next_inverse)[edge:-edge]))
logger.debug("End of iteration %s, with an error of %.2E s", niter, error)
niter += 1
logger.debug("End of THE with respect to TCB inversion after %s iterations with an error of %.2E s", niter, error)
logger.debug("End of SCET with respect to TCB inversion after %s iterations with an error of %.2E s", niter, error)
return inverse
def _write_attr(self, hdf5, *names):
......@@ -2063,11 +2082,11 @@ class Instrument:
self.local_usb_offsets.write(hdf5, 'local_usb_offsets')
self.local_usb_fluctuations.write(hdf5, 'local_usb_fluctuations')
logger.debug("Writing local THE with respect to TPS to '%s'", output)
self.the_wrt_tps_local.write(hdf5, 'the_wrt_tps_local')
logger.debug("Writing local SCET with respect to TPS to '%s'", output)
self.scet_wrt_tps_local.write(hdf5, 'scet_wrt_tps_local')
logger.debug("Writing THE with respect to TCB to '%s'", output)
self.the_wrt_tcb_withinitial.write(hdf5, 'the_wrt_tcb_withinitial')
logger.debug("Writing SCET with respect to TCB to '%s'", output)
self.scet_wrt_tcb_withinitial.write(hdf5, 'scet_wrt_tcb_withinitial')
logger.debug("Writing tilt-to-length couplings to '%s'", output)
self.local_ttls.write(hdf5, 'local_ttls')
......@@ -2079,8 +2098,8 @@ class Instrument:
self.distant_usb_offsets.write(hdf5, 'distant_usb_offsets')
self.distant_usb_fluctuations.write(hdf5, 'distant_usb_fluctuations')
logger.debug("Writing propagated THEs with respect to TCB to '%s'", output)
self.the_wrt_tps_distant.write(hdf5, 'the_wrt_tcb_distant')
logger.debug("Writing propagated SCETs with respect to TCB to '%s'", output)
self.scet_wrt_tps_distant.write(hdf5, 'scet_wrt_tcb_distant')
logger.debug("Writing propagated adjacent beams to '%s'", output)
self.adjacent_carrier_offsets.write(hdf5, 'adjacent_carrier_offsets')
......@@ -2149,33 +2168,33 @@ class Instrument:
self.tps_rfi_usb_offsets.write(hdf5, 'tps_rfi_usb_offsets')
self.tps_rfi_usb_fluctuations.write(hdf5, 'tps_rfi_usb_fluctuations')
logger.debug("Writing TPS with respect to THE to '%s'", output)
self.tps_wrt_the.write(hdf5, 'tps_wrt_the')
logger.debug("Writing inter-spacecraft beatnotes sampled to THE grid to '%s'", output)
self.the_isi_carrier_offsets.write(hdf5, 'the_isi_carrier_offsets')
self.the_isi_carrier_fluctuations.write(hdf5, 'the_isi_carrier_fluctuations')
self.the_isi_usb_offsets.write(hdf5, 'the_isi_usb_offsets')
self.the_isi_usb_fluctuations.write(hdf5, 'the_isi_usb_fluctuations')
logger.debug("Writing inter-spacecraft DWS measurements sampled to THE grid to '%s'", output)
self.the_isi_dws_phis.write(hdf5, 'the_isi_dws_phis')
self.the_isi_dws_etas.write(hdf5, 'the_isi_dws_etas')
logger.debug("Writing measured pseudo-ranges sampled to THE grid to '%s'", output)
self.the_mprs.write(hdf5, 'the_mprs')
logger.debug("Writing test-mass beatnotes sampled to THE grid to '%s'", output)
self.the_tmi_carrier_offsets.write(hdf5, 'the_tmi_carrier_offsets')
self.the_tmi_carrier_fluctuations.write(hdf5, 'the_tmi_carrier_fluctuations')
self.the_tmi_usb_offsets.write(hdf5, 'the_tmi_usb_offsets')
self.the_tmi_usb_fluctuations.write(hdf5, 'the_tmi_usb_fluctuations')
logger.debug("Writing reference beatnotes sampled to THE grid to '%s'", output)
self.the_rfi_carrier_offsets.write(hdf5, 'the_rfi_carrier_offsets')
self.the_rfi_carrier_fluctuations.write(hdf5, 'the_rfi_carrier_fluctuations')
self.the_rfi_usb_offsets.write(hdf5, 'the_rfi_usb_offsets')
self.the_rfi_usb_fluctuations.write(hdf5, 'the_rfi_usb_fluctuations')
logger.debug("Writing TPS with respect to SCET to '%s'", output)
self.tps_wrt_scet.write(hdf5, 'tps_wrt_scet')
logger.debug("Writing inter-spacecraft beatnotes sampled to SCET grid to '%s'", output)
self.scet_isi_carrier_offsets.write(hdf5, 'scet_isi_carrier_offsets')
self.scet_isi_carrier_fluctuations.write(hdf5, 'scet_isi_carrier_fluctuations')
self.scet_isi_usb_offsets.write(hdf5, 'scet_isi_usb_offsets')
self.scet_isi_usb_fluctuations.write(hdf5, 'scet_isi_usb_fluctuations')
logger.debug("Writing inter-spacecraft DWS measurements sampled to SCET grid to '%s'", output)
self.scet_isi_dws_phis.write(hdf5, 'scet_isi_dws_phis')
self.scet_isi_dws_etas.write(hdf5, 'scet_isi_dws_etas')
logger.debug("Writing measured pseudo-ranges sampled to SCET grid to '%s'", output)
self.scet_mprs.write(hdf5, 'scet_mprs')
logger.debug("Writing test-mass beatnotes sampled to SCET grid to '%s'", output)
self.scet_tmi_carrier_offsets.write(hdf5, 'scet_tmi_carrier_offsets')
self.scet_tmi_carrier_fluctuations.write(hdf5, 'scet_tmi_carrier_fluctuations')
self.scet_tmi_usb_offsets.write(hdf5, 'scet_tmi_usb_offsets')
self.scet_tmi_usb_fluctuations.write(hdf5, 'scet_tmi_usb_fluctuations')
logger.debug("Writing reference beatnotes sampled to SCET grid to '%s'", output)
self.scet_rfi_carrier_offsets.write(hdf5, 'scet_rfi_carrier_offsets')
self.scet_rfi_carrier_fluctuations.write(hdf5, 'scet_rfi_carrier_fluctuations')
self.scet_rfi_usb_offsets.write(hdf5, 'scet_rfi_usb_offsets')
self.scet_rfi_usb_fluctuations.write(hdf5, 'scet_rfi_usb_fluctuations')
logger.debug("Writing inter-spacecraft beatnotes with electronic delay to '%s'", output)
self.electro_isi_carrier_offsets.write(hdf5, 'electro_isi_carrier_offsets')
......
......@@ -243,26 +243,83 @@ def ranging(fs, size, asd):
return white(fs, size, asd)
def testmass(fs, size, asd, fknee):
def testmass(fs, size, asd, fknee, fbreak, frelax, shape):
"""Generate test-mass acceleration noise [m/s].
Expressed in acceleration, the noise power spectrum reads
S_delta(f) [ms^(-2)] = (asd)^2 [ 1 + (fknee / f)^2 ].
S_delta(f) [ms^(-2)] =
(asd)^2 [ 1 + (fknee / f)^2 ] [ 1 + (f / fbreak)^4)].
Multiplying by 1 / (2π f)^2 yields the noise as a velocity,
S_delta(f) [m/s] = (asd / 2π)^2 [ f^(-2) + (fknee^2 / f^4) ]
= (asd / 2π)^2 f^(-2) + (asd fknee / 2π)^2 f^(-4).
S_delta(f) [m/s] = (asd / 2π)^2 [ f^(-2) + (fknee^2 / f^4)
+ f^2 / fbreak^4 + fknee^2 / fbreak^4 ]
= (asd fknee / 2π)^2 f^(-4)
+ (asd / 2π)^2 f^(-2)
+ (asd fknee / (2π fbreak^2))^2
+ (asd / (2π fbreak^2)^2 f^2,
which corresponds to the incoherent sum of an infrared, a red, a white,
and a violet noise.
A relaxation for more pessimistic models extending below the official LISA
band of 1E-4 Hz can be added using the 'lowfreq-relax' shape, in which case
the noise in acceleration picks up an additional f^(-4) term,
S_delta(f) [ms^(-2)] = ... [ 1 + (frelax / f)^4 ].
In velocity, this corresponds to additional terms,
S_delta(f) [m/s] = ... [ 1 + (frelax / f)^4 ]
= ... + (asd fknee frelax^2 / 2π)^2 f^(-8)
+ (asd frelax^2 / 2π)^2 f^(-6)
+ (asd fknee frelax^2 / (2π fbreak^2))^2 f^(-4)
+ (asd frelax^2 / (2π fbreak^2)^2 f^(-2).
Args:
asd: amplitude spectral density [ms^(-2)/sqrt(Hz)]
fknee: cutoff frequency [Hz]
fknee: low-frequency cutoff frequency [Hz]
fbreak: high-frequency break frequency [Hz]
frelax: low-frequency relaxation frequency [Hz]
shape: spectral shape, either 'original' or 'lowfreq-relax'
"""
logger.debug("Generating test-mass noise (fs=%s Hz, size=%s, "
"asd=%s ms^(-2)/sqrt(Hz), fknee=%s Hz)", fs, size, asd, fknee)
return red(fs, size, asd / (2 * pi)) \
+ infrared(fs, size, asd * fknee / (2 * pi))
logger.debug(
"Generating test-mass noise (fs=%s Hz, size=%s, "
"asd=%s ms^(-2)/sqrt(Hz), fknee=%s Hz, fbreak=%s Hz, "
"frelax=%s Hz, shape=%s)",
fs, size, asd, fknee, fbreak, frelax, shape
)
if shape == 'original':
return (
infrared(fs, size, asd * fknee / (2 * pi))
+ red(fs, size, asd / (2 * pi))
+ white(fs, size, asd * fknee / (2 * pi * fbreak**2))
+ violet(fs, size, asd / (2 * pi * fbreak**2))
)
if shape == 'lowfreq-relax':
# We need to integrate infrared noises to get f^(-6) and f^(-8) noises
# Start with f^(-4) noises
relaxation1 = infrared(fs, size, asd * frelax**2 / (2 * pi))
relaxation2 = infrared(fs, size, asd * fknee * frelax**2 / (2 * pi))
# Integrate once for f^(-6)
relaxation1 = np.cumsum(relaxation1) * (2 * pi / fs)
relaxation2 = np.cumsum(relaxation2) * (2 * pi / fs)
# Integrate twice for f^(-8)
relaxation2 = np.cumsum(relaxation2) * (2 * pi / fs)
# Add the other components to the original noise
infrared_asd = asd * fknee * np.sqrt(1 + (frelax / fbreak)**4) / (2 * pi)
red_asd = asd * np.sqrt(1 + (frelax / fbreak)**4) / (2 * pi)
return (
relaxation2 # f^(-8)
+ relaxation1 # f^(-6)
+ infrared(fs, size, infrared_asd)
+ red(fs, size, red_asd)
+ white(fs, size, asd * fknee / (2 * pi * fbreak**2))
+ violet(fs, size, asd / (2 * pi * fbreak**2))
)
raise ValueError(f"invalid test-mass noise spectral shape '{shape}'")
def oms(fs, size, asd, fknee):
"""Generate optical metrology system (OMS) noise allocation [ffd].
......