Friday, June 16, 2017

A digital communication project using OFDM and 32-QAM

This past semester (my last one during my undergrad years!), I was enrolled in ECE4670 -- Intro to Digital Communication with Professor Aaron Wagner. The final project required constructing the encoder and decoder (in Matlab) to send data over an existing channel (Cornell’s commcloud server). The project was done in groups of 2 and I had worked with my friend Balazs Szegletes. It was a fun learning experience, and I wanted to share the details of the project here!

The communication channel is effectively a DAC that outputs the results of the encoder, X, a low-pass filter, and then an ADC that samples this and sends data, Y, to the decoder. The means for sending data to the channel involves writing the data to a wav file, which is then passed as an input parameter to a program called ccplay that is provided for the class. The program handles the DAC and ADC and saves the sampled output as another wav file. The goal is to be able to push data through the channel as quick as possible with few number of bit errors. The figure of merit is computed as:
FOM = (min(R, 300000)*(1-N/50000)10)/max(1,400*Po)
Where R is the data rate defined as (100,000/(L/44100)) where L is the length of the output of the encoder
N is the number of bit errors
Po is the average transmitted power

The channel properties can be modeled by the Toeplitz matrix H, such that:
Y = HX + Z, where Z is iid (independent, identically distributed) Gaussian noise. The commcloud server has two such channels, audio0 and audio1.

This builds on a previous lab, where orthogonal frequency division multiplexing (OFDM) is used with on/off keying to send data over the channel. This scheme achieved a data rate of about 14,000 bits per second with zero errors, resulting in a figure of merit of about 14,000. The high performance design utilizes orthogonal frequency division multiplexing (OFDM) and quadrature amplitude modulation (QAM) to achieve a figure of merit much higher than the previous lab.

The overall OFDM system block diagram is shown below (taken from Professor Wagner’s course’s Scribe notes):

The big gain in performance stems from the use of QAM, which allows encoding multiple bits per sample, in the complex plane. A modulation order (M) of 5 was selected for the design, ie 32-QAM. This was experimentally determined to be the optimal modulation order, which gives the highest data rate (R) before bit errors (N) start outweighing the benefits from a higher data rate.

The encoder encodes the incoming bit-stream into frequency components and then processes it as described below before sending out its time-domain signal over the channel.

The encoder accepts the input vector (of length 100,000) and encodes the vector using 32-QAM into 100,000/5 complex samples in a vector x. The encoder then separates x into 8 equal sized data blocks D1...D8 of length L1. The size of L1 is determined by modulation order M: L1=(100,000/(8*5)). Two training blocks, T1 and T2, each a vector consisting all 1’s and of length L1,  are inserted to create the following vector, x, of length 10L1.


Using a seed of 1, and Matlab’s rand function, a vector, p1, of pseudorandom phase angles (between 0 and 2 pi) is generated. These are used to create a vector p2 of phasors, where:
p2(i) = gamma*exp(1jp1(i)) for all i = indices of elements of p1,p2. The constant gamma sets the average power level.

The elements of this vector are then multiplied element-wise with the elements in vector x. This is to prevent the constructive addition of several frequency components in the time domain which would cause the time-domain signal to clip or saturate. 

Each block is then prepended by NZpre zero’s. NZpost zero’s are appended to each block. Let this vector be called X. X is then padded with 0 for DC and concatenated with X*. This ensures that the time domain signal is real-valued. The inverse DFT of this is taken to get the real-valued time domain signal. Each block is prepended with a cyclic prefix (length Kc) to make the (Toeplitz) channel transformation effectively look circulant. The encoder then outputs this vector.

The training blocks are inserted into the data vector so that it may be used at the decoder end for training of channel properties. Each element of a training block (in the frequency domain) has magnitude 1 and a phase theta_t. When this element goes through the channel, it is attenuated and incurs a phase shift and is presented to the decoder with magnitude gamma_c(attenuation of the channel) and phase (theat_t+theta_c) where theta_c is the phase added by the channel. Each frequency component has an associated attenuation and phase addition. Since the training block is the same length as any individual data block, there exists known training information about attenuation and phase for each frequency of interest in the system/encoding at the decoder end. The decoder is then able to use this block’s received data to cancel channel effects for the other blocks. However, it must be noted that the the attenuation and phase addition of the channel are not static and vary with time. Thus it is important not to let the training data be stale. This is the reason two training blocks are used, and also why they are inserted in the order they are, with respect to the data blocks.

