├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── convenience ├── convenience.c └── convenience.h ├── getopt ├── getopt.c └── getopt.h ├── rtl_fm.c ├── rtl_fm_python.c ├── rtl_fm_python_common.py ├── rtl_fm_python_thread.py ├── rtl_fm_python_web.py ├── start_web.sh └── static ├── index.html └── rtl_fm_python.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | build 10 | dist 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | *~ 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rtl_fm_python 2 | ============= 3 | 4 | An API and web application to interact with a running instance of RTL_FM 5 | 6 | Screenshot 7 | 8 | # What 9 | 10 | This is a Python library built upon the RTL-SDR project and allows you to use the 11 | RTL-SDR dongle to tune in arbitrary stations either with a simple web application 12 | running on a built-in server, or programmatically with Python or any language using 13 | the REST API provided. 14 | 15 | - http://sdr.osmocom.org/trac/wiki/rtl-sdr 16 | - http://www.reddit.com/r/rtlsdr 17 | 18 | # Why 19 | 20 | I wanted a minimalist remote control for demodulated audio coming from the usb stick 21 | and something that could also provide that functionality on the Raspberry PI, or 22 | allow for control of multiple dongles through web scripting and VPNs. 23 | 24 | # Features 25 | 26 | - Based on the rtl_fm utility from the RTL-SDR Project https://github.com/steve-m/librtlsdr 27 | - Drop in replacement for rtl_fm 28 | - Live web interface based on React http://facebook.github.io/react/ and Flask http://flask.pocoo.org/ 29 | - RESTful API 30 | - Change frequency, demodulation, and gain while running 31 | - Read the RMS signal level 32 | - Interact with rtl_fm with Python 33 | 34 | # License 35 | 36 | GPLv2 37 | 38 | # How to Build 39 | 40 | - Install the RTL-SDR software 41 | - Install Python dependencies for Flask 42 | 43 | sudo pip install flask 44 | 45 | - Compile and link the modified rtm_fm source rtl_fm_python.c 46 | 47 | ./build.sh 48 | 49 | If you have problems let me know. I may not be able to help as I'm not very experienced 50 | with building C applications. 51 | 52 | # Issues 53 | 54 | - Can crash, probably 55 | - Works best if started with WBFM modulation and sample rates if you're going to be switching around demodulation. 56 | - May get out of sync with the features of rtl_fm due to my time and interest. Pull requests accepted! 57 | 58 | # How to Run 59 | 60 | ## Web Interface & API 61 | 62 | Use the script *rtl_fm_python_web.py* as a replacement for *rtl_fm*. If you need to change the 63 | port or the host, that is available at the end of the file. 64 | 65 | Included is a script called *start_web.sh* that shows an example usage. This script tunes to a 66 | broadcast FM station and pipes the audio to Pulse Audio. 67 | 68 | By default the application should be running at http://127.0.0.1:10100/ 69 | 70 | ## Python interactive mode 71 | 72 | Use the script *rtl_fm_python_thread.py* with flags identical to the rtl_fm command. When 73 | you start the application you will be placed into an interactive shell where you can 74 | issue commands. 75 | 76 | # REST API 77 | 78 | ## /state 79 | 80 | Returns the current state of the device, for example: 81 | 82 | { 83 | "autogain": true, 84 | "freq_i": 102500000, 85 | "freq_s": "102.5M", 86 | "gain": -100, 87 | "mod": "w", 88 | "s_level": 14 89 | } 90 | 91 | Modulation modes are denoted as a single letter, w for WBFM, f for FM, a for AM, l for LSB, u for USB, and r for RAW. 92 | 93 | ## /frequency/ *value* 94 | 95 | Tune the device to a specific integer frequency. For example: 96 | 97 | /frequency/101100000 98 | /frequency/144390000 99 | /frequency/162550000 100 | 101 | ## /frequency/human/ *value* 102 | 103 | Tune to a human readable string representation of a frequency. For example: 104 | 105 | /frequency/human/101.1M 106 | /frequency/human/144390.0K 107 | /frequency/human/0.16255G 108 | 109 | ## /demod/ *value* 110 | 111 | Switch the modulation. For example: 112 | 113 | /demod/w 114 | /demod/f 115 | /demod/a 116 | /demod/l 117 | /demod/u 118 | /demod/r 119 | 120 | ## /gain/list 121 | 122 | Returns the real gain values available. For example: 123 | 124 | { 125 | "gains": [ 126 | -10, 127 | 15, 128 | 40, 129 | 65, 130 | 90, 131 | 115, 132 | 140, 133 | 165, 134 | 190, 135 | 215, 136 | 240, 137 | 290, 138 | 340, 139 | 420 140 | ] 141 | } 142 | 143 | 144 | ## /gain/ *value* 145 | 146 | Set a real gain value for the device. For example: 147 | 148 | /gain/-10 149 | /gain/115 150 | /gain/340 151 | 152 | This call turns off automatic gain. 153 | 154 | ## /gain/human/ *value* 155 | 156 | Sets the gain given an arbitrary number scale and tries to find a gain that matches. For example: 157 | 158 | /gain/human/0 159 | /gain/human/40 160 | 161 | This call turns off automatic gain. 162 | 163 | ## /gain/auto 164 | 165 | Sets the device to be in auto gain mode. The gain may read -100 in the above state call. 166 | 167 | # Python Functions 168 | 169 | ## Device functions 170 | 171 | get_s_level() 172 | get_frequency() 173 | set_demod_fm() 174 | set_demod_wbfm() 175 | set_demod_am() 176 | set_demod_lsb() 177 | set_demod_usb() 178 | set_demod_raw() 179 | set_frequency(frequency) 180 | set_squelch(level) #untested 181 | get_demod() 182 | set_demod(modulation) #w,f,a,l,u,r 183 | get_gains() 184 | get_gain() 185 | set_gain(value) 186 | get_auto_gain() 187 | set_gain_human(human_value) 188 | set_freq_human(human_frequency) 189 | get_freq_human() 190 | 191 | ## Utility functions 192 | 193 | str_to_freq(human_frequency) 194 | freq_to_str(frequency) 195 | printstderr(text) 196 | 197 | # Thanks 198 | 199 | The rtl-sdr team and community for being awesome. 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | gcc -I /usr/include/libusb-1.0 -I ./convenience/ -I ./getopt -shared -Wl,-soname,rtl_fm_python -o rtl_fm_python.so -fPIC rtl_fm_python.c convenience/convenience.c getopt/getopt.c -lrtlsdr 2 | -------------------------------------------------------------------------------- /convenience/convenience.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 by Kyle Keen 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /* a collection of user friendly tools 19 | * todo: use strtol for more flexible int parsing 20 | * */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef _WIN32 27 | #include 28 | #else 29 | #include 30 | #include 31 | #include 32 | #define _USE_MATH_DEFINES 33 | #endif 34 | 35 | #include 36 | 37 | #include "rtl-sdr.h" 38 | 39 | double atofs(char *s) 40 | /* standard suffixes */ 41 | { 42 | char last; 43 | int len; 44 | double suff = 1.0; 45 | len = strlen(s); 46 | last = s[len-1]; 47 | s[len-1] = '\0'; 48 | switch (last) { 49 | case 'g': 50 | case 'G': 51 | suff *= 1e3; 52 | case 'm': 53 | case 'M': 54 | suff *= 1e3; 55 | case 'k': 56 | case 'K': 57 | suff *= 1e3; 58 | suff *= atof(s); 59 | s[len-1] = last; 60 | return suff; 61 | } 62 | s[len-1] = last; 63 | return atof(s); 64 | } 65 | 66 | double atoft(char *s) 67 | /* time suffixes, returns seconds */ 68 | { 69 | char last; 70 | int len; 71 | double suff = 1.0; 72 | len = strlen(s); 73 | last = s[len-1]; 74 | s[len-1] = '\0'; 75 | switch (last) { 76 | case 'h': 77 | case 'H': 78 | suff *= 60; 79 | case 'm': 80 | case 'M': 81 | suff *= 60; 82 | case 's': 83 | case 'S': 84 | suff *= atof(s); 85 | s[len-1] = last; 86 | return suff; 87 | } 88 | s[len-1] = last; 89 | return atof(s); 90 | } 91 | 92 | double atofp(char *s) 93 | /* percent suffixes */ 94 | { 95 | char last; 96 | int len; 97 | double suff = 1.0; 98 | len = strlen(s); 99 | last = s[len-1]; 100 | s[len-1] = '\0'; 101 | switch (last) { 102 | case '%': 103 | suff *= 0.01; 104 | suff *= atof(s); 105 | s[len-1] = last; 106 | return suff; 107 | } 108 | s[len-1] = last; 109 | return atof(s); 110 | } 111 | 112 | int nearest_gain(rtlsdr_dev_t *dev, int target_gain) 113 | { 114 | int i, r, err1, err2, count, nearest; 115 | int* gains; 116 | r = rtlsdr_set_tuner_gain_mode(dev, 1); 117 | if (r < 0) { 118 | fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); 119 | return r; 120 | } 121 | count = rtlsdr_get_tuner_gains(dev, NULL); 122 | if (count <= 0) { 123 | return 0; 124 | } 125 | gains = malloc(sizeof(int) * count); 126 | count = rtlsdr_get_tuner_gains(dev, gains); 127 | nearest = gains[0]; 128 | for (i=0; i= 0 && device < device_count) { 263 | fprintf(stderr, "Using device %d: %s\n", 264 | device, rtlsdr_get_device_name((uint32_t)device)); 265 | return device; 266 | } 267 | /* does string exact match a serial */ 268 | for (i = 0; i < device_count; i++) { 269 | rtlsdr_get_device_usb_strings(i, vendor, product, serial); 270 | if (strcmp(s, serial) != 0) { 271 | continue;} 272 | device = i; 273 | fprintf(stderr, "Using device %d: %s\n", 274 | device, rtlsdr_get_device_name((uint32_t)device)); 275 | return device; 276 | } 277 | /* does string prefix match a serial */ 278 | for (i = 0; i < device_count; i++) { 279 | rtlsdr_get_device_usb_strings(i, vendor, product, serial); 280 | if (strncmp(s, serial, strlen(s)) != 0) { 281 | continue;} 282 | device = i; 283 | fprintf(stderr, "Using device %d: %s\n", 284 | device, rtlsdr_get_device_name((uint32_t)device)); 285 | return device; 286 | } 287 | /* does string suffix match a serial */ 288 | for (i = 0; i < device_count; i++) { 289 | rtlsdr_get_device_usb_strings(i, vendor, product, serial); 290 | offset = strlen(serial) - strlen(s); 291 | if (offset < 0) { 292 | continue;} 293 | if (strncmp(s, serial+offset, strlen(s)) != 0) { 294 | continue;} 295 | device = i; 296 | fprintf(stderr, "Using device %d: %s\n", 297 | device, rtlsdr_get_device_name((uint32_t)device)); 298 | return device; 299 | } 300 | fprintf(stderr, "No matching devices found.\n"); 301 | return -1; 302 | } 303 | 304 | // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab 305 | -------------------------------------------------------------------------------- /convenience/convenience.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 by Kyle Keen 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /* a collection of user friendly tools */ 19 | 20 | /*! 21 | * Convert standard suffixes (k, M, G) to double 22 | * 23 | * \param s a string to be parsed 24 | * \return double 25 | */ 26 | 27 | double atofs(char *s); 28 | 29 | /*! 30 | * Convert time suffixes (s, m, h) to double 31 | * 32 | * \param s a string to be parsed 33 | * \return seconds as double 34 | */ 35 | 36 | double atoft(char *s); 37 | 38 | /*! 39 | * Convert percent suffixe (%) to double 40 | * 41 | * \param s a string to be parsed 42 | * \return double 43 | */ 44 | 45 | double atofp(char *s); 46 | 47 | /*! 48 | * Find nearest supported gain 49 | * 50 | * \param dev the device handle given by rtlsdr_open() 51 | * \param target_gain in tenths of a dB 52 | * \return 0 on success 53 | */ 54 | 55 | int nearest_gain(rtlsdr_dev_t *dev, int target_gain); 56 | 57 | /*! 58 | * Set device frequency and report status on stderr 59 | * 60 | * \param dev the device handle given by rtlsdr_open() 61 | * \param frequency in Hz 62 | * \return 0 on success 63 | */ 64 | 65 | int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency); 66 | 67 | /*! 68 | * Set device sample rate and report status on stderr 69 | * 70 | * \param dev the device handle given by rtlsdr_open() 71 | * \param samp_rate in samples/second 72 | * \return 0 on success 73 | */ 74 | 75 | int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate); 76 | 77 | /*! 78 | * Enable or disable the direct sampling mode and report status on stderr 79 | * 80 | * \param dev the device handle given by rtlsdr_open() 81 | * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled 82 | * \return 0 on success 83 | */ 84 | 85 | int verbose_direct_sampling(rtlsdr_dev_t *dev, int on); 86 | 87 | /*! 88 | * Enable offset tuning and report status on stderr 89 | * 90 | * \param dev the device handle given by rtlsdr_open() 91 | * \return 0 on success 92 | */ 93 | 94 | int verbose_offset_tuning(rtlsdr_dev_t *dev); 95 | 96 | /*! 97 | * Enable auto gain and report status on stderr 98 | * 99 | * \param dev the device handle given by rtlsdr_open() 100 | * \return 0 on success 101 | */ 102 | 103 | int verbose_auto_gain(rtlsdr_dev_t *dev); 104 | 105 | /*! 106 | * Set tuner gain and report status on stderr 107 | * 108 | * \param dev the device handle given by rtlsdr_open() 109 | * \param gain in tenths of a dB 110 | * \return 0 on success 111 | */ 112 | 113 | int verbose_gain_set(rtlsdr_dev_t *dev, int gain); 114 | 115 | /*! 116 | * Set the frequency correction value for the device and report status on stderr. 117 | * 118 | * \param dev the device handle given by rtlsdr_open() 119 | * \param ppm_error correction value in parts per million (ppm) 120 | * \return 0 on success 121 | */ 122 | 123 | int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error); 124 | 125 | /*! 126 | * Reset buffer 127 | * 128 | * \param dev the device handle given by rtlsdr_open() 129 | * \return 0 on success 130 | */ 131 | 132 | int verbose_reset_buffer(rtlsdr_dev_t *dev); 133 | 134 | /*! 135 | * Find the closest matching device. 136 | * 137 | * \param s a string to be parsed 138 | * \return dev_index int, -1 on error 139 | */ 140 | 141 | int verbose_device_search(char *s); 142 | 143 | -------------------------------------------------------------------------------- /getopt/getopt.c: -------------------------------------------------------------------------------- 1 | /* Getopt for GNU. 2 | NOTE: getopt is now part of the C library, so if you don't know what 3 | "Keep this file name-space clean" means, talk to drepper@gnu.org 4 | before changing it! 5 | Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 6 | Free Software Foundation, Inc. 7 | This file is part of the GNU C Library. 8 | 9 | The GNU C Library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | The GNU C Library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with the GNU C Library; if not, write to the Free 21 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 22 | 02111-1307 USA. */ 23 | /* This tells Alpha OSF/1 not to define a getopt prototype in . 24 | Ditto for AIX 3.2 and . */ 25 | #ifndef _NO_PROTO 26 | # define _NO_PROTO 27 | #endif 28 | 29 | #ifdef HAVE_CONFIG_H 30 | # include 31 | #endif 32 | 33 | #if !defined __STDC__ || !__STDC__ 34 | /* This is a separate conditional since some stdc systems 35 | reject `defined (const)'. */ 36 | # ifndef const 37 | # define const 38 | # endif 39 | #endif 40 | 41 | #include 42 | 43 | /* Comment out all this code if we are using the GNU C Library, and are not 44 | actually compiling the library itself. This code is part of the GNU C 45 | Library, but also included in many other GNU distributions. Compiling 46 | and linking in this code is a waste when using the GNU C library 47 | (especially if it is a shared library). Rather than having every GNU 48 | program understand `configure --with-gnu-libc' and omit the object files, 49 | it is simpler to just do this in the source for each such file. */ 50 | 51 | #define GETOPT_INTERFACE_VERSION 2 52 | #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 53 | # include 54 | # if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION 55 | # define ELIDE_CODE 56 | # endif 57 | #endif 58 | 59 | #ifndef ELIDE_CODE 60 | 61 | 62 | /* This needs to come after some library #include 63 | to get __GNU_LIBRARY__ defined. */ 64 | #ifdef __GNU_LIBRARY__ 65 | /* Don't include stdlib.h for non-GNU C libraries because some of them 66 | contain conflicting prototypes for getopt. */ 67 | # include 68 | # include 69 | #endif /* GNU C library. */ 70 | 71 | #ifdef VMS 72 | # include 73 | # if HAVE_STRING_H - 0 74 | # include 75 | # endif 76 | #endif 77 | 78 | #ifndef _ 79 | /* This is for other GNU distributions with internationalized messages. */ 80 | # if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC 81 | # include 82 | # ifndef _ 83 | # define _(msgid) gettext (msgid) 84 | # endif 85 | # else 86 | # define _(msgid) (msgid) 87 | # endif 88 | #endif 89 | 90 | /* This version of `getopt' appears to the caller like standard Unix `getopt' 91 | but it behaves differently for the user, since it allows the user 92 | to intersperse the options with the other arguments. 93 | 94 | As `getopt' works, it permutes the elements of ARGV so that, 95 | when it is done, all the options precede everything else. Thus 96 | all application programs are extended to handle flexible argument order. 97 | 98 | Setting the environment variable POSIXLY_CORRECT disables permutation. 99 | Then the behavior is completely standard. 100 | 101 | GNU application programs can use a third alternative mode in which 102 | they can distinguish the relative order of options and other arguments. */ 103 | 104 | #include "getopt.h" 105 | 106 | /* For communication from `getopt' to the caller. 107 | When `getopt' finds an option that takes an argument, 108 | the argument value is returned here. 109 | Also, when `ordering' is RETURN_IN_ORDER, 110 | each non-option ARGV-element is returned here. */ 111 | 112 | char *optarg; 113 | 114 | /* Index in ARGV of the next element to be scanned. 115 | This is used for communication to and from the caller 116 | and for communication between successive calls to `getopt'. 117 | 118 | On entry to `getopt', zero means this is the first call; initialize. 119 | 120 | When `getopt' returns -1, this is the index of the first of the 121 | non-option elements that the caller should itself scan. 122 | 123 | Otherwise, `optind' communicates from one call to the next 124 | how much of ARGV has been scanned so far. */ 125 | 126 | /* 1003.2 says this must be 1 before any call. */ 127 | int optind = 1; 128 | 129 | /* Formerly, initialization of getopt depended on optind==0, which 130 | causes problems with re-calling getopt as programs generally don't 131 | know that. */ 132 | 133 | int __getopt_initialized; 134 | 135 | /* The next char to be scanned in the option-element 136 | in which the last option character we returned was found. 137 | This allows us to pick up the scan where we left off. 138 | 139 | If this is zero, or a null string, it means resume the scan 140 | by advancing to the next ARGV-element. */ 141 | 142 | static char *nextchar; 143 | 144 | /* Callers store zero here to inhibit the error message 145 | for unrecognized options. */ 146 | 147 | int opterr = 1; 148 | 149 | /* Set to an option character which was unrecognized. 150 | This must be initialized on some systems to avoid linking in the 151 | system's own getopt implementation. */ 152 | 153 | int optopt = '?'; 154 | 155 | /* Describe how to deal with options that follow non-option ARGV-elements. 156 | 157 | If the caller did not specify anything, 158 | the default is REQUIRE_ORDER if the environment variable 159 | POSIXLY_CORRECT is defined, PERMUTE otherwise. 160 | 161 | REQUIRE_ORDER means don't recognize them as options; 162 | stop option processing when the first non-option is seen. 163 | This is what Unix does. 164 | This mode of operation is selected by either setting the environment 165 | variable POSIXLY_CORRECT, or using `+' as the first character 166 | of the list of option characters. 167 | 168 | PERMUTE is the default. We permute the contents of ARGV as we scan, 169 | so that eventually all the non-options are at the end. This allows options 170 | to be given in any order, even with programs that were not written to 171 | expect this. 172 | 173 | RETURN_IN_ORDER is an option available to programs that were written 174 | to expect options and other ARGV-elements in any order and that care about 175 | the ordering of the two. We describe each non-option ARGV-element 176 | as if it were the argument of an option with character code 1. 177 | Using `-' as the first character of the list of option characters 178 | selects this mode of operation. 179 | 180 | The special argument `--' forces an end of option-scanning regardless 181 | of the value of `ordering'. In the case of RETURN_IN_ORDER, only 182 | `--' can cause `getopt' to return -1 with `optind' != ARGC. */ 183 | 184 | static enum 185 | { 186 | REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER 187 | } ordering; 188 | 189 | /* Value of POSIXLY_CORRECT environment variable. */ 190 | static char *posixly_correct; 191 | 192 | #ifdef __GNU_LIBRARY__ 193 | /* We want to avoid inclusion of string.h with non-GNU libraries 194 | because there are many ways it can cause trouble. 195 | On some systems, it contains special magic macros that don't work 196 | in GCC. */ 197 | # include 198 | # define my_index strchr 199 | #else 200 | 201 | # if 1 //HAVE_STRING_H 202 | # include 203 | # else 204 | # include 205 | # endif 206 | 207 | /* Avoid depending on library functions or files 208 | whose names are inconsistent. */ 209 | 210 | #ifndef getenv 211 | #ifdef _MSC_VER 212 | // DDK will complain if you don't use the stdlib defined getenv 213 | #include 214 | #else 215 | extern char *getenv (); 216 | #endif 217 | #endif 218 | 219 | static char * 220 | my_index (str, chr) 221 | const char *str; 222 | int chr; 223 | { 224 | while (*str) 225 | { 226 | if (*str == chr) 227 | return (char *) str; 228 | str++; 229 | } 230 | return 0; 231 | } 232 | 233 | /* If using GCC, we can safely declare strlen this way. 234 | If not using GCC, it is ok not to declare it. */ 235 | #ifdef __GNUC__ 236 | /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. 237 | That was relevant to code that was here before. */ 238 | # if (!defined __STDC__ || !__STDC__) && !defined strlen 239 | /* gcc with -traditional declares the built-in strlen to return int, 240 | and has done so at least since version 2.4.5. -- rms. */ 241 | extern int strlen (const char *); 242 | # endif /* not __STDC__ */ 243 | #endif /* __GNUC__ */ 244 | 245 | #endif /* not __GNU_LIBRARY__ */ 246 | 247 | /* Handle permutation of arguments. */ 248 | 249 | /* Describe the part of ARGV that contains non-options that have 250 | been skipped. `first_nonopt' is the index in ARGV of the first of them; 251 | `last_nonopt' is the index after the last of them. */ 252 | 253 | static int first_nonopt; 254 | static int last_nonopt; 255 | 256 | #ifdef _LIBC 257 | /* Stored original parameters. 258 | XXX This is no good solution. We should rather copy the args so 259 | that we can compare them later. But we must not use malloc(3). */ 260 | extern int __libc_argc; 261 | extern char **__libc_argv; 262 | 263 | /* Bash 2.0 gives us an environment variable containing flags 264 | indicating ARGV elements that should not be considered arguments. */ 265 | 266 | # ifdef USE_NONOPTION_FLAGS 267 | /* Defined in getopt_init.c */ 268 | extern char *__getopt_nonoption_flags; 269 | 270 | static int nonoption_flags_max_len; 271 | static int nonoption_flags_len; 272 | # endif 273 | 274 | # ifdef USE_NONOPTION_FLAGS 275 | # define SWAP_FLAGS(ch1, ch2) \ 276 | if (nonoption_flags_len > 0) \ 277 | { \ 278 | char __tmp = __getopt_nonoption_flags[ch1]; \ 279 | __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ 280 | __getopt_nonoption_flags[ch2] = __tmp; \ 281 | } 282 | # else 283 | # define SWAP_FLAGS(ch1, ch2) 284 | # endif 285 | #else /* !_LIBC */ 286 | # define SWAP_FLAGS(ch1, ch2) 287 | #endif /* _LIBC */ 288 | 289 | /* Exchange two adjacent subsequences of ARGV. 290 | One subsequence is elements [first_nonopt,last_nonopt) 291 | which contains all the non-options that have been skipped so far. 292 | The other is elements [last_nonopt,optind), which contains all 293 | the options processed since those non-options were skipped. 294 | 295 | `first_nonopt' and `last_nonopt' are relocated so that they describe 296 | the new indices of the non-options in ARGV after they are moved. */ 297 | 298 | #if defined __STDC__ && __STDC__ 299 | static void exchange (char **); 300 | #endif 301 | 302 | static void 303 | exchange (argv) 304 | char **argv; 305 | { 306 | int bottom = first_nonopt; 307 | int middle = last_nonopt; 308 | int top = optind; 309 | char *tem; 310 | 311 | /* Exchange the shorter segment with the far end of the longer segment. 312 | That puts the shorter segment into the right place. 313 | It leaves the longer segment in the right place overall, 314 | but it consists of two parts that need to be swapped next. */ 315 | 316 | #if defined _LIBC && defined USE_NONOPTION_FLAGS 317 | /* First make sure the handling of the `__getopt_nonoption_flags' 318 | string can work normally. Our top argument must be in the range 319 | of the string. */ 320 | if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) 321 | { 322 | /* We must extend the array. The user plays games with us and 323 | presents new arguments. */ 324 | char *new_str = malloc (top + 1); 325 | if (new_str == NULL) 326 | nonoption_flags_len = nonoption_flags_max_len = 0; 327 | else 328 | { 329 | memset (__mempcpy (new_str, __getopt_nonoption_flags, 330 | nonoption_flags_max_len), 331 | '\0', top + 1 - nonoption_flags_max_len); 332 | nonoption_flags_max_len = top + 1; 333 | __getopt_nonoption_flags = new_str; 334 | } 335 | } 336 | #endif 337 | 338 | while (top > middle && middle > bottom) 339 | { 340 | if (top - middle > middle - bottom) 341 | { 342 | /* Bottom segment is the short one. */ 343 | int len = middle - bottom; 344 | register int i; 345 | 346 | /* Swap it with the top part of the top segment. */ 347 | for (i = 0; i < len; i++) 348 | { 349 | tem = argv[bottom + i]; 350 | argv[bottom + i] = argv[top - (middle - bottom) + i]; 351 | argv[top - (middle - bottom) + i] = tem; 352 | SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); 353 | } 354 | /* Exclude the moved bottom segment from further swapping. */ 355 | top -= len; 356 | } 357 | else 358 | { 359 | /* Top segment is the short one. */ 360 | int len = top - middle; 361 | register int i; 362 | 363 | /* Swap it with the bottom part of the bottom segment. */ 364 | for (i = 0; i < len; i++) 365 | { 366 | tem = argv[bottom + i]; 367 | argv[bottom + i] = argv[middle + i]; 368 | argv[middle + i] = tem; 369 | SWAP_FLAGS (bottom + i, middle + i); 370 | } 371 | /* Exclude the moved top segment from further swapping. */ 372 | bottom += len; 373 | } 374 | } 375 | 376 | /* Update records for the slots the non-options now occupy. */ 377 | 378 | first_nonopt += (optind - last_nonopt); 379 | last_nonopt = optind; 380 | } 381 | 382 | /* Initialize the internal data when the first call is made. */ 383 | 384 | #if defined __STDC__ && __STDC__ 385 | static const char *_getopt_initialize (int, char *const *, const char *); 386 | #endif 387 | static const char * 388 | _getopt_initialize (argc, argv, optstring) 389 | int argc; 390 | char *const *argv; 391 | const char *optstring; 392 | { 393 | /* Start processing options with ARGV-element 1 (since ARGV-element 0 394 | is the program name); the sequence of previously skipped 395 | non-option ARGV-elements is empty. */ 396 | 397 | first_nonopt = last_nonopt = optind; 398 | 399 | nextchar = NULL; 400 | 401 | posixly_correct = getenv ("POSIXLY_CORRECT"); 402 | 403 | /* Determine how to handle the ordering of options and nonoptions. */ 404 | 405 | if (optstring[0] == '-') 406 | { 407 | ordering = RETURN_IN_ORDER; 408 | ++optstring; 409 | } 410 | else if (optstring[0] == '+') 411 | { 412 | ordering = REQUIRE_ORDER; 413 | ++optstring; 414 | } 415 | else if (posixly_correct != NULL) 416 | ordering = REQUIRE_ORDER; 417 | else 418 | ordering = PERMUTE; 419 | 420 | #if defined _LIBC && defined USE_NONOPTION_FLAGS 421 | if (posixly_correct == NULL 422 | && argc == __libc_argc && argv == __libc_argv) 423 | { 424 | if (nonoption_flags_max_len == 0) 425 | { 426 | if (__getopt_nonoption_flags == NULL 427 | || __getopt_nonoption_flags[0] == '\0') 428 | nonoption_flags_max_len = -1; 429 | else 430 | { 431 | const char *orig_str = __getopt_nonoption_flags; 432 | int len = nonoption_flags_max_len = strlen (orig_str); 433 | if (nonoption_flags_max_len < argc) 434 | nonoption_flags_max_len = argc; 435 | __getopt_nonoption_flags = 436 | (char *) malloc (nonoption_flags_max_len); 437 | if (__getopt_nonoption_flags == NULL) 438 | nonoption_flags_max_len = -1; 439 | else 440 | memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), 441 | '\0', nonoption_flags_max_len - len); 442 | } 443 | } 444 | nonoption_flags_len = nonoption_flags_max_len; 445 | } 446 | else 447 | nonoption_flags_len = 0; 448 | #endif 449 | 450 | return optstring; 451 | } 452 | 453 | /* Scan elements of ARGV (whose length is ARGC) for option characters 454 | given in OPTSTRING. 455 | 456 | If an element of ARGV starts with '-', and is not exactly "-" or "--", 457 | then it is an option element. The characters of this element 458 | (aside from the initial '-') are option characters. If `getopt' 459 | is called repeatedly, it returns successively each of the option characters 460 | from each of the option elements. 461 | 462 | If `getopt' finds another option character, it returns that character, 463 | updating `optind' and `nextchar' so that the next call to `getopt' can 464 | resume the scan with the following option character or ARGV-element. 465 | 466 | If there are no more option characters, `getopt' returns -1. 467 | Then `optind' is the index in ARGV of the first ARGV-element 468 | that is not an option. (The ARGV-elements have been permuted 469 | so that those that are not options now come last.) 470 | 471 | OPTSTRING is a string containing the legitimate option characters. 472 | If an option character is seen that is not listed in OPTSTRING, 473 | return '?' after printing an error message. If you set `opterr' to 474 | zero, the error message is suppressed but we still return '?'. 475 | 476 | If a char in OPTSTRING is followed by a colon, that means it wants an arg, 477 | so the following text in the same ARGV-element, or the text of the following 478 | ARGV-element, is returned in `optarg'. Two colons mean an option that 479 | wants an optional arg; if there is text in the current ARGV-element, 480 | it is returned in `optarg', otherwise `optarg' is set to zero. 481 | 482 | If OPTSTRING starts with `-' or `+', it requests different methods of 483 | handling the non-option ARGV-elements. 484 | See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. 485 | 486 | Long-named options begin with `--' instead of `-'. 487 | Their names may be abbreviated as long as the abbreviation is unique 488 | or is an exact match for some defined option. If they have an 489 | argument, it follows the option name in the same ARGV-element, separated 490 | from the option name by a `=', or else the in next ARGV-element. 491 | When `getopt' finds a long-named option, it returns 0 if that option's 492 | `flag' field is nonzero, the value of the option's `val' field 493 | if the `flag' field is zero. 494 | 495 | The elements of ARGV aren't really const, because we permute them. 496 | But we pretend they're const in the prototype to be compatible 497 | with other systems. 498 | 499 | LONGOPTS is a vector of `struct option' terminated by an 500 | element containing a name which is zero. 501 | 502 | LONGIND returns the index in LONGOPT of the long-named option found. 503 | It is only valid when a long-named option has been found by the most 504 | recent call. 505 | 506 | If LONG_ONLY is nonzero, '-' as well as '--' can introduce 507 | long-named options. */ 508 | 509 | int 510 | _getopt_internal (argc, argv, optstring, longopts, longind, long_only) 511 | int argc; 512 | char *const *argv; 513 | const char *optstring; 514 | const struct option *longopts; 515 | int *longind; 516 | int long_only; 517 | { 518 | int print_errors = opterr; 519 | if (optstring[0] == ':') 520 | print_errors = 0; 521 | 522 | if (argc < 1) 523 | return -1; 524 | 525 | optarg = NULL; 526 | 527 | if (optind == 0 || !__getopt_initialized) 528 | { 529 | if (optind == 0) 530 | optind = 1; /* Don't scan ARGV[0], the program name. */ 531 | optstring = _getopt_initialize (argc, argv, optstring); 532 | __getopt_initialized = 1; 533 | } 534 | 535 | /* Test whether ARGV[optind] points to a non-option argument. 536 | Either it does not have option syntax, or there is an environment flag 537 | from the shell indicating it is not an option. The later information 538 | is only used when the used in the GNU libc. */ 539 | #if defined _LIBC && defined USE_NONOPTION_FLAGS 540 | # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ 541 | || (optind < nonoption_flags_len \ 542 | && __getopt_nonoption_flags[optind] == '1')) 543 | #else 544 | # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') 545 | #endif 546 | 547 | if (nextchar == NULL || *nextchar == '\0') 548 | { 549 | /* Advance to the next ARGV-element. */ 550 | 551 | /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been 552 | moved back by the user (who may also have changed the arguments). */ 553 | if (last_nonopt > optind) 554 | last_nonopt = optind; 555 | if (first_nonopt > optind) 556 | first_nonopt = optind; 557 | 558 | if (ordering == PERMUTE) 559 | { 560 | /* If we have just processed some options following some non-options, 561 | exchange them so that the options come first. */ 562 | 563 | if (first_nonopt != last_nonopt && last_nonopt != optind) 564 | exchange ((char **) argv); 565 | else if (last_nonopt != optind) 566 | first_nonopt = optind; 567 | 568 | /* Skip any additional non-options 569 | and extend the range of non-options previously skipped. */ 570 | 571 | while (optind < argc && NONOPTION_P) 572 | optind++; 573 | last_nonopt = optind; 574 | } 575 | 576 | /* The special ARGV-element `--' means premature end of options. 577 | Skip it like a null option, 578 | then exchange with previous non-options as if it were an option, 579 | then skip everything else like a non-option. */ 580 | 581 | if (optind != argc && !strcmp (argv[optind], "--")) 582 | { 583 | optind++; 584 | 585 | if (first_nonopt != last_nonopt && last_nonopt != optind) 586 | exchange ((char **) argv); 587 | else if (first_nonopt == last_nonopt) 588 | first_nonopt = optind; 589 | last_nonopt = argc; 590 | 591 | optind = argc; 592 | } 593 | 594 | /* If we have done all the ARGV-elements, stop the scan 595 | and back over any non-options that we skipped and permuted. */ 596 | 597 | if (optind == argc) 598 | { 599 | /* Set the next-arg-index to point at the non-options 600 | that we previously skipped, so the caller will digest them. */ 601 | if (first_nonopt != last_nonopt) 602 | optind = first_nonopt; 603 | return -1; 604 | } 605 | 606 | /* If we have come to a non-option and did not permute it, 607 | either stop the scan or describe it to the caller and pass it by. */ 608 | 609 | if (NONOPTION_P) 610 | { 611 | if (ordering == REQUIRE_ORDER) 612 | return -1; 613 | optarg = argv[optind++]; 614 | return 1; 615 | } 616 | 617 | /* We have found another option-ARGV-element. 618 | Skip the initial punctuation. */ 619 | 620 | nextchar = (argv[optind] + 1 621 | + (longopts != NULL && argv[optind][1] == '-')); 622 | } 623 | 624 | /* Decode the current option-ARGV-element. */ 625 | 626 | /* Check whether the ARGV-element is a long option. 627 | 628 | If long_only and the ARGV-element has the form "-f", where f is 629 | a valid short option, don't consider it an abbreviated form of 630 | a long option that starts with f. Otherwise there would be no 631 | way to give the -f short option. 632 | 633 | On the other hand, if there's a long option "fubar" and 634 | the ARGV-element is "-fu", do consider that an abbreviation of 635 | the long option, just like "--fu", and not "-f" with arg "u". 636 | 637 | This distinction seems to be the most useful approach. */ 638 | 639 | if (longopts != NULL 640 | && (argv[optind][1] == '-' 641 | || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) 642 | { 643 | char *nameend; 644 | const struct option *p; 645 | const struct option *pfound = NULL; 646 | int exact = 0; 647 | int ambig = 0; 648 | int indfound = -1; 649 | int option_index; 650 | 651 | for (nameend = nextchar; *nameend && *nameend != '='; nameend++) 652 | /* Do nothing. */ ; 653 | 654 | /* Test all long options for either exact match 655 | or abbreviated matches. */ 656 | for (p = longopts, option_index = 0; p->name; p++, option_index++) 657 | if (!strncmp (p->name, nextchar, nameend - nextchar)) 658 | { 659 | if ((unsigned int) (nameend - nextchar) 660 | == (unsigned int) strlen (p->name)) 661 | { 662 | /* Exact match found. */ 663 | pfound = p; 664 | indfound = option_index; 665 | exact = 1; 666 | break; 667 | } 668 | else if (pfound == NULL) 669 | { 670 | /* First nonexact match found. */ 671 | pfound = p; 672 | indfound = option_index; 673 | } 674 | else if (long_only 675 | || pfound->has_arg != p->has_arg 676 | || pfound->flag != p->flag 677 | || pfound->val != p->val) 678 | /* Second or later nonexact match found. */ 679 | ambig = 1; 680 | } 681 | 682 | if (ambig && !exact) 683 | { 684 | if (print_errors) 685 | fprintf (stderr, _("%s: option `%s' is ambiguous\n"), 686 | argv[0], argv[optind]); 687 | nextchar += strlen (nextchar); 688 | optind++; 689 | optopt = 0; 690 | return '?'; 691 | } 692 | 693 | if (pfound != NULL) 694 | { 695 | option_index = indfound; 696 | optind++; 697 | if (*nameend) 698 | { 699 | /* Don't test has_arg with >, because some C compilers don't 700 | allow it to be used on enums. */ 701 | if (pfound->has_arg) 702 | optarg = nameend + 1; 703 | else 704 | { 705 | if (print_errors) 706 | { 707 | if (argv[optind - 1][1] == '-') 708 | /* --option */ 709 | fprintf (stderr, 710 | _("%s: option `--%s' doesn't allow an argument\n"), 711 | argv[0], pfound->name); 712 | else 713 | /* +option or -option */ 714 | fprintf (stderr, 715 | _("%s: option `%c%s' doesn't allow an argument\n"), 716 | argv[0], argv[optind - 1][0], pfound->name); 717 | } 718 | 719 | nextchar += strlen (nextchar); 720 | 721 | optopt = pfound->val; 722 | return '?'; 723 | } 724 | } 725 | else if (pfound->has_arg == 1) 726 | { 727 | if (optind < argc) 728 | optarg = argv[optind++]; 729 | else 730 | { 731 | if (print_errors) 732 | fprintf (stderr, 733 | _("%s: option `%s' requires an argument\n"), 734 | argv[0], argv[optind - 1]); 735 | nextchar += strlen (nextchar); 736 | optopt = pfound->val; 737 | return optstring[0] == ':' ? ':' : '?'; 738 | } 739 | } 740 | nextchar += strlen (nextchar); 741 | if (longind != NULL) 742 | *longind = option_index; 743 | if (pfound->flag) 744 | { 745 | *(pfound->flag) = pfound->val; 746 | return 0; 747 | } 748 | return pfound->val; 749 | } 750 | 751 | /* Can't find it as a long option. If this is not getopt_long_only, 752 | or the option starts with '--' or is not a valid short 753 | option, then it's an error. 754 | Otherwise interpret it as a short option. */ 755 | if (!long_only || argv[optind][1] == '-' 756 | || my_index (optstring, *nextchar) == NULL) 757 | { 758 | if (print_errors) 759 | { 760 | if (argv[optind][1] == '-') 761 | /* --option */ 762 | fprintf (stderr, _("%s: unrecognized option `--%s'\n"), 763 | argv[0], nextchar); 764 | else 765 | /* +option or -option */ 766 | fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), 767 | argv[0], argv[optind][0], nextchar); 768 | } 769 | nextchar = (char *) ""; 770 | optind++; 771 | optopt = 0; 772 | return '?'; 773 | } 774 | } 775 | 776 | /* Look at and handle the next short option-character. */ 777 | 778 | { 779 | char c = *nextchar++; 780 | char *temp = my_index (optstring, c); 781 | 782 | /* Increment `optind' when we start to process its last character. */ 783 | if (*nextchar == '\0') 784 | ++optind; 785 | 786 | if (temp == NULL || c == ':') 787 | { 788 | if (print_errors) 789 | { 790 | if (posixly_correct) 791 | /* 1003.2 specifies the format of this message. */ 792 | fprintf (stderr, _("%s: illegal option -- %c\n"), 793 | argv[0], c); 794 | else 795 | fprintf (stderr, _("%s: invalid option -- %c\n"), 796 | argv[0], c); 797 | } 798 | optopt = c; 799 | return '?'; 800 | } 801 | /* Convenience. Treat POSIX -W foo same as long option --foo */ 802 | if (temp[0] == 'W' && temp[1] == ';') 803 | { 804 | char *nameend; 805 | const struct option *p; 806 | const struct option *pfound = NULL; 807 | int exact = 0; 808 | int ambig = 0; 809 | int indfound = 0; 810 | int option_index; 811 | 812 | /* This is an option that requires an argument. */ 813 | if (*nextchar != '\0') 814 | { 815 | optarg = nextchar; 816 | /* If we end this ARGV-element by taking the rest as an arg, 817 | we must advance to the next element now. */ 818 | optind++; 819 | } 820 | else if (optind == argc) 821 | { 822 | if (print_errors) 823 | { 824 | /* 1003.2 specifies the format of this message. */ 825 | fprintf (stderr, _("%s: option requires an argument -- %c\n"), 826 | argv[0], c); 827 | } 828 | optopt = c; 829 | if (optstring[0] == ':') 830 | c = ':'; 831 | else 832 | c = '?'; 833 | return c; 834 | } 835 | else 836 | /* We already incremented `optind' once; 837 | increment it again when taking next ARGV-elt as argument. */ 838 | optarg = argv[optind++]; 839 | 840 | /* optarg is now the argument, see if it's in the 841 | table of longopts. */ 842 | 843 | for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) 844 | /* Do nothing. */ ; 845 | 846 | /* Test all long options for either exact match 847 | or abbreviated matches. */ 848 | for (p = longopts, option_index = 0; p->name; p++, option_index++) 849 | if (!strncmp (p->name, nextchar, nameend - nextchar)) 850 | { 851 | if ((unsigned int) (nameend - nextchar) == strlen (p->name)) 852 | { 853 | /* Exact match found. */ 854 | pfound = p; 855 | indfound = option_index; 856 | exact = 1; 857 | break; 858 | } 859 | else if (pfound == NULL) 860 | { 861 | /* First nonexact match found. */ 862 | pfound = p; 863 | indfound = option_index; 864 | } 865 | else 866 | /* Second or later nonexact match found. */ 867 | ambig = 1; 868 | } 869 | if (ambig && !exact) 870 | { 871 | if (print_errors) 872 | fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), 873 | argv[0], argv[optind]); 874 | nextchar += strlen (nextchar); 875 | optind++; 876 | return '?'; 877 | } 878 | if (pfound != NULL) 879 | { 880 | option_index = indfound; 881 | if (*nameend) 882 | { 883 | /* Don't test has_arg with >, because some C compilers don't 884 | allow it to be used on enums. */ 885 | if (pfound->has_arg) 886 | optarg = nameend + 1; 887 | else 888 | { 889 | if (print_errors) 890 | fprintf (stderr, _("\ 891 | %s: option `-W %s' doesn't allow an argument\n"), 892 | argv[0], pfound->name); 893 | 894 | nextchar += strlen (nextchar); 895 | return '?'; 896 | } 897 | } 898 | else if (pfound->has_arg == 1) 899 | { 900 | if (optind < argc) 901 | optarg = argv[optind++]; 902 | else 903 | { 904 | if (print_errors) 905 | fprintf (stderr, 906 | _("%s: option `%s' requires an argument\n"), 907 | argv[0], argv[optind - 1]); 908 | nextchar += strlen (nextchar); 909 | return optstring[0] == ':' ? ':' : '?'; 910 | } 911 | } 912 | nextchar += strlen (nextchar); 913 | if (longind != NULL) 914 | *longind = option_index; 915 | if (pfound->flag) 916 | { 917 | *(pfound->flag) = pfound->val; 918 | return 0; 919 | } 920 | return pfound->val; 921 | } 922 | nextchar = NULL; 923 | return 'W'; /* Let the application handle it. */ 924 | } 925 | if (temp[1] == ':') 926 | { 927 | if (temp[2] == ':') 928 | { 929 | /* This is an option that accepts an argument optionally. */ 930 | if (*nextchar != '\0') 931 | { 932 | optarg = nextchar; 933 | optind++; 934 | } 935 | else 936 | optarg = NULL; 937 | nextchar = NULL; 938 | } 939 | else 940 | { 941 | /* This is an option that requires an argument. */ 942 | if (*nextchar != '\0') 943 | { 944 | optarg = nextchar; 945 | /* If we end this ARGV-element by taking the rest as an arg, 946 | we must advance to the next element now. */ 947 | optind++; 948 | } 949 | else if (optind == argc) 950 | { 951 | if (print_errors) 952 | { 953 | /* 1003.2 specifies the format of this message. */ 954 | fprintf (stderr, 955 | _("%s: option requires an argument -- %c\n"), 956 | argv[0], c); 957 | } 958 | optopt = c; 959 | if (optstring[0] == ':') 960 | c = ':'; 961 | else 962 | c = '?'; 963 | } 964 | else 965 | /* We already incremented `optind' once; 966 | increment it again when taking next ARGV-elt as argument. */ 967 | optarg = argv[optind++]; 968 | nextchar = NULL; 969 | } 970 | } 971 | return c; 972 | } 973 | } 974 | 975 | int 976 | getopt (argc, argv, optstring) 977 | int argc; 978 | char *const *argv; 979 | const char *optstring; 980 | { 981 | return _getopt_internal (argc, argv, optstring, 982 | (const struct option *) 0, 983 | (int *) 0, 984 | 0); 985 | } 986 | 987 | #endif /* Not ELIDE_CODE. */ 988 | 989 | #ifdef TEST 990 | 991 | /* Compile with -DTEST to make an executable for use in testing 992 | the above definition of `getopt'. */ 993 | 994 | int 995 | main (argc, argv) 996 | int argc; 997 | char **argv; 998 | { 999 | int c; 1000 | int digit_optind = 0; 1001 | 1002 | while (1) 1003 | { 1004 | int this_option_optind = optind ? optind : 1; 1005 | 1006 | c = getopt (argc, argv, "abc:d:0123456789"); 1007 | if (c == -1) 1008 | break; 1009 | 1010 | switch (c) 1011 | { 1012 | case '0': 1013 | case '1': 1014 | case '2': 1015 | case '3': 1016 | case '4': 1017 | case '5': 1018 | case '6': 1019 | case '7': 1020 | case '8': 1021 | case '9': 1022 | if (digit_optind != 0 && digit_optind != this_option_optind) 1023 | printf ("digits occur in two different argv-elements.\n"); 1024 | digit_optind = this_option_optind; 1025 | printf ("option %c\n", c); 1026 | break; 1027 | 1028 | case 'a': 1029 | printf ("option a\n"); 1030 | break; 1031 | 1032 | case 'b': 1033 | printf ("option b\n"); 1034 | break; 1035 | 1036 | case 'c': 1037 | printf ("option c with value `%s'\n", optarg); 1038 | break; 1039 | 1040 | case '?': 1041 | break; 1042 | 1043 | default: 1044 | printf ("?? getopt returned character code 0%o ??\n", c); 1045 | } 1046 | } 1047 | 1048 | if (optind < argc) 1049 | { 1050 | printf ("non-option ARGV-elements: "); 1051 | while (optind < argc) 1052 | printf ("%s ", argv[optind++]); 1053 | printf ("\n"); 1054 | } 1055 | 1056 | exit (0); 1057 | } 1058 | 1059 | #endif /* TEST */ 1060 | -------------------------------------------------------------------------------- /getopt/getopt.h: -------------------------------------------------------------------------------- 1 | /* Declarations for getopt. 2 | Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. 3 | This file is part of the GNU C Library. 4 | 5 | The GNU C Library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | The GNU C Library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with the GNU C Library; if not, write to the Free 17 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 18 | 02111-1307 USA. */ 19 | 20 | #ifndef _GETOPT_H 21 | 22 | #ifndef __need_getopt 23 | # define _GETOPT_H 1 24 | #endif 25 | 26 | /* If __GNU_LIBRARY__ is not already defined, either we are being used 27 | standalone, or this is the first header included in the source file. 28 | If we are being used with glibc, we need to include , but 29 | that does not exist if we are standalone. So: if __GNU_LIBRARY__ is 30 | not defined, include , which will pull in for us 31 | if it's from glibc. (Why ctype.h? It's guaranteed to exist and it 32 | doesn't flood the namespace with stuff the way some other headers do.) */ 33 | #if !defined __GNU_LIBRARY__ 34 | # include 35 | #endif 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | /* For communication from `getopt' to the caller. 42 | When `getopt' finds an option that takes an argument, 43 | the argument value is returned here. 44 | Also, when `ordering' is RETURN_IN_ORDER, 45 | each non-option ARGV-element is returned here. */ 46 | 47 | extern char *optarg; 48 | 49 | /* Index in ARGV of the next element to be scanned. 50 | This is used for communication to and from the caller 51 | and for communication between successive calls to `getopt'. 52 | 53 | On entry to `getopt', zero means this is the first call; initialize. 54 | 55 | When `getopt' returns -1, this is the index of the first of the 56 | non-option elements that the caller should itself scan. 57 | 58 | Otherwise, `optind' communicates from one call to the next 59 | how much of ARGV has been scanned so far. */ 60 | 61 | extern int optind; 62 | 63 | /* Callers store zero here to inhibit the error message `getopt' prints 64 | for unrecognized options. */ 65 | 66 | extern int opterr; 67 | 68 | /* Set to an option character which was unrecognized. */ 69 | 70 | extern int optopt; 71 | 72 | #ifndef __need_getopt 73 | /* Describe the long-named options requested by the application. 74 | The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector 75 | of `struct option' terminated by an element containing a name which is 76 | zero. 77 | 78 | The field `has_arg' is: 79 | no_argument (or 0) if the option does not take an argument, 80 | required_argument (or 1) if the option requires an argument, 81 | optional_argument (or 2) if the option takes an optional argument. 82 | 83 | If the field `flag' is not NULL, it points to a variable that is set 84 | to the value given in the field `val' when the option is found, but 85 | left unchanged if the option is not found. 86 | 87 | To have a long-named option do something other than set an `int' to 88 | a compiled-in constant, such as set a value from `optarg', set the 89 | option's `flag' field to zero and its `val' field to a nonzero 90 | value (the equivalent single-letter option character, if there is 91 | one). For long options that have a zero `flag' field, `getopt' 92 | returns the contents of the `val' field. */ 93 | 94 | struct option 95 | { 96 | # if (defined __STDC__ && __STDC__) || defined __cplusplus 97 | const char *name; 98 | # else 99 | char *name; 100 | # endif 101 | /* has_arg can't be an enum because some compilers complain about 102 | type mismatches in all the code that assumes it is an int. */ 103 | int has_arg; 104 | int *flag; 105 | int val; 106 | }; 107 | 108 | /* Names for the values of the `has_arg' field of `struct option'. */ 109 | 110 | # define no_argument 0 111 | # define required_argument 1 112 | # define optional_argument 2 113 | #endif /* need getopt */ 114 | 115 | 116 | /* Get definitions and prototypes for functions to process the 117 | arguments in ARGV (ARGC of them, minus the program name) for 118 | options given in OPTS. 119 | 120 | Return the option character from OPTS just read. Return -1 when 121 | there are no more options. For unrecognized options, or options 122 | missing arguments, `optopt' is set to the option letter, and '?' is 123 | returned. 124 | 125 | The OPTS string is a list of characters which are recognized option 126 | letters, optionally followed by colons, specifying that that letter 127 | takes an argument, to be placed in `optarg'. 128 | 129 | If a letter in OPTS is followed by two colons, its argument is 130 | optional. This behavior is specific to the GNU `getopt'. 131 | 132 | The argument `--' causes premature termination of argument 133 | scanning, explicitly telling `getopt' that there are no more 134 | options. 135 | 136 | If OPTS begins with `--', then non-option arguments are treated as 137 | arguments to the option '\0'. This behavior is specific to the GNU 138 | `getopt'. */ 139 | 140 | #if (defined __STDC__ && __STDC__) || defined __cplusplus 141 | # ifdef __GNU_LIBRARY__ 142 | /* Many other libraries have conflicting prototypes for getopt, with 143 | differences in the consts, in stdlib.h. To avoid compilation 144 | errors, only prototype getopt for the GNU C library. */ 145 | extern int getopt (int __argc, char *const *__argv, const char *__shortopts); 146 | # else /* not __GNU_LIBRARY__ */ 147 | extern int getopt (); 148 | # endif /* __GNU_LIBRARY__ */ 149 | 150 | # ifndef __need_getopt 151 | extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, 152 | const struct option *__longopts, int *__longind); 153 | extern int getopt_long_only (int __argc, char *const *__argv, 154 | const char *__shortopts, 155 | const struct option *__longopts, int *__longind); 156 | 157 | /* Internal only. Users should not call this directly. */ 158 | extern int _getopt_internal (int __argc, char *const *__argv, 159 | const char *__shortopts, 160 | const struct option *__longopts, int *__longind, 161 | int __long_only); 162 | # endif 163 | #else /* not __STDC__ */ 164 | extern int getopt (); 165 | # ifndef __need_getopt 166 | extern int getopt_long (); 167 | extern int getopt_long_only (); 168 | 169 | extern int _getopt_internal (); 170 | # endif 171 | #endif /* __STDC__ */ 172 | 173 | #ifdef __cplusplus 174 | } 175 | #endif 176 | 177 | /* Make sure we later can get all the definitions and declarations. */ 178 | #undef __need_getopt 179 | 180 | #endif /* getopt.h */ 181 | -------------------------------------------------------------------------------- /rtl_fm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver 3 | * Copyright (C) 2012 by Steve Markgraf 4 | * Copyright (C) 2012 by Hoernchen 5 | * Copyright (C) 2012 by Kyle Keen 6 | * Copyright (C) 2013 by Elias Oenal 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | 23 | /* 24 | * written because people could not do real time 25 | * FM demod on Atom hardware with GNU radio 26 | * based on rtl_sdr.c and rtl_tcp.c 27 | * 28 | * lots of locks, but that is okay 29 | * (no many-to-many locks) 30 | * 31 | * todo: 32 | * sanity checks 33 | * scale squelch to other input parameters 34 | * test all the demodulations 35 | * pad output on hop 36 | * frequency ranges could be stored better 37 | * scaled AM demod amplification 38 | * auto-hop after time limit 39 | * peak detector to tune onto stronger signals 40 | * fifo for active hop frequency 41 | * clips 42 | * noise squelch 43 | * merge stereo patch 44 | * merge soft agc patch 45 | * merge udp patch 46 | * testmode to detect overruns 47 | * watchdog to reset bad dongle 48 | * fix oversampling 49 | */ 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #ifndef _WIN32 58 | #include 59 | #else 60 | #include 61 | #include 62 | #include 63 | #include "getopt/getopt.h" 64 | #define usleep(x) Sleep(x/1000) 65 | #ifdef _MSC_VER 66 | #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) 67 | #endif 68 | #define _USE_MATH_DEFINES 69 | #endif 70 | 71 | #include 72 | #include 73 | #include 74 | 75 | #include "rtl-sdr.h" 76 | #include "convenience/convenience.h" 77 | 78 | #define DEFAULT_SAMPLE_RATE 24000 79 | #define DEFAULT_BUF_LENGTH (1 * 16384) 80 | #define MAXIMUM_OVERSAMPLE 16 81 | #define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH) 82 | #define AUTO_GAIN -100 83 | #define BUFFER_DUMP 4096 84 | 85 | #define FREQUENCIES_LIMIT 1000 86 | 87 | static volatile int do_exit = 0; 88 | static int lcm_post[17] = {1,1,1,3,1,5,3,7,1,9,5,11,3,13,7,15,1}; 89 | static int ACTUAL_BUF_LENGTH; 90 | 91 | static int *atan_lut = NULL; 92 | static int atan_lut_size = 131072; /* 512 KB */ 93 | static int atan_lut_coef = 8; 94 | 95 | struct dongle_state 96 | { 97 | int exit_flag; 98 | pthread_t thread; 99 | rtlsdr_dev_t *dev; 100 | int dev_index; 101 | uint32_t freq; 102 | uint32_t rate; 103 | int gain; 104 | uint16_t buf16[MAXIMUM_BUF_LENGTH]; 105 | uint32_t buf_len; 106 | int ppm_error; 107 | int offset_tuning; 108 | int direct_sampling; 109 | int mute; 110 | struct demod_state *demod_target; 111 | }; 112 | 113 | struct demod_state 114 | { 115 | int exit_flag; 116 | pthread_t thread; 117 | int16_t lowpassed[MAXIMUM_BUF_LENGTH]; 118 | int lp_len; 119 | int16_t lp_i_hist[10][6]; 120 | int16_t lp_q_hist[10][6]; 121 | int16_t result[MAXIMUM_BUF_LENGTH]; 122 | int16_t droop_i_hist[9]; 123 | int16_t droop_q_hist[9]; 124 | int result_len; 125 | int rate_in; 126 | int rate_out; 127 | int rate_out2; 128 | int now_r, now_j; 129 | int pre_r, pre_j; 130 | int prev_index; 131 | int downsample; /* min 1, max 256 */ 132 | int post_downsample; 133 | int output_scale; 134 | int squelch_level, conseq_squelch, squelch_hits, terminate_on_squelch; 135 | int downsample_passes; 136 | int comp_fir_size; 137 | int custom_atan; 138 | int deemph, deemph_a; 139 | int now_lpr; 140 | int prev_lpr_index; 141 | int dc_block, dc_avg; 142 | void (*mode_demod)(struct demod_state*); 143 | pthread_rwlock_t rw; 144 | pthread_cond_t ready; 145 | pthread_mutex_t ready_m; 146 | struct output_state *output_target; 147 | }; 148 | 149 | struct output_state 150 | { 151 | int exit_flag; 152 | pthread_t thread; 153 | FILE *file; 154 | char *filename; 155 | int16_t result[MAXIMUM_BUF_LENGTH]; 156 | int result_len; 157 | int rate; 158 | pthread_rwlock_t rw; 159 | pthread_cond_t ready; 160 | pthread_mutex_t ready_m; 161 | }; 162 | 163 | struct controller_state 164 | { 165 | int exit_flag; 166 | pthread_t thread; 167 | uint32_t freqs[FREQUENCIES_LIMIT]; 168 | int freq_len; 169 | int freq_now; 170 | int edge; 171 | int wb_mode; 172 | pthread_cond_t hop; 173 | pthread_mutex_t hop_m; 174 | }; 175 | 176 | // multiple of these, eventually 177 | struct dongle_state dongle; 178 | struct demod_state demod; 179 | struct output_state output; 180 | struct controller_state controller; 181 | 182 | void usage(void) 183 | { 184 | fprintf(stderr, 185 | "rtl_fm, a simple narrow band FM demodulator for RTL2832 based DVB-T receivers\n\n" 186 | "Use:\trtl_fm -f freq [-options] [filename]\n" 187 | "\t-f frequency_to_tune_to [Hz]\n" 188 | "\t use multiple -f for scanning (requires squelch)\n" 189 | "\t ranges supported, -f 118M:137M:25k\n" 190 | "\t[-M modulation (default: fm)]\n" 191 | "\t fm, wbfm, raw, am, usb, lsb\n" 192 | "\t wbfm == -M fm -s 170k -o 4 -A fast -r 32k -l 0 -E deemp\n" 193 | "\t raw mode outputs 2x16 bit IQ pairs\n" 194 | "\t[-s sample_rate (default: 24k)]\n" 195 | "\t[-d device_index (default: 0)]\n" 196 | "\t[-g tuner_gain (default: automatic)]\n" 197 | "\t[-l squelch_level (default: 0/off)]\n" 198 | //"\t for fm squelch is inverted\n" 199 | //"\t[-o oversampling (default: 1, 4 recommended)]\n" 200 | "\t[-p ppm_error (default: 0)]\n" 201 | "\t[-E enable_option (default: none)]\n" 202 | "\t use multiple -E to enable multiple options\n" 203 | "\t edge: enable lower edge tuning\n" 204 | "\t dc: enable dc blocking filter\n" 205 | "\t deemp: enable de-emphasis filter\n" 206 | "\t direct: enable direct sampling\n" 207 | "\t offset: enable offset tuning\n" 208 | "\tfilename ('-' means stdout)\n" 209 | "\t omitting the filename also uses stdout\n\n" 210 | "Experimental options:\n" 211 | "\t[-r resample_rate (default: none / same as -s)]\n" 212 | "\t[-t squelch_delay (default: 10)]\n" 213 | "\t +values will mute/scan, -values will exit\n" 214 | "\t[-F fir_size (default: off)]\n" 215 | "\t enables low-leakage downsample filter\n" 216 | "\t size can be 0 or 9. 0 has bad roll off\n" 217 | "\t[-A std/fast/lut choose atan math (default: std)]\n" 218 | //"\t[-C clip_path (default: off)\n" 219 | //"\t (create time stamped raw clips, requires squelch)\n" 220 | //"\t (path must have '\%s' and will expand to date_time_freq)\n" 221 | //"\t[-H hop_fifo (default: off)\n" 222 | //"\t (fifo will contain the active frequency)\n" 223 | "\n" 224 | "Produces signed 16 bit ints, use Sox or aplay to hear them.\n" 225 | "\trtl_fm ... | play -t raw -r 24k -es -b 16 -c 1 -V1 -\n" 226 | "\t | aplay -r 24k -f S16_LE -t raw -c 1\n" 227 | "\t -M wbfm | play -r 32k ... \n" 228 | "\t -s 22050 | multimon -t raw /dev/stdin\n\n"); 229 | exit(1); 230 | } 231 | 232 | #ifdef _WIN32 233 | BOOL WINAPI 234 | sighandler(int signum) 235 | { 236 | if (CTRL_C_EVENT == signum) { 237 | fprintf(stderr, "Signal caught, exiting!\n"); 238 | do_exit = 1; 239 | rtlsdr_cancel_async(dongle.dev); 240 | return TRUE; 241 | } 242 | return FALSE; 243 | } 244 | #else 245 | static void sighandler(int signum) 246 | { 247 | fprintf(stderr, "Signal caught, exiting!\n"); 248 | do_exit = 1; 249 | rtlsdr_cancel_async(dongle.dev); 250 | } 251 | #endif 252 | 253 | /* more cond dumbness */ 254 | #define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m) 255 | #define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m) 256 | 257 | /* {length, coef, coef, coef} and scaled by 2^15 258 | for now, only length 9, optimal way to get +85% bandwidth */ 259 | #define CIC_TABLE_MAX 10 260 | int cic_9_tables[][10] = { 261 | {0,}, 262 | {9, -156, -97, 2798, -15489, 61019, -15489, 2798, -97, -156}, 263 | {9, -128, -568, 5593, -24125, 74126, -24125, 5593, -568, -128}, 264 | {9, -129, -639, 6187, -26281, 77511, -26281, 6187, -639, -129}, 265 | {9, -122, -612, 6082, -26353, 77818, -26353, 6082, -612, -122}, 266 | {9, -120, -602, 6015, -26269, 77757, -26269, 6015, -602, -120}, 267 | {9, -120, -582, 5951, -26128, 77542, -26128, 5951, -582, -120}, 268 | {9, -119, -580, 5931, -26094, 77505, -26094, 5931, -580, -119}, 269 | {9, -119, -578, 5921, -26077, 77484, -26077, 5921, -578, -119}, 270 | {9, -119, -577, 5917, -26067, 77473, -26067, 5917, -577, -119}, 271 | {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, 272 | }; 273 | 274 | #ifdef _MSC_VER 275 | double log2(double n) 276 | { 277 | return log(n) / log(2.0); 278 | } 279 | #endif 280 | 281 | void rotate_90(unsigned char *buf, uint32_t len) 282 | /* 90 rotation is 1+0j, 0+1j, -1+0j, 0-1j 283 | or [0, 1, -3, 2, -4, -5, 7, -6] */ 284 | { 285 | uint32_t i; 286 | unsigned char tmp; 287 | for (i=0; ilp_len) { 307 | d->now_r += d->lowpassed[i]; 308 | d->now_j += d->lowpassed[i+1]; 309 | i += 2; 310 | d->prev_index++; 311 | if (d->prev_index < d->downsample) { 312 | continue; 313 | } 314 | d->lowpassed[i2] = d->now_r; // * d->output_scale; 315 | d->lowpassed[i2+1] = d->now_j; // * d->output_scale; 316 | d->prev_index = 0; 317 | d->now_r = 0; 318 | d->now_j = 0; 319 | i2 += 2; 320 | } 321 | d->lp_len = i2; 322 | } 323 | 324 | int low_pass_simple(int16_t *signal2, int len, int step) 325 | // no wrap around, length must be multiple of step 326 | { 327 | int i, i2, sum; 328 | for(i=0; i < len; i+=step) { 329 | sum = 0; 330 | for(i2=0; i2rate_out; 346 | int slow = s->rate_out2; 347 | while (i < s->result_len) { 348 | s->now_lpr += s->result[i]; 349 | i++; 350 | s->prev_lpr_index += slow; 351 | if (s->prev_lpr_index < fast) { 352 | continue; 353 | } 354 | s->result[i2] = (int16_t)(s->now_lpr / (fast/slow)); 355 | s->prev_lpr_index -= fast; 356 | s->now_lpr = 0; 357 | i2 += 1; 358 | } 359 | s->result_len = i2; 360 | } 361 | 362 | void fifth_order(int16_t *data, int length, int16_t *hist) 363 | /* for half of interleaved data */ 364 | { 365 | int i; 366 | int16_t a, b, c, d, e, f; 367 | a = hist[1]; 368 | b = hist[2]; 369 | c = hist[3]; 370 | d = hist[4]; 371 | e = hist[5]; 372 | f = data[0]; 373 | /* a downsample should improve resolution, so don't fully shift */ 374 | data[0] = (a + (b+e)*5 + (c+d)*10 + f) >> 4; 375 | for (i=4; i> 4; 383 | } 384 | /* archive */ 385 | hist[0] = a; 386 | hist[1] = b; 387 | hist[2] = c; 388 | hist[3] = d; 389 | hist[4] = e; 390 | hist[5] = f; 391 | } 392 | 393 | void generic_fir(int16_t *data, int length, int *fir, int16_t *hist) 394 | /* Okay, not at all generic. Assumes length 9, fix that eventually. */ 395 | { 396 | int d, temp, sum; 397 | for (d=0; d> 15 ; 406 | hist[0] = hist[1]; 407 | hist[1] = hist[2]; 408 | hist[2] = hist[3]; 409 | hist[3] = hist[4]; 410 | hist[4] = hist[5]; 411 | hist[5] = hist[6]; 412 | hist[6] = hist[7]; 413 | hist[7] = hist[8]; 414 | hist[8] = temp; 415 | } 416 | } 417 | 418 | /* define our own complex math ops 419 | because ARMv5 has no hardware float */ 420 | 421 | void multiply(int ar, int aj, int br, int bj, int *cr, int *cj) 422 | { 423 | *cr = ar*br - aj*bj; 424 | *cj = aj*br + ar*bj; 425 | } 426 | 427 | int polar_discriminant(int ar, int aj, int br, int bj) 428 | { 429 | int cr, cj; 430 | double angle; 431 | multiply(ar, aj, br, -bj, &cr, &cj); 432 | angle = atan2((double)cj, (double)cr); 433 | return (int)(angle / 3.14159 * (1<<14)); 434 | } 435 | 436 | int fast_atan2(int y, int x) 437 | /* pre scaled for int16 */ 438 | { 439 | int yabs, angle; 440 | int pi4=(1<<12), pi34=3*(1<<12); // note pi = 1<<14 441 | if (x==0 && y==0) { 442 | return 0; 443 | } 444 | yabs = y; 445 | if (yabs < 0) { 446 | yabs = -yabs; 447 | } 448 | if (x >= 0) { 449 | angle = pi4 - pi4 * (x-yabs) / (x+yabs); 450 | } else { 451 | angle = pi34 - pi4 * (x+yabs) / (yabs-x); 452 | } 453 | if (y < 0) { 454 | return -angle; 455 | } 456 | return angle; 457 | } 458 | 459 | int polar_disc_fast(int ar, int aj, int br, int bj) 460 | { 461 | int cr, cj; 462 | multiply(ar, aj, br, -bj, &cr, &cj); 463 | return fast_atan2(cj, cr); 464 | } 465 | 466 | int atan_lut_init(void) 467 | { 468 | int i = 0; 469 | 470 | atan_lut = malloc(atan_lut_size * sizeof(int)); 471 | 472 | for (i = 0; i < atan_lut_size; i++) { 473 | atan_lut[i] = (int) (atan((double) i / (1< 0) 490 | {return 1 << 13;} 491 | if (cr == 0 && cj < 0) 492 | {return -(1 << 13);} 493 | if (cj == 0 && cr > 0) 494 | {return 0;} 495 | if (cj == 0 && cr < 0) 496 | {return 1 << 14;} 497 | } 498 | 499 | /* real range -32768 - 32768 use 64x range -> absolute maximum: 2097152 */ 500 | x = (cj << atan_lut_coef) / cr; 501 | x_abs = abs(x); 502 | 503 | if (x_abs >= atan_lut_size) { 504 | /* we can use linear range, but it is not necessary */ 505 | return (cj > 0) ? 1<<13 : -1<<13; 506 | } 507 | 508 | if (x > 0) { 509 | return (cj > 0) ? atan_lut[x] : atan_lut[x] - (1<<14); 510 | } else { 511 | return (cj > 0) ? (1<<14) - atan_lut[-x] : -atan_lut[-x]; 512 | } 513 | 514 | return 0; 515 | } 516 | 517 | void fm_demod(struct demod_state *fm) 518 | { 519 | int i, pcm; 520 | int16_t *lp = fm->lowpassed; 521 | pcm = polar_discriminant(lp[0], lp[1], 522 | fm->pre_r, fm->pre_j); 523 | fm->result[0] = (int16_t)pcm; 524 | for (i = 2; i < (fm->lp_len-1); i += 2) { 525 | switch (fm->custom_atan) { 526 | case 0: 527 | pcm = polar_discriminant(lp[i], lp[i+1], 528 | lp[i-2], lp[i-1]); 529 | break; 530 | case 1: 531 | pcm = polar_disc_fast(lp[i], lp[i+1], 532 | lp[i-2], lp[i-1]); 533 | break; 534 | case 2: 535 | pcm = polar_disc_lut(lp[i], lp[i+1], 536 | lp[i-2], lp[i-1]); 537 | break; 538 | } 539 | fm->result[i/2] = (int16_t)pcm; 540 | } 541 | fm->pre_r = lp[fm->lp_len - 2]; 542 | fm->pre_j = lp[fm->lp_len - 1]; 543 | fm->result_len = fm->lp_len/2; 544 | } 545 | 546 | void am_demod(struct demod_state *fm) 547 | // todo, fix this extreme laziness 548 | { 549 | int i, pcm; 550 | int16_t *lp = fm->lowpassed; 551 | int16_t *r = fm->result; 552 | for (i = 0; i < fm->lp_len; i += 2) { 553 | // hypot uses floats but won't overflow 554 | //r[i/2] = (int16_t)hypot(lp[i], lp[i+1]); 555 | pcm = lp[i] * lp[i]; 556 | pcm += lp[i+1] * lp[i+1]; 557 | r[i/2] = (int16_t)sqrt(pcm) * fm->output_scale; 558 | } 559 | fm->result_len = fm->lp_len/2; 560 | // lowpass? (3khz) highpass? (dc) 561 | } 562 | 563 | void usb_demod(struct demod_state *fm) 564 | { 565 | int i, pcm; 566 | int16_t *lp = fm->lowpassed; 567 | int16_t *r = fm->result; 568 | for (i = 0; i < fm->lp_len; i += 2) { 569 | pcm = lp[i] + lp[i+1]; 570 | r[i/2] = (int16_t)pcm * fm->output_scale; 571 | } 572 | fm->result_len = fm->lp_len/2; 573 | } 574 | 575 | void lsb_demod(struct demod_state *fm) 576 | { 577 | int i, pcm; 578 | int16_t *lp = fm->lowpassed; 579 | int16_t *r = fm->result; 580 | for (i = 0; i < fm->lp_len; i += 2) { 581 | pcm = lp[i] - lp[i+1]; 582 | r[i/2] = (int16_t)pcm * fm->output_scale; 583 | } 584 | fm->result_len = fm->lp_len/2; 585 | } 586 | 587 | void raw_demod(struct demod_state *fm) 588 | { 589 | int i; 590 | for (i = 0; i < fm->lp_len; i++) { 591 | fm->result[i] = (int16_t)fm->lowpassed[i]; 592 | } 593 | fm->result_len = fm->lp_len; 594 | } 595 | 596 | void deemph_filter(struct demod_state *fm) 597 | { 598 | static int avg; // cheating... 599 | int i, d; 600 | // de-emph IIR 601 | // avg = avg * (1 - alpha) + sample * alpha; 602 | for (i = 0; i < fm->result_len; i++) { 603 | d = fm->result[i] - avg; 604 | if (d > 0) { 605 | avg += (d + fm->deemph_a/2) / fm->deemph_a; 606 | } else { 607 | avg += (d - fm->deemph_a/2) / fm->deemph_a; 608 | } 609 | fm->result[i] = (int16_t)avg; 610 | } 611 | } 612 | 613 | void dc_block_filter(struct demod_state *fm) 614 | { 615 | int i, avg; 616 | int64_t sum = 0; 617 | for (i=0; i < fm->result_len; i++) { 618 | sum += fm->result[i]; 619 | } 620 | avg = sum / fm->result_len; 621 | avg = (avg + fm->dc_avg * 9) / 10; 622 | for (i=0; i < fm->result_len; i++) { 623 | fm->result[i] -= avg; 624 | } 625 | fm->dc_avg = avg; 626 | } 627 | 628 | int mad(int16_t *samples, int len, int step) 629 | /* mean average deviation */ 630 | { 631 | int i=0, sum=0, ave=0; 632 | if (len == 0) 633 | {return 0;} 634 | for (i=0; i len2) { 678 | tick -= len2; 679 | i++; 680 | } 681 | if (i >= len1) { 682 | i = len1 - 1; 683 | tick = len2; 684 | } 685 | } 686 | } 687 | 688 | void arbitrary_downsample(int16_t *buf1, int16_t *buf2, int len1, int len2) 689 | /* fractional boxcar lowpass, len1 > len2 */ 690 | { 691 | int i = 1; 692 | int j = 0; 693 | int tick = 0; 694 | double remainder = 0; 695 | double frac; // use integers... 696 | buf2[0] = 0; 697 | while (j < len2) { 698 | frac = 1.0; 699 | if ((tick + len2) > len1) { 700 | frac = (double)(len1 - tick) / (double)len2;} 701 | buf2[j] += (int16_t)((double)buf1[i] * frac + remainder); 702 | remainder = (double)buf1[i] * (1.0-frac); 703 | tick += len2; 704 | i++; 705 | if (tick > len1) { 706 | j++; 707 | buf2[j] = 0; 708 | tick -= len1; 709 | } 710 | if (i >= len1) { 711 | i = len1 - 1; 712 | tick = len1; 713 | } 714 | } 715 | for (j=0; jdownsample_passes; 735 | if (ds_p) { 736 | for (i=0; i < ds_p; i++) { 737 | fifth_order(d->lowpassed, (d->lp_len >> i), d->lp_i_hist[i]); 738 | fifth_order(d->lowpassed+1, (d->lp_len >> i) - 1, d->lp_q_hist[i]); 739 | } 740 | d->lp_len = d->lp_len >> ds_p; 741 | /* droop compensation */ 742 | if (d->comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { 743 | generic_fir(d->lowpassed, d->lp_len, 744 | cic_9_tables[ds_p], d->droop_i_hist); 745 | generic_fir(d->lowpassed+1, d->lp_len-1, 746 | cic_9_tables[ds_p], d->droop_q_hist); 747 | } 748 | } else { 749 | low_pass(d); 750 | } 751 | /* power squelch */ 752 | if (d->squelch_level) { 753 | sr = rms(d->lowpassed, d->lp_len, 1); 754 | if (sr < d->squelch_level) { 755 | d->squelch_hits++; 756 | for (i=0; ilp_len; i++) { 757 | d->lowpassed[i] = 0; 758 | } 759 | } else { 760 | d->squelch_hits = 0;} 761 | } 762 | d->mode_demod(d); /* lowpassed -> result */ 763 | if (d->mode_demod == &raw_demod) { 764 | return; 765 | } 766 | /* todo, fm noise squelch */ 767 | // use nicer filter here too? 768 | if (d->post_downsample > 1) { 769 | d->result_len = low_pass_simple(d->result, d->result_len, d->post_downsample);} 770 | if (d->deemph) { 771 | deemph_filter(d);} 772 | if (d->dc_block) { 773 | dc_block_filter(d);} 774 | if (d->rate_out2 > 0) { 775 | low_pass_real(d); 776 | //arbitrary_resample(d->result, d->result, d->result_len, d->result_len * d->rate_out2 / d->rate_out); 777 | } 778 | } 779 | 780 | static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) 781 | { 782 | int i; 783 | struct dongle_state *s = ctx; 784 | struct demod_state *d = s->demod_target; 785 | 786 | if (do_exit) { 787 | return;} 788 | if (!ctx) { 789 | return;} 790 | if (s->mute) { 791 | for (i=0; imute; i++) { 792 | buf[i] = 127;} 793 | s->mute = 0; 794 | } 795 | if (!s->offset_tuning) { 796 | rotate_90(buf, len);} 797 | for (i=0; i<(int)len; i++) { 798 | s->buf16[i] = (int16_t)buf[i] - 127;} 799 | pthread_rwlock_wrlock(&d->rw); 800 | memcpy(d->lowpassed, s->buf16, 2*len); 801 | d->lp_len = len; 802 | pthread_rwlock_unlock(&d->rw); 803 | safe_cond_signal(&d->ready, &d->ready_m); 804 | } 805 | 806 | static void *dongle_thread_fn(void *arg) 807 | { 808 | struct dongle_state *s = arg; 809 | rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len); 810 | return 0; 811 | } 812 | 813 | static void *demod_thread_fn(void *arg) 814 | { 815 | struct demod_state *d = arg; 816 | struct output_state *o = d->output_target; 817 | while (!do_exit) { 818 | safe_cond_wait(&d->ready, &d->ready_m); 819 | pthread_rwlock_wrlock(&d->rw); 820 | full_demod(d); 821 | pthread_rwlock_unlock(&d->rw); 822 | if (d->exit_flag) { 823 | do_exit = 1; 824 | } 825 | if (d->squelch_level && d->squelch_hits > d->conseq_squelch) { 826 | d->squelch_hits = d->conseq_squelch + 1; /* hair trigger */ 827 | safe_cond_signal(&controller.hop, &controller.hop_m); 828 | continue; 829 | } 830 | pthread_rwlock_wrlock(&o->rw); 831 | memcpy(o->result, d->result, 2*d->result_len); 832 | o->result_len = d->result_len; 833 | pthread_rwlock_unlock(&o->rw); 834 | safe_cond_signal(&o->ready, &o->ready_m); 835 | } 836 | return 0; 837 | } 838 | 839 | static void *output_thread_fn(void *arg) 840 | { 841 | struct output_state *s = arg; 842 | while (!do_exit) { 843 | // use timedwait and pad out under runs 844 | safe_cond_wait(&s->ready, &s->ready_m); 845 | pthread_rwlock_rdlock(&s->rw); 846 | fwrite(s->result, 2, s->result_len, s->file); 847 | pthread_rwlock_unlock(&s->rw); 848 | } 849 | return 0; 850 | } 851 | 852 | static void optimal_settings(int freq, int rate) 853 | { 854 | // giant ball of hacks 855 | // seems unable to do a single pass, 2:1 856 | int capture_freq, capture_rate; 857 | struct dongle_state *d = &dongle; 858 | struct demod_state *dm = &demod; 859 | struct controller_state *cs = &controller; 860 | dm->downsample = (1000000 / dm->rate_in) + 1; 861 | if (dm->downsample_passes) { 862 | dm->downsample_passes = (int)log2(dm->downsample) + 1; 863 | dm->downsample = 1 << dm->downsample_passes; 864 | } 865 | capture_freq = freq; 866 | capture_rate = dm->downsample * dm->rate_in; 867 | if (!d->offset_tuning) { 868 | capture_freq = freq + capture_rate/4;} 869 | capture_freq += cs->edge * dm->rate_in / 2; 870 | dm->output_scale = (1<<15) / (128 * dm->downsample); 871 | if (dm->output_scale < 1) { 872 | dm->output_scale = 1;} 873 | if (dm->mode_demod == &fm_demod) { 874 | dm->output_scale = 1;} 875 | d->freq = (uint32_t)capture_freq; 876 | d->rate = (uint32_t)capture_rate; 877 | } 878 | 879 | static void *controller_thread_fn(void *arg) 880 | { 881 | // thoughts for multiple dongles 882 | // might be no good using a controller thread if retune/rate blocks 883 | int i; 884 | struct controller_state *s = arg; 885 | 886 | if (s->wb_mode) { 887 | for (i=0; i < s->freq_len; i++) { 888 | s->freqs[i] += 16000;} 889 | } 890 | 891 | /* set up primary channel */ 892 | optimal_settings(s->freqs[0], demod.rate_in); 893 | if (dongle.direct_sampling) { 894 | verbose_direct_sampling(dongle.dev, 1);} 895 | if (dongle.offset_tuning) { 896 | verbose_offset_tuning(dongle.dev);} 897 | 898 | /* Set the frequency */ 899 | verbose_set_frequency(dongle.dev, dongle.freq); 900 | fprintf(stderr, "Oversampling input by: %ix.\n", demod.downsample); 901 | fprintf(stderr, "Oversampling output by: %ix.\n", demod.post_downsample); 902 | fprintf(stderr, "Buffer size: %0.2fms\n", 903 | 1000 * 0.5 * (float)ACTUAL_BUF_LENGTH / (float)dongle.rate); 904 | 905 | /* Set the sample rate */ 906 | verbose_set_sample_rate(dongle.dev, dongle.rate); 907 | fprintf(stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample); 908 | 909 | while (!do_exit) { 910 | safe_cond_wait(&s->hop, &s->hop_m); 911 | if (s->freq_len <= 1) { 912 | continue;} 913 | /* hacky hopping */ 914 | s->freq_now = (s->freq_now + 1) % s->freq_len; 915 | optimal_settings(s->freqs[s->freq_now], demod.rate_in); 916 | rtlsdr_set_center_freq(dongle.dev, dongle.freq); 917 | dongle.mute = BUFFER_DUMP; 918 | } 919 | return 0; 920 | } 921 | 922 | void frequency_range(struct controller_state *s, char *arg) 923 | { 924 | char *start, *stop, *step; 925 | int i; 926 | start = arg; 927 | stop = strchr(start, ':') + 1; 928 | stop[-1] = '\0'; 929 | step = strchr(stop, ':') + 1; 930 | step[-1] = '\0'; 931 | for(i=(int)atofs(start); i<=(int)atofs(stop); i+=(int)atofs(step)) 932 | { 933 | s->freqs[s->freq_len] = (uint32_t)i; 934 | s->freq_len++; 935 | if (s->freq_len >= FREQUENCIES_LIMIT) { 936 | break;} 937 | } 938 | stop[-1] = ':'; 939 | step[-1] = ':'; 940 | } 941 | 942 | void dongle_init(struct dongle_state *s) 943 | { 944 | s->rate = DEFAULT_SAMPLE_RATE; 945 | s->gain = AUTO_GAIN; // tenths of a dB 946 | s->mute = 0; 947 | s->direct_sampling = 0; 948 | s->offset_tuning = 0; 949 | s->demod_target = &demod; 950 | } 951 | 952 | void demod_init(struct demod_state *s) 953 | { 954 | s->rate_in = DEFAULT_SAMPLE_RATE; 955 | s->rate_out = DEFAULT_SAMPLE_RATE; 956 | s->squelch_level = 0; 957 | s->conseq_squelch = 10; 958 | s->terminate_on_squelch = 0; 959 | s->squelch_hits = 11; 960 | s->downsample_passes = 0; 961 | s->comp_fir_size = 0; 962 | s->prev_index = 0; 963 | s->post_downsample = 1; // once this works, default = 4 964 | s->custom_atan = 0; 965 | s->deemph = 0; 966 | s->rate_out2 = -1; // flag for disabled 967 | s->mode_demod = &fm_demod; 968 | s->pre_j = s->pre_r = s->now_r = s->now_j = 0; 969 | s->prev_lpr_index = 0; 970 | s->deemph_a = 0; 971 | s->now_lpr = 0; 972 | s->dc_block = 0; 973 | s->dc_avg = 0; 974 | pthread_rwlock_init(&s->rw, NULL); 975 | pthread_cond_init(&s->ready, NULL); 976 | pthread_mutex_init(&s->ready_m, NULL); 977 | s->output_target = &output; 978 | } 979 | 980 | void demod_cleanup(struct demod_state *s) 981 | { 982 | pthread_rwlock_destroy(&s->rw); 983 | pthread_cond_destroy(&s->ready); 984 | pthread_mutex_destroy(&s->ready_m); 985 | } 986 | 987 | void output_init(struct output_state *s) 988 | { 989 | s->rate = DEFAULT_SAMPLE_RATE; 990 | pthread_rwlock_init(&s->rw, NULL); 991 | pthread_cond_init(&s->ready, NULL); 992 | pthread_mutex_init(&s->ready_m, NULL); 993 | } 994 | 995 | void output_cleanup(struct output_state *s) 996 | { 997 | pthread_rwlock_destroy(&s->rw); 998 | pthread_cond_destroy(&s->ready); 999 | pthread_mutex_destroy(&s->ready_m); 1000 | } 1001 | 1002 | void controller_init(struct controller_state *s) 1003 | { 1004 | s->freqs[0] = 100000000; 1005 | s->freq_len = 0; 1006 | s->edge = 0; 1007 | s->wb_mode = 0; 1008 | pthread_cond_init(&s->hop, NULL); 1009 | pthread_mutex_init(&s->hop_m, NULL); 1010 | } 1011 | 1012 | void controller_cleanup(struct controller_state *s) 1013 | { 1014 | pthread_cond_destroy(&s->hop); 1015 | pthread_mutex_destroy(&s->hop_m); 1016 | } 1017 | 1018 | void sanity_checks(void) 1019 | { 1020 | if (controller.freq_len == 0) { 1021 | fprintf(stderr, "Please specify a frequency.\n"); 1022 | exit(1); 1023 | } 1024 | 1025 | if (controller.freq_len >= FREQUENCIES_LIMIT) { 1026 | fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT); 1027 | exit(1); 1028 | } 1029 | 1030 | if (controller.freq_len > 1 && demod.squelch_level == 0) { 1031 | fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n"); 1032 | exit(1); 1033 | } 1034 | 1035 | } 1036 | 1037 | int main(int argc, char **argv) 1038 | { 1039 | #ifndef _WIN32 1040 | struct sigaction sigact; 1041 | #endif 1042 | int r, opt; 1043 | int dev_given = 0; 1044 | int custom_ppm = 0; 1045 | dongle_init(&dongle); 1046 | demod_init(&demod); 1047 | output_init(&output); 1048 | controller_init(&controller); 1049 | 1050 | while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:E:F:A:M:h")) != -1) { 1051 | switch (opt) { 1052 | case 'd': 1053 | dongle.dev_index = verbose_device_search(optarg); 1054 | dev_given = 1; 1055 | break; 1056 | case 'f': 1057 | if (controller.freq_len >= FREQUENCIES_LIMIT) { 1058 | break;} 1059 | if (strchr(optarg, ':')) 1060 | {frequency_range(&controller, optarg);} 1061 | else 1062 | { 1063 | controller.freqs[controller.freq_len] = (uint32_t)atofs(optarg); 1064 | controller.freq_len++; 1065 | } 1066 | break; 1067 | case 'g': 1068 | dongle.gain = (int)(atof(optarg) * 10); 1069 | break; 1070 | case 'l': 1071 | demod.squelch_level = (int)atof(optarg); 1072 | break; 1073 | case 's': 1074 | demod.rate_in = (uint32_t)atofs(optarg); 1075 | demod.rate_out = (uint32_t)atofs(optarg); 1076 | break; 1077 | case 'r': 1078 | output.rate = (int)atofs(optarg); 1079 | demod.rate_out2 = (int)atofs(optarg); 1080 | break; 1081 | case 'o': 1082 | fprintf(stderr, "Warning: -o is very buggy\n"); 1083 | demod.post_downsample = (int)atof(optarg); 1084 | if (demod.post_downsample < 1 || demod.post_downsample > MAXIMUM_OVERSAMPLE) { 1085 | fprintf(stderr, "Oversample must be between 1 and %i\n", MAXIMUM_OVERSAMPLE);} 1086 | break; 1087 | case 't': 1088 | demod.conseq_squelch = (int)atof(optarg); 1089 | if (demod.conseq_squelch < 0) { 1090 | demod.conseq_squelch = -demod.conseq_squelch; 1091 | demod.terminate_on_squelch = 1; 1092 | } 1093 | break; 1094 | case 'p': 1095 | dongle.ppm_error = atoi(optarg); 1096 | custom_ppm = 1; 1097 | break; 1098 | case 'E': 1099 | if (strcmp("edge", optarg) == 0) { 1100 | controller.edge = 1;} 1101 | if (strcmp("dc", optarg) == 0) { 1102 | demod.dc_block = 1;} 1103 | if (strcmp("deemp", optarg) == 0) { 1104 | demod.deemph = 1;} 1105 | if (strcmp("direct", optarg) == 0) { 1106 | dongle.direct_sampling = 1;} 1107 | if (strcmp("offset", optarg) == 0) { 1108 | dongle.offset_tuning = 1;} 1109 | break; 1110 | case 'F': 1111 | demod.downsample_passes = 1; /* truthy placeholder */ 1112 | demod.comp_fir_size = atoi(optarg); 1113 | break; 1114 | case 'A': 1115 | if (strcmp("std", optarg) == 0) { 1116 | demod.custom_atan = 0;} 1117 | if (strcmp("fast", optarg) == 0) { 1118 | demod.custom_atan = 1;} 1119 | if (strcmp("lut", optarg) == 0) { 1120 | atan_lut_init(); 1121 | demod.custom_atan = 2;} 1122 | break; 1123 | case 'M': 1124 | if (strcmp("fm", optarg) == 0) { 1125 | demod.mode_demod = &fm_demod;} 1126 | if (strcmp("raw", optarg) == 0) { 1127 | demod.mode_demod = &raw_demod;} 1128 | if (strcmp("am", optarg) == 0) { 1129 | demod.mode_demod = &am_demod;} 1130 | if (strcmp("usb", optarg) == 0) { 1131 | demod.mode_demod = &usb_demod;} 1132 | if (strcmp("lsb", optarg) == 0) { 1133 | demod.mode_demod = &lsb_demod;} 1134 | if (strcmp("wbfm", optarg) == 0) { 1135 | controller.wb_mode = 1; 1136 | demod.mode_demod = &fm_demod; 1137 | demod.rate_in = 170000; 1138 | demod.rate_out = 170000; 1139 | demod.rate_out2 = 32000; 1140 | demod.custom_atan = 1; 1141 | //demod.post_downsample = 4; 1142 | demod.deemph = 1; 1143 | demod.squelch_level = 0;} 1144 | break; 1145 | case 'h': 1146 | default: 1147 | usage(); 1148 | break; 1149 | } 1150 | } 1151 | 1152 | /* quadruple sample_rate to limit to Δθ to ±π/2 */ 1153 | demod.rate_in *= demod.post_downsample; 1154 | 1155 | if (!output.rate) { 1156 | output.rate = demod.rate_out;} 1157 | 1158 | sanity_checks(); 1159 | 1160 | if (controller.freq_len > 1) { 1161 | demod.terminate_on_squelch = 0;} 1162 | 1163 | if (argc <= optind) { 1164 | output.filename = "-"; 1165 | } else { 1166 | output.filename = argv[optind]; 1167 | } 1168 | 1169 | ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH; 1170 | 1171 | if (!dev_given) { 1172 | dongle.dev_index = verbose_device_search("0"); 1173 | } 1174 | 1175 | if (dongle.dev_index < 0) { 1176 | exit(1); 1177 | } 1178 | 1179 | r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index); 1180 | if (r < 0) { 1181 | fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dongle.dev_index); 1182 | exit(1); 1183 | } 1184 | #ifndef _WIN32 1185 | sigact.sa_handler = sighandler; 1186 | sigemptyset(&sigact.sa_mask); 1187 | sigact.sa_flags = 0; 1188 | sigaction(SIGINT, &sigact, NULL); 1189 | sigaction(SIGTERM, &sigact, NULL); 1190 | sigaction(SIGQUIT, &sigact, NULL); 1191 | sigaction(SIGPIPE, &sigact, NULL); 1192 | #else 1193 | SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); 1194 | #endif 1195 | 1196 | if (demod.deemph) { 1197 | demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6))))); 1198 | } 1199 | 1200 | /* Set the tuner gain */ 1201 | if (dongle.gain == AUTO_GAIN) { 1202 | verbose_auto_gain(dongle.dev); 1203 | } else { 1204 | dongle.gain = nearest_gain(dongle.dev, dongle.gain); 1205 | verbose_gain_set(dongle.dev, dongle.gain); 1206 | } 1207 | 1208 | verbose_ppm_set(dongle.dev, dongle.ppm_error); 1209 | 1210 | if (strcmp(output.filename, "-") == 0) { /* Write samples to stdout */ 1211 | output.file = stdout; 1212 | #ifdef _WIN32 1213 | _setmode(_fileno(output.file), _O_BINARY); 1214 | #endif 1215 | } else { 1216 | output.file = fopen(output.filename, "wb"); 1217 | if (!output.file) { 1218 | fprintf(stderr, "Failed to open %s\n", output.filename); 1219 | exit(1); 1220 | } 1221 | } 1222 | 1223 | //r = rtlsdr_set_testmode(dongle.dev, 1); 1224 | 1225 | /* Reset endpoint before we start reading from it (mandatory) */ 1226 | verbose_reset_buffer(dongle.dev); 1227 | 1228 | pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller)); 1229 | usleep(100000); 1230 | pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output)); 1231 | pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod)); 1232 | pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle)); 1233 | 1234 | while (!do_exit) { 1235 | usleep(100000); 1236 | } 1237 | 1238 | if (do_exit) { 1239 | fprintf(stderr, "\nUser cancel, exiting...\n");} 1240 | else { 1241 | fprintf(stderr, "\nLibrary error %d, exiting...\n", r);} 1242 | 1243 | rtlsdr_cancel_async(dongle.dev); 1244 | pthread_join(dongle.thread, NULL); 1245 | safe_cond_signal(&demod.ready, &demod.ready_m); 1246 | pthread_join(demod.thread, NULL); 1247 | safe_cond_signal(&output.ready, &output.ready_m); 1248 | pthread_join(output.thread, NULL); 1249 | safe_cond_signal(&controller.hop, &controller.hop_m); 1250 | pthread_join(controller.thread, NULL); 1251 | 1252 | //dongle_cleanup(&dongle); 1253 | demod_cleanup(&demod); 1254 | output_cleanup(&output); 1255 | controller_cleanup(&controller); 1256 | 1257 | if (output.file != stdout) { 1258 | fclose(output.file);} 1259 | 1260 | rtlsdr_close(dongle.dev); 1261 | return r >= 0 ? r : -r; 1262 | } 1263 | 1264 | // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab 1265 | -------------------------------------------------------------------------------- /rtl_fm_python.c: -------------------------------------------------------------------------------- 1 | #include "rtl_fm.c" 2 | #include "getopt/getopt.h" 3 | #include "rtl-sdr.h" 4 | #include "convenience/convenience.h" 5 | 6 | #ifndef _WIN32 7 | struct sigaction sigact; 8 | #endif 9 | int r, opt; 10 | int dev_given = 0; 11 | int custom_ppm = 0; 12 | 13 | 14 | 15 | void lib_loop(){ 16 | while (!do_exit) { 17 | usleep(100000); 18 | } 19 | } 20 | 21 | 22 | void lib_init_first() 23 | { 24 | 25 | dongle_init(&dongle); 26 | demod_init(&demod); 27 | output_init(&output); 28 | controller_init(&controller); 29 | 30 | } 31 | 32 | void no_exit_usage(void) 33 | { 34 | fprintf(stderr, 35 | "rtl_fm, a simple narrow band FM demodulator for RTL2832 based DVB-T receivers\n\n" 36 | "Use:\trtl_fm -f freq [-options] [filename]\n" 37 | "\t-f frequency_to_tune_to [Hz]\n" 38 | "\t use multiple -f for scanning (requires squelch)\n" 39 | "\t ranges supported, -f 118M:137M:25k\n" 40 | "\t[-M modulation (default: fm)]\n" 41 | "\t fm, wbfm, raw, am, usb, lsb\n" 42 | "\t wbfm == -M fm -s 170k -o 4 -A fast -r 32k -l 0 -E deemp\n" 43 | "\t raw mode outputs 2x16 bit IQ pairs\n" 44 | "\t[-s sample_rate (default: 24k)]\n" 45 | "\t[-d device_index (default: 0)]\n" 46 | "\t[-g tuner_gain (default: automatic)]\n" 47 | "\t[-l squelch_level (default: 0/off)]\n" 48 | //"\t for fm squelch is inverted\n" 49 | //"\t[-o oversampling (default: 1, 4 recommended)]\n" 50 | "\t[-p ppm_error (default: 0)]\n" 51 | "\t[-E enable_option (default: none)]\n" 52 | "\t use multiple -E to enable multiple options\n" 53 | "\t edge: enable lower edge tuning\n" 54 | "\t dc: enable dc blocking filter\n" 55 | "\t deemp: enable de-emphasis filter\n" 56 | "\t direct: enable direct sampling\n" 57 | "\t offset: enable offset tuning\n" 58 | "\tfilename ('-' means stdout)\n" 59 | "\t omitting the filename also uses stdout\n\n" 60 | "Experimental options:\n" 61 | "\t[-r resample_rate (default: none / same as -s)]\n" 62 | "\t[-t squelch_delay (default: 10)]\n" 63 | "\t +values will mute/scan, -values will exit\n" 64 | "\t[-F fir_size (default: off)]\n" 65 | "\t enables low-leakage downsample filter\n" 66 | "\t size can be 0 or 9. 0 has bad roll off\n" 67 | "\t[-A std/fast/lut choose atan math (default: std)]\n" 68 | //"\t[-C clip_path (default: off)\n" 69 | //"\t (create time stamped raw clips, requires squelch)\n" 70 | //"\t (path must have '\%s' and will expand to date_time_freq)\n" 71 | //"\t[-H hop_fifo (default: off)\n" 72 | //"\t (fifo will contain the active frequency)\n" 73 | "\n" 74 | "Produces signed 16 bit ints, use Sox or aplay to hear them.\n" 75 | "\trtl_fm ... | play -t raw -r 24k -es -b 16 -c 1 -V1 -\n" 76 | "\t | aplay -r 24k -f S16_LE -t raw -c 1\n" 77 | "\t -M wbfm | play -r 32k ... \n" 78 | "\t -s 22050 | multimon -t raw /dev/stdin\n\n"); 79 | } 80 | 81 | void no_exit_sanity_checks(void) 82 | { 83 | if (controller.freq_len == 0) { 84 | fprintf(stderr, "Please specify a frequency.\n"); 85 | } 86 | 87 | if (controller.freq_len >= FREQUENCIES_LIMIT) { 88 | fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT); 89 | } 90 | 91 | if (controller.freq_len > 1 && demod.squelch_level == 0) { 92 | fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n"); 93 | } 94 | 95 | } 96 | 97 | 98 | 99 | void lib_process_args(int argc, char **argv) 100 | { 101 | 102 | 103 | while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:E:F:A:M:h")) != -1) { 104 | switch (opt) { 105 | case 'd': 106 | dongle.dev_index = verbose_device_search(optarg); 107 | dev_given = 1; 108 | break; 109 | case 'f': 110 | if (controller.freq_len >= FREQUENCIES_LIMIT) { 111 | break;} 112 | if (strchr(optarg, ':')) 113 | {frequency_range(&controller, optarg);} 114 | else 115 | { 116 | controller.freqs[controller.freq_len] = (uint32_t)atofs(optarg); 117 | controller.freq_len++; 118 | } 119 | break; 120 | case 'g': 121 | dongle.gain = (int)(atof(optarg) * 10); 122 | break; 123 | case 'l': 124 | demod.squelch_level = (int)atof(optarg); 125 | break; 126 | case 's': 127 | demod.rate_in = (uint32_t)atofs(optarg); 128 | demod.rate_out = (uint32_t)atofs(optarg); 129 | break; 130 | case 'r': 131 | output.rate = (int)atofs(optarg); 132 | demod.rate_out2 = (int)atofs(optarg); 133 | break; 134 | case 'o': 135 | fprintf(stderr, "Warning: -o is very buggy\n"); 136 | demod.post_downsample = (int)atof(optarg); 137 | if (demod.post_downsample < 1 || demod.post_downsample > MAXIMUM_OVERSAMPLE) { 138 | fprintf(stderr, "Oversample must be between 1 and %i\n", MAXIMUM_OVERSAMPLE);} 139 | break; 140 | case 't': 141 | demod.conseq_squelch = (int)atof(optarg); 142 | if (demod.conseq_squelch < 0) { 143 | demod.conseq_squelch = -demod.conseq_squelch; 144 | demod.terminate_on_squelch = 1; 145 | } 146 | break; 147 | case 'p': 148 | dongle.ppm_error = atoi(optarg); 149 | custom_ppm = 1; 150 | break; 151 | case 'E': 152 | if (strcmp("edge", optarg) == 0) { 153 | controller.edge = 1;} 154 | if (strcmp("dc", optarg) == 0) { 155 | demod.dc_block = 1;} 156 | if (strcmp("deemp", optarg) == 0) { 157 | demod.deemph = 1;} 158 | if (strcmp("direct", optarg) == 0) { 159 | dongle.direct_sampling = 1;} 160 | if (strcmp("offset", optarg) == 0) { 161 | dongle.offset_tuning = 1;} 162 | break; 163 | case 'F': 164 | demod.downsample_passes = 1; /* truthy placeholder */ 165 | demod.comp_fir_size = atoi(optarg); 166 | break; 167 | case 'A': 168 | if (strcmp("std", optarg) == 0) { 169 | demod.custom_atan = 0;} 170 | if (strcmp("fast", optarg) == 0) { 171 | demod.custom_atan = 1;} 172 | if (strcmp("lut", optarg) == 0) { 173 | atan_lut_init(); 174 | demod.custom_atan = 2;} 175 | break; 176 | case 'M': 177 | if (strcmp("fm", optarg) == 0) { 178 | demod.mode_demod = &fm_demod;} 179 | if (strcmp("raw", optarg) == 0) { 180 | demod.mode_demod = &raw_demod;} 181 | if (strcmp("am", optarg) == 0) { 182 | demod.mode_demod = &am_demod;} 183 | if (strcmp("usb", optarg) == 0) { 184 | demod.mode_demod = &usb_demod;} 185 | if (strcmp("lsb", optarg) == 0) { 186 | demod.mode_demod = &lsb_demod;} 187 | if (strcmp("wbfm", optarg) == 0) { 188 | controller.wb_mode = 1; 189 | demod.mode_demod = &fm_demod; 190 | demod.rate_in = 170000; 191 | demod.rate_out = 170000; 192 | demod.rate_out2 = 32000; 193 | demod.custom_atan = 1; 194 | //demod.post_downsample = 4; 195 | demod.deemph = 1; 196 | demod.squelch_level = 0;} 197 | break; 198 | case 'h': 199 | default: 200 | no_exit_usage(); 201 | break; 202 | } 203 | } 204 | 205 | 206 | /* quadruple sample_rate to limit to Δθ to ±π/2 */ 207 | demod.rate_in *= demod.post_downsample; 208 | 209 | if (!output.rate) { 210 | output.rate = demod.rate_out;} 211 | 212 | no_exit_sanity_checks(); 213 | 214 | if (controller.freq_len > 1) { 215 | demod.terminate_on_squelch = 0;} 216 | 217 | if (argc <= optind) { 218 | output.filename = "-"; 219 | } else { 220 | output.filename = argv[optind]; 221 | } 222 | 223 | ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH; 224 | 225 | if (!dev_given) { 226 | dongle.dev_index = verbose_device_search("0"); 227 | } 228 | 229 | if (dongle.dev_index < 0) { 230 | fprintf(stderr, "Device index less than zero.\n"); 231 | } 232 | 233 | 234 | } 235 | 236 | void lib_output_open() 237 | { 238 | if (strcmp(output.filename, "-") == 0) { /* Write samples to stdout */ 239 | output.file = stdout; 240 | #ifdef _WIN32 241 | _setmode(_fileno(output.file), _O_BINARY); 242 | #endif 243 | } else { 244 | output.file = fopen(output.filename, "wb"); 245 | if (!output.file) { 246 | fprintf(stderr, "Failed to open %s\n", output.filename); 247 | } 248 | } 249 | } 250 | 251 | void lib_input_open() 252 | { 253 | r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index); 254 | if (r < 0) { 255 | fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dongle.dev_index); 256 | } 257 | 258 | } 259 | 260 | void lib_register_signal_handler(){ 261 | #ifndef _WIN32 262 | sigact.sa_handler = sighandler; 263 | sigemptyset(&sigact.sa_mask); 264 | sigact.sa_flags = 0; 265 | sigaction(SIGINT, &sigact, NULL); 266 | sigaction(SIGTERM, &sigact, NULL); 267 | sigaction(SIGQUIT, &sigact, NULL); 268 | sigaction(SIGPIPE, &sigact, NULL); 269 | #else 270 | SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); 271 | #endif 272 | 273 | } 274 | 275 | void lib_go() 276 | { 277 | if (demod.deemph) { 278 | demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6))))); 279 | } 280 | if (dongle.gain == AUTO_GAIN) { 281 | verbose_auto_gain(dongle.dev); 282 | } else { 283 | dongle.gain = nearest_gain(dongle.dev, dongle.gain); 284 | verbose_gain_set(dongle.dev, dongle.gain); 285 | } 286 | verbose_ppm_set(dongle.dev, dongle.ppm_error); 287 | verbose_reset_buffer(dongle.dev); 288 | pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller)); 289 | usleep(100000); 290 | pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output)); 291 | pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod)); 292 | pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle)); 293 | 294 | } 295 | 296 | void lib_print_frequency(){ 297 | fprintf(stderr,"Frequency: %i.\n",controller.freqs[controller.freq_now]); 298 | } 299 | 300 | uint32_t lib_get_frequency(){ 301 | return controller.freqs[controller.freq_now]; 302 | } 303 | 304 | int lib_get_s_level(){ 305 | int s = rms(demod.lowpassed, demod.lp_len, 1); 306 | if (s > 0) { 307 | return s; 308 | } else { 309 | return 0; 310 | } 311 | } 312 | 313 | void lib_set_frequency(uint32_t new_frequency){ 314 | optimal_settings(new_frequency, demod.rate_in); 315 | controller.freqs[controller.freq_now] = new_frequency; 316 | rtlsdr_set_center_freq(dongle.dev, dongle.freq); 317 | } 318 | 319 | void lib_set_squelch_level(int l) { 320 | demod.squelch_level = l; 321 | } 322 | 323 | void util_set_wbfm_options(){ 324 | controller.wb_mode = 1; 325 | demod.custom_atan = 1; 326 | demod.deemph = 1; 327 | demod.squelch_level = 0; 328 | } 329 | 330 | void util_set_other_demod_options(){ 331 | controller.wb_mode = 0; 332 | demod.custom_atan = 0; 333 | demod.deemph = 0; 334 | demod.squelch_level = 0; 335 | } 336 | 337 | void lib_set_demod_wbfm(){ 338 | util_set_wbfm_options(); 339 | demod.mode_demod = &fm_demod; 340 | } 341 | 342 | void lib_set_demod_fm(){ 343 | util_set_other_demod_options(); 344 | demod.mode_demod = &fm_demod; 345 | } 346 | 347 | void lib_set_demod_am(){ 348 | util_set_other_demod_options(); 349 | demod.mode_demod = &am_demod; 350 | } 351 | 352 | void lib_set_demod_lsb(){ 353 | util_set_other_demod_options(); 354 | demod.mode_demod = &lsb_demod; 355 | } 356 | 357 | void lib_set_demod_usb(){ 358 | util_set_other_demod_options(); 359 | demod.mode_demod = &usb_demod; 360 | } 361 | 362 | void lib_set_demod_raw(){ 363 | util_set_other_demod_options(); 364 | demod.mode_demod = &raw_demod; 365 | } 366 | 367 | 368 | char lib_get_demod_mode(){ 369 | if (demod.mode_demod==&fm_demod){ 370 | if (controller.wb_mode==1){ 371 | return 'w'; 372 | } else { 373 | return 'f'; 374 | } 375 | } 376 | if (demod.mode_demod==&am_demod){ 377 | return 'a'; 378 | } 379 | if (demod.mode_demod==&lsb_demod){ 380 | return 'l'; 381 | } 382 | if (demod.mode_demod==&usb_demod){ 383 | return 'u'; 384 | } 385 | if (demod.mode_demod==&raw_demod){ 386 | return 'r'; 387 | } 388 | } 389 | 390 | void lib_set_auto_gain(){ 391 | r = rtlsdr_set_tuner_gain_mode(dongle.dev, 0); 392 | dongle.gain = -100; 393 | } 394 | 395 | void lib_set_gain(int g){ 396 | if (g >= 0){ 397 | int ng; 398 | rtlsdr_set_tuner_gain_mode(dongle.dev, 1); 399 | ng = nearest_gain(dongle.dev,g*10); 400 | rtlsdr_set_tuner_gain(dongle.dev, ng); 401 | dongle.gain=ng; 402 | } 403 | } 404 | 405 | void lib_set_real_gain(int g){ 406 | if (g > -100){ 407 | rtlsdr_set_tuner_gain_mode(dongle.dev, 1); 408 | rtlsdr_set_tuner_gain(dongle.dev, g); 409 | dongle.gain=g; 410 | } else { 411 | lib_set_auto_gain(); 412 | } 413 | } 414 | 415 | int lib_get_tuner_gains_count(){ 416 | int count,r; 417 | rtlsdr_set_tuner_gain_mode(dongle.dev, 1); 418 | return rtlsdr_get_tuner_gains(dongle.dev, NULL); 419 | } 420 | 421 | void lib_get_tuner_gains(int *gains){ 422 | int count,r; 423 | r = rtlsdr_set_tuner_gain_mode(dongle.dev, 1); 424 | count = rtlsdr_get_tuner_gains(dongle.dev, gains); 425 | } 426 | 427 | int lib_get_gain(){ 428 | if (dongle.gain == -100){ 429 | return -100; 430 | } else { 431 | return rtlsdr_get_tuner_gain(dongle.dev); 432 | } 433 | } 434 | 435 | 436 | uint32_t lib_frequency_convert(char *s){ 437 | return (uint32_t)atofs(s); 438 | } 439 | 440 | 441 | void lib_stop() 442 | { 443 | sighandler(0); 444 | rtlsdr_cancel_async(dongle.dev); 445 | pthread_join(dongle.thread, NULL); 446 | safe_cond_signal(&demod.ready, &demod.ready_m); 447 | pthread_join(demod.thread, NULL); 448 | safe_cond_signal(&output.ready, &output.ready_m); 449 | safe_cond_signal(&controller.hop, &controller.hop_m); 450 | pthread_join(controller.thread, NULL); 451 | demod_cleanup(&demod); 452 | output_cleanup(&output); 453 | controller_cleanup(&controller); 454 | 455 | } 456 | 457 | void lib_output_close() 458 | { 459 | if (output.file != stdout) { 460 | fclose(output.file);} 461 | } 462 | 463 | -------------------------------------------------------------------------------- /rtl_fm_python_common.py: -------------------------------------------------------------------------------- 1 | # rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver 2 | # Copyright (C) 2012 by Steve Markgraf 3 | # Copyright (C) 2012 by Hoernchen 4 | # Copyright (C) 2012 by Kyle Keen 5 | # Copyright (C) 2013 by Elias Oenal 6 | # Copyright (C) 2014 by Thomas Winningham 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | 21 | from __future__ import print_function 22 | import ctypes, sys 23 | from time import sleep 24 | from threading import Thread 25 | 26 | printstderr = lambda x : print(x,file=sys.stderr) 27 | 28 | 29 | fm = ctypes.CDLL('./rtl_fm_python.so') 30 | get_s_level = fm.lib_get_s_level 31 | get_frequency = fm.lib_get_frequency 32 | set_demod_fm = fm.lib_set_demod_fm 33 | set_demod_wbfm = fm.lib_set_demod_wbfm 34 | set_demod_am = fm.lib_set_demod_am 35 | set_demod_lsb = fm.lib_set_demod_lsb 36 | set_demod_usb = fm.lib_set_demod_usb 37 | set_demod_raw = fm.lib_set_demod_raw 38 | set_frequency = lambda f : fm.lib_set_frequency(ctypes.c_uint32(f)) 39 | set_squelch = lambda l : fm.lib_set_squelch_level(ctypes.c_int(l)) 40 | get_demod = lambda : chr(fm.lib_get_demod_mode()) 41 | str_to_freq = fm.lib_frequency_convert 42 | 43 | def process_args(l): 44 | c=len(l)+1 45 | argc=ctypes.c_int(c) 46 | argv_var=ctypes.c_char_p *c 47 | argu = ['rtl_fm'] + l 48 | argv=argv_var(*argu) 49 | return (argc,argv) 50 | 51 | def mag(value, e, suffix): 52 | t = float('1e%s' % e) 53 | i = int(value / float(t)) 54 | r = int(value % float(t)) 55 | r = str(r).rjust(e,'0') 56 | s = "%s.%s" % (i,r) 57 | while (s[-1]=="0" or s[-1]=="."): 58 | if s[-1]==".": 59 | s=s[:-1] 60 | break 61 | s=s[:-1] 62 | return s + suffix 63 | 64 | def freq_to_str(f): 65 | if f >= 1000000000: 66 | return mag(f,9,'G') 67 | if f >= 1000000: 68 | return mag(f,6,'M') 69 | if f >= 1000: 70 | return mag(f,3,'K') 71 | return str(f) 72 | 73 | set_freq_human = lambda f : set_frequency(str_to_freq(f)) 74 | get_freq_human = lambda : freq_to_str(get_frequency()) 75 | 76 | def set_demod(c): 77 | if c=='w' : set_demod_wbfm() 78 | if c=='f' : set_demod_fm() 79 | if c=='a' : set_demod_am() 80 | if c=='l' : set_demod_lsb() 81 | if c=='u' : set_demod_usb() 82 | if c=='r' : set_demod_raw() 83 | 84 | def get_gains(): 85 | c=fm.lib_get_tuner_gains_count() 86 | b=(ctypes.c_int * c)() 87 | fm.lib_get_tuner_gains(b) 88 | return list(b) 89 | 90 | def set_gain(g): 91 | fm.lib_set_real_gain(g) 92 | 93 | def get_gain(): 94 | return fm.lib_get_gain() 95 | 96 | def get_auto_gain(): 97 | return get_gain()==-100 98 | 99 | def set_gain_human(g): 100 | fm.lib_set_gain(g) 101 | 102 | def set_auto_gain(): 103 | fm.lib_set_auto_gain() 104 | 105 | 106 | 107 | def rtl_fm(args=["--help"]): 108 | fm.main(*process_args(args)) 109 | 110 | def rtl_fm_setup_and_go(args): 111 | fm.lib_init_first() 112 | fm.lib_process_args(*process_args(args)) 113 | fm.lib_input_open() 114 | fm.lib_output_open() 115 | fm.lib_go() 116 | 117 | def rtl_fm_loop(): 118 | fm.lib_loop() 119 | 120 | def rtl_fm_finish(): 121 | fm.lib_stop() 122 | fm.lib_output_close() 123 | 124 | 125 | def rtl_fm_wrapped(args=["--help"]): 126 | rtl_fm_setup_and_go(args) 127 | try: 128 | while True: 129 | sleep(0.01) 130 | except KeyboardInterrupt: 131 | rtl_fm_finish() 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /rtl_fm_python_thread.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -i 2 | 3 | # rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver 4 | # Copyright (C) 2012 by Steve Markgraf 5 | # Copyright (C) 2012 by Hoernchen 6 | # Copyright (C) 2012 by Kyle Keen 7 | # Copyright (C) 2013 by Elias Oenal 8 | # Copyright (C) 2014 by Thomas Winningham 9 | # 10 | # This program is free software: you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation, either version 2 of the License, or 13 | # (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | 23 | 24 | from rtl_fm_python_common import * 25 | 26 | running = True 27 | queue=[] 28 | 29 | def rtl_fm_thread(args): 30 | rtl_fm_setup_and_go(args) 31 | while running==True: 32 | if len(queue) > 0: 33 | queue.pop()() 34 | sleep(1) 35 | rtl_fm_finish() 36 | 37 | def make_rtl_fm_thread(args=sys.argv[1:],block=False): 38 | t = Thread(target=rtl_fm_thread,args=(args,)) 39 | t.daemon=True 40 | t.start() 41 | if block: 42 | try: 43 | while True: 44 | sleep(1) 45 | except KeyboardInterrupt: 46 | running = False 47 | 48 | def stop_thread(): 49 | running = False 50 | 51 | 52 | if __name__ == "__main__": 53 | make_rtl_fm_thread(block=False) 54 | 55 | -------------------------------------------------------------------------------- /rtl_fm_python_web.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver 3 | # Copyright (C) 2012 by Steve Markgraf 4 | # Copyright (C) 2012 by Hoernchen 5 | # Copyright (C) 2012 by Kyle Keen 6 | # Copyright (C) 2013 by Elias Oenal 7 | # Copyright (C) 2014 by Thomas Winningham 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | from flask import Flask, jsonify, url_for, redirect 23 | from rtl_fm_python_thread import * 24 | 25 | make_rtl_fm_thread(block=False) 26 | 27 | app = Flask(__name__) 28 | 29 | @app.route('/') 30 | def web_root(): 31 | return redirect(url_for('static', filename='index.html')) 32 | 33 | @app.route('/state') 34 | def web_state(): 35 | return jsonify( 36 | { 37 | 's_level' : get_s_level(), 38 | 'freq_s' : get_freq_human(), 39 | 'freq_i' : get_frequency(), 40 | 'mod' : get_demod(), 41 | 'gain' : get_gain(), 42 | 'autogain' : get_auto_gain() 43 | }) 44 | 45 | @app.route('/frequency/') 46 | def web_set_frequency(f): 47 | set_frequency(f) 48 | return web_state() 49 | 50 | @app.route('/frequency/human/') 51 | def web_set_human_frequency(f): 52 | set_freq_human(str(f)) 53 | return web_state() 54 | 55 | @app.route('/demod/') 56 | def web_set_demod(c): 57 | set_demod(str(c)) 58 | return web_state() 59 | 60 | @app.route('/gain/') 61 | def web_set_gain(g): 62 | gain = int(str(g)) 63 | set_gain(gain) 64 | return web_state() 65 | 66 | @app.route('/gain/human/') 67 | def web_set_gain_human(g): 68 | gain = int(str(g)) 69 | set_gain_human(gain) 70 | return web_state() 71 | 72 | @app.route('/gain/auto') 73 | def web_set_auto_gain(): 74 | set_auto_gain() 75 | return web_state() 76 | 77 | @app.route('/gain/list') 78 | def web_get_gain_list(): 79 | l=get_gains() 80 | return jsonify({'gains':l}) 81 | 82 | if __name__ == '__main__': 83 | app.run(host='0.0.0.0',port=10100) 84 | stop_thread() 85 | 86 | -------------------------------------------------------------------------------- /start_web.sh: -------------------------------------------------------------------------------- 1 | ./rtl_fm_python_web.py -M wbfm -f 101.1M - |aplay -r 32000 -f S16_LE -t raw -c 1 2 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /static/rtl_fm_python.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var ValuePair = React.createClass({ 4 | render: function(){ 5 | return ( 6 |
{this.props.key} {': '} {this.props.value}
7 | ) 8 | } 9 | }); 10 | 11 | var SignalMeter = React.createClass({ 12 | render: function(){ 13 | var signalStyle={ 14 | 'background-color': "black", 15 | width: this.props.signal, 16 | height: '5px', 17 | display: "block" 18 | } 19 | return (
20 |
{this.props.signal}
21 |
22 |
23 | ) 24 | } 25 | }); 26 | 27 | var FrequencyForm = React.createClass({ 28 | handleSubmit: function(){ 29 | var freq=this.refs.freq.getDOMNode().value.trim(); 30 | $.ajax({ 31 | url: '/frequency/human/' + freq, 32 | dataType: 'json' 33 | }); 34 | return false; 35 | }, 36 | render: function(){ 37 | return( 38 |
39 | 40 |
41 | ); 42 | } 43 | } 44 | ); 45 | 46 | var FrequencyDisplay = React.createClass({ 47 | render: function(){ 48 | return ( 49 |

{this.props.freq}

50 | ) 51 | } 52 | }); 53 | 54 | var AutoGainEnabled = React.createClass({ 55 | handleSubmit: function(){ 56 | var autogain=this.refs.autogain.getDOMNode().value.trim(); 57 | if (autogain=='on'){ 58 | $.ajax({ 59 | url: '/gain/auto', 60 | dataType: 'json' 61 | }); 62 | } 63 | }, 64 | render: function(){ 65 | return ( 66 |
67 | 68 | Auto 69 |
70 | ) 71 | } 72 | }); 73 | 74 | var GainOptions = React.createClass({ 75 | handleSubmit: function(){ 76 | var gain=this.refs.gain.getDOMNode().value.trim(); 77 | $.ajax({ 78 | url: '/gain/' + gain, 79 | dataType: 'json' 80 | }); 81 | return false; 82 | }, 83 | render: function(){ 84 | if (this.props.autogain) { 85 | this.props.gain=this.props.gains[0]; 86 | } 87 | var createOption = function(v,i){ 88 | return ; 89 | } 90 | return ( 91 |
92 | 95 | Gain 96 |
97 | ) 98 | } 99 | }); 100 | 101 | var ModulationOption = React.createClass({ 102 | handleSubmit: function(){ 103 | var mod=this.refs.mod.getDOMNode().value.trim(); 104 | $.ajax({ 105 | url: '/demod/' + mod, 106 | dataType: 'json' 107 | }); 108 | return false; 109 | }, 110 | render: function(){ 111 | return (
112 | 122 | Modulation 123 |
) 124 | } 125 | }); 126 | 127 | var dongle_gains=[]; 128 | 129 | var State = React.createClass({ 130 | refreshData : function(){ 131 | $.ajax({ 132 | url: '/state', 133 | dataType: 'json', 134 | success: function(data) { 135 | this.setState({data: data}); 136 | document.title=data.freq_s; 137 | }.bind(this), 138 | error: function(xhr, status, err) { 139 | console.error("/state", status, err.toString()); 140 | }.bind(this) 141 | }); 142 | }, 143 | refreshGainList : function(){ 144 | $.ajax({ 145 | url: '/gain/list', 146 | dataType: 'json', 147 | success: function(data) { 148 | dongle_gains=data.gains; 149 | }.bind(this), 150 | }); 151 | }, 152 | getInitialState: function() { 153 | return {data:[]}; 154 | }, 155 | componentDidMount: function() { 156 | this.interval = setInterval(this.refreshData, 500); 157 | this.refreshGainList(); 158 | }, 159 | componentWillUnmount: function() { 160 | clearInterval(this.interval); 161 | }, 162 | render: function() { 163 | return ( 164 |
165 | 166 | 167 |
168 | 169 | 170 | 171 | 172 |
173 | ); 174 | } 175 | }); 176 | 177 | React.renderComponent(, $("#state")[0]); 178 | --------------------------------------------------------------------------------