user57437: For a project where I intend to use an arduino and an accelerometer to get rpm of an diesel / gasoline engine i intend to use following solution.
I glued an mma8453 accelerometer to a neodium magnet which will be placed on a metallic part of the engine.
Using an arduino I am reading values from accelerometter and calculate one final value which is sqrt (acc.x^2 + acc.y^2 + acc.z^2), and also get millis for each value.
The values looks like this:
First column = millis, second acc value and third I identified maximums and millis since last maximum for them. Assuming that for an engine you have to max's per revolution I can calculate rpm of the engine.
My issue is that I have no idea how to write a function to do that.
Any help really appreciated
Thanks
269503  310     
269504  303     
269506  334     
269507  326     
269509  331     
269510  330     
269512  380     
269513  377     
269514  392     
269516  400     
269517  339     
269519  393     
269520  368     
269521  353     
269523  346     
269524  316     
269527  330     
269528  281     
269530  196     
269531  195     
269532  162     
269534  119     
269535  140     
269537  132     
269538  90      
269539  99      
269541  122     
269542  168     
269544  224     
269545  219     
269546  211     
269548  268     
269549  301     
269551  334     
269552  332     
269554  316     
269555  329     
269556  379     
269558  363     
269559  381     
269561  388     
269562  403     
269564  400     
269565  420 49  
269566  388     
269569  360     
269570  346     
269572  259     
269573  276     
269574  284     
269576  242     
269577  207     
269579  192     
269580  145     
269582  119     
269583  113     
269584  45      
269586  125     
269587  142     
269589  123     
269590  205     
269591  243     
269593  304     
269594  308     
269596  305     
269597  298     
269599  321     
269600  340     
269601  360     
269603  380     
269604  384     
269606  370     
269607  372     
269608  413     
269611  441 46  
269612  395     
269614  355     
269615  332     
269617  290     
269618  317     
269619  286     
269621  227     
269622  190     
269624  172     
269625  199     
269627  146     
269628  154     
269629  177     
269631  176     
269632  156     
269634  136     
269635  174     
269636  212     
269638  260     
269639  318     
269641  291     
269642  321     
269644  304     
269645  315     
269646  343     
269648  339     
269649  360     
269651  393     
269652  390     
269654  305     
269656  405     
269657  458 46  
269659  358     
269660  372     
269662  339     
269663  277     
269664  298     
269666  241     
269667  203     
269669  208     
269670  185     
269672  130     
269673  165     
269674  105     
269676  87      
269677  109     
269679  128     
269680  176     
269681  229     
269683  226     
269684  271     
269686  343     
269687  307     
269688  320     
269690  318     
269691  322     
269693  322     
269694  369     
269697  363     
269698  413     
269699  399     
269701  388     
269702  426 45  
269704  395     
269705  380     
269707  342     
269708  311     
269709  309     
269711  315     
269712  221     
269714  227     
269715  198     
269716  113     
269718  111     
269719  88      
269721  44      
269722  69      
269723  107     
269725  158     
269726  200     
269728  232     
269729  247     
269731  313     
269732  338     
269733  348     
269735  366     
269736  339     
269739  348     
269740  377     
269741  378     
269743  385     
269744  394     
269746  403     
269747  447     
269749  454 47  
269750  398     
269751  380     
269753  302     
269754  245     
269756  299     
269757  310     
269758  238     
269760  206     
269761  180     
269763  108     
269764  81      
269766  83      
269767  11      
269768  119     
269770  106     
269771  169     
269773  245     
269774  270     
269775  311     
269777  338     
269778  341     
269780  332     
269782  337     
269783  337     
269785  380     
269786  387     
269788  382     
269789  373     
269791  398     
269792  414 43  
269793  408     
269795  387     
269796  374     
269798  285     
269799  290     
269801  326     
269802  266     
269803  211     
269805  221     
269806  191     
269808  120     
269809  175     
269810  63      
269812  147     
269813  59      
269815  120     
269816  183     
269817  196     
269819  252     
269820  301     
269822  334     
269824  325     
269826  334     
269827  310     
269828  351     
269830  354     
269831  358     
269833  373     
269834  379     
269835  332     
269837  386     
269838  404 46  
269840  383     
269841  385     
269843  332     
269844  266     
269845  301     
269847  282     
269848  188     
269850  199     
269851  182     
269852  128     
269854  163     
269855  119     
269857  120     
269858  140     
269860  120     
269861  129     
269862  212     
269864  230     
269865  241     
269868  290     
269869  304     
269871  340     
269872  350     
269873  327     
269875  323     
269876  351     
269878  369     
269879  387     
269880  379     
269882  380     
269883  421 45  
269885  373     
269886  364     
269888  360     
269889  305     
269890  302     
269892  266     
269893  249     
269895  202     
269896  229     
269897  136     
269899  122     
269900  128     
269902  49      
269903  98      
269904  107     
269906  158     
269907  211     
269910  235     
269911  225     
269913  269     
269914  314     
269915  341     
269917  335     
269918  325     
269920  348     
269921  410     
269923  375     
269924  404     
269925  419     
269927  425     
269928  442 45  
269930  436     
269931  400     
269932  393     
269934  326     
269935  259     
269937  307     
269938  265     
269940  220     
269941  199     
269942  183     
269944  86      
269945  83      
269947  98      
269948  23      
269949  65      
269952  124     
269953  152     
269955  221     
269956  249     
269957  287     
269959  350     
269960  331     
269962  323     
269963  341     
269964  338     
269966  389     
269967  388     
269969  380     
269970  370     
269972  358     
269973  415 45  
269974  403     
269976  377     
269977  381     
269979  338     
269980  328     
269982  284     
269983  246     
269984  197     
269986  178     
269987  230     
269989  162     
269990  171     
269991  141     
269993  258     
269995  138     
269997  111     
269998  163     
270000  212     
270001  192     
270002  304     
270004  287     
270005  294     
270007  349     
270008  309     
270009  334     
270011  332     
270012  364     
270014  361     
270015  408     
270017  336     
270018  421     
270019  432 46  
270021  361     
270022  361     
270024  342     
270025  271     
270027  288     
270028  310     
270029  235     
270031  218     
270032  166     
270034  136     
270035  146     
270036  102     
270039  78      
270040  99      
270042  122     
270043  105     
270044  181     
270046  222     
270047  293     
270049  299     
270050  299     
270052  318     
270053  349     
270054  344     
270056  349     
270057  382     
270059  381     
270060  412     
270062  380     
270063  397     
270064  425 45  
270066  381     
270067  367     
270069  332     
270070  272     
270071  291     
270073  296     
270074  178     
270076  177     
270077  190     
270080  123     
270081  146     
270082  107     
270084  85      
270085  108     
270087  117     
270088  148     
270089  234     
270091  223     
270092  238     
270094  283     
270095  301     
270097  332     
270098  331     


