2 % (C) 2008 Gergely Imreh <imrehg@gmail.com>
8 % Crude Octave3 code to attempt to find GSM tower positions
9 % using log from Cell Locator:
10 % http://repo.or.cz/w/CellLocator.git
11 % http://projects.openmoko.org/projects/rocinante/
13 % Very rough, trying to fit free-space propagation model to
14 % recorded signal. Also visualizing log data.
20 % Parameters to modify
23 logfile = 'log_cells.csv';
24 % Clean up before use! Remove headers, and possible invalid lines
26 % Which GSM cell to check
28 % it is the number in the order of the cells listed in logfile
29 % cellcheck = [1..number-of-different-cellid]
31 % Whether to plot: 0/1
36 disp('Parameters ==============')
37 disp(['Logfile: ',logfile])
38 disp(['Cell to check : ' num2str(cellcheck)])
47 %% Parsing log into separate variables
56 bsid = data(:,10)*1000 + data(:,11);
57 % Received signal level
59 % MCC/MNC numbers for the network
63 %% Getting all distinct CellID numbers
64 bsidlist = unique(bsid);
65 %% Sometimes there's unknown cellid logged as '0' - omit
67 bsidlist = bsidlist(2:end);
69 disp(['Number of cells in log: ' num2str(length(bsidlist))])
70 disp(['MCC,MNC: ' num2str(mcc) ',' num2str(mnc)]);
72 if (cellcheck < 1) or (cellcheck > length(bsidlist))
73 disp('Invalid cell number - no such cell to check')
77 % plot complete logged GPS track
89 %%%%% In loop to allow for later extending to batch procession
90 %%%%% Currently only one at a time: need of visual inspection of data for each cell
91 for k = cellcheck:cellcheck
95 bssc_k = floor(id/1000);
96 bsic_k = id - bssc_k*1000;
97 disp(['MCC,MNC,Combined ID,BSIC,BSSC: ' num2str(mcc) ',' num2str(mnc) ',' num2str(id) ',' num2str(bsic_k) ',' num2str(bssc_k) ])
99 %% Number of logged points
100 nlog = length(bsid(bsid == id));
101 disp(['Logged points: ' num2str(nlog)]);
103 disp('Only one point in data, cannot calculate anything');
107 %% Select logged parameters for just this cell
108 clat = lat(bsid == id);
109 clon = lon(bsid == id);
110 crxl = rxl(bsid == id);
111 cbs = bsic(bsid == id);
112 basestations = unique(cbs);
113 disp(["Base stations: " num2str(length(basestations))])
115 %% Turn RxL (dB) into signal scale
116 csig = 10.^(crxl/10);
118 % Calculate all signal compared to maximum recorded
119 crxlnorm = max(crxl)-crxl;
120 % Distance scaling power: d^(-dsq) : dsq=2 is free space propagation
122 %%%% scale factor only for plotting, that should be handled more cleverly...
125 %% Approximate distance scale:
126 % RxL (dB) -> Signal (relative) -> Distance (with assumed scaling)
127 ds = 1./(10.^(crxlnorm/10)).^(1/dsc);
130 %% Plot data recorded for given cell
132 % - circles around logged positions
133 % - circle radius is inversely proportional to signal strength
134 % ---> tower 'close': small circle; tower 'far': big circle
139 c1 = dist(l)*cos(linspace(0,2*pi))+clon(l);
140 c2 = dist(l)*sin(linspace(0,2*pi))+clat(l);
147 %% For fitting, guess the tower position close to the max recorded signal
148 %% an add random offset
149 [maxsig maxsignum] = max(crxl);
151 fitofflon = offsetm*rand-offsetm/2;
152 fitofflat = offsetm*rand-offsetm/2;
153 b_in = [clon(maxsignum)+fitofflon clat(maxsignum)+fitofflat 0.01];
155 %% use the RxL for fitting...
156 [yhat, b_est] = leasqr([clon clat],crxl,b_in,"gsmsurf",0.00001,3000);
157 disp(['Fitted tower coordinates (Lat/Lon): ' num2str(b_est(2)) ',' num2str(b_est(1))]);
159 % Residual sum of squares (for fit quality purposes: smaller the better!
160 RSSin = sum((gsmsurf([clon clat],b_in)-crxl).^2);
161 RSSest = sum((gsmsurf([clon clat],b_est)-crxl).^2);
162 disp(['RSS: ' num2str(RSSin) ' -> ' num2str(RSSest)])
164 %%% Put fitted tower position on map: big red X
166 plot(b_est(1),b_est(2),'rx','MarkerSize',32);
167 xlabel(['CellID:' num2str(id) '/ RSS: ' num2str(RSSest) ' / Pos: ' num2str([b_est(1) b_est(2)])])
172 %% To assess fit quality:
177 r = sqrt((clon-lon0).^2 + (clat-lat0).^2);
178 % yhat = yhat./log(10)*10;
179 plot(r,crxl,'@',r,yhat,'x')
180 % plot(r,lcsig,'@',r,yhat,'x')
182 xlabel(['Distance from fitted tower position / CellID:' num2str(id) ])
185 disp(['GSMPOS: ' num2str(mcc) ',' num2str(mnc) ',' num2str(id) ',' num2str(b_est(2)) ',' num2str(b_est(1)) ])
186 disp(['OpenLayers: ' num2str(b_est(2)) '|' num2str(b_est(1)) '|cell ' num2str(id) '|mcc ' num2str(mcc) ' mnc ' num2str(mnc) '|icon_blue.png|24,24|0,-24'])
188 end % function gsmfit
192 %%% Additional sub-routines
195 function yhat = gsmsurf(X,b)
196 %% Signal propagation surface according to propagation assumption
198 %% Assumed distance scaling: d^(-dsc): dsc=2 is free-space propagation
200 %%%%% Separating input parameters into variables
201 % proposed cell position
204 % tower output scale factor - "signal at tower" - purely for fitting
208 % Distance from checked centre
209 r = sqrt((X(:,1)-lon0).^2 + (X(:,2)-lat0).^2);
211 yhat = 10*log(s0*r.^(-dsc))/log(10);