From 6f6811c9059158d4198e280e4dbc4c92e6f8c657 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Bayle <j2b.bayle@gmail.com>
Date: Thu, 20 Oct 2022 16:55:52 +0100
Subject: [PATCH] Update jitter noise model

---
 lisainstrument/instrument.py | 27 +++++++++++++++++++++------
 lisainstrument/noises.py     | 16 ++++++++++------
 2 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/lisainstrument/instrument.py b/lisainstrument/instrument.py
index 052f026..90105c5 100755
--- a/lisainstrument/instrument.py
+++ b/lisainstrument/instrument.py
@@ -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,
@@ -163,8 +164,12 @@ class Instrument:
                 distribution [-2.3, 2.3] mm/rad
             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]
@@ -373,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
@@ -1524,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")
diff --git a/lisainstrument/noises.py b/lisainstrument/noises.py
index f990775..57b854b 100644
--- a/lisainstrument/noises.py
+++ b/lisainstrument/noises.py
@@ -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.
-- 
GitLab