It was observed that placing only one training block at the beginning of the data vector allows determining channel phase reasonably well for only a couple data blocks adjacent to it. Additionally, it was noted that decreasing the number of blocks also made the prediction worse. By placing the two training blocks as they are in the vector allows using a block to cancel channel effects for two adjacent data blocks. The phase variation from the training data to the two data blocks is small enough to allow sufficiently high figure of merit. The phase variation from D1 to D4 is illustrated below:
While the shift is somewhat significant, it is still possible to correctly decode the information. As can be imagined, too far from this and the bit error rate gets terrible.

At the decoder end, synchronization is performed to determine the start of the signal. This is done by thresholding the average power level over five samples. To recover the samples encoded in the frequency domain, the decoder reverses what the encoder did, firstly ignoring the cyclic prefix, performing a DFT, dropping the flipped conjugate second half of the vector and then dropping the zero-padding. Since gamma_c*exp(1j*theta_c) is known from the training block for each of the frequency levels, the samples in D1 to D4 are divided by gamma_c*exp(1j*theta_c) from T1 to cancel the channel effects. Similarly, D5 to D8 use the channel information from T2. Finally, the pseudo-random phases added at the encoder side are removed by dividing by each of the known phases element-wise. These phases are known since the random number generator had a fixed seed of 1. The individual data blocks are then demodulated (QAM) and concatenated to each other to form the 100,000 long bit vector. For modulating and demodulating using QAM, Matlab’s inbuilt functions qammod and qamdemod are used.

The following constants were determined to give the highest figure of merit:
NZpre = 110
NZpost = 1050
gamma =7.8 (chosen to meet the power constraint of 1/400)
Kc = 170

Experimentally, the figure of merit scores are about 44,000, and 56,000 for channels audio0 and audio1 respectively. The constants NZpre, NZpost, gamma, modulation order M, number of data blocks, number of training blocks, and placement of training block were all experimented with and the results presented here represent the best combination for the highest figure of merit. With only one training block, the phase drift was high enough to negate the benefits of higher data rate obtained from 32-QAM. However, adding a third training block was seen as a waste since it would minimally reduce the number of bit errors but would hurt the data rate appreciably. Using convolutional coding was considered but it was determined as not worth the effort given the performance obtained and the added complexity.

I expect to write another post soon to discuss some technical background behind OFDM, training and synchronization. Let me know what you think and if there's something else from the project you'd like me to cover in more detail!

Friday, December 16, 2016

PIC32 JTAG Loader / ICSP programmer

This past semester, I worked on an independent study/project where I explored the programming of the PIC32. I've provided the details in the following PDF document. The key takeaway I believe is:

"This document presents sufficient background information on the project, and implementation specific details. The most important contribution this makes is adding the experiences and a full implementation of the project. For even more detail and a thorough understanding, it is recommended that this report be read along with the reference documents mentioned in Section 4.1."

I plan on improving this, as well as exploring the option of programming other PICs this summer. A cursory look made me think that the PIC32 programming standard was more complicated than the PIC16's. I'll find out!

All the project code and settings can be found here. This includes the PIC32 project, as well as the MATLAB code for the programmer software
https://drive.google.com/open?id=0B4SoPFPRNziHeUhXQlZSRWxyeW8
Please let me know if you have any comments or feedback!

Saturday, February 6, 2016

Creating my own TV Tuner IR remote with a PIC16F684


While I was home this winter, I saw that the remote for our TV tuner was damaged physically, causing the buttons to not function responsively. Some of them just didn't work. I saw this as an opportunity for a fun couple days' project to build a new remote controller for the tuner.

The TV tuner was manufactured by a brand name RealView and, as expected, I couldn't find much detail about it. Thus, I had to reverse engineer the remote. From my previous experience in working with IR remotes, I had a hunch that the IR was most likely modulated at ~38kHz or ~56kHz. For those of you who don't know how this works, I highly recommend going through this website: http://www.sbprojects.com/knowledge/ir/

Thus, I connected a 38kHz IR receiver I had at home (TSOP1738) to an oscilloscope in order to figure out what IR protocol is used by the remote. Upon pressing the 4 key on the remote, I saw the following waveform:

Fig. 1: Oscilloscope waveform capture of the received IR signal

Note that I inverted the signal on the oscilloscope since the IR receiver output is active-low.

I then compared the waveform to some of the common IR protocols, paying particular attention to the initial first high and low states. After going through SIRC, RC5, RC6 among others, I noticed that this matched the NEC protocol: http://www.sbprojects.com/knowledge/ir/nec.php

Fig. 2: Standard NEC IR protocol pulse train

Fig. 3: NEC IR protocol logic high and logic low signals

Using the waveform shown above, I found that ~address was not being sent, meaning that the extended NEC protocol was being used:

Fig. 4: Extended NEC IR protocol pulse train

From the waveform, I found that the address was 0xBD02. I then proceeded to make a simple decoder with a PIC16F877A since I had a development board with an IR sensor mounted on it. Using this, I found all the required commands for the different keys of the remote. I decided to exclude some of the keys that were never used (eg play, pause, stop, fast forward, rewind).
You can find this part of the project here:
https://drive.google.com/file/d/0B4SoPFPRNziHZjk2N3pNLVE5V1E/view?usp=sharing

This left me with the following keys and commands:


KeyCommand
09
10
21
32
43
54
65
76
87
98
UP18
DOWN19
RIGHT16
LEFT17
PREVIOUS CHANNEL13
-/--10
POWER20
MENU21
MUTE12

I then proceeded to write an IR transmitter using the PIC16F684 (using the MPLAB X IDE and XC8 compiler), following the timing information from the extended NEC protocol. In order to connect all the keys, I connected them in matrix keypad form.

In order to power the remote off 2xAA batteries, it is necessary to use sleep mode - otherwise the battery will be drained extremely quickly. So, in order to detect when a button is pressed, an interrupt is used. After the IR command is sent, the microcontroller goes to sleep. The interrupt wakes up the microcontroller when a button is pressed. Debouncing is achieved using simple software delays. When a button is held down, the NEC command repeat sequence is not sent. Instead, the remote relies on releasing the button and pressing it again.

To minimize leakage current through the input capacitor, I decided not to use an electrolytic capacitor. A red LED illuminates to confirm that a button has been pressed.

 Fig. 5: Schematic of IR remote design

Everything was put together, a PCB was designed and when tested with the TV tuner, everything worked as expected! I measured the sleep current with a portable DMM and it was read as 1.6μA! I'm not sure how accurate the DMM is at such low currents, so I wouldn't entirely trust this number - however, it does seem to be within spec for the PIC16F684.

Due to a lack of time, I had to put the remote together with electric tape! Here you can see pictures of the final design:

Fig. 6: Final IR remote, top

 Fig. 7: Final IR remote, bottom

I am back at college now, but I have been told that the same set of batteries are still working on the remote, and it is working perfectly fine!

You can find the MPLABX project, source code, schematic and pcb files all here:
https://drive.google.com/file/d/0B4SoPFPRNziHV2I1U1Bldm1OYUE/view?usp=sharing

Let me know what you think! If you have any questions, let me know in the comments section!

Monday, December 28, 2015

Using an input device on Embedded Linux: An example with a USB mouse on the Intel Edison

The Intel Edison test board, along with the USB mouse


I have recently been using the Intel Edison for the Cornell robotics project team (which co-hosts the Intel-Cornell Cup USA competition). Building on my previous knowledge of embedded systems, I started learning to use and program on Linux. The distro used is Yocto (all information is available on the Intel Edison website).

One of the prototypes we worked on relied on using a wireless Playstation 4 controller for locomotion user interface. The concept of using an input device on Linux is not complicated, but can be a daunting task for someone new to Linux programming. Hence, I have decided to write this article giving an example of using an input device on an Embedded Linux platform. This demo application I am showing uses a USB mouse as the input device connected to the Intel Edison.

Prerequisite: I have assumed that you have a flashed Intel Edison board, know how to access the terminal (through the USB COM port, or through SSH) and have the Eclipse IDE installed and can program with it. Of course, if you don't have the IDE, you can compile the code through the terminal and I'll tell you how to do it at the end. If you are using a platform other than the Edison, details may change but the general idea is similar. Additionally, it is assumed that you have a basic understanding of C programming.

First thing to note when you connect the USB mouse is that the switch on the board (labelled SW1) must be switched towards the USB-A connector from the default position facing the microUSB port.

The device drivers in Linux abstract away the low-level nitty gritty details of the interface with the input device, presenting an input through file descriptors that can be interfaced with as files. The input devices can be viewed and read from in the Linux environment just like files, as mentioned before. The input device appears in the /dev/input directory. Initially, before the mouse is plugged in, you can see that there is an event0 and an event1 file. Upon connecting the mouse, you can see an event2 file.

 
Fig. 1: Input device files without mouse connected

 
Fig. 2: Input device files with mouse connected