user20245: I think most cars use a magnetic pickup next to the fly wheel to derive engine timing.  The method picked here is likely to have several problems.  For example, how to avoid picking up the shock of more than one cylinder compressing and exploding.  Or, perhaps the motor developed on is out of balance.  And a more nominal motor vibrates less making the project difficult to apply universally.
That said, using FFTs (Fast Fourier Transforms) is an alternative but fundamentally similar approach to detecting zero crossings for finding fundamental frequencies, and hence the engine's RPM. 
This "FFT on an Arduino" article describes how to implement Fast Fourier Transforms on an Arduino.  It uses the FFT libraries hosted here on github.

user7508: The approach suggested by tttapa is an excellent suggestion. But just to
give you more options, you can consider using an hysteretic crossing
detector instead of a filter. The hysteresis in a comparator consists of
having one threshold value for detecting the high → low transition, and
another, higher value, for the low → high transition. This tends to
provide good immunity against noise, as noise spikes will not be able to
trigger false transitions as long as they are smaller than the
hysteresis (i.e. the difference between the thresholds).
Here is an example function that performs this detection. If you feed it
all your data samples, it will always return false, except exactly
once per signal period.
const int LOW_THRESHOLD = 220;
const int HIGH_THRESHOLD = 300;

/* Returns true when a high threshold crossing is detected. */
void crossing_detector(int x)
{
    static enum { BELOW, ABOVE } state;
    if (state == BELOW && x >= HIGH_THRESHOLD) {
        state = ABOVE;
        return true;
    }
    if (state == ABOVE && x < LOW_THRESHOLD) {
        state = BELOW;
    }
    return false;
}

