571/Repairs

From TekWiki
Jump to navigation Jump to search

It is possible with a logic analyzer to digitally capture the image the 571 is sending to its internal monitor. This can be used for distinguishing whether a problem is with the digital parts of the 571 versus the internal monitor. It can also be used for grabbing a screenshot in normal operation. It produces an image like this:
Tek 571 Screen Capture

Code:

// vcd_to_image converts a Value Change Dump (VCD) file from a logic analyzer into a PBM image file.
// The inputs to the logic analyzer are the HSYNC, VSYNC, and VIDEO signals intended for a monitor.
// A VCD file was captured from a Tektronix 571 using this command:
// sigrok-cli -d fx2lafw --config 'samplerate=24 MHz' -o cap.vcd -O vcd -C D0=VSYNC,D1=HSYNC,D2=VID --time 400
// Capturing for longer than the duration of a single frame improves image quality.
// The vcd_shorthand map needs to reflect the shorthand symbols in the VCD file.
// Compile: g++ -O2 -std=c++11 vcd_to_image.cc -o vcd_to_image
// Run: ./vcd_to_image < cap.vcd > cap.pbm

#include <cmath>
#include <iostream>
#include <map>
#include <regex>
#include <string>
#include <vector>

using namespace std;

map<string, string> vcd_shorthand = {
  {"!", "VSYNC"},
  {"\"", "HSYNC"},
  {"#", "VID"}
};

int tek_571_rows = 351;
int tek_571_cols = 640;
int image_upscaling = 8;  // Small values hurt legibility. Large values hurt efficiency. 
int ns_per_col = 100 / image_upscaling;
int image_rows = tek_571_rows * image_upscaling;
int image_cols = tek_571_cols * image_upscaling;
int vertical_spreading_amount = image_upscaling;

int main() {
  string line;
  
  // Skip the VCD file header lines.
  for (int line_number = 1; line_number <= 13; line_number++) {
    getline(std::cin, line);
  }

  int row_start_ns = -1;

  vector<vector<int>> frame_buf(image_rows, vector<int>(image_cols, 0));
  int white_start_col = -1;
  int row_number = -1;
  int frame_number = -1;
  bool seen_vsync_low = false;

  while (getline(std::cin, line)) {
    regex vcd_format("#([0-9]+) ([0-9])(.)");
    smatch s;
    regex_search(line, s, vcd_format);
    if (s.size() != 4) continue;

    int timestamp_ns = stoi(s[1]);
    int new_state = stoi(s[2]);
    string signal = vcd_shorthand[s[3]];
 
    if (signal == "VSYNC" && new_state == 1 && seen_vsync_low) {
      // The rising edge of the VSYNC signal marks the beginning of a frame.
      row_number = 0;
      frame_number++;
    }

    if (signal == "VSYNC" && new_state == 0) {
      seen_vsync_low = true;
    }

    if (signal == "HSYNC" && new_state == 1) {
      row_start_ns = timestamp_ns;
      row_number += image_upscaling;
    }

    if (frame_number < 0) continue;  // Ignore pixels if we haven't seen a VSYNC rising edge.

    if (signal == "VID" && new_state == 0) {  // We've reached the end of a run of white pixels.
      int black_start_col = round((double)(timestamp_ns - row_start_ns) / ns_per_col);    
      for (int col = white_start_col; col < black_start_col; col++) {
        for (int row = row_number; row < row_number + vertical_spreading_amount; row++) {
          frame_buf[row][col] = 1;
        }
      }
    }
    if (signal == "VID" && new_state == 1) {  // We've reached the end of a run of black pixels.
      white_start_col = round((double)(timestamp_ns - row_start_ns) / ns_per_col);
    }
  } 

  // Output a PBM file.
  cout << "P1" << endl;
  cout << image_cols << " " << image_rows << endl; 
  for (int i = 0; i < image_rows; i++) {
    for (int j = 0; j < image_cols; j++) {
      // Invert the intensity values so the PBM looks like a monochrome monitor.
      cout << ((frame_buf[i][j] == 0) ? 1 : 0) << " ";
    }
    cout << endl;
  }
  return 0;
}