diff --git a/tracking/monitors/__init__.py b/tracking/monitors/__init__.py
index 2f94fe639ee6b2337461aa8155f57816b4c4e70b..1d186dae35b7e1759984464db4dacf838da6fc71 100644
--- a/tracking/monitors/__init__.py
+++ b/tracking/monitors/__init__.py
@@ -9,7 +9,8 @@ from mbtrack2.tracking.monitors.monitors import (Monitor, BunchMonitor,
                                                  PhaseSpaceMonitor,
                                                  BeamMonitor,
                                                  ProfileMonitor,
-                                                 WakePotentialMonitor)
+                                                 WakePotentialMonitor,
+                                                 TuneMonitor)
 from mbtrack2.tracking.monitors.plotting import (plot_bunchdata, 
                                                  plot_phasespacedata,
                                                  plot_profiledata,
diff --git a/tracking/monitors/monitors.py b/tracking/monitors/monitors.py
index 0382055537a4508fcd02f6ad24e30184c7224aa7..1cd781da2df3e508129c8e243b68c68ce3ca177d 100644
--- a/tracking/monitors/monitors.py
+++ b/tracking/monitors/monitors.py
@@ -10,6 +10,8 @@ during tracking.
 
 import numpy as np
 import h5py as hp
+import PyNAFF as pnf
+import random
 from mbtrack2.tracking.element import Element
 from mbtrack2.tracking.particles import Bunch, Beam
 from abc import ABCMeta
@@ -749,4 +751,182 @@ class WakePotentialMonitor(Monitor):
             self.to_buffer(wake_potential_to_save)
         self.track_count += 1
 
-            
\ No newline at end of file
+class TuneMonitor(Monitor):
+    """
+    Monitor tunes in horizontal, vertical, and longitudinal plane. 
+    
+    Parameters
+    ----------
+    ring : Synchrotron object
+    bunch_number : int
+        Bunch to monitor
+    mp_number : int or float
+        Total number of macro-particles in the bunch.
+    sample_size : int or float
+        Number of macro-particles to be used for tune computation. This number
+        needs not to exceed mp_number.
+    file_name : string, optional
+        Name of the HDF5 where the data will be stored. Must be specified
+        the first time a subclass of Monitor is instancied and must be None
+        the following times.
+    save_every : int or float, optional
+        Set the frequency of the save. The tune is computed saved every 
+        save_every call of the montior.
+    buffer_size : int or float, optional
+        Size of the save buffer.
+    total_size : int or float, optional
+        Total size of the save. The following relationships between the 
+        parameters must exist: 
+            total_size % buffer_size == 0
+            number of call to track / save_every == total_size    
+    mpi_mode : bool, optional
+        If True, open the HDF5 file in parallel mode, which is needed to
+        allow several cores to write in the same file at the same time.
+        If False, open the HDF5 file in standard mode.
+        
+    Methods
+    -------
+    track(bunch):
+        Save tune data.
+    
+    """
+    
+    def __init__(self, ring, bunch_number, mp_number, sample_size, file_name=None, 
+                 save_every=10, buffer_size=5, total_size=10, mpi_mode=True):
+        
+        self.ring = ring
+        self.bunch_number = bunch_number
+        group_name = "TuneData_" + str(self.bunch_number)
+
+        dict_buffer = {"tune":(3, buffer_size), "tune_spread":(3, buffer_size,)}
+        dict_file = {"tune":(3, total_size), "tune_spread":(3, total_size,)}
+        
+        self.monitor_init(group_name, save_every, buffer_size, total_size,
+                          dict_buffer, dict_file, file_name, mpi_mode)
+        
+        self.dict_buffer = dict_buffer
+        self.dict_file = dict_file
+        
+        self.sample_size = int(sample_size)
+        self.x = np.zeros((self.sample_size, save_every+1))
+        self.y = np.zeros((self.sample_size, save_every+1))
+        self.tau = np.zeros((self.sample_size, save_every+1))
+        
+        index = np.arange(0, int(mp_number))
+        self.index_sample = sorted(random.sample(list(index), self.sample_size))
+        
+        self.save_every = save_every
+        self.save_count = 0
+        
+        self.buffer_count = 1
+        
+    def track(self, object_to_save):
+        """
+        Save tune data.
+
+        Parameters
+        ----------
+        object_to_save : Beam or Bunch object
+
+        """
+        skip = False
+        if isinstance(object_to_save, Beam):
+            if (object_to_save.mpi_switch == True):
+                if object_to_save.mpi.bunch_num == self.bunch_number:
+                    bunch = object_to_save[object_to_save.mpi.bunch_num]
+                else:
+                    skip = True
+            else:
+                bunch = object_to_save[self.bunch_number]
+        elif isinstance(object_to_save, Bunch):
+            bunch = object_to_save
+        else:
+            raise TypeError("object_to_save should be a Beam or Bunch object.")
+        
+        if skip is not True:
+            self.x[:, self.save_count] = bunch["x"][self.index_sample]
+            self.y[:, self.save_count] = bunch["y"][self.index_sample]
+            self.tau[:, self.save_count] = bunch["tau"][self.index_sample]
+            
+            self.save_count += 1
+            
+            if self.track_count > 0:
+                if self.track_count % self.save_every == 0:
+                    self.to_buffer(bunch)
+                    self.save_count = 0
+    
+            self.track_count += 1          
+        
+    def to_buffer(self, bunch):
+        """
+        A method to hold saved data before writing it to the output file.
+
+        """
+        mean, spread = self.get_tune(bunch)
+        self.time[self.buffer_count] = self.track_count
+        self.tune[:, self.buffer_count] = mean
+        self.tune_spread[:, self.buffer_count] = spread
+        
+        self.buffer_count += 1
+        
+        if self.buffer_count == self.buffer_size:
+            self.write()
+            self.buffer_count = 0
+            
+    def write(self):
+        """
+        Write data from buffer to output file.
+
+        """
+        self.file[self.group_name]["time"][self.write_count*self.buffer_size:(
+                    self.write_count+1)*self.buffer_size] = self.time
+
+        self.file[self.group_name]["tune"][:, 
+                self.write_count * self.buffer_size:(self.write_count+1) * 
+                self.buffer_size] = self.tune
+        self.file[self.group_name]["tune_spread"][:, 
+                self.write_count * self.buffer_size:(self.write_count+1) * 
+                self.buffer_size] = self.tune_spread
+            
+        self.file.flush()
+        self.write_count += 1
+
+    def get_tune(self, bunch):
+        """
+        Compute tune by using NAFF algorithm to indentify the fundamental
+        harmonic frequency of the particles' motion.
+
+        Parameters
+        ----------
+        bunch : Bunch object
+
+        """
+        
+        turn = self.save_every
+        freq = np.zeros((self.sample_size,3))
+        
+        for i in range(self.sample_size):
+            try:
+                freq[i,0] = pnf.naff(self.x[i,:], turns=turn-1, nterms=1)[0][1] \
+                                / self.ring.T0
+            except IndexError:
+                freq[i,0] = np.nan
+               
+            try:
+                freq[i,1] = pnf.naff(self.y[i,:], turns=turn-1, nterms=1)[0][1] \
+                                / self.ring.T0
+            except IndexError:
+                freq[i,1] = np.nan
+
+            try:              
+                freq[i,2] = pnf.naff(self.tau[i,:], turns=turn-1, nterms=1)[0][1] \
+                                / self.ring.T0
+            except IndexError:
+                freq[i,2] = np.nan
+        
+        tune_single_particle = freq / self.ring.f0
+        mean = np.nanmean(tune_single_particle, 0)
+        spread = np.nanstd(tune_single_particle, 0)
+        
+        return (mean, spread)
+    
\ No newline at end of file
diff --git a/tracking/monitors/plotting.py b/tracking/monitors/plotting.py
index de90791b3d3b595a973b8398cfc691c24f909bcc..5b02c9bb2a8ae45c488a7e9e92bdd17e10fc63b7 100644
--- a/tracking/monitors/plotting.py
+++ b/tracking/monitors/plotting.py
@@ -481,4 +481,45 @@ def plot_wakedata(filename, bunch_number, wake_type="Wlong", start=0,
         return fig
     elif streak_plot is True:
         return fig2
+    
+def plot_tunedata(filename, bunch_number):
+    """
+    Plot data recorded by TuneMonitor.
+    
+    Parameters
+    ----------
+    filename : str 
+        Name of the HDF5 file that contains the data.
+    bunch_number : int
+        Bunch to plot. This has to be identical to 'bunch_number' parameter in 
+        'BunchMonitor' object.
+        
+    Return
+    ------
+    fig : Figure
+        Figure object with the plot on it.
+
+    """
+    
+    file = hp.File(filename, "r")
+    
+    group = "TuneData_{0}".format(bunch_number)  # Data group of the HDF5 file
+    time = file[group]["time"]
+    tune = file[group]["tune"]
+    tune_spread = file[group]["tune_spread"]
+        
+    fig1, ax1 = plt.subplots()        
+    ax1.errorbar(x=time[1:], y=tune[0,1:], yerr=tune_spread[0,1:])
+    ax1.errorbar(x=time[1:], y=tune[1,1:], yerr=tune_spread[1,1:])
+    ax1.set_xlabel("Turn number")
+    ax1.set_ylabel("Transverse tunes")
+    plt.legend(["x","y"])
+    
+    fig2, ax2 = plt.subplots()        
+    ax2.errorbar(x=time[1:], y=tune[2,1:], yerr=tune_spread[2,1:])
+    ax2.set_xlabel("Turn number")
+    ax2.set_ylabel("Synchrotron tune")
+    
+    file.close()
+    return (fig1, fig2)
     
\ No newline at end of file