{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Protokol k projektu ISS 2024/25\n",
        "### Rozpoznávanie zvukov automobilových motorov\n",
        "#### **Autor**: Roman Nečas\n",
        "#### **Login**: xnecasr00\n",
        "#### **Dátum**: 7.12.2024\n",
        "### 1. Úvod a cieľ projektu\n",
        "Projekt sa zaoberá automatickým rozpoznávaním a klasifikáciou zvukov automobilových motorov. Hlavným cieľom je vyvinúť metodiku, ktorá dokáže priradiť neznáme zvukové nahrávky k referenčným nahrávkam rovnakého automobilu, aj keď motor beží na iných otáčkach.\n",
        "\n",
        "### 2. Teoretický rozbor\n",
        "#### 2.1 Charakteristika zvuku motora\n",
        "Zvuk motora je komplexný signál obsahujúci niekoľko kľúčových charakteristík:\n",
        "\n",
        "\n",
        "*   Základná frekvencia závislá od otáčok motora\n",
        "*   Harmonické frekvencie (násobky základnej frekvencie)\n",
        "*   Špecifické spektrálne charakteristiky dané konštrukciou motora\n",
        "\n",
        "#### 2.2 Metódy analýzy\n",
        "Pre analýzu som zvolil nasledujúce metódy:\n",
        "\n",
        "\n",
        "1.   Časová analýza\n",
        "  *   Zobrazenie priebehu signálu\n",
        "  *   Výpočet základných štatistických parametrov\n",
        "  \n",
        "2.   Frekvenčná analýza\n",
        "  *   Fourierova transformácia (FFT)\n",
        "  *   Analýza spektra\n",
        "  *   Identifikácia základnej frekvencie\n",
        "\n",
        "3.   Normalizovaná spektrálna analýza\n",
        "  *   Normalizácia spektra vzhľadom na základnú frekvenciu\n",
        "  *   Normalizácia amplitúd\n",
        "  *   Analýza harmonických komponentov\n",
        "\n",
        "Dôvod zvolenia týchto metód bol, aby som sa dostal k charakteristike zvukových nahrávok motorov, v ktorej by boli otáčky motora, teda základná frekvencia \"odfiltrované\" a mohol som porovnavať zvukové nahrávky nezávisle na otáčkach motora.\n",
        "\n",
        "\n",
        "### 3. Implementácia"
      ],
      "metadata": {
        "id": "43AksbDpPsRF"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "#### Knižnice a stiahnutie signálu"
      ],
      "metadata": {
        "id": "PmPbaKgw3SSB"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rt9zooK4PoOg"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "import re\n",
        "import glob\n",
        "import soundfile as sf\n",
        "from IPython.display import Audio\n",
        "from IPython.display import display\n",
        "from scipy import signal\n",
        "from scipy.io import wavfile\n",
        "from scipy.fft import fft, ifft, fftfreq\n",
        "import scipy.io\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "login = \"xnecasr00\"\n",
        "zip_file = login + \".zip\"\n",
        "assignment_file = \"https://www.fit.vut.cz/study/course/ISS/public/proj2024-25/personal/\" + zip_file\n",
        "!wget $assignment_file\n",
        "!unzip -o $zip_file\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "6dVSj2mxtuOw",
        "outputId": "d87199a8-5684-494c-8cab-d3e2c2900e43"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "--2024-12-07 15:43:24--  https://www.fit.vut.cz/study/course/ISS/public/proj2024-25/personal/xnecasr00.zip\n",
            "Resolving www.fit.vut.cz (www.fit.vut.cz)... 147.229.9.65, 2001:67c:1220:809::93e5:941\n",
            "Connecting to www.fit.vut.cz (www.fit.vut.cz)|147.229.9.65|:443... connected.\n",
            "HTTP request sent, awaiting response... 200 OK\n",
            "Length: 227217 (222K) [application/zip]\n",
            "Saving to: ‘xnecasr00.zip’\n",
            "\n",
            "xnecasr00.zip       100%[===================>] 221.89K   340KB/s    in 0.7s    \n",
            "\n",
            "2024-12-07 15:43:25 (340 KB/s) - ‘xnecasr00.zip’ saved [227217/227217]\n",
            "\n",
            "Archive:  xnecasr00.zip\n",
            "   creating: xnecasr00/\n",
            "  inflating: xnecasr00/BMW_318i_Drive.wav  \n",
            "  inflating: xnecasr00/test_f.wav    \n",
            "  inflating: xnecasr00/Fiat_Panda_Drive.wav  \n",
            "  inflating: xnecasr00/test_t.wav    \n",
            "  inflating: xnecasr00/Audi_A3_Drive.wav  \n",
            "  inflating: xnecasr00/test_s.wav    \n",
            "  inflating: xnecasr00/Peugeot_307_Drive.wav  \n",
            "  inflating: xnecasr00/test_i.wav    \n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# load the data\n",
        "# references will be in ref_signals, reference labels in ref_labels, reference count in N_ref.\n",
        "# tests  will be in test_signals, test labels in test_labels, test count in N_test.\n",
        "def get_signals(labs):\n",
        "    signals = []\n",
        "    N = len(labs)\n",
        "    for car in labs:\n",
        "      filename = login + \"/\" + car + \".wav\"\n",
        "      s, Fs = sf.read(filename)\n",
        "      signals.append(s)\n",
        "    return signals, N, Fs\n",
        "\n",
        "def play_signals(signals, Fs):\n",
        "  for signal in signals:\n",
        "    display(Audio(signal, rate=Fs))\n",
        "\n",
        "files = glob.glob(login + \"/*.wav\")\n",
        "names = [re.sub(login + \"/\", \"\", s) for s in files]\n",
        "labels = [re.sub(\".wav\", \"\",s) for s in names]\n",
        "print (\"----- test signals ---------\")\n",
        "r = re.compile(\"^test_\");  test_labels = list(filter(r.match, labels))\n",
        "print (test_labels); test_signals, N_test, Fs = get_signals(test_labels); play_signals (test_signals, Fs)\n",
        "print (\"----- reference signals ---------\")\n",
        "r = re.compile(\"(?!^test_)\");  ref_labels = list(filter(r.match, labels))\n",
        "print (ref_labels); ref_signals, N_ref, Fs = get_signals(ref_labels); play_signals (ref_signals, Fs)\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 457
        },
        "id": "BYYQvmDzyT5W",
        "outputId": "1fdd67da-8222-41d8-f02d-10365cd6fdae"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "----- test signals ---------\n",
            "['test_t', 'test_s', 'test_f', 'test_i']\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<IPython.lib.display.Audio object>"
            ],
            "text/html": [
              "\n",
              "                <audio  controls=\"controls\" >\n",
              "                    <source src=\"data:audio/wav;base64,[encoded data, 42728 characters]\" type=\"audio/wav\" />\n",
              "                    Your browser does not support the audio element.\n",
              "                </audio>\n",
              "              "
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<IPython.lib.display.Audio object>"
            ],
            "text/html": [
              "\n",
              "                <audio  controls=\"controls\" >\n",
              "                    <source src=\"data:audio/wav;base64,[encoded data, 42728 characters]\" type=\"audio/wav\" />\n",
              "                    Your browser does not support the audio element.\n",
              "                </audio>\n",
              "              "
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<IPython.lib.display.Audio object>"
            ],
            "text/html": [
              "\n",
              "                <audio  controls=\"controls\" >\n",
              "                    <source src=\"data:audio/wav;base64,[encoded data, 42728 characters]\" type=\"audio/wav\" />\n",
              "                    Your browser does not support the audio element.\n",
              "                </audio>\n",
              "              "
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<IPython.lib.display.Audio object>"
            ],
            "text/html": [
              "\n",
              "                <audio  controls=\"controls\" >\n",
              "                    <source src=\"data:audio/wav;base64,[encoded data, 42728 characters]\" type=\"audio/wav\" />\n",
              "                    Your browser does not support the audio element.\n",
              "                </audio>\n",
              "              "
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "----- reference signals ---------\n",
            "['Fiat_Panda_Drive', 'BMW_318i_Drive', 'Peugeot_307_Drive', 'Audi_A3_Drive']\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<IPython.lib.display.Audio object>"
            ],
            "text/html": [
              "\n",
              "                <audio  controls=\"controls\" >\n",
              "                    <source src=\"data:audio/wav;base64,[encoded data, 42728 characters]\" type=\"audio/wav\" />\n",
              "                    Your browser does not support the audio element.\n",
              "                </audio>\n",
              "              "
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<IPython.lib.display.Audio object>"
            ],
            "text/html": [
              "\n",
              "                <audio  controls=\"controls\" >\n",
              "                    <source src=\"data:audio/wav;base64,[encoded data, 42728 characters]\" type=\"audio/wav\" />\n",
              "                    Your browser does not support the audio element.\n",
              "                </audio>\n",
              "              "
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<IPython.lib.display.Audio object>"
            ],
            "text/html": [
              "\n",
              "                <audio  controls=\"controls\" >\n",
              "                    <source src=\"data:audio/wav;base64,[encoded data, 42728 characters]\" type=\"audio/wav\" />\n",
              "                    Your browser does not support the audio element.\n",
              "                </audio>\n",
              "              "
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<IPython.lib.display.Audio object>"
            ],
            "text/html": [
              "\n",
              "                <audio  controls=\"controls\" >\n",
              "                    <source src=\"data:audio/wav;base64,[encoded data, 42728 characters]\" type=\"audio/wav\" />\n",
              "                    Your browser does not support the audio element.\n",
              "                </audio>\n",
              "              "
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Analýza signálu v čase\n",
        "Najskôr si vykreslím časový priebeh signálov, spolu s ich základnými štatistickými parametrami, ktoré mi ale aj tak veľmi nepomohli."
      ],
      "metadata": {
        "id": "b3jOXClCDBSu"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def analyze_signals(signals, Fs, labels, title):\n",
        "    \"\"\"\n",
        "    Analyzuje a vizualizuje základné vlastnosti zvukových signálov\n",
        "\n",
        "    Parametre:\n",
        "    signals: list zvukových signálov\n",
        "    Fs: vzorkovacia frekvencia\n",
        "    labels: názvy signálov\n",
        "    title: názov pre graf\n",
        "    \"\"\"\n",
        "    n_signals = len(signals)\n",
        "    time = np.arange(len(signals[0])) / Fs\n",
        "\n",
        "    plt.figure(figsize=(15, 10))\n",
        "\n",
        "    # Časové priebehy\n",
        "    for i, (sig, label) in enumerate(zip(signals, labels)):\n",
        "        plt.subplot(n_signals, 1, i+1)\n",
        "        plt.plot(time, sig)\n",
        "        plt.title(f'{label} - Časový priebeh')\n",
        "        plt.xlabel('Čas [s]')\n",
        "        plt.ylabel('Amplitúda')\n",
        "\n",
        "        # Výpis základných štatistík\n",
        "        print(f\"\\nŠtatistiky pre {label}:\")\n",
        "        print(f\"Maximum: {np.max(sig):.3f}\")\n",
        "        print(f\"Minimum: {np.min(sig):.3f}\")\n",
        "        print(f\"Stredná hodnota: {np.mean(sig):.3f}\")\n",
        "        print(f\"Smerodajná odchýlka: {np.std(sig):.3f}\")\n",
        "\n",
        "    plt.suptitle(title)\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "# Analýza referenčných signálov\n",
        "analyze_signals(ref_signals, Fs, ref_labels, \"Referenčné signály\")\n",
        "\n",
        "# Analýza testovacích signálov\n",
        "analyze_signals(test_signals, Fs, test_labels, \"Testovacie signály\")"
      ],
      "metadata": {
        "id": "S1tLZITSE0Sh",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 2838
        },
        "outputId": "5129b1b1-1c5c-48d3-ed29-c1fc46a36f7f"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "Štatistiky pre Fiat_Panda_Drive:\n",
            "Maximum: 0.669\n",
            "Minimum: -0.635\n",
            "Stredná hodnota: -0.001\n",
            "Smerodajná odchýlka: 0.291\n",
            "\n",
            "Štatistiky pre BMW_318i_Drive:\n",
            "Maximum: 0.020\n",
            "Minimum: -0.020\n",
            "Stredná hodnota: 0.000\n",
            "Smerodajná odchýlka: 0.006\n",
            "\n",
            "Štatistiky pre Peugeot_307_Drive:\n",
            "Maximum: 0.277\n",
            "Minimum: -0.301\n",
            "Stredná hodnota: -0.000\n",
            "Smerodajná odchýlka: 0.079\n",
            "\n",
            "Štatistiky pre Audi_A3_Drive:\n",
            "Maximum: 0.480\n",
            "Minimum: -0.482\n",
            "Stredná hodnota: 0.000\n",
            "Smerodajná odchýlka: 0.147\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1000 with 4 Axes>"
            ],
            "image/png": "[encoded data, 391488 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "Štatistiky pre test_t:\n",
            "Maximum: 0.100\n",
            "Minimum: -0.096\n",
            "Stredná hodnota: -0.000\n",
            "Smerodajná odchýlka: 0.027\n",
            "\n",
            "Štatistiky pre test_s:\n",
            "Maximum: 0.125\n",
            "Minimum: -0.135\n",
            "Stredná hodnota: 0.000\n",
            "Smerodajná odchýlka: 0.034\n",
            "\n",
            "Štatistiky pre test_f:\n",
            "Maximum: 0.322\n",
            "Minimum: -0.365\n",
            "Stredná hodnota: -0.000\n",
            "Smerodajná odchýlka: 0.111\n",
            "\n",
            "Štatistiky pre test_i:\n",
            "Maximum: 0.061\n",
            "Minimum: -0.059\n",
            "Stredná hodnota: 0.000\n",
            "Smerodajná odchýlka: 0.020\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1000 with 4 Axes>"
            ],
            "image/png": "[encoded data, 329172 characters]\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Spektrálna analýza\n",
        "Z časového priebehu sa dá pozorovať periodicita signálov. Je pomerne ľahko viditeľná základná frekvencia, teda rýchlost otáčok motora.Pomocou spektrálnej analýzi si teda zobrazíme frekvenčné spektrum."
      ],
      "metadata": {
        "id": "_v9CuGVsD4T9"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Spektrálna analýza\n",
        "def spectral_analysis(signals, Fs, labels, title):\n",
        "    \"\"\"\n",
        "    Vykoná spektrálnu analýzu signálov\n",
        "\n",
        "    Parametre:\n",
        "    signals: list zvukových signálov\n",
        "    Fs: vzorkovacia frekvencia\n",
        "    labels: názvy signálov\n",
        "    title: názov pre graf\n",
        "    \"\"\"\n",
        "    n_signals = len(signals)\n",
        "    plt.figure(figsize=(15, 10))\n",
        "\n",
        "    for i, (sig, label) in enumerate(zip(signals, labels)):\n",
        "        # Výpočet FFT\n",
        "        freq = np.fft.fftfreq(len(sig), 1/Fs)\n",
        "        fft_sig = np.fft.fft(sig)\n",
        "\n",
        "        # Zobrazenie len pozitívnych frekvencií\n",
        "        positive_freq_idx = freq >= 0\n",
        "\n",
        "        plt.subplot(n_signals, 1, i+1)\n",
        "        plt.plot(freq[positive_freq_idx], np.abs(fft_sig)[positive_freq_idx])\n",
        "        plt.title(f'{label} - Frekvenčné spektrum')\n",
        "        plt.xlabel('Frekvencia [Hz]')\n",
        "        plt.ylabel('Magnitúda')\n",
        "\n",
        "    plt.suptitle(title)\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "# Spektrálna analýza referenčných signálov\n",
        "spectral_analysis(ref_signals, Fs, ref_labels, \"Spektrálna analýza - Referenčné signály\")\n",
        "\n",
        "# Spektrálna analýza testovacích signálov\n",
        "spectral_analysis(test_signals, Fs, test_labels, \"Spektrálna analýza - Testovacie signály\")\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1986
        },
        "id": "GD9rdOFgYjpn",
        "outputId": "1de969b0-0145-48ca-dac8-1618095aab90"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1000 with 4 Axes>"
            ],
            "image/png": "[encoded data, 161916 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1000 with 4 Axes>"
            ],
            "image/png": "[encoded data, 147796 characters]\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Hľadanie základných frekvencií\n",
        "Nájdem a označím si základnú frekvenciu, je dominantná a zo všetkých grafou ju je ľahko vidieť. Vypíšem jej hodnotu. Podľa tejto hodnoty by sa v podstate dali zistiť konkrétne otáčky všetkých motorov."
      ],
      "metadata": {
        "id": "LAGKncYiFI-R"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def analyze_fundamental_frequency(signal, fs, freq_range=(20, 500)):\n",
        "    \"\"\"\n",
        "    Analyzuje frekvenčné spektrum a nájde základnú frekvenciu\n",
        "\n",
        "    Parametre:\n",
        "    signal: vstupný signál\n",
        "    fs: vzorkovacia frekvencia\n",
        "    freq_range: rozsah frekvencií pre hľadanie základnej frekvencie (min, max)\n",
        "\n",
        "    Návratové hodnoty:\n",
        "    freqs: všetky frekvencie\n",
        "    magnitude: magnitudy frekvencií\n",
        "    fund_freq: základná frekvencia\n",
        "    \"\"\"\n",
        "    # Výpočet FFT\n",
        "    fft_sig = np.fft.fft(signal)\n",
        "    freqs = np.fft.fftfreq(len(signal), 1/fs)\n",
        "    magnitude = np.abs(fft_sig)\n",
        "\n",
        "    # Obmedzenie na pozitívne frekvencie\n",
        "    positive_freq_mask = freqs >= 0\n",
        "    freqs = freqs[positive_freq_mask]\n",
        "    magnitude = magnitude[positive_freq_mask]\n",
        "\n",
        "    # Nájdenie základnej frekvencie v danom rozsahu\n",
        "    freq_mask = (freqs >= freq_range[0]) & (freqs <= freq_range[1])\n",
        "    fund_freq = freqs[freq_mask][np.argmax(magnitude[freq_mask])]\n",
        "\n",
        "    return freqs, magnitude, fund_freq\n",
        "\n",
        "def plot_frequency_spectra(signals, labels, fs, title):\n",
        "    \"\"\"\n",
        "    Vykreslí frekvenčné spektrá so zvýraznenou základnou frekvenciou\n",
        "    \"\"\"\n",
        "    n_signals = len(signals)\n",
        "    plt.figure(figsize=(15, 3*n_signals))\n",
        "\n",
        "    for i, (signal, label) in enumerate(zip(signals, labels)):\n",
        "        # Analýza frekvenčného spektra\n",
        "        freqs, magnitude, fund_freq = analyze_fundamental_frequency(signal, fs)\n",
        "\n",
        "        # Vykreslenie spektra\n",
        "        plt.subplot(n_signals, 1, i+1)\n",
        "        plt.plot(freqs, magnitude)\n",
        "\n",
        "        # Zvýraznenie základnej frekvencie\n",
        "        fund_idx = np.argmin(np.abs(freqs - fund_freq))\n",
        "        plt.plot(fund_freq, magnitude[fund_idx], 'r*',\n",
        "                markersize=15, label=f'Základná frekvencia: {fund_freq:.1f} Hz')\n",
        "\n",
        "        plt.title(f'{label} - Frekvenčné spektrum')\n",
        "        plt.xlabel('Frekvencia [Hz]')\n",
        "        plt.ylabel('Magnitúda')\n",
        "        plt.grid(True)\n",
        "        plt.legend()\n",
        "\n",
        "        # Obmedzenie zobrazenia na relevantný rozsah frekvencií\n",
        "        plt.xlim(0, 1000)  # zobrazíme len prvých 1000 Hz\n",
        "\n",
        "    plt.suptitle(title)\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "# Analýza referenčných signálov\n",
        "plot_frequency_spectra(ref_signals, ref_labels, Fs, \"Frekvenčné spektrá - Referenčné signály\")\n",
        "\n",
        "# Analýza testovacích signálov\n",
        "plot_frequency_spectra(test_signals, test_labels, Fs, \"Frekvenčné spektrá - Testovacie signály\")\n",
        "\n",
        "# Výpis základných frekvencií\n",
        "print(\"\\nZákladné frekvencie referenčných signálov:\")\n",
        "for signal, label in zip(ref_signals, ref_labels):\n",
        "    _, _, fund_freq = analyze_fundamental_frequency(signal, Fs)\n",
        "    print(f\"{label}: {fund_freq:.1f} Hz\")\n",
        "\n",
        "print(\"\\nZákladné frekvencie testovacích signálov:\")\n",
        "for signal, label in zip(test_signals, test_labels):\n",
        "    _, _, fund_freq = analyze_fundamental_frequency(signal, Fs)\n",
        "    print(f\"{label}: {fund_freq:.1f} Hz\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 2584
        },
        "id": "JWkRY8EsMthR",
        "outputId": "c60896c0-6823-4f0a-989f-0742ac20023c"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1200 with 4 Axes>"
            ],
            "image/png": "[encoded data, 256100 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1200 with 4 Axes>"
            ],
            "image/png": "[encoded data, 249552 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "Základné frekvencie referenčných signálov:\n",
            "Fiat_Panda_Drive: 216.0 Hz\n",
            "BMW_318i_Drive: 38.0 Hz\n",
            "Peugeot_307_Drive: 123.0 Hz\n",
            "Audi_A3_Drive: 127.0 Hz\n",
            "\n",
            "Základné frekvencie testovacích signálov:\n",
            "test_t: 25.0 Hz\n",
            "test_s: 59.0 Hz\n",
            "test_f: 166.0 Hz\n",
            "test_i: 42.0 Hz\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Normalizácia frekvenčnej osi\n",
        "Znormalizujem si frekvenčnú os na násobky základnej frekvencie. Frekvenčné spektrum sa bude dať porovnávať medzi nahrávkami."
      ],
      "metadata": {
        "id": "2hAheJQkGD8v"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def get_normalized_spectrum(signal, fs):\n",
        "    \"\"\"\n",
        "    Vypočíta a posunie frekvenčné spektrum tak, aby základná frekvencia bola na fixnej pozícii\n",
        "    \"\"\"\n",
        "\n",
        "    # Výpočet FFT\n",
        "    fft_sig = np.fft.fft(signal)\n",
        "    freqs = np.fft.fftfreq(len(signal), 1/fs)\n",
        "    magnitude = np.abs(fft_sig)\n",
        "\n",
        "    # Nájdenie základnej frekvencie (v rozsahu 20-500 Hz)\n",
        "    positive_mask = freqs >= 0\n",
        "    freq_range_mask = (freqs >= 20) & (freqs <= 500)\n",
        "    valid_mask = positive_mask & freq_range_mask\n",
        "\n",
        "    fund_freq = freqs[valid_mask][np.argmax(magnitude[valid_mask])]\n",
        "\n",
        "    # Vytvorenie normalizovanej frekvenčnej osi\n",
        "    # Posunieme frekvencie tak, aby základná frekvencia bola na 1\n",
        "    normalized_freqs = freqs / fund_freq\n",
        "\n",
        "    return normalized_freqs[positive_mask], magnitude[positive_mask], fund_freq\n",
        "\n",
        "def plot_normalized_spectra(signals, labels, fs, title):\n",
        "    \"\"\"\n",
        "    Vykreslí normalizované frekvenčné spektrá\n",
        "    \"\"\"\n",
        "    plt.figure(figsize=(15, 10))\n",
        "\n",
        "    for i, (signal, label) in enumerate(zip(signals, labels)):\n",
        "        # Získanie normalizovaného spektra\n",
        "        norm_freqs, magnitude, fund_freq = get_normalized_spectrum(signal, fs)\n",
        "\n",
        "        plt.subplot(len(signals), 1, i+1)\n",
        "        plt.plot(norm_freqs, magnitude)\n",
        "        plt.axvline(x=1, color='r', linestyle='--', label='Základná frekvencia')\n",
        "\n",
        "        # Zvýraznenie harmonických\n",
        "        for n in range(2, 6):\n",
        "            plt.axvline(x=n, color='g', linestyle=':', alpha=0.5)\n",
        "\n",
        "        plt.title(f'{label} (Základná frekvencia: {fund_freq:.1f} Hz)')\n",
        "        plt.xlabel('Normalizovaná frekvencia (f/f0)')\n",
        "        plt.ylabel('Magnitúda')\n",
        "        plt.xlim(0, 5)  # Zobrazíme prvých 5 harmonických\n",
        "        plt.grid(True)\n",
        "        plt.legend()\n",
        "\n",
        "    plt.suptitle(title)\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "# Analýza referenčných signálov\n",
        "print(\"Referenčné signály:\")\n",
        "plot_normalized_spectra(ref_signals, ref_labels, Fs, \"Normalizované frekvenčné spektrá - Referenčné signály\")\n",
        "\n",
        "# Analýza testovacích signálov\n",
        "print(\"\\nTestovacie signály:\")\n",
        "plot_normalized_spectra(test_signals, test_labels, Fs, \"Normalizované frekvenčné spektrá - Testovacie signály\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 2037
        },
        "id": "52xXcPH4PG6m",
        "outputId": "89a0b144-868b-4b7a-99de-2d2485ab27b3"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Referenčné signály:\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1000 with 4 Axes>"
            ],
            "image/png": "[encoded data, 245056 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "Testovacie signály:\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1000 with 4 Axes>"
            ],
            "image/png": "[encoded data, 226672 characters]\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Teraz už z grafov je možné badať podobnosti, prípadne rozdiely medzi jednotlivými signálmi.\n",
        "### Výsledná analýza spektra a výpočet vzdialeností medzi nahrávkami\n",
        "Ďalej normalizujem amplitúdu, a vypočítam pomer medzi základnou frekvenciou a jej harmonickými násobkami. Avšak hodnotu frekvencií hramonických násobkov budem brať ako maximálnu hodnotu z malého okolia harmonického násobku aby som obmedzil nepresnosti, napríklad pri nahrávke Audi_A3_Drive sú vrcholy frekvencií v harmonických násobkoch mierne posunuté, čo by mohlo spôsobovať nepresné výsledky.\n",
        "Vypočítam vzdialenosti medzi pomermi, pričom do výpočtu budem brať len prvých 6 harmonických frekvencií okrem základnej, je to z dôvodu toho že aj keď som zobral viac, tak sa už výsledok zásadne nezmenil."
      ],
      "metadata": {
        "id": "X8RGq27yGkFP"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def get_normalized_spectrum_with_peak_ratios(signal, fs, window_width=0.05):\n",
        "    \"\"\"\n",
        "    Vykonáva spektrálnu analýzu zvukových signálov založenú na pomeroch\n",
        "    maximálnych amplitúd v okolí harmonických frekvencií.\n",
        "\n",
        "    Parametre:\n",
        "    signal: Časový priebeh zvukového signálu\n",
        "    fs: Vzorkovacia frekvencia\n",
        "    window_width: Šírka okna okolo harmonických pre analýzu (ako zlomok harmonickej frekvencie)\n",
        "\n",
        "    Vracia:\n",
        "    norm_freqs: Normalizované frekvencie (relatívne k základnej)\n",
        "    magnitude: Normalizované magnitudové spektrum\n",
        "    fund_freq: Základná frekvencia\n",
        "    peak_ratios: Normalizované pomery maximálnych amplitúd harmonických komponentov\n",
        "    highlight_masks: Boolovské masky pre vizualizáciu\n",
        "    \"\"\"\n",
        "    # Aplikujeme Hanningovo okno pre zníženie spektrálneho presakovania\n",
        "    window = np.hanning(len(signal))\n",
        "    signal = signal * window\n",
        "\n",
        "    # Vypočítame FFT a získame frekvenčnú os\n",
        "    fft_sig = np.fft.fft(signal)\n",
        "    freqs = np.fft.fftfreq(len(signal), 1/fs)\n",
        "    magnitude = np.abs(fft_sig)\n",
        "\n",
        "    # Vytvoríme masky pre platné frekvenčné rozsahy\n",
        "    positive_mask = freqs >= 0\n",
        "    # Zameriame sa na typický rozsah frekvencií motora (20-500 Hz)\n",
        "    freq_range_mask = (freqs >= 20) & (freqs <= 500)\n",
        "    valid_mask = positive_mask & freq_range_mask\n",
        "\n",
        "    # Odhadneme úroveň šumu pre lepšiu detekciu špičiek\n",
        "    noise_floor = np.median(magnitude[valid_mask])\n",
        "    signal_threshold = noise_floor * 3\n",
        "\n",
        "    # Nájdeme významné špičky nad úrovňou šumu\n",
        "    peaks = magnitude[valid_mask] > signal_threshold\n",
        "    potential_freqs = freqs[valid_mask][peaks]\n",
        "    potential_mags = magnitude[valid_mask][peaks]\n",
        "\n",
        "    # Identifikujeme základnú frekvenciu z najsilnejšej špičky\n",
        "    if len(potential_mags) > 0:\n",
        "        fund_freq_idx = np.argmax(potential_mags)\n",
        "        fund_freq = potential_freqs[fund_freq_idx]\n",
        "        fund_magnitude = potential_mags[fund_freq_idx]\n",
        "    else:\n",
        "        max_idx = np.argmax(magnitude[valid_mask])\n",
        "        fund_freq = freqs[valid_mask][max_idx]\n",
        "        fund_magnitude = magnitude[valid_mask][max_idx]\n",
        "\n",
        "    # Normalizujeme amplitúdy vzhľadom na amplitúdu základnej frekvencie\n",
        "    magnitude = magnitude / fund_magnitude\n",
        "\n",
        "    # Normalizujeme frekvencie relatívne k základnej\n",
        "    norm_freqs = freqs[positive_mask] / fund_freq\n",
        "    magnitude = magnitude[positive_mask]\n",
        "\n",
        "    # Analyzujeme harmonické komponenty pomocou maximálnych amplitúd\n",
        "    peak_ratios = []\n",
        "    n_harmonics = 6\n",
        "    highlight_masks = []\n",
        "\n",
        "    for n in range(1, n_harmonics + 1):\n",
        "        # Vytvoríme úzke okno okolo každej harmonickej frekvencie\n",
        "        window_mask = (norm_freqs >= n - window_width) & \\\n",
        "                     (norm_freqs <= n + window_width)\n",
        "        highlight_masks.append(window_mask)\n",
        "\n",
        "        # Použijeme maximálnu hodnotu v okne\n",
        "        if np.any(window_mask):\n",
        "            peak_value = np.max(magnitude[window_mask])\n",
        "            peak_ratios.append(peak_value)\n",
        "        else:\n",
        "            peak_ratios.append(noise_floor / fund_magnitude)  # Normalizovaná hodnota šumu\n",
        "\n",
        "    return norm_freqs, magnitude, fund_freq, np.array(peak_ratios), highlight_masks\n",
        "\n",
        "def plot_normalized_spectra(signals, labels, fs, title, window_width=0.05):\n",
        "    \"\"\"\n",
        "    Vytvára vizualizáciu normalizovaných spektier so zvýraznenými harmonickými frekvenciami.\n",
        "    \"\"\"\n",
        "    plt.figure(figsize=(15, 10))\n",
        "\n",
        "    for i, (signal, label) in enumerate(zip(signals, labels)):\n",
        "        norm_freqs, magnitude, fund_freq, peak_ratios, highlight_masks = \\\n",
        "            get_normalized_spectrum_with_peak_ratios(signal, fs, window_width)\n",
        "\n",
        "        plt.subplot(len(signals), 1, i+1)\n",
        "        plt.plot(norm_freqs, magnitude)\n",
        "\n",
        "        # Zvýrazníme oblasti harmonických frekvencií a pridáme označenie pomerov\n",
        "        colors = ['blue', 'green', 'pink', 'yellow', 'gray', 'orange']\n",
        "        for n, (mask, ratio, color) in enumerate(zip(highlight_masks, peak_ratios, colors)):\n",
        "            plt.fill_between(norm_freqs[mask], magnitude[mask], alpha=0.3, color=color)\n",
        "            center_freq = n + 1\n",
        "            max_mag = np.max(magnitude[mask]) if np.any(mask) else 0\n",
        "            plt.text(center_freq, max_mag, f'P{n+1}: {ratio:.2f}',\n",
        "                    horizontalalignment='center', verticalalignment='bottom')\n",
        "\n",
        "        plt.axvline(x=1, color='r', linestyle='--', label='Základná f')\n",
        "        plt.axhline(y=1, color='g', linestyle='--', label='Základná A')\n",
        "\n",
        "        plt.title(f'{label} (f0: {fund_freq:.1f} Hz)')\n",
        "        plt.xlabel('Normalizovaná frekvencia (f/f0)')\n",
        "        plt.ylabel('Norm. amplitúda (A/A0)')\n",
        "        plt.xlim(0, 7)\n",
        "        plt.grid(True)\n",
        "        plt.legend()\n",
        "\n",
        "    plt.suptitle(f'{title}\\nPomery maximálnych amplitúd (±{window_width * 100}% okno)')\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "def match_test_to_reference(ref_signals, test_signals, ref_labels, test_labels, fs, window_width=0.05):\n",
        "    \"\"\"\n",
        "    Priraďuje testovacie signály k referenčným na základe porovnania\n",
        "    pomerov maximálnych amplitúd harmonických frekvencií.\n",
        "    Pre každý referenčný signál nájde testovací signál s najmenšou vzdialenosťou.\n",
        "    \"\"\"\n",
        "    # Získame pomery špičiek pre všetky signály\n",
        "    ref_ratios = []\n",
        "    test_ratios = []\n",
        "\n",
        "    for signal in ref_signals:\n",
        "        _, _, _, ratios, _ = get_normalized_spectrum_with_peak_ratios(signal, fs, window_width)\n",
        "        ref_ratios.append(ratios)\n",
        "\n",
        "    for signal in test_signals:\n",
        "        _, _, _, ratios, _ = get_normalized_spectrum_with_peak_ratios(signal, fs, window_width)\n",
        "        test_ratios.append(ratios)\n",
        "\n",
        "    ref_ratios = np.array(ref_ratios)\n",
        "    test_ratios = np.array(test_ratios)\n",
        "\n",
        "    # Vypočítame euklidovskú vzdialenosť medzi pomermi\n",
        "    all_distances = np.zeros((len(test_signals), len(ref_signals)))\n",
        "    for i, test_ratio in enumerate(test_ratios):\n",
        "        for j, ref_ratio in enumerate(ref_ratios):\n",
        "            distance = np.sqrt(np.sum((test_ratio - ref_ratio) ** 2))\n",
        "            all_distances[i, j] = distance\n",
        "\n",
        "    # Výpis matice vzdialeností\n",
        "    print(\"\\nVzdialenosti medzi pomermi maximálnych amplitúd:\")\n",
        "    print(\"-\" * 50)\n",
        "    print(f\"{'':15}\", end=\"\")\n",
        "    for ref_label in ref_labels:\n",
        "        print(f\"{ref_label:15} \", end=\"\")\n",
        "    print()\n",
        "\n",
        "    for i, test_label in enumerate(test_labels):\n",
        "        print(f\"{test_label:15}\", end=\"\")\n",
        "        for j in range(len(ref_labels)):\n",
        "            print(f\"{all_distances[i,j]:15.3f}\", end=\"\")\n",
        "        print()\n",
        "\n",
        "    # Pre každý referenčný signál nájdeme najbližší testovací signál\n",
        "    final_matches = {}\n",
        "\n",
        "    for ref_idx, ref_label in enumerate(ref_labels):\n",
        "        # Nájdeme najbližší testovací signál pre tento referenčný signál\n",
        "        distances_to_ref = all_distances[:, ref_idx]\n",
        "        closest_test_idx = np.argmin(distances_to_ref)\n",
        "        test_label = test_labels[closest_test_idx]\n",
        "        min_distance = distances_to_ref[closest_test_idx]\n",
        "\n",
        "        final_matches[test_label] = (ref_label, min_distance)\n",
        "\n",
        "    # Výpis výsledkov\n",
        "    print(\"\\nNájdené zhody:\")\n",
        "    print(\"-\" * 50)\n",
        "    for test_label, (ref_label, distance) in final_matches.items():\n",
        "        print(f\"{test_label} -> {ref_label} (vzdialenosť: {distance:.3f})\")\n",
        "\n",
        "    # Nájdeme nepriradené testovacie signály\n",
        "    matched_test_labels = set(final_matches.keys())\n",
        "    unmatched_test_labels = set(test_labels) - matched_test_labels\n",
        "\n",
        "    if unmatched_test_labels:\n",
        "        print(\"\\nNepriradené testovacie signály:\")\n",
        "        for test_label in unmatched_test_labels:\n",
        "            print(f\"- {test_label}\")\n",
        "\n",
        "    return final_matches\n",
        "\n",
        "def analyze_car_sounds(ref_signals, test_signals, ref_labels, test_labels, fs):\n",
        "    \"\"\"\n",
        "    Vykonáva kompletnú analýzu zvukov automobilových motorov.\n",
        "    \"\"\"\n",
        "    plot_normalized_spectra(ref_signals, ref_labels, fs,\n",
        "                          \"Analýza referenčných signálov\")\n",
        "\n",
        "    plot_normalized_spectra(test_signals, test_labels, fs,\n",
        "                          \"Analýza testovacích signálov\")\n",
        "\n",
        "    matches = match_test_to_reference(\n",
        "        ref_signals, test_signals, ref_labels, test_labels, fs)\n",
        "\n",
        "    return matches\n",
        "# Spustíme analýzu\n",
        "matches = analyze_car_sounds(ref_signals, test_signals, ref_labels, test_labels, Fs)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 2239
        },
        "id": "QIQjrYFN52DE",
        "outputId": "64bd1de8-cf81-4e73-8dcc-44bc7ae4e4a9"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1000 with 4 Axes>"
            ],
            "image/png": "[encoded data, 289220 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1500x1000 with 4 Axes>"
            ],
            "image/png": "[encoded data, 265876 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "Vzdialenosti medzi pomermi maximálnych amplitúd:\n",
            "--------------------------------------------------\n",
            "               Fiat_Panda_Drive BMW_318i_Drive  Peugeot_307_Drive Audi_A3_Drive   \n",
            "test_t                   0.400          0.356          0.295          0.263\n",
            "test_s                   0.153          0.146          0.244          0.414\n",
            "test_f                   0.036          0.080          0.329          0.513\n",
            "test_i                   0.098          0.054          0.253          0.468\n",
            "\n",
            "Nájdené zhody:\n",
            "--------------------------------------------------\n",
            "test_f -> Fiat_Panda_Drive (vzdialenosť: 0.036)\n",
            "test_i -> BMW_318i_Drive (vzdialenosť: 0.054)\n",
            "test_s -> Peugeot_307_Drive (vzdialenosť: 0.244)\n",
            "test_t -> Audi_A3_Drive (vzdialenosť: 0.263)\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "###4. Výsledky\n",
        "Ku každému autu som dokázal násť neznámu nahrávku s najmenšou vzdialenosťou. Pri Peugeote sú výsledky najmenej jednoznačné. Ale všetky nahrávky zrejme patria k daným autám. Ďalej môžeme vidieť, že Fiat aj BMW maju veľmi malú vzdialenosť k svojim priradeným nahrávkam, pričom Peugeot a Audi majú výrazne vyššiu túto vzdialenosť. To môže byť spôsobené výraznejším rozdielom medzi otáčkami motorov oboch párov nahrávok Peugeotu a Audi, prípadne rôznym šumom v pozadí. Hoci za ideálnych okolností by mali byť tieto vplivy odfiltrované.\n",
        "\n",
        "###5. Záver\n",
        "Implementovaný systém by mal demonštrovať schopnosť rozpoznávať a priraďovať zvuky automobilových motorov na základe ich spektrálnych charakteristík, konkrétne porovnávaním pomerov základnej frekvencie k jej harmonickým frekvenciám."
      ],
      "metadata": {
        "id": "bWeBNWGKJZRi"
      }
    }
  ]
}