{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\nStart to end prototype example\n==============================\nThis example shows a start to end workflow which begins with detecting \ncalls across channels in an audio file, and then moving to localising the\ncall positions. It ends with a display of the localised positions in comparison\nwith the original simulated positions. \n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import batracker\nfrom batracker.localisation import friedlander_1987 as fr87\nimport matplotlib.pyplot as plt\nplt.rcParams['agg.path.chunksize']=10000\nfrom mpl_toolkits.mplot3d import Axes3D\nimport numpy as np \nimport pandas as pd\nimport scipy.io.wavfile as wavfile\nfrom batracker.signal_detection import detection\nfrom batracker.correspondence_matching import multichannel_match as matching\nfrom batracker.tdoa_estimation import tdoa_estimators"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Load the simulated audio file \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import tacost\nfrom tacost import calculate_toa as ctoa"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Generate an array geometry and use simulated source positions\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "mic_positions = np.array([[ 0, 0, 0],\n                          [ 1, 0, 0],\n                          [-1, 1, 0],\n                          [ 0, 0.5, 0],\n                          [-2,2,1]\n                       ])\n\nx = np.linspace(2,10,3)\ny = x.copy()\nz = x.copy()\n\nxyz = np.array(np.meshgrid(x,y,z)).T.reshape(-1,3)\n\ntest_pos = xyz\ntoa = ctoa.calculate_mic_arrival_times(test_pos,\n                                       array_geometry=mic_positions,\n                                       intersound_interval=0.05)\n\ntest_audio, fs = tacost.make_sim_audio.create_audio_for_mic_arrival_times(toa,\n                                                        call_snr=80)\naudio = test_audio.copy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Detect the calls in each channel \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "detections = detection.cross_channel_threshold_detector(audio, fs,\n                                              dbrms_window=0.5*10**-3,\n                                              dbrms_threshold=-60)\n\n# Spectrogram of the cross-corr boundaries\nplt.figure()\nax= plt.subplot(411)\nplt.specgram(audio[:,0], Fs=fs)\nfor each in detections[0]:\n    plt.vlines(each, 0, fs*0.5, linewidth=0.2)\n\nfor i in range(2,5):\n    plt.subplot(410+i, sharex=ax)\n    plt.specgram(audio[:,i-1], Fs=fs)\n    for each in detections[i-1]:\n        plt.vlines(each, 0, fs*0.5, linewidth=0.2)\n\n\n# Waveformsof the detection \n\nplt.figure()\nax= plt.subplot(411)\nplt.plot(audio[:,0])\nfor each in detections[0]:\n    plt.vlines(np.array(each)*fs, np.min(audio), np.max(audio), 'k',linewidth=0.5)\n\nfor i in range(2,5):\n    plt.subplot(410+i, sharex=ax)\n    plt.plot(audio[:,i-1])\n    for each in detections[i-1]:\n        plt.vlines(np.array(each)*fs,\n                   np.min(audio), np.max(audio), 'k',linewidth=0.5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The cross-cor boundary needs to be calculated keeping the whole signal duration \nin mind too!!\n\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Perform correspondence matching and generate the common boundaries\nacross channels for cross=correlation. Also, load the mic array geometry \nas the max- inter-mic distances are required  for the calculation of max\ninter-mic delays\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "ag = pd.DataFrame(mic_positions)\nag.columns  = ['x','y','z']\n\ncrosscor_boundaries = matching.generate_crosscor_boundaries(detections, ag)\n\nnum_channels = audio.shape[1]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Estimate time-difference-of-arrival across different channels and sounds\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "reference_ch = 3\n\nall_tdoas = {}\nfor i,each_common in enumerate(crosscor_boundaries):\n    start, stop = each_common\n    start_sample, stop_sample = int(start*fs), int(stop*fs)\n    \n    tdoas = tdoa_estimators.measure_tdoa(audio[start_sample:stop_sample,:], fs, ref_channel=reference_ch)\n    all_tdoas[i] = tdoas"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Use the TDOAs to calculate positions of sound sources\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "vsound = 338.0\nall_positions = []\nnum_rows = mic_positions.shape[0]-1\ncalculated_positions = np.zeros((test_pos.shape[0], 3))\nfor det_number, tdoas in all_tdoas.items():\n    try:\n        d = vsound*tdoas\n        pos = fr87.solve_friedlander1987(mic_positions, d, j=reference_ch, \n                                         use_analytical=False).flatten()\n        calculated_positions[det_number,:] = pos\n    except:\n        print(f'COULD NOT CALCULATE POSITION FOR TEST POSITION {det_number}')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Accuracy\n--------\nNow let's estimate the accuracy of the positions in general, in terms of their ranges\nfrom the origin. \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "test_ranges = np.apply_along_axis(fr87.distance_to_point, 1, test_pos, [0,0,0])\ncalc_ranges = np.apply_along_axis(fr87.distance_to_point, 1, calculated_positions, [0,0,0])\n\nrange_accuracy = calc_ranges/test_ranges\n\nplt.figure(figsize=(10,8))\nplt.plot(test_ranges, range_accuracy, '-*')\nplt.ylabel('Range accuracy, $\\\\frac{Calculated\\ range}{Actual\\ range}$', fontsize=12)\nplt.xlabel('Range, m', fontsize=12)\nplt.tight_layout()\n\nfig = plt.figure(figsize=(10,8))\nax = fig.add_subplot(111, projection='3d')\nax.view_init(elev=24, azim=16)\nax.plot(test_pos[:,0], test_pos[:,1], test_pos[:,2],'*', label='actual')\nax.plot(calculated_positions[:,0], calculated_positions[:,1],\n        calculated_positions[:,2],'*', label='calculated')\n\nax.plot(mic_positions[0:2,0], mic_positions[0:2,1],\n        mic_positions[0:2,2],'g-*')\nax.plot(mic_positions[2:4,0], mic_positions[2:4,1],\n        mic_positions[2:4,2],'g-*')\nax.plot(mic_positions[[0,4],0], mic_positions[[0,4],1],\n        mic_positions[[0,4],2],'g-*')\nax.plot(mic_positions[[0,3],0], mic_positions[[0,3],1],\n        mic_positions[[0,3],2],'g-*', label='Mic array')\nplt.legend()\nplt.tight_layout()\nax.set_xlabel('X', fontsize=12);ax.set_ylabel('Y', fontsize=12); ax.set_zlabel('Z', fontsize=12)"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.9"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}