Matt Brown masterfully bridges the gap between abstract signal theory and practical SDR implementation. This concise breakdown of OOK demodulation is an essential primer for anyone looking to master the physical layer of IoT security.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Intro to RF Signal Processing - OOK Demoduation via libuhd
Added:What's up everybody? This is Matt Brown with another IoT hacking video. Today we are going to be using our softwaredefined radio and our flipper zero to tear back the curtain a little bit and try to understand the world of RF at its lower levels. Specifically, I have been on a bit of a learning journey myself, and I wanted to bring bring you along for the ride in doing manual demodulation in C++ using my software defined radio. If you don't understand what all that means, we're going to explain it all in a little bit. But if you're new to the channel, welcome. My name is Matt Brown. My day job is performing security testing and doing security research on IoT devices. And I also love to share this educational content for free on this channel with you. And so if that sounds cool, hit that subscribe button. It really helps out the channel and the community.
Speaking of community, head over to the Discord. There's a link in the description as well for that community.
We're over 5,000 strong now. And uh really good group of people, really smart. Check it out. And so let's hop into what we're going to be talking about today. So, uh, real fl quick flash over to the workbench and then we'll go to my computer. So, uh, it's really simple today. All we're working with is my software defined radio that I've talked about before. This is a little bit of a higherend software defined radio. So, we are going to talk about some alternatives to that and some of the pros and cons. And then, of course, we have the the trusty flipper zero. And so let's talk about what what we're going to talk about. All right. So what I have done on this channel in the past is we've used some you know some uh tools to perform de modulation and decoding of radio data radio traffic.
And so the process of taking digital data and converting it to a radio wave is called modulation, right? And so here uh by the way, this website is like awesome. Uh RF wireless world, they just have like tons of guides on everything RF. And so I've really come to like them. That's total aside. Um, but I wanna I want to kind of focus in on this little diagram here because I think this gives the best picture of what we're going to try to do in code instrumenting our software defined radio to do it. And so, uh, right here is is the simplest diagram, uh, I can I can kind of find of modulation where you have some digital signal, you have some bits, some ones and zeros, and you want to send that over the air using using radio, right?
And so you are going to apply some transformation to that. That's called modulation. Here it says FSK. We're going to be dealing with a different modulation scheme today, but don't worry about that for now. And then you see on the other side out of that transformation comes a waveform, right?
And and don't worry about the nature of that waveform. We'll we'll we'll get to that later on in the video. But then, you know, that goes over the air. But then obviously the receiving radio has to then do the opposite right to get it back to those ones and zeros. And that is called de modulation. And so here this this de modulator is taking a radio wave sensing it and somehow through some processes converting it back to ones and zeros. And this is the core goal that like like these two operations are the thing that me as a computer scientist, a computer engineer who is uh you know kind of a casual double E wants to get done. that this is the part that uh confuses me the most and has kind of led me on this learning journey to uh break out of some of these standard tools that we have you know like a new radio again universal radio hacker to uh actually kind of pull back the curtain and see like okay h how would I actually design my own you know de modulator if the the current ones and current tools didn't do the job and so um that is what we're going to do today so we're going to start with what I think is the simplest signal and that's what I have in uh the repository that uh I hope to make you know kind of the first link when you look down in this video description uh actually I'll head over there now. So I I've posted kind of this this repository to my GitHub uh UHD Signal Fun. We'll talk about what UHD stands for in a minute, but uh you can see there is a folder and right now it just has one signal file that you can load into a flipper zero device to transmit. And then I have some C++ code that makes use of this UHD library that works with my softwaredefined radio. And so let's talk a little bit about that and then you know let l later half of the video is going to be me like going through this code and showing you how it works and actually doing a live de modulation and decoding of the signal live that's being sent from our our flipper flipper zero over here. So uh let's go and talk about this UHD thing. What does that stand for? Well, that is it. It it stands for the USRP hardware driver. And so this is a set of APIs and libraries that allows for communication with specifically you these USRP radios. And so they do tend to be on the more expensive side. Uh expenses expensive is always a relative term like expensive compared to what, right? So, uh, in the history of softwaredefined radios, a a radio that costs this much is actually kind of cheap, right? Uh, there there there are plenty of softwaredefined radios out there that have, you know, one or two or three more zeros on on the end of that number. And so, uh, is it cheap? Is it is it expensive? Well, it depends on like compared to what. So, uh, people have asked, are there cheaper alternatives?
And there there that there are I I cannot personally vouch for these, but I've heard other people vouch for these radios. Uh, and this this blog is actually vouching for uh them as well.
Uh, that there are these like TZT B200 minis. They're effectively a clone uh a Chinese clone of this radio device for you know potentially you know a fourth or a fifth of the cost. And so that is something that makes it a possibility for a lot more people. Uh you might you might miss something. So I'll leave a link to this article as well. They they dive into all the pros and cons. But I do want to say that there are there are ways to uh work alongside what I'm doing here uh using this library because I really do think that this library is like one of the better libraries out there for interacting with softwaredefined radios. And so, especially when I'm new to the whole the whole field of um you know, digital signal processing, I kind of want to use like good tooling so that uh the tooling is not the thing. It's it's just me that's getting in the way uh and not my tooling. And so, uh that is uh yeah, it's it's like a light recommendation to to to go with, you know, a cheaper uh tool. Obviously, if you can pay the money, uh this radio has been really good for me. So uh but let's let's get into this. Uh I have cloned this repository here and I'm we're we're gonna we're gonna walk through that and then over here uh you'll notice that I have loaded that first that that signal signal one file. Right now there's only one signal. Uh the spoiler is that there yes there will be more. I'm working on the other ones right now. And uh we we can just we can send the signal here using our flipper zero. this I mean this just like is like a basically a screen share of the flipper that I have over on the workbench. But let's look at the code here. So in the project I do have a make file. So you can just run make and it will compile uh this this code right here into this binary. Um and then obviously like that's where I got the flipper file. But let's go ahead and let's look at this C++ code. So uh so this library is uh it's C++. You can also program in C against it. It's uh I I I'm I'm guessing there's probably some way to have Python interface with it.
GNU Radio does a lot of Python stuff, but I also like that this is a little bit lower level uh being in C++. And again, because my goal here is the learning of how modulation is working and kind of tearing back the curtain a little bit. So, let's start uh with the program. Ignore this little top function I have here. So, in main, I am going to set up all of my settings here. And so there are a lot of ways where you can figure out what settings, you know, to set on a radio. But here we know ahead of time what our center frequency is right here on the flipper. This signal is going to be sent at 433.92 megahertz. And so that is where we get this frequency value. Our sample rate is going to be 1 million samples per second. uh we can honestly this signal like we don't need to sample that fast.
It's just a nice round number and our radio supports it. And then our gain is just that that's a value that depending on what the noise situation in your lab is. You might need to increase or decrease that. And then the antenna. So on my radio there are two antenna slots.
There is a TXRX and then there's a RX2.
So you can receive like like like two at a time. Well, no, no, it's it can only do one at a time in my radio. There there are better ones that you can like do multiple in, multiple out. This one doesn't do that. Um, but anyway, this is like the name of the antenna slot. So, there's two there are two antenna slots that I have to specify between. And then there's some arguments that we're just going to leave blank. And then, uh, we have these different calls, right? So this is all coming from this UHD library which you do need to have installed on your Linux system. That's the one dependency that this C++ code has is on that lib UHD.
And so we're going to call all of these setters to set up the radio with the right frequency, sample rate, the gain, and the antenna. And then we have this uh this get streamer. So we're basically here telling it what kind of data to like like like the data structure of how to send data back over the USB line.
This is this is kind of the default that everyone picks. So I'm not pretending to completely understand this part of the code. Okay. And then uh we grab a pointer to an RX stream, a rest a receive stream. And then we're going to uh yeah it's we're going to start streaming here. So we're going to send a stream command here uh that tells it to you know stream you know mode number of samples and done that. So uh you can kind of you can kind of just kind of guess what that means. It says like okay we're we're going to have this set number of samples that we take and then we're just going to we're just going to turn off the radio. So that's awesome. So uh what's the number of samples? Well, we are going to take 5 million samples. And so, if you're already thinking about that, I I said that our sample rate is 1 million samples per second. We're going to we're going to grab 5 million. That means we have a 5-second window. When we start recording to send like within that 5-second window our message on our flipper zero. So, that's just a that's just a helpful practical tip here. Um, then we have some other stuff. And then I want to discuss this right here. So this is a very important line of code because it kind of introduces us to how sampling of a radio signal is turned into like a a digital quantity that is then sent over the USB cord to our computer. Okay? And it is as a complex number. So, if you're not familiar, if you're not like a big C++ person, a vector is a list or an array. That's it's the C++ native way of of having a list structure. Um, it has a lot of nice bells and whistles that like C doesn't have with its arrays. Um, and so here we are defining a list, a vector of type complex and then complex. What what what type of complex is it? Well, it's it's uh under the underlying data structure of the complex numbers are uh floats and so uh and then we say there are going to be 5 million spots inside of this buffer. So, so we have this buffer variable that is a vector of complex numbers. What is a complex number? This is the biggest part of the of why I want this is like this just shows why I wanted to do this, right? Because fundamentally like I I like like before I understood at a very like general level like yeah yeah like it it takes samples and it sends it over the USB cord and then somehow the software like just turns that into knowledge about the radio signal. But like how is it doing that is the question. So it it sends across samples as complex numbers, which uh this has made me I've had many times throughout my career where I've I was like, man, I really wish I paid more attention in math class. And so a complex number, for those of you like like me about a week ago who don't remember what a complex number is, a complex number is is a number that is made up of a real and an imaginary part.
So uh and and it can be represented in a couple of different ways, but one of the ways it can be represented is here on a cartisian uh coordinate system, right?
And so you can think of the real numbers as in like you remember back in grade school you had like the number line, right? All all real numbers exist on that number line. Think think of it was it's just like it's just onedimensional, right? And and so you can go you can go forward to the positive direction and you can go backward to the negative direction on that number line. Every real number exists right here on where y equals zero in in the kind of cartisian system here. Uh the imaginary numbers the imaginary part of the number is represented by traversing the y direction here. So, so here this little you know dot out here this this uh vector if you want to think of it like that is it's you know three in the real direction and four in the imaginary direction and uh I like like I I remember memorizing rules about about about I in math class and not and not in any way really comprehending what we were talking about. Right? So, so complex numbers, the best way I can describe it is it's like when you want to represent two different things and kind of it's it's like a it's like it's like a data structure hack when you it's like mathematicians wanted to return two things all inside of one number, right?
It's it's just telling you two different ways to look at the number. And so uh so here we have you know like like it can you you you can think of it this way and this is important for for what we're going to talk about. I'm I'm not going to give a perfect uh explanation of this but here you can see this dot it's three in the real direction.
Yeah four in the imaginary direction.
the hypo. So you can see there that that kind of makes that that makes a right triangle, right? And the hypotenuse is the the magnitude, right, of of of that vector. And that that correlates in the in the RF world to the amplitude to like like like how much power is uh is there being transmitted by the signal. And that is very helpful because I'm going to go over to my other awesome RF wireless world uh page here and we're going to talk about the first kind of signal that we are going to de modulate and and and that's what uh signal this flipper this like signal one is is okay on off keying and so here we have a represent a representation of onoff keying in that domain I was just kind of talking about in the real and imaginary domain. And so the really interest the really cool thing about starting with this is that if you don't understand the imaginary numbers that's okay because with o it doesn't matter because uh in in the radio world right so think of it a complex number we're trying to deliver two bits of like two things we're trying to tell two things about the signal in one number in one complex number and so the the real part is the amplitude how much power the signal has. And the imaginary part is the I'm going to explain this probably incorrectly. It's the it's the frequency or it's the it's the rate of change of like how fast is the phase of the signal changing. And so, uh, if you remember like the unit circle and stuff like that, uh, in in math class, it has to do with all that. I'm still learning, so we can learn this together.
And if you have a better way of explaining it, let me know down in the comments. But the cool thing is we can ignore the imaginary part. We can ignore frequency with onoff keying because it is literally like it's like if you were playing like a piano and you had like a power switch like on an electric piano.
Let's say you just like played a note like one person's like holding down a note and then somebody's like turning it off and on and off and on and this s like this sound signal is just like it's either on or it's off and you don't care about the frequency. You don't care about the note that's being played. We just care about is there sound or is there not sound. Well, in onoff keying we just care is is there radio being transmitted or is there not. And so that is how this is being described here. So a zero if we want to transmit a zero in onoff keying we just we just don't transmit anything and then we transmit the carrier uh with some amount of power when we want to transmit a one and then we define some threshold somewhere in between there where we can differentiate between a zero and a one. So that is onoff keying and that is a a very bad introduction into complex numbers. But that all goes back to our code which is to say that inside of these complex numbers that we're going to be putting into our buffer, we're going to be sampling over USB from our radio. We are going to there's going to be extra information in there that in this first exercise we're not going to deal with.
We're not going to deal with the imaginary part. We're not going to care about the frequency of the signal. We're just going to care about the amplitude, the magnitude of that signal. So, let's keep going here. So, then we have this little receive function. And basically what we do is we're going to loop until the amount of data received is greater than or equal to our target, which is 5 million. So once we grab our five million samples, we're going to uh kick out of there. And then we're just going to, you know, I I'm just doing some printout things here to confirm what's going on, right? So I I print out, you know, like, okay, what's the vector size, uh, the first value, the middle value. Oh, here. Okay. And here I am printing out the absolute value of the complex number. So what exactly is that doing? So let's go and look at this um this uh ABS function that takes in a complex number. Well, it is it it tells us here in the documentation. Wow, that's really small. Um that that effectively under the hood what this function is doing is it is calculating the hypotenuse of the real part of the number and the imaginary part of the number. Right?
Okay. So it it did help that I explained this, right? So if we want to figure out how powerful the signal is, we want to we want to measure this. We want to measure the hypotenuse of this right triangle. Well, the way you do that is you you calculate it based on the two sides, right? A^2 plus B2= C^2. I remember that part from math class. Not complete. It hasn't all it hasn't all left yet. All right. So uh so so that is what's being done by this function under the hood. And it's it obviously it's like the the absolute value. So it's not going to give you a negative number. Um so we're good to go. Um that's what that's what this function does. And so it gives us back Yeah. So yeah, you pass in the complex number of type T. Ours are ours are complex floats. Um and yeah, so there we go. So that's that's what we're doing. That's where that function comes in handy. And you'll see we'll see that later on. So what we're going to do in our code uh we are going to we're going to loop over it and we're going to grab the minimum uh the the the minimum amplitude and the maximum amplitude because what we're going to do is we want to we want to dynamically pick a threshold value. Right? So going back over here to our diagram, we want to kind of on the fly once we sample, we want to say like, hey, like, you know, what's the maximum, you know, amplitude?
What's the minimum? The minimum is basically going to be zero, right? With onoff keying, it's going to be zero. Uh, and then we want to kind of dynamically pick what this threshold value is that we want to pick in here is. So, uh, so what we do is we loop over every sample that we've gotten inside of our buffer and we accumulate what the min and the max is and then down here we calculate the threshold. So here we're actually adding together, you know, the minimum and the maximum. And we're not picking right in the middle. We're kind of purposefully picking uh a little bit towards the lower end. Um, and there's some reasons for that, but you you you could pick you could pick in the middle and it would probably decode fine. Uh, and then we're going to and and oh, and then then what we're going to do is we are going to decide um okay, so I have this start length and end length. So this is where it is helpful to actually understand our our signal and how it works. Okay. So, in this flipper file, so we're we're going to peek inside this. If you were if you were reverse engineering the signal and you didn't have access to the to the flipper like code side of the signal, you would have to do a little bit of trial and error to figure this out. But you'll notice that at the beginning we transmit high for 5,000 micro seconds. So, uh microsconds, millionth of a second. We're we're sampling. Now you can see why it's helpful for us to sample uh at one million samples per second because then uh what like how many samples do we think are going to be high as the indication that we're starting to transmit? 5,000 and same thing with the end. It's going to go low for uh for 20,000 and that is the indication that the message is over. All right, explain that.
So let's keep going here. And then uh so what we're going to do is we're going to accumulate the message. Okay. And so what we need to do is we need to detect the state change. So so the fir so the beginning state is there's not a message being uh being transmitted. The next state is we are in that start you know we're we're kind of in that synchronization start period of 5,000 microsconds. Then the next state after that is the actual data of the messages being transmitted. And then the last stage uh which again is going to be you know is going to be 20,000 micros. And and notice that I kind of like set a little threshold in here to be like okay if it's a little bit less that's fine.
Um if there there's room for error and things like that. Um, and then the last state is like we're in the end state and then we're like and then and and then you go back and and you go back to your original state of there's no message being there's no message there's no start uh sequence.
And so what I have here is I'm looping over the values. I am I am grabbing this r value which is the magnitude. Right?
So every time through I'm I'm going to loop through every one of my samples and I'm just I'm I only care about the magnitude. I don't care about uh the the real or the imaginary part. I just care about the hypotenuse. I just care about how strong my signal is. And then uh each time I'm going to accumulate, you know, hey, if we're below the threshold, then it's it's a zero. It's it's off, right? And so then what I'm going to do is I'm going to increase the off counter and I'm going to set an on counter to zero. And in the other case, I'm going to do the opposite. Okay? And then uh we have uh our our our kind of state machine detection here. So if we're not in the preamble, that first part, and we're not in the middle of the message, and the amount of high signals, the amount of on signals is greater than our start length is greater than, you know, give or take 5,000, right? Uh then we want to transition into the preamble state. So now we understand that we are in the preamble. So the next state change we want to check for is if we're in the preamble and we uh you know if if if it ever goes off, right? So if we're in the preamble and like off is greater than zero. So in in in C and C++, if an integer is anything but zero, it evaluates evaluates to true. That's that's kind of what I'm using here. So then we just say, oh, sorry, sorry, sorry. No, no, no. If you're in the preamble and then it goes low, that's not that's not you exiting. That means you're exiting the preamble and you're starting to actually process the message. So that's what we have here. And so and then we set, you know, then we're like, okay, now we're in the message state. And then we have like a detection of the message is over.
And so if we're in the message and the signal is off more than that 20,000 give or take, then we exit out of the message state. So uh and then we have this cool little thing which uh I I'm going to explain this visually rather than kind of walk through this code. Uh but basically what we're doing here, let me find a signal. Okay, so like like look like look at this down here. So uh right here there is let me zoom in. So here we have like a zero one 0 0. So what I want to do is I want to sample kind of like in the middle of the zero. Right? So what I'm doing is I'm saying okay if this has if there's a thousand samples is like where one symbol goes then I want to at the 500 offsets right kind of like right in the middle. I want to sample here whether I see a one or a zero. I want to sample here. I want to sample here. I want to sample here.
That's that's how I'm going to pull the the actual like one digital ones and zeros out here. And what I'm doing is I'm pushing them onto a message vector which is of type boolean. And so a boolean is one or zero. That works perfect for storing binary data. And then uh when I'm all done, we're going to pass it to our to asky function. But I'm actually gonna write uh a function here to just to just kind of show you like a little bit of like the coding stuff I've been doing and uh to kind of show you how I get the raw binary data.
So let's actually loop through this uh this message again. So message is the ones and zeros that I that I've that I've decoded out of the air. And let's go ahead and convert them into okay, we're going to use our trusty turnary operator. If you're not familiar how this works is like if B is true, then it will then I will be a one. If it's false, then it'll be a zero.
All right.
And then we're going to output that. And then down here, we'll we'll just throw in our little guy there. All right. So, now it's game time. Oh, 137.
Hey, there we go. Okay, signal RX. And here here's the here's the real fun part. All right.
So, we are going to listen and once it says that it's in the receive state, then I will go ahead and transmit my signal. There you saw it.
And there we go. So, move this aside.
So, you can see here uh all my kind of like testing stuff that gets printed out. But here is the actual data those actual ones and zeros that I am de modulating right based on the magnitude of the signal from those complex uh the the complex data structures that are coming across the USB line. Uh and then you know we're we're do we wrote our own de modulation uh code which is pretty simple because uh onoff keying is probably the simplest modulation out there but uh it's kind of satisfying to get ones and zeros and then our two asy function uh converts that to hack the planet but we could also you know like go to cyershe here and then say from binary and you you can see there hack the planet in it. Um, it does. Yeah. So, it does have like a bunch of trailing zeros. So, uh, you you like like how you determine where the where the message ends is usually a protocol dependent thing. If I wanted to design a better binary protocol, maybe before hack the planet, before the message, I would have like a length field, right? Maybe the first bite would would tell me how many more bytes would be a part of the message and that would help me uh parse things better. And so maybe that's maybe that's something I'll do in the next step of the signal design. And uh the the other next step that we're going to do is we're going to do frequency modulation. We're going to do FM. So that's where I'm going to actually have to care about the imaginary uh part of the complex number. I can't just be like, okay, take them both and shove them into that uh ABS function uh and get back the magnitude of the signal uh the amplitude, right? Um no, we'll actually have to care about the frequency uh in FM. And so I think that'll help me understand those kind of modulations better. And uh yeah, so this was just to just to kind of take you along for the ride in my learning. And uh the other thing about this code is that I do write a lot of code with cloud code nowadays. I actually wrote all that code myself uh w with with with with some assistance, right? Uh but I would encourage other people to do that. Like there's tons of our life that we can automate with these tools that then give us time to learn. And so uh other things on my docket are actually to go back uh MIT and Stanford, they have like great college courses that are just like all the lectures are freely available online. So, I would encourage if like this sparked any interest for you to go back and like there there's so much free learning material out there and so uh it can be daunting. Just just take it in chunks and just like find little wins throughout the day. Uh anyway, the the rant over. Uh, as always, have a great
Related Videos
LBF101 Creating an XML Changelog
liquibase7511
3K views•2026-06-15
Alta Labs Cloud Dashboard Real time Network & Xnet Insights!
ShinyTechThings
158 views•2026-06-17
Wait... Group Policy Not Applying? Check This First!
keeplearning_iT
144 views•2026-06-15
Leetcode Weekly Contest 506 | Life's boring these days
Pudeesht
2K views•2026-06-14
microJAM: MAKING A MICRO GAME FOR A GAME JAM IN CLOJURESCRIPT AND TOTALLY NOT C
janetacarr
156 views•2026-06-18
Partitioning vs Bucketing vs Clustering: How to Make Queries 100x Faster
thedataandaiguy
194 views•2026-06-16
Design Claude Code Like a Senior Engineer
hayk.simonyan
344 views•2026-06-19
Linus Torvalds: AI Won’t Replace Understanding Code
SavvyNik
140 views•2026-06-19