Now, it is perfectly possible to combine tttapa's filter idea with this
hysteretic crossing detector. The issue with the low pass filter is that
it is tricky to find the right time constant. If you filter too
aggressively, you may kill the signal at high RPMs. If you don't filter
enough, you may end up keeping enough noise to trigger false zero
crossings. If you use an hysteretic detector instead of a plain
zero-crossing detector, then you will be able to use less aggressive
filtering, as you will not be sensitive to the small residual noise.

user37857: A simple approach would be to filter the signal using a low-pass filter, and then just calculate the time between two zero crossings.  
Here's an example that works on your data. It's written in Python for easy testing and plotting, but trans-coding it to C++ should be trivial.

I used an Exponential Moving Average low-pass filter to get rid of the noise. Depending on the frequencies involved, you can change the shift factor. The higher this factor, the lower the cut-off frequency, and the greater the phase shift. 
I chose a fixed DC offset, but if necessary, you could use another (very slow) filter to get rid of it before applying the zero-cross detection.  
If you want to, you could use both zero crossings. Use the average of the two last RPM measurements in that case, to compensate for asymmetric waveforms.
You could add another EMA to smooth out the RPM measurements.
class ZeroCrossRPMCounter:
    def __init__(self,
                 max_rpm: float):
        self.min_distance = int(round(60_000 / max_rpm))

        self.first_crossing = True
        self.time_of_last_crossing = 0
        self.rpm = float('nan')
        self.previous_value = 0

    def __call__(self, time_ms: int, value: int):
        new_crossing = self.previous_value >= 0 and value < 0
        if new_crossing:
            if self.first_crossing:
                self.time_of_last_crossing = time_ms
                self.first_crossing = False
            elif (time_ms - self.time_of_last_crossing) >= self.min_distance:
                self.rpm = 60_000 / (time_ms - self.time_of_last_crossing)
                self.time_of_last_crossing = time_ms
            else:
                new_crossing = False
        self.previous_value = value
        return new_crossing


class EMA_shift_round:
    def __init__(self, shiftfac: int, initial: int = 0):
        self.shiftfac = shiftfac
        self.fixedPointOneHalf = 1 << (shiftfac - 1)
        self.z = initial << shiftfac

    def __call__(self, x: int):
        self.z += x
        shifted = (self.z + self.fixedPointOneHalf) >> self.shiftfac
        self.z -= shifted
        return shifted