By reading the event2 file, you can read the mouse data. To dump data from the file, you can use the od command (man page: http://man7.org/linux/man-pages/man1/od.1.html)

For example, to view the output dump in hex format:
od -x event2

Move the mouse around, press the buttons, scroll the wheel and you'll see data appear on the console:

Fig. 3: File event2 data dump using od command

Hit Ctrl+C when you're satisfied you've seen enough of the dump.

Now to make sense of this input, decipher it and meaningfully use it, I have written a simple C application. I'll walk you through the process of developing it before I provide the code.

First thing to do is to go through these references as part of the kernel documentation:
https://www.kernel.org/doc/Documentation/input/event-codes.txt
https://www.kernel.org/doc/Documentation/input/input.txt

Additionally, you should go through the linux/input.h header file. You can find a copy here:
http://lxr.free-electrons.com/source/include/linux/input.h?v=2.6.38

You can also type it into Eclipse, hit Ctrl on your keypad and left mouse click on the header file name to view the file itself.

From the kernel documentation and the input.h file, you should find that the data output happens such that every time an event occurs, it can be "fit" into the following structure (defined in linux/input.h):

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};


You can find that this has a total length of 16 bytes. You can look through the different data types and add, and confirm using the sizeof function in Eclipse:
fprintf(stdout, "Size of event is: %d\n", sizeof(event));

Each event has a timestamp, type, code and value as you can guess from the input structure. Additionally events are separated by EV_SYN type events which are just markers. EV_SYN is defined as 0.

You can read the file in a C program and then just print out the values separated as fields in the input event structure to confirm that and observe the different types of data as you interact with the mouse. You can limit the type of event as you interact with your mouse. To understand the meaning of the numbers you receive, peruse the linux/input.h file and the kernel documentation linked above. You will see a section describing the events:

/*
 * Event types
 */

#define EV_SYN            0x00
#define EV_KEY            0x01
#define EV_REL            0x02
#define EV_ABS            0x03
#define EV_MSC            0x04
#define EV_SW             0x05
#define EV_LED            0x11
#define EV_SND            0x12
#define EV_REP            0x14
#define EV_FF             0x15
#define EV_PWR            0x16
#define EV_FF_STATUS      0x17
#define EV_MAX            0x1f
#define EV_CNT            (EV_MAX+1)


You can also find a section describing the different keys/buttons for a keyboard, gamepad, mouse, etc. The section describing the mouse is:

#define BTN_MOUSE       0x110
#define BTN_LEFT        0x110
#define BTN_RIGHT       0x111
#define BTN_MIDDLE      0x112
#define BTN_SIDE        0x113
#define BTN_EXTRA       0x114
#define BTN_FORWARD     0x115
#define BTN_BACK        0x116
#define BTN_TASK        0x117


/*
 * Relative axes
 */

#define REL_X            0x00
#define REL_Y            0x01
#define REL_Z            0x02
#define REL_RX           0x03
#define REL_RY           0x04
#define REL_RZ           0x05
#define REL_HWHEEL       0x06
#define REL_DIAL         0x07
#define REL_WHEEL        0x08
#define REL_MISC         0x09
#define REL_MAX          0x0f
#define REL_CNT          (REL_MAX+1)


You can compare these against the values you see to see if they make sense (they should!). Then, you can proceed to mold this to read the different codes, types and values based on these. This is what I have done in my demo application, which should be commented enough for you to understand. (Obviously, if you have questions, let me know in the comments section!)

One last thing that I haven't covered yet (but you may already know) is how to do the file read. I have used the low-level file IO functions open and read:

Opening the file:

// Use low-level Linux file IO operations
// Device is presented as a file, event2 in /dev/input for the Edison
int fid = open("/dev/input/event2", O_RDONLY);
if (fid == 0){
    fprintf(stderr, "Could not open event2 device!\n");
    return EXIT_FAILURE;
}
fprintf(stdout, "Opened event2 device!\n");


Reading the file:

int nbytes;
struct input_event event;

// Event type from <linux/input.h>
nbytes = read(fid, &event, sizeof(event));

The demo application prints out messages describing mouse motion, wheel motion and left, middle (wheel) and right button presses. See Fig. 4 below.

You can find the full code for my demo project here: https://drive.google.com/file/d/0B4SoPFPRNziHUUVCRHVPNjkwZ2s/view?usp=sharing

A typical output is shown below:
 
Fig. 4: Output of the demo application 


