Close

It Works!

A project log for Oscilloscope Vector Game Display

Raspberry Pi (or Linux) vector display using audio output

ted-yapoTed Yapo 02/08/2018 at 16:550 Comments

The latest code in GitHub works pretty well.  Here's the bouncing_ball example displayed on my DS1054Z:

This example refreshes the display between 85-112 fps, although the software limits the position updates to 60 fps (this is adjustable).  The video above was taken with the inputs set to AC coupling to simulate an AC-coupled soundcard.  You can see the compensation dots along the sides of the image.  Also visible are a bunch of "noise" dots - these are caused by the audio adapter upsampling the output.  This is particularly annoying because the upsampling algorithm appears to use a sinc filter, which would be great if this were really audio.  For a time domain display like this one, a Gaussian filter would be much better because it minimizes rise and fall times with zero overshoot.  I'll make another log about what this particular audio adapter does and why it's so annoying.

The ac-coupling compensation isn't perfect because the code doesn't predict where the oversampling points will be inserted by the audio adapter.  This causes the image to drift slightly from frame to frame.  Although the effect is noticeable, it doesn't seem like a big deal.  Again, with a DC-coupled audio adapter, it doesn't matter.

Without the AC-coupling compensation, the code maintains a fairly steady 114 fps update on the example program.

So, the code is fairly complete as a simple low-level C driver.  Here's the meat of the code for the above example:

int main()
{
  display_params_t display_params;

  /* set the display parameters and initialize the display */
  /* display_params.pcm_device = "default"; */
  display_params.pcm_device = "hw:CARD=Device,DEV=0";
  display_params.frame_rate = 60;
  display_params.sample_rate = 48000;
  display_params.slew = 10;
  display_params.ac_coupling = 1;
  InitDisplay(&display_params);

  const int n_balls = 10;
  ball balls[n_balls];
  create_random_balls(balls, n_balls);

  float dt = 1.; /* simulation timestep */
  uint32_t update_count = 0;
  while(1){

    update_positions(balls, n_balls, dt);

    /* initialize a display list */
    DisplayList dl;
    InitDisplayList(&dl);

    /* render balls into display list */
    for (int i=0; i<n_balls; ++i){
      draw_circle(&dl, balls[i].cx, balls[i].cy, balls[i].r, 20);
    }

    /* update the display, and free the display list */
    int limit_fps = 1;
    UpdateDisplay(&display_params, &dl, limit_fps);
    FreeDisplayList(&dl);

    /* periodically print the display drawing rate */
    update_count++;
    if (!(update_count % 32)){
      fprintf(stderr, "%4.1f fps\n", GetDisplayFPS(&display_params));
    }
  }

  CloseDisplay(&display_params);
  return 0;
}

 The drawing interface is rather crude, requiring you to fill a display list of line segments for each frame.  It works, but it's a little tedious to program.  I'm working on a C++ layer on top that exposes a set of primitives and transformations to make programming a game easier.

So far, I've mostly been developing on my desktop linux box. Now I have to set up some room somewhere to experiment with a Raspberry Pi, an oscilloscope, and a USB retro game controller...

Discussions