On the influence of temperature in 16 MHz clock frequency measurement

This notebook discusses the behaviour of accuracy and stability of 2 types of clock generators (ceramic resonator & quartz) under influence of temperature for highly precision frequency measurements.


In [1]:
import numpy as np
import matplotlib.pyplot as plt

Ceramic Resonator


In [2]:
data_ceramic = np.genfromtxt('temp_and_freq_error_data_ceramic')

In [3]:
freq_error_ceramic = data_ceramic[:3600, 0]
temp_ceramic = data_ceramic[:, 1]

In [4]:
t = np.arange(0, len(freq_error_ceramic))

In [5]:
fig, ax1 = plt.subplots()
plt.title("Clock frequency accuracy at 26 °C (ceramic resonator)")
plt.grid()
ax1.plot(t / 60, freq_error_ceramic, color='b')
ax1.set_ylabel("Frequency Error / Hz", color='b')
ax1.set_xlabel("t / min")
ax1.tick_params('y', colors='b')

plt.ylim([7000,9000])
ax2 = ax1.twinx()
ax2.plot(t / 60, (freq_error_ceramic / 16000000) * 10**6, color='r')
ax2.set_ylabel('PPM', color='r')
ax2.tick_params('y', colors='r')
fig.tight_layout()


plt.show()


Standard Deviation


In [6]:
std_dev_ceramic = np.std(freq_error_ceramic)

Average


In [7]:
average_ceramic = np.average(freq_error_ceramic)

In [8]:
ppm_average_ceramic = (average_ceramic / 16000000) * 10**6

In [9]:
ppm_average_ceramic


Out[9]:
495.63444444444445

The frequency tolerance of the used ceramic resonator of the Arduino UNO is approx. 500 ppm. Consequently, this means for mains frequency measurements:


In [10]:
(50 * 500 * 10**-6) * 1000 # (mains frequency * ppm) * 1000 in mHZ


Out[10]:
24.999999999999996

In [11]:
np.max(freq_error_ceramic)


Out[11]:
8960.0

In [12]:
np.min(freq_error_ceramic)


Out[12]:
6848.0

The error for mains frequency measurements is 25 mHz.

Ceramic resonator behaviour under the influence of increasing temperature


In [13]:
data_temp_ceramic = np.genfromtxt('ceramic_behaviour_increasing_temperature')

In [14]:
freq_error_temp_ceramic = data_temp_ceramic[:, 0]
increasing_temp_ceramic = data_temp_ceramic[:, 1]

In [17]:
fig, ax3 = plt.subplots()
plt.title("Clock frequency stability (ceramic resonator)")
plt.grid()
ax3.plot(increasing_temp_ceramic, freq_error_temp_ceramic, color='b')
ax3.set_ylabel("Frequency Error / Hz", color='b')
ax3.set_xlabel("T / °C")
ax3.tick_params('y', colors='b')
ax4 = ax3.twinx()
ax4.plot(increasing_temp_ceramic, (freq_error_temp_ceramic  / 16000000) * 10**6, color='r')
ax4.set_ylabel('PPM', color='r')
ax4.tick_params('y', colors='r')
fig.tight_layout()
plt.show()


Quartz


In [18]:
data_quartz = np.genfromtxt('temp_and_freq_error_data_quartz')

In [19]:
freq_error_quartz = data_quartz[:3600, 0]
temp_quartz = data_quartz[:, 1]

In [20]:
t = np.arange(0, len(freq_error_quartz))

In [22]:
fig, ax5 = plt.subplots()
plt.title("Clock frequency accuracy at 26 °C (quartz)")
plt.grid()
ax5.plot(t / 60, freq_error_quartz, color='b')
ax5.set_ylabel("Frequency Error / Hz", color='b')
ax5.tick_params('y', colors='b')
ax5.set_xlabel("t / min")
ax6 = ax5.twinx()
ax6.plot(t / 60, (freq_error_quartz / 16000000) * 10**6, color='r')
ax6.set_ylabel('PPM', color='r')
ax6.tick_params('y', colors='r')
fig.tight_layout()
plt.show()


Standard Deviation


In [23]:
std_dev_quartz = np.std(freq_error_quartz)

Average


In [24]:
average_quartz = np.average(freq_error_quartz)

In [25]:
ppm_average_quartz = (average_quartz / 16000000) * 10**6

In [26]:
ppm_average_quartz


Out[26]:
2.2266666666666666

In [31]:
average_quartz


Out[31]:
35.626666666666665

In [32]:
np.max(freq_error_quartz)


Out[32]:
256.0

In [33]:
np.min(freq_error_quartz)


Out[33]:
-192.0

Quartz behaviour under the influence of increasing temperature


In [27]:
data_temp_quartz = np.genfromtxt('quartz_behaviour_increasing_temperature.txt')

In [28]:
freq_error_temp_quartz = data_temp_quartz[:, 0]
increasing_temp_quartz = data_temp_quartz[:, 1]

In [30]:
fig, ax7 = plt.subplots()
plt.title("Clock frequency stability (quartz)")
plt.grid()
ax7.plot(increasing_temp_quartz, freq_error_temp_quartz, color='b')
ax7.set_ylabel("Frequency Error / Hz", color='b')
ax7.tick_params('y', colors='b')
ax7.set_xlabel("T / °C")
ax8 = ax7.twinx()
ax8.plot(increasing_temp_quartz, (freq_error_temp_quartz / 16000000) * 10**6, color='r')
ax8.set_ylabel('PPM', color='r')
ax8.tick_params('y', colors='r')
fig.tight_layout()
plt.show()



In [ ]: