Update thesis
This commit is contained in:
parent
a803d10b03
commit
6ab3ac3dff
18 changed files with 13076 additions and 208 deletions
|
|
@ -177,7 +177,7 @@ static void sr_jtdev_tdi(struct jtdev *p, int out) {
|
|||
|
||||
static void sr_jtdev_rst(struct jtdev *p, int out) {
|
||||
UNUSED(p);
|
||||
sr_gpio_write(SR_GPIO_RST, !out);
|
||||
sr_gpio_write(SR_GPIO_RST, out);
|
||||
}
|
||||
|
||||
static void sr_jtdev_tst(struct jtdev *p, int out) {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 75,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 57,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
"(129, 470)"
|
||||
]
|
||||
},
|
||||
"execution_count": 57,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 58,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
|
@ -128,7 +128,7 @@
|
|||
"3.90625"
|
||||
]
|
||||
},
|
||||
"execution_count": 58,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
|
@ -139,21 +139,13 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 61,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<ipython-input-61-530955947ba4>:1: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).\n",
|
||||
" fig, ax = plt.subplots(len(test_data), figsize=(8, 20), sharex=True)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "30b7e529e3f94c1b8241c6b6760b7e69",
|
||||
"model_id": "c7a38939cb3a42cfbfe99fe7d603e11a",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -182,21 +174,13 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 76,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<ipython-input-76-31c82486a777>:1: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).\n",
|
||||
" fig, ax = plt.subplots(len(test_data), figsize=(8, 20), sharex=True)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "9e0976ade81c4990a10fd1182f0f20d5",
|
||||
"model_id": "e6b769f15870407ba59cff627632d447",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -225,7 +209,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 62,
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
|
@ -255,7 +239,7 @@
|
|||
" 492.1875 , 496.09375, 500. ])"
|
||||
]
|
||||
},
|
||||
"execution_count": 62,
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
|
@ -266,21 +250,13 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 63,
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<ipython-input-63-888d30b0f7d6>:1: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).\n",
|
||||
" fig, axs = plt.subplots(len(test_data), figsize=(8, 20), sharex=True)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "cef58f5753084c54a648418e134775d8",
|
||||
"model_id": "db4021b95410428cb07f76edd932907c",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -388,7 +364,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.1"
|
||||
"version": "3.8.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
|
|
|||
BIN
lab-windows/fig_out/mains_voltage_spectrum-eps-converted-to.pdf
Normal file
BIN
lab-windows/fig_out/mains_voltage_spectrum-eps-converted-to.pdf
Normal file
Binary file not shown.
10404
lab-windows/fig_out/mains_voltage_spectrum.eps
Normal file
10404
lab-windows/fig_out/mains_voltage_spectrum.eps
Normal file
File diff suppressed because it is too large
Load diff
BIN
lab-windows/fig_out/ocxo_freq_stability-eps-converted-to.pdf
Normal file
BIN
lab-windows/fig_out/ocxo_freq_stability-eps-converted-to.pdf
Normal file
Binary file not shown.
1822
lab-windows/fig_out/ocxo_freq_stability.eps
Normal file
1822
lab-windows/fig_out/ocxo_freq_stability.eps
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -36,7 +36,7 @@
|
|||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"db = sqlite3.connect('data/waveform-raspi.sqlite3')"
|
||||
"db = sqlite3.connect('data/waveform-raspi-2-2.sqlite3')"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Run 000: 2020-01-31 19:05:24 - 2020-02-01 01:13:45 ( 6:08:21.589, 22126080sp)\n"
|
||||
"Run 000: 2020-03-25 16:07:36 - 2020-03-26 00:15:13 ( 8:07:37.266, 29261120sp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
@ -100,12 +100,12 @@
|
|||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "0b088957d9a24a74ab1b81d68099aa99",
|
||||
"model_id": "582c4360e293466e9baed5bc66a47883",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"HBox(children=(FloatProgress(value=0.0, max=691440.0), HTML(value='')))"
|
||||
"HBox(children=(FloatProgress(value=0.0, max=914410.0), HTML(value='')))"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
|
|
@ -133,7 +133,7 @@
|
|||
" 'SELECT seq, data FROM measurements WHERE run_id = ? ORDER BY rx_ts LIMIT ? OFFSET ?',\n",
|
||||
" (last_run, limit, n_records-limit))), total=n_records):\n",
|
||||
" \n",
|
||||
" if last_seq is None or seq == (last_seq + 1)%0xffff:\n",
|
||||
" if last_seq is None or seq == (last_seq + 1)%0x10000:\n",
|
||||
" last_seq = seq\n",
|
||||
" idx = write_index if skip_dropped_sections else i\n",
|
||||
" data[idx*record_size:(idx+1)*record_size] = np.frombuffer(chunk, dtype='<H')\n",
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"227.68691180713367"
|
||||
"227.0922848236977"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
|
|
@ -177,7 +177,7 @@
|
|||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "8a6f7e9ac8f04d1b84035c29623cfa99",
|
||||
"model_id": "ecf3e3e261c54d87b169c1eb391a67f9",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -266,7 +266,7 @@
|
|||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "a3824e732a0647d4a12e8c5a567f5dc0",
|
||||
"model_id": "cc101709475d440ea77e68bcb56ce3b7",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -299,12 +299,12 @@
|
|||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "342f89b5d55f435ab132deb5d051c926",
|
||||
"model_id": "b0202d59643548cf83b6fa6fd7580d2d",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"HBox(children=(FloatProgress(value=0.0, max=221260.0), HTML(value='')))"
|
||||
"HBox(children=(FloatProgress(value=0.0, max=292611.0), HTML(value='')))"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
|
|
@ -364,7 +364,7 @@
|
|||
" #print(coeff)\n",
|
||||
" A, mu, sigma, *_ = coeff\n",
|
||||
" f_mean[le_t] = mu\n",
|
||||
" except Exception:\n",
|
||||
" except Exception as e:\n",
|
||||
" f_mean[le_t] = np.nan"
|
||||
]
|
||||
},
|
||||
|
|
@ -376,7 +376,7 @@
|
|||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "33431729d479469cb89bcf86e22508ad",
|
||||
"model_id": "59d50a2876634433bdfb751a7a66d9ca",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -420,6 +420,8 @@
|
|||
"for i in np.where(np.isnan(f_mean))[0]:\n",
|
||||
" ax.axvspan(f_t[i], f_t[i+1], color='lightblue')\n",
|
||||
"\n",
|
||||
"formatter = matplotlib.ticker.FuncFormatter(lambda s, x: str(datetime.timedelta(seconds=s)))\n",
|
||||
"ax.xaxis.set_major_formatter(formatter)\n",
|
||||
"ax.set_xlabel('recording time t [s]')\n",
|
||||
"None"
|
||||
]
|
||||
|
|
@ -432,7 +434,7 @@
|
|||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "e8a4ecab028f4fcf8d9821dd66a42027",
|
||||
"model_id": "95cfea97c3b946ed9cdf351b16c2c45e",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -529,13 +531,80 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"execution_count": 53,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<ipython-input-53-c54c3e4ac2be>:20: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).\n",
|
||||
" fig, ax = plt.subplots(figsize=(5, 2))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "bcf4b384d95949c38a4754cdb47ae512",
|
||||
"model_id": "ad5454f664734681adb8640f480ce69a",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Text(20, 1, '50 Hz')"
|
||||
]
|
||||
},
|
||||
"execution_count": 53,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Number of samplepoints\n",
|
||||
"N = len(data)\n",
|
||||
"# sample spacing\n",
|
||||
"T = 1.0 / sampling_rate\n",
|
||||
"x = np.linspace(0.0, N*T, N)\n",
|
||||
"yf = scipy.fftpack.fft(data * sig.blackman(N))\n",
|
||||
"xf = np.linspace(0.0, 1.0/(2.0*T), N//2)\n",
|
||||
"\n",
|
||||
"yf = 2.0/N * np.abs(yf[:N//2])\n",
|
||||
"\n",
|
||||
"average_from = lambda val, start, average_width: np.hstack([val[:start], [ np.mean(val[i:i+average_width]) for i in range(start, len(val), average_width) ]])\n",
|
||||
"\n",
|
||||
"average_width = 6\n",
|
||||
"average_start = 20\n",
|
||||
"yf = average_from(yf, average_start, average_width)\n",
|
||||
"xf = average_from(xf, average_start, average_width)\n",
|
||||
"yf = average_from(yf, 200, average_width)\n",
|
||||
"xf = average_from(xf, 200, average_width)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 68,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<ipython-input-68-21b49a5af249>:1: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).\n",
|
||||
" fig, ax = plt.subplots(figsize=(6, 3))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "4a3edb0925fe47eb8751150aa7da8c22",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -550,60 +619,48 @@
|
|||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/home/user/safety-reset/lab-windows/env/lib/python3.8/site-packages/numpy/core/_asarray.py:85: ComplexWarning: Casting complex values to real discards the imaginary part\n",
|
||||
" return array(a, dtype, copy=False, order=order)\n"
|
||||
"The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"5.0"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"data = f_copy\n",
|
||||
"ys = scipy.fftpack.fft(data)\n",
|
||||
"ys = scipy.fftpack.fftshift(ys)\n",
|
||||
"#ys = 2.0/len(data) * np.abs(ys[:len(data)//2])\n",
|
||||
"#s = 3\n",
|
||||
"\n",
|
||||
"#ys = np.convolve(ys, np.ones((s,))/s, mode='valid')\n",
|
||||
"\n",
|
||||
"#xs = np.linspace(0, 5, len(data)//2)\n",
|
||||
"xs = np.linspace(-5, 5, len(data))\n",
|
||||
"\n",
|
||||
"#ys *= 2*np.pi*xs[s//2:-s//2+1]\n",
|
||||
"#ys *= xs\n",
|
||||
"\n",
|
||||
"#xs = np.linspace(len(data)/2, 1, len(data)/2)\n",
|
||||
"\n",
|
||||
"fig, ax = plt.subplots(figsize=(9,5))\n",
|
||||
"#ax.loglog(xs[s//2:-s//2+1], ys)\n",
|
||||
"#ax.loglog(xs[s//2:-s//2+1], ys)\n",
|
||||
"#ax.loglog(xs, ys)\n",
|
||||
"#ys[len(xs)//2] = 0\n",
|
||||
"#ax.set_yscale('log')\n",
|
||||
"ax.plot(xs, ys)\n",
|
||||
"fig, ax = plt.subplots(figsize=(6, 3))\n",
|
||||
"fig.tight_layout()\n",
|
||||
"ax.loglog(xf, yf)\n",
|
||||
"#ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _pos: f'{1/x:.1f}'))\n",
|
||||
"ax.set_xlabel('f [Hz]')\n",
|
||||
"ax.set_ylabel('Amplitude V [V]')\n",
|
||||
"ax.grid()\n",
|
||||
"#plt.show()\n",
|
||||
"xs[-1]"
|
||||
"ax.set_xlim([0.1, 500])\n",
|
||||
"fig.subplots_adjust(bottom=0.2)\n",
|
||||
"\n",
|
||||
"for le_f in (50, 150, 250, 350, 450):\n",
|
||||
" ax.axvline(le_f, color=(1, 0.5, 0.5), zorder=-2)\n",
|
||||
"ax.annotate('50 Hz', xy=(20, 1), xycoords='data', bbox=dict(fc='white', alpha=0.8, ec='none'))\n",
|
||||
"font = {'family' : 'normal',\n",
|
||||
" 'weight' : 'normal',\n",
|
||||
" 'size' : 10}\n",
|
||||
"matplotlib.rc('font', **font)\n",
|
||||
"fig.savefig('fig_out/mains_voltage_spectrum.eps')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 43,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<ipython-input-43-2e31f0cb9460>:21: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).\n",
|
||||
" fig, ax = plt.subplots()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "cbc03380a4234b4d8160d25e867ff5a9",
|
||||
"model_id": "37e159955f114f36824dd42da060b9ea",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -617,41 +674,49 @@
|
|||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(1.6666666666666667e-05, 0.5)"
|
||||
"[<matplotlib.lines.Line2D at 0x7fafc108cdc0>]"
|
||||
]
|
||||
},
|
||||
"execution_count": 17,
|
||||
"execution_count": 43,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Number of samplepoints\n",
|
||||
"N = len(data)\n",
|
||||
"newcopy = np.copy(f_mean[1:-2])\n",
|
||||
"N = len(newcopy)\n",
|
||||
"# sample spacing\n",
|
||||
"T = 1.0 / 10.0\n",
|
||||
"T = 1.0 / 10\n",
|
||||
"x = np.linspace(0.0, N*T, N)\n",
|
||||
"yf = scipy.fftpack.fft(data)\n",
|
||||
"xf = np.linspace(0.0, 1.0/(2.0*T), N//2)\n",
|
||||
"yf = scipy.fftpack.fft(newcopy * sig.blackman(N))\n",
|
||||
"xf = np.linspace(0.0, 10/2, N//2)\n",
|
||||
"\n",
|
||||
"yf = 2.0/N * np.abs(yf[:N//2])\n",
|
||||
"\n",
|
||||
"average_from = lambda val, start, average_width: np.hstack([val[:start], [ np.mean(val[i:i+average_width]) for i in range(start, len(val), average_width) ]])\n",
|
||||
"\n",
|
||||
"average_width = 6\n",
|
||||
"average_start = 20\n",
|
||||
"yf = average_from(yf, average_start, average_width)\n",
|
||||
"xf = average_from(xf, average_start, average_width)\n",
|
||||
"yf = average_from(yf, 200, average_width)\n",
|
||||
"xf = average_from(xf, 200, average_width)\n",
|
||||
"average_width1, average_start1 = 3, 40\n",
|
||||
"average_width2, average_start2 = 4, 100\n",
|
||||
"yf = average_from(yf, average_start1, average_width1)\n",
|
||||
"xf = average_from(xf, average_start1, average_width1)\n",
|
||||
"yf = average_from(yf, average_start2, average_width2)\n",
|
||||
"xf = average_from(xf, average_start2, average_width2)\n",
|
||||
"\n",
|
||||
"fig, ax = plt.subplots()\n",
|
||||
"ax.loglog(xf, yf)\n",
|
||||
"ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _pos: f'{1/x:.1f}'))\n",
|
||||
"ax.set_xlabel('T in s')\n",
|
||||
"ax.set_ylabel('Amplitude Δf')\n",
|
||||
"ax.set_xlabel('T [s]')\n",
|
||||
"ax.set_ylabel('Amplitude Δf [Hz]')\n",
|
||||
"\n",
|
||||
"for i, t in enumerate([60, 300, 450, 1200, 1800]):\n",
|
||||
" ax.axvline(1/t, color='red', alpha=0.5)\n",
|
||||
" ax.annotate(f'{t} s', xy=(1/t, 3e-5), xytext=(-15, 0), xycoords='data', textcoords='offset pixels', rotation=90)\n",
|
||||
"#ax.text(1/60, 10,'60 s', ha='left')\n",
|
||||
"ax.grid()\n",
|
||||
"ax.set_xlim([1/60000, 0.5])"
|
||||
"#ax.set_xlim([1/60000, 0.5])\n",
|
||||
"ax.set_ylim([5e-7, 2e-2])\n",
|
||||
"ax.plot(xf[1:], 2e-6/xf[1:])"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -662,7 +727,7 @@
|
|||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "8eb3621b74c04c4fb7dd3e919f0d1fed",
|
||||
"model_id": "671ae919bf124e72b54144310ea1602d",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
|
|
@ -672,59 +737,20 @@
|
|||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(5e-07, 0.02)"
|
||||
]
|
||||
},
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Number of samplepoints\n",
|
||||
"N = len(data)\n",
|
||||
"# sample spacing\n",
|
||||
"T = 1.0 / 10.0\n",
|
||||
"x = np.linspace(0.0, N*T, N)\n",
|
||||
"yf = scipy.fftpack.fft(data)\n",
|
||||
"xf = np.linspace(0.0, 1.0/(2.0*T), N//2)\n",
|
||||
"\n",
|
||||
"yf = 2.0/N * np.abs(yf[:N//2])\n",
|
||||
"\n",
|
||||
"average_from = lambda val, start, average_width: np.hstack([val[:start], [ np.mean(val[i:i+average_width]) for i in range(start, len(val), average_width) ]])\n",
|
||||
"\n",
|
||||
"average_width = 6\n",
|
||||
"average_start = 20\n",
|
||||
"yf = average_from(yf, average_start, average_width)\n",
|
||||
"xf = average_from(xf, average_start, average_width)\n",
|
||||
"yf = average_from(yf, 70, 4)\n",
|
||||
"xf = average_from(xf, 70, 4)\n",
|
||||
"\n",
|
||||
"fig, ax = plt.subplots()\n",
|
||||
"ax.loglog(xf, yf)\n",
|
||||
"ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _pos: f'{1/x:.1f}'))\n",
|
||||
"ax.set_xlabel('T in s')\n",
|
||||
"ax.set_ylabel('Amplitude Δf')\n",
|
||||
"\n",
|
||||
"for i, t in enumerate([45, 60, 600, 1200, 1800, 3600]):\n",
|
||||
" ax.axvline(1/t, color='red', alpha=0.5)\n",
|
||||
" ax.annotate(f'{t} s', xy=(1/t, 3e-5), xytext=(-15, 0), xycoords='data', textcoords='offset pixels', rotation=90)\n",
|
||||
"#ax.text(1/60, 10,'60 s', ha='left')\n",
|
||||
"ax.grid()\n",
|
||||
"ax.set_xlim([1/60000, 0.5])\n",
|
||||
"ax.set_ylim([5e-7, 2e-2])"
|
||||
"ax.plot(np.linspace(0, (len(f_mean)-3)/10, len(f_mean)-3) , f_mean[1:-2])\n",
|
||||
"ax.grid()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "winlabenv",
|
||||
"display_name": "labenv",
|
||||
"language": "python",
|
||||
"name": "winlabenv"
|
||||
"name": "labenv"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
|
@ -736,7 +762,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.6"
|
||||
"version": "3.8.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -9,9 +9,9 @@ MAKEFLAGS += --no-builtin-rules
|
|||
all: safety_reset.pdf
|
||||
|
||||
%.pdf: %.tex %.bib
|
||||
pdflatex $<
|
||||
pdflatex -shell-escape $<
|
||||
biber $*
|
||||
pdflatex $<
|
||||
pdflatex -shell-escape $<
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
|
|
|||
12
ma/murks.tex
12
ma/murks.tex
|
|
@ -17,8 +17,10 @@
|
|||
|
||||
\makeatletter
|
||||
|
||||
\newcommand*{\@titelTitel}{Titel der Arbeit}
|
||||
\newcommand{\titel}[1]{\renewcommand*{\@titelTitel}{#1}} % Titel der Arbeit
|
||||
\newcommand*{\@titelTitelEN}{Titel der Arbeit (EN)}
|
||||
\newcommand*{\@titelTitelDE}{Titel der Arbeit (DE)}
|
||||
\newcommand{\titelen}[1]{\renewcommand*{\@titelTitelEN}{#1}} % Titel der Arbeit
|
||||
\newcommand{\titelde}[1]{\renewcommand*{\@titelTitelDE}{#1}} % Titel der Arbeit
|
||||
\newcommand*{\@titelArbeit}{Arbeitstyp}
|
||||
\newcommand{\typ}[1]{\renewcommand*{\@titelArbeit}{#1}} % Typ der Arbeit
|
||||
\newcommand*{\@titelGrad}{akademischer Grad}
|
||||
|
|
@ -75,8 +77,10 @@
|
|||
\begin{center}
|
||||
\begin{doublespace}
|
||||
\vspace{\baselineskip}
|
||||
{\LARGE \textbf{\@titelTitel}}\\
|
||||
%\vspace{1\baselineskip}
|
||||
{\LARGE \textbf{\@titelTitelEN}}\\
|
||||
\vspace{1cm}
|
||||
{\large \textbf{\@titelTitelDE}}\\
|
||||
\vspace{1cm}
|
||||
{\Large
|
||||
\@titelArbeit\\
|
||||
zur Erlangung des akademischen Grades\\
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
% Encoding: UTF-8
|
||||
@online{bnetza1,
|
||||
author = {Bundesnetzagentur},
|
||||
publisher = {Bundesnetzagentur},
|
||||
|
|
@ -563,16 +564,16 @@
|
|||
title = {Power System Oscillations},
|
||||
year = {2000}
|
||||
}
|
||||
|
||||
@proceedings{grebe01,
|
||||
author = {E. Grebe and J. Kabouris and S. L{\'o}pez Barba and W. Sattinger and W. Winter},
|
||||
doi = { 10.1109/PES.2010.5589932 {\textperiodcentered}},
|
||||
journaltitle = {IEEE PES General Meeting},
|
||||
month = aug,
|
||||
publisher = {IEEE},
|
||||
title = {Low Frequency Oscillations in the Interconnected System of Continental Europe},
|
||||
year = {2010}
|
||||
}
|
||||
|
||||
@Proceedings{grebe01,
|
||||
title = {Low Frequency Oscillations in the Interconnected System of Continental Europe},
|
||||
doi = {10.1109/PES.2010.5589932 {\textperiodcentered}},
|
||||
publisher = {IEEE},
|
||||
author = {E. Grebe and J. Kabouris and S. L{\'o}pez Barba and W. Sattinger and W. Winter},
|
||||
journaltitle = {IEEE PES General Meeting},
|
||||
month = aug,
|
||||
year = {2010},
|
||||
}
|
||||
|
||||
@article{mcdaniel01,
|
||||
author = {McDaniel Patrick and McLaughlin Stephen},
|
||||
|
|
@ -582,18 +583,17 @@
|
|||
title = {Security and Privacy Challenges in the Smart Grid},
|
||||
year = {2009}
|
||||
}
|
||||
|
||||
@article{schafer01,
|
||||
author = {Benjamin Sch{\"a}fer and Moritz Matthiae and Marc Timme and Dirk Witthaut},
|
||||
doi = { doi:10.1088/1367-2630/17/1/015002
|
||||
},
|
||||
journaltitle = {New Journal of Physics},
|
||||
month = jan,
|
||||
publisher = {IOP/DPG},
|
||||
title = {Decentral Smart Grid Control},
|
||||
volume = {17},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
@Article{schafer01,
|
||||
author = {Benjamin Sch{\"a}fer and Moritz Matthiae and Marc Timme and Dirk Witthaut},
|
||||
journaltitle = {New Journal of Physics},
|
||||
title = {Decentral Smart Grid Control},
|
||||
doi = {doi:10.1088/1367-2630/17/1/015002},
|
||||
volume = {17},
|
||||
month = jan,
|
||||
publisher = {IOP/DPG},
|
||||
year = {2015},
|
||||
}
|
||||
|
||||
@article{kosut01,
|
||||
author = {Oliver Kosut and Liyan Jia and Robert J. Thomas and Lang Tong},
|
||||
|
|
@ -675,4 +675,149 @@
|
|||
volume = {66},
|
||||
year = {2014}
|
||||
}
|
||||
|
||||
|
||||
@Article{dzung01,
|
||||
author = {Dacfey Dzung and Inigo Berganza and Alberto Sendin},
|
||||
date = {2011},
|
||||
journaltitle = {2011 IEEE International Symposium on Power Line Communications and Its Applications},
|
||||
title = {Evolution of powerline communications for smart distribution: From Ripple Control to OFDM},
|
||||
doi = {10.1109/ISPLC.2011.5764444},
|
||||
url = {https://www.researchgate.net/profile/Inigo_Berganza/publication/224236306_Evolution_of_powerline_communications_for_smart_distribution_From_ripple_control_to_OFDM/links/5c658800299bf1d14cc74cbd/Evolution-of-powerline-communications-for-smart-distribution-From-ripple-control-to-OFDM.pdf},
|
||||
}
|
||||
|
||||
@WWW{hovi01,
|
||||
author = {Jochen Fritz and Alexander Hovi},
|
||||
date = {2020},
|
||||
title = {Transkommando-System},
|
||||
url = {http://www.rundsteuerung.de/entwicklung/transkommando.html},
|
||||
journaltitle = {Rundsteuertechnik: Übertragung von Steuersignalen über das Energieversorgungsnetz},
|
||||
}
|
||||
|
||||
@Book{kundur01,
|
||||
author = {Kundur, Prabha and Balu, Neal J and Lauby, Mark G},
|
||||
date = {1994},
|
||||
title = {Power system stability and control},
|
||||
publisher = {McGraw-hill New York},
|
||||
volume = {7},
|
||||
year = {1994},
|
||||
}
|
||||
|
||||
@Book{goiser01,
|
||||
author = {Alois M. J. Goiser},
|
||||
date = {1998},
|
||||
title = {Handbuch der Spread-Spectrum Technik},
|
||||
isbn = {3-211-83080-4},
|
||||
publisher = {Springer},
|
||||
}
|
||||
|
||||
@TechReport{lamport02,
|
||||
author = {Lamport, Leslie},
|
||||
date = {19},
|
||||
institution = {Technical Report CSL-98, SRI International},
|
||||
title = {Constructing digital signatures from a one-way function},
|
||||
year = {1979},
|
||||
}
|
||||
|
||||
@InProceedings{buchmann01,
|
||||
author = {Buchmann, Johannes and Dahmen, Erik and Ereth, Sarah and H{\"u}lsing, Andreas and R{\"u}ckert, Markus},
|
||||
booktitle = {International Conference on Cryptology in Africa},
|
||||
title = {On the security of the Winternitz one-time signature scheme},
|
||||
organization = {Springer},
|
||||
pages = {363--378},
|
||||
year = {2011},
|
||||
}
|
||||
|
||||
@InProceedings{merkle01,
|
||||
author = {Merkle, Ralph C},
|
||||
booktitle = {Conference on the Theory and Application of Cryptology},
|
||||
title = {A certified digital signature},
|
||||
organization = {Springer},
|
||||
pages = {218--238},
|
||||
year = {1989},
|
||||
}
|
||||
|
||||
@InProceedings{dods01,
|
||||
author = {Dods, Chris and Smart, Nigel P and Stam, Martijn},
|
||||
booktitle = {IMA International Conference on Cryptography and Coding},
|
||||
title = {Hash based digital signature schemes},
|
||||
organization = {Springer},
|
||||
pages = {96--115},
|
||||
year = {2005},
|
||||
}
|
||||
|
||||
@TechReport{gasior01,
|
||||
author = {Gasior, M and Gonzalez, JL},
|
||||
institution = {CERN-AB-Note-2004-021},
|
||||
title = {Improving FFT frequency measurement resolution by parabolic and gaussian interpolation},
|
||||
year = {2004},
|
||||
}
|
||||
|
||||
@InProceedings{giudice01,
|
||||
author = {Del Giudice, Antonio and Graditi, Giorgio and Pietrosanto, Antonio and Paciello, Vincenzo},
|
||||
booktitle = {2015 IEEE International Workshop on Measurements \& Networking (M\&N)},
|
||||
title = {Power quality in smart distribution grids},
|
||||
organization = {IEEE},
|
||||
pages = {1--6},
|
||||
year = {2015},
|
||||
}
|
||||
|
||||
@Article{virtanen01,
|
||||
author = {{Virtanen}, Pauli and {Gommers}, Ralf and {Oliphant}, Travis E. and {Haberland}, Matt and {Reddy}, Tyler and {Cournapeau}, David and {Burovski}, Evgeni and {Peterson}, Pearu and {Weckesser}, Warren and {Bright}, Jonathan and {van der Walt}, St{\'e}fan J. and {Brett}, Matthew and {Wilson}, Joshua and {Jarrod Millman}, K. and {Mayorov}, Nikolay and {Nelson}, Andrew R.~J. and {Jones}, Eric and {Kern}, Robert and {Larson}, Eric and {Carey}, CJ and {Polat}, {\.I}lhan and {Feng}, Yu and {Moore}, Eric W. and {Vand erPlas}, Jake and {Laxalde}, Denis and {Perktold}, Josef and {Cimrman}, Robert and {Henriksen}, Ian and {Quintero}, E.~A. and {Harris}, Charles R and {Archibald}, Anne M. and {Ribeiro}, Ant{\^o}nio H. and {Pedregosa}, Fabian and {van Mulbregt}, Paul and {Contributors}, SciPy 1. 0},
|
||||
title = {{SciPy 1.0: Fundamental Algorithms for Scientific Computing in Python}},
|
||||
doi = {https://doi.org/10.1038/s41592-019-0686-2},
|
||||
pages = {261--272},
|
||||
volume = {17},
|
||||
adsurl = {https://rdcu.be/b08Wh},
|
||||
journal = {Nature Methods},
|
||||
year = {2020},
|
||||
}
|
||||
|
||||
@Article{derviskadic01,
|
||||
author = {Dervi{\v{s}}kadi{\'c}, Asja and Romano, Paolo and Paolone, Mario},
|
||||
title = {Iterative-interpolated DFT for synchrophasor estimation: A single algorithm for P-and M-class compliant PMUs},
|
||||
number = {3},
|
||||
pages = {547--558},
|
||||
volume = {67},
|
||||
journal = {IEEE Transactions on Instrumentation and Measurement},
|
||||
publisher = {IEEE},
|
||||
year = {2017},
|
||||
}
|
||||
|
||||
@Article{narduzzi01,
|
||||
author = {Narduzzi, Claudio and Bertocco, Matteo and Frigo, Guglielmo and Giorgi, Giada},
|
||||
title = {Fast-TFM—Multifrequency phasor measurement for distribution networks},
|
||||
number = {8},
|
||||
pages = {1825--1835},
|
||||
volume = {67},
|
||||
journal = {IEEE Transactions on Instrumentation and Measurement},
|
||||
publisher = {IEEE},
|
||||
year = {2018},
|
||||
}
|
||||
|
||||
@Article{cheshire01,
|
||||
author = {Stuart Cheshire and Mary Baker},
|
||||
title = {Consistent overhead Byte stuffing},
|
||||
doi = {10.1109/90.769765},
|
||||
number = {2},
|
||||
pages = {159--172},
|
||||
volume = {7},
|
||||
bibsource = {dblp computer science bibliography, https://dblp.org},
|
||||
biburl = {https://dblp.org/rec/journals/ton/CheshireB99.bib},
|
||||
journal = {{IEEE/ACM} Trans. Netw.},
|
||||
year = {1999},
|
||||
}
|
||||
|
||||
@InProceedings{kluyver01,
|
||||
author = {Thomas Kluyver and Benjamin Ragan{-}Kelley and Fernando P{\'{e}}rez and Brian E. Granger and Matthias Bussonnier and Jonathan Frederic and Kyle Kelley and Jessica B. Hamrick and Jason Grout and Sylvain Corlay and Paul Ivanov and Dami{\'{a}}n Avila and Safia Abdalla and Carol Willing and et al.},
|
||||
booktitle = {Positioning and Power in Academic Publishing: Players, Agents and Agendas, 20th International Conference on Electronic Publishing, G{\"{o}}ttingen, Germany, June 7-9, 2016},
|
||||
title = {Jupyter Notebooks - a publishing format for reproducible computational workflows},
|
||||
doi = {10.3233/978-1-61499-649-1-87},
|
||||
editor = {Fernando Loizides and Birgit Schmidt},
|
||||
pages = {87--90},
|
||||
publisher = {{IOS} Press},
|
||||
bibsource = {dblp computer science bibliography, https://dblp.org},
|
||||
biburl = {https://dblp.org/rec/conf/elpub/KluyverRPGBFKHG16.bib},
|
||||
year = {2016},
|
||||
}
|
||||
|
||||
@Comment{jabref-meta: databaseType:biblatex;}
|
||||
|
|
|
|||
Binary file not shown.
1
ma/safety_reset.pdf
Symbolic link
1
ma/safety_reset.pdf
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
/mnt/c/Users/jaseg/shared/safety_reset.pdf
|
||||
|
|
@ -20,8 +20,12 @@
|
|||
\usepackage{multirow}
|
||||
\usepackage{multicol}
|
||||
\usepackage{tikz}
|
||||
\usepackage{mathtools}
|
||||
\DeclarePairedDelimiter{\ceil}{\lceil}{\rceil}
|
||||
\DeclarePairedDelimiter{\paren}{(}{)}
|
||||
|
||||
\usetikzlibrary{arrows}
|
||||
\usetikzlibrary{chains}
|
||||
\usetikzlibrary{backgrounds}
|
||||
\usetikzlibrary{calc}
|
||||
\usetikzlibrary{decorations.markings}
|
||||
|
|
@ -42,7 +46,7 @@
|
|||
\usepackage[underline=false]{pgf-umlsd}
|
||||
\usetikzlibrary{calc}
|
||||
%\usepackage[pdftex]{graphicx,color}
|
||||
%\usepackage{epstopdf}
|
||||
\usepackage{epstopdf}
|
||||
% Needed for murks.tex
|
||||
\usepackage{setspace}
|
||||
\usepackage[draft=false,babel,tracking=true,kerning=true,spacing=true]{microtype} % optischer Randausgleich etc.
|
||||
|
|
@ -56,15 +60,15 @@
|
|||
|
||||
% Beispielhafte Nutzung der Vorlage für die Titelseite (bitte anpassen):
|
||||
\input{murks}
|
||||
\titel{FIXME} % Titel der Arbeit
|
||||
\typ{Masterarbeit} % Typ der Arbeit: Diplomarbeit, Masterarbeit, Bachelorarbeit
|
||||
\grad{Master of Science (M. Sc.)} % erreichter Akademischer Grad
|
||||
% z.B.: Master of Science (M. Sc.), Master of Education (M. Ed.), Bachelor of Science (B. Sc.), Bachelor of Arts (B. A.), Diplominformatikerin
|
||||
\titelen{A Post-Attack Recovery Architecture for Smart Electricity Meters}
|
||||
\titelde{Eine Architektur zur Kontrollwiederherstellung nach Angriffen auf Smart Metering in Stromnetzen}
|
||||
\typ{Masterarbeit}
|
||||
\grad{Master of Science (M. Sc.)}
|
||||
\autor{Jan Sebastian Götte}
|
||||
\gebdatum{Aus datenschutzrechtlichen Gründen nicht abgedruckt} % Geburtsdatum des Autors
|
||||
\gebort{Aus datenschutzrechtlichen Gründen nicht abgedruckt} % Geburtsort des Autors
|
||||
\gutachter{Prof. Dr. Björn Scheuermann}{FIXME} % Erst- und Zweitgutachter der Arbeit
|
||||
\mitverteidigung % entfernen, falls keine Verteidigung erfolgt
|
||||
\gebdatum{Aus Datenschutzgründen nicht abgedruckt} % Geburtsdatum des Autors
|
||||
\gebort{Aus Datenschutzgründen nicht abgedruckt} % Geburtsort des Autors
|
||||
\gutachter{Prof. Dr. Björn Scheuermann}{Prof. Dr.-Ing. Eckhard Grass}
|
||||
\mitverteidigung % entfernen, falls keine Verteidigung erfolgt %FIXME
|
||||
\makeTitel
|
||||
\selbstaendigkeitserklaerung{31.03.2020}
|
||||
\newpage
|
||||
|
|
@ -99,13 +103,13 @@ Smart meters usually are built around a standard microcontroller. \label{sm-cpu}
|
|||
|
||||
\section{Regulatory frameworks around the world}
|
||||
\subsection{International standards}
|
||||
\subsection{Regulations in Europe}
|
||||
\subsection{The regulatory situation in Germany}
|
||||
\subsection{The regulatory situation in France}
|
||||
\subsection{The regulatory situation in the UK}
|
||||
\subsection{The regulatory situation in Italy}
|
||||
\subsection{The regulatory situation in northern America}
|
||||
\subsection{The regulatory situation in Japan}
|
||||
\subsection{The regulatory situation in selected countries}
|
||||
\subsubsection{Germany}
|
||||
\subsubsection{France}
|
||||
\subsubsection{the UK}
|
||||
\subsubsection{Italy}
|
||||
\subsubsection{Northern America}
|
||||
\subsubsection{Japan}
|
||||
\subsection{Common themes}
|
||||
|
||||
\section{Security in smart grids}
|
||||
|
|
@ -213,7 +217,7 @@ Communication channel attacks are attacks on the communication links between sma
|
|||
attacks on IP-connected parts of the core network or attacks on shared busses between smart meters and IP gateways in
|
||||
substations. Generally, these attacks can be mitigated by securing the aforementioned communication links using modern
|
||||
cryptography. IP links can be protected using TLS, and more low-level busses can be protected using more lightweight
|
||||
Noise-based protocols. % FIXME cite
|
||||
Noise\cite{perrin01}-based protocols.
|
||||
Cryptographic security transforms an attackers ability to manipulate communication contents into a mere denial of
|
||||
service attack. Thus, in addition to cryptographic security safety under DoS conditions must be ensured to ensure
|
||||
continued system performance under attacks. This safety property is identical with the safety required to withstand
|
||||
|
|
@ -452,7 +456,7 @@ Microcontrollers have gained enormously in both performance/efficiency as well a
|
|||
gains have largely been driven by insatiable customer demand for faster, more powerful chips and for a long time
|
||||
security has not been considered important outside of some specific niches such as smartcards. Traditionally a
|
||||
microcontroller would spend its entire lifetime without ever being exposed to any networks. Though this trend has been
|
||||
reversing with the increasing adoption of internet-of-things things % FIXME is this pun ok?
|
||||
reversing with the increasing adoption of internet-of-things things
|
||||
and more advanced security features have started appearing in general-purpose microcontrollers, most still lack even
|
||||
basic functionality found in processors for computers or smartphones.
|
||||
|
||||
|
|
@ -470,41 +474,528 @@ simple to reduce attack surface there.
|
|||
\subsection{Safety vs. Security: Opting for restoration instead of prevention}
|
||||
|
||||
|
||||
\subsection{Technical outline of a safety reset}
|
||||
\subsection{Technical outline of a safety reset system}
|
||||
|
||||
\section{Communication channels on the grid}
|
||||
\subsection{Powerline communication systems and their use}
|
||||
|
||||
There is a number of well-established technologies for communication on or along power lines. We can distinguish three
|
||||
basic system categories: Systems using separate wires (such as DSL over landline telephone wiring), wireless radio
|
||||
systems (such as LTE) and \emph{powerline communication} (PLC) systems that re-use the existing mains wiring and
|
||||
superimpose data transmissions on the 50 Hz mains sine\cite{gungor01,kabalci01}.
|
||||
|
||||
For our scenario, we will ignore short-range communication systems. There exists a large number of \emph{wideband}
|
||||
powerline communication systems that are popular with consumers for bridging ethernet between parts of an apartment or
|
||||
house. These systems transmit at up to several hundred megabits over distances up to several tens of
|
||||
meters\cite{kabalci01}. Technologically, these wideband PLC systems are very different from \emph{narrowband} systems
|
||||
used by utilities for load management among other applications and they are not relevant to our analysis.
|
||||
|
||||
\subsection{Powerline communication (PLC) systems and their use}
|
||||
In long-distance communications for applications such as load management, PLC systems are attractive since they allow
|
||||
re-using the existing wiring infrastructure and have been used as early as in the 1930s\cite{hovi01}. Narrowband PLC
|
||||
systems are a potentially low-cost solution to the problem of transmitting data at small bandwidth over distances of
|
||||
several hundred meters up to tens of kilometers.
|
||||
|
||||
Narrowband PLC systems transmit on the order of kilobits per second or slower. A common use of this sort of system are
|
||||
\emph{ripple control} systems. These systems superimpose a low-frequency signal at some few hundred Hertz carrier
|
||||
frequency on top of the 50Hz mains sine. This low-frequency signal is used to encode switching commands for
|
||||
non-essential residential or industrial loads. Ripple control systems provide utilities with the ability to actively
|
||||
control demand while promising small savings in electricity cost to consumers\cite{dzung01}.
|
||||
|
||||
In any PLC system there is a strict tradeoff between bandwidth, power and distance. Higher bandwidth requires higher
|
||||
power and reduces maximum transmission distance. Where ripple control systems usually use few transmitters to cover
|
||||
the entire grid of a regional distribution utility, higher-bandwidth bidirectional systems used for automatic meter
|
||||
reading (AMR) in places such as italy or france require repeaters within a few hundred meters of a transmitter.
|
||||
|
||||
\subsection{Landline and wireless IP-based systems}
|
||||
Especially in automated meter reading (AMR) infrastructure the cost-benefit tradeoff of powerline systems does not
|
||||
always work out for utilities. A common alternative in these systems is to use the public internet for communication.
|
||||
Using the public internet has the advantage of low initial investment on the part of the utility company as well as
|
||||
quick commissioning. Disadvantages compared to a PLC system are potentially higher operational costs due to recurring
|
||||
fees to network providers as well as lower reliability. Being integrated into power grid infrastructure, a PLC system's
|
||||
failure modes are highly correlated with the overall grid. Put briefly, if the PLC interface is down, there is a good
|
||||
chance that power is out, too. In contrast to this general internet services exhibit a multitude of failures that are
|
||||
entirely decorrelated from power grid stability.
|
||||
|
||||
For purposes such as meter reading for billing purposes, this stability is sufficient. However for systems that need to
|
||||
hold up in crisis situations such as the recovery system we are contemplating in this thesis, the public internet may
|
||||
not provide sufficient reliability.
|
||||
|
||||
\subsection{Proprietary wireless systems}
|
||||
\subsection{Landline IP}
|
||||
\subsection{IP-based wireless systems}
|
||||
% FIXME
|
||||
|
||||
\subsection{Frequency modulation as a communication channel}
|
||||
|
||||
For our system, we chose grid frequency modulation (henceforth GFC) as a low-bandwidth uni-directional communications channel.
|
||||
Compared to traditional PLC GFC requires no additional hardware, works reliably throughout the grid and is harder to
|
||||
manipulate by a malicious actor.
|
||||
% FIXME \cite{urtasun01}
|
||||
For our system, we chose grid frequency modulation (henceforth GFM) as a low-bandwidth uni-directional broadcast
|
||||
communications channel. Compared to traditional PLC GFM requires only a small amount of additional hardware, works
|
||||
reliably throughout the grid and is harder to manipulate by a malicious actor.
|
||||
|
||||
Grid frequency in europe's synchronous areas is nominally 50 Hertz, but there are small load-dependent variations from
|
||||
this nominal value. Any device connected to the power grid (or even just within physical proximity of power wiring) can
|
||||
reliably and accurately measure grid frequency at low hardware overhead. By intentionally modifying grid frequency, we
|
||||
can create a very low-bandwidth broadcast communication channel. Grid frequency modulation has only ever been proposed
|
||||
as a communications channel at very small scales in microgrids before\cite{urtasun01} but to our knowledge has not yet
|
||||
been considered for large-scale application.
|
||||
|
||||
Advantages of using grid frequency for communication are low receiver hardware complexity as well as the fact that a
|
||||
single transmitter can cover an entire synchronous area. Though the transmitter has to be very large and powerful, setup
|
||||
of a single large transmitter faces lower bureaucratic hurdles than integration of hundreds of smaller ones into
|
||||
hundreds of local systems each with autonomous goverance.
|
||||
|
||||
\subsubsection{The frequency dependance of grid frequency}
|
||||
% FIXME find a solid citation on this
|
||||
|
||||
\subsubsection{Control systems coupled to grid frequency}
|
||||
|
||||
\subsubsection{Avoiding dangerous modes}
|
||||
|
||||
Modern power systems are complex electromechanical systems. Each component is controlled by several carefully tuned
|
||||
feedback loops to ensure voltage, load and frequency regulation. Multiple components are coupled through transmission
|
||||
lines that themselves exhibit complex dynamic behavior. The overall system is generally stable, but may exhbit some
|
||||
instabilities to particular small-signal stimuli. These instabilities, called \emph{modes} occur when due to mis-tuning
|
||||
of parameters or physical constraints the overall system exhibits oscillation at particular frequencies.
|
||||
\textcite{kundur01} splits these into four categories:
|
||||
|
||||
\begin{description}
|
||||
\item[Local modes] where a single power station oscillates in some parameter
|
||||
\item[Interarea modes] where subsections of the overall grid oscillate w.r.t.\ each other due to weak coupling
|
||||
between them
|
||||
\item[Control modes] caused by imperfectly tuned control systems
|
||||
\item[Torsional modes] that originate from electromechanical oscillations in the generator itself
|
||||
\end{description}
|
||||
|
||||
The oscillation frequencies associated with each of these modes are usually between a few tens of Millihertz and a few
|
||||
Hertz, see for example \textcite{grebe01} and \textcite{entsoe01}. It is hard to predict the particular modes of a
|
||||
power system at the scale of the central-european interconnected system. Theoretical analysis and simulation may give
|
||||
rough indications but cannot yield conclusive results. Due to the obvious danger as well as high economical impact due
|
||||
to inefficiencies experimental measurements are infeasible. Finally, modes are highly dependent on the power grid's
|
||||
structure and will change with changes in the power grid over time. For all of these reasons, a grid frequency
|
||||
modulation system must be designed very conservatively without relying on the absence (or presence) of modes at
|
||||
particular frequencies. A concrete design guideline that we can derive from this situation is that the frequency
|
||||
spectrum of any grid frequency modulation system should not exhibit any notable peaks and should avoid a concentration
|
||||
of spectral energy in certain frequency ranges. On the one hand this rules out some modulation schemes. On the other
|
||||
hand it provides us with a useful pointer towards those techniques that might work well: Spread-spectrum techniques. By
|
||||
employing spread-spectrum modulation we can produce an almost ideal frequency-domain behavior while at the same time
|
||||
achieving some modulation gain, increasing system sensitivity.
|
||||
|
||||
By using spread-spectrum techniques we can spread the energy of our modulation over a maximum in
|
||||
bandwidth\cite{goiser01}. The coding gain spread-spectrum techniques yield potentially allows for a weaker excitation,
|
||||
thereby allowing further reduction of the probability of disturbance to the overall system. Spread-spectrum techniques
|
||||
also inherently allow a tradeoff between receiver sensitivity and data rate which is a highly useful parameter to have
|
||||
for the overall system design.
|
||||
|
||||
\subsubsection{Overall system parameters}
|
||||
\subsubsection{An outline of practical implementation}
|
||||
|
||||
\section{From grid frequency to a reliable communications channel}
|
||||
\subsection{Channel properties}
|
||||
|
||||
|
||||
\subsection{Modulation and its parameters}
|
||||
|
||||
|
||||
\subsection{Error-correcting codes}
|
||||
|
||||
|
||||
\subsection{Cryptographic security}
|
||||
|
||||
Informally the system we are looking for can be modelled as consisting of three parties: The trusted
|
||||
\textsc{Transmitter}, one of a large number of untrusted \textsc{Receivers}, and an \textsc{Attacker} according to the
|
||||
following rules:
|
||||
|
||||
\begin{enumerate}
|
||||
\item \textsc{Transmitter} and \textsc{Attacker} can both transmit any bit sequence
|
||||
\item \textsc{Receiver} receives any transmission by either \textsc{Transmitter} or \textsc{Attacker} but cannot
|
||||
distinguish between the two on the signal level
|
||||
\item \textsc{Attacker} knows anything a \textsc{Receiver} might know
|
||||
\item \textsc{Transmitter} is stronger than \textsc{Attacker} and will ``win'' in simultaneous transmission
|
||||
\item Both \textsc{Transmitter} and \textsc{Receiver} can be seeded with some information on each other such as
|
||||
public key fingerprints.
|
||||
\end{enumerate}
|
||||
|
||||
We are not interested in congestion scenarios where an attacker attempts to disrupt an ongoing transmission by the
|
||||
transmitter. In practice there are several avenues to prevent such attempts including the following. Compromised loads
|
||||
that are being abused by the attacker can be manually disconnected by the utility. Error-correcting codes can be used to
|
||||
provide resiliency against small-scale disturbances. Finally, the transmitter can be designed to have high enough power
|
||||
to be able to override any likely attacker.
|
||||
|
||||
Our goal is to find a cryptographic primitive that has the following properties:
|
||||
\begin{enumerate}
|
||||
\item \textsc{Transmitter} can produce a transmission bit sequence $\mathbf{s}$ (or equivalently a set of such
|
||||
sequences) that \textsc{Receiver} can uniquely identify as being generated by \textsc{Transmitter}:
|
||||
$\mathcal{R}\left(\mathbf{s}\right) = 1$. Upon reception of this sequence, \textsc{Receiver} performs the safety
|
||||
reset.
|
||||
\item \textsc{Attacker} cannot forge $\mathbf{s}$, that is find $\mathbf{s}'$ such that
|
||||
$\mathbf{s} \neq \mathbf{s}' \land \mathcal{R}\left(\mathbf{s}'\right) = 1$
|
||||
\item Our system conforms to an at-most-once semantic. That is, upon transmission of a valid bit sequence coded for
|
||||
a particular \textsc{Receiver} or set of receivers each one either performs exactly one safety reset or none at
|
||||
all. We cannot achieve an exactly-once semantic since we are using an unidirectional lossy communication
|
||||
primitive. More coloquially, \textsc{Receiver} might be offline due to a localized power outage and might thus
|
||||
not hear \textsc{Transmitter} even if our broadcast primitive is reliable. The practical impact of this
|
||||
limitation can be mitigated by transmitter simply repeating itself until the desired effect has been achieved.
|
||||
\end{enumerate}
|
||||
|
||||
An important limitation from the rules of our setup above is that \textsc{Attacker} can always record the bit sequence
|
||||
\textsc{Transmitter} transmits and replay that same sequence later. Before considering any cryptographic approaches we
|
||||
can make the preliminary observation that we can trivially prevent \textsc{Attacker} from violating the
|
||||
at-most-once criterion by simply requiring \textsc{Receiver} to memorize all bit sequences that have been transmitted
|
||||
thus far and only reacting to new bit sequences. This means an attacker might be able to cause offline receivers to
|
||||
reset at a later point, but considering our goal is to reset them in the first place this would not pose a danger to the
|
||||
system.
|
||||
% FIXME elaborate why this is not a threat, and possible mitigations
|
||||
|
||||
As it seems we need a cryptographic primitive that looks somewhat like a signature. Different from a signature however,
|
||||
we have somewhat relaxed constraints here: While cryptographic signatures need to work over arbitrary inputs, all we
|
||||
want to ``sign'' here is the instruction to perform a safety reset. Since this is the only message we might ever want to
|
||||
transmit, our message space has only one entry and thus the informational content of our message is 0 bit! All the
|
||||
information we want to transmit is already encoded \emph{in the fact that we are transmitting}, and we do not require
|
||||
any further payload to be transmitted. This means we can omit the entirety of the message and just transmit whatever
|
||||
``signature'' we produce. This is useful since we have to conserve transmission bits so our transmissions do not take
|
||||
exceeedingly long time over our extremely slow communication channel.
|
||||
|
||||
We could use any of several traditional asymmetric cryptographic primitives to produce these signatures. The
|
||||
comparatively high computational effort required for signature verification would not be an issue. Transmissions take
|
||||
several minutes anyway and we can afford to spend some tens of seconds even in signature verification. Transmission
|
||||
length and by proxy system latency would be determined by the length of the signature. For RSA signature length is the
|
||||
modulus length (i.e. larger than 1000 bit for even basic contemporary security). For elliptic curve-based systems
|
||||
signature size is approximately twice the curve length (i.e. ~300 bit for contemporary security). However, we can do
|
||||
better than this: We can exploit the strange nature of our setting that our effective message entropy is 0 bit to derive
|
||||
a more efficient scheme.
|
||||
|
||||
\subsubsection{Lamport signatures}
|
||||
|
||||
In 1979, \textcite{lamport02} introduced a signature scheme that is based only on a one-way function such as a
|
||||
cryptographic hash function. The basic observation is that by choosing a random secret input to a one-way function and
|
||||
publishing the output, one can later prove knowledge of the input by simply publishing it. In the following paragraphs
|
||||
we will describe a construction of a one-time signature scheme based on this observation. The scheme we describe is the
|
||||
one usually called a ``Lamport Signature'' in modern literature and is slightly different from the variant described in
|
||||
the 1979 paper, but for our purposes we can consider both to be equivalent.
|
||||
|
||||
\paragraph{Setup.} In a Lamport signature, for an n-bit hash function $H$ the signer generates a private key $s =
|
||||
\left(s_{b, i} | b\in\left\{0, 1\right\}, 0\le i<n\right)$ of $2n$ random strings of length $n$. The signer publishes a
|
||||
public key $p = \left(p_{b, i} = H\left(s_{b, i}\right), b\in\left\{0, 1\right\}, 0\le i<n\right)$ that is simply the
|
||||
list of hashes of each of the random strings that make up the private key.
|
||||
|
||||
\paragraph{Signing.} To sign a message $m$, the signer publishes the signature $\sigma = \left(\sigma_i = k_{H(m)_i,
|
||||
i}\right)$ where $H(m)_i$ is the $i$-th bit of $H$ applied to $m$. That is, for the $i$-th bit of the message's hash
|
||||
$H(m)$ the signer publishes either of $p_{0, i}$ or $p_{1, i}$ depending on the hash bit's value, keeping the other
|
||||
entry of $P$ secret.
|
||||
|
||||
\paragraph{Verification.} The verifier can compute $H(m)$ themselves and check the corresponding entries $\sigma_i =
|
||||
k_{H(m)_i}$ of $S$ correctly evaluate to $p_{b, i} = H\left(s_{b, i}\right)$ from $P$ under $H$.
|
||||
|
||||
The above scheme is a one-time signature scheme only. After one signature has been published for a given key, the
|
||||
corresponding key must not be re-used for other signatures. This is intutively clear as we are effectively publishing
|
||||
part of the private key as the signature, and if we were to publish a signature for another message an attacker could
|
||||
derive additional signatures by ``mixing'' the two published signatures.
|
||||
|
||||
\subsubsection{Winternitz Signatures}
|
||||
An improvement to basic Lamport signatures as described above are Winternitz signatures as detailed in
|
||||
\textcite{merkle01} and \textcite{dods01}. Winternitz signatures reduce public key length as well as signature length
|
||||
for hash length $n$ from $2n$ to $\mathcal O \left(n/t\right)$ for some choice of parameter $t$ (usually a small number
|
||||
such as 4).
|
||||
|
||||
\paragraph{Setup.} The signer generates a private key $s = \left(s_i\right)$ consisting of $\ceil{\frac{n}{t}}$ random
|
||||
bit strings. The signer publishes a public key $p = \left(H^{2^t}\left(s_i\right)\right)$ where each element
|
||||
$H^{2^t}\left(s_i\right)$ is the $2^t$-fold recursive application of $H$ to $s_i$.
|
||||
|
||||
\paragraph{Signing.} The signer splits $m$ padded to a multiple of $t$ bits into $\ceil{\frac{n}{t}}$ chunks $m_i$ of
|
||||
$t$ bit each. The signer publishes the signature $\sigma = \left( \sigma_i = H^{m_i}\left(s_i\right) \right)$.
|
||||
|
||||
\paragraph{Verification.} The verifier can calculate for each $\sigma_i = H^{m_i}\left(s_i\right)$ that $H^{2^t -
|
||||
m_i}\left(\sigma_i\right) = H^{2^t - m_i}\left(H^{m_i}\left(s_i\right)\right) = H^{2^t - m_i + m_i} \left(s_i\right) =
|
||||
p_i$.
|
||||
|
||||
To prevent an attacker from forging additional signatures from one signature by calculating $\sigma_i' =
|
||||
H\left(\sigma_i\right)$ matching $m_i' = m_i + 1$, this scheme is usually paired with a simple checksum as described in
|
||||
\textcite{merkle01}.
|
||||
|
||||
\subsubsection{Using hash-based signatures for trigger authentication}
|
||||
The most basic possible trigger authentication scheme would be to simply generate a random bit string secret key $s$ and
|
||||
publish $p = H(s)$ for some hash function $H$. To activate the trigger, $\sigma = s$ would be published and listeners
|
||||
could verify that $H(\sigma) = p = H(s)$. This simplistic scheme has one main disadvantage: It is a fundamentally
|
||||
one-time construction. To prevent an attacker from re-triggering a listener a second time by replaying a valid trigger
|
||||
$\sigma$ all listeners have to blacklist any ``used'' $\sigma$. Alas, this means we can only ever trigger a listener
|
||||
\emph{once}. The good part is that any listener that missed this trigger can still be triggered later, but the bad part
|
||||
is that once $s$ is burned we are out of options. The trivial solution to this would be to simply inform each listener
|
||||
with a whole list of public keys in advance. This however takes $n$ times the amount of space for $n$-fold
|
||||
retriggerability. Luckily we can easily derive a scheme that yields $n$-fold retriggerability while using no more same
|
||||
space than the original scheme by taking some inspiration from Winternitz signatures above.
|
||||
|
||||
In this scheme the secret key $s$ is still a random bit string. The public key is $p = H^n(s)$ for n-times
|
||||
retriggerability. The $i$-th time the trigger is activated, $\sigma_i = H^n-i(s)$ is published, and every listener can
|
||||
verify that $\sigma_{i-1} = H\left(\sigma_i\right)$ with $\sigma_0 = p$. In case a listener missed one or more previous
|
||||
triggers it can simply continue computing $H\left(H\left(\sigma_i\right)\right)$ and
|
||||
$H\left(H\left(H\left(\sigma_i\right)\right)\right)$ until either reaching the $n$-th recursion level (indicating an
|
||||
invalid signature) or finding $H^n\left(\sigma_i\right) = \sigma_j$ with $sigma_j$ being the last signature this
|
||||
listener recorded, or $p$ in case there is none.
|
||||
|
||||
This scheme provides replay protection through listeners memorizing the last signature they activated to. Public key
|
||||
length is equal to the length of the hash function $H$ used. Even for our embedded systems use case $n$ can
|
||||
realistically be up to $\mathcal O\left(10^3\right)$, which is easily enough for our application.
|
||||
|
||||
% FIXME here and in previous ~2 pages get transmitter/receiver and sender/listener terminology straight. Also perhaps do
|
||||
% some sort of scenario definition introducing those terms somewhere.
|
||||
|
||||
\chapter{Practical implementation}
|
||||
\section{Cryptographic validation}
|
||||
|
||||
\section{Data collection for channel validation}
|
||||
|
||||
To design a solid system we needed to parametrize mains frequency variations under normal conditions. To set modulation
|
||||
amplitude as well as parameters of our modulation scheme we need a frequency spectrum of mains frequency variations
|
||||
(that is $\mathcal F\left(f(V(t))\right)$: Taking mains frequency $f(x)$ as a variable, the frequency spectrum of that
|
||||
variable, as opposed to the frequency spectrum of mains voltage $V(t)$ itself).
|
||||
|
||||
\subsection{Grid Frequency Estimation}
|
||||
\label{frequency_estimation}
|
||||
In commercial power systems Phasor Measurement Units (PMUs) are used to precisely measure parameters of a mains voltage
|
||||
waveform. One of the parameters PMUs measure is mains frequency. PMUs are used as part of SCADA systems controlling
|
||||
transmission networks to characterize the operational state of the network.
|
||||
|
||||
From a superficial viewpoint measuring mains frequency might seem like a simple problem. Take the mains voltage
|
||||
waveform, measure time between two rising-edge (or falling-edge) zero-crossings and take the inverse $f = t^{-1}$. In
|
||||
practice, phasor measurement units are significantly more complex than this. This discrepancy is due to the unhealthy
|
||||
% FIXME is this pun ok?
|
||||
combination of both high precision and quick response that is demanded from these units. High precision is necessary
|
||||
since variations of mains frequency under normal operating conditions are quite small--in the range of $5-10 \text{mHz}$
|
||||
over short intervals of time. Relative to the nominal $50 \text{Hz}$ this is a derivation of less than $100 \text{ppm}$.
|
||||
Relative to the corresponding $20 \text{ms}$ period that means a time derivation of about $2 \mu\text{s}$ from cycle to
|
||||
cycle. From this it is already obvious why a simplistic measurement cannot yield the required precision for manageable
|
||||
averaging times--we would need either a ADC sampling rate in the order of megabits or for a reconstruction through
|
||||
interpolated readings an impractically high ADC resolution.
|
||||
|
||||
Detail on the inner workings of commercial phasor measurement units is scarce but given their essential role to SCADA
|
||||
systems there is a large amount of academic research on such algorithms\cite{narduzzi01,derviskadic01}. A popular
|
||||
approach to these systems is to perform a Short-Time Fourier Transform (STFT) on ADC data sampled at high sampling rate
|
||||
(e.g. $10 \text{kHz}$) and then perform some analysis on the frequency-domain data to precisely locate the strong peak
|
||||
around $50 \text{Hz}$. A key observation here is that FFT bin size is going to be much larger than required frequency
|
||||
resolution. This fundamental limitiation follows from the nyquist criterion %FIXME maybe cite? and if we had to process
|
||||
an \emph{arbitrary} signal this would highly limit our practical measurement accuracy
|
||||
\footnote{
|
||||
Some software packages providing FFT or STFT primitives such as scipy\cite{virtanen01} allow the user to
|
||||
super-sample FFT output by specifying an FFT width larger than input data length, padding the input data with zeros
|
||||
on both sides. Note that in line with Nyquist this \emph{does not} actually provide finer output resolution but
|
||||
instead just amounts to an interpolation between output bins. Depending on the downstream analysis algorithm it may
|
||||
still be sensible to use this property of the DFT for interpolation, but in general it will be computationally
|
||||
expensive compared to other interpolation methods and in any case it will not yield any better frequency resolution
|
||||
aside from a hypothetical numerical advantage\cite{gasior01}.
|
||||
}.
|
||||
For this reason all approaches to mains frequency estimation are based on a model of the mains voltage waveform.
|
||||
Nominally, this waveform would be a perfect sine at $f = 50 \text{Hz}$. In practice it is a sine at $f \approx 50
|
||||
\text{Hz}$ superimposed with some aperiodic noise (e.g. irregular spikes from inductive loads being energized) as well
|
||||
as harmonic distortion that is caused by grid-topologically nearby devices with power factor
|
||||
\footnote{
|
||||
Power factor is a power engineering term that is used to describe how close the current waveform of a load is to
|
||||
that of a purely resistive load. Given sinusoidal input voltage $V(t) = V_\text{pk} \sin \paren{\omega_\text{nom}
|
||||
t}$ with $\omega_\text{nom} = 2 \pi f_\text{nom} = 2 \pi \cdot 50 \text{Hz}$ being the nominal angular frequency,
|
||||
the current waveform of a resistor with resistance $R \left[\Omega\right]$ according to Ohm's law would be $I(t) =
|
||||
\frac{V(t)}{R} = \frac{1}{R} V_\text{pk} \sin\paren{\omega_\text{nom} t}$. In this case voltage and current are
|
||||
perfectly in phase, i.e. the current at time $t$ is linear in voltage at constant factor $\frac{1}{R}$.
|
||||
|
||||
In contrast to this idealized scenario reality provides us with two common issues: One, the load may be reactive.
|
||||
This means its current waveform is an ideal sinusoid, but there is a phase difference between mains voltage and load
|
||||
current like so: $I(t) = \frac{V(t)}{R} = \frac{1}{\left|Z\right|} V_\text{pk} \sin\paren{\omega_\text{nom} t +
|
||||
\varphi}$ $Z$ would be the load's complex impedance combining inductive, capacitive and resistive components and
|
||||
$\varphi$ the phase difference between the resulting current waveform and the mains voltage waveform. A common case
|
||||
of such loads are motors and the inductive ballasts in old fluorescent lighting fixtures.
|
||||
|
||||
The second potential issue are loads with non-sinusoidal current waveform. There are many classes of these but the
|
||||
most common one are switching-mode power supplies. Most SMPS for modern electronic devices have an input stage
|
||||
consisting of a bridge rectifier followed by a capacitor that provide high-voltage DC power to the following
|
||||
switch-mode convert circuit. This rectifier-capacitor input stage under normal load draws a high current only at the
|
||||
very peak of the input voltage sinusoid and draws almost zero current for most of the period.
|
||||
|
||||
These two cases are measured by \emph{displacement power factor} and \emph{distortion power factor} that when
|
||||
combined yield the overall true power factor. The power factor is a key quantity in the design and operation of the
|
||||
power grid since a high power factor (close to $1.0$ or an in-phase sinusoidal current waveform) yields lowest
|
||||
transmission and generation losses.
|
||||
}
|
||||
$\cos \theta \neq 1.0$. Under a continous fourier transform over a long period the frequency spectrum of a signal
|
||||
distorted like this will be a low noise floor depending mainly on aperiodic noise on which a comb of harmonics as well
|
||||
as some sub-harmonics of $f \approx f_\text{nom} = 50Hz$ rides. The main peak at $f \approx f_\text{nom}$ will be very
|
||||
strong with the harmonics being approximately an order of magnitude weaker in energy and the noise floor being at least
|
||||
another order of magnitude weaker. See figure \ref{mains_voltage_spectrum} for a measured spectrum. This domain
|
||||
knowledge about the expected frequency spectrum of the signal can be employed in a number of interpolation techniques to
|
||||
re-construct the precise frequency of the spectrum's main component despite comparatively coarse STFT resolution and
|
||||
despite numerous distortions.
|
||||
|
||||
\begin{figure}
|
||||
\centering
|
||||
\includegraphics{../lab-windows/fig_out/mains_voltage_spectrum}
|
||||
\caption{Fourier transform of an 8 hour capture of mains voltage. Data was captured using our frequency measurement
|
||||
sensor described in section \ref{sec-fsensor} and FFT'ed after applying a blackman window. Vertical lines indicate
|
||||
$50 \text{Hz}$ and odd harmonics.}
|
||||
\label{mains_voltage_spectrum}
|
||||
\end{figure}
|
||||
|
||||
Published grid frequency estimation algorithms such as \textcite{narduzzi01} or \textcite{derviskadic01} are rather
|
||||
sophisticated and use a combination of techniques to reduce numerical errors in FFT calculation and peak fitting. Given
|
||||
that we do not need reference standard-grade accuracy for our application we chose to start with a very basic algorithm
|
||||
instead. We chose to use a general approach developed by experimental physicists at CERN that is described by
|
||||
\textcite{gasior01}. This approach assumes a general sinusoidal signal superimposed with harmonics and broadband noise.
|
||||
Applicable to a wide spectrum of practical signal analysis tasks it is a reasonable first-degree approximation of the
|
||||
much more sophisticated estimation algorithms developed specifically for power systems. Some algorithms have components
|
||||
such as kalman filters\cite{narduzzi01} that require a phyiscal model. As a general algorithm from \textcite{gasior01}
|
||||
does not require this kind of application-specific tuning, eliminating one source of error.
|
||||
|
||||
\subsection{Frequency sensor hardware design}
|
||||
\label{sec-fsensor}
|
||||
Our safety reset controller % FIXME is this the right term?
|
||||
will have to measure mains frequency to later demodulate a reset signal transmitted through it. Since we have decided to
|
||||
do our own frequency measurement system here we can use this frequency measurement setup as a prototype for the
|
||||
frequency measurement subcomponent of the demodulation system we will later develop. Since we do not plan to do a
|
||||
large-scale field deployment of our measurement setup we can keep the hardware implementation simple by moving most of
|
||||
the signal processing to a regular computer and concentrating our hardware efforts on raw signal capture.
|
||||
|
||||
\begin{figure}
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[start chain = going below, node distance = 12mm and 50mm, every join/.style = {norm}]
|
||||
\tikzset{
|
||||
base/.style = {draw, on chain, on grid, align=center, minimum height = 4ex, font=\footnotesize},
|
||||
text/.style = {base},
|
||||
component/.style = {base, rectangle, text width=40mm},
|
||||
coord/.style = {coordinate, on chain, on grid, node distance=6mm and 25mm}
|
||||
}
|
||||
\node[text centered] (input) {Single-Phase Mains Input};
|
||||
\node[component] (safety) [below = of input] {Input Protection};
|
||||
\node[coord] (safety-anchor) [below = of safety] {};
|
||||
\node[component] (analog) [below = of safety-anchor] {Analog Signal Processing};
|
||||
\node[component] (powersupply) [left = of analog] {Power supply};
|
||||
\node[component] (adc) [below = of analog] {ADC};
|
||||
\node[component] (micro) [below = of adc] {Microcontroller};
|
||||
\node[component] (isol) [below = of micro] {Galvanic Digital Isolation};
|
||||
\node[coord] (isol-left) [left = 6cm of isol.west] {};
|
||||
\node[coord] (isol-right) [right = 1cm of isol.east] {};
|
||||
\node[component] (usb) [below = of isol] {USB interface};
|
||||
|
||||
\draw[->] (input.south) -- (safety.north);
|
||||
\draw[-] (safety.south) -- (safety-anchor);
|
||||
\draw[->] (safety-anchor) -| (powersupply.north);
|
||||
\draw[->] (safety-anchor) -| (analog.north);
|
||||
\draw[->] (powersupply.south) |- (adc.west);
|
||||
\draw[->] (powersupply.south) |- (micro.west);
|
||||
\draw[->] (analog.south) -- (adc.north);
|
||||
\draw[->] (adc.south) -- (micro.north);
|
||||
\draw[->] (micro.south) -- (isol.north);
|
||||
\draw[->] (isol.south) -- (usb.north);
|
||||
|
||||
\draw[dashed] (isol.west) -- (isol-left.east);
|
||||
\draw[dashed] (isol.east) -- (isol-right.west);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Frequency sensor hardware diagram}
|
||||
\label{fmeas-sens-diag}
|
||||
\end{figure}
|
||||
|
||||
An overall block diagram of our system is shown in fig. \ref{fmeas-sens-diag}. The mircrocontroller we chose is an
|
||||
\texttt{STM32F030F4P6} ARM Cortex-M0 microcontroller made by ST Microelectronics. The ADC in fig. \ref{fmeas-sens-diag}
|
||||
in our design is the integrated 12-bit ADC of this microcontroller, which is sufficient for our purposes. The USB
|
||||
interface is a simple USB to serial converter IC (\texttt{CH340G}) and the galvanic digital isolation is accomplished
|
||||
with a pair of high-speed optocouplers on its \texttt{RX} and \texttt{TX} lines. The analog signal processing is a
|
||||
simple voltage divider using high-power resistors to get the required creepage along with some high-frequency filter
|
||||
capacitors and an op-amp buffer. The power supply is an off-the-shelf mains-input power module. The system is
|
||||
implemented on a single two-layer PCB that is housed in an off-the-shelf industrial plastic case fitted with a printed
|
||||
label and a few status lights on its front.
|
||||
|
||||
\subsection{Clock accuracy considerations}
|
||||
|
||||
Our measurement hardware will sample line voltage at some sampling rate $f_S$, e.g.\ $1 \text{kHz}$. All downstream
|
||||
processsing is limited in accuracy by the accuracy of $f_S$\footnote{
|
||||
We are not considering the effects of clock jitter. We are highly oversampling the signal and the FFT done in our
|
||||
downstream processing will eliminate small jitter effects leaving only frequency stability to worry about. }. We
|
||||
generate our sampling clock in hardware by clocking the ADC from one of the microcontroller's timer blocks clocked from
|
||||
the microcontroller's system clock. This means our ADC's sampling window will be synchronized cycle-accurate to the
|
||||
microcontroller's system clock.
|
||||
|
||||
Our downstream measurement of mains frequency by nature is relative to our sampling frequency $f_S$. In the setup
|
||||
described above this means we have to make sure our system clock is fairly stable. A frequency derivation of $1
|
||||
\text{ppm}$ in our system clock causes a proportional grid frequency measurement error of $\Delta f = f_\text{nom} \cdot
|
||||
10^{-6} = 50 \mu\text{Hz}$. In a worst-case where our system is clocked from a particularly bad crystal that exhibits
|
||||
$100 \text{ppm}$ of instabilities over our measurement period we end up with an error of $5 \text{mHz}$. This is well
|
||||
within our target measurement range, so we need a more stable clock source. Ideally we want to avoid writing our own
|
||||
clock conditioning code where we try to change an oscillators operating frequency to match some reference. Clock
|
||||
conditioning algorithms are highly complex and in our case post-processing of measurement data and simply adding and
|
||||
offset is simpler and less error-prone.
|
||||
|
||||
Our solution to these problems is to use a crystal oven\footnote{
|
||||
A crystal oven is a crystal oscillator thermally coupled closely to a heater and temperature sensor and enclosed in
|
||||
a thermally isolated case. The heater is controlled to hold the crystal oscillator at a near-constant temperature
|
||||
some few ten degrees above ambient. Any ambient temperature variations will be absorbed by the temperature control.
|
||||
This yields a crystal frequency that is almost completely unaffected by ambient temperature variations below the
|
||||
oven temperature and whose main remaining instability is aging.
|
||||
}as our main system clock source. Crystal ovens are expensive compared to ordinary crystal oscillators. Since any
|
||||
crystal oven will be much more accurate than a standard room-temperature crystal we chose to reduce cost by using one
|
||||
recycled from old telecommunications equipment.
|
||||
|
||||
To verify clock accuracy we routed an externally accessible SMA connector to a microcontroller pin that is routed to one
|
||||
of the microcontroller's timer inputs. By connecting a GPS 1pps signal to this pin and measuring its period we can
|
||||
calculate our system's Allan variance\footnote{
|
||||
Allan variance is a measure of frequency stability between two clocks.
|
||||
}, thereby measuring both clock stability and clock accuracy.
|
||||
We ran a 4 hour test of our frequency sensor that generated the histogram shown in figure \ref{ocxo_freq_stability}.
|
||||
These results show that while we get a systematic error of about $10 \text{ppm}$ due to manufacturing tolerances the
|
||||
random error at less than $10 \text{ppb}$ is smaller than that of a room-temperature crystal oscillator by 3-4 orders of
|
||||
magnitude. Since we are interested in grid frequency variations over time but not in the absolute value of grid
|
||||
frequency the systematic error is of no consequence to us. The random error at $3.66 \text{ppb}$ corresponds to a
|
||||
frequency measurement error of about $0.2 \mu\text{Hz}$, well below what we can achieve at reasonable sampling rates and
|
||||
ADC resolution.
|
||||
|
||||
\begin{figure}
|
||||
\centering
|
||||
\includegraphics{../lab-windows/fig_out/ocxo_freq_stability}
|
||||
\caption{OCXO Frequency derivation from nominal $19.440 \text{MHz}$ measured against GPS 1pps}
|
||||
\label{ocxo_freq_stability}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Firmware implementation}
|
||||
|
||||
The firmware uses one of the microcontroller's timers clocked from an external crystal oscillator to produce an $1
|
||||
\text{ms}$ tick that the internal ADC is triggered from for a sample rate of $1 \text{ksps}$. Higher sample rates would
|
||||
be possible but reliable data transmission over the opto-isolated serial interface might prove challenging and $1
|
||||
\text{ksps}$ corresponds to $20$ samples per cycle at $f_\text{nominal}$. This is $10\times$ nyquist and should be
|
||||
plenty for accurate measurements.
|
||||
|
||||
The ADC measurements are read using DMA and written into a circular buffer. Using some DMA controller features this
|
||||
circular buffer is split in back and front halves with one being written to and the other being read at the same time.
|
||||
Buffer contents are moved from the ADC DMA buffer into a packet-based reliable UART interface as they come in. The UART
|
||||
packet interface keeps two ringbuffers: One byte-based ringbuffer for transmission data and one ringbuffer pointer
|
||||
structure that keeps track of ADC data packet boundaries in the byte-based ringbuffer. Every time a chunk of data is
|
||||
available from the ADC the data is framed into the byte-based ringbuffer and the packet boundaries are logged in the
|
||||
packet pointer ringbuffer. If the UART transmitter is idle at this time a DMA-backed transmission of the oldest packet
|
||||
in the packet ringbuffer is triggered at this point. Data is framed using Consistent Overhead Byte Stuffing
|
||||
(COBS)\footnote{
|
||||
COBS is a framing technique that allows encoding $n$ bytes of arbitray data into exactly $n+1$ bytes with no embedded
|
||||
$0$-bytes that can then be delimited using $0$-bytes. COBS is simple to implement and allows both one-pass decoding and
|
||||
encoding. The encoder either needs to be able to read up to $256 \text{bytes}$ ahead or needs a buffer of $256
|
||||
\text{bytes}$. COBS is very robust in that it allows self-synchronization. At any point a receiver can reliably
|
||||
synchronize itself against a COBS data stream by waiting for the next $0$-byte. The constant overhead allows precise
|
||||
bandwidth and buffer planning and provides constant, good efficiency close to the theoretical maximum.
|
||||
}\cite{cheshire01} along with a CRC-32 checksum for error checking. When the host receives a new packet with a
|
||||
valid checksum it returns an acknowledgement packet to the sensor. When the sensor receives the acknowledgement, the
|
||||
acknowledged packet is dropped from the transmission packet ringbuffer. When the host detects an incorrect checksum it
|
||||
simply stays quiet and waits for the sensor to resume with retransmission when the next ADC buffer has been received.
|
||||
|
||||
% FIXME make actual error rate measurements
|
||||
|
||||
The serial interface logic presents most of the complexity of the sensor firmware. This complexity is necessary since
|
||||
we need reliable, error-checked transmission to the host. Though rare, bit errors on a serial interface do happen and
|
||||
data corruption is unacceptable. The packet-layer queueing on the sensor is necessary since the host is not a realtime
|
||||
system and unpredictable latency spikes of several hundred milliseconds are possible.
|
||||
|
||||
The host in our recording setup is a Raspberry Pi 3 model B running a Python script. The Python script handles serial
|
||||
communication and logs data and errors into an SQLite database file. SQLite has been chosen for its simple yet flexible
|
||||
interface and its good tolerance of system resets due to unexpected power loss.
|
||||
|
||||
\subsection{Frequency sensor measurement results}
|
||||
|
||||
Captured raw waveform data is processed in the Jupyter Lab environment\cite{kluyver01} and grid frequency estimates are
|
||||
extracted as described in sec. \ref{frequency_estimation} using the \textcite{gasior01} technique.
|
||||
|
||||
% FIXME comparison against reference measurements?
|
||||
|
||||
\section{Channel simulation and parameter validation}
|
||||
|
||||
|
||||
\section{Implementation of a demonstrator unit}
|
||||
|
||||
\section{Experimental results}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue