 Welcome to this lecture on digital communication using GNU radio. My name is Kumar Appaya and I belong to the Department of Electrical Engineering at the Indian Institute of Technology Bombay. This lecture will be a continuation of our discussion on error correction codes. In particular our focus will be on binary hamming codes and we will implement them on GNU radio using embedded python blocks. Embed python blocks gives us a very powerful way of realizing these hamming codes because we have full control over the way in which the coding is implemented. Our focus will be on the 7-4 hamming code that is the code takes 4 information bits and encodes them into 7 coded bits and this code is very useful in the sense that any single bit error can be corrected without any loss in fidelity. We will see and confirm that the hamming code works and we will inspect its performance in various scenarios. If you recall from the previous lecture the hamming code was defined in the class using the parity check matrix initially wherein we chose 1 1 0 1 1 0 1 1 1 as the initial part of the matrix followed by the identity matrix. Subsequently we used this generator matrix where we place the identity matrix and the transpose of this matrix which means that the columns are 1 1 0 1 1 0 1 1 and 0 1 1 1. Let us first see how we can implement this generation operation using python so that we can then translate this to building a GNU radio python block. We will now use a python prompt to check out how we can create the generator matrix and check all the code words that are possible. Remember for the 7-4 hamming code there are 16 possible 7 length code words. Let us first begin by writing the generator matrix. We will first import numpy. Once we import numpy by typing import numpy as np let us first construct the generator matrix G. We will first create a zero array and to ensure that the data type is what we want we will say d type equal to int we will just specify that this is 4 7 so that we have a 4 cross 7 matrix. Now we know that the first 4 by 4 sub matrix is an identity matrix. So we can do colon 4 comma colon 4 this accesses the first sub matrix. So now if we set it to I4 if we check our G you see that you have the identity. Now all we need to do is to specify the fifth sixth and seventh columns which correspond to the four five and six columns because python uses zero based indexing. So let us now just enter make the entries properly. So we are going to just say colon comma four is equal to now this will be then colon comma five will be and colon comma six will be. Now if you inspect our G this is indeed the generator matrix that we wanted. For the next step let us also construct the parity check matrix but since we have constructed the generator matrix constructing the parity check matrix is easy because the parity check matrix has an identity on the right and the transpose of this A on the left. So we can just take this part of G that does not have the identity and use that to set the values of H. So let us begin we will set it to zeros remember the seven four three Hamming code has a parity check matrix of size three cross seven. Next we specify that the last three columns are an identity matrix colon minus three. So all rows the last column onwards. So we have placed the identity matrix in the second half. Now we just need to take G's second part and take its transpose and feed it in as H's first part. So we can say H colon four is equal to G all rows four colon transpose. Now this is the parity check matrix that you wanted. How did we get this remember in the lecture we discussed that if G had the form IA then H has the form A transpose I for a binary matrix. If you want to verify that this is indeed the correct pair of generator and parity check matrices it is very easy. We just need to perform modulo two matrix multiplication. So let us do H multiplied by G transpose. If you do that then you will get numbers let us do this here you will get numbers of the form two two zero two and so on. But remember we want to do XOR operations which means we want to perform matrix multiplications modulo two in which case you get the all zeros matrix. Now this is fine but what are all the sixteen code words. So to get all the sixteen code words let us actually just go through it manually and get it. So for example I am going to now start encoding all the possible four bit vectors. So for example let us say my messages is we will say zero zero zero zero zero zero zero one zero one one zero one zero zero zero zero one. Then I am going to have zero one one zero and zero one one one. Now if you look at this right these are eight of the messages I wrote them manually but of course I need to add the ones where there is a one in the front. So I can just say something like this this is in python I can write a list comprehension I can say something like one plus i one colon for i in M and this will give me the corresponding entries for one what this does is it takes all the entries from the second one onwards each of these and prepends one. So now I am just going to do M is equal to M plus this and I have all sixteen binary vectors. In fact you can do this more efficiently with some other methods but I am just sticking to this. So now I am just going to say code words is and all I am going to do is I am going to do this list comprehension approach I am going to just multiply this M by G transpose. So I am just going to say M sorry say I at G transpose for I in M sorry so if you look at G and if you look at M and if you look at M zero multiplied by G sorry I am sorry I should have multiplied by G directly so I am just going to say code words is I at G for I in M. So now if you look at the code words you get all sixteen code words but you see the mistake that we made we did not do modulo two so all we do is we say percentage two and if you look at code words you have these. Now these are perpetually the hamming code words there are sixteen of them to verify that these are indeed the valid code words we can use the parity check matrix so we can do something like H at I for I in code words of course modulo two remember to do that and indeed they are all zero confirming that these are valid code words. Let us now make a final observation of these code words look and you can check that no two code words are going to be are going to know any two code words you pick they will differ in at least three bits that is you take the first one in the second one they differ in four bits this these you know these two differ in these three bits so there is always a difference of at least three bits among any pair of code words. Now the final aspect that I wanted to mention is if you want to implement this in GNU radio then we need to actually perform the XOR operation of course one way is to add you know add them and perform modulo two but there is another way let us for example take the error vector I am going to take an error vector say zero one zero zero zero zero now if I say H at E modulo two I get this syndrome okay that's that's basically the error syndrome and if I want my Y to be a code word let's say I take code words four and if I want this E to be XORed with this it's not very obvious as to how you can directly do it of course you could just make this E into an umpire A and you can just do code words four plus E that will give you this error I mean that'll give you the erroneous sequence if you do modulo two you have to put it in parenthesis but this is a little cumbersome an easier way would be to directly do the XOR and it turns out that you can do this XOR using the XOR operator inbuilt into Python so this does the XOR by twice I mean it's like there was this the XOR of each of these elements for example if you take one XOR two remember if you express these in binary one is expressed in a byte as seven zeros followed by a one and two expressed as six zeros followed by one zero so the XOR of these two will give you one one at the end which is three similarly if you do four XOR one you're going to get five because it's like one zero zero XOR zero zero one and four XOR two is going to be six four XOR three is going to be seven and four XOR four is going to be zero so you can directly perform the XOR using the carrot operator in Python and that is what we're going to use in the GNU radio implementation for our implementation in GNU radio we will take the same approach as we took earlier wherein we construct a binary symmetric channel using a Gaussian channel and use the Hamming code in parallel and show that the error performance is improved let us quickly put together the same elements let us begin with a random source control f random source a binary source is enough because we will use BPSK we will use byte to control f or command f we'll get the constellations related aspects we'll get our constellation object we'll get our constellation encoder we'll also get the constellation decoder and place it here because we will need it soon the constellation object we will call it my BPSK we'll make it a BPSK constellation we'll make this use my BPSK copy this also will use my BPSK we'll connect these over here we'll add a throttle control f or command f we'll say throttle next we will add a float you know complex to float complex to real in fact control f or command f we'll say complex to real we'll add a noise source control f or command f we'll say noise we'll make this a real noise source which has amplitude noise std of course we'll add a range that uses this which starts from zero goes up to let's say 10 steps 0.1 we'll add we'll have an addition source I mean adder control f or command f we'll say add this add will take float we can combine this combine this and finally this is going to be decoded so we'll add a float to complex control f or command f we'll say float to complex as usual this is the real part the imaginary part has to be 0 so we'll get a constant source control f or command f we'll say constant we'll take a constant source that has a float 0 connect it over here connect this to the constellation decoder view these in a time sink so control f or command f we'll say time sink we place the time sink the time sink we can give it two inputs we'll also make it float then we can just add a character to float so control f or command f we'll say care to float and convert these bytes so that they can be viewed over here another care to float to view the original constellation or original bpsk's basically so now if we observe they overlap very nicely if I increase the noise you will see that the blue and red don't match because there are errors in order to again visualize the errors we can add a histogram sink let's first rotate this by using the left key so that it's easier to lay out I'll rotate this also I'll rotate this also to the right a little bit maybe the I'll just keep this as is now I'll add a histogram sink control f or command f I'll say histogram sink and the way our histogram sink is going to operate is that you will essentially take these two subtract them and take the absolute value subtraction will give zero one or minus one a minus one or one indicating an error so we'll say control f or command f we'll say subtract the subtract operator which takes float we'll take this we'll take this this is going to be viewed on our histogram sink and if you execute the flow graph you see there are no errors if you increase this you will see that there are errors because of these plus one and minus one let's just change things around let's make this go from let's say zero to one and let's also remove the auto scale and now the only change which you have to make is that we want to add an absolute value over here so we'll do control f or command f and we'll say abs and this absolute value we'll rotate double click make it float goes here goes here and now we are set we can now get a visual on the errors so the correct is here the error is here let's just make things a little more convenient by making the x let's say minus 0.5 and now you should be able to see both so as you can see there are errors over here now we are set I think it's adding cumulatively let me get rid of that also accumulate should be no we can add a grid now we are set we can always just visualize the errors over here by counting these and comparing with this this is the number of correct number of errors we are now set with the base now we have to repeat this with a hamming code now in order to repeat this with a hamming code let's just make our flow graph a little more comfortable convenient and we're going to take the same random source but we're going to duplicate many of these in order to get the you know get the bits out and the right and the hamming code and everything we'll do that as we proceed the first thing is that we take the random source and we need to now perform the hamming encoding so for that we will use a python block that takes in these bits four at a time and yields seven so the first thing we're going to do is we're going to demultiplex the stream so ctrl f for command f and we'll say stream demux we'll double click we want to demux this into four so i'll do star one multiplied by four and put four here and i have my four demuxed stream with data type byte i'll also add the stream mux that will be needed over here not surprisingly this stream mux will need because we're using a seven four hamming code we will need seven and seven which will be byte type we will now make the hamming encoder the hamming encoder will be implemented as a python block so ctrl f for command f we'll say python and we'll grab the python block we can double click to edit it we'll say open in editor we'll choose whichever you can choose any convenient editor in which you can write python now as always we'll remove the unnecessary content so that we can focus on the code we don't need this example parameter we don't need this we will name our block seven four hamming encoder and it takes four int eight inputs and yields seven int eight outputs and we do not need any example parameter we do not need this and this is just going to change we're going to have output items 0 1 2 3 4 5 6 and input items 0 1 2 3 and we're just going to exhort them okay so now the question is how do you exhort them if you look at the generator matrix it is evident that the first four elements are directly going to be repeated directly from the message because the message appears as is then the fifth element is message ones messages bit one XOR with bit two XOR with bit four so it's like one two four one three four and two three four are XOR to get the remaining three let us implement this using the XOR operator on the GNU radio python block so a direct way to do this is to just implement the same thing on the first four 1 2 yeah so 0 1 2 3 3 2 1 so we have now completed the XOR operation sorry we have completed the encoding operation for the first four bits for the remaining we will use the XOR operation this is the redundancy now output items four which is the fifth bit is remember it's the first the third and the fourth so I'll say input items 0 XOR input items 2 XOR input items 3 next output items 5 is input items 0 XOR input items 1 XOR input items 3 the final one is output items 6 is input items 1 XOR input items 2 XOR input items 3 you can verify that this is indeed the correct implementation of the XOR operations that correspond to the generator matrix I think I believe I got these swapped around yes so this you can verify because it is 1 1 0 1 1 0 1 1 0 1 1 1 now we are set we can just save and exit and we have our hamming encoder ready now of course we could have implemented this as a matrix operation as well but I thought it would be more instructive to implement it in this manner now let us connect the stream and let us deem you know let us multiplex the stream back so that we are ready to transmit this and the hamming encoder is done now we need to perform the same modulation operation and addition of noise and so on let's do that by copy pasting I'm just going to copy the encoder control c control v we have our encoder and then we'll have a complex tutorial control c control v we'll have a complex tutorial then we'll have noise control c control v we add noise control c control v we have an addition block next we will do a float to complex with a constant source so control copy we'll we'll just copy these directly control c and control v and move these over here and we can just connect this let's make things a little neater by moving things around a little bit so I'll move the stream demux over here the hamming here the stream mux here I'll make more space so complex I'll put the noise maybe a complex tutorial I'll take here noise source here add take the constant source and now you have a constellation decoder we'll make some space over here and we'll rotate this to the left okay um yeah so one thing is before we do the character float we need to perform the hamming decoding now for the having decoding we will use a syndrome based decoder for which we will use the parity check matrix that we saw earlier so we will implement this particular parity check matrix and as you're aware by directly looking at which seek syndrome we get and finding out which index of h that is we can directly compute where the error is and let us now perform that operation let us grab a python block again and again start editing it let us once again start making our edits so we can get rid of this get rid of this now we need to make our seven four hamming decoder and as you remember this has seven inputs and four outputs we don't need this example parameter we don't need this and now we come to the main part of the code where we need to find the syndrome and based on the syndrome we can just flip the bit and because our codes generator matrix has the form where the original four length sequence appears in the first part after flipping the bit correct bit we can just read off the decoded data by looking at the first four bits now let us do our decoding process the first thing is to find our syndromes all we need to do is we need to perform an three appropriate exhaust okay we need to perform three appropriate exhaust of our received you know inputs so i'm just going to say this for convenience let's just write down the hamming parity check matrix over here as a reference so let us write the parity check matrix 1 1 0 1 1 0 0 1 0 1 1 0 0 1 1 0 0 1 this is the parity check matrix now we are ready to perform the operations to obtain the syndrome so we are essentially going to take this as the first bit this is the second bit this is the third bit so i'm just going to say syndrome one is equal to i'm going to take all input items on mass XOR so i'm just going to find the first syndrome so i'm going to XOR input item 0 with input items 1 because it's 1 1 0 i'm not going to take input items 2 i'm going to take input items 3 and input items 4 and with this i get my first syndrome then syndrome 2 is going to be input items 1 1 0 1 1 so and then XOR input items oh it should be 0 sorry and then put items 2 and then input items 3 and XOR input items 5 because it's 0 1 0 at the end finally syndrome 3 3 is input items 1 because 0 at the beginning XOR input items 2 XOR input items 3 XOR the last one input items 6 now to find the overall syndrome and say syndromes i'm going to take the first syndrome as the least significant bit this is the second and this is the most significant bit so i'm just going to say syndrome 1 plus syndrome 2 times 2 plus syndrome 3 times 4 why am i doing this that way i'm going to now treat these as numbers for example this 001 is essentially this the number 4 0 1 0 is 2 1 0 0 is 1 so let's write those numbers down you know so the effective syndrome that i am going to consider remember the first one is my least significant bit so this is 3 then the 1 0 1 is going to be 4 sorry 1 plus 4 5 0 1 1 is 6 1 1 1 is 7 then 1 0 0 is 1 0 1 0 is 2 0 0 1 is 4 so this corresponds to the the syndrome based on which syndrome this is i will correct that particular bit so let us now move ahead so now what i need to do is i need to now perform a correction of the bits appropriately i'm just going to correct it directly in the input items based on the syndrome so let's go through this for i in and i'm going to say for i and syndrome in enumerate syndromes now this will give me an you know iterator as well as the syndrome so now based on what syndrome i get i'm going to perform the correction so let's say i start with 3 if 3 is the syndrome i get the first bit is an error so i'm going to say if syndrome is 3 i'm just going to swap the first bit so input items 0 i is i'm just going to swap it 1 minus input items 0 i this is the first bits l if syndrome is 5 this corresponds to the second bit being flipped so i just need to flip the second bit so how do i do that i just press 1 here let's copy this and then let's look at the third bit for the third bit the syndrome is 6 i have just put 2 here for the fourth bit i'll just put all of those fourth fifth sixth seventh okay i need to do it only for the first four because others don't matter but we'll just do the full thing anyway so for the third bit it's six so we have three five six for the fourth bit it is seven and this is going to be three this is going to be three for the fifth bit it is one and two and four here and this is going to be four five six six five four now we have essentially corrected all the one bit errors by finding the syndrome and swapping the corresponding bits if the syndrome is zero then none of these essentially get activated and no corrections happen which is the way it should be now the final thing is to copy the outputs back and for that we will use a line like this output item 0 is input items 0 output items 1 is input items 1 because these are corrected and the first four bits in the input correspond to the actual message and with this our decoder is done so let's just show the decoding code so this decoding code essentially performs the syndrome detection and based on what syndrome you get you are able to correct the errors let us save this now we have our having decoder now to use our having decoder we need to split this constellation by demuxing it so controller for command if we'll say stream and we'll get the demux the stream demux we will rotate hamming code also we can rotate the stream demux is going to be of a byte type taking seven inputs sorry one time seven seven and connect this over here we have now connected we'll connect these corresponding over here and then we need a stream mux so we'll grab a stream mux we'll rotate this also this is going to be a byte type one times four we need four connect these and get the care to float and again we'll do the subtract we'll we can first connect it to the time sink we'll make it three we'll add a histogram input we'll make it two we'll connect this directly and again add a subtract and abs so control c and control v we'll add a subtraction get the original and get the abs also we'll do control c control v we'll get the abs over here connect this over here and now we are set now if you execute this flow graph you can see yeah if you set it to this noise level you can see that some amount of error correction seems to be happening let's do one thing let us actually increase the number of let's increase the histogram number of essentially points and let's also increase the number of samples and let's perform this with some noise if you wait long enough you will see the blue one coming up slightly if you put it here you see the blue one while the red one is slightly below because single bit error seems to seem to be corrected let's move it a little back it takes time for it to settle you will see that the peak of the red one is always lower indicating that one bit errors are being corrected if you want to confirm that all one bit errors are being corrected you can actually play a trick just to confirm for example if you have even in the absence of noise you can manually introduce a bit flip here and check for example let's do that let's just do control f for command f and we'll say add constant add add constant and we'll just make this into a byte and we're going to add the constant minus one so this is doing minus one plus the bit and we'll multiply this by minus one oh we'll do multiply by constant and make this we have to multiply by constant for sure it takes a float fine let's do one thing let's actually add the bit error here manually let's just edit the hamming encoder and then add some error okay let us just introduce the bit error in this bit this just flips the bit now let us execute our flow graph yeah so this multiply has to go if you execute our flow graph you will see that there are no bit errors that have occurred because the having code corrects single bit errors accurately because that we have implemented it correctly of course let us now reset it so that we don't have that manually introduce error and with this we can confirm that the hamming encoder is able to correct one bit errors accurately even in the presence of noise let's wait you can see that the error correction is very effective so in this manner we have implemented a hamming encoder and decoder using a python block and this is a very very powerful code and GNU radio also has other code facilities for example if you just say control f and type error or rather you say you know coding you can look at read solomon error coding yes you can say feca sync decoder encode and all those things you have all polar code you have all these kinds of encoders and decoders that you can put together and use to good effect as a final closing and optional remark one aspect is that we have implemented our decoder quite inefficiently because of the fact that we checked every syndrome and flip the bit basically manually so there is an alternative way the alternative way is to use python to good effect and use a mapping between the syndrome and the bit to flip in order to perform the flip in a more efficient manner so let's for example look over here so over here it is evident that if the syndrome is three then the bit we have to shift is zero the syndrome is five the bit we have to shift is six and so on so we can create a simple mapping using a python dictionary we can say syndrome map and we can just create this dictionary manually so we say we map three to zero five to one six to two seven to three one to four two to five and four to six this syndrome map is now a very convenient thing so we don't need to write all these lines all we need is a single line over here we just need to say syndrome map of syndrome copy is equal to now the advantage of this code is that if the syndrome is three then syndrome map of syndrome is automatically zero and the zero bit is flipped in fact let's actually just make it a little more readable let's just use this to get the index and say idx is this and we just so say idx and idx this way you have made the code much much simpler because you just have a mapping between the syndrome and the actual bit flip that it corresponds to we also have to confirm that the syndrome is not actually zero if syndrome is not equal to zero if syndrome is not equal to zero syndrome being zero is what we had to check now if you execute the flow graph you get the original performance back if you add the noise you will see that let's reduce the noise a little bit you'll see the hamming code works pretty well in this lecture we have seen one way to implement the binary hamming code on GNU radio using the embedded python block you can go ahead and discover several other error control features that are inbuilt in GNU radio but the use of the embedded python block gives you a simple yet powerful way of realizing the hamming code we have also verified the single error correction ability of the hamming code by def deliberately introducing a bit error and seeing that the hamming code is able to correct for this error in the coming lectures we will see how error correction codes also fit in as part of a as part of the building blocks of a communication system and see how you can use this to improve the performance of your communication system overall thank you