From a21dc48f0c534e14517d99b98b4c84f90188abce Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Bayle <j2b.bayle@gmail.com>
Date: Tue, 24 Aug 2021 22:46:21 +0200
Subject: [PATCH] Add TDIR assistance tone

---
 lisainstrument/instrument.py | 41 ++++++++++++++++++++++++++++++------
 1 file changed, 34 insertions(+), 7 deletions(-)

diff --git a/lisainstrument/instrument.py b/lisainstrument/instrument.py
index 0ed00d2..2b1c9b9 100644
--- a/lisainstrument/instrument.py
+++ b/lisainstrument/instrument.py
@@ -68,7 +68,7 @@ class Instrument:
                  # Laser sources
                  laser_asds=28.2, central_freq=2.816E14,
                  # Laser phase modulation
-                 modulation_asds='default', modulation_freqs='default',
+                 modulation_asds='default', modulation_freqs='default', tdir_tone=None,
                  # Clocks
                  clock_asds=6.32E-14, clock_offsets=0, clock_freqoffsets='default',
                  clock_freqlindrifts='default', clock_freqquaddrifts='default',
@@ -112,6 +112,7 @@ class Instrument:
                 on each MOSA [s/sqrt(Hz)], or 'default' for a default set of levels with a factor
                 10 higher on right-sided MOSAs to account for the frequency distribution system
             modulation_freqs: dictionary of modulation frequencies [Hz], or 'default'
+            tdir_tone: tuple (amplitude [Hz], frequency [Hz]) for TDIR assistance tone, or None
             clock_asds: dictionary of clock noise amplitude spectral densities
             clock_offsets: dictionary of clock offsets
             clock_freqoffsets: dictionary of clock frequency offsets [s^-1], or 'default'
@@ -196,6 +197,15 @@ class Instrument:
         else:
             self.modulation_freqs = ForEachMOSA(modulation_freqs)
 
+        if tdir_tone is not None:
+            self.tdir_tone_amplitudes = ForEachMOSA(tdir_tone[0])
+            self.tdir_tone_frequencies = ForEachMOSA(tdir_tone[1])
+            logger.debug("Using assistance tone for TDIR (amplitude=%s, frequency=%s)",
+                self.tdir_tone_amplitudes, self.tdir_tone_frequencies)
+        else:
+            self.tdir_tone_amplitudes = ForEachMOSA(0)
+            self.tdir_tone_frequencies = ForEachMOSA(0)
+
         # Clocks
         self.clock_asds = ForEachSC(clock_asds)
         self.clock_offsets = ForEachSC(clock_offsets)
@@ -579,6 +589,16 @@ class Instrument:
 
         self.simulate_noises()
 
+        ## TDIR tone
+
+        self.tdir_tones = ForEachMOSA(lambda mosa:
+            0 if self.tdir_tone_amplitudes[mosa] == 0 \
+            else self.tdir_tone_amplitudes[mosa] * numpy.sin(
+                2 * numpy.pi * self.tdir_tone_frequencies[mosa]
+                * (self.physics_t + self.local_timer_deviations[mosa[0]])
+            )
+        )
+
         ## Local beams
 
         logger.info("Simulating local beams")
@@ -1204,7 +1224,7 @@ class Instrument:
 
         logger.debug("Computing carrier fluctuations for primary local beam %s", mosa)
         self.local_carrier_fluctuations[mosa] = \
-            self.laser_noises[mosa] + self.glitch_lasers[mosa]
+            self.laser_noises[mosa] + self.glitch_lasers[mosa] + self.tdir_tones[mosa]
 
         logger.debug("Computing upper sideband offsets for primary local beam %s", mosa)
         self.local_usb_offsets[mosa] = self.offsets_freqs[mosa] \
@@ -1212,7 +1232,7 @@ class Instrument:
 
         logger.debug("Computing upper sideband fluctuations for primary local beam %s", mosa)
         self.local_usb_fluctuations[mosa] = \
-            self.laser_noises[mosa] + self.glitch_lasers[mosa] \
+            self.laser_noises[mosa] + self.glitch_lasers[mosa] + self.tdir_tones[mosa] \
             + self.modulation_freqs[mosa] * (self.clock_noise_fluctuations[sc(mosa)] + self.modulation_noises[mosa])
 
     def lock_on_adjacent(self, mosa):
@@ -1236,7 +1256,8 @@ class Instrument:
             + self.central_freq * self.backlink_noises[mosa]
         self.local_carrier_fluctuations[mosa] = adjacent_carrier_fluctuations \
             + self.offsets_freqs[mosa] * self.clock_noise_fluctuations[sc(mosa)] \
-            + self.central_freq * self.oms_ref_carrier_noises[mosa]
+            + self.central_freq * self.oms_ref_carrier_noises[mosa] \
+            + self.tdir_tones[mosa]
 
         logger.debug("Computing upper sideband offsets for local beam %s "
                      "locked on adjacent beam %s", mosa, adjacent(mosa))
@@ -1250,7 +1271,8 @@ class Instrument:
             + self.central_freq * self.backlink_noises[mosa]
         self.local_usb_fluctuations[mosa] = adjacent_usb_fluctuations \
             + self.offsets_freqs[mosa] * self.clock_noise_fluctuations[sc(mosa)] \
-            + self.central_freq * self.oms_ref_usb_noises[mosa]
+            + self.central_freq * self.oms_ref_usb_noises[mosa] \
+            + self.tdir_tones[mosa]
 
     def lock_on_distant(self, mosa):
         """Compute carrier and upper sideband offsets and fluctuations for locked laser to distant beam.
@@ -1282,7 +1304,8 @@ class Instrument:
             - (self.central_freq + self.local_carrier_offsets[mosa]) * self.local_ttls[mosa] / c
         self.local_carrier_fluctuations[mosa] = distant_carrier_fluctuations \
             + self.offsets_freqs[mosa] * self.clock_noise_fluctuations[sc(mosa)] \
-            + self.central_freq * self.oms_isc_carrier_noises[mosa]
+            + self.central_freq * self.oms_isc_carrier_noises[mosa] \
+            + self.tdir_tones[mosa]
 
         logger.debug("Computing upper sideband offsets for local beam %s "
                      "locked on distant beam %s", mosa, distant(mosa))
@@ -1305,7 +1328,8 @@ class Instrument:
             - (self.central_freq + self.local_usb_offsets[mosa]) * self.local_ttls[mosa] / c
         self.local_usb_fluctuations[mosa] = distant_usb_fluctuations \
             + self.offsets_freqs[mosa] * self.clock_noise_fluctuations[sc(mosa)] \
-            + self.central_freq * self.oms_isc_usb_noises[mosa]
+            + self.central_freq * self.oms_isc_usb_noises[mosa] \
+            + self.tdir_tones[mosa]
 
     def simulate_locking(self):
         """Simulate local beams from the locking configuration."""
@@ -1433,6 +1457,9 @@ class Instrument:
             self.glitch_tms.write(hdf5, 'glitch_tms')
             self.glitch_lasers.write(hdf5, 'glitch_lasers')
 
+            logger.debug("Writing TDIR assistance tone to %s", output)
+            self.tdir_tones.write(hdf5, 'tdir_tones')
+
             logger.debug("Writing laser noise to '%s'", output)
             self.laser_noises.write(hdf5, 'laser_noises')
 
-- 
GitLab