TDR (Time-domain reflectometer) concept. On the right track?

I've got my low-resolution TDR working now. I'm using a single comparator to detect both positive and negative reflections, with the reference input fed from a PWM pin feeding a low-pass filter. The software sets the reference input voltage, allows it to settle, starts timer 1, and sends a pulse into the cable. The comparator output is fed to the ICP1 pin, so that when a reflection is detected, the time of the reflection is captured in ICR1. This is repeated while ramping up the reference voltage, so that the software can look for positive steps in the voltage at the end of the cable. Then it ramps the voltage down, looking for negative steps. From the number and amplitude of the steps found, it can distinguish between no cable connected, short circuit at cable terminals, cable open circuit at (distance), cable short circuit at (distance), and cable correctly terminated (i.e. no reflections).

The biggest problem was stopping the comparator oscillating. It really wanted to oscillate at about 200MHz. The datasheet warns about the importance of good layout, and I was prototyping on a breadboard, so I guess it isn't too surprising. Eventually, the combination of putting a decoupling capacitor right on the SMD adapter, keeping the output away from the input, grounding unused breadboard rows, and adding a large amount of hysteresis solved the problem. It should be much easier on a PCB.

The resolution is currently only 62.5ns (about 6.4m for cat5 cable) because I am using a 16MHz Arduino for prototyping. Next step is to add an LCD display, and programmable delay line between the mcu and R1 to increase the resolution. I have a DS1023S-25+ on order, which will theoretically provide 0.25ns resolution, although I expect that jitter will reduce that.

Here is the code.

// Time domain reflectometer

const int stepPin = 4;       // digital output pin we generate the step on. Later in the code, we assume this is port D bit 4.
const int refPin = 11;       // PWM output pin that we use to generate the comparator reference voltage. Must be on timer 2.
const int shutdownPin = 7;   // digital output port used to put comparator in shutdown mode - drive HIGH before sleeping

const int maxSteps = 4;      // maximum number of steps in each direction that we capture

// Define the fraction of the speed of light at which waves propagate in the cable
// Some typical values;
//  cat 5 cable 68%
//  RG58 or RG213 coax (solid polyethylene dieletric) 66%
//  Airspaced coax up to 92%
const float propagationFactor = 0.68;  // fraction of speed of light that waves travel in the cable under test

void setup()
{
  pinMode(stepPin, OUTPUT);
  pinMode(refPin, OUTPUT);
  pinMode(shutdownPin, OUTPUT);
  
  TCCR1A = 0;
  TCCR1B = (1 << ICNC1);   // input capture noise canceller enabled, capture on falling edge
  TIMSK1 = 0;              // timer 1 interrupts disabled
  ACSR = 0;                // input capture from ICP1 pin
  
  TCCR2B = (1 << CS20);    // change timer 2 PWM frequency to 31.25kHz because we're using pin 11 as a DAC
  
  Serial.begin(19200);
}

struct Step
{
  unsigned int time;
  unsigned int amplitude;
};

// Take a single measurement, using either a positive or negative edge from the comparator.
// The comparator reference voltage must have been set up and allowed to stablise before calling this.
unsigned int takeMeasurement(bool posEdge)
{
  byte reg1b = (posEdge) ? 0 : (1 << ICES1);    // input capture noise canceller disabled, set up input capture polarity, stop timer
  reg1b |= (1 << CS10);
  TCCR1B = reg1b;
  TCNT1H = 0;
  TCNT1L = 0;              // clear timer
  unsigned int capture = 0;
  unsigned long start = micros();  // get the time
  
  cli();
  TCNT1H = 0;
  TCNT1L = 0;              // clear timer
  TIFR1 = (1 << ICF1);     // clear timer 1 input capture bit
  PORTD |= (1 << 4);       // set output high
  sei();

  do
  {
    if ((TIFR1 & (1 << ICF1)) && capture == 0)
    {
      byte temp = ICR1L;
      capture = (ICR1H << 8) | temp;
    }
  } while (micros() - start < 100);
  
  PORTD &= ~(1 << 4);          // set output low
  return capture;
}