Programming without the Eclipse IDE: As I have mentioned before, even if you don't have the Eclipse IDE (which you should get), you can still program the Edison. Here are a few ways you can do so. You can copy-paste the code from a text editor to the terminal (using PuTTY, mouse right-click is paste), or even write the code on the terminal. Additionally, you can use a program such as WinSCP to transfer a C file. Be careful with Windows files since lines end in a newline and a carriage return character, whereas on Linux, they end with only a newline character. The carriage return character will be displayed as ^M if you open the file with the text editor. Once the file is on the Edison file system somewhere, cd into that folder and compile it:

gcc -o <output name> <source file name>
eg: gcc -o mouse mouse.c

Then you can run it:
eg: ./mouse

I have attempted to make the code self-explanatory and provide sufficient background detail here for you to understand what's going on. By changing the code and type checks, you can extend this to other devices. Hopefully you'll find this useful! Let me know what you think!

Sunday, August 2, 2015

PIC32 Tic-Tac-Toe: Demonstration of using touch-screen, TFT and the Protothreads threading library





I had previously used the Adafruit TFT display using my library (ported from the Adafruit Arduino library). I decided to optimize the library to improve drawing speed. The same display I use comes with a 4-wire resistive touch-screen as well. I decided to write a simple library for the touch-screen and give Protothreads a try. To incorporate all this, I thought it would be cool if I used these to make a simple game. Tic-tac-toe came to mind as a fun little demo.


I'm sure everyone's familiar with the game so I won't explain the rules there. The touch-screen is simply two resistive sheets placed on top of each other on top of the TFT screen. When it is pressed down at a given place, the two sheets make contact and a voltage divider is formed. Using the IO's and the ADC, this voltage is read in the X and Y directions to register a touch.

Here is a very good pictorial depiction of the resistive touch screen (taken from the Atmel AVR341 document):

So in order to read the touch, the X+ and X- points are applied power, and one of Y+ or Y- is read to read the x-coordinate. Then Y+ and Y- are applied power and one of X+ or X- is read to read the y-coordinate. X+, X-, Y+ and Y- are connected to four GPIO pins on the PIC32 that are configured to outputs when driving the touch-screen and analog inputs when reading. Every time the IO pin switches state, a long delay is provided to allow the outputs to stabilize. Alternately, the ADC is significantly slowed down to negate effects of capacitive charging by high source impedance. The library is written in the form of a simple state machine cycling through its states every few milliseconds, decided by the application calling the library functions. In my application, I use 5 milliseconds.

To organize the game, I've made use of the Protothreads threading library. Protothreads is a very light-weight, stackless, threading library written entirely as C macros by Adam Dunkels. Bruce Land has ported Protothreads over for the PIC32. You can find more details on his excellent site: http://people.ece.cornell.edu/land/courses/ece4760//PIC32/index_Protothreads.html

There are two main executing threads, one is the main game thread and the other is a clock thread that keeps track of, and displays, time since the program was started. There is a third thread used to retrieve touch information. It is spawned by the main game thread when touch input is required. The main Protothreads functions (macros) I've made use of are:

PT_setup()
PT_INIT()
PT_SCHEDULE()
PT_SPAWN()
PT_WAIT_UNTIL()
PT_YIELD_TIME_msec()
PT_GET_TIME()

Pin connections:

BL (backlight): I left it unconnected, but you can connect it to 3.3V for backlight.
SCK: connected to RB14 on the PIC
MISO: left unconnected, since I'm not reading anything from the screen
MOSI: connected to RB11 on the PIC
CS: connected to RB1 on the PIC
SDCS: left unconnected as I'm not using the microSD card for this
RST: connected to RB2 on the PIC
D/C: connected to RB0 on the PIC
X+: connected to RA4 on the PIC
X-: connected to RB13 on the PIC
Y+: connected to RB5 on the PIC
Y-: connected to RB15 on the PIC
VIN: connected to 3.3V supply
GND: connected to gnd

Here is a demo of the game:



Besides the game itself, you can see the running clock on the bottom left right above the players' scores. To the bottom right you can see a flickering circle that is either green or red, depending on if it's player 1 or 2's turn, respectively. Once the game is over, you have the option of playing another game while score is being tracked.

Here is a link to the MPLABX project with all required header and source files:
https://drive.google.com/file/d/0B4SoPFPRNziHbURwVWVSN1c3VVE/view?usp=sharing

I have commented the code to make it fairly self-explanatory. If you have doubts or questions about anything, let me know and I'll add more detail. Let me know what you think!