├── path-loss ├── __init__.py ├── __pycache__ │ ├── Pathloss.cpython-37.pyc │ └── Pathloss.cpython-38.pyc ├── received_power.py ├── path-loss-exponent.py └── Pathloss.py ├── README.md ├── minmax_results.txt ├── trilateration_results.txt └── algorithms ├── minmax.py └── trilateration.py /path-loss/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /path-loss/__pycache__/Pathloss.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifindev/indoor-positioning-algorithms/HEAD/path-loss/__pycache__/Pathloss.cpython-37.pyc -------------------------------------------------------------------------------- /path-loss/__pycache__/Pathloss.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifindev/indoor-positioning-algorithms/HEAD/path-loss/__pycache__/Pathloss.cpython-38.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Indoor Positioning Algorithms 2 | 3 | ## Description 4 | In this repository, you can python code implementation of trilateration and min-max methods for calculating positions in an indoor setting using Wi-Fi RSSI data. There are two folders in this repo. These are the description of each one of them: 5 | - algorithms: Repo for trilateration and min-max algorithm implementations. 6 | - path-loss: Repo containing a path loss model simulation and a Path Loss calculator build using a very simple python OOP. 7 | 8 | ## How to Run the Code 9 | To run the codes, use this steps: 10 | - Install `ipython3` on your computer. 11 | - Install the required python modules such as `numpy`, `matplotlib`, `pylab`, and `Pillow`. 12 | - On command prompt (windows) or on terminal (linux), type `ipython3` and enter. 13 | - On `ipyhton3` console, write `%run python-file-name.py` and enter to run the code. 14 | - Enjoy. 15 | 16 | ## Copyright 17 | (C) Muhammad Arifin - Engineering Physics 2015, Universitas Gadjah Mada 18 | -------------------------------------------------------------------------------- /path-loss/received_power.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | d = np.linspace(1,10,100) 5 | pt = 1 6 | Gl = 1 7 | c = 3*(10**8) 8 | 9 | f1 = 300 #MHz 10 | f2 = 600*(10**6) #MHz 11 | f3 = 900*(10**6) #GHz 12 | 13 | lm1 = c/f1 14 | lm2 = c/f2 15 | lm3 = c/f3 16 | 17 | pr1 = pt * ((np.sqrt(Gl)*lm1) / (4*np.pi*d))**2 18 | pr2 = pt * ((np.sqrt(Gl)*lm2) / (4*np.pi*d))**2 19 | pr3 = pt * ((np.sqrt(Gl)*lm3) / (4*np.pi*d))**2 20 | 21 | #pr1 = np.log10(pt) + 10*np.log10(Gl) + 20*np.log10(lm1) - 20*np.log10(4*np.pi) - 20*np.log10(d) 22 | #pr2 = np.log10(pt) + 10*np.log10(Gl) + 20*np.log10(lm2) - 20*np.log10(4*np.pi) - 20*np.log10(d) 23 | 24 | plt.title("Received Power vs Frequency") 25 | plt.plot(d, pr1*(10**3),linestyle='-.', color='black',label="$f$ = 300 MHz") 26 | plt.plot(d, pr2*(10**3),linestyle='-', color='black',label="$f$ = 600 MHz") 27 | plt.plot(d, pr3*(10**3),linestyle='--', color='black',label="$f$ = 900 MHz") 28 | plt.xlim([1,10]) 29 | plt.ylim([0,6]) 30 | plt.xlabel("d [m]") 31 | plt.ylabel("$P_{r}$ [mW]") 32 | plt.legend() 33 | plt.grid() 34 | plt.show() 35 | -------------------------------------------------------------------------------- /minmax_results.txt: -------------------------------------------------------------------------------- 1 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Variasi Jarak ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | 3 | Localization using ESP32 1D1 4 | MSE Min-Max 1D1: 0.38 5 | 6 | Localization using ESP32 1D2 7 | MSE Min-Max 1D2: 0.1 8 | 9 | Localization using ESP32 1D3 10 | MSE Min-Max 1D3: 0.17 11 | 12 | Localization using ESP32 1D4 13 | MSE Min-Max 1D4: 0.36 14 | 15 | Localization using ESP32 2D1 16 | MSE Min-Max 2D1: 0.37 17 | 18 | Localization using ESP32 2D2 19 | MSE Min-Max 2D2: 0.19 20 | 21 | Localization using ESP32 2D3 22 | MSE Min-Max 2D3: 0.57 23 | 24 | Localization using ESP32 2D4 25 | MSE Min-Max 2D4: 0.76 26 | 27 | Localization using ESP32 3D1 28 | MSE Min-Max 3D1: 0.76 29 | 30 | Localization using ESP32 3D2 31 | MSE Min-Max 3D2: 0.53 32 | 33 | Localization using ESP32 3D3 34 | MSE Min-Max 3D3: 0.38 35 | 36 | Localization using ESP32 3D4 37 | MSE Min-Max 3D4: 1.51 38 | 39 | Localization using ESP32 4D1 40 | MSE Min-Max 4D1: 0.83 41 | 42 | Localization using ESP32 4D2 43 | MSE Min-Max 4D2: 0.72 44 | 45 | Localization using ESP32 4D3 46 | MSE Min-Max 4D3: 0.89 47 | 48 | Localization using ESP32 4D4 49 | MSE Min-Max 4D4: 1.53 50 | 51 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Variasi Jarak dan Manusia ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52 | 53 | Localization using ESP32 1D1 54 | MSE Min-Max 1D1: 0.2 55 | 56 | Localization using ESP32 1D2 57 | MSE Min-Max 1D2: 0.28 58 | 59 | Localization using ESP32 1D3 60 | MSE Min-Max 1D3: 0.5 61 | 62 | Localization using ESP32 1D4 63 | MSE Min-Max 1D4: 0.83 64 | 65 | Localization using ESP32 2D1 66 | MSE Min-Max 2D1: 0.14 67 | 68 | Localization using ESP32 2D2 69 | MSE Min-Max 2D2: 0.38 70 | 71 | Localization using ESP32 2D3 72 | MSE Min-Max 2D3: 0.93 73 | 74 | Localization using ESP32 2D4 75 | MSE Min-Max 2D4: 1.07 76 | 77 | Localization using ESP32 3D1 78 | MSE Min-Max 3D1: 0.49 79 | 80 | Localization using ESP32 3D2 81 | MSE Min-Max 3D2: 0.57 82 | 83 | Localization using ESP32 3D3 84 | MSE Min-Max 3D3: 0.19 85 | 86 | Localization using ESP32 3D4 87 | MSE Min-Max 3D4: 1.38 88 | 89 | Localization using ESP32 4D1 90 | MSE Min-Max 4D1: 0.63 91 | 92 | Localization using ESP32 4D2 93 | MSE Min-Max 4D2: 0.74 94 | 95 | Localization using ESP32 4D3 96 | MSE Min-Max 4D3: 0.45 97 | 98 | Localization using ESP32 4D4 99 | MSE Min-Max 4D4: 1.54 -------------------------------------------------------------------------------- /trilateration_results.txt: -------------------------------------------------------------------------------- 1 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Variasi Jarak ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | 3 | D:/Skripsweetku/Raw Data/Pengukuran Variasi Jarak Ulangan/ 4 | 5 | 6 | Localization using ESP32 1D1.csv 7 | 8 | MSE Trilaterasi : 0.38 9 | 10 | 11 | Localization using ESP32 1D2.csv 12 | 13 | MSE Trilaterasi : 0.11 14 | 15 | 16 | Localization using ESP32 1D3.csv 17 | 18 | MSE Trilaterasi : 0.17 19 | 20 | 21 | Localization using ESP32 1D4.csv 22 | 23 | MSE Trilaterasi : 0.56 24 | 25 | 26 | Localization using ESP32 2D1.csv 27 | 28 | MSE Trilaterasi : 0.23 29 | 30 | 31 | Localization using ESP32 2D2.csv 32 | 33 | MSE Trilaterasi : 0.26 34 | 35 | 36 | Localization using ESP32 2D3.csv 37 | 38 | MSE Trilaterasi : 0.57 39 | 40 | 41 | Localization using ESP32 2D4.csv 42 | 43 | MSE Trilaterasi : 0.46 44 | 45 | 46 | Localization using ESP32 3D1.csv 47 | 48 | MSE Trilaterasi : 0.81 49 | 50 | 51 | Localization using ESP32 3D2.csv 52 | 53 | MSE Trilaterasi : 0.58 54 | 55 | 56 | Localization using ESP32 3D3.csv 57 | 58 | MSE Trilaterasi : 0.73 59 | 60 | 61 | Localization using ESP32 3D4.csv 62 | 63 | MSE Trilaterasi : 1.64 64 | 65 | 66 | Localization using ESP32 4D1.csv 67 | 68 | MSE Trilaterasi : 0.47 69 | 70 | 71 | Localization using ESP32 4D2.csv 72 | 73 | MSE Trilaterasi : 1.27 74 | 75 | 76 | Localization using ESP32 4D3.csv 77 | 78 | MSE Trilaterasi : 1.06 79 | 80 | 81 | Localization using ESP32 4D4.csv 82 | 83 | MSE Trilaterasi : 0.99 84 | 85 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Variasi Jarak dan Manusia ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 86 | 87 | D:/Skripsweetku/Raw Data/Pengukuran Variasi Jarak dan Manusia/ 88 | 89 | 90 | Localization using ESP32 1D1.csv 91 | 92 | MSE Trilaterasi : 1.05 93 | 94 | 95 | Localization using ESP32 1D2.csv 96 | 97 | MSE Trilaterasi : 1.7 98 | 99 | 100 | Localization using ESP32 1D3.csv 101 | 102 | MSE Trilaterasi : 3.26 103 | 104 | 105 | Localization using ESP32 1D4.csv 106 | 107 | MSE Trilaterasi : 1.22 108 | 109 | 110 | Localization using ESP32 2D1.csv 111 | 112 | MSE Trilaterasi : 0.33 113 | 114 | 115 | Localization using ESP32 2D2.csv 116 | 117 | MSE Trilaterasi : 0.84 118 | 119 | 120 | Localization using ESP32 2D3.csv 121 | 122 | MSE Trilaterasi : 1.9 123 | 124 | 125 | Localization using ESP32 2D4.csv 126 | 127 | MSE Trilaterasi : 1.06 128 | 129 | 130 | Localization using ESP32 3D1.csv 131 | 132 | MSE Trilaterasi : 0.71 133 | 134 | 135 | Localization using ESP32 3D2.csv 136 | 137 | MSE Trilaterasi : 0.59 138 | 139 | 140 | Localization using ESP32 3D3.csv 141 | 142 | MSE Trilaterasi : 1.07 143 | 144 | 145 | Localization using ESP32 3D4.csv 146 | 147 | MSE Trilaterasi : 1.32 148 | 149 | 150 | Localization using ESP32 4D1.csv 151 | 152 | MSE Trilaterasi : 0.48 153 | 154 | 155 | Localization using ESP32 4D2.csv 156 | 157 | MSE Trilaterasi : 1.32 158 | 159 | 160 | Localization using ESP32 4D3.csv 161 | 162 | MSE Trilaterasi : 0.96 163 | 164 | 165 | Localization using ESP32 4D4.csv 166 | 167 | MSE Trilaterasi : 0.94 -------------------------------------------------------------------------------- /path-loss/path-loss-exponent.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : Muhammad Arifin 3 | Institution : Universitas Gadjah Mada, Yogyakarta, Indonesia 4 | Description : Python code for calculating the path loss exponent (PLE) of 5 | path loss measurement data with simplified log-model. 6 | Date : 20th April 2020 7 | 8 | How this code works? 9 | 1. Sympy is used to do symbolic calculations. 10 | 2. Numpy is used to do array operations. 11 | 12 | 13 | Licensing : This program is licensed under MIT License. 14 | MIT License 15 | 16 | Copyright (c) [2020] [Muhammad Arifin] 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all 26 | copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | """ 36 | import numpy as np 37 | import sympy as sym 38 | 39 | 40 | # Real Path Loss Data 41 | # Measured on 20 April 2020 42 | # Measured on the parking lot of Pogung Baru F29 43 | 44 | 45 | #dist = np.array([1.0, 1.5, 2.0, 2.5, 3.0,3.5, 4.0, 4.5, 5.0, 5.5]) 46 | #measured = np.array([-49, -53, -58, -60, -68, -60, -59, -60, -64, -68]) 47 | def finding_ple(distance, measured, frequency): 48 | 49 | c = 3e8 50 | 51 | # Finding log distance and K 52 | log_dist = np.log10(dist) 53 | 54 | k = -20*np.log10(4*np.pi * frequency/c) # Path loss at 1.0 meter 55 | 56 | # Symbolic path loss exponent (n) 57 | n = sym.Symbol('n') 58 | 59 | # Calculating F(n) 60 | fn = (measured - k + 10*n*log_dist)**2 61 | fn_result = 0 62 | 63 | for res in fn: 64 | fn_result += res # Summing all the component 65 | 66 | # Calculating PLE (n) by differentiating F(n) 67 | # then assign dF(n) / dn = 0 for minimum error value 68 | diffn = sym.diff(fn) 69 | diff_result = 0 70 | 71 | for num in diffn: 72 | diff_result += num 73 | 74 | str_result = str(diff_result).replace('*n','').split('-') #list values 75 | ple_result = round(float(str_result[1])/float(str_result[0]), 2) 76 | 77 | return fn_result, str_result, ple_result 78 | 79 | 80 | def finding_std_dev(dist, measured, ple, frequency): 81 | 82 | # Finding log distance and K 83 | log_dist = np.log10(dist) 84 | 85 | c = 3e8 86 | 87 | k = -20*np.log10(4*np.pi * frequency/c) # Path loss at 1.0 meter 88 | 89 | # Calculating average of F(n) 90 | fn_total = (measured - k + 10*ple*log_dist)**2 91 | avg_fn_total = 0 92 | 93 | for num in fn_total: 94 | avg_fn_total += num # Summing all the component 95 | 96 | variance = avg_fn_total / len(dist) 97 | 98 | std_dev = round(np.sqrt(variance),3) 99 | 100 | return std_dev 101 | 102 | 103 | # Distance and measured rssi data 104 | #dist = np.arange(0.5,6.0,0.5) 105 | #meas = np.array([-39,-49,-53,-58,-60,-68,-60,-59, -60, -64, -68]) 106 | 107 | dist = np.array([10, 20, 50, 100, 300]) 108 | meas = np.array([-70, -75, -90, -110, -125]) 109 | f = 2e9 110 | 111 | path_loss = finding_ple(dist, meas, f) 112 | stdev = finding_std_dev(dist, meas, path_loss[2], f) 113 | 114 | fn_res = path_loss[0] 115 | diff_res = path_loss[1] 116 | ple_res = path_loss[2] 117 | 118 | # Print the F(n), diffn, and ple_result 119 | print("F(n) = ",fn_res,"\n") 120 | print("dF(n)/dn = ",diff_res,"\n") 121 | print("PLE (n) = ",ple_res,"\n") 122 | print("Stdev = ", stdev) 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /path-loss/Pathloss.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : Muhammad Arifin 3 | Institution : Department of Nuclear Engineering and Engineering Physics, Universitas Gadjah Mada 4 | Initial Release : 29th April, 2020 5 | License : MIT License 6 | 7 | Description : This program is a direct implementation of mathematical equations 8 | used to calculate path loss exponent information from path loss 9 | measurement data. The implementation is based on theoritical 10 | explanation on Andreas Goldsmith's Wireless Communications book 11 | chapter 2 on Path Loss and Shadowing. 12 | 13 | Licensing : This program is licensed under MIT License. 14 | MIT License 15 | 16 | Copyright (c) [2020] [Muhammad Arifin] 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all 26 | copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | """ 36 | 37 | import numpy as np 38 | import sympy as sym 39 | import matplotlib.pyplot as plt 40 | 41 | 42 | class Pathloss: 43 | def __init__(self, distance, measured, frequency): 44 | """ 45 | Class for creating a path loss object. 46 | 47 | Distance is an array of distances from Tx to Rx. 48 | Measured is the measured rssi values 49 | corresponding to the given distance. 50 | """ 51 | 52 | # distance and rssi values 53 | self.dist = distance 54 | self.meas = measured 55 | 56 | # Finding total Number of data 57 | self.num_data = len(self.dist) 58 | 59 | # Finding measured rssi at d0 60 | self.freq = frequency 61 | self.light_speed = 3e8 62 | self.wavelength = self.light_speed / self.freq 63 | self.d0 = 1.0 64 | #self.k = -20*np.log10(4 * np.pi * self.d0 / self.wavelength) 65 | self.k = self.meas[1] 66 | 67 | # Finding log distance 68 | self.log_dist = np.log10(self.dist) 69 | 70 | # symbolic path loss exponent 71 | self.n = sym.Symbol('n') 72 | 73 | 74 | def finding_ple(self): 75 | """ 76 | Pathloss class method to calculate path loss exponent 77 | given distances and measured rssi data. 78 | 79 | Calculation is based on Andreas Goldsmith Wireless 80 | Communications book p.40 of examples 2.3 81 | 82 | """ 83 | # Calculating F(n) 84 | self.fn = (self.meas - self.k + 10*self.n*self.log_dist)**2 85 | self.fn_result = 0 86 | 87 | for res in self.fn: 88 | self.fn_result += res # Summing all the component 89 | 90 | # Calculating PLE (n) by differentiating F(n) 91 | # then assign dF(n) / dn = 0 for minimum error value 92 | self.diffn = sym.diff(self.fn) 93 | self.diff_result = 0 94 | 95 | for num in self.diffn: 96 | self.diff_result += num 97 | 98 | self.str_result = str(self.diff_result).replace('*n','').split('-') #list values 99 | self.ple_result = round(float(self.str_result[1])/float(self.str_result[0]), 2) 100 | 101 | return self.ple_result 102 | 103 | def finding_stdev(self): 104 | """ 105 | Pathloss class method to calculate standar deviation 106 | from given distances and rssi data. 107 | 108 | Calculation is based on Andreas Goldsmith Wireless 109 | Communications book p.46 of examples 2.4 110 | """ 111 | # finding variance 112 | self.fn_total = (self.meas - self.k + 10*self.ple_result*self.log_dist)**2 113 | self.variance = 0 114 | 115 | for var in self.fn_total: 116 | self.variance += var # summing all the components 117 | 118 | self.std_dev = round(np.sqrt(self.variance / self.num_data) ,2) 119 | 120 | return self.std_dev 121 | 122 | def path_loss_model_simplified(self): 123 | """ 124 | Pathloss class method to calculate the simplified path loss model. 125 | """ 126 | 127 | self.path_loss_simplified = self.k - 10*self.ple_result*np.log10(self.dist) 128 | #self.path_loss_simplified = self.k - 10*3.71*np.log10(self.dist) 129 | return self.path_loss_simplified 130 | 131 | 132 | def path_loss_model_shadowing(self): 133 | """ 134 | Pathloss class method to calculate the path loss model with shadowing. 135 | Shadowing is modeled using gaussian random noise 136 | """ 137 | 138 | #Gaussian random noise with mean = 0 and sigma = std_dev 139 | self.mean = 0 140 | self.gaussian_noise = np.random.normal(self.mean, self.std_dev, self.num_data) 141 | #self.gaussian_noise = np.random.normal(self.mean, 3.65, self.num_data) 142 | 143 | # Path loss model with shadowing modeled as gaussian random noise 144 | self.path_loss_shadowing = self.path_loss_simplified + self.gaussian_noise 145 | 146 | return self.path_loss_shadowing 147 | 148 | 149 | def plotGraph(fx, x, legend): 150 | #plt.figure("Path Loss Graph") 151 | plt.plot(x, fx, "o-", label = str(legend)) 152 | plt.title("Path Loss") 153 | plt.xlabel("d (m)") 154 | plt.ylabel("RSSI (dBm)") 155 | plt.ylim([-75, -35]) 156 | plt.xlim([0, max(x)]) 157 | plt.xticks(np.arange(0.0,5.0,0.5)) 158 | plt.yticks(np.arange(-75,-30,5)) 159 | plt.legend() 160 | plt.grid(color='lightgray', zorder = 10) 161 | plt.show() 162 | 163 | def main(): 164 | # Distance and measured rssi data 165 | #dist = np.arange(0.5,4.5,0.5) 166 | #meas = np.array([-41,-54,-55,-58,-61,-59,-62,-67]) 167 | 168 | #dist = np.array([0.5, 0.7, 0.9, 1.0, 1.2, 1.5, 2.0, 2.5, 3.0, 3.2, 3.5, 3.7, 4.0, 4.2, 4.5, 4.7, 5.0, 5.5]) 169 | #meas = np.array([-39, -42, -51, -49, -50, -53, -58, -60, -68, -60, -60, -56, -59, -62, -60, -62, -64, -68]) 170 | f = 2.4e9 171 | 172 | pathloss = Pathloss(dist, meas, f) 173 | plexp = pathloss.finding_ple() 174 | stdev = pathloss.finding_stdev() 175 | plmodel_simplified = pathloss.path_loss_model_simplified() 176 | plmodel_shadowing = pathloss.path_loss_model_shadowing() 177 | 178 | print("Path Loss Exponent: ", plexp) 179 | print("Standard Deviation: ", stdev) 180 | print("Simplified Path Loss Model: ", plmodel_simplified) 181 | print("Path Loss Model with Shadowing: ", plmodel_shadowing) 182 | 183 | plotGraph(meas, dist, "Measured") 184 | plotGraph(plmodel_simplified, dist, "Simplified") 185 | plotGraph(plmodel_shadowing, dist, "Shadowing") 186 | 187 | if __name__ == '__main__': 188 | try: 189 | main() 190 | except ValueError: 191 | print("ValueError: Number of distance and measured elements must be the same!") 192 | -------------------------------------------------------------------------------- /algorithms/minmax.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : Muhammad Arifin 3 | Institution : Department of Nuclear Engineering and Engineering Physics, Universitas Gadjah Mada 4 | Initial Release : 29th April, 2020 5 | License : MIT License 6 | Description : This program is a direct implementation of mathematical equations used to calculate 7 | min-max algorithm for indoor positioning using Received Signal Strength Indicator (RSSI) 8 | data from four Wi-Fi routers. The mathematics are derived in my thesis and in other 9 | journal papers on internet. 10 | 11 | 12 | Licensing : This program is licensed under MIT License. 13 | 14 | MIT License 15 | 16 | Copyright (c) [2020] [Muhammad Arifin] 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all 26 | copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | """ 36 | 37 | import numpy as np 38 | import matplotlib.pyplot as plt 39 | import pandas as pd 40 | import os 41 | 42 | 43 | 44 | def main(): 45 | # path = 'D:/Skripsweetku/Raw Data/Pengukuran Variasi Jarak Ulangan/' 46 | path = 'D:/Skripsweetku/Raw Data/Pengukuran Variasi Jarak dan Manusia/' 47 | cases = os.listdir(path) 48 | # case = '4D4.csv' # for testing purpose 49 | for case in cases: 50 | # read data 51 | df = pd.read_csv(path+case) 52 | 53 | # drop time column 54 | df = df.drop(columns=["Time"]) 55 | 56 | # drop NaN values 57 | df = df.dropna() 58 | 59 | # take only data from column 1-2 60 | df = df.iloc[:,0:3] 61 | 62 | # Rename columns to AP1, AP2, and AP3 63 | df.columns = ["AP1", "AP2", "AP3"] 64 | 65 | #~~~~~~~~~~~~~~~~ Distance calculation from rssi using ple data ~~~~~~~~~~~~~~~# 66 | 67 | # Add path loss exponent and rssi at d0 parameters 68 | # Create a lambda function to convert RSSI data into distance 69 | dist = lambda k, n, rssi: 10**((k - rssi) / (10*n)) 70 | 71 | # path loss parameters 72 | rssi_d0 = -49 73 | ple = 2.255 74 | 75 | # Add distance columns in df 76 | for i in range(1,4): 77 | df["dist%d"%i] = dist(rssi_d0, ple, df["AP%d"%i]) 78 | 79 | 80 | # Determining AP coordinate from case 81 | # 1Dx means d = 1, 2Dx means d = 2, cont.. 82 | 83 | if case[0] == "1": 84 | d = 1 85 | elif case[0] == "2": 86 | d = 2 87 | elif case[0] == "3": 88 | d = 3 89 | else: 90 | d = 4 91 | 92 | # Setting APs coordinates 93 | # AP1 94 | x1 = 0 95 | y1 = 0 96 | 97 | # AP2 98 | x2 = 0 99 | y2 = 1 * d 100 | 101 | # AP3 102 | x3 = 1 * d 103 | y3 = 1 * d 104 | 105 | xcoords = [x1, x2, x3] 106 | ycoords = [y1, y2, y3] 107 | 108 | # Target coordinates 109 | case = case.upper() 110 | if case[1:3] == "D1": 111 | xreal = (1/2) * d 112 | yreal = 1*d 113 | elif case[1:3] == "D2": 114 | xreal = (1/4) * d 115 | yreal = (3/4) * d 116 | elif case[1:3] == "D3": 117 | xreal = (1/2) * d 118 | yreal = (1/2) * d 119 | elif case[1:3] == "D4": 120 | xreal = (1/2) * d 121 | yreal = 0 * d 122 | 123 | #~~~~~~~~~~~~~~~~~~~~~ Min and Max coordinates calculations ~~~~~~~~~~~~~~~~~~~~# 124 | # Add xmax, xmin, ymin, and ymax of all APs into df 125 | for i in range(1,4): 126 | df["x%d_max"%i] = xcoords[i-1] + df["dist%d"%i] 127 | df["x%d_min"%i] = xcoords[i-1] - df["dist%d"%i] 128 | df["y%d_max"%i] = ycoords[i-1] + df["dist%d"%i] 129 | df["y%d_min"%i] = ycoords[i-1] - df["dist%d"%i] 130 | 131 | #print(df.head()) 132 | 133 | #~~~~~~~~~~~~~~~~~~~~~ Min and Max coordinates calculations ~~~~~~~~~~~~~~~~~~~~# 134 | 135 | # Concantenate all the max and min coordinates 136 | xmax = np.array(pd.concat([df["x1_max"], df["x2_max"], df["x3_max"]]).sort_values(ascending=True)) 137 | xmin = np.array(pd.concat([df["x1_min"], df["x2_min"], df["x3_min"]]).sort_values(ascending=False)) 138 | ymax = np.array(pd.concat([df["y1_max"], df["y2_max"], df["y3_max"]]).sort_values(ascending=True)) 139 | ymin = np.array(pd.concat([df["y1_min"], df["y2_min"], df["y3_min"]]).sort_values(ascending=False)) 140 | 141 | # assign 100 values for each minmax and maxmin values 142 | # minmax = minimum value from a set of maximum values 143 | # maxmin = maximum value from a set of minimum values 144 | 145 | xminmax = xmax[:100] 146 | xmaxmin = xmin[:100] 147 | yminmax = ymax[:100] 148 | ymaxmin = ymin[:100] 149 | 150 | #~~~~~~~~~~~~~~~~~~~~~~~~~ Min-Max Target calculations ~~~~~~~~~~~~~~~~~~~~~~~# 151 | x_pred = (xminmax + xmaxmin)/2 152 | y_pred = (yminmax + ymaxmin)/2 153 | 154 | #~~~~~~~~~~~~~~~~~~~~~~~~~ Sqrt Error and Mean Sqrt Error ~~~~~~~~~~~~~~~~~~~~# 155 | SQEmm = np.sqrt((x_pred - xreal)**2 + (y_pred - yreal)**2) 156 | MSEmm = np.mean(SQEmm) 157 | 158 | print("Localization using ESP32 %s" %case[0:3]) 159 | print("MSE Min-Max %s:" %case[0:3],round(MSEmm,2),"\n") 160 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~ Plot Graphical Results ~~~~~~~~~~~~~~~~~~~~~~~~~~# 161 | 162 | if case[0] == "1": 163 | limits = [-0.5, 1.5] 164 | ticks = np.arange(-0.5, 1.51, 0.5) 165 | elif case[0] == "2": 166 | limits = [-1, 3.0] 167 | ticks = np.arange(-1, 3.1, 0.5) 168 | elif case[0] == "3": 169 | limits = [-1, 4] 170 | ticks = np.arange(-1, 4.1, 0.5) 171 | else: 172 | limits = [-1, 5] 173 | ticks = np.arange(-1, 5.1, 0.5) 174 | 175 | plt.figure("%s"%case[0:3],figsize = [8,6]) 176 | # plt.title("Min-Max Kasus %s\n Variasi Jarak + Manusia" %case[0:3], fontsize = 15, fontweight = "bold") 177 | plt.title("Min-Max Kasus %s\n Variasi Jarak" %case[0:3], fontsize = 15, fontweight = "bold") 178 | plt.plot(x1, y1, "X", label = "AP1", markersize = 12, c = "red") 179 | plt.plot(x2, y2, "X", label = "AP2", markersize = 12, c = "navy") 180 | plt.plot(x3, y3, "X", label = "AP3", markersize = 12, c = "darkgreen") 181 | plt.scatter(x_pred, y_pred, label = "minmax", c = "blue") 182 | plt.plot(xreal, yreal, "D", label = "real", markersize = 9, c = "brown") 183 | plt.plot(np.mean(x_pred), np.mean(y_pred), "*", label = "$avg_{minmax}$", markersize = 12, c = "black") 184 | plt.ylim(limits) 185 | plt.xlim(limits) 186 | plt.xticks(ticks, fontsize = 12) 187 | plt.yticks(ticks, fontsize = 12) 188 | plt.xlabel("\nx (m)", fontsize = 12) 189 | plt.ylabel("y (m)", fontsize = 12) 190 | plt.legend(loc = "lower right", fontsize = 12) 191 | plt.grid() 192 | # plt.savefig(fname="D:/Skripsweetku/Raw Data/Grafik PNG/minmax/Variasi Jarak/%s"%case[0:3], dpi=100) 193 | # plt.savefig(fname="D:/Skripsweetku/Raw Data/Grafik PNG/minmax/Variasi Jarak dan Manusia/%s"%case[0:3], dpi=100) 194 | plt.show() 195 | plt.close() 196 | 197 | #~~~~~~~~~~~~~~~~~~~~~ Run Program ~~~~~~~~~~~~~~~~~~~~# 198 | if __name__ == '__main__': 199 | main() 200 | -------------------------------------------------------------------------------- /algorithms/trilateration.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : Muhammad Arifin 3 | Date : 7th May, 2020 4 | Subject : Implementation of trilateration calculation 5 | 6 | Licensing : This program is licensed under MIT License. 7 | MIT License 8 | 9 | Copyright (c) [2020] [Muhammad Arifin] 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | """ 29 | 30 | import os 31 | import numpy as np 32 | import pandas as pd 33 | import matplotlib.pyplot as plt 34 | 35 | ######## Function to calculate trilateration parameters ######## 36 | 37 | def trilat_params(xi,yi,xj,yj,ri,rj): 38 | a = -2*xi + 2*xj 39 | b = -2*yi + 2*yj 40 | c = ri**2 - rj**2 - xi**2 + xj**2 - yi**2 + yj**2 41 | 42 | return a,b,c 43 | 44 | 45 | ######## Implementation of trilateration calculation process ######## 46 | # Function of Trilateration Calculation Process 47 | # Data to be processed is stored as csv file. 48 | # Data is processed entirely using pandas dataframe 49 | 50 | def trilateration_process(path, case, ple, rssi_d0): 51 | # Read csv file as panda data frame 52 | df = pd.read_csv(path+case) 53 | 54 | # Drop 'Time' column 55 | df = df.drop(columns=["Time"]) 56 | 57 | # Drop NaN values 58 | df = df.dropna() 59 | 60 | # Take only column 0-3 which is AP1, AP2, and AP3 61 | # This step is important as sometimes the csv data 62 | # has other column aside Time, and RSSI values of 63 | # AP1, AP2, and AP3 64 | 65 | df = df.iloc[:,0:3] 66 | 67 | # Rename columns to AP1, AP2, and AP3 68 | df.columns = ["AP1", "AP2", "AP3"] 69 | 70 | 71 | # Add path loss exponent and rssi at d0 parameters 72 | # Create a lambda function to convert RSSI data into distance 73 | dist = lambda k, n, rssi: 10**((k - rssi) / (10*n)) 74 | 75 | # Add distance columns in df 76 | for i in range(1,4): 77 | df["dist%d"%i] = dist(rssi_d0, ple, df["AP%d"%i]) 78 | 79 | 80 | # Determining what is the distance case 81 | # 1Dx means d = 1, 2Dx means d = 2, cont.. 82 | 83 | if case[0] == "1": 84 | d = 1 85 | elif case[0] == "2": 86 | d = 2 87 | elif case[0] == "3": 88 | d = 3 89 | else: 90 | d = 4 91 | 92 | 93 | # Setting APs coordinates 94 | # AP1 95 | x1 = 0 96 | y1 = 0 97 | 98 | # AP2 99 | x2 = 0 100 | y2 = 1 * d 101 | 102 | # AP3 103 | x3 = 1 * d 104 | y3 = 1 * d 105 | 106 | ap_coordinates = [[x1,y1], [x2, y2], [x3, y3]] 107 | 108 | # Calculation target Coordinates 109 | # check which STA position case is being analysed 110 | # Also check whether the entered case is in capital or not 111 | 112 | case = case.upper() 113 | if case[1:3] == "D1": 114 | xreal = (1/2) * d 115 | yreal = 1*d 116 | elif case[1:3] == "D2": 117 | xreal = (1/4) * d 118 | yreal = (3/4) * d 119 | elif case[1:3] == "D3": 120 | xreal = (1/2) * d 121 | yreal = (1/2) * d 122 | elif case[1:3] == "D4": 123 | xreal = (1/2) * d 124 | yreal = 0 * d 125 | 126 | # Trilateration parameters calculation 127 | # Calculate A, B, C, D, E, and F 128 | A, B, C = trilat_params(x1, y1, x2, y2, df["dist1"], df["dist2"]) 129 | D, E, F = trilat_params(x2, y2, x3, y3, df["dist2"], df["dist3"]) 130 | 131 | # Calculate x and y using trilateration 132 | # x = CE - BF / AE - BD; y = CD - AF / BD - AE 133 | xtr = (C*E - B*F) / (A*E - B*D) 134 | ytr = (C*D - A*F) / (B*D - A*E) 135 | 136 | # Determine the Squared Root Error and the Mean Squared Error 137 | SQEtr = np.sqrt((xtr - xreal)**2 + (ytr - yreal)**2) 138 | MSEtr = np.mean(SQEtr) 139 | 140 | # Add target coordinates, 141 | # predicted coordinates by trilateration 142 | # also the SQRT error in df 143 | 144 | df["xreal"] = xreal 145 | df["xtrilat"] = xtr 146 | 147 | df["yreal"] = yreal 148 | df["ytrilat"] = ytr 149 | 150 | df["SQEtr"] = SQEtr 151 | 152 | # Return the trilateration df and MSE 153 | return (df, MSEtr, ap_coordinates) 154 | 155 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# 156 | #~~~~~~~~~~~~~~~~~~~~~~~~ Main Program ~~~~~~~~~~~~~~~~~~~~~~~~# 157 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# 158 | def main(): 159 | 160 | """ Run the program """ 161 | # Data Paths 162 | # path = 'D:/Skripsweetku/Raw Data/Pengukuran Variasi Jarak Ulangan/' 163 | path = 'D:/Skripsweetku/Raw Data/Pengukuran Variasi Jarak dan Manusia/' 164 | cases = os.listdir(path) 165 | #cases = ['3D1.csv'] 166 | 167 | # RSSI @d0 and path loss exponent (gamma) 168 | gamma = 2.255 169 | K = -49 170 | 171 | # Run all cases in the directory 172 | print(path) 173 | for x in cases: 174 | df, mse, ap_coords = trilateration_process(path, x, gamma, K) 175 | 176 | # Assign APs coordinates 177 | ap1_xcoord = ap_coords[0][0] 178 | ap1_ycoord = ap_coords[0][1] 179 | 180 | ap2_xcoord = ap_coords[1][0] 181 | ap2_ycoord = ap_coords[1][1] 182 | 183 | ap3_xcoord = ap_coords[2][0] 184 | ap3_yxoord = ap_coords[2][1] 185 | 186 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# 187 | """ Print All the Results """ 188 | print("\n") 189 | print("Localization using ESP32 %s \n" %x) 190 | # print(df) 191 | # print(ap_coords) 192 | # x_real = np.mean(df["xreal"]) 193 | # y_real = np.mean(df["yreal"]) 194 | # x_trilat = round(np.mean(df["xtrilat"]),2) 195 | # y_trilat = round(np.mean(df["ytrilat"]),2) 196 | # print("Real x coordinate : ",x_real) 197 | # print("Mean x Trilateration : ",x_trilat) 198 | # print("Real y coordinate : ",y_real) 199 | # print("Mean y Trilateration : ",y_trilat) 200 | print("MSE Trilaterasi : ",round(mse,2)) 201 | # print("Standar Deviasi x-tri: ",round(np.std(df["xtrilat"]),2)) 202 | # print("Standar Deviasi y-tri: ",round(np.std(df["ytrilat"]),2)) 203 | 204 | # Graph limits and ticks based on case 205 | if x[0] == "1": 206 | limits = [-0.5, 4] 207 | ticks = np.arange(-0.5, 4.1, 0.5) 208 | elif x[0] == "2": 209 | limits = [-1, 3.0] 210 | ticks = np.arange(-1, 3.1, 0.5) 211 | elif x[0] == "3": 212 | limits = [-1, 4] 213 | ticks = np.arange(-1, 4.1, 0.5) 214 | else: 215 | limits = [-1, 5] 216 | ticks = np.arange(-1, 5.1, 0.5) 217 | 218 | # Graphing the Results 219 | # Plot the results 220 | plt.figure("%s" %x[0:3], figsize = [8,6]) 221 | # plt.title("Trilaterasi Kasus %s\n Variasi Jarak" %x[0:3], fontsize = 15, fontweight = "bold") 222 | plt.title("Trilaterasi Kasus %s\n Variasi Jarak + Manusia" %x[0:3], fontsize = 15, fontweight = "bold") 223 | plt.plot(ap1_xcoord, ap1_ycoord, "X", label = "AP1", markersize = 12, c = "red") 224 | plt.plot(ap2_xcoord, ap2_ycoord, "X", label = "AP2", markersize = 12, c = "navy") 225 | plt.plot(ap3_xcoord, ap3_yxoord, "X", label = "AP3", markersize = 12, c = "darkgreen") 226 | plt.scatter(df["xtrilat"], df["ytrilat"], label = "tri", c = "blue") 227 | plt.plot(df["xreal"], df["yreal"], "D", label = "real", markersize = 9, c = "brown") 228 | plt.plot(np.mean(df["xtrilat"]), np.mean(df["ytrilat"]), "*", label = "$avg_{tri}$", markersize = 12, c = "black") 229 | 230 | # Setting the limits of graph 231 | # also the ticks 232 | # Save the image in image directory 233 | plt.ylim(limits) 234 | plt.xlim(limits) 235 | plt.xticks(ticks, fontsize = 12) 236 | plt.yticks(ticks, fontsize = 12) 237 | plt.xlabel("\nx (m)", fontsize = 12) 238 | plt.ylabel("y (m)", fontsize = 12) 239 | plt.legend(loc = "lower right", fontsize = 12) 240 | plt.grid() 241 | # plt.savefig(fname="D:/Skripsweetku/Raw Data/Grafik PNG/trilaterasi/Variasi Jarak/%s"%x[0:3], dpi=100) 242 | # plt.savefig(fname="D:/Skripsweetku/Raw Data/Grafik PNG/trilaterasi/Variasi Jarak dan Manusia/%s"%x[0:3], dpi=100) 243 | plt.show() 244 | plt.close() 245 | 246 | if __name__ == '__main__': 247 | main() 248 | --------------------------------------------------------------------------------