if __name__ == '__main__':
    import numpy as np
    from matplotlib import pyplot as plt

    # Load the data
    data = np.genfromtxt('rpm-ctr-data.csv', delimiter='\t', dtype=np.int)
    time, signal = data[:, 0], data[:, 1]
    t_start, t_end = time[0] * 1e-3, time[-1] * 1e-3

    # Find the DC offset
    offset = int(round(np.mean(signal)))
    print(offset)

    # Initialize the filter and RPM counter
    lp_filter = EMA_shift_round(3)
    counter = ZeroCrossRPMCounter(2000)

    # Iterate over the data, filter it, and count the RPM
    peaks = []
    rpms = []
    filtered = np.zeros(len(time))
    for n in range(len(time)):
        filtered[n] = lp_filter(signal[n] - offset)
        new_crossing = counter(time[n], filtered[n])
        if new_crossing:
            peaks.append(time[n] * 1e-3)
            rpms.append(counter.rpm)

    # Plot the results
    plt.figure(figsize=(10, 6))
    plt.subplot(3, 1, 1)
    plt.title('Original signal')
    plt.plot(time * 1e-3, signal)
    for peak in peaks:
        plt.axvline(peak, color='red')
    plt.xlim((t_start, t_end))
    plt.subplot(3, 1, 2)
    plt.title('Filtered signal')
    plt.plot(time * 1e-3, filtered)
    for peak in peaks:
        plt.axvline(peak, color='red')
    plt.axhline(0, color='black', linestyle=':')
    plt.xlim((t_start, t_end))
    plt.subplot(3, 1, 3)
    plt.title('RPM measurements')
    plt.plot(peaks, rpms, '-o')
    plt.axhline(np.mean(rpms[1:]), color='red', linestyle=':')
    plt.xlim((t_start, t_end))
    plt.xlabel("Time [s]")
    plt.tight_layout()
    plt.show()


57437: <p>For a project where I intend to use an arduino and an accelerometer to get rpm of an diesel / gasoline engine i intend to use following solution.
I glued an mma8453 accelerometer to a neodium magnet which will be placed on a metallic part of the engine.
Using an arduino I am reading values from accelerometter and calculate one final value which is sqrt (acc.x^2 + acc.y^2 + acc.z^2), and also get millis for each value.
The values looks like this:</p>

<p>First column = millis, second acc value and third I identified maximums and millis since last maximum for them. Assuming that for an engine you have to max's per revolution I can calculate rpm of the engine.</p>

<p>My issue is that I have no idea how to write a function to do that.</p>

<p>Any help really appreciated</p>

<p>Thanks</p>

