├── LICENSE.md ├── README.md ├── classroom_activities ├── Ch01_Diving_in_Headfirst │ ├── 005_Prerequisites.md │ ├── 010_What_is_an_SDR.md │ ├── 015_gqrx_FM_Receive.md │ ├── 020_Spec_A_paragradio.md │ ├── 030_FM_Radio_paragradio.md │ ├── 040_Noise_Tx_paragradio.md │ └── 050_PSK_Tx_paragradio.md ├── Ch02_Activities │ ├── 060_SDRangel.md │ └── manual_modulation_activity.md ├── Ch03_Analyzing_Signals_URH │ ├── 010_Install_URH.md │ ├── 020_Modulation.md │ ├── 030_Generate_a_signal.md │ ├── 040_Interpret_unknown_signal.md │ ├── 050_Interpret_unknown_noisy_signal.md │ ├── 060_Cropping_a_signal.md │ ├── 070_Interpret_multiple_noisy_signals.md │ ├── 080_Interpret_multiple_noisy_signals.md │ ├── 090_Record_a_real_signal.md │ ├── 099_RC_Car.md │ ├── Images │ │ ├── ASK_modulated_signal.png │ │ ├── amplitude25%.png │ │ ├── catscreenshot.png │ │ ├── crop.png │ │ ├── final_message.png │ │ ├── final_settings.png │ │ ├── fsk_highlight.png │ │ ├── fsk_modulation.png │ │ ├── generated_data.png │ │ ├── generated_file.png │ │ ├── highlightfirst1.png │ │ ├── pauses.png │ │ ├── psk_highlight.png │ │ ├── psk_modulation.png │ │ ├── record_signal.png │ │ ├── singlebithighlighted.png │ │ ├── unknown_signal_1.png │ │ ├── unknown_signal_1_determine_samplerate.png │ │ ├── unknown_signal_1_interpret.png │ │ ├── unknown_signal_2_centerset.png │ │ ├── unknown_signal_2_demodulated.png │ │ ├── unknown_signal_2_determine_samplerate.png │ │ ├── unknown_signal_2_interpret.png │ │ ├── unknown_signal_2_spectrogram.png │ │ ├── unknown_signal_2_spectrogram_highlight.png │ │ ├── unknown_signal_2_with_filtered.png │ │ └── urh_screenshot.png │ ├── unknown_signal_1.complex │ ├── unknown_signal_2.complex │ ├── unknown_signal_3.complex │ ├── unknown_signal_4.complex │ └── unknown_signal_5.complex ├── Ch04_Analyzing_Signals_Python │ ├── 005_pcdr_single_freq_transmit.md │ ├── 010_pcdr_ook_tx_intro.md │ ├── 020_pcdr_ook_waves.md │ ├── 030_pcdr_frequency_domain_Real.md │ ├── 040_pcdr_frequency_domain_Complex.md │ ├── 050_pcdr_simple_receiver.md │ └── in_progress │ │ ├── 130_pcdr_tx_2.md │ │ ├── 140_pcdr_rx_intro.md │ │ ├── 150_pcdr_ook_rx_intro.md │ │ ├── 230_GnuRadio_Dashing_Block_Example_Python.py │ │ ├── activity_detector_v0_2.py │ │ ├── activity_recorder.md │ │ ├── dash_plot_examples.md │ │ └── pcdr_simple_wbfm_transmit.md └── Ch05_Concepts │ ├── 010-Transmit-and-Receive-Pure-Sine.md │ ├── 011_numpy.md │ ├── 012_matplotlib.md │ ├── 020_Sample_Rates_Intro.md │ ├── 021_Sample_Rates_CPU_temps.md │ ├── 022_Sample_Rates_turtle_ripples.md │ ├── 023_Sample_Rates_py_practice.md │ ├── 024_Sample_Rates_grc_practice.md │ ├── 025_Sample_Rates_RepeatBlock.md │ ├── 026_Sample_Rates_RealisticData.md │ ├── 030_AnalyzeFreq_of_Combined_Signals.md │ ├── 034_Oversampling_Undersampling.md │ ├── 040_Unicode_and_File_Source.md │ ├── 050_Clipping.md │ ├── 099_Additional_Practice.md │ ├── 099_GRC_FM_Receiver.md │ ├── 099_GRC_FM_Transmitter.md │ ├── 099_GRC_Spectrum_Analyzer.md │ ├── 099_GRC_info.md │ ├── 099_Improved_PSK_Digital_Jammer.md │ ├── 099_Noise_Jammer.md │ ├── 099_PSK_Digital_Jammer.md │ ├── 099_Spectrum_Painting.md │ └── 099_Subtle_Jammer.md └── resources ├── Common-GNURadio-error-messages.md ├── Definitions.md ├── README.md ├── Using-amplifier.md ├── assets ├── WBFM_Rx.png ├── hw_bb_filt_illustration.png ├── specanhelp1.png ├── specansim1.png └── specansim2.png ├── ghz.jpg ├── sdrsetup.sh ├── templates ├── digital_jammer_template.grc ├── fm_receiver_template.grc ├── noise_jammer_template.grc └── specanny_template.grc ├── toc ├── 3day.md ├── 5day.md ├── 7day.md ├── 7day_workingcopy.md └── readme.md └── unit-conversion-review.md /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2022 by the Authors of "python-can-define-radio". 2 | 3 | Authors: m-pcdr, james-pcdr, wolverine-is-the-best 4 | Creator: james-pcdr 5 | 6 | Permission is hereby granted, free of charge, to any individual person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, or modify copies of the Software, and to permit persons to whom the Software is furnished to do so in order **_to further the individual's education only._** The Software is **_not for sale or for Commercial use_** unless written permission is granted by at least two "Authors" of the "python-can-define-radio" associated repositories, one of which must be the original "Creator" of the "python-can-define-radio" account. The same copyright terms are true of all content in github.com/python-can-define-radio public and private repositories, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 16 | SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Software-Defined Radio (SDR) Course 2 | 3 | - [Daily Schedule of Events](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/toc/7day.md) 4 | 5 | ## Where to start: 6 | 7 | Start with the [classroom_activities](https://github.com/python-can-define-radio/sdr-course/tree/main/classroom_activities). 8 | 9 | ## Some background info: 10 | 11 | This is an in-progress collection of tutorials about the foundations of Software Defined Radios. Readers are encouraged to supplement with [other resources](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/README.md). 12 | 13 | ## Possible hardware: 14 | 15 | - [RTL-SDR](https://www.rtl-sdr.com/) -- Top recommendation due to low cost; receive only 16 | - [PlutoSDR](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/adalm-pluto.html#eb-overview) -- Second recommendation due to full-duplex ability 17 | - [HackRF One](https://hackrf.readthedocs.io/en/latest/faq.html) -- half-duplex 18 | - Nooelec NESDR SMArt: receive only 19 | -------------------------------------------------------------------------------- /classroom_activities/Ch01_Diving_in_Headfirst/005_Prerequisites.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | 1. We recommend learning **Marimo** through [this lesson in the Python course](https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch02_Advanced/01_marimo.md) before proceeding because many elements of the SDR-course will refer back to concepts from that lesson. 4 | 5 | 2. Converting between different frequency units is essential to fluent radio usage. [This set of exercises](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/unit-conversion-review.md) provides a review. 6 | -------------------------------------------------------------------------------- /classroom_activities/Ch01_Diving_in_Headfirst/010_What_is_an_SDR.md: -------------------------------------------------------------------------------- 1 | 2 | # SDR Discussion 3 | 4 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 21-22). 5 | 6 | ### Before starting: 7 | 8 | 9 | 10 | - If you have changed classrooms, it may be necessary to repeat some steps of the Python [preliminaries](https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch01_Basics/ex_0a_preliminaries.md) lesson. 11 | 12 | - If your instructor plans to use SDRangel, run the [SDRangel setup script](https://raw.githubusercontent.com/python-can-define-radio/sdr-course/refs/heads/main/resources/sdrsetup.sh). 13 | 14 | - Create a folder for your SDR files with your name (and no spaces) on the Desktop. 15 | 16 | ### Introduction: 17 | 18 | What is an SDR? Well, it's a Software Defined Radio. But you're probably looking for a more in-depth answer than that. 19 | 20 | So, let's look at what a radio is. Even before that, we'll start with what a wave is. 21 | 22 | ### What is a wave? Slinkies and Sound 23 | 24 | Here's a [slinky](https://www.youtube.com/watch?v=g8GcMn7K0u4?t=11). 25 | 26 | What's waving? The metal. 27 | 28 | You can also make the metal wave [longitudinally](https://www.youtube.com/watch?v=fMJrtheQfZw). 29 | 30 | But what if you wanted to convey audio? 31 | 32 | Let's talk about the classic [string-between-two-cans](https://duckduckgo.com/?q=string+between+two+cans&t=h_&iar=images&iax=images&ia=images). 33 | 34 | ``` 35 | ------- ------- 36 | Can ------- String ------ Can 37 | ------- ------- 38 | ``` 39 | 40 | 1. What's happening? 41 | 2. What's waving? 42 | 3. Is it a longitudinal wave or a transverse wave? 43 | 44 | (_discuss in small groups. For more info about waves, [see this video](https://www.khanacademy.org/science/physics/mechanical-waves-and-sound/sound-topic/v/sound-properties-amplitude-period-frequency-wavelength). See also [a speaker in slow motion](https://www.youtube.com/watch?v=J2BUvWRCBGM)._ ) 45 | 46 | ### Microphones 47 | 48 | Let's imagine a new, similar setup: 49 | 50 | ``` 51 | -------------- ------------- ----------- 52 | Microphone ---Wire--- Amplifier ----- Wire ------ Speaker 53 | -------------- ------------- ----------- 54 | ``` 55 | 56 | What's the difference between this and the string+cans setup? 57 | 58 | What kind of signal is carried on the string? On the wire? 59 | 60 | (_discuss in small groups._) 61 | 62 | ### Radios 63 | 64 | A radio system is similarily interconnected: 65 | 66 | ``` 67 | 68 | -------------- -------------- ---------- ----------- 69 | Microphone ---Wire--- Transmitter ~~~~~~ Air ~~~~~~ Receiver --- Wire ------ Speaker 70 | -------------- -------------- ---------- ----------- 71 | ``` 72 | 73 | A traditional radio transmitter (Radio 1) is composed of electronics hardware without software. 74 | So too, a traditional radio receiver (Radio 2) is composed of electronics hardware without software. 75 | 76 | A "gimme" question: What type of signal is exchanged through the air? 77 | 78 | ### Software Defined Radio (SDR) 79 | 80 | In an SDR, the transmitter and/or receiver functionality is divided into hardware and software: 81 | 82 | ``` 83 | 84 | -------------- -------------- ------------- ---------- 85 | Microphone ---Wire-- Transmitter(TX) ~~~ Air ~~~~~ Receiver(RX) --- Wire ---- Speaker 86 | -------------- [software] [software] ---------- 87 | + + 88 | [hardware] [hardware] 89 | -------------- ------------- 90 | ``` 91 | 92 | The hardware and software of the software defined radio (SDR) may be housed in a single package or unit, e.g., a two-way hand-held digital radio or a digital car radio receiver, or may be housed in separate packaging, e.g., a computer and an SDR device. 93 |
ℹ️ Expand for further explanation 94 | 95 | * The diagram above shows two separate SDR's, one designated as the transmitter and the other as the receiver. Either SDR may, optionally, communicate with a traditional radio as well. 96 | * Most SDR devices can be operated in both a transmission mode and a reception mode. 97 | * Depending on its mode of operation, an SDR device may be called the "transmitter", the "reciever", or the "transciever. For either mode of SDR operation (transmission or reception), some of the functionality is allocated to a separate computing device. 98 | * In current discussion, the term "computer" may refer to a desktop computer, a laptop computer, a tablet computer, or a mobile smart telephone, as examples. 99 | * Although wires are shown in the diagram, in some SDR systems, the microphone, speaker, etc. may be connected to the SDR via wireless communication technology. 100 | 101 |
102 | 103 | _Further reading: https://wiki.gnuradio.org/index.php?title=What_Is_GNU_Radio_ 104 | -------------------------------------------------------------------------------- /classroom_activities/Ch01_Diving_in_Headfirst/015_gqrx_FM_Receive.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2023 Aug 18: 050_gqrx_FM_Receive.md
 4 | 2023 May 22: 040_gqrx_FM_Receive.md
 5 | 
6 |
7 | 8 | # gqrx FM Receive 9 | 10 | ℹ️ This material coincides with material from SDR slideshow B (slides 69-80). 11 | 12 | ⚠️ **Remember to respect your fellow classmates (audio level) they may be trying to work as well.** 13 | ### Initial setup 14 | - Attach an SDR device, e.g. a HackRF One, to your computer 15 | - Open a terminal (command line interface or CLI) window on your 🖥️ computer. 16 | - The first time you launch gqrx you may want to reset to its default settings using the terminal command below. 17 | `$ gqrx -r` (Note: "$" represents your command prompt. Do not type it.) 18 | - On the Configure I/O devices window select the "device" dropdown menu. 19 | - Choose the first HackRF entry which should look something like this: 20 | - HackRF HackRF One `123456f` (The numbers will be part of a device identifier.) 21 | 22 | - Press the "OK" button at the bottom. 23 | - Maximize or make your gqrx window as large as is practical. 24 | 25 | ### Settings 26 | - Check the audio volume level on your computer. Set it to low or moderate as a starting point. 27 | - Set gain levels (amplification) for SDR system -- Go to right side of window. Select "Input controls". Set these parameters: 28 | - RF gain: 0 (radio frequency gain) ⚠️ **Always keep the RF gain at zero.** ⚠️ 29 | - IF gain: 32 (intermediate freqency gain) -- may be varied 30 | - BB gain: 50 (baseband gain) -- may be varied 31 | - Click the ▶️ Play button in the top left under "File". (If the sound is much too load, Press Play button again. Adjust system or gqrx (@ bottom right) sound level, and Play again.) 32 | - You should see some static (noise) and spikes in the spectrum display. 33 | - If you do not hear any static ensure your 🖥️ computer audio settings are enabled. 34 | - If you still do not hear any static you may need to adjust your gain slider on the Audio pane (bottom right within gqrx). 35 | 36 | - **Frequency** 37 | - Tune your frequency to `98.000.000`, which means 98 Million Hz, or 98 MHz. (This should allow you to see everything from 88.1 to 107.9 MHz "The FM band".) (Note: periods are used in place of commas to separate place values for one thousand, one million, etc.) 38 | - HOW: gqrx can be tuned either in the spectrum view window or in the Receiver Options pane (a tab on the right side). 39 | - In the spectrum view, clicking on the top of the numbers increases, and clicking on the bottom of the numbers decreases the frequency. 40 | - In the Frequency box of the Receiver Options pane, it would be typed: `98000.000 kHz`. 41 | - **Squelch** 42 | - With your mouse, click anywhere on the spectrum where there is no spike present. 43 | - In the receiver options pane (on the right), click the A button (short for automatic) next to "Squelch". 44 | - This will readjust the noise floor dB level from `-150` dB to somewhere between `-60` dB and `-80` dB depending on the amount of "noise" present, and the speakers should go silent. 45 | - **Mode** 46 | - Using the dropdown menu next to "Mode", select Wideband Frequency Modulation (WFM), either mono or stereo. 47 | - **Experiment** 48 | - Click around on different spikes in the spectrum view (some of them will be radio stations). 49 | - Again you may have to adjust the 🖥️ computer audio settings and/or the audio gain slider in gqrx for optimal sound quality. 50 | - Also remember antenna placement is very important. 51 | - Other than the RF gain, feel free to play around with the settings. You can always reset to default configuration with the `$ gqrx -r` terminal command. 52 | - **Optional** 53 | - You can increase the decimation in the Configure I/O devices window to get better view of the tuned frequency. Ask for a demonstration if this is unclear. 54 | 55 | ⚠️ **Keep the RF gain at zero.** ⚠️ 56 | 57 | ⚠️ **Remember to respect your fellow classmates (audio level). They may be trying to work as well.** 58 | 59 | ### Note: versions 60 | 61 | - You have the option of using the GQRX AppImage, which is newer than our pre-installed GQRX. This is left as an activity for the student. 62 | 63 | ### ℹ️ Some useful resources for gqrx and HackRF One: 64 | (Suggestion: When using a web-link, Right-Click with your mouse and select "open in new tab" or new window and then go to that tab/window.) 65 | - https://gqrx.dk/ 66 | - https://hackrf.readthedocs.io/en/latest/index.html 67 | -------------------------------------------------------------------------------- /classroom_activities/Ch01_Diving_in_Headfirst/040_Noise_Tx_paragradio.md: -------------------------------------------------------------------------------- 1 | # Paragradio Noise transmitter 2 | 3 | ℹ️ This material coincides with material from SDR slideshow E (all slides). 4 | 5 | ### Introduction 6 | 7 | Transmitting noise has a few uses. It can be used for research purposes, to test how well a signal is able to be demodulated in the presence of background noise. It can also be used to prevent others from using a specific frequency or range of frequencies. If the intent is disrupting legitimate users, it would be called jamming. 8 | 9 | _Disclaimer: jamming is illegal in many countries. This should only be used in an environment that is sufficiently distant/radio-shielded from other electronics using those frequencies._ 10 | 11 | ### Dependencies 12 | 13 | In the terminal, if you haven't already, run these: 14 | 15 | ``` 16 | pip install --upgrade "paragradio==2025.3.*" 17 | pip install marimo 18 | ``` 19 | 20 | ### Noise Transmitter 21 | 22 | Open a terminal and type `marimo edit`. Create a new notebook and save it as **noise_tx.py**. (For an intro to marimo, reference this lesson [01_marimo.md](https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch02_Advanced/01_marimo.md) in the Github python-course). 23 | 24 | Copy the following: 25 | 26 | ```python3 27 | ## Exercise 1 28 | ## Try this. 29 | #### Name the first cell "imports". Put this code: 30 | import marimo as mo 31 | from paragradio.v2025_03 import Noise_Tx 32 | 33 | #### Name the second cell "launch". Put this code: 34 | Noise_Tx.config( 35 | running=True, 36 | center_freq=2.45e9, 37 | ) 38 | ``` 39 | 40 | If it runs, you should see a waterfall display. Note that it's possible to make a noise transmitter without a waterfall display. The display does not make the transmitter work better; it just provides a view of what is being transmitted. 41 | 42 | Many of the parameters of `config` are the same as the `SpecAn` discussed in the [Spectrum Analyzer Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/020_Spec_A_paragradio.md), such as `center_freq`. There are also a few new ones. As before, we recommend investigating and experimenting with each to learn what functionality is available. Try using the "help(Noise_Tx.config)" in a marimo cell for more information. 43 | 44 | ```python3 45 | ## Exercise 2 46 | ## Name the third cell "create_ui". 47 | ## Name the fourth cell "render_ui". 48 | ## In the "create_ui" cell, add a slider to control the amplitude. 49 | ## The parameters should have these values: 50 | ## - Left Limit: 0 51 | ## - Right Limit: 30 52 | ## - Step value: 0.01 53 | ## - Default value: 0.5 54 | ## - Label: Amplitude 55 | ## Render the element in the "render_ui" cell. 56 | ``` 57 | 58 | ```python3 59 | ## Exercise 3 60 | ## In the "create_ui" cell, add a `mo.ui.number` element to control the center frequency. 61 | ## Set the left and right limits to match the frequency range available to the HackRF One. 62 | ## Set the label to "Center Frequency". 63 | ## Render the element in the "render_ui" cell. 64 | ``` 65 | 66 | ```python3 67 | ## Exercise 4 68 | ## In the "create_ui" cell, add a slider to control the IF gain. 69 | ## The options should match the HackRF One's hardware specs. 70 | ## Set the label to "IF Gain". 71 | ## Hint: we're transmitting. What are the IF gain requirements? 72 | ## Render the element in the "render_ui" cell. 73 | ``` 74 | 75 | ```python3 76 | ## Exercise 5 77 | ## In the "create_ui" cell, add a slider to control the filter cutoff frequency. 78 | ## The parameters should have these values: 79 | ## - Left Limit: 2 kHz 80 | ## - Right Limit: 400 kHz 81 | ## - Step value: 1 kHz 82 | ## - Label: "Cutoff Frequency" 83 | ## Render the element in the "render_ui" cell. 84 | ``` 85 | 86 | ```python3 87 | ## Exercise 6 88 | ## In the "create_ui" cell, add a slider to control the filter transition width. 89 | ## The parameters should have these values: 90 | ## - Left Limit: 2 kHz 91 | ## - Right Limit: 500 kHz 92 | ## - Step value: 1 kHz 93 | ## - Label: "Transition width" 94 | ## Render the element in the "render_ui" cell. 95 | ``` 96 | 97 | ```python3 98 | ## Exercise 7 99 | ## In the "create_ui" cell, add a switch element that controls whether the Noise_Tx is running. 100 | ## Set the label to "Off/On". 101 | ## Render the element in the "render_ui" cell. 102 | ``` 103 | 104 | 105 | 106 | #### Checkpoint Activity 107 | 108 | The instructor will provide 30 minutes for students to experiment. Each student should ensure with a partner that their Noise Transmitter signal can be seen, adjust each UI element, and watch for changes in the partners view to ensure proper functionality. asking an instructor for assistance as needed. 109 | 110 | #### What to expect on the assessment 111 | 112 | See [here](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/020_Spec_A_paragradio.md#what-to-expect-on-the-assessment). 113 | -------------------------------------------------------------------------------- /classroom_activities/Ch01_Diving_in_Headfirst/050_PSK_Tx_paragradio.md: -------------------------------------------------------------------------------- 1 | # Paragradio PSK transmitter 2 | 3 | ℹ️ This material coincides with material from SDR slideshow F (all slides). 4 | 5 | ### Introduction 6 | 7 | In the modern world, trillions of bits are sent wirelessly every day. How do we use electromagnetic waves to communicate this massive amount of data? One way to do so is using Phase Shift Keying, or PSK. 8 | 9 | ### Dependencies 10 | 11 | In the terminal, if you haven't already, run these: 12 | 13 | ``` 14 | pip install --upgrade "paragradio==2025.3.*" 15 | pip install marimo 16 | ``` 17 | 18 | ### PSK Transmitter 19 | 20 | Create a new notebook and save it as **psk_tx_loop.py**. (For an intro to marimo, reference the lesson in the github python course [Marimo Lesson](https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch02_Advanced/01_marimo.md)). 21 | 22 | Copy the following: 23 | 24 | ```python3 25 | ## Exercise 1 26 | ## Try this. 27 | #### Name the first cell "imports". Put this code: 28 | import marimo as mo 29 | from paragradio.v2025_03 import PSK_Tx_loop 30 | 31 | #### Name the second cell "launch". Put this code: 32 | PSK_Tx_loop.config( 33 | running=True, 34 | data=[1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0], 35 | center_freq=2.43e9, 36 | amplitude=1, 37 | ) 38 | ``` 39 | 40 | If it runs, you should see a window open. It includes a waterfall sink and a time sink so you can see the bits being transmitted. 41 | 42 | Many of the parameters of `config` are the same as the `SpecAn` discussed in the [Spectrum Analyzer Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/020_Spec_A_paragradio.md), such as `center_freq`. There are also a few new ones. As before, we recommend investigating and experimenting with each to learn what functionality is available. Try using the "help(PSK_Tx_loop.config)" in a marimo cell for more information. 43 | 44 | ```python3 45 | ## Exercise 2 46 | ## Name the third cell "create_ui". 47 | ## Name the fourth cell "render_ui". 48 | ## In the "create_ui" cell, add a `mo.ui.number` element to control the center frequency. 49 | ## Set the left and right limits to match the frequency range available to the HackRf One. 50 | ## Set the label to "Center Frequency". 51 | ## Render the element in the "render_ui" cell. 52 | ``` 53 | 54 | ```python3 55 | ## Exercise 3 56 | ## In the "create_ui" cell, add a slider element to control the IF gain. 57 | ## The parameters should match the HackRF One's hardware specs. 58 | ## Set the label to "IF Gain". 59 | ## Render the element in the "render_ui" cell. 60 | ``` 61 | 62 | ```python3 63 | ## Exercise 4 64 | ## In the "create_ui" cell, add a dropdown menu to pick between all available modulation options. 65 | ## Set the label to "Modulation". 66 | ## Render the element in the "render_ui" cell. 67 | ## Remember to use "help(PSK_Tx_loop.config)" in a marimo cell for more information. 68 | ``` 69 | 70 | ```python3 71 | ## Exercise 5 72 | ## In the "create_ui" cell, add a switch element that controls whether the PSK_Tx is running. 73 | ## Set the label to "Off/On". 74 | ## Render the element in the "render_ui" cell. 75 | ``` 76 | 77 | 78 | #### Checkpoint Activity 79 | 80 | The instructor will provide 30 minutes for students to experiment. Each student should ensure with a partner that their PSK Transmitter signal can be seen, adjust each UI element, and watch for changes in the partners view to ensure proper functionality. asking an instructor for assistance as needed. 81 | 82 | #### Creating more interesting data 83 | 84 | You can generate random bits like so: 85 | 86 | ```python3 87 | import random 88 | bits = random.choices([0, 1], k=100) 89 | print(bits) 90 | ``` 91 | 92 | As an exercise, try to use the `bits` variable as your `data` in `PSK_Tx_loop.config`. 93 | 94 | #### What to expect on the assessment 95 | 96 | See [here](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/020_Spec_A_paragradio.md#what-to-expect-on-the-assessment). 97 | -------------------------------------------------------------------------------- /classroom_activities/Ch02_Activities/060_SDRangel.md: -------------------------------------------------------------------------------- 1 | # SDRangel 2 | 3 | Start with this video: [One to rule them all - Crossplatform SDR decoder - SDRANGEL - short review and examples](https://www.youtube.com/watch?v=zrhBcy8L-dA) 4 | 5 | ### Basics of Hack RF receiving: 6 | 7 | 1. Launch SDR Angel. 8 | 2. Set up a Hack RF in Receive mode. 9 | 3. Tune in to a broadcast FM station. 10 | 4. Try a variety of sample rates. What does this setting do? 11 | 5. Try adjusting the gains (they are named __ and __). 12 | 6. Try a variety of bandwidths. What does this setting do? 13 | 1. Why would you want to adjust the bandwith? Answer: to avoid aliasing. 14 | 15 | ### Receiving Broadcast FM 16 | 7. Create a broadcast FM receiving channel. 17 | 8. Try adjusting the frequency on which you are receiving. 18 | 9. Try adjusting the band-pass filter. 19 | 10. Try the Radio Data Service (RDS). 20 | 21 | ### Receiving Narrow FM 22 | 11. Create a Narrow FM receiving channel. 23 | 12. ... 24 | 25 | ### Saving a Workspace 26 | 13. ... 27 | 28 | ### Preparing to Transmit 29 | 13. Close all subwindows, but keep SDRAngel open. 30 | 14. Set up a Hack RF in Transmit mode. 31 | 32 | ### Transmitting Wide FM 33 | 15. Create a Wide FM transmit channel. 34 | 16. Try the Morse Code (CW) feature. 35 | 17. Try setting the audio device. 36 | 37 | ### Transmitting Wide FM from an audio file 38 | 18. First, you'll need to convert the audio file of interest to `.raw`, which is documented [here](https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch03_Misc_examples/soundFile.md#convert-a-wav-file-to-raw) in the Python course. 39 | 19. After converting the file, pick it in the [WFM Modulator](https://github.com/f4exb/sdrangel/blob/master/plugins/channeltx/modwfm/readme.md) subwindow. 40 | 41 | ### Transmitting two Wide FM channels simultaneously 42 | 20. Create another Wide FM transmit channel. 43 | 21. Pick another sound file, or set it to transmit Morse. 44 | 22. Set it to a different frequency. 45 | 46 | ### Transmitting text using ChirpChat 47 | 23. Create a ChirpChat transmit channel. 48 | 24. ... 49 | 50 | ### Receiving text using ChirpChat 51 | 25. Create a ChirpChat receive channel. 52 | 26. ... 53 | 54 | 55 | ---- 56 | 57 | ### Other videos 58 | 59 | https://www.youtube.com/watch?v=kBuGDshziMg 60 | -------------------------------------------------------------------------------- /classroom_activities/Ch02_Activities/manual_modulation_activity.md: -------------------------------------------------------------------------------- 1 | # Manual modulation 2 | 3 | ### Summary 4 | 5 | Most of us have heard terms like amplitude and frequency, but how do we use these invisible electromagnetic waves to communicate in our everyday lives? This activity provides an opportunity to explore and experience these concepts. 6 | 7 | You'll work in groups of 4 to develop a wireless communication scheme. You'll start with audio, and then move to using EM waves. Each level will incorporate a way to improve the communication scheme. 8 | 9 | ### Difficulty Level 1 10 | 11 | **Directions**: Describe a communication system that uses humming. The system should be able to communicate any single word that is at most five letters long. 12 | 13 | **Constraints**: 14 | - You cannot use any visual cues. 15 | - You cannot use any other sounds (clicking, snapping, clapping, spoken words, etc). 16 | - You will not know the word in advance. 17 | - The word will be written using Latin characters (a-z). 18 | - The system must be at least loosely based on Unicode or ASCII. 19 | 20 | **Execution**: 21 | - All student groups will submit their directions anonymously. 22 | - The instructor will briefly look at each submission and decide the order of presentation. Note: the instructor will probably choose to present flawed systems first so as to demonstrate the flaws for learning purposes. 23 | - For each submission, the instructor will... 24 | - Show the submitted directions on the board and read them aloud. 25 | - Ask for a pair of students to volunteer to execute the directions. (Ideally, this pair is **not** from the group that submitted the directions. However, maintaining submission anonymity is first priority because the instructor will likely point out flaws in the communication scheme.) 26 | - Facilitate a discussion of the pros and cons of the approach. 27 | 28 | **Conclusion**: If any of the following concepts did not arise naturally during the student presentations, the instructor will present them: 29 | - OOK 30 | - ASK 31 | - FSK 32 | 33 | ### Difficulty level 2 34 | 35 | **Directions**: Describe a communication system that uses the **EMS (ElectroMagnetic Spectrum)**. The system should be able to communicate any single word that is at most five letters long. 36 | 37 | **Constraints**: 38 | - You must modulate and demodulate the word by hand. 39 | - You will not know the word in advance. 40 | - The word will be written using Latin characters (a-z). 41 | 42 | **Execution**: Same as Level 1. 43 | 44 | **Conclusion**: Same as Level 1. 45 | 46 | ### Difficulty level 3 47 | 48 | **Directions**: Same as level 2, but **the transmission must last less than half a second.** Decoding the message must take less than 10 minutes. 49 | 50 | **Constraints, Execution, Conclusion**: Same as previous. 51 | 52 | ### Difficulty level 4 53 | 54 | **Directions**: Same as level 3, with the following changes: 55 | - Instead of one word, **you will communicate two sentences**. (max character count will be the same as a single SMS). 56 | - Transmission must last less than **one** second. 57 | - Encoding the message must be reasonably doable in less than 20 minutes. 58 | - Decoding the message must be reasonably doable in less than 20 minutes. 59 | 60 | **Constraints, Execution, Conclusion**: Same as previous. 61 | 62 | ### Difficulty level 5 63 | 64 | **Directions**: Same as level 4, with the following changes: 65 | - Must include Forward Error Correction encoding that can survive a single burst of noise. The length of the noise burst will be no more than 5% of the total transmission time. The FEC encoding and decoding must be doable by hand with only a 4-operation calculator. 66 | - Time to encode: less than an hour. Time to decode: less than an hour. 67 | 68 | ### Difficulty level 6 69 | 70 | **Directions**: Same as level 5, with the following changes: 71 | - The communication must be **encrypted**. Encryption must be (a) doable by hand, and (b) must take at least 10 attempts to break using brute force. 72 | - Time to encrypt: less than 30 min. Time to encode: less than 20 min. 73 | - Time to decrypt: less than 30 min. Time to decode: less than 20 min. 74 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/010_Install_URH.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2023 May 22: 010_Install_URH.md
 4 | 
5 |
6 | 7 | # Install Universal Radio Hacker (URH) 📻 8 | 9 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 10 | 11 | ### Initial setup 12 | 13 | - Click on the 9 dots in the taskbar of your linux machine and type `terminal` in the search window. 14 | 15 | - In terminal type `pip install cython`. 16 | 17 | - In terminal type `pip install "urh==2.9.4"`. 18 | 19 | - In terminal type `pip install --upgrade numpy`. 20 | 21 | - Optional: In terminal type `pip install --upgrade pcdr`. (locally built package) 22 | 23 | - In terminal type `urh` to launch the program. 24 | 25 | - If you get an error when launching urh type `source ~/.profile`. 26 | 27 | - Relaunch by typing `urh` again. 28 | 29 | - It should look something like this: 30 | 31 | ![urh_screenshot.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/urh_screenshot.png?raw=true) 32 | 33 | - At the top left of urh select `File > Open folder` or the keyboard shortcut (Ctrl + Shift + O). 34 | 35 | - Navigate to your folder created on the Desktop. 36 | 37 | - Select Open at the top right of the Open folder popup. 38 | 39 | - When it asks if you want to make it a project folder select No. 40 | 41 | ### ℹ️ Some useful resources for urh: 42 | 43 | - https://github.com/jopohl/urh 44 | 45 | ##

[Next Lesson →](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/020_Modulation.md)

46 | 47 | 48 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/020_Modulation.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
  3 | 2023 May 22: 020_Modulation.md
  4 | 
5 |
6 | 7 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 8 | 9 | You may remember that we introduced the idea of [Modulation](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch02_Basics/020_Sample_Rates_Intro.md#what-is-modulation) in a previous lesson. 10 | 11 | In this section, we will visually show you what those modulation techniques look like using urh. 12 | 13 | # Demonstrate Modulation using URH 📻 14 | 15 | - There are 3 basic types of modulation we will demonstrate. 16 | - Amplitude Shift Keying (ASK) 17 | - Frequency Shift Keying (FSK) 18 | - Phase Shift Keying (PSK) 19 | 20 | - Open a terminal window. 21 | 22 | - Type `urh` to launch the program. 23 | 24 | - Select the Generator tab at the top of urh window. 25 | 26 | - Then select `Edit` at the bottom of the urh window. 27 | 28 | - This opens a popup showing a carrier signal, some raw data, and what the signal would look like combined with the data. 29 | 30 | - Set it up something like this to start: 31 | 32 | ### Carrier section: 33 | 34 | | | | 35 | |-------|-----| 36 | |Frequency:| 10.0| 37 | |Phase:| 0.000°| 38 | 39 | ### Data (raw bits) section: 40 | 41 | | | | 42 | |-------|-----| 43 | |Data:| 1010110010101100| 44 | |Samples per Symbol:| 200000| 45 | |Sample Rate (Sps):| 2.0M| 46 | 47 | ### Modulation section: 48 | 49 | | | | 50 | |-------|-----| 51 | |dropdown menu ▾|Amplitude Shift Keying (ASK)| 52 | |Bits per Symbol:| 1| 53 | |Amplitudes in %:| 0/100| 54 | 55 | - It should look something like this: 56 | - ASK should be pretty easy determining the difference between a 1 and a 0. 57 | 58 | ![ASK_modulated_signal.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/ASK_modulated_signal.png?raw=true) 59 | 60 | - Now on the modulated signal click and drag to try to highlight a single bit (1 or 0). 61 | - Notice the Samples selected should be around 200000 which lines up with our Samples per Symbol setting above. 62 | 63 | ![singlebithighlighted.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/singlebithighlighted.png?raw=true) 64 | 65 | - You can also adjust the Amplitudes in % value which essentially changes the value/height/amplitude of a "0". 66 | 67 | ![amplitude25%.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/amplitude25%25.png?raw=true) 68 | 69 | - Try changing the modulation. 70 | 71 | ### Modulation section: 72 | 73 | | | | 74 | |-------|-----| 75 | |dropdown menu ▾|Frequency Shift Keying (FSK)| 76 | |Bits per Symbol:| 1| 77 | |Frequencies in Hz:|20/200| 78 | 79 | - What do you see different? 80 | 81 | ![fsk_modulation.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/fsk_modulation.png?raw=true) 82 | 83 | - To determine Samples per Symbol of FSK highlight the smallest section of a single frequency that you can find. 84 | - This becomes harder and harder the closer the frequencies are to each other. 85 | 86 | ![fsk_highlight.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/fsk_highlight.png?raw=true) 87 | 88 | - The last thing we are going to look at is Phase Shift Keying (PSK). 89 | 90 | - Try this: 91 | 92 | ### Modulation section: 93 | 94 | | | | 95 | |-------|-----| 96 | |dropdown menu ▾|Phase Shift Keying (PSK)| 97 | |Bits per Symbol:| 1| 98 | |Phases in degree:|0/180 or 180/0| 99 | 100 | - It should look something like this: 101 | 102 | ![psk_modulation.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/psk_modulation.png?raw=true) 103 | 104 | - To determine Samples per Symbol of PSK highlight the smallest portion of the signal between phase changes that you can find. 105 | 106 | ![psk_modulation.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/psk_highlight.png?raw=true) 107 | 108 | - In Phase Shift Keying the change of phase marks the shift between a 1 and a 0. 109 | 110 | - This simple example is known as Binary Phase Shift Keying or BPSK. 111 | - Other examples of PSK 112 | - QPSK (2 bits per symbol) 113 | - 8PSK (3 bits per symbol) 114 | - 16QAM (4 bits per symbol) 115 | - ℹ️ Note: 16QAM involves modulating both the phase and the amplitude. This is currently outside of urh's capability. 116 | 117 | ### ℹ️ Some useful resources for urh: 118 | 119 | - https://github.com/jopohl/urh 120 | 121 | ##

[← Previous Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/010_Install_URH.md) -------- [Next Lesson →](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/030_Generate_a_signal.md)

122 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/030_Generate_a_signal.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2023 May 22: 030_Generate_a_signal.md
 4 | 
5 |
6 | 7 | # Generate a signal using URH 📻 8 | 9 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 10 | 11 | - Click on the Generator tab at the top of the urh window. 12 | 13 | - Make sure your defaults are set by clicking the `Edit` button and using the following information: 14 | 15 | ### Carrier section: 16 | 17 | | | | 18 | |-------|-----| 19 | |Frequency:| 10.0| 20 | |Phase:| 0.000°| 21 | 22 | ### Data (raw bits) section: 23 | 24 | | | | 25 | |-------|-----| 26 | |Data:| 1010110010101100| 27 | |Samples per Symbol:| 200000| 28 | |Sample Rate (Sps):| 2.0M| 29 | 30 | ### Modulation section: 31 | 32 | | | | 33 | |-------|-----| 34 | |dropdown menu ▾|Amplitude Shift Keying (ASK)| 35 | |Bits per Symbol:| 1| 36 | |Amplitudes in %:| 0/100| 37 | 38 | - Close the `Edit` window. 39 | 40 | - On the right side under "Generated Data" in the white space right-click and select `add empty message`. 41 | 42 | - Type in 32 and hit `ok`. 43 | 44 | - At the bottom right of the window select `ASCII` from the `viewtype` dropdown menu. 45 | 46 | - This is what your generated data window should now look like. 47 | 48 | ![generated_data.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/generated_data.png?raw=true) 49 | 50 | - Notice that the 32 bit message we selected is now showing up as 4 bytes in the generated data window. 51 | 52 | - Now, add the « character which will serve as a preamble of 10101011 in the first slot, so we will know when our useful data begins. 53 | Here's how: 54 | - The Ubuntu keyboard shortcut: Ctl + Shift + u, then release. Type `ab`. Press enter. 55 | - The Windows keyboard shortcut: Alt + 0171 then release. 56 | - Alternatively, you can just copy and paste the character above. 57 | 58 | - The next step would be to add a simple 3 letter word in the remaining slots. The cursor control is unconventional: you must press `Tab` after each character to move the focus to the next character slot. 59 | 60 | - It should now look like this 61 | 62 | ![catscreenshot.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/catscreenshot.png?raw=true) 63 | 64 | - Select the Pauses tab as shown. 65 | 66 | ![pauses.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/pauses.png?raw=true) 67 | 68 | - Right click and select Edit on the pause in the window, change the Pause Length to 0 and hit ok. 69 | 70 | - Click the Generate file button at the bottom and navigate to your folder and save the file. 71 | - It is not necessary to rename the file, if you do ensure it is still a `.complex` file. 72 | 73 | - Now change tabs to the Interpretation tab. 74 | - Click and drag the file `generated.complex` (or whatever you renamed it) to the grey space on the right. 75 | 76 | ![generated_file.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/generated_file.png?raw=true) 77 | 78 | - Now ensure your settings match the picture above and change the `Show data as` dropdown menu to `ASCII` and you should see your message in the window. 79 | - In the next lesson we will learn how to do the same thing without already knowing all the settings. 80 | 81 | ### ℹ️ Some useful resources for urh: 82 | 83 | - https://github.com/jopohl/urh 84 | 85 | ##

[← Previous Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/020_Modulation.md) -------- [Next Lesson →](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/040_Interpret_unknown_signal.md)

86 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/040_Interpret_unknown_signal.md: -------------------------------------------------------------------------------- 1 | 2 |
Naming history (click to expand) 3 |
 4 | 2023 May 22: 040_Interpret_unknown_signal.md
 5 | 
6 |
7 | 8 | # Interpret a signal using URH 📻 9 | 10 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 11 | 12 | - Launch urh and open your folder. 13 | 14 | - Click on the Interpretation tab at the top of the urh window. 15 | 16 | - Download the following file (link provided) from github [unknown_signal_1.complex](https://github.com/python-can-define-radio/sdr-course/raw/main/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_1.complex). 17 | 18 | - Put it in your folder. 19 | 20 | - It should now look something like this: 21 | - You may have other files showing than the ones you see here. 22 | 23 | ![unknown_signal_1.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1.png?raw=true) 24 | 25 | - On the left side you should see the signal you received. 26 | 27 | - Click and drag it to the grey space on the right to begin interpretation. 28 | 29 | ![unknown_signal_1_interpret.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1_interpret.png?raw=true) 30 | 31 | - Looking at the signal try to determine whether it is ASK, FSK, or PSK modulation. 32 | 33 | - Looking at the signal try to identify a single `0` or `1` and highlight it. 34 | - Ensure you highlight the `smallest` section of signal or lack of signal that you can find. 35 | - You may need to zoom into the signal to see it more clearly. 36 | - It is actually easier to select just a single `0` or `1` in the bottom window but this may be inaccurate because your Samples/Symbol setting may not be correct. 37 | 38 | ![unknown_signal_1_determine_samplerate.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1_determine_samplerate.png?raw=true) 39 | 40 | - When you highlight a portion of the signal urh will tell you how many samples you have selected like in the above image it says right below the highlighted portion "50 selected". 41 | 42 | - If you actually highlighted a single 1 or 0 then we can now change our settings to 50 Samples/Symbol and 1 Bit(s)/Symbol. 43 | - ⚠️ Your signal may be different than this example. 44 | 45 | - Now ensure your settings are correct: 46 | - ⚠️ Your settings may be different than this example. 47 | 48 | ![final_settings.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/final_settings.png?raw=true) 49 | 50 | - If you did everything correctly you should now see your secret message (it will be different than the message shown below). 51 | 52 | ![final_message.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/final_message.png?raw=true) 53 | 54 | ### Additional practice 55 | 56 | For additional practice, you can generate a file using this Python code: 57 | 58 | ```python3 59 | from pcdr.v0_compat import generate_ook_modulated_example_file 60 | generate_ook_modulated_example_file("my_example_ook_file.complex") 61 | ``` 62 | 63 | If you copy and run that, it will create a file named `my_example_ook_file.complex` in your current working directory. You can then try demodulating the message in that file for extra practice. It picks random parameters, so feel free to run it as many times as you like. 64 | 65 | For more info on `generate_ook_modulated_example_file`, look at the docstring: 66 | 67 | ```python3 68 | from pcdr.v0_compat import generate_ook_modulated_example_file 69 | print(generate_ook_modulated_example_file.__doc__) 70 | ``` 71 | 72 | ### ℹ️ Some useful resources for urh: 73 | 74 | - https://github.com/jopohl/urh 75 | 76 | ##

[← Previous Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/030_Generate_a_signal.md) -------- [Next Lesson →](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md)

77 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md: -------------------------------------------------------------------------------- 1 | 2 |
Naming history (click to expand) 3 |
 4 | 2023 May 22: 050_Interpret_unknown_noisy_signal.md
 5 | 
6 |
7 | 8 | # Interpret a signal using URH 📻 9 | 10 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 11 | 12 | - Launch urh and open your folder. 13 | 14 | - Click on the Interpretation tab at the top of the urh window. 15 | 16 | - Download the following file (link provided) from github [unknown_signal_2.complex](https://github.com/python-can-define-radio/sdr-course/raw/main/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_2.complex). 17 | 18 | - Put it in your folder. 19 | 20 | - Click and drag the signal and drop it to the right (On the Interpretation tab). 21 | 22 | - The first thing we need to do is clean up the `noisy` signal by adding a filter. 23 | 24 | - Change the Signal View to `Spectrogram`. (You should see something like this) 25 | 26 | ![unknown_signal_2_spectrogram.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_spectrogram.png?raw=true) 27 | 28 | - Now highlight the signal vertically as shown. 29 | 30 | ![unknown_signal_2_spectrogram_highlight.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_spectrogram_highlight.png?raw=true) 31 | 32 | - Now somewhere in the reddish area right-click and select `Apply bandpass filter (filter bw=0.08)`. 33 | 34 | - Now you should see both signals in the window like this (don't worry if your filtered signal looks different). 35 | 36 | ![unknown_signal_2_with_filtered.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_with_filtered.png?raw=true) 37 | 38 | - Now you can close the first unfiltered signal by clicking the red ✖️ on the top signal. 39 | 40 | - Its probably a good idea to save this new filtered signal, click the 💾 icon in the Interpretation tab window. 41 | 42 | - Give it a new name like `unknown_signal_2_filtered.complex` do not save over your original signal in case you need to start over for any reason. 43 | 44 | - Go back to Signal View `Analog` and determine signals [`modulation`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/020_Modulation.md) type as in previous lesson. 45 | 46 | - Now we need to adjust the `Center` setting. 47 | 48 | - Initially set your `Noise` and your `Center` settings to `0` if it isn't already. 49 | 50 | - Change the Signal View to `Demodulated`. 51 | 52 | - It should look something like the following picture. 53 | - If the signal is "weak" you may need to adjust the Y-Scale, with the slider on the right, to get a better look at the signal in order to set the Center. 54 | 55 | ![unknown_signal_2_demodulated.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_demodulated.png?raw=true) 56 | 57 | - You can click and drag the line (between purple and green) so that it rests somewhere between your high `1` and your low `0` like this. 58 | 59 | 60 | ![unknown_signal_2_centerset.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_centerset.png?raw=true) 61 | 62 | - Determine signals `Samples/Symbol` as in previous lesson. 63 | 64 | - Set Show data as to `ASCII` and verify all settings to reveal your message. 65 | 66 | ### Additional practice 67 | 68 | As in the previous lesson you can also generate noisy signals for practice using this Python code: 69 | 70 | ```python3 71 | from pcdr.v0_compat import generate_ook_modulated_example_file 72 | generate_ook_modulated_example_file("my_example_ook_file.complex", noise=True) 73 | ``` 74 | 75 | ### ℹ️ Some useful resources for urh: 76 | 77 | - https://github.com/jopohl/urh 78 | 79 | ##

[← Previous Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/040_Interpret_unknown_signal.md) -------- [Next Lesson →](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/060_Cropping_a_signal.md)

80 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/060_Cropping_a_signal.md: -------------------------------------------------------------------------------- 1 | 2 |
Naming history (click to expand) 3 |
 4 | 2023 July 11: 060_Cropping_a_signal.md
 5 | 
6 |
7 | 8 | # Interpret a signal using URH 📻 9 | 10 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 11 | 12 | - Launch urh and open your folder. 13 | 14 | - Click on the Interpretation tab at the top of the urh window. 15 | 16 | - Download the following file (link provided) from github [unknown_signal_3.complex](https://github.com/python-can-define-radio/sdr-course/raw/main/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_3.complex). 17 | 18 | - Put it in your folder. 19 | 20 | - Click and drag the signal and drop it to the right (On the Interpretation tab). 21 | 22 | - Clean up your [`noisy`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md) signal by adding a filter like in the previous lesson 23 | 24 | - Determine signals [`Modulation`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/020_Modulation.md) type as in previous lesson. 25 | 26 | - The next thing we need to do is set the [`Center`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md) like the previous lesson. 27 | 28 | - Determine signals [`Samples/Symbol`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/040_Interpret_unknown_signal.md) as in previous lesson. 29 | 30 | - Crop your signal to eliminate leading zeros. 31 | - Since we know our preample starts with a `1` now we can highlight everything past the first `1` in the bits window. 32 | 33 | ![highlightfirst1.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/highlightfirst1.png?raw=true) 34 | 35 | - Right-click in the signal window and select `Crop to selection`. 36 | 37 | ![crop.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/crop.png?raw=true) 38 | 39 | - Set Show data as to `ASCII` and verify all settings to reveal your message. 40 | 41 | ### Additional practice 42 | 43 | Yet another option for the `generate_ook_modulated_example_file` function is `message_delay`: 44 | 45 | ```python3 46 | from pcdr.v0_compat import generate_ook_modulated_example_file 47 | generate_ook_modulated_example_file("my_example_ook_file.complex", noise=True, message_delay=True) 48 | ``` 49 | 50 | ### ℹ️ Some useful resources for urh: 51 | 52 | - https://github.com/jopohl/urh 53 | 54 | ##

[← Previous Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md) -------- [Next Lesson →](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/070_Interpret_multiple_noisy_signals.md)

55 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/070_Interpret_multiple_noisy_signals.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2023 May 22: 060_Interpret_multiple_noisy_signals.md
 4 | 2023 July 10: 070_Interpret_multiple_noisy_signals.md
 5 | 
6 |
7 | 8 | # Interpret a signal using URH 📻 9 | 10 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 11 | 12 | - Launch urh and open your folder. 13 | 14 | - Click on the Interpretation tab at the top of the urh window. 15 | 16 | - Download the following file (link provided) from github [unknown_signal_4.complex](https://github.com/python-can-define-radio/sdr-course/raw/main/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_4.complex). 17 | 18 | - Put it in your folder. 19 | 20 | - The first thing we need to do is seperate and save each signal. 21 | 22 | - The second thing we need to do is clean up each [`noisy`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md) signal by adding a filter like in the previous lesson 23 | 24 | - Go back to Signal View `Analog` and determine signals [`modulation`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/020_Modulation.md) type as in previous lesson. 25 | 26 | - The next thing we need to do is set the [`Center`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md) like the previous lesson. 27 | 28 | - Determine signals [`Samples/Symbol`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/040_Interpret_unknown_signal.md) as in previous lesson. 29 | 30 | - [`Crop`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/060_Cropping_a_signal.md) your signal to eliminate leading zeros. 31 | 32 | - Set Show data as to `ASCII` and verify all settings to reveal your message. 33 | 34 | 35 | ### ℹ️ Some useful resources for urh: 36 | 37 | - https://github.com/jopohl/urh 38 | 39 | ### General order of operations for analyzing a signal: 40 | | | Step | 41 | |-----|-----------| 42 | |1| Separate signals.| 43 | |2| Apply a bandpass filter to eliminate unwanted signal (noise).| 44 | |3| Determine the signal's modulation scheme.| 45 | |4| Determine and set the "Center" between a `1` and a `0`.| 46 | |5| Determine the signal's Sample per Symbol.| 47 | |6| Crop the signal to eliminate the portion without data.| 48 | |7| Verify all settings.| 49 | 50 | ##

[← Previous Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/060_Cropping_a_signal.md) -------- [Next Lesson →](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/080_Interpret_multiple_noisy_signals.md)

51 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/080_Interpret_multiple_noisy_signals.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2023 July 10: 080_Interpret_multiple_noisy_signals.md
 4 | 
5 |
6 | 7 | # Interpret signals using URH 📻 8 | 9 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 10 | 11 | - So far all we have evaluated are ASK signals lets change that up. 12 | 13 | - Launch urh and open your folder. 14 | 15 | - Click on the Interpretation tab at the top of the urh window. 16 | 17 | - Download the following file (link provided) from github [unknown_signal_5.complex](https://github.com/python-can-define-radio/sdr-course/raw/main/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_5.complex). 18 | 19 | - Put it in your folder. 20 | 21 | - The first thing we need to do is seperate and save each signal. 22 | 23 | - The second thing we need to do is clean up each [`noisy`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md) signal by adding a filter like in the previous lesson. 24 | - Before adding a filter to each signal right-click and select `Configure filter bandwidth` and select `Narrow` at 0.01Hz then click ok. 25 | - The closer your signals are to each other the narrower your filter bandwidth should be. 26 | 27 | - Go back to Signal View `Analog` and determine signals [`modulation`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/020_Modulation.md) type as in previous lesson. 28 | 29 | - The next thing we need to do is set the [`Center`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/050_Interpret_unknown_noisy_signal.md) like the previous lesson. 30 | 31 | - Determine signals [`Samples/Symbol`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/040_Interpret_unknown_signal.md) as in previous lesson. 32 | 33 | - [`Crop`](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/060_Cropping_a_signal.md) your signal to eliminate leading zeros. 34 | 35 | - Set Show data as to `ASCII` and verify all settings to reveal your message. 36 | 37 | 38 | ### ℹ️ Some useful resources for urh: 39 | 40 | - https://github.com/jopohl/urh 41 | 42 | ### General order of operations for analyzing a signal: 43 | | | Step | 44 | |-----|-----------| 45 | |1| Separate signals.| 46 | |2| Apply a bandpass filter to eliminate unwanted signal (noise).| 47 | |3| Determine the signal's modulation scheme.| 48 | |4| Determine and set the "Center" between a `1` and a `0`.| 49 | |5| Determine the signal's Sample per Symbol.| 50 | |6| Crop the signal to eliminate the portion without data.| 51 | |7| Verify all settings.| 52 | 53 | ##

[← Previous Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/070_Interpret_multiple_noisy_signals.md) -------- [Next Lesson →](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/090_Record_a_real_signal.md)

54 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/090_Record_a_real_signal.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2023 May 22: 090_Record_a_real_signal.md
 4 | 
5 |
6 | 7 | # Record a signal using URH 📻 8 | 9 | ℹ️ This material coincides with material from SDR slideshow A (slides 7-8, 14, 20, 22, 25, 28-32, 34-39, 40-46). 10 | 11 | - The first thing you will need when recording a signal is to know what frequency you want to record. 12 | 13 | - Open File>Record signal. 14 | 15 | - On the Device dropdown menu select HackRF. 16 | - Note: if you do not see HackRF, see the troubleshooting in footnote 1. 17 | 18 | - On the Device Identifier click the green refresh button. 🔄 (It is green, I couldn't find a more accurate emoji) 19 | 20 | - Set your Frequency (Hz) to 2.45G (GigaHertz). 21 | 22 | - Set you Sample Rate (Sps) to 2.0 Million. 23 | 24 | - Set your Bandwidth (Hz) to 2.0 Million. 25 | 26 | - ⚠️ Do not adjust your RF Gain leave it at 0. 27 | 28 | - Set your IF Gain to 24. 29 | 30 | - Set your BB Gain to 40. 31 | 32 | - It should now look like this: 33 | 34 | ![record_signal.png](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/Images/record_signal.png?raw=true) 35 | 36 | - Press start when you are ready to begin recording. 37 | - Hint: Make sure that whoever is transmitting has their message on repeat. 38 | - You will only need to record for 3-5 seconds or the file will be large. 39 | 40 | - Now you can go to the Interpretation tab and use what you learned previously to Demodulate the message. 41 | 42 | - Once you have been successful feel free to partner up with a classmate and try to generate and send your own messages that your partner can Demodulate. 43 | 44 | ### Optional exercise 45 | 46 | - Incorporate RC Car with URH: 47 | - Simple Record and Replay 48 | - Demodulate and generate: 49 | - Record (Make sure you're offset to avoid DC Spike) 50 | - Demod (Get zeros and ones; use URH's Generate tab to verify) 51 | - Generate and Transmit using Python [pcdr OOK transmit][010_pcdr_ook_tx_intro] 52 | - Use GUIZero to create up/down/left/right buttons to control car 53 | 54 | ### ℹ️ Some useful resources for urh: 55 | 56 | - https://github.com/jopohl/urh 57 | 58 | ##

[← Previous Lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/080_Interpret_multiple_noisy_signals.md)

59 | 60 | 61 | ### Footnotes 62 | 63 | Footnote 1: Troubleshooting: If you do not see the HackRF device option, do the following: 64 | - Go to the main URH window, and click Edit > Options. 65 | - Check the checkbox for HackRF. 66 | - If the checkbox is unavailable, try downgrading to urh version 2.9.4 by running the following command in a terminal: `pip install "urh==2.9.4"` 67 | 68 | 69 | [010_pcdr_ook_tx_intro]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch04_Analyzing_Signals_Python/010_pcdr_ook_tx_intro.md 70 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/099_RC_Car.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
  3 | 2022 Sep 23: 075-RC-Car.md
  4 | 2022 Oct 17: 275-RC-Car.md
  5 | 2023 May 22: 030_RC_Car.md
  6 | 
7 |
8 | 9 | # RC Car 10 | 11 | ## Disclaimer 12 | 13 | Many uses of this are illegal. Stay legal, ethical, moral, honorable, and kind. 14 | 15 | ## Summary 16 | 17 | This project requires the user to have a functional remote-controled (RC) vehicle and its remote controller. Optionally, you may use another remote controlled device and an appropriate remote control sending unit. Other devices that might work include a television and remote controlled light(s), as examples. Of course, both must be tuned to the same communication frequency. 18 | 19 | This involves building two .grc flowgraph programs in **GNU Radio Companion** software. One program records data from the remote control sending unit. A second program transmits the data to the vehicle (or other device) to command it. You can copy and modify the record and replay .grc files created in Exercise 270. As an option, a method for removing unwanted portions of the recorded data is suggested, at least for Linux-OS users. 20 | 21 | This excercise assumes the data is exchanged between the remote control and the vehicle using a fixed communication frequency, without encryption. The method of frequency hopping spread-sectrum (FHSS) is not addressed. 22 | 23 | It is necessary to know the frequency of your devices. You might find the frequency written on your device, in the documentation for your device, or you may perform a web-search using the name and model number of your device. Otherwise, you can look for it using **GNU Radio Companion** or **GQRX**. Finding the frequency is not discussed any further in this exercise/lesson. 24 | 25 | ## Record: 26 | 27 | `rc_car_record.grc` 28 | 29 | ``` 30 | GUI Range 31 | 32 | GUI Range 33 | 34 | osmocom source --> Band pass filter --> Time Sink 35 | --> File Sink 36 | --> Waterfall Sink 37 | ``` 38 | 39 | Parameters: 40 | - Variable (_already in the flowgraph_): 41 | - Id: `samp_rate` 42 | - Value: `2e6` _(You may need more depending on whether the signal fits in the waterfall.)_ 43 | - osmocom Source: 44 | - Device Arguments: `"hackrf=0"` 45 | - Ch0: Frequency (Hz): For the in-class example, `50e6`. In general, you'll have to figure this out based on the device you're working with. I highly recommend tuning off-center from the signal that you are intending to record. Whatever frequency you pick for recording should match the frequency you pick for replaying. 46 | - Ch0: RF Gain (dB): `0` 47 | - Ch0: IF Gain (dB): Will depend on your situation. Try to adjust so that the signal you're recording is approximately -0.6 to 0.6 on the Time Sink. 48 | - Ch0: BB Gain (dB): See note on IF Gain. 49 | - GUI Range (First): 50 | - Id: `IFGain` 51 | - `0` to `40`, steps of `8` 52 | - GUI Range (Second): 53 | - Id: `BBGain` 54 | - `0` to `62`, steps of `2` 55 | - Band pass filter: 56 | - FIR Type: `Complex -> Complex (Complex Taps) (Decim)` 57 | - Low Cut: `-300e3` 58 | - High Cut: `-50e3` (Note: the pass-through region will be off-set from the center frequency) 59 | - Transition Width: `50e3` 60 | - File sink: 61 | - File name: Pick using the "...". Make sure to pick a file that does NOT exist. One good name could be `my_car_recording.iqdata`. Do NOT pick a `.grc` file.
Details if you're curious: When faced with a file-picking dialog, beginners will often navigate to the current `.grc` file. This is definitely not what you want -- as soon as you run the flowgraph, it will overwrite the saved flowgraph file with the data that you're recording. 62 | 63 | Instead, I recommend picking a directory for your file, and naming it `my_gnu_recording.iqdata`. The file extension can be anything you want (GNU Radio will treat it the same regardless), but `.iqdata` seems somewhat common in the SDR community. 64 | A good example may look like this: 65 | `/home/yourusername/Desktop/my_gnu_recording.iq` 66 |
67 | 68 | ## Replay: 69 | 70 | `rc_car_replay.grc` 71 | 72 | ``` 73 | GUI Range 74 | 75 | File Source --> osmocom sink 76 | --> Time sink 77 | --> Waterfall Sink 78 | ``` 79 | 80 | Parameters: 81 | - Variable (_already in the flowgraph_): 82 | - Id: `samp_rate` 83 | - Value: _match your recording sample rate_ 84 | - osmocom Sink: 85 | - Device Arguments: `"hackrf=0"` 86 | - Ch0: Frequency (Hz): _match your recording Ch0 Freq` 87 | - Ch0: RF Gain (dB): `0` 88 | - Ch0: IF Gain (dB): `IFGain` 89 | - Ch0: BB Gain (dB): `0` 90 | - GUI Range: 91 | - Id: `IFGain` 92 | - 0 to 47, steps of 1 93 | - File Source: 94 | - Pick your `.iqdata` file (must make the recording first). 95 | 96 | Optionally, you can add a selector in the middle on to allow choosing from multiple recordings. 97 | 98 | ## To edit the data file prior to replay (Optional) 99 | Linux OS users might find and use **"inspectrum"** software to edit the data file(s) recorded from the RC vehicled remote control unit. The software may be available on github.com or another source. Other software might be found having suitable capabilitiees. The student or end-user will need to determine the suitability and appropriateness of any software he or she loads. 100 | -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/ASK_modulated_signal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/ASK_modulated_signal.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/amplitude25%.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/amplitude25%.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/catscreenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/catscreenshot.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/crop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/crop.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/final_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/final_message.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/final_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/final_settings.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/fsk_highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/fsk_highlight.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/fsk_modulation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/fsk_modulation.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/generated_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/generated_data.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/generated_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/generated_file.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/highlightfirst1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/highlightfirst1.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/pauses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/pauses.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/psk_highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/psk_highlight.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/psk_modulation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/psk_modulation.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/record_signal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/record_signal.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/singlebithighlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/singlebithighlighted.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1_determine_samplerate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1_determine_samplerate.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1_interpret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_1_interpret.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_centerset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_centerset.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_demodulated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_demodulated.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_determine_samplerate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_determine_samplerate.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_interpret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_interpret.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_spectrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_spectrogram.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_spectrogram_highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_spectrogram_highlight.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_with_filtered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/unknown_signal_2_with_filtered.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/Images/urh_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/Images/urh_screenshot.png -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_1.complex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_1.complex -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_2.complex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_2.complex -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_3.complex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_3.complex -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_4.complex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_4.complex -------------------------------------------------------------------------------- /classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_5.complex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/classroom_activities/Ch03_Analyzing_Signals_URH/unknown_signal_5.complex -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/005_pcdr_single_freq_transmit.md: -------------------------------------------------------------------------------- 1 | ## Disclaimer 2 | 3 | Broadcasting without a license is illegal in most countries. This should only be used for research purposes in an environment that is sufficiently radio-shielded from other electronics. 4 | 5 | ## Transmitting on a Single Frequency 6 | 7 | This lesson demonstrates the `OsmoSingleFrequencyTransmitter`, which transmits a pure sine wave on a specified frequency. It provides an introduction to methods such as `set_center_freq` which we will use in the Wide-Band-Frequency-Modulator (WBFM). 8 | 9 | ```python3 10 | ## 1 11 | ## Try this. 12 | import time 13 | from pcdr.unstable.flow import OsmoSingleFreqTransmitter 14 | transmitter = OsmoSingleFreqTransmitter("hackrf=0", 2.45e9) 15 | transmitter.start() 16 | print() 17 | print("Transmission started.") 18 | transmitter.set_if_gain(37) 19 | time.sleep(1) 20 | transmitter.set_center_freq(2.4501e9) 21 | time.sleep(1) 22 | transmitter.set_center_freq(2.4502e9) 23 | time.sleep(1) 24 | print("Stopping...") 25 | transmitter.stop_and_wait() 26 | 27 | 28 | ## 2 29 | ## Try setting the transmitter to the following frequencies: 30 | ## 2.4503 GHz 31 | ## 2.4505 GHz 32 | ## 2450.4 MHz 33 | 34 | 35 | ## 3 36 | ## Try using set_if_gain with a variety of numbers. 37 | ## What are the highest and lowest allowable gains? 38 | 39 | 40 | ## 3 41 | ## Try changing the sleep time to adjust how long the 42 | ## transmitter spends on each frequency. 43 | ## You'll notice that stop_and_wait takes a noticeable amount of extra time, 44 | ## about 1 second at time of writing. 45 | ## We, the authors, do not know a way to quickly 46 | ## stop a transmission, but one alternative option 47 | ## is to lower the IF gain -- 48 | ## for example, set_if_gain(0). 49 | ## This is not truly OFF, but it makes the 50 | ## transmission relatively low power. 51 | 52 | 53 | ## 4 54 | ## Try transmitting on top of a commercial FM broadcast. 55 | ## How does it impact the broadcast? 56 | 57 | 58 | ## 5 59 | ## Using Python, loop the following: 60 | ## - Set the frequency to an FM broadcast station 61 | ## - Jam it for half a second 62 | ## - Set the frequency to a different FM broadcast station 63 | ## - Jam it for half a second 64 | 65 | 66 | ## 6 67 | ## Copy and modify the previous example. 68 | ## In this version, instead of cycling between the two stations, 69 | ## randomly pick one every half second. 70 | ## Then, try the same exercise with three stations. 71 | ``` 72 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/030_pcdr_frequency_domain_Real.md: -------------------------------------------------------------------------------- 1 | # Frequency Domain and Real Waves 2 | 3 | ℹ️ This material coincides with material from python slideshows B (slides 1-11, 16, 24, 39, 54-63, 69) C (slides 30-38) and D (slides 4-15, 31-37), and SDR slideshow A (slides 22, 25, 28-32, 40-42). 4 | 5 | ### Viewing added waves in the Time domain 6 | 7 | When the signal is made up of a single wave, we can fairly easily identify the frequency of that wave by simply looking at a plot of the wave's amplitude over time. Even if there are two frequencies present, it's not too difficult to distinguish the frequencies that are present: 8 | 9 | ```python3 10 | ## 1 11 | ## Try this example, which plots a 2 Hz wave and a 25 Hz wave. 12 | ## Notice that we're only plotting the sum of the waves. 13 | ## Can you still distinguish the two separate frequencies? 14 | import matplotlib.pyplot as plt 15 | from pcdr.v0_compat import makeWave 16 | 17 | seconds = 2 18 | samp_rate = 300 19 | timestamps, wave1 = makeWave(samp_rate, 2, "real", seconds=seconds) 20 | timestamps, wave2 = makeWave(samp_rate, 25, "real", seconds=seconds) 21 | added = wave1 + wave2 22 | plt.plot(timestamps, added, "*-", markersize=5) 23 | plt.show() 24 | 25 | 26 | ## 2 27 | ## Copy and modify the previous example. 28 | ## Change the first wave's frequency to 3 Hz. 29 | ## Can you still distinguish the two waves? 30 | 31 | 32 | ## 3 33 | ## Copy and modify the previous example. 34 | ## Change the first wave's frequency to 11 Hz. 35 | ## Notice that distinguishing the two waves is more difficult. 36 | ``` 37 | 38 | We've been looking at the **Time Domain** view of the wave. In other words, we're viewing the measured samples over a period of time. As you saw in the most recent example, there can be difficulties distinguishishing the frequencies that are present. 39 | 40 | ### The frequency domain 41 | 42 | The solution to the limitations of the Time Domain is to view the signal in the **Frequency Domain**. Here's an example: 43 | 44 | ```python3 45 | ## 4 46 | import matplotlib.pyplot as plt 47 | from pcdr.v0_compat import makeWave, make_fft_positive_freqs_only 48 | 49 | maxTime = 2 50 | samp_rate = 300 51 | timestamps, wave = makeWave(samp_rate, 10, "real", seconds=maxTime) 52 | sample_freqs, fft_mag = make_fft_positive_freqs_only(wave, samp_rate) 53 | plt.subplot(2, 1, 1, title="Time Domain") 54 | plt.plot(timestamps, wave, "*-", markersize=5) 55 | plt.subplot(2, 1, 2, title="Frequency Domain") 56 | plt.plot(sample_freqs, fft_mag, '.g-') 57 | plt.show() 58 | ``` 59 | 60 | In the window that opens, you'll see two subplots, labeled "Time Domain" and "Frequency Domain". Notice that there is a spike in the Frequency Domain at 10 Hz to indicate the frequency that is present in this signal. 61 | 62 | ```python3 63 | ## 5 64 | ## Plot the time domain and the frequency domain for 65 | ## a (real) sine wave with frequency = 4 Hz. 66 | ``` 67 | 68 | Let's try it with two signals added: 69 | 70 | ```python3 71 | ## 6 72 | ## Try this. 73 | import matplotlib.pyplot as plt 74 | from pcdr.v0_compat import makeWave, make_fft_positive_freqs_only 75 | 76 | maxTime = 2 77 | samp_rate = 300 78 | timestamps, wave1 = makeWave(samp_rate, 2, "real", seconds=maxTime) 79 | timestamps, wave2 = makeWave(samp_rate, 25, "real", seconds=maxTime) 80 | added = wave1 + wave2 81 | sample_freqs, fft_mag = make_fft_positive_freqs_only(added, samp_rate) 82 | plt.subplot(2, 1, 1, title="Time Domain") 83 | plt.plot(timestamps, added, "*-", markersize=5) 84 | plt.subplot(2, 1, 2, title="Frequency Domain") 85 | plt.plot(sample_freqs, fft_mag, '.g-') 86 | plt.tight_layout() 87 | plt.show() 88 | ``` 89 | 90 | In this example, the Frequency Domain has two spikes, located at 2 Hz and 25 Hz. 91 | 92 | ```python3 93 | ## 7 94 | ## Copy and modify the previous example. 95 | ## Change this line: 96 | ## added = wave1 + wave2 97 | ## ...to this: 98 | ### added = wave1 + (0.5*wave2) 99 | ``` 100 | 101 | Notice that with the `0.5` modification, the amplitude of the 25 Hz wave is now half of the amplitude of the 2 Hz wave. 102 | 103 | Also notice that this change is visible in both the time domain and the frequency domain, but it is much more obvious in the frequency domain. 104 | 105 | For the next exercise, you may wish to refer to the [matplotlib subplot command documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplot.html). 106 | 107 | ```python3 108 | ## 8 109 | ## Copy modify the previous example so that it has four subplots: 110 | ## - The original wave (wave1 + wave2), displayed in the time domain 111 | ## - The original wave (wave1 + wave2), displayed in the frequency domain 112 | ## - The new wave (wave1 + (0.5*wave2)), displayed in the time domain 113 | ## - The new wave (wave1 + (0.5*wave2)), displayed in the frequency domain 114 | ## You may wish to consult the matplotlib subplot documentation listed above. 115 | ``` 116 | 117 | ### The Dark Side of the Frequency Domain (Negative Frequencies) 118 | 119 | Up to this point, we've been using the function `make_fft_positive_freqs_only`. The name begs the question -- why are we only looking at only positive frequencies? What happens if you use `make_fft` instead? Let's try it. 120 | 121 | ```python3 122 | ## 9 123 | ## Try this. 124 | import matplotlib.pyplot as plt 125 | from pcdr.v0_compat import makeWave, make_fft 126 | 127 | maxTime = 2 128 | samp_rate = 300 129 | timestamps, wave1 = makeWave(samp_rate, 2, "real", seconds=maxTime) 130 | timestamps, wave2 = makeWave(samp_rate, 25, "real", seconds=maxTime) 131 | added = wave1 + (0.5*wave2) 132 | sample_freqs, fft_mag = make_fft(added, samp_rate) 133 | plt.subplot(2, 1, 1, title="Time Domain") 134 | plt.plot(timestamps, added, "*-", markersize=5) 135 | plt.subplot(2, 1, 2, title="Frequency Domain") 136 | plt.plot(sample_freqs, fft_mag, '.g-', markersize=4) 137 | plt.tight_layout() 138 | plt.show() 139 | ``` 140 | 141 | Notice in the frequency domain that there are now four spikes: -25 Hz, -2 Hz, +2 Hz, +25 Hz. 142 | 143 | Let's try a different signal: 144 | 145 | ```python3 146 | ## 10 147 | ## Copy and modify the previous example. Change both frequencies. 148 | ``` 149 | 150 | You'll notice that no matter what frequencies you pick, the Frequency Domain will always be symmetric. 151 | 152 | You may wonder: why we would ever display the negative frequencies if they'll always be the same as their positive counterparts? The answer: for real signals, the Frequency Domain will always be symmetric, but for signals that are not purely real, they won't necessarily be symmetric. 153 | 154 | _A non-real signal? What does that mean?_ See the next lesson for the answer! 155 | 156 | A good video showing how the position of a circle corresponds to the values on a sine wave. 157 | [sine_wave_video](https://www.youtube.com/watch?v=k8FXF1KjzY0&t=4s) 158 | 159 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/040_pcdr_frequency_domain_Complex.md: -------------------------------------------------------------------------------- 1 | # Frequency Domain and Complex waves 2 | 3 | ℹ️ This material coincides with material from python slideshows B (slides 1-11, 16, 24, 39, 54-63, 69) C (slides 30-38) and D (slides 4-15, 31-37), and SDR slideshow A (slides 22, 25, 28-32, 40-42). 4 | 5 | In the previous lesson, all of the waves that we generated and plotted were real waves. In other words, they had only a real component, and no imaginary component. This lesson will introduce complex waves, which are those that have both a real and an imaginary component. In an SDR context, these are often called IQ signals, which means In-phase (I) and Quadrature (Q). The I refers to the real part, and the Q refers to the imaginary part. 6 | 7 | ### Why are we working with imaginary numbers? 8 | 9 | The math behind SDRs (and, consequently, the software used with SDRs) is based on complex numbers. This has a few advantages. One such advantage: 10 | **Complex numbers allow for a representation of both positive and negative frequencies.** 11 | 12 | ### Negative Frequencies 13 | 14 | Often, when beginning to work with SDRs, people ask, "What is a negative frequency?" The question is reasonable, as it seems that if something is shaking twice per second, it doesn't matter which direction it's shaking — it's simply shaking twice per second. 15 | 16 | The PySDR website has a [useful diagram](https://pysdr.org/content/frequency_domain.html#negative-frequencies) for this. You'll see that their SDR's center frequency is tuned to 100 MHz. When the HackRF One gives the signal to the computer via the USB cable, the signals will have been **downconverted**. That means... 17 | 18 | - The "yellow trapezoid" signal, which was ≈ 104 MHz, will be downconverted to ≈ +4 MHz. 19 | - The "green trapezoid" signal, which was ≈ 100 MHz, will be downconverted to ≈ 0 MHz. 20 | - The "blue triangle" signal, which was ≈ 97.5 MHz, will be downconverted to ≈ -2.5 MHz. 21 | 22 | Notice that all three frequencies are still represented, even though one is now negative. They are now shifted to near zero based on where they were in relation to your tuned frequency. 23 | 24 | So, how do you represent a negative frequency? Let's demonstrate that in a plot using Python. 25 | 26 | ```python3 27 | ## 1 28 | ## Try this. 29 | import matplotlib.pyplot as plt 30 | from pcdr.v0_compat import makeWave, make_fft 31 | 32 | samp_rate = 50 33 | freq = -2 34 | 35 | timestamps, wave = makeWave(samp_rate, freq, "complex", seconds=2) 36 | sample_freqs, fft_mag = make_fft(wave, samp_rate) 37 | 38 | plt.subplot(2, 1, 1, title=f"{freq} Hz Wave: Time Domain") 39 | plt.plot(timestamps, wave.real, "^-", color="blue", label="Real") 40 | plt.plot(timestamps, wave.imag, "*-", color="red", label="Imag") 41 | plt.legend(loc="upper right") 42 | 43 | plt.subplot(2, 1, 2, title=f"{freq} Hz Wave: Frequency Domain") 44 | plt.plot(sample_freqs, fft_mag, ".g-") 45 | 46 | plt.tight_layout() 47 | plt.show() 48 | 49 | 50 | ## 2 51 | ## Copy and modify the previous example. Set the frequency to +2 Hz. 52 | ``` 53 | 54 | Notice that in the Frequency Domain, there's only one spike, which occurs at the chosen frequency. 55 | 56 | Can you see the difference between the Time Domain plots? 57 |
Click here for answer... 58 | 59 | - For the wave with positive frequency, the real (blue) part is one-quarter-cycle BEFORE the imaginary (red) part. 60 | - For the wave with negative frequency, the real (blue) part is one-quarter-cycle AFTER the imaginary (red) part. 61 | 62 |
63 | 64 | If you'd like to delve into the underlying concepts, Arachnoid.com provides a [great explanation](https://arachnoid.com/software_defined_radios/#Theory__The_Frequency_Domain) (along with a [really cool interactive Javascript applet](https://arachnoid.com/software_defined_radios/#Theory__I_Q_Exploration_Applet)) for exploring IQ signals. However, a basic understanding will suffice for accomplishing most tasks. 65 | 66 | Let's get some more practice working with complex waves. 67 | 68 | ```python3 69 | ## 3 70 | ## Use the makeWave function to make a complex wave with frequency 3 Hz. 71 | ## Plot the wave from 0 to 4 seconds. 72 | 73 | 74 | ## 4 75 | ## Use the makeWave function to make a complex wave with frequency 0.5 Hz. 76 | ## Plot the wave from 0 to 2 seconds. 77 | ``` 78 | 79 | ### Adding complex waves (Addition forthcoming) 80 | 81 | - do two ook signals 82 | - save to file, demod with urh 83 | 84 | ## Resources about Complex waves & Quadrature mapping of waves 85 | 86 | - "Basics of IQ [Quadature] Signals and IQ modulation & demodulation - A tutorial", by user w2aew, https://www.youtube.com/watch?v=h_7d-m1ehoY 87 | ("channel of author: https://www.youtube.com/@w2aew) 88 | - Good explanations using Phasordiagrams & Oscilloscope obervations (Time stamps listed here are approximate.) 89 | - 3:31 "Two signals are said to be in quadrature when they are 90 degrees apart." 90 | - 11:14 BPSC modulation using I(t) & Q(t) digital signals 91 | - ~14:00 QPSK modulation 92 | 93 | - "Quadrature Mixers, IQ Demodulation, and the Tayloe Detector" video, by user devttys0, https://www.youtube.com/watch?v=JuuKF1RFvBM 94 | - Good explanations using diagrams & Oscilloscope obervations 95 | - 31:00 Diagram of a Tayoloe 4-to-1 mutliplexer configured to mix an RF & an oscilator signal to get I & Q quadrature signals. 96 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/in_progress/130_pcdr_tx_2.md: -------------------------------------------------------------------------------- 1 | ℹ️ This material coincides with material from python slideshows B (slides 1-11, 16, 24, 39, 54-63, 69) C (slides 30-38) and D (slides 4-15, 31-37), and SDR slideshow A (slides 22, 25, 28-32, 40-42). 2 | 3 | 4 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/in_progress/140_pcdr_rx_intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to Receving in Python 2 | 3 | ℹ️ This material coincides with material from python slideshows B (slides 1-11, 16, 24, 39, 54-63, 69) C (slides 30-38) and D (slides 4-15, 31-37), and SDR slideshow A (slides 22, 25, 28-32, 40-42). 4 | 5 | There are at least three challenges that arise on the receiving end: 6 | 7 | - Noise 8 | - Signal strength variation 9 | - Timing 10 | 11 | We'll address each of these in more detail, but first, we'll examine a minimal simulated receiver. Then, for those who have SDR hardware, we'll receive some data from the air. 12 | 13 | ```python3 14 | receiver = gnuradio_ 15 | while True: 16 | TODO: get some data 17 | 18 | ``` 19 | 20 | - TODO: Plot FFT 21 | - TODO: What is maximum frequency in FFT? 22 | - TODO: Which frequencies in FFT surpass some threshold? 23 | 24 | ------- 25 | 26 | TODO: discuss harmonics and reflections (note that the latter is not the proper term. Referring to a signal appearing in multiple locations on an FFT.) 27 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/in_progress/150_pcdr_ook_rx_intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to OOK Receving in Python 2 | 3 | ℹ️ This material coincides with material from python slideshows B (slides 1-11, 16, 24, 39, 54-63, 69) C (slides 30-38) and D (slides 4-15, 31-37), and SDR slideshow A (slides 22, 25, 28-32, 40-42). 4 | 5 | At this point, we've modulated and transmitted On-Off Keyed binary data using Python. Unfortunately, receiving and demodulating is more challenging. 6 | 7 | 8 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/in_progress/230_GnuRadio_Dashing_Block_Example_Python.py: -------------------------------------------------------------------------------- 1 | """ 2 | Commandline functionality for displaying signal data in the terminal using 3 | the dashing module built on blessed. 4 | https://github.com/FedericoCeratto/dashing 5 | """ 6 | 7 | from __future__ import annotations 8 | from gnuradio import gr 9 | from gnuradio import blocks 10 | import time 11 | import numpy as np 12 | import sys 13 | import signal 14 | from dashing import HChart 15 | 16 | 17 | 18 | def configure_exit_signal(tb: gr.top_block): 19 | """The portion of GNU Radio boilerplate that 20 | catches SIGINT and SIGTERM, and tells the flowgraph 21 | to gracefully stop before exiting the program. 22 | 23 | Used mainly for non-graphical flowgraphs.""" 24 | def sig_handler(sig=None, frame=None): 25 | tb.stop() 26 | tb.wait() 27 | sys.exit(0) 28 | 29 | signal.signal(signal.SIGINT, sig_handler) 30 | signal.signal(signal.SIGTERM, sig_handler) 31 | 32 | 33 | class dashing_display(gr.sync_block): 34 | 35 | def __init__(self, sleep_seconds=1.0/90): 36 | gr.sync_block.__init__( 37 | self, 38 | name="Print block", 39 | in_sig=[np.complex64], 40 | out_sig=[] 41 | ) 42 | self.ui = HChart(title="Some Horizontal Chart", color=7, border_color=2) 43 | self.sleep_seconds = sleep_seconds 44 | 45 | def work(self, input_items, output_items): 46 | singleDataPoint = input_items[0][0] 47 | self.ui.append(singleDataPoint) 48 | self.ui.display() 49 | time.sleep(self.sleep_seconds) 50 | return 1 51 | 52 | class Random_Signal_Generator(gr.top_block): 53 | def __init__(self): 54 | gr.top_block.__init__(self, "Top block") 55 | d = np.array([2, 10, 50], dtype=np.complex64) 56 | self.myFirstBlock = blocks.vector_source_c(d, repeat=True) 57 | self.dashing_display = dashing_display() 58 | self.connect(self.myFirstBlock, self.dashing_display) 59 | 60 | tb = Random_Signal_Generator() 61 | configure_exit_signal(tb) 62 | tb.start() 63 | while True: 64 | time.sleep(0.25) 65 | 66 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/in_progress/activity_detector_v0_2.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import time 3 | from pcdr.flow import OsmoSingleFreqReceiver 4 | import os 5 | from playsound import playsound 6 | 7 | 8 | def safePlay(fn, block): 9 | try: 10 | playsound(fn, block=block) 11 | except Exception: 12 | print("Failed to play the sound.") 13 | 14 | 15 | rec = OsmoSingleFreqReceiver("hackrf=0", 462.7e6) 16 | rec.start() 17 | short_avg = 0 18 | long_avg = 5 # Arbitrary; adjust to taste 19 | count = 0 20 | start_time = time.time() 21 | fn = "activity.csv" 22 | if os.path.exists(fn): 23 | newfile = False 24 | else: 25 | newfile = True 26 | with open(fn, "a", encoding="utf-8") as f: 27 | if newfile: 28 | f.write("passedThresh,date,time,unix_timestamp,time_since_start,strength,short_avg,long_avg\n") 29 | 30 | while True: 31 | stren = rec.get_strength() 32 | short_avg = 0.9*short_avg + 0.1*stren 33 | long_avg = 0.9999*long_avg + 0.0001*stren 34 | count += 1 35 | if count == 100: # Record every hundredth 36 | count = 0 37 | 38 | nowtime = time.time() 39 | timedelta = nowtime - start_time 40 | dt = datetime.datetime.fromtimestamp(nowtime) 41 | print(f"{dt} Recording to file.") 42 | 43 | if short_avg > 1.2 * long_avg: 44 | safePlay("Bell.wav", block=False) 45 | print("BIG SPIKE") 46 | f.write("BIG SPIKE,") 47 | elif short_avg > 1.1 * long_avg: 48 | safePlay("Buzzer.wav", block=False) 49 | print("SPIKE") 50 | f.write("SPIKE,") 51 | else: 52 | f.write("NO,") 53 | f.write(f"{dt.date()},{dt.time()},{nowtime},{timedelta},{stren},{short_avg},{long_avg}\n") 54 | f.flush() 55 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/in_progress/activity_recorder.md: -------------------------------------------------------------------------------- 1 | ```python3 2 | import datetime 3 | import time 4 | from pcdr.flow import OsmoSingleFreqReceiver 5 | import os 6 | 7 | 8 | rec = OsmoSingleFreqReceiver("hackrf=0", 104.1e6) 9 | rec.start() 10 | avg = 0 11 | count = 0 12 | fn = "activity.csv" 13 | if os.path.exists(fn): 14 | newfile = False 15 | else: 16 | newfile = True 17 | with open(fn, "a", encoding="utf-8") as f: 18 | if newfile: 19 | f.write("date,time,unix_timestamp,strength,avg_strength\n") 20 | while True: 21 | stren = rec.get_strength() 22 | avg = 0.99*avg + 0.01*stren 23 | 24 | count += 1 25 | if count == 100: # Record every hundredth 26 | count = 0 27 | nowtime = time.time() 28 | dt = datetime.datetime.fromtimestamp(nowtime) 29 | print(f"{dt} Activity. Recording to file.") 30 | f.write(f"{dt.date()},{dt.time()},{nowtime},{stren},{avg}\n") 31 | f.flush() 32 | 33 | ``` 34 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/in_progress/dash_plot_examples.md: -------------------------------------------------------------------------------- 1 | ### Plotting from a given list of integers 2 | Create a scatter plot via updating list. Random values are generated into list for this example. 3 | ```python3 4 | import dash, random 5 | from dash import Dash, dcc, html, Input, Output, callback, Patch 6 | import plotly.express as px 7 | 8 | gui = Dash(__name__) 9 | gui.layout = html.Div( # structures web app 10 | html.Div([ 11 | html.H4('Frequency counter'), 12 | dcc.Graph(id='live-update-graph',style={'overflow-x': 'scroll'}), 13 | dcc.Interval(id='interval-component', interval=50), 14 | ]) 15 | ) 16 | x = [] 17 | y = [] 18 | count = 0 19 | 20 | @callback(Output('live-update-graph', 'figure'), 21 | Input('interval-component', 'n_intervals')) 22 | def live_status(n): # generate data and plot 23 | global count 24 | count += 1 25 | x.append(count) 26 | y.append(random.randint(1,5)) 27 | fig = px.scatter(x=x, y=y,) 28 | fig.update_layout(xaxis_range=[0,count+100], width=count+10000) 29 | return fig 30 | 31 | if __name__ == '__main__': # run web app 32 | gui.run(debug=True,port=5000) 33 | ``` 34 | ### Plotting a noisy sine wave 35 | Create a noisy sine wave using a scatter plot. Hz count can be adjusted via slider. 36 | ```python3 37 | import dash, random 38 | from dash import Dash, dcc, html, Input, Output, callback 39 | import plotly.express as px 40 | import numpy as np 41 | 42 | gui = Dash(__name__) 43 | gui.layout = html.Div( # structures web app 44 | html.Div([ 45 | html.H4('Frequency counter'), 46 | dcc.Graph(id='live-update-graph'), 47 | dcc.Interval(id='interval-component', interval=50), 48 | dcc.Slider(1,5,1, value=10,id='my-slider'), 49 | html.Div(id='slider-output-container') 50 | ]) 51 | ) 52 | 53 | @callback(Output('live-update-graph', 'figure'), 54 | Input('interval-component', 'n_intervals'), 55 | Input('my-slider', 'value')) 56 | def live_status(n, slidervalue): # generate data and plot 57 | rng = np.random.default_rng() 58 | noise = rng.standard_normal(1024) #1024 points of noise 59 | x = np.linspace(0, 4, 1024) 60 | cycle = 2*3.14*x 61 | Hz = cycle * slidervalue 62 | y = np.sin(Hz) + 0.1*noise 63 | fig = px.scatter(x=x, y=y,) 64 | fig.update_layout(xaxis_range=[0,4], yaxis_range=[-1.5,1.5]) 65 | return fig 66 | 67 | if __name__ == '__main__': # run web app 68 | gui.run(debug=True,port=5000) 69 | ``` 70 | -------------------------------------------------------------------------------- /classroom_activities/Ch04_Analyzing_Signals_Python/in_progress/pcdr_simple_wbfm_transmit.md: -------------------------------------------------------------------------------- 1 | ## Disclaimer 2 | 3 | Broadcasting without a license is illegal in most countries. This should only be used for research purposes in an environment that is sufficiently radio-shielded from other electronics. 4 | 5 | ## WBFM Transmitting 6 | 7 | This lesson shows how to use the pcdr module to transmit Wide-Band-Frequency-Modulated audio. 8 | 9 | However, before we use the SDR hardware, let's verify that we can play an audio file locally. 10 | 11 | ```python3 12 | ## 1 13 | ## Try this. It will play audio locally. 14 | ## You must provide a wav file. 15 | ## One possible wav file is drum_cowbell.wav from here: 16 | ## https://github.com/adafruit/Adafruit-Sound-Samples/tree/master/sonic-pi 17 | ## Note: For the file to play at the proper speed, you must 18 | ## set the audio_sample_rate based on the properties of 19 | ## the wav file. You can view that in a GUI file browser, 20 | ## or using the `file` terminal command on GNU Linux: 21 | ## file drum_cowbell.wav 22 | ## ^^^^^^^^^^^^^^^^ replace this part with your wav file's name. 23 | ## Alternatively, set the audio_sample_rate to 60_000 and 24 | ## then adjust based on how it sounds. 25 | import pcdr.simple 26 | import time 27 | 28 | player = pcdr.simple.AudioPlayer( 29 | wavfile="drum_cowbell.wav", 30 | audio_sample_rate=22050 31 | ) 32 | player.start() 33 | player.wait() 34 | 35 | 36 | ## 2 37 | ## Try this variation. What is different? 38 | import pcdr.simple 39 | import time 40 | 41 | player = pcdr.simple.AudioPlayer( 42 | wavfile="drum_cowbell.wav", 43 | audio_sample_rate=22050, 44 | repeat=True 45 | ) 46 | player.start() 47 | time.sleep(3) 48 | player.stop_and_wait() 49 | ``` 50 | 51 | Now, using the hardware: 52 | 53 | ``` 54 | ## 3 55 | ## Try this. 56 | import pcdr.simple 57 | import time 58 | transmitter = pcdr.simple.OsmosdrWBFMTransmitter( 59 | "hackrf=0", 60 | freq=2.45e9, 61 | wavfile="drum_cowbell.wav", 62 | repeat=True, 63 | audio_sample_rate=22050 64 | ) 65 | transmitter.start() 66 | transmitter.set_if_gain(37) 67 | time.sleep(10) 68 | transmitter.stop_and_wait() 69 | 70 | 71 | ## 4 72 | ## Try changing the transmission frequency by 73 | ## changing the 2.45e9 in the above example. 74 | 75 | 76 | ## 5 77 | ## Try this. What is different? 78 | import pcdr.simple 79 | import time 80 | transmitter = pcdr.simple.OsmosdrWBFMTransmitter( 81 | "hackrf=0", 82 | freq=2.45e9, 83 | wavfile="drum_cowbell.wav", 84 | repeat=True, 85 | audio_sample_rate=22050 86 | ) 87 | transmitter.start() 88 | transmitter.set_if_gain(37) 89 | time.sleep(2) 90 | transmitter.set_center_freq(2.4501e9) 91 | time.sleep(2) 92 | transmitter.stop_and_wait() 93 | 94 | 95 | ## 6 96 | ## Using Python, do the following: 97 | ## - Set the frequency to an FM broadcast station 98 | ## - Jam it for half a second 99 | ## - Set the frequency to a different FM broadcast station 100 | ## - Jam it for half a second 101 | 102 | 103 | ## 7 104 | ## Copy and modify the previous exercise: 105 | ## Loop the actions forever using a `while` loop. 106 | 107 | 108 | ## 8 109 | ## Copy and modify the previous example. 110 | ## In this version, instead of cycling between the two stations, 111 | ## randomly pick every half second. 112 | ## Then, try the same exercise with three stations. 113 | ``` 114 | 115 | Now that you've seen how to transmit a `.wav` file, here's how to use the "pulse_monitor" source to transmit whatever is currently playing on your computer. 116 | 117 | Note that this method only works on a GNU Linux OS, and only after doing [this configuration](https://wiki.gnuradio.org/index.php?title=ALSAPulseAudio#Monitoring_the_audio_input_of_your_system_with_PulseAudio). 118 | 119 | Also, if you have multiple output devices (for example, if you have headphones plugged in), you may have to either unplug the headphones or change your `~/.asoundrc` file. 120 | 121 | ```python3 122 | ## 8 123 | ## Try this. 124 | import pcdr.simple 125 | import time 126 | transmitter = pcdr.simple.OsmosdrWBFMTransmitter("hackrf=0", 2.45e9, audio_device="pulse_monitor") 127 | transmitter.start() 128 | transmitter.set_if_gain(37) 129 | time.sleep(1) 130 | transmitter.set_center_freq(2.4501e9) 131 | time.sleep(1) 132 | transmitter.set_center_freq(2.4502e9) 133 | 134 | ``` 135 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/010-Transmit-and-Receive-Pure-Sine.md: -------------------------------------------------------------------------------- 1 | **Disclaimer**: Broadcasting without a license is illegal in many countries. This should only be used for research purposes in an environment that is sufficiently radio-shielded from other electronics. 2 | 3 | # Transmitting and receiving a pure sine wave 4 | 5 | ℹ️ This material coincides with material from SDR slideshow B (slides 56-63). 6 | 7 | Since the Hack RF is half-duplex, you'll need two flowgraphs to do this activity: one for transmit, and one for receive. 8 | 9 | The Instructor will assign work groups. Coordinate within your assigned group so that one student transmits, and the other receives. Then rotate roles. 10 | 11 | You'll also notice that the osmocom frequencies are unspecified. Use the frequency band assigned to your group: 12 | 13 | - Group 0: `2.40e9` 14 | - Group 1: `2.41e9` 15 | - Group 2: `2.42e9` 16 | - Group 3: `2.43e9` 17 | - Group 4: `2.44e9` 18 | - Group 5: `2.45e9` 19 | - Group 6: `2.46e9` 20 | - Group 7: `2.47e9` 21 | - Group 8: `2.48e9` 22 | - Group 9: `2.49e9` 23 | 24 | _Practical note:_ When retracting the antenna, please push gently in the center of the antenna. Pushing at the top causes the antenna to bend. 25 | 26 | ## Flowgraph #1: transmit_pure_sine.grc 27 | 28 | ``` 29 | GUI Range 30 | 31 | GUI Range 32 | 33 | GUI Range 34 | 35 | 36 | Signal Source ---> osmocom Sink 37 | ---> Time Sink 38 | ---> Waterfall Sink 39 | ---> Frequency Sink 40 | ``` 41 | 42 | _Note: the following block names are abbreviated:_ 43 | - _Time Sink: QT GUI Time Sink_ 44 | - _Waterfall Sink: QT GUI Waterfall Sink_ 45 | - _Frequency Sink: QT GUI Frequency Sink_ 46 | 47 | ### Parameters: 48 | 49 | - First GUI Range: 50 | - Id: `amplitu` 51 | - Default: `0.5` 52 | - Start: `0` 53 | - Stop: `1` 54 | - Step: `0.01` 55 | - Second GUI Range: 56 | - Id: `sigfreq` 57 | - Default: `70e3` 58 | - Start: `10e3` 59 | - Stop: `200e3` 60 | - Step: `1e3` 61 | - Third GUI Range: 62 | - Id: `ifgain` 63 | - Default: `20` 64 | - Start: `0` 65 | - Stop: `32` 66 | - Step: `1` 67 | - Variable (_already in the flowgraph_): 68 | - Id: `samp_rate` 69 | - Value: `2e6` 70 | - Signal Source: 71 | - Frequency: `sigfreq` 72 | - Amplitude: `amplitu` 73 | - osmocom Sink: 74 | - Device Arguments: `"hackrf=0"` 75 | - Ch0: Frequency (Hz): `Use frequency from group # above` 76 | - Ch0: RF Gain (dB): `0` 77 | - Ch0: IF Gain (dB): `ifgain` 78 | - Ch0: BB Gain (dB): `0` 79 | - Time Sink: 80 | - "General" Tab: 81 | - _No changes._ 82 | - "Trigger" Tab: 83 | - Trigger Mode: `Normal` 84 | - Frequency Sink: 85 | - Y Min: `-110` 86 | 87 | 88 | ## Flowgraph #2: receiver.grc 89 | 90 | ``` 91 | osmocom Source ---> Time Sink 92 | ---> Waterfall Sink 93 | ---> Frequency Sink 94 | ``` 95 | 96 | ### Parameters: 97 | 98 | - Variable (_already in the flowgraph_): 99 | - Id: `samp_rate` 100 | - Value: `2e6` 101 | - osmocom Source: 102 | - Device Arguments: `"hackrf=0"` 103 | - Ch0: Frequency (Hz): `Use frequency from group # above` 104 | - Ch0: RF Gain (dB): `0` 105 | - Ch0: IF Gain (dB): `32` 106 | - Ch0: BB Gain (dB): `32` 107 | - Time Sink: 108 | - "General" Tab: 109 | - _No changes._ 110 | - "Trigger" Tab: 111 | - Trigger Mode: `Normal` 112 | - Frequency Sink: 113 | - Y Min: `-110` 114 | 115 | 116 | ### Notes 117 | 118 | 1. You should be able to see the transmitted wave on the receiving end. If you don't, ask for assistance. 119 | 2. Adjust the transmitter's IF Gain slider to be lower if possible (to reduce the amount of unnecessary energy transmitted). 120 | 3. Play with the amplitude and frequency sliders. After about a second, you'll see the changes reflected on the receiving end. 121 | 4. You may or may not see a difference between the transmitted frequency and the received frequency. This is due to variations in equipment. 122 | 5. You'll notice the receiver has a spike in the center of the received spectrum. This is called the "DC Spike" -- it is produced by the Hack RF. We often choose to tune to a frequency slightly offset from our desired frequency to avoid interference caused by this spike. 123 | 124 | ### FAQ 125 | 126 | - Q: Why are there two waves (blue and red)? 127 | A: This is Complex data, meaning it has a Real part and an Imaginary part. We'll discuss more details later, but for now, you can ignore or turn off one of the parts. 128 | 129 | ### Questions 130 | 131 | 1. Adjust the `sigfreq` slider to `100,000 Hz`. What is the period of the wave? Calculate the period first, then verify visually. 132 | _**Note:** The period is conventionally represented with a capital T._ 133 | 2. For a frequency of `200,000 Hz`, what is the period of the wave? Calculate the period first, then verify visually. 134 | 3. When you change the `amplitu` slider, are the changes reflected on the transmission side, the receiving side, or both? 135 | 4. When you change the `ifgain` slider, are the changes reflected on the transmission side, the receiving side, or both? 136 | 5. What happens on the receiving end when too much power is received? 137 | 6. At what point do you start to see clipping? 138 | 7. Can clipping happen on the transmitter's side? 139 | _**Hint:** Try setting the transmitter's amplitude to a number such as `5`. You may need to modify the GUI Range that adjusts amplitude._ 140 | 8. How could you communicate data using just these sliders? 141 | 142 |
Click for answers. 143 | 144 | 1. 0.00001 seconds or .01 milliseconds or 10 microseconds. 145 | 146 | 2. 0.000005 s, .005 ms, or 5 μs. 147 | 148 | 3. Both. 149 | 150 | 4. Receive only, because the IF gain amplification happens inside the HackRF right before it is transmitted. 151 | 152 | 5. The signal clips. For more info, look up "Clipping in Signal Processing". 153 | 154 | 6. At 1 and -1 155 | 156 | 7. Clipping on the transmitter side is only visible on the receiving side. It often appears as a waveform that is distorted, but isn't obviously clipped. 157 | 158 | 8. Basic OOK (On-Off Keying), ASK (Amplitude shift keying), or FSK (Frequency shift keying) 159 | 160 |
161 | 162 | ### Exercises 163 | 164 | 1. Modify your receiver to have slidable IF and BB gains. Use proper start, stop, step from [the HackRF docs](https://hackrf.readthedocs.io/). 165 | 2. In the Signal Source, there's an option for the Waveform. Experiment with the different waveforms. 166 | 167 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/020_Sample_Rates_Intro.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
  3 | 2022 Aug 30: 030-Sample-Rates-1.md
  4 | 2022 Sep 15: 050-Sample-Rates-1.md
  5 | 2023 May 22: 020_Sample_Rates_Intro-1.md
  6 | 
7 |
8 | 9 | # Sample rates: Introduction 10 | 11 | ℹ️ This material coincides with material from SDR slideshow A (slides 22, 25, 28-32, 40-42). 12 | 13 | At this point, you've used various Graphical User Interface (GUI) features to manually adjust the amplitude and the frequency of a pure sine wave. Human-powered modulation is certainly useful, as thousands of telegraph operators could tell you. 14 | 15 | But as we know, the telegraph has fallen out of style. Why? Because we now have computers that can do the moduation for us. 16 | 17 | ### What is Modulation? 18 | 19 | Strictly speaking, to "modulate" something simply means to change it. But in the context of communications, modulation refers to making a change in something (amplitude, frequency, etc) for the purpose of communicating information. 20 | 21 | Imagine you want to communicate the following binary data using a flashlight. 22 | 23 | ``` 24 | 0 1 0 1 0 1 1 0 1 0 0 1 0 25 | ``` 26 | 27 | You may choose to alternate between turning the light on and off: 28 | 29 | ``` 30 | off on off on off on on off on off off on off 31 | 0 1 0 1 0 1 1 0 1 0 0 1 0 32 | ``` 33 | 34 | If you were to look at a graph of the brightness over time, it would look like this: 35 | 36 | ``` 37 | ____ ____ ________ ____ ____ 38 | ____ ____ ____ ____ ________ ____ 39 | 0 1 0 1 0 1 1 0 1 0 0 1 0 40 | ``` 41 | 42 | The name for this modulation scheme is "On Off Keying". The website _Open Learn_ has a [great figure for visualizing On Off Keying](https://www.open.edu/openlearn/science-maths-technology/exploring-communications-technology/content-section-1.4) 43 | 44 | So, how do we make the Hack RF transmit and receive OOK modulated data? To answer that question, we'll be taking a long detour to explore how sample rates work. 45 | 46 | ### What is a Sample Rate? 47 | 48 | ### Thermometers and paper 49 | 50 | Imagine that you're keeping a record of temperatures in your area. You have a thermometer, a piece of paper, and a pencil. You occasionally look at the thermometer and write down the current temperature. 51 | 52 | Each measurement of the temperature is called a **sample**. The **sample rate** refers to how frequently you take a measurement. 53 | 54 | How often would you want to sample the temperature? You could write down the temperature once every minute. In that case, the sample rate would be `1 sample per minute`, or `60 samples per hour`. 55 | 56 | But is it really necessary to measure the temperature so frequently? Perhaps one or two samples per hour would be more reasonable, given that the ambient temperature doesn't change very much within a minute. 57 | 58 | Let's imagine that you did one sample every two hours, or 12 samples per day. It might look like this: 59 | 60 | ``` 61 | 12 am 52 °F 62 | 2 am 50 °F 63 | 4 am 48 °F 64 | 6 am 48 °F 65 | 8 am 46 °F 66 | 10 am 58 °F 67 | 12 pm 63 °F 68 | 2 pm 64 °F 69 | 4 pm 67 °F 70 | 6 pm 66 °F 71 | 8 pm 63 °F 72 | 10 pm 57 °F 73 | ``` 74 | 75 | Let's graph this in Python. For simplicity, I'm omitting the times. 76 | 77 | Open your editor of choice (for example, VS Code). Open your directory (the one you created on the Desktop) inside of VS Code. Using VS Code, create this file in that directory: 78 | 79 | Filename: `temperature_graph_1.py` 80 | 81 | ```python3 82 | import matplotlib.pyplot as plt 83 | 84 | times = [ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] 85 | temps = [52, 50, 48, 48, 46, 58, 63, 64, 67, 66, 63, 57] 86 | 87 | font1 = {"family":"serif", "color":"blue", "size":20} 88 | font2 = {"family":"serif", "color":"darkred", "size":15} 89 | 90 | plt.title("Temperature Samples", fontdict=font1) 91 | plt.xlabel("Time (Hours)", fontdict=font2) 92 | plt.ylabel("Temperature (° F)", fontdict=font2) 93 | 94 | plt.plot(times, temps, marker="o", linestyle="dotted") 95 | 96 | plt.show() 97 | ``` 98 | 99 | Now, let's graph it in GNU Radio Companion. 100 | 101 | Filename: `temperature_graph_2.grc` 102 | 103 | ``` 104 | Vector Source --> Time Sink 105 | ``` 106 | 107 | Parameters: 108 | - Variable (_already in the flowgraph_): 109 | - Id: `samp_rate` 110 | - Value: `0.5` 111 | - Vector Source: 112 | - Output Type: `float` 113 | - Vector: `(52, 50, 48, 48, 46, 58, 63, 64, 67, 66, 63, 57, 0, 0, 0)` 114 | - Repeat: `No` 115 | - Time Sink: 116 | - General tab: 117 | - Type: `float` 118 | - Y Axis Label: `Temperature` 119 | - Y Axis Unit: `Deg F` 120 | - Number of Points: `12` 121 | - Y min: `0` 122 | - Y max: `100` 123 | - Config tab: 124 | - Line 1 Style: `0` 125 | - Line 1 Marker: `Circle` 126 | 127 | Notes: 128 | - The zeros at the end of the data in the Vector Source are unfortunately necessary because of a quirk in GNU Radio: the Time Sink won't display _any_ data unless you add a few extra data points past the 12 that it is going to show. (The value of zero is not special; any extra data works.) My guess is that it requires the chunks of data to have some minimum length before moving the data from block to block. Also, I believe those chunks must be longer than the number of points that you are viewing in the Time Sink. 129 | - We put a sample rate of 0.5 samples per second. It should actually be 0.5 samples per hour (meaning 1 sample every two hours), but GNU Radio is quirky with low sample rates. Pretend that the x-axis label says "hours" instead of "seconds". 130 | 131 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/021_Sample_Rates_CPU_temps.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2022 Oct 05: 051-Sample-Rates-2.md
 4 | 2023 Jan 04: 051-Sample-Rates-2-CPU-temps.md
 5 | 2023 May 22: 021_Sample_Rates_CPU_temps.md
 6 | 
7 |
8 | 9 | # Sample rates: CPU temperatures 10 | 11 | ℹ️ This material coincides with material from SDR slideshow A (slides 22, 25, 28-32, 40-42). 12 | 13 | ### Need for speed 14 | 15 | In `020_Sample_Rates_Intro.md`, we measured temperature every 2 hours. That works for weather, but now, imagine you're dealing with a thermometer on a processor. Actually, you don't need to use your imagination — on Linux, simply run this in the terminal: 16 | 17 | ``` 18 | watch -n 0.2 -- cat /sys/class/thermal/thermal_zone*/temp 19 | ``` 20 | 21 | This means to check the temperature reading every 0.2 seconds, which is a sample rate of 5 Hz (5 times per second). 22 | 23 | The units are thousandths of a degree Celsius, so `27800` means 27.8 °C. 24 | 25 | Is 5 times per second a good measurement frequency? Let's make the temperature go up by running a useless (but processor-intensive) Python program: 26 | 27 | ```python3 28 | for x in range(1000000000000): 29 | y = x 30 | ``` 31 | 32 | Incredibly, the temperature jumps up by about 13 °C in less than a fifth of a second. (At least, it did on my computer.) So, if you were setting up an automatic throttle (limit) for the processor, it would probably need to take samples even more frequently than 5 samples per second (5 Hz). 33 | 34 |
In fact, we tried it... 35 | ...on our classroom computers, it takes approx 0.1 seconds to go up 13 degrees Celsius. That's fast! 36 | 37 | If you take out the delay in the python script below, you can try this yourself! Feel free to ask for instructor help. 38 |
39 | 40 | ### Recording the data 41 | 42 | The terminal command above works well for viewing the temperature live. Now, let's take measurements using Python. Name this file `temp_measure.py`: 43 | 44 | ```python3 45 | import time 46 | 47 | outputFileName = "myTempReadings.txt" 48 | 49 | # If this doesn't work, try changing it to thermal_zone0, thermal_zone1, thermal_zone3, etc 50 | inputFileName = "/sys/class/thermal/thermal_zone2/temp" 51 | 52 | f_out = open(outputFileName, "w") 53 | 54 | for i in range(1, 12+1): # Take a specified number of temperature readings 55 | f_temp = open(inputFileName, "r") 56 | contents = f_temp.read() 57 | f_temp.close() 58 | T = int(contents)/1000 # Convert T from millidegrees to deg. Celsius. 59 | print(T) 60 | f_out.write(str(T) + ", ") 61 | time.sleep(0.2) 62 | 63 | f_out.close() 64 | 65 | ``` 66 | 67 |
Note: This will take a measurement approximately five times per second. Click for more info. 68 | 69 | > For our purposes in this class, "approx 5 times per second" is completely fine. 70 | > 71 | > However, if you ever need a more precise sample rate for something outside of this class, you would want to use a different approach. See [here](https://stackoverflow.com/a/67930185) and [here](https://mail.python.org/pipermail/python-list/2000-November/060154.html). Fair warning that both links go fairly deeply into the topic. 72 | 73 |
74 | 75 | When you run that Python file, it will write to a file called `myTempReadings.txt`. 76 | 77 | ### Visualizing the data 78 | 79 | Reopen your file `temperature_graph_1.py` from `020_Sample_Rates_Intro.md`. Replace the `temps` variable with the measured processor temperatures that are in `myTempReadings.txt`. Run that to graph the data. 80 | 81 | Also, reopen your file `temperature_graph_2.grc` from `020_Sample_Rates_Intro.md`. Make the following changes: 82 | 83 | Parameters: 84 | - Variable (_already in the flowgraph_): 85 | - Id: `samp_rate` 86 | - Value: `5` 87 | - Vector Source: 88 | - Vector: _Your data goes here. You'll need to append some zeros at the end just as we did before._ 89 | - Time Sink: 90 | - General Tab: 91 | - Number of Points: _Approximately 15. Should be the same as the number of data points that you recorded._ 92 | - Y max: `100` 93 | 94 | Run that to visualize your data. Make sure it matches the Python-generated graph. 95 | 96 | Notice the x-axis on the GNU Radio-generated graph. How much time is there between data points? Is it what you expected? 97 | 98 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/023_Sample_Rates_py_practice.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2022 Oct 06: 053-Sample-Rates-4.md
 4 | 2023 Jan 04: 053-Sample-Rates-4-py-practice.md
 5 | 2023 May 22: 023_Sample_Rates_py_practice.md
 6 | 
7 |
8 | 9 | # Sample Rates: Practice problems 10 | 11 | ℹ️ This material coincides with material from SDR slideshow A (slides 22, 25, 28-32, 40-42). 12 | 13 | Run this. Name it `rough_sine_wave.py`. You'll see that it plots a very rough sine wave. 14 | 15 | ```python3 16 | import matplotlib.pyplot as plt 17 | 18 | temps = [0.0, 2.3, 5, 10.3, 12.2, 12.5, 13.5, 15, 14, 4, 0, -9, -11, -12.5, -15, -16, -14.2, -12.5, -10, -5] 19 | 20 | plt.plot(temps, "o") # the "o" means to use circles as markers of the points 21 | 22 | plt.show() 23 | ``` 24 | 25 | Your tasks: 26 | 27 | 1. How many samples are there? 28 | 2. If your measurement tool was measuring with a sample rate of 10 samples per second, then how many seconds of data is this? 29 | 3. If the sample rate was instead 20 samples per second, then how many seconds of data is this? 30 | 4. If the sample rate was instead 40 sps (samples per second), then how many seconds of data is this? 31 | 5. How many full cycles of the rough sine wave appear to be depicted? 32 | 6. If the sample rate was 40 samples per second, then what is the frequency of the rough sine wave? 33 | 7. Fix the data so that the sine wave is clean. 34 | 35 |
Answers: 36 | 37 | 1. 20 samples 38 | 2. 2 seconds 39 | 3. 1 second 40 | 4. 0.5 seconds 41 | 5. 1 cycle 42 | 6. 2 cycles per second, or, equivalently, 2 Hz. If the sample rate = 40 sps, then the data given is half a second. Our given wave completes one full cycle in this time, and would therefore complete two cycles in a full second. 43 | 7. There's more than one possible answer. If you're in class, ask an instructor or another student. 44 | One of many valid answers would be this: 45 | `[0.0, 4.64, 8.82, 12.14, 14.27, 15.0, 14.27, 12.14, 8.82, 4.64, 0.0, -4.64, -8.82, -12.14, -14.27, -15.0, -14.27, -12.14, -8.82, -4.64]` 46 |   47 | For those who are curious, that list was generated using this code: 48 | ```python3 49 | import numpy as np 50 | 51 | dat = 15*np.sin(np.linspace(0, 2 * np.pi, 20, endpoint=False)) 52 | 53 | def roundtwo(x): 54 | return round(x, 2) 55 | 56 | datRounded = list(map(roundtwo, dat)) 57 | print(datRounded) 58 | ``` 59 | 60 |
61 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/024_Sample_Rates_grc_practice.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2022 Oct 06: 054-Sample-Rates-5.md
 4 | 2023 Jan 04: 054-Sample-Rates-5-grc-practice.md
 5 | 2023 May 22: 024_Sample_Rates_grc_practice.md
 6 | 
7 |
8 | 9 | # Sample rates: GRC practice 10 | 11 | ℹ️ This material coincides with material from SDR slideshow A (slides 22, 25, 28-32, 40-42). 12 | 13 | ### Practice problem set A 14 | 15 | Make this flowgraph. Name it `messy_sine_vector_source.grc`. 16 | 17 | ``` 18 | Vector Source --> Time Sink 19 | ``` 20 | 21 | Parameters: 22 | - Variable (_already in the flowgraph_): 23 | - Id: `samp_rate` 24 | - Value: `40` 25 | - Vector Source: 26 | - General tab: 27 | - Output Type: `float` 28 | - Vector: `[0.0, 2.3, 5, 10.3, 12.2, 12.5, 13.5, 15, 14, 4, 0, -9, -11, -12.5, -15, -16, -14.2, -12.5, -10, -5]` 29 | - Repeat: `Yes` 30 | - Advanced tab: 31 | - Comment: `A simulated messy sine wave` 32 | - Time Sink: 33 | - General tab: 34 | - Type: `float` 35 | - Number of Points: `80` 36 | - Y min: `-20` 37 | - Y max: `20` 38 | - Config tab: 39 | - Line 1 Style: `0` 40 | - Line 1 Marker: `Circle` [NOTE: If "X-Cross" or "9" is chosen, the graph may not display data.] 41 | 42 | You'll see the same messy sine wave from the previous exercise, but repeated four times. 43 | 44 | In the "View" menu, ensure that "Show block Comments" is checked. This will allow you to see the comment "A simulated messy sine wave" below the block. 45 | 46 | Exercises: 47 | 1. Change the Number of Points to 40. Then try 160. 48 | 2. What should the Number of Points be to show only one cycle of the wave? 49 | 3. How much time does a full cycle take? 50 | 4. Try changing the `samp_rate` to see how the Time Sink x-axis labeling changes. 51 | 52 | 53 |
54 | Answers: 55 | 56 | 1. N/A 57 | 2. 20 points 58 | 3. Half a second 59 | 4. Try it 60 | 61 |
62 | 63 | ### Practice problem set B 64 | 65 | Copy and modify the previous flowgraph. Save the new copy as `square_vector_source.grc`. 66 | 67 | 1. Change the `Vector` in the Vector Source so that it outputs a square wave. It should look like this: 68 | 69 | ``` 70 | ••••• ••••• ••••• 71 | 72 | ••••• ••••• ••••• 73 | ``` 74 | 75 | Hints: 76 | 77 | - Since the Vector Source repeats, you only need to put a single cycle of data points in the Vector. 78 | - If you're stuck, start trying random numbers until it looks square. 79 | 80 | 2. Change your data points so that the square wave has a frequency of 2 Hz. 81 | 3. For this square wave with a frequency of 2 Hz, how many seconds does it take to complete a full cycle? (The time for one full cycle is called the **period**.) 82 | 4. The wave cycles between "high" and "low". What percent of the cycle does it spend "high"? How many seconds is that? 83 | 5. Given that the sample rate is 40 sps (samples per second), how many **samples** does this 2 Hz square wave require to complete a full cycle? 84 | 85 | Answers: 86 | 87 |
88 | Answers: 89 | 90 | 1. Try it 91 | 2. Try it 92 | 3. Half a second 93 | 4. 50%, A quarter of a second 94 | 5. 20 samples 95 | 96 |
97 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/025_Sample_Rates_RepeatBlock.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
  3 | 2022 Oct 07: 055-Sample-Rates-6.md
  4 | 2022 Oct 27: 055-Sample-Rates-6-RepeatBlock.md
  5 | 2023 May 22: 025_Sample_Rates_RepeatBlock.md
  6 | 
7 |
8 | 9 | # Sample rates: Repeat block 10 | 11 | ℹ️ This material coincides with material from SDR slideshow A (slides 22, 25, 28-32, 40-42). 12 | 13 | In `024_Sample_Rates_grc_practice.md`, we created a square wave using a Vector Source block. 14 | 15 | Let's imagine we want to make a square wave with a frequency of 2 Hz, and a sample rate of 1 Msps (Million samples per second). 16 | 17 | One second of data would look the same as before... 18 | 19 | ``` 20 | ___________ ___________ 21 | 22 | ___________ ___________ 23 | ``` 24 | 25 | ... but it would be represented using MANY more samples. 26 | 27 | As we discussed in `024_Sample_Rates_grc_practice.md`, the period of a 2 Hz signal is half a second, and consequently, it spends a quarter second "high", and a quarter second "low". 28 | 29 |
Question: With a sample rate of 1 Msps, how many samples are in half a second? How many are in a quarter second? (Click for answer) 30 | 31 | Answer: Half a second is 500,000 samples, and a quarter second is 250,000 samples. 32 |
33 | 34 | We could type 250,000 zeros and 250,000 ones in the vector source, but that's definitely not the ideal solution! 35 | 36 | The solution? The Repeat block. 37 | 38 | To start our experimentation with the Repeat block, let's use a smaller sample rate: 10 samples per second. 39 | 40 | Create this flowgraph. Name it `repeat_demo_1.grc`. 41 | 42 | ``` 43 | Vector Source --> Repeat --> Time Sink 44 | ``` 45 | 46 | ### How to set the Parameters: 47 | 48 | - Variable (_already in the flowgraph_): 49 | - Id: `samp_rate` 50 | - Value: `10` 51 | - Vector Source: 52 | - Output Type: `float` 53 | - Vector: `[0, 1]` 54 | - Repeat: `Yes` 55 | - Repeat: 56 | - Type: `float` 57 | - Interpolation: `5` 58 | - Time Sink: 59 | - General tab: 60 | - Type: `float` 61 | - Number of Points: `40` 62 | - Y min: `-2` 63 | - Y max: `2` 64 | - Config tab: 65 | - Line 1 Style: `0` 66 | - Line 1 Marker: `Circle` 67 | 68 | Questions: 69 | 70 | 1. What is the ratio of the Number of Points to the Sample Rate? 71 | 2. How many seconds are shown in the Time Sink? How does that compare to the ratio asked in the previous question? 72 | 3. What does the Repeat block appear to do? 73 | 4. What is the frequency of the square wave currently? 74 | 5. Try different values for Interpolation. How does that affect the frequency of the square wave? What number should you pick if you want the square wave to have a frequency of 0.5 Hz (in other words, one full cycle every two seconds)? 75 | 76 |
Answers... 77 |
 78 | #1: Ratio: 4 to 1
 79 | #2: 4 seconds
 80 | #3: Each sample that flows into the Repeat block is repeated 5 times.
 81 | #4: 1 Hz
 82 | #5: Higher interpolation (more repeating) makes the frequency lower.
 83 |     An interpolation of 10 would make the square wave's frequency 0.5 Hz.
 84 | 
85 |
86 | 87 | Now, set the sample rate to 20. (This should only require changing the `samp_rate` variable; all others should change automatically.) 88 | 89 | 6. How many seconds are shown in the Time Sink? 90 | 7. What Repeat Interpolation should you pick to make a square wave with a frequency of... 91 | 1. 1 Hz? 92 | 2. 0.5 Hz? 93 | 3. 2 Hz? 94 | 8. What Number of Points should you pick to view 1 second of data? 95 | 9. How much time would you be able to see if you set the Number of Points to `60`? 96 | 97 |
Answers... 98 |
 99 | #6: 2 seconds
100 | #7: 1 Hz:    Interp = 10
101 |     0.5 Hz:  Interp = 20
102 |     2 Hz:    Interp = 5
103 | #8: 20 points
104 | #9: 3 seconds
105 | 
106 |
107 | 108 | Now, let's significantly raise the sample rate. There are two changes that help lower the chance of freezing the flowgraph: 109 | - Add a throttle 110 | - Increase the time between updates (the Update Period) on the Time Sink 111 | 112 | Copy and paste your previous flowgraph. Name it `repeat_demo_2.grc`. Add a throttle as shown. 113 | 114 | ``` 115 | Vector Source --> Repeat --> Throttle --> Time Sink 116 | ``` 117 | 118 | ### How to set the Parameters: 119 | 120 | - Variable (_already in the flowgraph_): 121 | - Id: `samp_rate` 122 | - Value: `1e6` 123 | - Vector Source: 124 | - Output Type: _same as above_ 125 | - Vector: _same as above_ 126 | - Repeat: _same as above_ 127 | - Repeat: 128 | - Type: _same as above_ 129 | - Interpolation: `10` 130 | - Throttle: 131 | - Type: _Determine based on the types of the other blocks_ 132 | - Time Sink: 133 | - General tab: 134 | - Type: _same as above_ 135 | - Number of Points: `40` 136 | - Y min: _same as above_ 137 | - Y max: _same as above_ 138 | - Update Period: `15` 139 | - Config tab: 140 | - Line 1 Style: _same as above_ 141 | - Line 1 Marker: _same as above_ 142 | 143 | 10. In this new flowgraph, how much time is shown in the Time sink? 144 | 11. What is the period of the square wave? 145 | 12. If we want the period to be 100 microseconds, what should the Interpolation be? 146 | 13. Set the Interpolation to 200. What is the period? What Number of Points should you pick so you can se the entire cycle of the square wave? 147 | 14. If we want the frequency to be 2 Hz, what should the period be? What should the Interpolation be? What should the Number of Points be? 148 | 15. If you want to see a full second of data, what should the Number of Points be? 149 | 150 | With a significantly larger Number of Points, the window may become unresponsive. You may need to lower the Number of Points to avoid freezing. Unfortunately, as a result, you may not be able to see the entirety of the square wave. 151 | 152 | 16. Why would we want to set the sample rate so high? (Hint: what is the minimum sample rate of the Hack RF?) 153 | 154 |
Answers... 155 |
156 | #10: 40 microseconds
157 | #11: 20 microseconds
158 | #12: Interpolation = 50
159 | #13: 400 microseconds; at least 400 points in the Time Sink
160 | #14: Period= 0.5 second
161 |      Interp = 250000 which could be written as int(250e3)
162 |      Points = At least 500000 to see one cycle
163 |               Note: 500000 could be written as int(500e3) or int(5e5)
164 | #15: Points = 1 Million = int(1e6) to see a full second of data
165 | #16: The Hack RF has a minimum sample rate of 2 Million samples per second,
166 |      so it’s helpful to practice working with large sample rates in order to 
167 |      build an intuition with scientific notation, microseconds, etc.
168 | 
169 |
170 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/026_Sample_Rates_RealisticData.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2022 Oct 11: 056-Sample-Rates-7.md
 4 | 2022 Oct 27: 056-Sample-Rates-7-RealisticData.md
 5 | 2023 May 22: 026_Sample_Rates_RealisticData.md
 6 | 
7 |
8 | 9 | # Sample rates: Realistic data 10 | 11 | ℹ️ This material coincides with material from SDR slideshow A (slides 22, 25, 28-32, 40-42). 12 | 13 | We've seen how to create a square wave using the Vector Source. Now, let's simulate a more interesting signal. 14 | 15 | Create this flowgraph. Name it `repeat_demo_3.grc`. 16 | 17 | ``` 18 | Vector Source --> Repeat --> Time Sink 19 | ``` 20 | 21 | ### How to set the Parameters: 22 | 23 | - Variable (_already in the flowgraph_): 24 | - Id: `samp_rate` 25 | - Value: `10` 26 | - Vector Source: 27 | - Output Type: `float` 28 | - Vector: `[0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]` 29 | - Repeat: `Yes` 30 | - Repeat: 31 | - Type: `float` 32 | - Interpolation: `5` 33 | - Time Sink: 34 | - General tab: 35 | - Type: `float` 36 | - Number of Points: `100` 37 | - Y min: `-2` 38 | - Y max: `2` 39 | - Config tab: 40 | - Line 1 Style: `0` 41 | - Line 1 Marker: `Circle` 42 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/030_AnalyzeFreq_of_Combined_Signals.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
  3 | 2022 Aug 12: 119-AnalyzeFreq-of-Combined-Signals.md
  4 | 2022 Aug 30: 219-AnalyzeFreq-of-Combined-Signals.md
  5 | 2023 May 22: 030_AnalyzeFreq_of_Combined_Signals.md
  6 | 
7 |
8 | 9 | # Analyze Frequency of Combined Signals/Waves 10 | 11 | This is a demonstration of how GNU Radio Companion can evaluate a signal comprising multiple waveforms (e.g., multiple sine or cosine waves) to determine the frequencies of the individual waves. 12 | 13 | ## Summary of the GUI-program 14 | 15 | ``` 16 | 17 | GUI Range (1st) 18 | 19 | GUI Range (2nd) 20 | 21 | Variable 22 | 23 | Signal Source (1st) ─┌─⟶ Add ⟶ Throttle ─┌─⟶ QT GUI Frequency Sink 24 | 25 | Signal Source (2nd) ─┴ ┴⟶ QT GUI Time Sink 26 | 27 | 28 | ``` 29 | Memos: 30 | - In addition to the wiring shown above --- After updating the parameters for QT GUI Time Sink, listed below, wire the 1st and 2nd Signal Sources to separate inputs on the QT GUI Time Sink. 31 | - All ports should be floating point numbers, i.e., the type "float", which will be orange in color. 32 | 33 | Goals: 34 | - Produce two signals, a.k.a. "waves" using the 1st & 2nd Signal Sources and make the frequency of each wave adjustable by including two sliders to pick (adjust) the frequencies you're producing. 35 | - Using the Add block, combine the waves (signals) together to represent a combined waveform that may be received from a piece of local equipment or from broadcast signals. 36 | - Plot the orignal waves and the combined waveform using the Time Sink. 37 | - Using the Frequency Sink, demonstrate the ability of GNU Radio Companion to analyze the combined waveform, which means extracting (disassociating) and ploting the individual waves that produced the combined waveform. 38 | - Use a Variable block to assist with arranging the display blocks on the GUI operation screen. 39 | 40 | ## How to set the Parameters 41 | Enter the values shown in the bubbles below. Do not enter the units, e.g., "Hz". For any parameter not listed, the pre-programmed default value may be used. 42 | 43 | ### For the `samp_rate` variable (_already in the flowgraph_): 44 | 45 | - Value: `20e3` = 20,000 Hz 46 | Later, try varying this sampling rate and watch the maximum x-value on the GUI Frequency Sink graph. You will need to stop and restart the GUI. What is the relationship is between the `samp_rate` parameter an the max. x-value? Why did the programmers establish that relationship? 47 | 48 | ### For the First GUI Range selector: 49 | 50 | - Id: `freq1` 51 | - Default Value: `50` Hz 52 | - Start: `0` Hz 53 | - Stop: `samp_rate/2` Hz (What is the motivation for this relationship?) 54 | - Step: `25` Hz 55 | - GUI Hint: `(0,0)` (This sets the position of the Range selector on the GUI screen. 1st number is the row. 2nd number is the column.) 56 | 57 | ### For the Second GUI Range selector: 58 | 59 | - Id: `freq2` 60 | - Default Value: `2000` Hz 61 | - Start: `0` Hz 62 | - Stop: `5e3` = 5,000 Hz 63 | - Step: `25` Hz 64 | - GUI Hint: `(0,1)` (This sets the position of the Range selector on the GUI screen, @ row 0 (1st), col. 1 (2nd)) 65 | 66 | ### For the Variable block: 67 | (Used for "GUI Hint" of graphing blocks (sinks) to set their width.) 68 | 69 | - Id: `GUI_width` 70 | - Value: `3` 71 | 72 | ### For the First Signal Source: 73 | 74 | - Frequency: `freq1` 75 | 76 | ### For the Second Signal Source: 77 | 78 | - Frequency: `freq2` 79 | 80 | ### For the Add block: 81 | Use the defalut values, which are shown here. 82 | - IO Type: `Float` 83 | - Num Inputs: `2` 84 | - Vec Length: `1` 85 | 86 | ### For GUI Frequency Sink (Graph): 87 | 88 | - Name: `"Frequency Spectrum from the Added Waves"` 89 | - Spectrum Width: `Half` (The Spectrum Width option only appears after you've changed the ports to Orange as indicated in the Memos section above.) 90 | - Y min = `-90` (Note the negative. This setting crops the lower portion of the data to hide the "noise" in the frequencies) 91 | - Y max = `10` 92 | - GUI Hint: `(5, 0, 2, GUI_width)` (This sets the position on the GUI screen, @ row 5, col. 0, width or span. If row 3 or 4 does not exist, the block will fill a position higher than row 5.) 93 | 94 | ### For the Time Sink: 95 | 96 | "General" Tab 97 | - Autoscale: `Yes` 98 | - Number of Inputs: `3` 99 | - GUI Hint: `(3, 0, 2, GUI_width)` 100 | 101 | "Trigger" Tab 102 | - Trigger Mode: `Auto` (This will give the graph the appearance of being "frozen" even as data continues to be fed to it.) 103 | 104 | "Config" Tab 105 | - Control Panel: `yes` 106 | - Line 1 Label: `Signal 1` (The default value) 107 | - Line 2 Label: `Signal 2` (The default value) 108 | - Line 3 Label: `Sum(Sig1, Sig2)` 109 | 110 | ## Discussion 111 | 112 | - You may remember, to accurately represent or reproduce a signal (a wave), the Nyquist Criteria requires: sample rate >= 2 * frequency of the signal. Otherwise, with too few samples per second, the plot or the sound produced by the data will be distorted as compared to the orignal signal. 113 | - Try this: In one or more GUI Range selector, change the Stop value to be greater than the Nyquist Criteria allows, e.g., Stop: `samp_rate` Hz. Then, while the program runs, adjust that frequency to be higher than the previous limit allowed. What do you observe? 114 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/040_Unicode_and_File_Source.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
  3 | 2022 Oct 12: 057-File-Source.md
  4 | 2022 Oct 17: 065-File-Source.md
  5 | 2023 Feb 22: 065-Unicode-and-File-Source.md
  6 | 2023 May 22: Ch05/010_Unicode_and_File_Source.md
  7 | 2023 Aug 29: Ch02/040_Unicode_and_File_Source.md
  8 | 
9 |
10 | 11 | # Unicode and file source 12 | 13 | ℹ️ This material coincides with material from SDR slideshow F (all slides). 14 | 15 | We've talked about sending pulses to represent binary. However, most "real life" data is more interesting than simply zeros and ones (even if it is stored as zeros and ones on the hard drive). We're going to talk about how text is stored as binary, and how to work with text in both GNU Radio and Python. 16 | 17 | ### Reading from Files 18 | 19 | We've used a Vector Source block to provide data. Now, let's try a File Source. First, we're going to build the concepts in Python. 20 | 21 | Let's create a file. Name this `create_text_file_1.py`. 22 | 23 | ```python3 24 | f = open("my_text_file.txt", "w") 25 | f.write("ABCD") 26 | f.close() 27 | ``` 28 | 29 | When you run this, it won't display any output in the terminal; it only creates a text file. Open that text file in the editor of your choice to verify that it contains ABCD. 30 | 31 | Now, let's read the file in Python. Name this `read_text_file_basic.py`. 32 | 33 | ```python3 34 | f = open("my_text_file.txt", "r") 35 | contents = f.read() 36 | f.close() 37 | print(contents) 38 | ``` 39 | 40 | This reads the file. 41 | 42 | However, we'll see later that when GNU Radio reads the file, it's going to produce numbers instead of letters. Where do the numbers come from? 43 | 44 | ### Encoding 45 | 46 | All data on a computer is stored as binary (0s and 1s). So when we asked Python to write "ABCD", behind the scenes, it converted each letter to a sequence of zeros and ones. This is called _encoding_ the data. 47 | 48 | The most common way to encode text is Unicode. Here are a few characters in Unicode: 49 | 50 | | Letter | Decimal | Hexadecimal | Binary | 51 | |--------|---------|-------------|----------| 52 | | A | 65 | 41 | 01000001 | 53 | | B | 66 | 42 | 01000010 | 54 | | C | 67 | 43 | 01000011 | 55 | 56 |
:information_source: Note: 57 | 58 | If you look up a [Unicode table](https://unicode-table.com/en/#0041), you'll often see the hexadecimal representation. 59 | 60 | Also, we've generally found that more people have heard of ASCII than Unicode. Unicode and ASCII are the same for the Basic Latin characters (A through Z, a through z), so for the purpose of this class, you can think of them as synonymous. 61 | 62 |
63 | 64 | So, when we asked Python to write "ABCD", it wrote this: 65 | 66 | ``` 67 | 01000001 01000010 01000011 01000100 68 | ``` 69 | 70 |
:information_source: Note: 71 | 72 | The spaces between binary numbers are not actually written to the file. They are there for ease of reading. 73 | 74 |
75 | 76 | We usually don't see the binary because most tools that open the file will interpret the numbers as text. But we can ask Python to interpret it as numbers: 77 | 78 | Filename: `read_text_file_integers.py` 79 | 80 | ```python3 81 | f = open("my_text_file.txt", "rb") 82 | contents = f.read() 83 | f.close() 84 | contents_as_numbers = list(map(int, contents)) 85 | print(contents_as_numbers) 86 | ``` 87 | 88 | This will print the decimal representations: [65, 66, 67, 68], which corresponds to ABCD. 89 | 90 |
:information_source: If you want to see it as binary: 91 | 92 | We can ask Python to display the numbers as binary: 93 | 94 | ```python3 95 | def binformat(x): 96 | return f"{x:08b}" 97 | contents_as_binary = list(map(binformat, contents_as_numbers)) 98 | print(contents_as_binary) 99 | ``` 100 |
101 | 102 | ### Plotting in Python 103 | 104 | To prepare for what we'll see in GNU Radio, let's plot some data in Python. 105 | 106 | Name this `python_plot_basic_3.py`. 107 | 108 | ```python3 109 | import matplotlib.pyplot as plt 110 | 111 | datapoints = [20, 30, 40, 10, 50, 60, 80] 112 | 113 | plt.plot(datapoints, "o") 114 | 115 | plt.show() 116 | ``` 117 | 118 | That example doesn't read from a file. Let's make it do that: 119 | 120 | `python_plot_from_file_1.py` 121 | ```python3 122 | import matplotlib.pyplot as plt 123 | 124 | f = open("my_text_file.txt", "rb") 125 | contents = f.read() 126 | f.close() 127 | datapoints = list(map(int, contents)) 128 | 129 | plt.plot(datapoints, "o") 130 | 131 | plt.show() 132 | ``` 133 | 134 | You should see four data points (one each for the characters ABCD). 135 | 136 | Let's make the file a little bit longer. This will help when we get to the GNU Radio part. 137 | 138 | Run this to write the longer file: 139 | 140 | `create_text_file_2.py` 141 | ```python3 142 | f = open("my_text_file.txt", "w") 143 | f.write("ABC ABC ABC ABC THERE ARE WORDS IN THIS FILE") 144 | f.close() 145 | ``` 146 | 147 | Then re-run your python plotting program (`python_plot_from_file_1.py` above). See if you can spot the ABC ABC ABC ABC in the plot. 148 | 149 | ### Plotting in GNU Radio 150 | 151 | We're going to do the same thing in GNU Radio. 152 | 153 | Name this `file_source_time_sink_1.grc` 154 | ``` 155 | File Source --> UChar to Float --> Time Sink 156 | ``` 157 | 158 |
:information_source: What's UChar to Float? (Click to expand) 159 | 160 | We want to read the File Source using the purple type, a.k.a. Integer 8. That type tells GNU Radio to interpret the file data in the way described above. However, the Time Sink expects Float data instead of Integer data, so we must convert the values using UChar to Float. 161 | 162 |
163 | 164 | ### How to set the Parameters: 165 | 166 | - Variable (_already in the flowgraph_): 167 | - Id: `samp_rate` 168 | - Value: `1` 169 | - File Source: 170 | - File: _Click the "..." button, and then pick the text file that you created earlier using Python (`my_text_file.txt`)._ 171 | - Output Type: `byte` 172 | - Repeat: `No` 173 | - Time Sink: 174 | - General Tab: 175 | - Type: `Float` 176 | - Number of Points: `40` 177 | - Y min: `0` 178 | - Y max: `100` 179 | - Config Tab: 180 | - Line 1 Style: `0` 181 | - Line 1 Marker: `Circle` 182 | 183 | You should see a graph that looks very similar to the one we saw in Python. 184 | 185 | Things to try: 186 | - Use the Print block in addition to the Time Sink. Does it print what you expect? 187 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/050_Clipping.md: -------------------------------------------------------------------------------- 1 | Clipping happens when the signal is larger than the ADC is able to measure. Example: 2 | 3 | ```python3 4 | import pyqtgraph as pg 5 | import numpy as np 6 | from pcdr.unstable import make_wave 7 | 8 | def clip_to_1(dat: np.ndarray) -> np.ndarray: 9 | cop = dat.copy() 10 | cop[cop>1] = 1 11 | cop[cop<-1] = -1 12 | return cop 13 | 14 | samp_rate = 100 15 | seconds = 2 16 | wave = make_wave("real", samp_rate, freq=3, seconds=seconds) 17 | x = wave.x 18 | y_rescaled = 1.2 * wave.y 19 | y_clipped = clip_to_1(y_rescaled) 20 | ## TODO: make these on the same plot with two colors 21 | pg.plot(x, y_rescaled) 22 | pg.plot(x, y_clipped) 23 | 24 | 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_Additional_Practice.md: -------------------------------------------------------------------------------- 1 | # Additional Practice 2 | 3 | _The instructor may choose to incorporate these exercises._ 4 | 5 | ### Sampling basics 6 | 7 | 1. Draw an arbitrary function, i.e., a hand-drawn curve that isn't based on a formula. 8 | 2. Have a student take samples of the function with a given sample rate. 9 | 3. New student and new sample rate. 10 | 4. Change the number of seconds shown on the time axis (the x-axis) after a few students. Change more often as the exercise progresses. 11 | 12 | ### Frequency domain 13 | 14 | 1. Draw a sinusoidal function. 15 | 2. Have a student draw the frequency domain. 16 | 3. Repeat with other students. 17 | 4. As before, change the time axis (of the time domain representation) during the exercise. 18 | 19 | ### Frequency domain, multiple components 20 | 21 | 1. Do the previous exercise again with a function that has two frequency components. 22 | - To ensure that the frequencies will be visually distinguishable, the lower frequency should be either 2 Hz or 3 Hz, and the higher frequency should be an integer multiple at least 5 times larger. Examples: 23 | - 2 Hz and 10 Hz; 2 Hz and 12 Hz; 2 Hz and 14 Hz, etc 24 | - 3 Hz and 15 Hz, 3 Hz and 18 Hz; 3 Hz and 21 Hz, etc 25 | 26 | ### Minimum sample rate 27 | 28 | 1. Draw a sinusoidal function. 29 | 2. Have students take samples at a sample rate that is at least 20 times the frequency of the wave. 30 | 3. Erase the original wave, and reconstruct the wave from the samples. 31 | 4. Draw the original and the reconstruted in the frequency domain. (They should be the same until you reach the point of aliasing). 32 | 5. Repeat the process for lower and lower sample rates. 33 | 6. Point out that even when the wave is very roughly represented, it still has the right _frequency_ as long as `samp_rate > 2*signal_freq`. 34 | 35 | ### Downconversion 36 | 37 | 1. Draw a sinusoidal function with a frequency of 12 Hz. 38 | 2. Ask a student to draw the function downconverted by 10 Hz. 39 | - For simplicity, let's assume the downconverted wave starts at the origin. footnote 1 40 | 3. Repeat with a frequency of 13 Hz, 14 Hz, and as many examples as desired. Make sure to try 10 Hz to show that it downconverts to 0 Hz. 41 | 42 | ### Upconversion 43 | 44 | 1. Draw a sinusoidal function with a frequency of 2 Hz. 45 | 2. Ask a student to draw the function upconverted by 10 Hz. 46 | 3. Repeat with a frequency of 3 Hz, 4 Hz, and as many examples as desired. Make sure to try 0 Hz (i.e., a non-zero constant) to show that it upconverts to 10 Hz. 47 | 48 | ### Downconversion and the basics of IQ data 49 | 50 | Ask the class what happens if you try to downconvert 9 Hz by 10 Hz. They may say you can't do it. This leads us IQ data. 51 | 52 | 1. Draw a sinusoidal function with a frequency of 12 Hz. 53 | 2. Ask a student to draw the function downconverted by 10 Hz using a real wave and an imaginary wave, with the imaginary wave 90 degrees behind the real wave. 54 | - Ideally, use different colors to distinguish, but you can use a dotted line if necessary. 55 | - As before, let's assume the real wave starts at the origin. footnote 1 56 | - The real wave is called the "in-phase" (I) part of the signal, and the imaginary wave is called the "quadrature" (Q) part of the signal. This is the origin of the name "IQ data". 57 | 3. Repeat with a frequency of 13 Hz, 14 Hz, and as many examples as desired. 58 | 4. Downconvert a 8 Hz wave. (It will become a negative 2 Hz wave, which is expressed with the imaginary part 90 degrees **before** the real part.) 59 | 60 | Footnote 1: For this beginner level, we're ignoring any phase shifts caused by downconversion. If you go deeper into learning SDRs, know that the IQ wave that we draw may be phase-shifted from what really happens inside of the SDR peripheral. However, the downconversion result is otherwise accurate. 61 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_GRC_FM_Receiver.md: -------------------------------------------------------------------------------- 1 | 2 | # GRC FM Receiver 3 | 4 | ℹ️ This material coincides with material from SDR slideshow C (slides 1-44). However, the slides are not necessary for building and operating this program. 5 | 6 | Build this flow diagram in the GNU Radio Companion progam on your computer. (This instruction may not be repeated in subsequent lessons.) 7 | 8 | ## Summary 9 | 10 | ``` 11 | GUI Chooser 12 | 13 | GUI Range 14 | 15 | GUI Range 16 | 17 | GUI Check Box 18 | 19 | ┌─⟶ Time sink 20 | ├─⟶ Waterfall sink ┌─⟶ Waterfall sink 21 | Osmocom Source ─┴─⟶ Band Pass Filter ─┴─⟶ WBFM Receive ⟶ Rational Resampler ⟶ Audio Sink 22 | 23 | 24 | ``` 25 | 26 | You may wish to reference these Common GNU Radio Companion [error messages](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/Common-GNURadio-error-messages.md). 27 | 28 | ## How to set the Parameters 29 | 30 | ### For the GUI Chooser: 31 | 32 | - Id: `favorite_stations` 33 | - Type: `Float` 34 | - Num Options: `4` 35 | - Default Option: `104.3e6` (_This only applies if your version of GRC has a `Default Option` separated from `Option 0`._) 36 | - Option 0: `104.3e6` (_Replace these with actual stations_) 37 | - Label 0: `Popular country rock jazz` 38 | - Option 1: `93.9e6` 39 | - Label 1: `Baroque heavy metal` 40 | - Option 2: `100.9e6` 41 | - Label 2: `Noisy noise` 42 | - Option 3: `105.7e6` 43 | - Label 3: `Some other creative station name` 44 | - Widget: `Radio Buttons` 45 | 46 | 47 | ### For the First GUI Range: 48 | 49 | - Id: `if_gain_slider`[ footnote](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/030_GRC_FM_Receiver.md#footnotes) 50 | - Default Value: `24` 51 | - Start: `0` 52 | - Stop: `40` 53 | - Step: `8` 54 | 55 | ### For the Second GUI Range: 56 | 57 | - Id: `center_freq_slider` 58 | - Default Value: `favorite_stations` 59 | - Start: `88e6` 60 | - Stop: `108e6` 61 | - Step: `10e3` 62 | 63 | ### For the GUI Check Box: 64 | 65 | - Id: `hardware_filter` 66 | - Default Value: `False` 67 | 68 | ### For the `samp_rate` variable (_already in the flowgraph_): 69 | 70 | - Value: `8e6` 71 | 72 | ### For the Osmocom Source: 73 | 74 | - Device Arguments: `"hackrf=0"`[ footnote](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/030_GRC_FM_Receiver.md#footnotes) 75 | - Ch0: Frequency (Hz): `center_freq_slider` 76 | - Ch0: RF Gain (dB): `0` 77 | - Ch0: IF Gain (dB): `if_gain_slider` 78 | - Ch0: BB Gain (dB): `50` 79 | - Ch0: Bandwidth (Hz): `hardware_filter * 2.75e6` 80 | 81 | ### For the Band Pass Filter: 82 | 83 | - FIR Type: `Complex -> Complex (Complex Taps) (Decim)` 84 | - Low Cutoff Freq: `-100e3` (notice the negative) 85 | - High Cutoff Freq: `100e3` 86 | - Transition Width: `90e3` 87 | 88 | ### For the WBFM Receive: 89 | 90 | - Quadrature Rate: `samp_rate` 91 | - Audio Decimation: `1` 92 | 93 | ### For the Rational Resampler: 94 | 95 | - Type: `Float -> Float (Real Taps)` 96 | - Interpolation: `int(48e3)` 97 | - Decimation: `int(samp_rate)` 98 | 99 | ### For the Audio Sink: 100 | 101 | - Sample Rate: `48 kHz` (Pick from drop-down menu) 102 | 103 | ### First Waterfall Sink (directly connected from the osmocom Source): 104 | 105 | - Name: `"Original spectrum"` 106 | 107 | ### Second Waterfall Sink (after the band pass filter): 108 | 109 | - Name: `"Filtered: a 200 kHz band in the center of the received spectrum"` 110 | - Center Frequency (Hz): `center_freq_slider` 111 | 112 | ### For the Time Sink: 113 | 114 | - Leave all as defaults. 115 | 116 | ### Now Run the program: 117 | 118 | - Press the triangular-shaped "run" button at the top of the program window, or press the F6 button on your keyboard. 119 | - A new window with a GUI operation window will appear, showing graphs and controls. 120 | - To stop, either close the GUI operation window, or in the programming window, press the square "stop" button at the top, or press F7 on your keyboard. 121 | 122 | ### Discussion 123 | 124 | - If you have any errors, remember to look at the list of Common GNU Radio Error messages in the [resources](https://github.com/python-can-define-radio/sdr-course/tree/main/resources) folder. 125 | 126 | - You'll notice that sometimes you need to move the antenna to ensure good reception. Watching the Waterfall can help with seeing how good your reception is. 127 | 128 | - In our experience, the osmocom Source's Bandwidth parameter only works if you set it during runtime. Ask if you'd like to know details. 129 | 130 | - The purpose of the `hardware_filter` is to avoid aliasing. For more info, see [this lesson](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch05_Concepts/034_Oversampling_Undersampling.md). 131 | 132 | ### Questions 133 | 134 | - Why did we pick the given `Start` and `Stop` for the `center_freq_slider`? 135 | - Does this range include all Commercial FM stations in the United States? 136 | - If not, how should you adjust it to include any missing frequencies? 137 | 138 | ### Footnotes 139 | 1. `hackrf=0` explanation 140 | - Normally index zero is assigned to the first HackRF plugged in. If you have multiple HackRFs, they will be numbered sequentially (0, 1, 2, 3, etc). 141 | 2. IF Gain slider 142 | - Notice when you adjust the IF Gain slider, the intensity changes. (In the frequency sink, this is seen as a Y-axis increase. In the waterfall sink, this is seen as a color change). 143 | - Check out the [HackRF One FAQ](https://hackrf.readthedocs.io/en/latest/faq.html) to find out the Intermediate Frequency (IF), Radio Frequency (RF), and Baseband (BB) gain capabilities of the HackRF One. You'll most likely want to do Ctrl + f search for "gain" on that page. 144 | 145 | ### Optional exercise 146 | 147 | - Student activity, groups of 5: 148 | - Draw pictures to show what each of these blocks is doing: 149 | - Band pass filter 150 | - WBFM Receive 151 | - Rational Resampler 152 | - Give textual descriptions of what every other block does. 153 | - Discuss activity as a class 154 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_GRC_FM_Transmitter.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2023 Aug 18: 040_GRC_FM_Transmitter.md
 4 | 2023 May 22: 030_GNU_Radio_FM_Transmitter.md
 5 | 2022 Aug 30: 250-GNU-Radio-FM-Transmitter.md
 6 | 2022 Aug 08: 150-GNU-Radio-FM-Transmitter.md
 7 | 
8 |
9 | 10 | # GRC FM Transmitter 11 | 12 | ## Disclaimer 13 | 14 | Broadcasting without a license is illegal in most countries. This should only be used for research purposes in an environment that is sufficiently radio-shielded from other electronics. 15 | 16 | ## Summary 17 | 18 | ``` 19 | GUI Range 20 | 21 | Wav File Source ⟶ Rational Resampler ⟶ WBFM Transmit ⟶ Osmocom sink 22 | ⟶ Waterfall sink 23 | ``` 24 | 25 | - Have a (working) slider to pick the frequency that you're tuning in to. Working means the HackRF changes frequency when the slider changes 26 | - Modulate the sound and transmit it 27 | 28 | 29 | ## How to set the Parameters 30 | 31 | ### For the `samp_rate` variable (_already in the flowgraph_): 32 | 33 | - Value: `2e6` 34 | 35 | ### For the GUI Range: 36 | 37 | - Id: `center_freq_slider` 38 | - Default Value: `98.5e6` 39 | - Start: `88e6` 40 | - Stop: `108e6` 41 | - Step: `10e3` 42 | 43 | ### For the Wav File Source: 44 | 45 | - _NOTE: You can use a Audio Source instead if you'd like. See notes at the bottom of this page._ 46 | - File: Pick a file by pressing the "..." button. It must be a wav file, not an mp3, etc. You can find wav files [here](https://github.com/adafruit/Adafruit-Sound-Samples/tree/master/sonic-pi). 47 | 48 | - Repeat: Up to you; do you want it to repeat? 49 | 50 | ### For the Rational Resampler: 51 | 52 | - Type: `Float -> Float (Real Taps)` 53 | - Interpolation: `int(samp_rate)` 54 | - Decimation: This should match the sample rate that you'll find in the properties of the wav file. If you can't find the sample rate, put `int(60e3)` (which is 60000 Hz, or 60 kHz). The song will probably play too fast; adjust to make the song sound normal. 55 | 56 | ### For the WBFM Transmit: 57 | 58 | - Audio Rate: `int(samp_rate)` 59 | - Quadrature Rate: `int(samp_rate)` 60 | 61 | ### For the Osmocom Sink: 62 | 63 | - Device Arguments: `"hackrf=0"` 64 | - Ch0: Frequency (Hz): `center_freq_slider` 65 | - Ch0: RF Gain (dB): `0` 66 | - Ch0: IF Gain (dB): `32` 67 | - Ch0: BB Gain (dB): `0` 68 | 69 | ### For the Waterfall Sink: 70 | 71 | - Leave all as defaults. 72 | 73 | ### Discussion 74 | 75 | - Try different audio files, different IF Gains, and different broadcast frequencies. 76 | 77 | ### Using an Audio Source 78 | 79 | You have two options: 80 | 1. Simply using an `Audio Source` block allows you to use a microphone as your source. You'll leave the "Device Name" empty, and set the "Sample Rate" to 48 kHz. You'll also set the `Rational Resampler`'s Decimation to 48000. 81 | 2. You can also use an `Audio Source` block with a special configuration to broadcast whatever is currently playing on your computer: 82 | 1. Do the setup described on the [GNU Radio Wiki](https://wiki.gnuradio.org/index.php?title=ALSAPulseAudio#Monitoring_the_audio_input_of_your_system_with_PulseAudio). 83 | 2. In your `Audio Source` block, for the Device Name, put "pulse_monitor". 84 | 3. Set the `Audio Source`'s sample rate to 48000. 85 | 4. Set the `Rational Resampler`'s Decimation to 48000. 86 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_GRC_Spectrum_Analyzer.md: -------------------------------------------------------------------------------- 1 | 2 | # GRC Spectrum Analyzer 3 | 4 | ℹ️ This material coincides with material from SDR slideshow D (slides 1-27). However, the slides are not necessary for building and operating this program. 5 | 6 | Build this flow diagram in the GNU Radio Companion progam on your computer. 7 | 8 | ## Summary 9 | 10 | ``` 11 | GUI Range 12 | 13 | GUI Range 14 | 15 | 16 | Osmocom ---> QT GUI sink 17 | Source 18 | ``` 19 | 20 | You may wish to reference these Common GNU Radio Companion [error messages](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/Common-GNURadio-error-messages.md). 21 | 22 | ## How to set the Parameters: 23 | 24 | ### For the First GUI Range: 25 | 26 | - Id: `if_gain_slider` 27 | - Default Value: `24` 28 | - Start: `0` 29 | - Stop: `40` 30 | - Step: `8` 31 | 32 | ### For the Second GUI Range: 33 | 34 | - Id: `center_freq_slider` 35 | - Default Value: `107_900_000` #This is the same as 107.9e6 36 | - Start: `40e6` 37 | - Stop: `500e6` 38 | - Step: `10e3` 39 | 40 | ### For the `samp_rate` variable (_already in the flowgraph_): 41 | 42 | - Value: `20e6` 43 | 44 | ### For the QT GUI sink: 45 | 46 | - FFT Size: `8192` 47 | - Center Frequency (Hz): `center_freq_slider` 48 | - Update Rate: `20` 49 | - Show RF Freq: `Yes` 50 | 51 | ### For the Osmocom Source: 52 | 53 | - Device Arguments: `"hackrf=0"` 54 | - Ch0: Frequency (Hz): `center_freq_slider` 55 | - Ch0: RF Gain (dB): `0` 56 | - Ch0: IF Gain (dB): `if_gain_slider` 57 | - Ch0: BB Gain (dB): `50` 58 | 59 | ### Now Run the program 60 | 61 | - Press the triangular-shaped "run" button at the top of the program window, or press F6 button on your keyboard. 62 | - A new window with a GUI operation window will appear, showing graphs and controls. 63 | - To stop, either close the GUI operation window, or in the programming window, press the square "stop" button at the top. 64 | 65 | ### Discussion 66 | 67 | - What are the limits of the Hack RF's IF gain? 68 | - How big of a slice of the spectrum do you see? What part of the flowgraph defines this? 69 | - While running, in the Frequency Display tab, try raising the Average in the bottom right corner. Does it help make signals more visible? 70 | - What happens when various variables are either left as default or set incorrectly? 71 | - In particular, the instructor will demonstrate this problem using the GUI Sink's Center Frequency attribute. 72 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_GRC_info.md: -------------------------------------------------------------------------------- 1 | ### Common pitfalls of GNU Radio Companion (GRC): 2 | 3 | - When using the "save" dialog in the Ubuntu GNOME desktop environment, it's easy to accidentally search instead of typing the file name. 4 | - Solution: Click in the file name field before typing a file name. 5 | - When you are searching for blocks, there are odd behaviors if you have a block selected. 6 | - Solution: Be extra careful to deselect blocks before searching. 7 | - Solution (alternate): always press Ctrl+F before typing. 8 | - When editing the "Properties" of a block (Access these by double-clicking or right-clicking the block): 9 | - When you scroll, you may accidentally scroll above the arrow of a drop-down menu, which changes the contents. 10 | - Solution: Scroll with your cursor on the left side of the window. 11 | - The `Apply` button is buggy: it will sometimes NOT apply the changes, such as if you use it twice in a row. 12 | - Solution: Don't use the "Apply" button. Press "Ok" instead. 13 | 14 | ### Some useful keyboard shortcuts in GNU Radio Companion (GRC): 15 | 16 | - Ctrl + F : Find blocks 17 | - D : Disable a block 18 | - B : Bypass a block 19 | - E : Enable a block 20 | 21 | ### Tutorial: 22 | 23 | - Go to [this tutorial page at GNUradio.org](https://wiki.gnuradio.org/index.php?title=Tutorials) (https://wiki.gnuradio.org/index.php?title=Tutorials). 24 | 25 | - Under `Beginner Tutorials > Introducing GNU Radio` we will work through the "Your First Flowgraph" tutorial together as a class. 26 | 27 | - Once you have finished "Your First Flowgraph", complete these tutorials from the `Flowgraph Fundamentals` category: 28 | 29 | - Python Variables in GRC 30 | - Variables in Flowgraphs 31 | - Runtime Updating Variables 32 | - Signal Data Types 33 | - Converting Data Types 34 | 35 | - Once those are completed you should return to `Ch01_Diving_in_Headfirst` and try constructing the FM receiver in `030_GRC_FM_Receiver.md`. 36 | 37 | [GNU Radio tutorials](https://wiki.gnuradio.org/index.php?title=Tutorials) 38 | [GNU Radio error messages](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/Common-GNURadio-error-messages.md) -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_Improved_PSK_Digital_Jammer.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2023 Mar 01: 245-Improved-Digital-Jammer.md
 4 | 2023 May 22: 022_Improved_Digital_Jammer.md
 5 | 
6 |
7 | 8 | # Improved Digital Jammer 9 | 10 | ℹ️ This material coincides with material from SDR slideshow F (all slides). 11 | 12 | This digital jammer is more effective than the original because it can match the symbol rate of the target. 13 | 14 | ``` 15 | 16 | GUI Range 17 | 18 | Constellation Object 19 | 20 | Variable 21 | 22 | 23 | Random source ⟶ Constellation modulator ⟶ Rational Resampler ⟶ Osmocom sink 24 | ⟶ Waterfall sink 25 | ⟶ Time sink 26 | ``` 27 | 28 | - Variable `samp_rate`: Same as PSK_Digital_Jammer 29 | - Variable (a new variable block): 30 | - Id: `symbol_rate` 31 | - Value: `100e3` 32 | - Rational Resampler: 33 | - Interpolation: `int(samp_rate)` 34 | - Decimation: `int(symbol_rate)` 35 | - Time Sink: 36 | - Y min: `-2` 37 | - Y max: `2` 38 | - All other parameters: 39 | - Same as the normal digital jammer. 40 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_Noise_Jammer.md: -------------------------------------------------------------------------------- 1 | 2 | # Noise Jammer 3 | 4 | ℹ️ This material coincides with material from SDR slideshow E (all slides). 5 | 6 | ## Disclaimer 7 | 8 | Intentionally disrupting someone's wireless connection (i.e., using a jammer) is illegal in most countries. This should only be used for research purposes in an environment that is sufficiently radio-shielded from other electronics. 9 | 10 | ## Summary 11 | 12 | ``` 13 | GUI Range 14 | 15 | GUI Range 16 | 17 | GUI Range 18 | 19 | 20 | Noise Source ⟶ Low Pass Filter ⟶ Osmocom sink 21 | ⟶ Waterfall sink 22 | ``` 23 | 24 | You may wish to reference these Common GNU Radio Companion [error messages](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/Common-GNURadio-error-messages.md). 25 | 26 | - In the Low Pass Filter, the **cutoff** and **transition width** should be sliders. 27 | - Know how to change Noise Type in Noise Source. 28 | - Have a slidable frequency to control where you are jamming. 29 | 30 | ## How to set the Parameters 31 | 32 | ### For the First GUI Range: 33 | 34 | - Id: `cut_freq_slider` 35 | - Default Value: `300e3` 36 | - Start: `20e3` 37 | - Stop: `900e3` 38 | - Step: `10e3` 39 | 40 | ### For the Second GUI Range: 41 | 42 | - Id: `tr_width_slider` 43 | - Default Value: `70e3` 44 | - Start: `20e3` 45 | - Stop: `900e3` 46 | - Step: `10e3` 47 | 48 | ### For the Third GUI Range: 49 | 50 | - Id: `center_freq_slider` 51 | - Default Value: Choose a value between your start and stop values 52 | - Start: `88e6` 53 | - Stop: `108e6` 54 | - Step: `10e3` 55 | 56 | ### For the Low Pass Filter: 57 | 58 | - Cutoff Freq: `cut_freq_slider` 59 | - Transition Width: `tr_width_slider` 60 | 61 | ### For the Noise Source: 62 | 63 | - Noise Type: `Uniform` 64 | - Amplitude: `50` 65 | 66 | ### For the `samp_rate` variable (_already in the flowgraph_): 67 | 68 | - Value: `2e6` 69 | 70 | ### For the Osmocom Sink: 71 | 72 | - Device Arguments: `"hackrf=0"` 73 | - Ch0: Frequency (Hz): `center_freq_slider` 74 | - Ch0: RF Gain (dB): `0` 75 | - Ch0: IF Gain (dB): `32` 76 | - Ch0: BB Gain (dB): `0` 77 | 78 | ### For the Waterfall Sink: 79 | 80 | - Center Frequency (Hz): `center_freq_slider` 81 | 82 | ## Discussion 83 | 84 | ### How do I know if it's working? 85 | 86 | - You'll need to have a receiving device to verify that the transmission is working. In class, the easiest approach is to ask another student to open GQRX. 87 | - You can increase the power by setting the IF Gain higher. The highest IF Gain for a Hack RF is documented [here](https://hackrf.readthedocs.io/en/latest/faq.html#what-gain-controls-are-provided-by-hackrf). 88 | 89 | ### Noise Types 90 | 91 | - Notice that the Noise Source has multiple Noise Types. As per the [documentation](https://wiki.gnuradio.org/index.php/Noise_Source), only Uniform and Gaussian are supported. As a result, the other two Noise Types will produce nothing, and the Waterfall will, as a result, be empty. 92 | - What's the difference between Uniform and Gaussian? Try it yourself! 93 | Here's the easiest way to see the difference: `Noise Source ⟶ Time Sink` 94 | (I am not an expert on the practical implications of this difference, but I believe that Gaussian is more similar to "naturally occurring" noise.) 95 | 96 | ### Filter parameters 97 | 98 | Briefly, the cutoff and transition width both control how wide of a band is being jammed. 99 | 100 | For more info, see the exercise about the Band Pass Filter (at time of writing, numbered #160). 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_PSK_Digital_Jammer.md: -------------------------------------------------------------------------------- 1 | 2 | # Digital Jammer 3 | 4 | ℹ️ This material coincides with material from SDR slideshow F (all slides). 5 | 6 | ## Disclaimer 7 | 8 | Intentionally disrupting someone's wireless connection (i.e., using a jammer) is illegal in most countries. This should only be used for research purposes in an environment that is sufficiently radio-shielded from other electronics. 9 | 10 | ## Summary 11 | 12 | ``` 13 | GUI Range 14 | 15 | Constellation object 16 | 17 | 18 | Random source ⟶ Constellation modulator ⟶ Osmocom sink 19 | ⟶ Waterfall sink 20 | ``` 21 | 22 | You may wish to reference these Common GNU Radio Companion [error messages](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/Common-GNURadio-error-messages.md). 23 | 24 | - The constellation modulator should have a correct Constellation attribute. 25 | - Have a slidable frequency to control where you are jamming. 26 | 27 | ## How to set the Parameters 28 | 29 | ### For the `samp_rate` variable (_already in the flowgraph_): 30 | 31 | - Value: `2e6` 32 | 33 | ### For the GUI Range: 34 | 35 | - Id: `center_freq_slider` 36 | - Default Value: Choose a value between your start and stop values 37 | - Start: `88e6` 38 | - Stop: `108e6` 39 | - Step: `10e3` 40 | 41 | ### For the Constellation Object: 42 | 43 | - ID: `myconstell` 44 | - Constellation Type: `BPSK`. _Also, try these: `QPSK`, `8PSK`, `DQPSK`, `16QAM`_ 45 | - Leave all other attributes as defaults. 46 | 47 | ### For the Random Source: 48 | 49 | - Output Type: `Byte` 50 | - Leave all other attributes as defaults. 51 | 52 | ### For the Constellation Modulator: 53 | 54 | - Constellation: `myconstell` 55 | - Leave all other attributes as defaults. 56 | 57 | ### For the Osmocom Sink: 58 | 59 | - Device Arguments: `"hackrf=0"` 60 | - Ch0: Frequency (Hz): `center_freq_slider` 61 | - Ch0: RF Gain (dB): `0` 62 | - Ch0: IF Gain (dB): `32` 63 | - Ch0: BB Gain (dB): `0` 64 | 65 | ### For the Waterfall Sink: 66 | 67 | - Center Frequency (Hz): `center_freq_slider` 68 | 69 | ## Discussion: 70 | 71 | - Generally speaking, a jammer that matches the modulation scheme of the target is more effective than a simple noise jammer. 72 | - For example, if someone were to use this Digital Jammer to jam an FM radio station, it would probably be negligibly different from a noise jammer. 73 | - But if someone were transmitting data using BPSK (which is a modulation technique that is used in cell phones, for example), then a BPSK jammer _with matching parameters_ would be able to jam more effectively and/or with less power than a simple noise jammer. 74 | - What parameters must match? Perhaps the most important would be the number of symbols sent every second. The goal of a digital jammer is to closely imitate a real transmission, so that the receiver has trouble distinguishing the real data from the fake random data that the jammer is sending. 75 | 76 | ## Exercise: 77 | 78 | - Sender: Whistle short pulses and long pulses to represent zeros and ones. Let's say a short pulse is a zero, and a long pulse is a one. 79 | - Receiver: Announce what you're receiving. 80 | - Rest of class: Make a "psshhhhh" noise. That's noise jamming of a broad range of frequencies. 81 | - Now, rest of class, whistle on same pitch (frequency), with very short pulse-lengths. That's a type of noise jamming called spot jamming -- all of your energy is focused on the target frequency. 82 | - Finally, rest of class, whistle on the same frequency, but this time, whistle a random sequence of fake data using the same pulse lengths that the sender is using. That's digital jamming. Notice that whistle pulses that are similar in length to the sender's are more effective than pulses that merely match pitch (and not pulse duration). 83 | 84 | ### See also... 85 | 86 | - https://itecnotes.com/electrical/electrical-question-about-digital-modulation/ 87 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_Spectrum_Painting.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
  3 | 2023 Mar 07: 999-spectrum-painting.md
  4 | 2023 May 22: 040_Spectrum_Painting.md
  5 | 
6 |
7 | 8 | # Spectrum Painting 9 | 10 | ## Frequency Spectrum Painting using Python & Software-Defined Radio (SDR). 11 | See Python program, below, for description. 12 | Page updated April 25, 2023. 13 | 14 | ### Resources required for full functionality: 15 | ``` 16 | - Software to run a Python program, 17 | - "GNU Radio Companion" or equivalent software, 18 | - One SDR device to transmit (TX), and 19 | - A second SDR device to receive (RX) signals. 20 | Optionally, a single SDR device running in TX+RX duplex mode. 21 | ``` 22 | 23 | Source: https://www.gkbrk.com/2021/02/spectrum-drawing/ 24 | 25 | Our modified version is below. 26 | 27 | After running the program, you'll have a file in your working directory named `waterpaint.iqdata`. You can use GNU Radio Companion to transmit the file using a flowgraph containing `File Source --> osmocom Sink`. 28 | 29 | ### Python program to create a broadcast-capable data file for spectrum painting: 30 | ```python3 31 | """ 32 | This Python3 program reads a black and white .jpg or .jpeg image file and converts it to 33 | an "iqdata" file, for broadcast (TX) through an SDR such as a HackRF device + GNU Radio Companion (GRC). 34 | A separte program is required to perform the broadcast in GRC. 35 | The painted image will be visible on a second SDR in the receiving (RX) mode (or one SDR with duplex TX/RX) 36 | with a GRC program having a waterfall display for vieiwing a freqeuncy spectrum. 37 | 38 | source: https://github.com/python-can-define-radio/sdr-course/edit/main/classroom_activities/999-spectrum-painting.md 39 | based on https://www.gkbrk.com/2021/02/spectrum-drawing/ 40 | """ 41 | 42 | from PIL import Image 43 | import math 44 | import numpy as np 45 | 46 | 47 | in_filename = "hi.jpeg" # Update this to point to a B&W image stored in JPG or JPEG format. 48 | out_filename = "waterpaint.iqdata" 49 | 50 | RATE = 2000000 # sample rate 51 | TRANSMIT_TIME = 5 # Seconds 52 | FREQ_DEV = 1000000 # Hz The image will extend this many HZ lower than your broadcast's tuned or center frequency. 53 | 54 | parameters_info = f""" 55 | Sample rate: {RATE} samples per second 56 | Time for one full picture transmission: {TRANSMIT_TIME} seconds 57 | Bandwidth of transmission: {FREQ_DEV} Hz 58 | """ 59 | 60 | 61 | def write(i, q): 62 | # i = int(i * 127) 63 | # q = int(q * 127) 64 | # i = int(i * 31) 65 | # q = int(q * 31) 66 | ## TODO next time: what type is bb? gnu radio needs to interpret it correctly 67 | # data = struct.pack("bb", i, q) 68 | outfile.write(np.complex64(q + 1j*i).tobytes()) 69 | 70 | 71 | outfile = open(out_filename, "wb") 72 | im_raw = Image.open(in_filename) 73 | im = im_raw.convert("1") # 1 means 1-bit image 74 | 75 | 76 | print(f"Generating '{out_filename}' with these parameters:") 77 | print(parameters_info) 78 | print("Progress countdown starting now.") 79 | 80 | t = 0 81 | 82 | ## y loops backwards (presumably because we want to transmit 83 | ## the bottom of the image first) 84 | for y in range(im.height)[::-1]: 85 | print(y) 86 | target = t + TRANSMIT_TIME / im.height 87 | 88 | # Read the line backwards to paint forwards, by including: [::-1] 89 | line = [im.getpixel((x, y)) for x in range(im.width)[::-1]] 90 | while t < target: 91 | i = 0 92 | q = 0 93 | 94 | for x, pix in enumerate(line): 95 | if not pix: 96 | continue 97 | offs = x / im.width 98 | offs *= FREQ_DEV 99 | i += math.cos(2 * math.pi * offs * t) * 0.01 100 | q += math.sin(2 * math.pi * offs * t) * 0.01 101 | write(i, q) 102 | t += 1.0 / RATE 103 | 104 | print(f"File '{out_filename}' is ready. You can now transmit the iqdata file using GNU Radio Companion.") 105 | print("Reminder of parameters:") 106 | print(parameters_info) 107 | ``` 108 | -------------------------------------------------------------------------------- /classroom_activities/Ch05_Concepts/099_Subtle_Jammer.md: -------------------------------------------------------------------------------- 1 |
Naming history (click to expand) 2 |
 3 | 2022 Aug 08: 160-GNU-Radio-FM-Subtle-Jammer.md
 4 | 2022 Aug 30: 260-GNU-Radio-FM-Subtle-Jammer.md
 5 | 2023 May 22: 023_Subtle_Jammer.md
 6 | 
7 |
8 | 9 | # Subtle Jammer 10 | 11 | ℹ️ This material coincides with material from SDR slideshow B (slide 56, 61-62). 12 | 13 | ## Disclaimer 14 | 15 | Intentionally disrupting someone's wireless connection (i.e., using a jammer) is illegal in most countries. This should only be used for research purposes in an environment that is sufficiently radio-shielded from other electronics. 16 | 17 | ## Summary 18 | 19 | ``` 20 | GUI Range 21 | 22 | Constant Source ⟶ Osmocom sink 23 | ⟶ Waterfall sink 24 | ``` 25 | 26 | - Have a slidable frequency to control where you are jamming. 27 | 28 | ## How to set the Parameters 29 | 30 | ### For the GUI Range: 31 | 32 | - Id: `center_freq_slider` 33 | - Default Value: `98.5e6` 34 | - Start: `88e6` 35 | - Stop: `108e60` 36 | - Step: `10e3` 37 | 38 | ### For the Constant Source: 39 | 40 | - Output Type: `Complex` 41 | - Constant: `1` 42 | 43 | ### For the `samp_rate` variable (_already in the flowgraph_): 44 | 45 | - Value: `2e6` 46 | 47 | ### For the Osmocom Sink: 48 | 49 | - Device Arguments: `"hackrf=0"` 50 | - Ch0: Frequency (Hz): `center_freq_slider` 51 | - Ch0: RF Gain (dB): `0` 52 | - Ch0: IF Gain (dB): `32` 53 | - Ch0: BB Gain (dB): `0` 54 | 55 | ### For the Waterfall Sink: 56 | 57 | - Center Frequency (Hz): `center_freq_slider` 58 | 59 | ## Discussion 60 | 61 | The [Wikipedia Radio Jamming article](https://en.wikipedia.org/wiki/Radio_jamming#Method) says this regarding subtle jamming: "Thanks to the FM capture effect, frequency modulated broadcasts may be jammed, unnoticed, by a simple unmodulated carrier." 62 | 63 | It may seem strange that a "Constant Source" block is used to accomplish this. The reason this works is because behind the scenes, the Hack RF combines (using multiplication) the given signal with the specified carrier frequency. That means that the transmission that goes to the antenna is actually a pure sinusoidal wave of frequency 98.5 MHz. 64 | -------------------------------------------------------------------------------- /resources/Common-GNURadio-error-messages.md: -------------------------------------------------------------------------------- 1 | ## Expression None is invalid 2 | 3 | ``` 4 | Expression None is invalid for type 'real'. 5 | ``` 6 | 7 | Most often, this means... 8 | 9 | - You have a typo in your variable name. 10 | - You used a keyword, such as `if`, as a variable. Variables must not be keywords (`if`, `else`, `while`, etc) 11 | 12 | ## Expression ... is invalid 13 | 14 | ``` 15 | Expression 200.0 is invalid for type 'int'. 16 | ``` 17 | 18 | If you're using `2e6`, for example, you may need to type `int(2e6)`. 19 | 20 | ## NameError 21 | 22 | ``` 23 | NameError: name '...' is not defined 24 | ``` 25 | 26 | Also usually a typo. 27 | 28 | ## Can't open file 29 | 30 | ``` 31 | RuntimeError: can't open file 32 | ``` 33 | 34 | Most often, this means you omitted the `File` in the `File Source` block. 35 | 36 | ## Syntax error involving a comma 37 | 38 | ``` 39 | File "/..../stuff.py", line 9999 40 | , 41 | ^ 42 | SyntaxError: invalid syntax 43 | ``` 44 | 45 | Most often, this means that you left a parameter blank that should not be blank. 46 | 47 | **Solution**: 48 | 49 | - Open the .py file that is mentioned. 50 | - Look at (or above) the mentioned line to figure out which block is the issue. 51 | 52 | ## ... not enough devices 53 | 54 | ``` 55 | RuntimeError: Failed to use '0' as HackRF device index: not enough devices 56 | ``` 57 | 58 | Usually your HackRF is not plugged in. 59 | 60 | ## "Done" 61 | 62 | ``` 63 | >>> Done (return code -11) 64 | ``` 65 | 66 | Usually your HackRF is not plugged in. 67 | 68 | ## Resource Busy 69 | 70 | ``` 71 | Device or resource busy. 72 | ``` 73 | 74 | Usually you have another program open that is using the HackRF, such as GQRX or another GNU Radio program. 75 | 76 | If you don't see anything else open, then press the "Reset" button on the HackRF. 77 | 78 | ## Firdes check 79 | 80 | ``` 81 | RuntimeError: firdes check failed: 0 < fa <= sampling_freq / 2 82 | ``` 83 | 84 | There are two common causes for this error: 85 | 1. Wrong FIR Type (usually in the Band Pass Filter block) 86 | 2. Violating Nyquist theorem (usually in one of the Filter blocks). 87 | Example: If you have the `samp_rate` set too low, or if you have the `low_cut` or `high_cut` set outside of the allowed boundaries. 88 | 89 | ## Assertion... 90 | 91 | ``` 92 | Assertion "start <= value <= stop" failed. 93 | ``` 94 | 95 | In GUI Ranges, the Start and Stop are the left and right limits of the slider. The Default Value is where the slide-piece is placed when the flowgraph runs. 96 | 97 | - The left limit (the "Start") must be the lowest value. 98 | - The right limit (the "Stop") must be the highest value. 99 | - The Default Value must be somewhere in between. 100 | 101 | One common cause is forgetting the `e3`, `e6`, etc. For example: 102 | 103 | This is incorrect: Start: `88e6`, Stop: `108e6`, Default Value: `95` 104 | _Notice: 95 is not between the given start and stop values_ 105 | This is correct: Start: `88e6`, Stop: `108e6`, Default Value: `95e6` 106 | 107 | 108 | ## ID `default` is blacklisted 109 | 110 | How to fix: Open the `Options` block, and set the Id to something other than default 111 | 112 | ## "must begin with a letter and may contain letters, numbers, and underscores." 113 | 114 | How to fix: Open the `Options` block, and set the Id to something that satisfies the criteria described. 115 | For example: 116 | 117 | - `my cool flowgraph` won't work because it has spaces. You could change it to `my_cool_flowgraph` or `mycoolflowgraph` or similar. 118 | - `my-cool-flowgraph` won't work because of the hyphens. 119 | - `030_flowgraph` won't work because it starts with a number. You could change it to `exercise_030_flowgraph` or `my030` or similar. 120 | - `my_flowgraph ` won't work because it ends with a space. Those are especially hard to see! 121 | - `my_flowgraph.grc` won't work because it has a period in it. Simply remove the ".grc" part in the Id. 122 | 123 | If you're having trouble finding the culprit characters, you can always erase the Id entirely and type a new Id. 124 | 125 | ## Overflow 126 | 127 | ``` 128 | OverflowError: argument ... overflowed: value must be in the range -2147483648 to 2147483647 129 | ``` 130 | 131 | Often, this is caused by GUI Ranges. For example, if you chose the following arguments... 132 | 133 | - Start: 0 134 | - Stop: 1e12 135 | - Step: 2 136 | 137 | ... then the computer would attempt to create a slider with 500 billion steps. Sliders can't have that many steps. (Also, in most cases, you wouldn't need the precision of stepping by 2 when working with such a large range of values.) 138 | 139 | ## Topology 140 | 141 | ``` 142 | RuntimeError: check topology failed on time_sink_f(1) using ninputs=1, noutputs=0 143 | ``` 144 | 145 | This means the Time Sink has more available inputs than incoming data. For example, this error will occur if your time sink has three input ports, but only one is connected. 146 | 147 | On our exercises, we used text-based diagrams to express the flowgraphs due to time constraints, so in some cases, we couldn't express all of the connecting arrows. We indicated any missing connection-arrows in the "Memos" section of each exercise. 148 | -------------------------------------------------------------------------------- /resources/Definitions.md: -------------------------------------------------------------------------------- 1 | These are terms that relate to GNU Radio and/or to SDRs in general. 2 | 3 | **Decimate, Decimation**: Reduce the sample rate of a signal. 4 | ℹ️ This definition coincides with material from SDR slideshow A (slides 22, 25, 28-32, 40-42). 5 | Decimation is a mathematical operation that reduces the original sample rate of a sequence to a lower rate. It is the opposite of interpolation. In mathematics, decimation refers to the creation of a new sequence comprising only every n-th element of a source sequence.1. 6 | 7 | Common reasons to do this include... 8 | 9 | - Hardware compatibility: A common SDR use-case is demodulating and playing sound. Sound cards usually only allow sample rates up to 48 thousand samples per second, while SDR hardware often samples at much high rates, such as 2 million samples per second. 10 | - Reduced computational effort and storage space: Generally speaking, decreasing the number of samples helps with both of these. 11 | 12 | **Interpolate, Interpolation**: Increase the sample rate of a signal. 13 | The word "Extrapolate" is fairly common in non-technical English. The word "Interpolate" has a related meaning. 14 | As used in mathematics the definition of interpolation is: The insertion of an intermediate value or term into a series by estimating or calculating it from surrounding known values. 15 | 16 | **Rational Resampler**: Changes the sample rate using interpolation, decimation, or both. 17 | For example, in the FM Receiver flowgraph, the Rational Resampler reduces the sample rate because the sound card (which the Audio Sink interfaces with) cannot handle the high sample rate that comes from the SDR device (which the osmocom Source interfaces with). 18 | 19 | **Sample Rate**: (todo) 20 | 21 | **Sink**: in GNU Radio, a block that has "in" ports (data can flow into it), but no "out" ports. 22 | Common sinks that we use: 23 | 24 | - GUI Sinks (Display data in a Graphical User Interface) 25 | - File Sinks (Write data to a file) 26 | - osmocom Sink (transmits data using an osmocom-compatible device) 27 | 28 | -------------------------------------------------------------------------------- /resources/README.md: -------------------------------------------------------------------------------- 1 | # Resources 2 | 3 | - ["HackRF as the best SDR friend for hackers"](https://www.ivanglinkin.com/hackrf-as-the-best-sdr-friend-for-hackers/), from Ivan Glinkin. A very long page with a lot of useful info, including... 4 | - [Frequency bands](https://www.ivanglinkin.com/hackrf-as-the-best-sdr-friend-for-hackers#_Toc143872452): a summary of what you'll find on each band 5 | - [Hacking a doorbell](https://www.ivanglinkin.com/hackrf-as-the-best-sdr-friend-for-hackers#_Toc143872468) 6 | - [Hacking a remote-control car – 27 MHz](https://www.ivanglinkin.com/hackrf-as-the-best-sdr-friend-for-hackers#_Toc143872481) 7 | - [Hacking a remote-control car – 2.4 GHz](https://www.ivanglinkin.com/hackrf-as-the-best-sdr-friend-for-hackers#_Toc143872485) 8 | - [Hacking a portable weather station](https://www.ivanglinkin.com/hackrf-as-the-best-sdr-friend-for-hackers#_Toc143872490) 9 | - https://greatscottgadgets.com/sdr/ : Hack RF course by Michael Ossmann of Great Scott Gadgets. 10 | - https://gallicchio.github.io/learnSDR/ : Harvey Mudd College's SDR course. 11 | - [Tutorials on the GNU Radio Wiki](https://wiki.gnuradio.org/index.php/Tutorials) 12 | - [PySDR](https://pysdr.org/): A Guide to SDR and DSP using Python 13 | - [Software Defined Radio Academy](youtube.com/c/SoftwareDefinedRadioAcademy) youtube channel 14 | - https://wirelesspi.com/ 15 | - [RTL-SDR Blog](https://www.rtl-sdr.com/): Software defined radio news and projects. 16 | - Includes some tutorials, but mostly I recommend this for: 17 | - Inspiration (they post cool news), or 18 | - To buy a real (non-counterfeit) RTL-SDR. 19 | - [Ettus SDR Tutorials](https://files.ettus.com/tutorials/) 20 | - [Wave Walker DSP](https://www.wavewalkerdsp.com/) 21 | - Many tutorials. Fairly mathy. Some good foundational info too, such as: 22 | - [Why are Sinusoids used in DSP and RF?](https://www.wavewalkerdsp.com/2021/09/18/why-are-sinusoids-used-in-dsp-and-rf/) 23 | - [Signal and Noise: The Vocabulary of RF](https://www.wavewalkerdsp.com/2021/08/31/signal-and-noise-the-vocabulary-of-rf/) 24 | - [Morse code example](https://github.com/duggabe/gr-morse-code-gen) 25 | - [FM Reception with the GNU Radio Companion](https://www.nutsvolts.com/magazine/article/fm-reception-with-the-gnu-radio-companion): Great tutorial. Includes: 26 | - How to listen to FM 27 | - How to get the digital data (current song name, etc) 28 | - [Gnuradio Mini Projects](https://udel.edu/~mm/gr/): A variety of projects. 29 | - [DSP Illustrations](https://dspillustrations.com/): DSP using Python. 30 | - ["But what is a Fourier series? From heat flow to drawing with circles | DE4"](https://www.youtube.com/watch?v=r6sGWTCMz2k) Has cool animations. Pretty heavy math. 31 | - [SDR Peripheral Comparison table](https://1.bp.blogspot.com/-tFTzeEj9VK8/XHlhJJYnCnI/AAAAAAAAK7c/vIIP_1-L2M4kdItgoCZ3rF44AJG8qYKywCLcBGAs/s1600/sdr%2Bcomparison_201902.jpg) (source: [someone's blog](https://bv3ue.blogspot.com/)) 32 | - Nyquist-Shannon theorem: 33 | - NE204 BU's video: https://www.youtube.com/watch?v=sgYkOkrlQ_E 34 | - Another, that goes into many other facets: https://www.youtube.com/watch?v=pWjdWCePgvA 35 | 36 | ------------------------------ 37 | 38 | Other cool stuff: 39 | - [Double Slit Experiment: Veritasium](https://www.youtube.com/watch?v=Iuv6hY6zsd0) 40 | - ["Kid krypto": Public Key encryption](https://classic.csunplugged.org/documents/activities/public-key-encryption/unplugged-18-public_key_encryption_0.pdf): Don't let the name fool you -- this is the best explanation of Public Key Encryption that I've seen. It does require some reading, but it well worth it. Also see [source](https://classic.csunplugged.org/activities/public-key-encryption/) and [an interesting subsequent work](https://files.eric.ed.gov/fulltext/EJ1177112.pdf) 41 | - https://www.rtl-sdr.com/creating-a-low-cost-ground-penetrating-radar-with-two-hackrfs/ 42 | - http://www.diva-portal.org/smash/get/diva2:933202/FULLTEXT01.pdf 43 | - Error checking: 44 | - [Two dimensional parity](https://thecsemonk.com/two-dimensional-parity/) 45 | - [Hamming code (interesting; more advanced)](https://www.youtube.com/watch?v=X8jsijhllIA) 46 | - [gr-bokehgui: Web based display for GNU Radio](https://github.com/gnuradio/gr-bokehgui) 47 | - Air-Gap Covert Channels demos: https://cyber.bgu.ac.il/advanced-cyber/airgap 48 | - Wireless mouse (and keyboard) vulnerability: https://www.mousejack.com/ 49 | - Garage doors: [First link](https://www.rtl-sdr.com/decoding-a-garage-door-opener-with-an-rtl-sdr/); [Second link](https://www.rtl-sdr.com/reverse-engineering-a-30-year-old-wireless-garage-door-opener-with-a-hackrf-and-gnu-radio/) 50 | - Keyfob security: 51 | - This has good pictures: https://www.andrewmohawk.com/2016/02/05/bypassing-rolling-code-systems/ 52 | - [RTL-SDR blog regarding Hondas](https://www.rtl-sdr.com/?s=honda) 53 | - ["How Do Key Fobs Work?"](https://www.theifod.com/how-do-key-fobs-work/) 54 | - Cool video: lightning, fire, etc: https://www.youtube.com/watch?v=Q3oItpVa9fs 55 | - Veritasium's video: [The Remarkable Story Behind The Most Important Algorithm Of All Time](https://www.youtube.com/watch?v=nmgFG7PUHfo) (i.e., the Fast Fourier Transform or FFT) 56 | - Vinyl LP Record - Electron microscope slow-motion video - https://www.youtube.com/watch?v=GuCdsyCWmt8 57 | - When GNU Radio Companion is tuned to a radio station, graph ("sink") shows similiarity to the groove on the record? 58 | 59 | Videos from Veritasium explaining analog and digital: 60 | 61 | - https://www.youtube.com/watch?v=IgF3OX8nT0w 62 | - https://www.youtube.com/watch?v=GVsUOuSjvcg 63 | 64 | ----------------------------- 65 | 66 | Not as cool, but worth saving for other reasons: 67 | - [Behind the Veil: A Peek at GNU Radio’s Buffer Architecture](https://www.gnuradio.org/blog/2017-01-05-buffers/) -- This explains why the size of a chunk-of-data-provided-to-a-block varies. 68 | - https://www.rtl-sdr.com/lessons-learned-using-sdr-in-the-classroom/ 69 | -------------------------------------------------------------------------------- /resources/Using-amplifier.md: -------------------------------------------------------------------------------- 1 | - As per the [Hack RF FAQ](https://hackrf.readthedocs.io/en/latest/faq.html): "If you connect an external amplifier, you should also use an external bandpass filter for your operating frequency." 2 | - Here's an [example](http://www.learningaboutelectronics.com/Articles/Passive-bandpass-filter-circuit.php) of how to build an inexpensive band pass filter. THIS MAY NOT BE SUITABLE FOR ALL PURPOSES. PLEASE TEST YOUR SIGNAL USING AN OSCILLOSCOPE BEFORE AMPLIFYING IT! -------------------------------------------------------------------------------- /resources/assets/WBFM_Rx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/resources/assets/WBFM_Rx.png -------------------------------------------------------------------------------- /resources/assets/hw_bb_filt_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/resources/assets/hw_bb_filt_illustration.png -------------------------------------------------------------------------------- /resources/assets/specanhelp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/resources/assets/specanhelp1.png -------------------------------------------------------------------------------- /resources/assets/specansim1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/resources/assets/specansim1.png -------------------------------------------------------------------------------- /resources/assets/specansim2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/resources/assets/specansim2.png -------------------------------------------------------------------------------- /resources/ghz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-can-define-radio/sdr-course/b930da8bc43c448451b07780d16c44522f4fba77/resources/ghz.jpg -------------------------------------------------------------------------------- /resources/sdrsetup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## Assumptions: 3 | ## - The file "sdr_proot_env.tar" is in "/run/user/*/gvfs/*student*/sdr_resources/sdr_angel_tar/". 4 | ## - The file "sdr_proot_env.tar" contains one directory, 5 | ## which is named ".sdr_proot_env" (Note the preceeding dot). 6 | ## - The directory ".sdr_proot_env" contains... 7 | ## - a script, "run_sdr_angel.sh" 8 | ## - anything else needed. 9 | ## Other notes: 10 | ## - Currently (2024 Oct), the `.sdr_proot_env" contains: 11 | ## - the aforementioned "run_sdr_angel.sh", with these contents: 12 | ## #!/bin/bash 13 | ## ~/.sdr_proot_env/proot -R ~/.sdr_proot_env/rootfs /usr/bin/sdrangel 14 | ## - the `proot` executable, which is referenced in "run_sdr_angel.sh" 15 | ## - a rootfs folder which... 16 | ## - was obtained by extracting a rootfs.tar.xz from here: 17 | ## https://images.linuxcontainers.org/images/ubuntu/jammy/amd64/default/ 18 | ## - has sdrangel installed inside of it from the .deb file on the sdrangel release page: 19 | ## https://github.com/f4exb/sdrangel/releases 20 | ## Further reading: 21 | ## - Some details about how to work with proot are listed here: 22 | ## https://github.com/python-can-define-radio/more-sdr/blob/main/2024-07-11/proot-directions.md 23 | 24 | cd /run/user/*/gvfs/*student*/ || { echo "Samba appears to not be connected."; exit; } 25 | 26 | samba_root=$(pwd) 27 | source_file_sdr_angel_tar="$samba_root/sdr_resources/sdr_angel_tar" 28 | destination_dir_sdr_angel_tar="$HOME/Desktop/sdr_angel_tar" 29 | 30 | echo "Copying the SDR Angel tar folder." 31 | echo "It is not locked up or stuck. Be patient; the sdr_angel_tar folder is large and may take a minute to download." 32 | cp -r "$source_file_sdr_angel_tar" "$destination_dir_sdr_angel_tar" 33 | 34 | if [ $? -eq 0 ]; then 35 | echo -e "\e[32m- Successfully copied sdr angel tar folder to the Desktop.\e[35m" 36 | else 37 | echo "Move failed." 38 | fi 39 | 40 | echo "Extracting tar file (also a slow step)." 41 | tar -xf "$destination_dir_sdr_angel_tar/sdr_proot_env.tar" --directory=$HOME 42 | rm -r "$destination_dir_sdr_angel_tar" 43 | chmod +x ~/.sdr_proot_env/run_sdr_angel.sh 44 | mkdir -p ~/.local/bin/ 45 | ln -s ~/.sdr_proot_env/run_sdr_angel.sh ~/.local/bin/sdrangel 46 | source ~/.profile 47 | echo "You should now be able to run the command 'sdrangel' from the terminal. You may need to log out and log in." 48 | -------------------------------------------------------------------------------- /resources/templates/digital_jammer_template.grc: -------------------------------------------------------------------------------- 1 | options: 2 | parameters: 3 | author: template 4 | category: '[GRC Hier Blocks]' 5 | cmake_opt: '' 6 | comment: '' 7 | copyright: '' 8 | description: '' 9 | gen_cmake: 'On' 10 | gen_linking: dynamic 11 | generate_options: qt_gui 12 | hier_block_src_path: '.:' 13 | id: digital_jammer_template 14 | max_nouts: '0' 15 | output_language: python 16 | placement: (0,0) 17 | qt_qss_theme: '' 18 | realtime_scheduling: '' 19 | run: 'True' 20 | run_command: '{python} -u {filename}' 21 | run_options: prompt 22 | sizing_mode: fixed 23 | thread_safe_setters: '' 24 | title: Not titled yet 25 | window_size: '' 26 | states: 27 | bus_sink: false 28 | bus_source: false 29 | bus_structure: null 30 | coordinate: [8, 8] 31 | rotation: 0 32 | state: enabled 33 | 34 | blocks: 35 | - name: center_freq_slider 36 | id: variable_qtgui_range 37 | parameters: 38 | comment: '' 39 | gui_hint: '' 40 | label: '' 41 | min_len: '200' 42 | orient: Qt.Horizontal 43 | rangeType: float 44 | start: '0' 45 | step: '1' 46 | stop: '100' 47 | value: '50' 48 | widget: counter_slider 49 | states: 50 | bus_sink: false 51 | bus_source: false 52 | bus_structure: null 53 | coordinate: [277, 14] 54 | rotation: 0 55 | state: true 56 | - name: samp_rate 57 | id: variable 58 | parameters: 59 | comment: '' 60 | value: '32000' 61 | states: 62 | bus_sink: false 63 | bus_source: false 64 | bus_structure: null 65 | coordinate: [184, 12] 66 | rotation: 0 67 | state: enabled 68 | connections: [] 69 | 70 | metadata: 71 | file_format: 1 72 | -------------------------------------------------------------------------------- /resources/templates/noise_jammer_template.grc: -------------------------------------------------------------------------------- 1 | options: 2 | parameters: 3 | author: template 4 | category: '[GRC Hier Blocks]' 5 | cmake_opt: '' 6 | comment: '' 7 | copyright: '' 8 | description: '' 9 | gen_cmake: 'On' 10 | gen_linking: dynamic 11 | generate_options: qt_gui 12 | hier_block_src_path: '.:' 13 | id: noise_jammer_template 14 | max_nouts: '0' 15 | output_language: python 16 | placement: (0,0) 17 | qt_qss_theme: '' 18 | realtime_scheduling: '' 19 | run: 'True' 20 | run_command: '{python} -u {filename}' 21 | run_options: prompt 22 | sizing_mode: fixed 23 | thread_safe_setters: '' 24 | title: Not titled yet 25 | window_size: '' 26 | states: 27 | bus_sink: false 28 | bus_source: false 29 | bus_structure: null 30 | coordinate: [8, 8] 31 | rotation: 0 32 | state: enabled 33 | 34 | blocks: 35 | - name: center_freq_slider 36 | id: variable_qtgui_range 37 | parameters: 38 | comment: '' 39 | gui_hint: '' 40 | label: '' 41 | min_len: '200' 42 | orient: Qt.Horizontal 43 | rangeType: float 44 | start: '0' 45 | step: '1' 46 | stop: '100' 47 | value: '50' 48 | widget: counter_slider 49 | states: 50 | bus_sink: false 51 | bus_source: false 52 | bus_structure: null 53 | coordinate: [524, 14] 54 | rotation: 0 55 | state: true 56 | - name: cut_freq_slider 57 | id: variable_qtgui_range 58 | parameters: 59 | comment: '' 60 | gui_hint: '' 61 | label: '' 62 | min_len: '200' 63 | orient: Qt.Horizontal 64 | rangeType: float 65 | start: '0' 66 | step: '1' 67 | stop: '100' 68 | value: '50' 69 | widget: counter_slider 70 | states: 71 | bus_sink: false 72 | bus_source: false 73 | bus_structure: null 74 | coordinate: [274, 14] 75 | rotation: 0 76 | state: true 77 | - name: samp_rate 78 | id: variable 79 | parameters: 80 | comment: '' 81 | value: '32000' 82 | states: 83 | bus_sink: false 84 | bus_source: false 85 | bus_structure: null 86 | coordinate: [184, 12] 87 | rotation: 0 88 | state: enabled 89 | - name: tr_width_slider 90 | id: variable_qtgui_range 91 | parameters: 92 | comment: '' 93 | gui_hint: '' 94 | label: '' 95 | min_len: '200' 96 | orient: Qt.Horizontal 97 | rangeType: float 98 | start: '0' 99 | step: '1' 100 | stop: '100' 101 | value: '50' 102 | widget: counter_slider 103 | states: 104 | bus_sink: false 105 | bus_source: false 106 | bus_structure: null 107 | coordinate: [398, 14] 108 | rotation: 0 109 | state: true 110 | connections: [] 111 | 112 | metadata: 113 | file_format: 1 114 | -------------------------------------------------------------------------------- /resources/templates/specanny_template.grc: -------------------------------------------------------------------------------- 1 | options: 2 | parameters: 3 | author: template 4 | category: '[GRC Hier Blocks]' 5 | cmake_opt: '' 6 | comment: '' 7 | copyright: '' 8 | description: '' 9 | gen_cmake: 'On' 10 | gen_linking: dynamic 11 | generate_options: qt_gui 12 | hier_block_src_path: '.:' 13 | id: specanny_template 14 | max_nouts: '0' 15 | output_language: python 16 | placement: (0,0) 17 | qt_qss_theme: '' 18 | realtime_scheduling: '' 19 | run: 'True' 20 | run_command: '{python} -u {filename}' 21 | run_options: prompt 22 | sizing_mode: fixed 23 | thread_safe_setters: '' 24 | title: Not titled yet 25 | window_size: '' 26 | states: 27 | bus_sink: false 28 | bus_source: false 29 | bus_structure: null 30 | coordinate: [8, 8] 31 | rotation: 0 32 | state: enabled 33 | 34 | blocks: 35 | - name: center_freq_slider 36 | id: variable_qtgui_range 37 | parameters: 38 | comment: '' 39 | gui_hint: '' 40 | label: '' 41 | min_len: '200' 42 | orient: Qt.Horizontal 43 | rangeType: float 44 | start: '0' 45 | step: '1' 46 | stop: '100' 47 | value: '50' 48 | widget: counter_slider 49 | states: 50 | bus_sink: false 51 | bus_source: false 52 | bus_structure: null 53 | coordinate: [435, 13] 54 | rotation: 0 55 | state: true 56 | - name: if_gain_slider 57 | id: variable_qtgui_range 58 | parameters: 59 | comment: '' 60 | gui_hint: '' 61 | label: '' 62 | min_len: '200' 63 | orient: Qt.Horizontal 64 | rangeType: float 65 | start: '0' 66 | step: '1' 67 | stop: '100' 68 | value: '50' 69 | widget: counter_slider 70 | states: 71 | bus_sink: false 72 | bus_source: false 73 | bus_structure: null 74 | coordinate: [289, 9] 75 | rotation: 0 76 | state: true 77 | - name: samp_rate 78 | id: variable 79 | parameters: 80 | comment: '' 81 | value: '32000' 82 | states: 83 | bus_sink: false 84 | bus_source: false 85 | bus_structure: null 86 | coordinate: [184, 12] 87 | rotation: 0 88 | state: enabled 89 | connections: [] 90 | 91 | metadata: 92 | file_format: 1 93 | -------------------------------------------------------------------------------- /resources/toc/3day.md: -------------------------------------------------------------------------------- 1 | # Software Defined Radio (SDR) Course 2 | 3 | ## Table of Contents (TOC) 4 | 5 | ### Day 1 6 | 7 |
👨🏽‍🏫
8 | 9 | - ~[What is an SDR][What_is_an_SDR] 10 | - ~[GQRX][GQRX] 11 | - +Demonstrate Aliasing 12 | - +Outdoor activity 13 | - ~Introduce GNU Radio Companion 14 | - ~Saving the file first 15 | - ~Pitfalls: 16 | - ~Don't use the "Apply" button 17 | - ~Searching for program blocks: deselect first, or Ctrl+F always 18 | - 🔬 [Spectrum Analyzer][GRC_Spectrum_Analyzer] 19 | - 🔬 [FM Receiver][GRC_FM_Receiver] 20 | - 🔬 [FM Transmitter][GRC_FM_Transmitter] 21 | - 🔬 [Noise Jammer][Noise_Jammer] 22 | 23 | ### Day 2 24 | 25 | - ~Discussion of flowgraphs from Day 1 26 | - +[Digital Jammer][Digital_Jammer] 27 | - **Intro to Python** 28 | - ~Exercises #1-#20, #28b, #28c, #28d in [ex_2a_print_and_inputs][ex_2a_print_and_inputs] 29 | - +Exercises #11-#14 in [ex_3a_if_else][ex_3a_if_else] 30 | - ~Exercise #5 in [ex_6a_for_loops][ex_6a_for_loops] 31 | - +Exercise #28 in [ex_6b_while_loops][ex_6b_while_loops] 32 | 33 | ### Day 3 34 | 35 | - **Sample rates** 36 | - ~[Sample Rates: Turtle Ripples][Sample_Rates_turtle_ripples] 37 | - +Nyquist Theorem / Criterion: 38 | 1. +We'll start with [a lesson from Harvey Mudd][Harvey_Mudd_Sampling]. 39 | 2. +We'll look at the images on [this page from allaboutcircuits.com][all_about_circuits_sampling]. 40 | 3. +Watch & discuss [Helicoptor blade video][Helicoptor_aliasing] 41 | - ~Meaning of FFT using the [GRC showing the sum of two pure sine waves][AnalyzeFreq_of_Combined_Signals]1. 42 | 43 | 44 | - **On-Off Keying Data Transmission using Python** 45 | - +OOK Class Activity using light and sound signals 46 | - +[OOK Intro Using Python: 010_pcdr_ook_tx_intro.md][010_pcdr_ook_tx_intro] 47 | - +Instructor Receive & Demodulate student msg on URH (Universal Radio Hacker) — Demonstration 48 | - 🔬 OOK tx intro exercises 49 | 50 | - **Automated Signal Strength Scanning using Python** 51 | - ~[PCDR Simple receiver][pcdr_simple] 52 | - 🔬 Simple receiver exercises 53 | 54 | ----- 55 | 56 | ### If time allows... 57 | 58 | - [URH exercises](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch03_Analyzing_Signals_URH/010_Install_URH.md) 59 | 60 | #### Footnotes 61 | 62 | 1: For those who would like to dig into the math and history of the FFT, see [this video from Veritasium][veritasium_fft_video] 63 | 64 | 65 | 66 | 67 | 68 | [ex_2a_print_and_inputs]: https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch01_Basics/ex_2a_print_and_inputs.md 69 | [ex_3a_if_else]: https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch01_Basics/ex_3a_if_else.md 70 | [ex_6a_for_loops]: https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch01_Basics/ex_6a_for_loops.md 71 | [ex_6b_while_loops]: https://github.com/python-can-define-radio/python-course/blob/main/classroom_activities/Ch01_Basics/ex_6b_while_loops.md 72 | 73 | 74 | [What_is_an_SDR]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/060_What_is_an_SDR.md 75 | [GQRX]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/050_gqrx_FM_Receive.md 76 | [Beginnings]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/010_Beginnings.md 77 | [GRC_Spectrum_Analyzer]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/020_GRC_Spectrum_Analyzer.md 78 | [GRC_FM_Receiver]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/030_GRC_FM_Receiver.md 79 | [GRC_FM_Transmitter]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch01_Diving_in_Headfirst/040_GRC_FM_Transmitter.md 80 | [Noise_Jammer]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch02_Applications/020_Noise_Jammer.md 81 | [Digital_Jammer]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch02_Applications/021_Digital_Jammer.md 82 | [Sample_Rates_turtle_ripples]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch02_Basics/022_Sample_Rates_turtle_ripples.md 83 | [AnalyzeFreq_of_Combined_Signals]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch02_Basics/030_AnalyzeFreq_of_Combined_Signals.md 84 | [010_pcdr_ook_tx_intro]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch04_Analyzing_Signals_Python/010_pcdr_ook_tx_intro.md 85 | [pcdr_simple]: https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch04_Analyzing_Signals_Python/050_pcdr_simple.md 86 | 87 | 88 | [Harvey_Mudd_Sampling]: https://gallicchio.github.io/learnSDR/lesson06.html 89 | [all_about_circuits_sampling]: https://www.allaboutcircuits.com/technical-articles/nyquist-shannon-theorem-understanding-sampled-systems/ 90 | [Helicoptor_aliasing]: https://www.youtube.com/watch?v=yr3ngmRuGUc 91 | [veritasium_fft_video]: https://www.youtube.com/watch?v=nmgFG7PUHfo 92 | -------------------------------------------------------------------------------- /resources/toc/5day.md: -------------------------------------------------------------------------------- 1 | # Software Defined Radio (SDR) Course 2 | 3 | ## Table of Contents (TOC) 4 | 5 | ### Day 1 6 | 7 | - Overview slides 8 | - Distribute equipment 9 | - Terminal tutorial 10 | - Set up Thonny and VS Code on laptops 11 | - Verify GQRX and GRC can start on laptops 12 | - Python print and inputs exercises: #1-#20, #28b, #28c, #28d 13 | 14 | ### Day 2 15 | 16 | - Demo: Turning on a HackRF transmitter using pcdr module (and GQRX to receive) 17 | - Do: (same) (show GQRX on board) 18 | - GQRX FM Receive 19 | - SDR Angel 20 | - Discussion: Underworkings of Python, libraries 21 | - What happens when an "import" is done 22 | - the logics of different Python "things" 23 | - take turns transmitting / receiving to each other on the Broadcast FM band. 24 | - Check out waveforms if possible, WiFi, Bluetooth, GSM? CDMA? 25 | 26 | ### Day 3 27 | - Python if-else exercises 6 through 16c 28 | - For students who are ahead, do the for-loops exercises 29 | - "cool": Gpredict 30 | - GQRX Keyfob demo. Talk about non-standardized dB units (it's not dBm) 31 | - Python Keyfob 32 | - Python pcdr basic Rx strength: reiterate non-standardized unit 33 | - Python while loop to repeatedly read 34 | - Python while loop exercises #1 - #9 35 | - Optional Homework: Finish if-else exercises 36 | 37 | ### Day 4 38 | 39 | - Incorporate PCDR module 40 | - [Single frequency transmitter](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch04_Analyzing_Signals_Python/005_pcdr_single_freq_transmit.md): work through together 41 | - [Single frequency receiver](https://github.com/python-can-define-radio/sdr-course/blob/main/classroom_activities/Ch04_Analyzing_Signals_Python/050_pcdr_simple_receiver.md): 42 | - Demo first exercise. GQRX open to confirm functionality. 43 | - Have students work independently up to exercise #9. 44 | - Incorporate playsound 45 | - Some students may hit the installation requirement challenge 46 | - Incorporate file writing/naming: 47 | - .csv file 48 | - writing to a file 49 | - When/why we need to flush to a file 50 | - Incorporate time functions (nowtime/datetime) 51 | - _Lunch_ 52 | - FM Transmitter using Python/PCDR 53 | - FM Receiver using Python/PCDR 54 | - Homework: 55 | - Successfully write an activity detector that... 56 | - Takes continuous measurements of the strength of a specified frequency 57 | - Records data to a csv file with ability to view immediately (i.e., flush to file) 58 | - Plays three different sounds depending on RSSI (low|med|high strength measured) 59 | 60 | ### Day 5 61 | 62 | - Tone-varying DF tool built in Python; go outside 63 | - _Lunch_ 64 | - URH: 65 | - Record and Replay 66 | - Modulation: OOK, ASK, FSK (as time allows) 67 | -------- 68 | 69 | ### Weather-dependent 70 | 71 | DF with Yagi-uda 72 | 73 | ### Other 74 | 75 | - GRC: Noise jammer 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /resources/toc/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains Tables of Contents. 2 | -------------------------------------------------------------------------------- /resources/unit-conversion-review.md: -------------------------------------------------------------------------------- 1 | # Unit conversions 2 | 3 | Many of us have heard frequency units mentioned, for example, when buying a router that has 2.4 GHz or 5 GHz capability. This lesson discusses how to convert between different frequency units, and how to express those using scientific notation (e.g., `2.4e9` = 2.4 GHz). 4 | 5 | ### Reference Table 6 | 7 | | | 1 Hz | 1 kHz | 1 MHz | 1 GHz | 8 | |--------------------|------|----------|--------------|------------------| 9 | | Converted to Hz | 1 Hz | 1,000 Hz | 1,000,000 Hz | 1,000,000,000 Hz | 10 | | Converted to GHz | 0.000 000 001 GHz | 0.000 001 GHz | 0.001 GHz | 1 GHz | 11 | | Scientific notation| 1 Hz | 1e3 Hz | 1e6 Hz | 1e9 Hz | 12 | 13 | ![Image showing MHz, kHz, and Hz place values](https://github.com/python-can-define-radio/sdr-course/blob/main/resources/ghz.jpg?raw=true) 14 | 15 | _Image source: https://www.trendytechbuzz.com/2016/11/what-is-gigahertz-ghz-or-megahertz-mhz.html_ 16 | 17 | ### Reviewing the basics 18 | 19 | Most of us learned fairly early in school how to say numbers like these: 20 | 21 | - 23,000 = twenty three thousand 22 | - 6,200,000 = six point two million 23 | 24 | We may also have experience saying these numbers using metric prefixes: 25 | 26 | - 23,000 Hz = twenty three thousand Hertz = twenty three kilohertz 27 | - 6,200,000 = six point two million Hertz = six point two Megahertz 28 | 29 | The challenge often arises when we need to convert between, for example, Gigahertz and Megahertz. For example, how would you express 2 GHz in Megahertz? We are going to try to use an intuitive approach. 30 | 31 | _Answers appear at the end of the page for checking your work._ 32 | 33 | 34 | ``` 35 | ## Exercise 1 36 | ## Given: 1 GHz = 1,000 MHz 37 | ## Determine the following: 38 | ## a. 2 GHz = ____ MHz 39 | ## b. 2.3 GHz = ____ MHz 40 | ## c. 52 GHz = ____ MHz 41 | ## d. 0.3 GHz = ____ MHz 42 | ## e. 0.04 GHz = ____ MHz 43 | ## f. ____ GHz = 7000 MHz 44 | ## g. ____ GHz = 2500 MHz 45 | ## h. ____ GHz = 734 MHz 46 | ## i. ____ GHz = 40 MHz 47 | ## j. 23.73 GHz = ____ MHz 48 | ``` 49 | 50 | ``` 51 | Exercise 2 52 | ## Given: 1 MHz = 1,000,000 Hz 53 | ## Given: 1 kHz = 1,000 Hz 54 | ## Determine the following: 55 | ## a. 2 MHz = ____ Hz = ____ kHz 56 | ## b. 1 MHz = ____ kHz 57 | ## c. 7.3 MHz = ____ kHz 58 | ## d. 0.6 MHz = ____ kHz 59 | ## e. 23.6 MHz = ____ kHz 60 | ## f. ____ MHz = 2345 kHz 61 | ## g. ____ MHz = 600 kHz 62 | ## h. ____ MHz = 106200 kHz 63 | ``` 64 | 65 | ``` 66 | ## Exercise 3 67 | ## Given: 1 GHz = 1,000,000,000 Hz 68 | ## Given: 1 MHz = 1,000,000 Hz 69 | ## Given: 1 kHz = 1,000 Hz 70 | ## Determine the following: 71 | ## a. 3 GHz = ___ Hz 72 | ## b. 10 MHz = ___ Hz 73 | ## c. 3,260,000 Hz = ___ MHz 74 | ## d. 342 MHz = ___ GHz 75 | ``` 76 | 77 | ``` 78 | ## Exercise 4 79 | ## The channels for a particular radio frequency band range from 88.1 MHz to 107.9 MHz with a spacing of 200 kHz. 80 | ## a. The first channel spans from 88.1 MHz to ___ MHz. 81 | ## b. The center of the first channel is ____ MHz. 82 | ## c. The second channel spans from _____ to ____ MHz. 83 | ## d. The center of the second channel is ____ MHz. 84 | ## e. The center of the third, fourth, fifth, and sixth channels are ____, ____, ____, ____. 85 | ## f. (optional, may require Internet research) In the United States, these are channels that are licensed for ____. 86 | ``` 87 | 88 | 89 | ```python3 90 | ## Exercise 5 91 | ## In python, evaulate the following: 92 | print(7e3) 93 | ## It should display 7000.0, that is, seven thousand. 94 | ## Convert the following from scientific notation to standard notation: 95 | ## a. 26e3 Hz = ____ 96 | ## b. 2.7e3 Hz = ____ 97 | ## c. 7e6 Hz = ____ 98 | ## d. 98e6 Hz = ____ 99 | ``` 100 | 101 | ``` 102 | ## Exercise 6 103 | ## a. List the numbers from 10 to 20 with a step size of 2, including both endpoints. 104 | ## b. Same exercise, but from 3000 to 7000 with a step size of 1000. 105 | ## Now try doing it with scientific notation. (e.g. 3e3 is our starting point) 106 | ## c. Same exercise, but start=2e3, stop=6e3, step=1e3. 107 | ## d. Same exercise, but start=8e6, stop=9e6, step=200e3. 108 | ## e. Same exercise, but start=2.4e9, stop=2.6e9, step=500e6. 109 | ``` 110 | 111 |
Exercise 1 Answers: 112 | 113 | ``` 114 | a. 2,000 MHz 115 | b. 2,300 MHz 116 | c. 52,000 MHz 117 | d. 300 MHz 118 | e. 40 MHz 119 | f. 7 GHz 120 | g. 2.5 GHz 121 | h. 0.734 GHz 122 | i. 0.04 GHz 123 | j. 23,730 MHz 124 | ``` 125 | 126 |
127 | 128 |
Exercise 2 Answers: 129 | 130 | ``` 131 | a. 2,000,000 Hz = 2,000 kHz 132 | b. 1,000 kHz 133 | c. 7,300 kHz 134 | d. 600 kHz 135 | e. 23,600 kHz 136 | f. 2.345 MHz 137 | g. 0.6 MHz 138 | h. 106.2 MHz 139 | ``` 140 | 141 |
142 | 143 |
Exercise 3 Answers: 144 | 145 | ``` 146 | a. 3,000,000,000 Hz 147 | b. 10,000,000 Hz 148 | c. 3.26 MHz 149 | d. 0.342 GHz 150 | ``` 151 | 152 |
153 | 154 |
Exercise 4 Answers: 155 | 156 | ``` 157 | a. 88.2 MHz 158 | b. 88.1 MHz 159 | c. 88.2 to 88.4 MHz 160 | d. 88.3 MHz 161 | e. 88.5, 88.7, 88.9, 90.1 162 | f. FM Radio 163 | ``` 164 | 165 |
166 | 167 |
Exercise 5 Answers: 168 | 169 | ``` 170 | a. 26,000 Hz 171 | b. 2,700 Hz 172 | c. 7,000,000 Hz 173 | d. 98,000,000 Hz 174 | ``` 175 | 176 |
177 | 178 |
Exercise 6 Answers: 179 | 180 | ``` 181 | a. 10, 12, 14, 16, 18, 20 182 | b. 3000, 4000, 5000, 6000, 7000 or 183 | 3e3, 4e3, 5e3, 6e3, 7e3 184 | c. 2e3, 3e3, 4e3, 5e3, 6e3 185 | d. 8e6, 8.2e6, 8.4e6, 8.6e6, 8.8e6, 9.0e6 186 | e. 2.4e9, 2.45e9, 2.5e9, 2.55e9, 2.6e9 187 | ``` 188 | 189 |
190 | --------------------------------------------------------------------------------