diff --git a/.pylintrc b/.pylintrc
index 8a1b76f74082030df0add1d4ab99205360b68ec0..4b7439a7ecbf61c934686b421f45814890899b26 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -19,3 +19,6 @@ max-locals=30
 
 # Maximum number of arguments for function / method
 max-args=10
+
+# Maximum number of public methods
+max-public-methods=30
diff --git a/lisainstrument/instrument.py b/lisainstrument/instrument.py
index 47ec2c45ad3d0c2ac1b01d18388bdabd85a41dc1..8413f7bc01ee64eef2d83ed42c5af326d2ce8649 100644
--- a/lisainstrument/instrument.py
+++ b/lisainstrument/instrument.py
@@ -12,6 +12,7 @@ import logging
 import h5py
 import scipy.interpolate
 import numpy
+import matplotlib.pyplot
 
 from .containers import ForEachSC
 from .containers import ForEachMOSA
@@ -31,7 +32,7 @@ class Instrument:
 
     def __init__(self, size=2592000, dt=0.3, t0=0,
         # Inter-spacecraft propagation
-        orbits=None, gws=None, interpolation=('lagrange', 31),
+        orbits=None, gws=None, glitches=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,
@@ -52,6 +53,7 @@ class Instrument:
             t0: initial time [s]
             orbits: path to orbit file or None for default set of proper pseudo-ranges and derivatives thereof
             gws: dictionary of gravitational-wave responses or path to gravitational-wave file
+            orbits: dictionary of glitch signals for injection points or path to glitch file
             interpolation: interpolation function or interpolation method and parameters; when using 'lagrange',
                 use a tuple ('lagrange', order) with `order` the odd interpolation order; an arbitrary function should
                 take (x, shift [number of samples]) as parameter
@@ -152,12 +154,10 @@ class Instrument:
             '12': 8.1E6, '23': 9.2E6, '31': 10.3E6, '13': 1.4E6, '32': -11.6E6, '21': -9.5E6,
         })
 
-        # Orbits and gravitational waves
-        self.gws = None
-        self.pprs = None
-        self.d_pprs = None
+        # Orbits, gravitational waves, glitches
         self.init_orbits(orbits)
         self.init_gws(gws)
+        self.init_glitches(glitches)
 
         # Interpolation and antialiasing filter
         self.init_interpolation(interpolation)
@@ -282,8 +282,8 @@ class Instrument:
             self.gw_file = gws
             logging.info("Interpolating gravitational-wave responses from GW file '%s'", self.gw_file)
             gwf = h5py.File(self.gw_file, 'r')