<pre><code>269503  310     
269504  303     
269506  334     
269507  326     
269509  331     
269510  330     
269512  380     
269513  377     
269514  392     
269516  400     
269517  339     
269519  393     
269520  368     
269521  353     
269523  346     
269524  316     
269527  330     
269528  281     
269530  196     
269531  195     
269532  162     
269534  119     
269535  140     
269537  132     
269538  90      
269539  99      
269541  122     
269542  168     
269544  224     
269545  219     
269546  211     
269548  268     
269549  301     
269551  334     
269552  332     
269554  316     
269555  329     
269556  379     
269558  363     
269559  381     
269561  388     
269562  403     
269564  400     
269565  420 49  
269566  388     
269569  360     
269570  346     
269572  259     
269573  276     
269574  284     
269576  242     
269577  207     
269579  192     
269580  145     
269582  119     
269583  113     
269584  45      
269586  125     
269587  142     
269589  123     
269590  205     
269591  243     
269593  304     
269594  308     
269596  305     
269597  298     
269599  321     
269600  340     
269601  360     
269603  380     
269604  384     
269606  370     
269607  372     
269608  413     
269611  441 46  
269612  395     
269614  355     
269615  332     
269617  290     
269618  317     
269619  286     
269621  227     
269622  190     
269624  172     
269625  199     
269627  146     
269628  154     
269629  177     
269631  176     
269632  156     
269634  136     
269635  174     
269636  212     
269638  260     
269639  318     
269641  291     
269642  321     
269644  304     
269645  315     
269646  343     
269648  339     
269649  360     
269651  393     
269652  390     
269654  305     
269656  405     
269657  458 46  
269659  358     
269660  372     
269662  339     
269663  277     
269664  298     
269666  241     
269667  203     
269669  208     
269670  185     
269672  130     
269673  165     
269674  105     
269676  87      
269677  109     
269679  128     
269680  176     
269681  229     
269683  226     
269684  271     
269686  343     
269687  307     
269688  320     
269690  318     
269691  322     
269693  322     
269694  369     
269697  363     
269698  413     
269699  399     
269701  388     
269702  426 45  
269704  395     
269705  380     
269707  342     
269708  311     
269709  309     
269711  315     
269712  221     
269714  227     
269715  198     
269716  113     
269718  111     
269719  88      
269721  44      
269722  69      
269723  107     
269725  158     
269726  200     
269728  232     
269729  247     
269731  313     
269732  338     
269733  348     
269735  366     
269736  339     
269739  348     
269740  377     
269741  378     
269743  385     
269744  394     
269746  403     
269747  447     
269749  454 47  
269750  398     
269751  380     
269753  302     
269754  245     
269756  299     
269757  310     
269758  238     
269760  206     
269761  180     
269763  108     
269764  81      
269766  83      
269767  11      
269768  119     
269770  106     
269771  169     
269773  245     
269774  270     
269775  311     
269777  338     
269778  341     
269780  332     
269782  337     
269783  337     
269785  380     
269786  387     
269788  382     
269789  373     
269791  398     
269792  414 43  
269793  408     
269795  387     
269796  374     
269798  285     
269799  290     
269801  326     
269802  266     
269803  211     
269805  221     
269806  191     
269808  120     
269809  175     
269810  63      
269812  147     
269813  59      
269815  120     
269816  183     
269817  196     
269819  252     
269820  301     
269822  334     
269824  325     
269826  334     
269827  310     
269828  351     
269830  354     
269831  358     
269833  373     
269834  379     
269835  332     
269837  386     
269838  404 46  
269840  383     
269841  385     
269843  332     
269844  266     
269845  301     
269847  282     
269848  188     
269850  199     
269851  182     
269852  128     
269854  163     
269855  119     
269857  120     
269858  140     
269860  120     
269861  129     
269862  212     
269864  230     
269865  241     
269868  290     
269869  304     
269871  340     
269872  350     
269873  327     
269875  323     
269876  351     
269878  369     
269879  387     
269880  379     
269882  380     
269883  421 45  
269885  373     
269886  364     
269888  360     
269889  305     
269890  302     
269892  266     
269893  249     
269895  202     
269896  229     
269897  136     
269899  122     
269900  128     
269902  49      
269903  98      
269904  107     
269906  158     
269907  211     
269910  235     
269911  225     
269913  269     
269914  314     
269915  341     
269917  335     
269918  325     
269920  348     
269921  410     
269923  375     
269924  404     
269925  419     
269927  425     
269928  442 45  
269930  436     
269931  400     
269932  393     
269934  326     
269935  259     
269937  307     
269938  265     
269940  220     
269941  199     
269942  183     
269944  86      
269945  83      
269947  98      
269948  23      
269949  65      
269952  124     
269953  152     
269955  221     
269956  249     
269957  287     
269959  350     
269960  331     
269962  323     
269963  341     
269964  338     
269966  389     
269967  388     
269969  380     
269970  370     
269972  358     
269973  415 45  
269974  403     
269976  377     
269977  381     
269979  338     
269980  328     
269982  284     
269983  246     
269984  197     
269986  178     
269987  230     
269989  162     
269990  171     
269991  141     
269993  258     
269995  138     
269997  111     
269998  163     
270000  212     
270001  192     
270002  304     
270004  287     
270005  294     
270007  349     
270008  309     
270009  334     
270011  332     
270012  364     
270014  361     
270015  408     
270017  336     
270018  421     
270019  432 46  
270021  361     
270022  361     
270024  342     
270025  271     
270027  288     
270028  310     
270029  235     
270031  218     
270032  166     
270034  136     
270035  146     
270036  102     
270039  78      
270040  99      
270042  122     
270043  105     
270044  181     
270046  222     
270047  293     
270049  299     
270050  299     
270052  318     
270053  349     
270054  344     
270056  349     
270057  382     
270059  381     
270060  412     
270062  380     
270063  397     
270064  425 45  
270066  381     
270067  367     
270069  332     
270070  272     
270071  291     
270073  296     
270074  178     
270076  177     
270077  190     
270080  123     
270081  146     
270082  107     
270084  85      
270085  108     
270087  117     
270088  148     
270089  234     
270091  223     
270092  238     
270094  283     
270095  301     
270097  332     
270098  331     
</code></pre>
 20245: <p>I think most cars use a magnetic pickup next to the fly wheel to derive engine timing.  The method picked here is likely to have several problems.  For example, how to avoid picking up the shock of more than one cylinder compressing and exploding.  Or, perhaps the motor developed on is out of balance.  And a more nominal motor vibrates less making the project difficult to apply universally.</p>

<p>That said, using <a href="https://en.wikipedia.org/wiki/Fast_Fourier_transform" rel="nofollow noreferrer">FFTs (Fast Fourier Transforms)</a> is an alternative but fundamentally similar approach to detecting zero crossings for finding fundamental frequencies, and hence the engine's RPM. </p>

<p>This <a href="https://www.norwegiancreations.com/2017/08/what-is-fft-and-how-can-you-implement-it-on-an-arduino/" rel="nofollow noreferrer">"FFT on an Arduino"</a> article describes how to implement Fast Fourier Transforms on an Arduino.  It uses the FFT libraries hosted <a href="https://github.com/kosme/arduinoFFT" rel="nofollow noreferrer">here on github</a>.</p>
 7508: <p>The approach suggested by tttapa is an excellent suggestion. But just to
give you more options, you can consider using an <a href="https://en.wikipedia.org/wiki/Hysteresis" rel="nofollow noreferrer">hysteretic</a> crossing
detector instead of a filter. The hysteresis in a comparator consists of
having one threshold value for detecting the high → low transition, and
another, higher value, for the low → high transition. This tends to
provide good immunity against noise, as noise spikes will not be able to
trigger false transitions as long as they are smaller than the
hysteresis (i.e. the difference between the thresholds).</p>

<p>Here is an example function that performs this detection. If you feed it
all your data samples, it will always return <code>false</code>, except exactly
<em>once</em> per signal period.</p>

<pre class="lang-cpp prettyprint-override"><code>const int LOW_THRESHOLD = 220;
const int HIGH_THRESHOLD = 300;

/* Returns true when a high threshold crossing is detected. */
void crossing_detector(int x)
{
    static enum { BELOW, ABOVE } state;
    if (state == BELOW &amp;&amp; x &gt;= HIGH_THRESHOLD) {
        state = ABOVE;
        return true;
    }
    if (state == ABOVE &amp;&amp; x &lt; LOW_THRESHOLD) {
        state = BELOW;
    }
    return false;
}
</code></pre>

<p>Now, it is perfectly possible to combine tttapa's filter idea with this
hysteretic crossing detector. The issue with the low pass filter is that
it is tricky to find the right time constant. If you filter too
aggressively, you may kill the signal at high RPMs. If you don't filter
enough, you may end up keeping enough noise to trigger false zero
crossings. If you use an hysteretic detector instead of a plain
zero-crossing detector, then you will be able to use less aggressive
filtering, as you will not be sensitive to the small residual noise.</p>
 37857: <p>A simple approach would be to filter the signal using a low-pass filter, and then just calculate the time between two zero crossings.  </p>

<p>Here's an example that works on your data. It's written in Python for easy testing and plotting, but trans-coding it to C++ should be trivial.</p>

<p><a href="https://i.stack.imgur.com/MvIPH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/MvIPH.png" alt="Plot of original data, filtered data using an EMA, and the calculated RPM"></a></p>

