How to add a trigger to an oscilloscope program?

I'm working on a very bare bones oscilloscope program using the minim library:

import ddf.minim.*;

Minim minim;
AudioInput in;
color white;

void setup()
{
  size(512, 100, P2D);
  white = color(255);
  colorMode(HSB,100);
  minim = new Minim(this);
  minim.debugOn();

  in = minim.getLineIn(Minim.STEREO, 512);
  background(0);
}

void draw()
{
  background(0);
  // draw the waveforms
  for(int i = 0; i < in.bufferSize() - 1; i++)
  {
    stroke((1+in.left.get(i))*50,100,100);
    line(i, 50 + in.left.get(i)*50, i, 50 + in.left.get(i+1)*50);
  }
}


void stop()
{
  in.close();
  minim.stop();
  super.stop();
}

My question is, how do I make the waveform trigger/stabilize like an actual oscilloscope? I'm trying to figure that out so I can make the waveform stay at a certain spot so it doesn't jump around. Basically keep the waveform stable at all times.

Thanks for your help.

Answers

  • You need to manage your in.bufferSize() as it has the current data to display. You could create a container made of time-amplitude pair values. Then, one way is to trigger on the leading edge or falling edge of a signal. This can be easily accomplish using a threshold applied to your values. When you find a signal going above the threshold, then you display that position at the center of the screen and the other values should be shifted up or down accordingly.

    There is an oscilloscope library in case you don't know about it.

    Kf

  • edited July 15

    I do know about the library, but I'm trying to make it bare bones as I said. I tried making an if statement where when in.left is rising it resets the waveform buffer but that did nothing. I don't really know how to make the position at the center of the screen. I don't know how I can reset the waveform buffer. I tried that if statement and then it would make i = 0 but that didn't do anything either. I'd be nice if you could point me in the right direction with example code. Thanks.

  • Doing something like this requires to know about the nature of your data, and exactly how you envision your trigger to work. Here I have implemented the concept based on leading edge crossing. White line is based line. Blue line is the current threshold definition. The data from minim is in a range of -1..+1. When displaying, I multiply this data by a normalization factor which I defined as height/2. I set my threshold to 0.4 based on observations from the printInfo() function in my code below. Green line shows the data points.

    The concept is this. If the wave crosses the threshold, capture this crossing point. Set this crossing point as the center of the display (across the sketch width) which means all the data is shifted either to the left or to the right. Because the display width is 512 and the data buffer is 512, if the data is shifted with respect to the leading edge crossing, then it is natural that either the left end or right end will have no data... these data bins were set to zero (Notice this is because I copy the original data to a temporal array before I reset those containers to zero).

    I removed the hsb color definition, added an inversion on the y axis, and instead of calling background(), I am using a fill with alpha to slowly erased my sketch content. So this creates an effect of memory on the display where you can see that previous waves are attenuated and removed after few iterations, and not immediately in the case you call background() function.

    As in an oscilloscope, I work with the time-amplitude domain. However, the examples of the minim is based on a buffer that provides amplitude only, no time information. Then What I do is that I take the index of the array and I set them to be my time. The amplitude from minim is used as my amplitude data, so I get two internal arrays: time and amplitude which store the data to display.

    Kf

    //REFERENCE: https://forum.processing.org/two/discussion/28074/how-to-add-a-trigger-to-an-oscilloscope-program#latest // //Title: Oscilloscope trigger based on the leading edge. //Author: Kfrajer, July 15, 2018

    import ddf.minim.*;
    
    Minim minim;
    AudioInput in;
    color white;
    
    WavePacket wp;
    
    void setup()
    {
      size(512, 200, P2D);
      white = color(255);
    
      minim = new Minim(this);
      minim.debugOn();
    
      in = minim.getLineIn(Minim.STEREO, 512);
    
      wp=new WavePacket(in.bufferSize());
    
      background(0);
    }
    
    void draw()
    {
      //clone wafeform
      wp.loadData(in);
      wp.printInfo();
      wp.searchHThresholdCrossing();
      wp.shiftWaveBasedOnHThreshold();
    
      //This next enables drawing only of those signals above the threshold
      //Remove or comment out if you want to see all the signals
      if (!wp.thresholdFound()) {
        return;
      }
    
      fill(0, 60);
      rect(0, 0, width, height);
      translate(0, height/2);
      scale(1, -1);
      wp.drawBaseLine();
      wp.display();
      wp.drawThresold(); 
    }
    
    
    void stop()
    {
      in.close();
      minim.stop();
      super.stop();
    }
    
    class WavePacket {
    
      final float NORMFACTOR=height/2;
    
      int len;
      FloatList time;
      FloatList amp;
    
      float highThreshold;
      float idxTimeFirstCrossing;
    
      WavePacket(int n) {
        len=n;
        time=new FloatList(len);
        amp=new FloatList(len);
    
        highThreshold=0.4;  //Chosen based on initial observations from printInfo()
    
        println("Size set to ", len);
      }
    
      void loadData(AudioInput in) {    
    
        for (int i = 0; i < in.bufferSize(); i++) {
          time.set(i, i);
          amp.set(i, in.left.get(i));
        }
      }
    
      //It finds index when the curve crosses the threshold for the first time
      void searchHThresholdCrossing() {
    
        idxTimeFirstCrossing = -1;  //Resets
    
        for (int i = 0; i < in.bufferSize(); i++) {
          if (amp.get(i)>=highThreshold) {
            idxTimeFirstCrossing=i;
            return;
          }
        }
      }
    
      boolean thresholdFound() {
        return idxTimeFirstCrossing>=0;
      }
    
      void shiftWaveBasedOnHThreshold() {
    
        int middlePoint=(len/2);
        //If wave did not cross threshold, then define delta time correction as zero
        int deltaTime = thresholdFound() ? int(middlePoint-idxTimeFirstCrossing) : 0;
    
        FloatList tempX=new FloatList();
        FloatList tempY=new FloatList();
    
        //Create temporal containers with shifted tinme data 
        for (int i = 0; i < in.bufferSize(); i++) {
          tempX.set(i, time.get(i)+deltaTime);
          tempY.set(i, amp.get(i));
        }
    
    
        //Zero original containers
        for (int i = 0; i < in.bufferSize() - 1; i++) {
          time.set(i, 0);
          amp.set(i, 0);
        }
    
        //Search for first index
        int idx=0;
        for (; idx < in.bufferSize(); idx++) {
    
          if (tempX.get(idx)<0)
            continue;
    
          break;
        }
    
        //println("Threshold:", highThreshold, "idxTimeFirstCrossing", idxTimeFirstCrossing, "idx:", idx);
    
        //Populate arrays with shifted data using temporal holders
        for (int j=idx; j<len; j++) {
    
          //println(idx);
          //println(int(tempX.get(j)), tempY.get(j));
    
          time.set(int(tempX.get(j)), tempX.get(j));
          amp.set(int(tempX.get(j)), tempY.get(j));
        }
      }
    
      void display() {
        // draw the waveforms
        stroke(0, 250, 10);  //Green
        strokeWeight(2);
        for (int i = 0; i < in.bufferSize(); i++) {
    
          point(i, amp.get(i)*NORMFACTOR);
        }
      }
    
      void drawThresold() {
        stroke(0, 0, 200);  //Blue
        strokeWeight(1);
    
        int step=2;
        for (int k=0; k<len-step*2; k+=step*2)
          line(k, highThreshold*NORMFACTOR, k+step, highThreshold*NORMFACTOR);
      }
    
      void drawBaseLine() {
        stroke(250);  //White
        strokeWeight(1);
        line(0, 0, width, 0);
      }
    
    
      void printInfo() {
        println("Report range: ", amp.min(), amp.max());
        println("Report Norm Factor", NORMFACTOR);
      }
    }
    
  • There also is a new forum

Sign In or Register to comment.