This lesson

    Course

    Course Information

    • Syllabus
    • Materials

    Module 1 — Foundations of Detection & Link Budgets

    • Module 1 overview
    • L1 — Course Introduction
    • L2 — Noise Figure and Link Budget
    • L3 — Signal Space

    Module 2 — SDR & GNU Radio Foundations

    • Module 2 overview
    • L4 — SDR Introduction
    • L5 — GNU Radio Introduction
    • L5 Lab — First Flowgraphs

    Module 3 — Analog Modulation

    • Module 3 overview
    • L6 — AM Simulation
    • L6 Lab — AM Simulation
    • L7 — AM and FM Receivers
    • L7 Lab — AM and FM Receivers

    Module 4 — Digital Modulation

    • Module 4 overview
    • L8 — FSK
    • L8 Lab — FSK
    • L9 — PAM Theory
    • L10 — ASCII over PAM
    • L10 Lab — PAM Link

    Module 5 — Custom Blocks & Midterm

    • Module 5 overview
    • L11 — Out-of-Tree Modules and Custom Blocks
    • L11 Lab — Custom Block
    • Midterm — Fox Hunt and CTF

    Module 6 — Waveform Applications & RF Reverse Engineering

    • Module 6 overview
    • L12 — CW
    • L12 Lab — CW
    • L13 — DTMF
    • L13 Lab — DTMF
    • L14 — HD Radio
    • L14 Lab — HD Radio
    • L15 — mPSK
    • L15 Lab — mPSK
    • L16 — Frequency Hopping
    • L16 Lab — Frequency Hopping
    • L17 — RF Reverse Engineering
    • L17 Lab — RF Reverse Engineering
    • L18 — ADS-B
    • L19 — AIS
    • L18–L19 Lab — ADS-B and AIS

    Module 7 — Final Project

    • Module 7 overview
    • L20 — QAM and OFDM
    • Final Project
    Home ECE 343 ECE 444 ECE 448 Projects

    Getting around

    Everything is in the bar at the bottom of the screen.

    ECE 448
    The other course sites, the home page, and this help.
    read
    Read shows the whole lesson as one page. Present shows one frame at a time, for class. P switches.
    tools
    Laser pointer L, spotlight S, and fullscreen F.
    3/9
    Where you are in the lesson. Tap it for every frame here and every lesson in the course G. Double-tap to go back to the top.

    Presenting

    → or Space next frame, ← back, D this frame’s detail, Esc close a panel.

    Printing

    Print from present mode for the slides, one frame a page. Print from read mode for the whole lesson.

    Press ? to open this again.Open this again from the ECE 448 menu.

    L10 Lab — PAM Link#

    ECE 448 · Spring 2027

    PAM Link

    Build the packet transmitter, watch noise break the text and the matched filter repair it, and send your own line through a PlutoSDR.

    L10 Lab · Module 4: Digital Modulation · Dr. Neil Rogers

    Learning Objectives

    1. I can implement a PAM link that transmits and receives ASCII text end-to-end.

    From Lesson to Bench

    • Pre-Lab: run the link and work out one packet.

    • Bench: find the bytes by hand, build the transmitter, turn up the noise and the clock error, then use a PlutoSDR.

    • Writeup: explain what you saw, in words.

    Lesson 10 sent a line of text over the PAM link of Lesson 9. Today you first line up the bytes by hand, with no packets, to see the problem the access code solves. Then you build the packet transmitter yourself, turn up the noise until the simulated link breaks, switch the matched filter off and on, and then run the same link on a PlutoSDR. The lab packet has three parts, and you turn it in once, complete, before Lesson 11. Part 1, the Pre-Lab, is on your own before the period: run L10-pam-link.grc once and work out the bits and time in one packet and the length field of a line of your own. Part 2 is what you observe in class. Part 3, the Writeup, explains it in words.

    Questions 1 to 6 run on your laptop and transmit nothing. Question 7 transmits, into a cable only, as the PlutoSDR frame below describes.

    Part 1: Before the Period

    • L10-pam-link.grc runs and shows the received line.

    • Work out one packet’s bits and time.

    • Write your own line’s length field and first byte.

    Do the Pre-Lab before you arrive. Question 1 checks that the link flowgraph opens and runs on your laptop: if GRC reports an error, the L5 lesson page lists the install checks, and EI before the lab is the place to fix it.

    Question 2 is the packet arithmetic from the lesson: the bits and time in one packet of the default line and how many packets 30 s holds, and the length field and first character of a line of your own choosing. You will see both on the time sink of the transmitter you build in Part 2.

    Pre-Lab Check

    Check

    Expected

    One packet

    344 bits, 86 ms

    Packets in 30 s

    about 349

    Length field for 31 bytes

    0000000000011111

    These follow from Lesson 10. The packet size is exact: 64 bits of access code, 32 of header, and 8 per byte of the line. The bench is where you find out how much noise the link takes, by turning the noise up and watching.

    Part 2: Bytes Without Packets

    1. Open and run L10-pam-raw.grc. It is the L10 link with no access code and no header: the transmitter sends the line’s bytes back to back, and the receiver groups the bits into bytes from whichever bit it starts listening on. Each run starts at a random bit.

    2. Read the Received bytes label. A byte that is not a printable ASCII character shows as a dot, and a newline as a bar.

    3. Step the Bit offset from 0 up, one at a time, waiting about 2 s at each, until the text reads. Record that offset for Question 3(a).

    4. Stop the flowgraph and run it again. Find the offset again, and record it. Repeat for a third run.

    5. Now open and run L10-pam-link.grc, the version with packets. Stop and start it three times, and record for Question 3(b) how long the first line takes to appear each time and whether you had to adjust anything.

    The Access Code Lines Up the Bytes

    The L10 raw-bits flowgraph running twice: at a bit offset of 0 the Received bytes label shows dots and stray symbols; at a bit offset of 3 it reads ver PAM., a bar for the newline, then USAFA ECE 448: ASCII over PAM.

    • Without packets, one offset in eight reads, and it changes every run.

    • The access-code search finds the right offset by itself.

    The figure shows one run of L10-pam-raw.grc. At an offset of 0 the receiver groups the bits one to seven bits away from the true byte boundary, and almost nothing it prints is a character. At an offset of 3 the same bits read as the line of text. The right offset depends on the bit at which the receiver started listening, so it changes from run to run, and a person has to find it again each time. The receiver in the 2025 reading, Mathys (2016), Motivating Undergraduate Communication Theory Using GNU Radio, used this kind of hand-set delay.

    The access code removes that step. Correlate Access Code - Tag Stream compares every 64-bit window of the received bits with the code, so it finds the first bit of each packet wherever the receiver started, and Repack Bits starts each byte there. That is why L10-pam-link.grc shows text within about a second every time it starts.

    Part 2: Build the Transmitter

    1. Start GRC and choose File, New, QT GUI. Do not open L10-pam-tx.grc; build it yourself.

    2. Set samp_rate to 32000. Add Variables sps = 8, alpha = 0.35, msg = your line from Question 2(b) in quotes, hdr = digital.header_format_default(digital.packet_utils.default_access_code, 0), and tx_taps = firdes.root_raised_cosine(sps, samp_rate, samp_rate/sps, alpha, 11*sps).

    3. Add a Vector Source: type byte, vector list(msg.encode()) + [10], repeat Yes. Then Stream to Tagged Stream: type byte, packet length len(msg) + 1, length tag key "packet_len".

    4. Add a Protocol Formatter with format object hdr and length tag name "packet_len", and a Tagged Stream Mux with 2 inputs and the same tag name. Connect Stream to Tagged Stream to the formatter and to the mux’s second input, and the formatter to the mux’s first input.

    5. Add Unpack K Bits with K = 8, then Chunks to Symbols: input byte, output float, symbol table [-1, 1].

    6. Add a Throttle (float, sample rate samp_rate/sps) and an Interpolating FIR Filter (float in and out, interpolation sps, taps tx_taps).

    7. Add a QT GUI Time Sink on the Throttle’s output: type float, 112 points, sample rate samp_rate/sps, trigger mode Tag, trigger tag key packet_len. Add a QT GUI Frequency Sink (float, bandwidth samp_rate) on the filter’s output. Every arrow must be black.

    8. Run it. Screenshot the canvas for Question 4(a) and the time sink for Question 4(b), and read the spectrum’s edges for Question 4(c).

    Part 2: Noise, Matched Filter, and Clock Error

    The whole L10 link flowgraph in GNU Radio Companion: the variables, sliders, and matched-filter chooser at the top, the packet transmitter from Vector Source to Chunks to Symbols, then Throttle, the RRC filter, the Fractional Resampler and Noise Source of the channel, and the receiver from the matched filter through Symbol Sync, Binary Slicer, Correlate Access Code - Tag Stream, and Repack Bits to the Text Sink

    1. Open and run L10-pam-link.grc. The Lines label counts the lines received since a slider or the switch last moved.

    2. Note the eye and the Symbol Sync output at the default noise, 0.3. Then, with the clock error at 500 ppm and the matched filter on, set the noise amplitude to 0.5 and raise it by 0.1 at a time. At each step, wait about 20 s and watch the Lines label, the eye, and the Symbol Sync output. Record for Question 5(a) the setting where lines start to break and the setting where no line arrives intact, and describe the displays for Question 5(b).

    3. Near 1.1, wait for a line with a wrong character in the Received label and screenshot it for Question 5(c).

    4. Set the noise to 0.5. Switch the matched filter off, wait 20 s, and record the Lines label and the eye for Question 5(d). Switch it back on and record them again.

    5. Set the noise back to 0.3. Set the clock error to 0, then raise it to 10,000, 20,000, 30,000, and 40,000 ppm in that order, without stopping the flowgraph. At each, wait about 20 s and record the Lines label and the eye’s appearance for Question 6(a). Screenshot 10,000 ppm for Question 6(b).

    6. Stop the flowgraph, run it again so the loop locks at the default 500 ppm, and then type 40,000 into the clock-error box in one jump. Record the Lines label after 20 s in the last column of Question 6(a).

    If No Text Arrives

    If the Received label stays at “(waiting for a packet)”

    Close the flowgraph and run it again with the sliders at their defaults, a noise amplitude of 0.3 and a clock error of 500 ppm, where every line should arrive within about a second. In your own transmitter, check that the Stream to Tagged Stream packet length is len(msg) + 1, so that it counts the newline, and that all three tag names are "packet_len", with the quotes. If you changed the message, keep it on one line.

    Part 2: The Link on a PlutoSDR

    The L10 PlutoSDR flowgraph in GNU Radio Companion: the packet transmitter and RRC filter feed Float To Complex, a Multiply Const of 0.5, a Rational Resampler up by 32, and a PlutoSDR Sink at 915 MHz; a PlutoSDR Source feeds a Rational Resampler down by 32, an AGC, the matched filter, Symbol Sync, a Costas Loop, Complex To Real, a polarity Multiply Const, the Binary Slicer, the access-code search, Repack Bits, and the Text Sink, with a frequency sink and a constellation sink

    • We cable one Pluto’s TX port to its RX port through a 30 dB attenuator, at 915 MHz.

    • The cable goes on before the flowgraph runs, and we never connect an antenna.

    L10-pam-pluto.grc is the Lesson 10 link with the simulated channel replaced by a PlutoSDR. The transmit half raises the 32 kHz baseband stream to 1.024 MS/s and sends it from the Pluto’s TX port at 915 MHz; the receive half takes it from the RX port, lowers it to 32 kHz, and runs the same matched filter, Symbol Sync, and access-code search. An AGC sets the received level, and a Costas Loop, which Lesson 15 explains, locks to the carrier’s phase.

    In class we transmit only into a cable, into a shielded enclosure, or at very low power in an ISM band, and only when the lesson says to. This question transmits only into the cable between the Pluto’s own TX and RX ports, through an inline 30 dB SMA attenuator. The TX attenuation slider opens at 60 dB and cannot go below 30 dB. Never set TX attenuation below 30 dB or change the slider’s range; the RX input is rated to only a few dBm, and the transmitter can put out more than that.

    PlutoSDR Steps

    1. With the Pluto unplugged, connect its TX port to its RX port with a short SMA cable and an inline 30 dB SMA attenuator. Do not connect an antenna, and do not connect the cable without the attenuator.

    2. Plug the Pluto into USB and run iio_info -s at the radioconda prompt; it should list a PlutoSDR context. The flowgraph uses the Pluto’s default address, ip:192.168.2.1.

    3. Open and run L10-pam-pluto.grc. In simulation, the constellation shows two clusters on the horizontal axis within a few seconds, one on each side of zero. Never set TX attenuation below 30 dB or change the slider’s range.

    4. If no line arrives within 5 s, switch Polarity. If neither setting works, raise the TX attenuation by 10 dB and try both again. Screenshot the window once lines arrive, for Question 7(a).

    5. Stop and start the flowgraph three times, and record each time which polarity delivers lines, for Question 7(b).

    6. Stop it, put your line from Question 2(b) in msg, and run it again for Question 7(c). Stop the flowgraph and unplug the Pluto before you disconnect the cable.

    Part 3: After the Lab

    • Explain why the text goes from all to none so quickly.

    • Explain what the matched filter did.

    • Compare the clock-error limit with a real crystal, and explain the polarity.

    The Writeup is four questions, answered in words. The first asks why your link went from every line to none over a small change in the noise, and what that means for a real radio. The second asks why switching off the matched filter broke the text. The third compares the clock error your link tolerated with the error between two real crystals, and asks what would happen without Symbol Sync. The fourth asks why the polarity that works on the Pluto changes from run to run, and what change to the receiver would make the switch unnecessary.

    Lab Packet

    • L10 Lab packet (PDF)

    • L10-pam-raw.grc: the link with no packets and a bit-offset slider. No hardware needed.

    • L10-pam-tx.grc and L10-pam-link.grc

    • L10-pam-pluto.grc: the link on one PlutoSDR, transmitting at 915 MHz into a cable from its TX port to its RX port. Needs a PlutoSDR.

    Looking Ahead

    • Lesson 11 writes and packages custom blocks.

    • Lesson 15 explains the Costas Loop that locked to the Pluto’s carrier.

    Keep your transmitter flowgraph and your tables. Lesson 11 starts Module 5 with out-of-tree modules and custom blocks, including Python blocks like the Text Sink that displayed every line today. Lesson 15, on mPSK, explains carrier recovery, the job the Costas Loop did on the Pluto, and why a phase-modulated signal can lock upside down.