-            self.gws = ForEachMOSA(lambda mosa: scipy.interpolate.InterpolatedUnivariateSpline(
-                gwf['t'][:], gwf[f'l_{mosa}'][:], k=1, ext='raise')(self.physics_t)
+            self.glitch_tms = ForEachMOSA(lambda mosa: scipy.interpolate.InterpolatedUnivariateSpline(
+                gwf['t'][:], gwf[f'l_{mosa}'][:], k=5, ext='raise')(self.physics_t)
             )
             gwf.close()
         elif gws is None:
@@ -295,6 +295,26 @@ class Instrument:
             self.gw_file = None
             self.gws = ForEachMOSA(gws)
 
+    def init_glitches(self, glitches):
+        """Initialize glitches."""
+        self.glitch_tms = ForEachMOSA(0)
+        if isinstance(glitches, str):
+            self.glitch_file = glitches
+            logging.info("Interpolating glitch signals from glitch file '%s'", self.glitch_file)
+            glitchf = h5py.File(self.glitch_file, 'r')
+            for mosa in self.MOSAS:
+                inj_point = f'tm_{mosa}'
+                if inj_point in glitchf:
+                    logging.debug("Interpolating injection point '%s'", inj_point)
+                    self.glitch_tms[mosa] = scipy.interpolate.InterpolatedUnivariateSpline(
+                        glitchf['t'][:], glitchf[inj_point][:], k=5, ext='const')(self.physics_t)
+            glitchf.close()
+        elif glitches is None:
+            logging.debug("No glitch")
+            self.glitch_file = None
+        else:
+            raise ValueError(f"invalid value '{glitches}' for glitches")
+
     def disable_all_noises(self, but=None):
         """Turn off all instrumental noises.
 
@@ -540,6 +560,7 @@ class Instrument:
         logging.info("Computing test-mass carrier beatnote frequency fluctuations on TPS")
         self.tps_tm_carrier_fluctuations = ForEachMOSA(lambda mosa:
             self.adjacent_tm_carrier_fluctuations[mosa] - self.local_tm_carrier_fluctuations[mosa]
+            + self.glitch_tms[mosa]
         )
 
         logging.info("Computing test-mass upper sideband beatnote frequency offsets on TPS")
@@ -1058,3 +1079,137 @@ class Instrument:
 
         logging.info("Closing output file '%s'", output)
         hdf5.close()
+
+    def plot_fluctuations(self, output=None, skip=0):
+        """Plot measurements generated by the simulation.
+
+        Args:
+            output: output file, None to show the plots
+            skip: number of initial samples to skip [samples]
+        """
+        # Run simulation if needed
+        if not self.simulated:
+            self.simulate()
+        # Plot signals
+        logging.info("Plotting beatnote frequency fluctuations")
+        _, axes = matplotlib.pyplot.subplots(3, 1, figsize=(12, 12))
+        plot = lambda axis, x, label: axis.plot(self.t[skip:], numpy.broadcast_to(x, self.size)[skip:], label=label)
+        for mosa in self.MOSAS:
+            plot(axes[0], self.isc_carrier_fluctuations[mosa], mosa)
+            plot(axes[1], self.tm_carrier_fluctuations[mosa], mosa)
+            plot(axes[2], self.ref_carrier_fluctuations[mosa], mosa)
+        # Format plot
+        axes[0].set_title("Beatnote frequency fluctuations")
+        axes[2].set_xlabel("Time [s]")
+        axes[0].set_ylabel("Inter-spacecraft frequency [Hz]")
+        axes[1].set_ylabel("Test-mass frequency [Hz]")
+        axes[2].set_ylabel("Reference frequency [Hz]")
+        for axis in axes:
+            axis.grid()
+            axis.legend()
+        # Save or show glitch
+        if output is not None:
+            logging.info("Saving plot to %s", output)
+            matplotlib.pyplot.savefig(output, bbox_inches='tight')
+        else:
+            matplotlib.pyplot.show()
+
+    def plot_offsets(self, output=None, skip=0):
+        """Plot measurements generated by the simulation.
+
+        Args:
+            output: output file, None to show the plots
+            skip: number of initial samples to skip [samples]
+        """
+        # Run simulation if needed
+        if not self.simulated:
+            self.simulate()
+        # Plot signals
+        logging.info("Plotting beatnote frequency offsets")
+        _, axes = matplotlib.pyplot.subplots(3, 1, figsize=(12, 12))
+        plot = lambda axis, x, label: axis.plot(self.t[skip:], numpy.broadcast_to(x, self.size)[skip:], label=label)
+        for mosa in self.MOSAS:
+            plot(axes[0], self.isc_carrier_offsets[mosa], mosa)
+            plot(axes[1], self.tm_carrier_offsets[mosa], mosa)
+            plot(axes[2], self.ref_carrier_offsets[mosa], mosa)
+        # Format plot
+        axes[0].set_title("Beatnote frequency offsets")
+        axes[2].set_xlabel("Time [s]")
+        axes[0].set_ylabel("Inter-spacecraft frequency [Hz]")
+        axes[1].set_ylabel("Test-mass frequency [Hz]")
+        axes[2].set_ylabel("Reference frequency [Hz]")
+        for axis in axes:
+            axis.grid()
+            axis.legend()
+        # Save or show glitch
+        if output is not None:
+            logging.info("Saving plot to %s", output)
+            matplotlib.pyplot.savefig(output, bbox_inches='tight')
+        else:
+            matplotlib.pyplot.show()
+
+    def plot_totals(self, output=None, skip=0):
+        """Plot measurements generated by the simulation.
+
+        Args:
+            output: output file, None to show the plots
+            skip: number of initial samples to skip [samples]
+        """
+        # Run simulation if needed
+        if not self.simulated:
+            self.simulate()
+        # Plot signals
+        logging.info("Plotting beatnote total frequencies")
+        _, axes = matplotlib.pyplot.subplots(3, 1, figsize=(12, 12))
+        plot = lambda axis, x, label: axis.plot(self.t[skip:], numpy.broadcast_to(x, self.size)[skip:], label=label)
+        for mosa in self.MOSAS:
+            plot(axes[0], self.isc_carriers[mosa], mosa)
+            plot(axes[1], self.tm_carriers[mosa], mosa)
+            plot(axes[2], self.ref_carriers[mosa], mosa)
+        # Format plot
+        axes[0].set_title("Beatnote total frequencies")
+        axes[2].set_xlabel("Time [s]")
+        axes[0].set_ylabel("Inter-spacecraft frequency [Hz]")
+        axes[1].set_ylabel("Test-mass frequency [Hz]")
+        axes[2].set_ylabel("Reference frequency [Hz]")
+        for axis in axes:
+            axis.grid()
+            axis.legend()
+        # Save or show glitch
+        if output is not None:
+            logging.info("Saving plot to %s", output)
+            matplotlib.pyplot.savefig(output, bbox_inches='tight')
+        else:
+            matplotlib.pyplot.show()
+
+    def plot_mprs(self, output=None, skip=0):
+        """Plot measurements generated by the simulation.
+
+        Args:
+            output: output file, None to show the plots
+            skip: number of initial samples to skip [samples]
+        """
+        # Run simulation if needed
+        if not self.simulated:
+            self.simulate()
+        # Plot signals
+        logging.info("Plotting measured pseudo-ranges")
+        _, axes = matplotlib.pyplot.subplots(2, 1, figsize=(12, 8))
+        plot = lambda axis, x, label: axis.plot(self.t[skip:], numpy.broadcast_to(x, self.size)[skip:], label=label)
+        for mosa in self.MOSAS:
+            plot(axes[0], self.mprs[mosa], mosa)
+            plot(axes[1], numpy.gradient(self.mprs[mosa], self.dt), mosa)
+        # Format plot
+        axes[0].set_title("Measured pseudo-ranges")
+        axes[1].set_xlabel("Time [s]")
+        axes[0].set_ylabel("Pseudo-range [s]")
+        axes[1].set_ylabel("Pseudo-range derivative [s/s]")
+        for axis in axes:
+            axis.grid()
+            axis.legend()
+        # Save or show glitch
+        if output is not None:
+            logging.info("Saving plot to %s", output)
+            matplotlib.pyplot.savefig(output, bbox_inches='tight')
+        else:
+            matplotlib.pyplot.show()