兴趣内容
相关好价
全部产品
精选
最新
RTL-SDR on Mac OS Part 1: Install & Setup
RTL-SDR on Mac OS Part 1: Install & SetupThis project walks through how to get started with the RTL-SDR on Mac OS 14BeginnerProtip1 hour7,302Things used in this projectHardware componentsRTL-SDR RTL2832U DVB-T Tuner Dongle×1Software apps and online servicesGNU Radio Story I moved recently, during which time I came across my original RTL-SDR dongle plus some variants I had found on Amazon from other vendors. I had originally purchased them to compare and play around with some waveform design, but they got shoved in a drawer and forgotten about however many years back when I bought them. Now that I have found them again with a few more years of SDR development behind me, I wanted to do some tutorial content to show how these devices are great for learning quite a bit about waveform design and signal processing in SDRs. To start, I just want to make sure my setup works. That entails getting some interface software installed on my host machine, then I'd like to try to listen to some local FM radio stations to verify my RTL-SDR dongle works properly. Since I'm using Mac OS, the best option for interface software is GNU Radio with the GNU OsmoSDR device library installed which contains the RTL-SDR drivers and GNU Radio flowgraph blocks. It's also super easy to throw together a flowgraph to receive/demodulate FM radio stations in GNU Radio. Mac OS Environment Prep If you haven't already, install Xcode from the Mac App Store it's free, open it to be able to agree to the T&C pop-ups, and download the base environment stuff for Mac OS. Then open a terminal to install the Apple Command Line Developer Tools: ~ % xcode-select --install If you already have Xcode and the Apple Command Line Developer Tools installed, make sure Xcode is fully up-to-date and repoint the Apple Command Line Developer Tools it with the following command they seem to cache the old version of Xcode and it'll confuse any package managers trying to using it: ~ % sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer If you haven't picked up already, a package manager is required to install the RTL-SDR software itself and most other SDR-related software to use the RTL-SDR with such as GNU Radio. The best two I've found so far for Mac OS are MacPorts and Homebrew. Install GNU Radio & RTL-SDR Software Unfortunately, I discovered that both the GNU Radio and GNU OsmoSDR gr-osmosdr packages were severely out-of-date in both the MacPorts and Homebrew repositories at the time of writing, the repositories are at v3.8.5.0. I took a stab and installed them anyways; because if it still works for my application, I don't really care if it's out-of-date. However, I found that on Mac OS version 14 Ventura the GNU Radio v3.8.5.0 does not install properly and the entire gr-digital library shows as missing which means no digital modulation line FSK, PSK, OFDM, etc. This is when I went back to GNU Radio's website and noticed their recommended installation method had changed it had been MacPorts for years which is why I didn't check initially. The new recommended installation method for Mac OS and Windows for GNU Radio is to now use the Radioconda installer.Radioconda is a collection of open-source SDR packages/libraries bundled together and installed with the Conda package manager. This is super handy because GNU Radio plus all of the devices libraries such as the RTL SDR drivers - full list here get installed all at once. It appears that going forward, this Conda repository is what will be updated with the latest versions of GNU Radio and the corresponding SDR device libraries. So I'm doubtful that the repositories in MacPorts or Homebrew are going to be updated. There is a dmg package installer for both Intel-based Macs and Apple Silicon Macs. I'm using an Intel-based but once installed, everything works the same way regardless. Simply run the dmg package installer and follow the prompts to install: After installation, open a terminal window and launch the GNU Radio Companion unfortunately there is no icon in Applications folder for GNU Radio with the Radioconda installer: ~ % gnuradio-companion Connect to RTL-SDR Dongle As a sanity check before trying to connect to the RTL-SDR dongle from GNU Radio Companion, I opened a separate terminal window and typed rtl followed by the tab key twice to verify the RTL-SDR device libraries were installed/available: ~ % rtl_ rtl_adsb rtl_biast rtl_eeprom rtl_fm rtl_power rtl_sdr rtl_tcp rtl_test The rtl_test command is perfect for validating a successful connection with the RTL-SDR dongle: ~ % rtl_test Then the rtl_power command is handy to see the received power levels across a specified range of frequencies. So I did this for the typical FM radio frequency range/spacing: ~ % rtl_power -f 88M:108M:125k Which is a great reminder that antenna selection is extremely important based on the target application... Antenna Selection for FM Radio Station Receiver The most basic thing to know about antenna selection for any RF application like SDR, is that the antenna's dimensions are a direct result of the target frequency of your application. The ideal length of type of antenna for an application is 1/4 one-quarter the wavelength of the target frequency. This is referred to as the "resonant length" of an antenna for a specific frequency. When a sine wave starts out at the input of the antenna the feedpoint, it has one polarity. When it hits the end of the antenna 1/4 of a cycle later, it is reflected back in the opposite polarity and makes it back to the feedpoint another 1/4 of a cycle later. Since that reflected wave in the opposite polarity makes it back to the feedpoint 1/2 of a cycle later, it now matches the polarity of the 2nd half of the cycle of the original sine wave is being driven into it. Therefore the current at the antenna is "reinforced", giving it a low impedance. In other words, it can drive larger current at a lower voltage; and the larger the current, the stronger the EMI field is that radiates from the antenna. This also gets the antenna to that standard 50Ω mark and minimize the amount of complex impedance matching that needs to be done to avoid loss. I have a simple dipole antenna that's adjustable in length so I can use it across the VHF band of frequencies by simply adjusting each side to 1/4 of the target frequency wavelength. The formula to calculate wavelength of a given frequency is simply the speed of light divided by that frequency. There are plenty of online wavelength calculators like this one. Then divide the wavelength by four to get the 1/4 wavelength! FM Radio Station Receiver in GNU Radio I've attached my flowgraph below for a simple FM radio receiver, and it's very similar to examples that have been put out in the past: I'm going to save the signal processing details for the next write-up as I feel it deserves a more in-depth explanation. But for now, the main objective is just to verify my RTL-SDR dongle works and I've selected the right antenna. So I simply took each radio station's frequency, calculated the 1/4 wavelength, set my dipole to the right length, and ran the flowgragh: I looked up my local radio stations using radio-locator.com since they tell me the broadcast distance, and I experimented with tuning into different stations closer and further away from me to demonstrate what signal integrity issues/symptoms occurred due to simply being out-of-range of a given station's broadcast versus what issues occurred due to improper signal processing in my flowgraph. It was interesting to see how quickly it became obvious what sounds in the audio were due to improper signal processing voices sounded distorted or the wrong speed versus the signal just being too weak and being taken over by noise voices sounded normal, but just under a layer of static. This is also a great exercise to see how changing the length of the antenna impacts the signal quality in case you weren't convinced of the 1/4 wavelength figure. And that's the RTL-SDR with GNU Radio on Mac! Stay tuned for future write-ups! Code rtlsdr_tester.grc Plain text options: parameters: author: whitneyknitter catch_exceptions: 'True' category: '[GRC Hier Blocks]' cmake_opt: '' comment: '' copyright: '' description: '' gen_cmake: 'On' gen_linking: dynamic generate_options: qt_gui hier_block_src_path: '.:' id: rtlsdr_tester max_nouts: '0' output_language: python placement: 0,0 qt_qss_theme: '' realtime_scheduling: '' run: 'True' run_command: '{python} -u {filename}' run_options: prompt sizing_mode: fixed thread_safe_setters: '' title: RTL-SDR Tester window_size: 1000,1000 states: bus_sink: false bus_source: false bus_structure: null coordinate: [8, 8] rotation: 0 state: enabled blocks: - name: audio_decimation id: variable parameters: comment: '' value: '10' states: bus_sink: false bus_source: false bus_structure: null coordinate: [256, 64.0] rotation: 0 state: enabled - name: cutoff_freq id: variable parameters: comment: '' value: 270e3 states: bus_sink: false bus_source: false bus_structure: null coordinate: [344, 8.0] rotation: 0 state: enabled - name: decimation_factor id: variable parameters: comment: '' value: intsamp_rate/quad_rate states: bus_sink: false bus_source: false bus_structure: null coordinate: [256, 8.0] rotation: 0 state: enabled - name: fm_freq id: variable parameters: comment: '' value: 97.1e6 states: bus_sink: false bus_source: false bus_structure: null coordinate: [192, 8.0] rotation: 0 state: enabled - name: quad_rate id: variable parameters: comment: '' value: '512000' states: bus_sink: false bus_source: false bus_structure: null coordinate: [464, 8.0] rotation: 0 state: enabled - name: samp_rate id: variable parameters: comment: '' value: '2048000' states: bus_sink: false bus_source: false bus_structure: null coordinate: [120, 8.0] rotation: 0 state: enabled - name: volume id: variable parameters: comment: '' value: '1' states: bus_sink: false bus_source: false bus_structure: null coordinate: [408, 8.0] rotation: 0 state: enabled - name: analog_wfm_rcv_0 id: analog_wfm_rcv parameters: affinity: '' alias: '' audio_decimation: audio_decimation comment: '' maxoutbuf: '0' minoutbuf: '0' quad_rate: quad_rate states: bus_sink: false bus_source: false bus_structure: null coordinate: [616, 72.0] rotation: 0 state: enabled - name: audio_sink_0 id: audio_sink parameters: affinity: '' alias: '' comment: '' device_name: '' num_inputs: '1' ok_to_block: 'True' samp_rate: '48000' states: bus_sink: false bus_source: false bus_structure: null coordinate: [1112, 76.0] rotation: 0 state: enabled - name: blocks_multiply_const_vxx_0 id: blocks_multiply_const_vxx parameters: affinity: '' alias: '' comment: '' const: '1' maxoutbuf: '0' minoutbuf: '0' type: float vlen: '1' states: bus_sink: false bus_source: false bus_structure: null coordinate: [960, 76.0] rotation: 0 state: enabled - name: low_pass_filter_0 id: low_pass_filter parameters: affinity: '' alias: '' beta: '6.76' comment: '' cutoff_freq: cutoff_freq decim: '1' gain: '1' interp: '1' maxoutbuf: '0' minoutbuf: '0' samp_rate: samp_rate type: fir_filter_ccf width: 900e3 win: window.WIN_BLACKMAN states: bus_sink: false bus_source: false bus_structure: null coordinate: [408, 136.0] rotation: 0 state: enabled - name: qtgui_freq_sink_x_0 id: qtgui_freq_sink_x parameters: affinity: '' alias: '' alpha1: '1.0' alpha10: '1.0' alpha2: '1.0' alpha3: '1.0' alpha4: '1.0' alpha5: '1.0' alpha6: '1.0' alpha7: '1.0' alpha8: '1.0' alpha9: '1.0' autoscale: 'False' average: '0.05' axislabels: 'True' bw: samp_rate color1: '"blue"' color10: '"dark blue"' color2: '"red"' color3: '"green"' color4: '"black"' color5: '"cyan"' color6: '"magenta"' color7: '"yellow"' color8: '"dark red"' color9: '"dark green"' comment: '' ctrlpanel: 'False' fc: fm_freq fftsize: '4096' freqhalf: 'True' grid: 'False' gui_hint: '' label: Relative Gain label1: '' label10: '''''' label2: '''''' label3: '''''' label4: '''''' label5: '''''' label6: '''''' label7: '''''' label8: '''''' label9: '''''' legend: 'True' maxoutbuf: '0' minoutbuf: '0' name: '""' nconnections: '1' norm_window: 'False' showports: 'False' tr_chan: '0' tr_level: '0.0' tr_mode: qtgui.TRIG_MODE_FREE tr_tag: '""' type: complex units: dB update_time: '0.10' width1: '1' width10: '1' width2: '1' width3: '1' width4: '1' width5: '1' width6: '1' width7: '1' width8: '1' width9: '1' wintype: window.WIN_BLACKMAN_hARRIS ymax: '10' ymin: '-140' states: bus_sink: false bus_source: false bus_structure: null coordinate: [616, 156.0] rotation: 0 state: enabled - name: qtgui_waterfall_sink_x_0 id: qtgui_waterfall_sink_x parameters: affinity: '' alias: '' alpha1: '1.0' alpha10: '1.0' alpha2: '1.0' alpha3: '1.0' alpha4: '1.0' alpha5: '1.0' alpha6: '1.0' alpha7: '1.0' alpha8: '1.0' alpha9: '1.0' axislabels: 'True' bw: samp_rate color1: '0' color10: '0' color2: '0' color3: '0' color4: '0' color5: '0' color6: '0' color7: '0' color8: '0' color9: '0' comment: '' fc: fm_freq fftsize: '4096' freqhalf: 'True' grid: 'False' gui_hint: '' int_max: '10' int_min: '-140' label1: '' label10: '' label2: '' label3: '' label4: '' label5: '' label6: '' label7: '' label8: '' label9: '' legend: 'True' maxoutbuf: '0' minoutbuf: '0' name: '""' nconnections: '1' showports: 'False' type: complex update_time: '0.10' wintype: window.WIN_BLACKMAN_hARRIS states: bus_sink: false bus_source: false bus_structure: null coordinate: [616, 252.0] rotation: 0 state: enabled - name: rational_resampler_xxx_0 id: rational_resampler_xxx parameters: affinity: '' alias: '' comment: '' decim: decimation_factor fbw: '0' interp: '1' maxoutbuf: '0' minoutbuf: '0' taps: '[]' type: ccc states: bus_sink: false bus_source: false bus_structure: null coordinate: [256, 152.0] rotation: 0 state: enabled - name: rational_resampler_xxx_1 id: rational_resampler_xxx parameters: affinity: '' alias: '' comment: '' decim: '50' fbw: '0' interp: '48' maxoutbuf: '0' minoutbuf: '0' taps: '[]' type: fff states: bus_sink: false bus_source: false bus_structure: null coordinate: [792, 64.0] rotation: 0 state: enabled - name: rtlsdr_source_0 id: rtlsdr_source parameters: affinity: '' alias: '' ant0: '' ant1: '' ant10: '' ant11: '' ant12: '' ant13: '' ant14: '' ant15: '' ant16: '' ant17: '' ant18: '' ant19: '' ant2: '' ant20: '' ant21: '' ant22: '' ant23: '' ant24: '' ant25: '' ant26: '' ant27: '' ant28: '' ant29: '' ant3: '' ant30: '' ant31: '' ant4: '' ant5: '' ant6: '' ant7: '' ant8: '' ant9: '' args: '0' bb_gain0: '20' bb_gain1: '20' bb_gain10: '20' bb_gain11: '20' bb_gain12: '20' bb_gain13: '20' bb_gain14: '20' bb_gain15: '20' bb_gain16: '20' bb_gain17: '20' bb_gain18: '20' bb_gain19: '20' bb_gain2: '20' bb_gain20: '20' bb_gain21: '20' bb_gain22: '20' bb_gain23: '20' bb_gain24: '20' bb_gain25: '20' bb_gain26: '20' bb_gain27: '20' bb_gain28: '20' bb_gain29: '20' bb_gain3: '20' bb_gain30: '20' bb_gain31: '20' bb_gain4: '20' bb_gain5: '20' bb_gain6: '20' bb_gain7: '20' bb_gain8: '20' bb_gain9: '20' bw0: '0' bw1: '0' bw10: '0' bw11: '0' bw12: '0' bw13: '0' bw14: '0' bw15: '0' bw16: '0' bw17: '0' bw18: '0' bw19: '0' bw2: '0' bw20: '0' bw21: '0' bw22: '0' bw23: '0' bw24: '0' bw25: '0' bw26: '0' bw27: '0' bw28: '0' bw29: '0' bw3: '0' bw30: '0' bw31: '0' bw4: '0' bw5: '0' bw6: '0' bw7: '0' bw8: '0' bw9: '0' clock_source0: '' clock_source1: '' clock_source2: '' clock_source3: '' clock_source4: '' clock_source5: '' clock_source6: '' clock_source7: '' comment: '' corr0: '0' corr1: '0' corr10: '0' corr11: '0' corr12: '0' corr13: '0' corr14: '0' corr15: '0' corr16: '0' corr17: '0' corr18: '0' corr19: '0' corr2: '0' corr20: '0' corr21: '0' corr22: '0' corr23: '0' corr24: '0' corr25: '0' corr26: '0' corr27: '0' corr28: '0' corr29: '0' corr3: '0' corr30: '0' corr31: '0' corr4: '0' corr5: '0' corr6: '0' corr7: '0' corr8: '0' corr9: '0' dc_offset_mode0: '0' dc_offset_mode1: '0' dc_offset_mode10: '0' dc_offset_mode11: '0' dc_offset_mode12: '0' dc_offset_mode13: '0' dc_offset_mode14: '0' dc_offset_mode15: '0' dc_offset_mode16: '0' dc_offset_mode17: '0' dc_offset_mode18: '0' dc_offset_mode19: '0' dc_offset_mode2: '0' dc_offset_mode20: '0' dc_offset_mode21: '0' dc_offset_mode22: '0' dc_offset_mode23: '0' dc_offset_mode24: '0' dc_offset_mode25: '0' dc_offset_mode26: '0' dc_offset_mode27: '0' dc_offset_mode28: '0' dc_offset_mode29: '0' dc_offset_mode3: '0' dc_offset_mode30: '0' dc_offset_mode31: '0' dc_offset_mode4: '0' dc_offset_mode5: '0' dc_offset_mode6: '0' dc_offset_mode7: '0' dc_offset_mode8: '0' dc_offset_mode9: '0' freq0: fm_freq freq1: 100e6 freq10: 100e6 freq11: 100e6 freq12: 100e6 freq13: 100e6 freq14: 100e6 freq15: 100e6 freq16: 100e6 freq17: 100e6 freq18: 100e6 freq19: 100e6 freq2: 100e6 freq20: 100e6 freq21: 100e6 freq22: 100e6 freq23: 100e6 freq24: 100e6 freq25: 100e6 freq26: 100e6 freq27: 100e6 freq28: 100e6 freq29: 100e6 freq3: 100e6 freq30: 100e6 freq31: 100e6 freq4: 100e6 freq5: 100e6 freq6: 100e6 freq7: 100e6 freq8: 100e6 freq9: 100e6 gain0: '20' gain1: '10' gain10: '10' gain11: '10' gain12: '10' gain13: '10' gain14: '10' gain15: '10' gain16: '10' gain17: '10' gain18: '10' gain19: '10' gain2: '10' gain20: '10' gain21: '10' gain22: '10' gain23: '10' gain24: '10' gain25: '10' gain26: '10' gain27: '10' gain28: '10' gain29: '10' gain3: '10' gain30: '10' gain31: '10' gain4: '10' gain5: '10' gain6: '10' gain7: '10' gain8: '10' gain9: '10' gain_mode0: 'False' gain_mode1: 'False' gain_mode10: 'False' gain_mode11: 'False' gain_mode12: 'False' gain_mode13: 'False' gain_mode14: 'False' gain_mode15: 'False' gain_mode16: 'False' gain_mode17: 'False' gain_mode18: 'False' gain_mode19: 'False' gain_mode2: 'False' gain_mode20: 'False' gain_mode21: 'False' gain_mode22: 'False' gain_mode23: 'False' gain_mode24: 'False' gain_mode25: 'False' gain_mode26: 'False' gain_mode27: 'False' gain_mode28: 'False' gain_mode29: 'False' gain_mode3: 'False' gain_mode30: 'False' gain_mode31: 'False' gain_mode4: 'False' gain_mode5: 'False' gain_mode6: 'False' gain_mode7: 'False' gain_mode8: 'False' gain_mode9: 'False' if_gain0: '20' if_gain1: '20' if_gain10: '20' if_gain11: '20' if_gain12: '20' if_gain13: '20' if_gain14: '20' if_gain15: '20' if_gain16: '20' if_gain17: '20' if_gain18: '20' if_gain19: '20' if_gain2: '20' if_gain20: '20' if_gain21: '20' if_gain22: '20' if_gain23: '20' if_gain24: '20' if_gain25: '20' if_gain26: '20' if_gain27: '20' if_gain28: '20' if_gain29: '20' if_gain3: '20' if_gain30: '20' if_gain31: '20' if_gain4: '20' if_gain5: '20' if_gain6: '20' if_gain7: '20' if_gain8: '20' if_gain9: '20' iq_balance_mode0: '0' iq_balance_mode1: '0' iq_balance_mode10: '0' iq_balance_mode11: '0' iq_balance_mode12: '0' iq_balance_mode13: '0' iq_balance_mode14: '0' iq_balance_mode15: '0' iq_balance_mode16: '0' iq_balance_mode17: '0' iq_balance_mode18: '0' iq_balance_mode19: '0' iq_balance_mode2: '0' iq_balance_mode20: '0' iq_balance_mode21: '0' iq_balance_mode22: '0' iq_balance_mode23: '0' iq_balance_mode24: '0' iq_balance_mode25: '0' iq_balance_mode26: '0' iq_balance_mode27: '0' iq_balance_mode28: '0' iq_balance_mode29: '0' iq_balance_mode3: '0' iq_balance_mode30: '0' iq_balance_mode31: '0' iq_balance_mode4: '0' iq_balance_mode5: '0' iq_balance_mode6: '0' iq_balance_mode7: '0' iq_balance_mode8: '0' iq_balance_mode9: '0' maxoutbuf: '0' minoutbuf: '0' nchan: '1' num_mboards: '1' sample_rate: samp_rate sync: pc_clock time_source0: '' time_source1: '' time_source2: '' time_source3: '' time_source4: '' time_source5: '' time_source6: '' time_source7: '' type: fc32 states: bus_sink: false bus_source: false bus_structure: null coordinate: [48, 108.0] rotation: 0 state: enabled connections: - [analog_wfm_rcv_0, '0', rational_resampler_xxx_1, '0'] - [blocks_multiply_const_vxx_0, '0', audio_sink_0, '0'] - [low_pass_filter_0, '0', analog_wfm_rcv_0, '0'] - [low_pass_filter_0, '0', qtgui_freq_sink_x_0, '0'] - [low_pass_filter_0, '0', qtgui_waterfall_sink_x_0, '0'] - [rational_resampler_xxx_0, '0', low_pass_filter_0, '0'] - [rational_resampler_xxx_1, '0', blocks_multiply_const_vxx_0, '0'] - [rtlsdr_source_0, '0', rational_resampler_xxx_0, '0'] metadata: file_format: 1 grc_version: 3.10.9.2 CreditsWhitney Knitter 169 projects • 1701 followers All thoughts/opinions are my own and do not reflect those of any company/entity I currently/previously associate with.