In my Digital Communications class, I am trying to help my students learn digital communication/computer networks by discovering the problems of the domain and solve them. In the last class we built a Morse code transmitter/receiver using Laser and LDR.

Today, I extended this to transfer digital data; data encoded in stream of 0 and 1. In Morse code, ‘dot’ and ‘dash’ are equivalent to 0 and 1 of digital data. However, we have explicit signal for 0/dot. Absence of signal is not interpreted as a zero. This makes the channel costlier. I could achieve ~18 words per minute (PARIS is generally used as a reference word for this calculation).

square_wave

Since my transmitter is a laser diode, switched off state can be considered to be a 0 while switched on state can be considered as 1. However, this poses a challenge as how to differentiate a set of 0 representing data from 0 representing silence (absence of data transmission).

Since we are presented with this problem, I added a start message header/pattern (1011) and an end message header/pattern (1101). I continuously sample data on my LDR receiver and add read value 0/1 to my data. Then, I check for end pattern and if it is found, I check for start header before my 8 bit data.

1011<8_bit_data>1101

H: 1011010010001101
E: 1011010001011101
L: 1011010011001101
L: 1011010011001101
O: 1011010011111101

I could decode this pattern on my receiver.

DigitalData_Top.PNG

However, this simple pattern to encapsulate a message is not foolproof. It can easily be cracked by a rogue data series.

DigitalData_Error.PNG

So how do I verify that the data received is actually the data sent from the other end?

I added a small signature to the end of data; number of set bits. Since my data is 8 bits, I added another 4 bits to represent max 8 bits that could be set in the data.

1011<8_bit_data><4_sign_bits>1101

H: 10110100100000101101
E: 10110100010100111101
L: 10110100110000111101
L: 10110100110000111101
O: 10110100111101011101

I could decode this pattern as well. Result, I could filter some garbage out

DigitalData_Signed.PNG

I do have data loss, but less garbage now. So how can I improve this further? Can I fix transmission errors? Can I make better use of sign bits? Loads of possible improvements, but we shall solve when we the problem hit us in face and we are convinced that the problem is worth solving. May be next class…

Transmitter:

#define PULSE 50

#define START_MSG one(); zero(); one(); one();
#define   END_MSG one(); one(); zero(); one();

#define MSG_PACK(x) START_MSG x END_MSG

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  // <8_Data_Bits><4_Sign_Bits>
  //H: 2 set bits
  MSG_PACK(zero(); one(); zero(); zero(); one(); zero(); zero(); zero(); \
           zero(); zero(); one(); zero(); )
  //E: 3 set bits
  MSG_PACK(zero(); one(); zero(); zero(); zero(); one(); zero(); one(); \
           zero(); zero(); one(); one(); )
  //L: 3 set bits
  MSG_PACK(zero(); one(); zero(); zero(); one(); one(); zero(); zero(); \
           zero(); zero(); one(); one(); )
  //L: 3 set bits
  MSG_PACK(zero(); one(); zero(); zero(); one(); one(); zero(); zero(); \
           zero(); zero(); one(); one(); )
  //O: 5 set bits
  MSG_PACK(zero(); one(); zero(); zero(); one(); one(); one(); one(); \
           zero(); one(); zero(); one(); )
  //CR: 3 set bits
  MSG_PACK(zero(); zero(); zero(); zero(); one(); one(); zero(); one(); \
           zero(); zero(); one(); one(); )
  //LF: 2 set bits
  MSG_PACK(zero(); zero(); zero(); zero(); one(); zero(); one(); zero(); \
           zero(); zero(); one(); zero(); )
}

void zero()
{
  digitalWrite(13, LOW);
  delay(PULSE);
}

void one()
{
  digitalWrite(13, HIGH);
  delay(PULSE);
}

Receiver:

#define SOM 0xB
#define EOM 0xD
#define PULSE 50

unsigned long data = 0; 

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  int val = analogRead(4);
  unsigned long sign = 0;

  data = (data << 1);
  if(val >= 10){

    data |= 1;
  }else {

    data |= 0;
  }

  //check tail signature
  if(EOM == (data&EOM))
  {
    //check head signature
    if(SOM == ((data >> 16)&0xF)){
      sign = (data>>4)&0xF; // 4 bits of signature
      data = (data>>8)&0xFF;// 8 bits of data

      if(sign == getSetBits(data))
      {
        Serial.print((char)data);
      }

      //Diagnostics
      //if(data==72||data==69||data==76||data==79||data==13||data==10){
        //data
        //digitalWrite(13, HIGH);
      //}else{
        //error
        //digitalWrite(12, HIGH);
      //}
      data = 0;
    }
  }
  delay(PULSE);
  //digitalWrite(13, LOW);
  //digitalWrite(12, LOW);
}

unsigned long getSetBits(unsigned long n)
{
  unsigned long count = 0;
  while(n)
  {
    n &= (n-1);
    count++;
  }
  return count;
}

BTW, I achieved about 1 byte data per second speed with the final code published here. Including headers, it comes to 20-bits per second!

EDIT
To detect errors in data, my signature mechanism of adding number of set bits in data wasn’t proving good. I need a better and cheaper solution that does not add a lot of data overhead.
I XOR high nibble and low nibble to generate a 4-bit signature. This signature is less likely to fail since error (bit flip) has to happen at corresponding bits in both high nibble and low nibble.
if(sign == ((data & 0xF) ^ ((data >> 4) & 0xF))){
  Serial.print((char)data);
}

The results, as expected are better than before. Very rarely I see a rogue byte pass this test.
DigitalData_XORBits
In ideal condition I do not get errors. I do not see rogue bytes (yet) but I do see packets dropped. How do I ensure that I have received all data in the correct order?
This is the next problem to solve. Build an ACK for each packet so that Sender can confirm what he sent has actually been received. I plan to send back the XOR sign as received. However I do not have another laser diode for the purpose…
Advertisements