size_t findSteps(bool positive, struct Step *results, size_t maxResults)
{
  byte amplitude = (positive) ? 5 : 250;
  analogWrite(refPin, amplitude);
  delay(100);      // wait 100ms for the output to stabilise
  unsigned int lastReading = 0;
  size_t numResults = 0;
  unsigned int stepSize = 0;        // 0 means not in a step
#ifdef DEBUG  
  Serial.print((positive) ? "pos " : "neg ");
#endif
  for (int i = 0; i < 50; ++i)
  {
    analogWrite(refPin, amplitude);
    delay(10);
    unsigned int currentReading = takeMeasurement(positive);
    unsigned int currentDiff = currentReading - lastReading;    // diff since start of possible step
    if (stepSize == 0)
    {
      // Not currently in a step
      if (i != 0 && currentReading != 0 && currentDiff == 0)
      {
        // Found the start of a possible step
        ++stepSize;
      }
      lastReading = currentReading;
    }
    else
    {
      if (currentDiff > 2 || i + 1 == 50)
      {
        // Step has endeed, so record it if it is big enough
        if (stepSize >= 2)
        {
          results->time = lastReading;
          results->amplitude = amplitude - 5;
          ++results;
          ++numResults;
          if (numResults == maxResults) break;
        }
        stepSize = 0;
        lastReading = currentReading;
      }
      else if (currentDiff == 0)
      {
        ++stepSize;
      }
    }
#ifdef DEBUG    
    if (i != 0) Serial.write(',');
    Serial.print(currentReading);
#endif    
    if (positive)
    {
      amplitude += 5;
    }
    else
    {
      amplitude -= 5;
    }
  }
#ifdef DEBUG  
  Serial.println();
#endif
  return numResults;
}

// Convert a number of clocks delay to a cable length in metres
float clocksToMetres(unsigned int clocks)
{
  float delayTime = (float)clocks/(float)F_CPU;    // delay in seconds
  return (delayTime * 3.0e8 * propagationFactor)/2.0;
}

// Diagnose the cable condition. We have the following common possibilities:
// 1. No cable connected, or a very short open-circuit cable connected.
//    In this case we should see the original positive step, very close to zero delay, with amplitude nearly 5V.
// 2. Direct short, or very short cable with shorted end.
//    In this case, we will not even see the original positive-going step, or it will have a very low amplitude.
// 3. Cable with open circuit.
//    We see the original positive step close to zero delay, and a further positive step going to nearly twice the amplitude later.
// 4. Cable with short circuit.
//    We see the original positive step close to zero delay, and a negative step going back down to nearly zero later.
// 5. Correctly terminated cable.
//    We see only the original positive step, with amplitudfe well below 5V.
void loop()
{
  Step posSteps[maxSteps], negSteps[maxSteps];
  size_t numPosSteps = findSteps(true, posSteps, maxSteps);
  size_t numNegSteps = findSteps(false, negSteps, maxSteps);
  if (numPosSteps == 0)
  {
    Serial.print("Direct short");
  }
  else if (numPosSteps == 1 && numNegSteps == 0)
  {
    if (posSteps[0].amplitude >= 200)
    {
      Serial.print("No cable connected");
    }
    else
    {
      Serial.print("Cable correctly terminated");
    }
  }
  else if (numPosSteps >= 2 && numNegSteps == 0)
  {
    Serial.print("Open at ");
    Serial.print(clocksToMetres(posSteps[1].time - posSteps[0].time), 1);
  }
  else if (numPosSteps == 1 && numNegSteps == 1)
  {
    Serial.print("Short at ");
    Serial.print(clocksToMetres(negSteps[0].time - posSteps[0].time), 1);
  }
  else
  {
    Serial.print("Failed to diagnose fault");
  }

#ifdef DEBUG
  Serial.print(" (pos=");
  for (size_t i = 0; i < numPosSteps; ++i)
  {
    if (i != 0)
    {
      Serial.write(',');
    }
    Serial.print(posSteps[i].time);
    Serial.write('|');
    Serial.print(posSteps[i].amplitude);    
  }
  Serial.print(" neg=");
  for (size_t i = 0; i < numNegSteps; ++i)
  {
    if (i != 0)
    {
      Serial.write(',');
    }
    Serial.print(negSteps[i].time);
    Serial.write('|');
    Serial.print(negSteps[i].amplitude);    
  }
  Serial.println(")");
#else
  Serial.println();
#endif
}

1 Like