{"id":962,"date":"2016-03-06T13:27:15","date_gmt":"2016-03-06T21:27:15","guid":{"rendered":"https:\/\/partofthething.com\/thoughts\/?p=962"},"modified":"2016-03-07T12:56:07","modified_gmt":"2016-03-07T20:56:07","slug":"determine-your-location-from-relative-fm-radio-signal-strengths","status":"publish","type":"post","link":"https:\/\/partofthething.com\/thoughts\/determine-your-location-from-relative-fm-radio-signal-strengths\/","title":{"rendered":"Determine your location from relative FM radio signal strengths"},"content":{"rendered":"<p>In this post, we&#8217;ll take a brief measurement of regular old FM radio stations and try to determine where we are. It&#8217;s like a GPS but with local FM transmitters instead of satellites. I did this just for fun. I wonder if it could be used for indoor location and stuff? It is nowhere near as accurate as GPS. But whatever.<\/p>\n<h2><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibrated_result1-1.png\" rel=\"attachment wp-att-968\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-968 size-full\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibrated_result1-1.png\" alt=\"calibrated_result1\" width=\"796\" height=\"466\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibrated_result1-1.png 796w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibrated_result1-1-300x176.png 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibrated_result1-1-768x450.png 768w\" sizes=\"auto, (max-width: 796px) 100vw, 796px\" \/><\/a>Reading FM radio signal power<\/h2>\n<p>The first step is to get a reading of all the nearby radio stations. I used <a href=\"http:\/\/gnuradio.org\/redmine\/projects\/gnuradio\/wiki\">gnuradio<\/a> and a <a href=\"https:\/\/greatscottgadgets.com\/hackrf\/\">HackRF One<\/a> software defined radio. A simple flow-chart that takes the FFT and dumps it to a file is all I needed. I had to throttle the I\/O or else my computer would freeze. I used 16M sample rate to have as wide a bandwidth as possible.<\/p>\n<figure id=\"attachment_964\" aria-describedby=\"caption-attachment-964\" style=\"width: 858px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/gnuradio_chart.png\" rel=\"attachment wp-att-964\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-964\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/gnuradio_chart.png\" alt=\"The gnuradio flow-chart used to gather data\" width=\"858\" height=\"528\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/gnuradio_chart.png 858w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/gnuradio_chart-300x185.png 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/gnuradio_chart-768x473.png 768w\" sizes=\"auto, (max-width: 858px) 100vw, 858px\" \/><\/a><figcaption id=\"caption-attachment-964\" class=\"wp-caption-text\">The gnuradio flow-chart used to gather data<\/figcaption><\/figure>\n<p>The file sink saves the FFT results in 4-byte integers and just has one 1024-length vector after the other. In a few seconds, I had a 50 MB of data. I did all the post-processing in an external Python script.<\/p>\n<p><!--more--><\/p>\n<h2>Peak-finding in the FFT<\/h2>\n<p>Next I needed to automatically measure the heights and locations of all the stations recorded on the Fourier transform. Sounds like a task for wavelets! This feature is basically built in to scipy, so it ended up being very easy. I used <tt>scipy.signal.find_peaks_cwt <\/tt>with peak widths between 10 and 20 (In the US, commercial FM signals are spaced by 200 kHz, so with a sample rate of 16M and an FFT size of 1024, 0.2\/16*1024 = 12). This worked pretty well, though I did a little guess-and-check comparing while adjusting wavelet widths. I then rounded to the nearest tenth of a MHz. Woo-eee! That 100kW KUOW tower really booms in from on top of Capitol Hill!<\/p>\n<p><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/fft.png\" rel=\"attachment wp-att-966\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-966\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/fft.png\" alt=\"FFT of local radio stations in Seattle\" width=\"800\" height=\"600\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/fft.png 800w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/fft-300x225.png 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/fft-768x576.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/a><\/p>\n<h2>Learning about nearby FM stations<\/h2>\n<p>I needed frequency, power level, callsign, and latitude\/longitude coordinates for all the nearby FM stations. I found a nice <a href=\"https:\/\/www.fcc.gov\/media\/radio\/fm-query\">FM search feature<\/a> at the FCC webpage. Searching by city and filtering to intermediate results, you get something like this:<\/p>\n<figure id=\"attachment_965\" aria-describedby=\"caption-attachment-965\" style=\"width: 660px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/radio_database.png\" rel=\"attachment wp-att-965\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-965\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/radio_database-1024x197.png\" alt=\"Radio database\" width=\"660\" height=\"127\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/radio_database-1024x197.png 1024w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/radio_database-300x58.png 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/radio_database-768x148.png 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/radio_database.png 1544w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/a><figcaption id=\"caption-attachment-965\" class=\"wp-caption-text\">Radio database<\/figcaption><\/figure>\n<p>Excellent! I can parse that in my Python script as well.<\/p>\n<h2>Computing the receiver location<\/h2>\n<p>Figuring out receiver location from various reference signals is called <a href=\"https:\/\/en.wikipedia.org\/wiki\/Multilateration\">multilateration<\/a>, and has been used extensively for years in things like LORAN and GPS. These systems typically have precisely calibrated clocks and use the time-differential at arrival (TDOA) to compute their location. In this project, we are only looking for a rough approximation so we&#8217;ll forget about those decades of precise engineering and just assume measured signal strength is somehow correlated to distance. This assumption could break down for a bunch of reasons like terrain and asymmetric building materials. But whatever, let&#8217;s see if we can get anything meaningful.<\/p>\n<p>Assume for a second that I can map measured signal strength to distance. Then I can just build a minimization problem solve it using existing tools. The problem to solve is exactly as described <a href=\"http:\/\/stackoverflow.com\/a\/9528985\/1279313\">in this post<\/a>, though I can get away with a 2D formulation as long as I stay on the ground.<\/p>\n<p>The scipy.optimize package in Python makes it nearly trivial to use these tools using standard non-linear programming, as you can see in my code.<\/p>\n<h2>Signal strength to distance calibration<\/h2>\n<p>To solve the problem above, I need to map measured signal strength at one frequency to the distance between me and the tower. Attenuation of radio waves in air follows a frequency-dependent <a href=\"https:\/\/en.wikipedia.org\/wiki\/Path_loss#Radio_engineer_formula\">path loss formula. <\/a>So, to calibrate, I just computed the actual distance between my known location and each tower and evaluated the formula for the expected signal power given the full power of each tower. I plotted this result vs. my measured signal power and did a least-squares fit. The fit was pretty good!<\/p>\n<p><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibration.png\" rel=\"attachment wp-att-969\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-969\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibration.png\" alt=\"calibration\" width=\"800\" height=\"600\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibration.png 800w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibration-300x225.png 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/calibration-768x576.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/a>I removed KPWK 93.3 from consideration because I was receiving signals from multiple transmitters it has around and it was messing everything up. Without it, things are lookin&#8217; great! So with this line and the path attenuation formula, I&#8217;m good to go on the final minimization routine.<\/p>\n<h2>Results<\/h2>\n<p>From my base location, it got me within 500 meters, which I was pretty happy about. But I calibrated off of a known location. So what if I drive around town a bit?<\/p>\n<h4>Test 1: Work in Bellevue<\/h4>\n<p>I work in Bellevue, about a mile north of the I-90 and I-405 intersection. Let&#8217;s see how the system does:<\/p>\n<p><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/work_result.png\" rel=\"attachment wp-att-974\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-974\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/work_result.png\" alt=\"work_result\" width=\"939\" height=\"533\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/work_result.png 939w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/work_result-300x170.png 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/2016\/03\/work_result-768x436.png 768w\" sizes=\"auto, (max-width: 939px) 100vw, 939px\" \/><\/a>Woo! Not excellent, but not bad either for being so far from the main cluster of towers. Looks like it&#8217;s working.<\/p>\n<p>More tests coming soon.<\/p>\n<h2>Next steps<\/h2>\n<p>I was wondering if instead of solving the multilateration equations if you could just use machine learning and pattern matching and get similar or even better results. I could imagine a fleet of mobile radios (like, in people&#8217;s cars for example) driving around taking readings and using GPS to train the system. Then once it&#8217;s all trained up, it might be very accurate, and it would work well in tunnels and in the event of a GPS outage. Not sure if this is worth looking into but it could be fun.<\/p>\n<p>The other thing you could do is tap in more formally to the databases of FM stations worldwide and figure out where you are in places other than just Seattle.<\/p>\n<h2>Epilogue<\/h2>\n<p>I had a lot of fun with this project because it really touches on a wide variety of pretty cool things, from radio wave attenuation to wavelet transforms to GIS mapping to nonlinear optimization. Good stuff. What a world!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, we&#8217;ll take a brief measurement of regular old FM radio stations and try to determine where we are. It&#8217;s like a GPS but with local FM transmitters instead of satellites. I did this just for fun. I wonder if it could be used for indoor location and stuff? It is nowhere near &hellip; <a href=\"https:\/\/partofthething.com\/thoughts\/determine-your-location-from-relative-fm-radio-signal-strengths\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Determine your location from relative FM radio signal strengths<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":""},"categories":[3,69,67,8],"tags":[],"class_list":["post-962","post","type-post","status-publish","format-standard","hentry","category-computers","category-electronics-and-physics","category-radio","category-seattle"],"_links":{"self":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/962","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/comments?post=962"}],"version-history":[{"count":7,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/962\/revisions"}],"predecessor-version":[{"id":976,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/962\/revisions\/976"}],"wp:attachment":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/media?parent=962"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/categories?post=962"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/tags?post=962"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}