<p>I used an <a href="https://tttapa.github.io/Pages/Mathematics/Systems-and-Control-Theory/Digital-filters/Exponential%20Moving%20Average/Exponential-Moving-Average.html" rel="nofollow noreferrer">Exponential Moving Average low-pass filter</a> to get rid of the noise. Depending on the frequencies involved, you can change the shift factor. The higher this factor, the lower the cut-off frequency, and the greater the phase shift. </p>

<p>I chose a fixed DC offset, but if necessary, you could use another (very slow) filter to get rid of it before applying the zero-cross detection.  </p>

<p>If you want to, you could use both zero crossings. Use the average of the two last RPM measurements in that case, to compensate for asymmetric waveforms.</p>

<p>You could add another EMA to smooth out the RPM measurements.</p>

<pre><code>class ZeroCrossRPMCounter:
    def __init__(self,
                 max_rpm: float):
        self.min_distance = int(round(60_000 / max_rpm))

        self.first_crossing = True
        self.time_of_last_crossing = 0
        self.rpm = float('nan')
        self.previous_value = 0

    def __call__(self, time_ms: int, value: int):
        new_crossing = self.previous_value &gt;= 0 and value &lt; 0
        if new_crossing:
            if self.first_crossing:
                self.time_of_last_crossing = time_ms
                self.first_crossing = False
            elif (time_ms - self.time_of_last_crossing) &gt;= self.min_distance:
                self.rpm = 60_000 / (time_ms - self.time_of_last_crossing)
                self.time_of_last_crossing = time_ms
            else:
                new_crossing = False
        self.previous_value = value
        return new_crossing


class EMA_shift_round:
    def __init__(self, shiftfac: int, initial: int = 0):
        self.shiftfac = shiftfac
        self.fixedPointOneHalf = 1 &lt;&lt; (shiftfac - 1)
        self.z = initial &lt;&lt; shiftfac

    def __call__(self, x: int):
        self.z += x
        shifted = (self.z + self.fixedPointOneHalf) &gt;&gt; self.shiftfac
        self.z -= shifted
        return shifted


if __name__ == '__main__':
    import numpy as np
    from matplotlib import pyplot as plt

    # Load the data
    data = np.genfromtxt('rpm-ctr-data.csv', delimiter='\t', dtype=np.int)
    time, signal = data[:, 0], data[:, 1]
    t_start, t_end = time[0] * 1e-3, time[-1] * 1e-3

    # Find the DC offset
    offset = int(round(np.mean(signal)))
    print(offset)

    # Initialize the filter and RPM counter
    lp_filter = EMA_shift_round(3)
    counter = ZeroCrossRPMCounter(2000)

    # Iterate over the data, filter it, and count the RPM
    peaks = []
    rpms = []
    filtered = np.zeros(len(time))
    for n in range(len(time)):
        filtered[n] = lp_filter(signal[n] - offset)
        new_crossing = counter(time[n], filtered[n])
        if new_crossing:
            peaks.append(time[n] * 1e-3)
            rpms.append(counter.rpm)

    # Plot the results
    plt.figure(figsize=(10, 6))
    plt.subplot(3, 1, 1)
    plt.title('Original signal')
    plt.plot(time * 1e-3, signal)
    for peak in peaks:
        plt.axvline(peak, color='red')
    plt.xlim((t_start, t_end))
    plt.subplot(3, 1, 2)
    plt.title('Filtered signal')
    plt.plot(time * 1e-3, filtered)
    for peak in peaks:
        plt.axvline(peak, color='red')
    plt.axhline(0, color='black', linestyle=':')
    plt.xlim((t_start, t_end))
    plt.subplot(3, 1, 3)
    plt.title('RPM measurements')
    plt.plot(peaks, rpms, '-o')
    plt.axhline(np.mean(rpms[1:]), color='red', linestyle=':')
    plt.xlim((t_start, t_end))
    plt.xlabel("Time [s]")
    plt.tight_layout()
    plt.show()
</code></pre>
