1

So I'm doing a simple oscilloscope in C. It reads audio data from the output buffer (and drops buffer write counter when called so the buffer is refreshed). I tried making simple zero-cross triggering since most of the time users will see simple (sine, pulse, saw, triangle) waves but the best result I got with the code below is a wave that jumps back and forth for half of its cycle. What is wrong?

Signal that is fed in goes from -32768 to 32767 so zero is where it should be.

If you didn't understand what I meant you can see the video: click

Upd: Removed the code unrelated to triggering so all function may be understood easier.

extern Mused mused;

void update_oscillscope_view(GfxDomain *dest, const SDL_Rect* area)
{
  if (mused.output_buffer_counter >= OSC_SIZE * 12) {
    mused.output_buffer_counter = 0;
  }

  for (int x = 0; x < area->h * 0.5; x++) {
    //drawing a black rect so bevel is hidden when it is under oscilloscope
    gfx_line(domain,
             area->x,               area->y + 2 * x,
             area->x + area->w - 1, area->y + 2 * x,
             colors[COLOR_WAVETABLE_BACKGROUND]);
  }

  Sint32 sample, last_sample, scaled_sample;

  for (int i = 0; i < 2048; i++) {
    if (mused.output_buffer[i] < 0 && mused.output_buffer[i - 1] > 0) {
      //here comes the part with triggering

      if (i < OSC_SIZE * 2) {
        for (int x = i; x < area->w + i; ++x) {
          last_sample = scaled_sample;
          sample = (mused.output_buffer[2 * x] + mused.output_buffer[2 * x + 1]) / 2;

          if (sample      >  OSC_MAX_CLAMP) { sample      =  OSC_MAX_CLAMP; }
          if (sample      < -OSC_MAX_CLAMP) { sample      = -OSC_MAX_CLAMP; }
          if (last_sample >  OSC_MAX_CLAMP) { last_sample =  OSC_MAX_CLAMP; }
          if (last_sample < -OSC_MAX_CLAMP) { last_sample = -OSC_MAX_CLAMP; }

          scaled_sample = (sample * OSC_SIZE) / 32768;

          if(x != i) {
            gfx_line(domain,
                     area->x + x - i - 1, area->h / 2 + area->y + last_sample,
                     area->x + x - i,     area->h / 2 + area->y + scaled_sample,
                     colors[COLOR_WAVETABLE_SAMPLE]);
          }
        }
      }

      return;
    }
  }
}
ndim
  • 35,870
  • 12
  • 47
  • 57
LTVA
  • 11
  • 5
  • What is "_pure C_"? Any why the qualification? – Clifford Jan 23 '22 at 08:03
  • Pure C means that it would not accept and C++ things and I want algorithm that wouldn't use any external lib where some oscilloscope-specific functions are used. Here you just have an array of Sint's and you have to find exact array member from which oscilloscope must start drawing new frame. Also iirc it is C99. – LTVA Jan 23 '22 at 08:10
  • "_that jumps back and forth for half of its cycle._" I have not studied the code yet since viewing it on a phone screen is not fun, but it sounds from the description that you are not qualifying your trigger with rising or falling edge. A waveform will cross zero in alternate directions each half cycle. You need to trigger on rising or falling but not both. – Clifford Jan 23 '22 at 08:10
  • `if(mused.output_buffer[i] <= 0 && mused.output_buffer[i - 1] > 0)` This is falling edge if I understand correctly. If i write `if(mused.output_buffer[i] < 0 && mused.output_buffer[i - 1] > 0)` result is the same. – LTVA Jan 23 '22 at 08:11
  • Also as you may see from the vid it does not just swing between rising and falling edge, it goes through all intermediate positions and why it does so is a complete mystery for me. – LTVA Jan 23 '22 at 08:13
  • 1
    Then the qualification is unnecessary, since you tagg d it C and not C++. If you don't want external libraries, then remove the SDL tag - it kind of invites it. There is nothing "impure" about using libraries, but your simple problem hardly suggests thier use in any case. The term is ambiguous and distracting and adds little or nothing to the question. Best in any case not to attempt to constrain how anyone would answer a question, answers unacceptable you simply do not accept - they may be useful to the community or contain useful ideas nonetheless. – Clifford Jan 23 '22 at 08:18
  • 1
    Yeah thanks, I removed tag. My first Stackoverflow question in a years... – LTVA Jan 23 '22 at 08:24
  • 1
    As I said I am yet to study the code. There seems to be rather too much of it for a simple zero-cross test. Also it would be more generally useful to have a "trigger-level cross" test, with the trigger level set to zero, you'd get what you need, but with much greater flexibility. That is after all how a real scope works. – Clifford Jan 23 '22 at 08:24
  • 1
    Quick look, it is not obvious to me where the trigger code is. Perhaps describe the code in more detail (or add comments ;-) ). I'd expect the code to wait until the trigger condition was met then start plotting from that point. I would also expect (from a design perspective) that to be separate from the "view update". – Clifford Jan 23 '22 at 08:35
  • Search by word "triggering". I basically made 2 variants: if it finds trigger point then it starts from it, else it starts from beginning of the buffer. – LTVA Jan 23 '22 at 09:04
  • My failure to spot the relevant code was entirely due to trying to view it on a phone! See it now - apologies. – Clifford Jan 23 '22 at 13:15
  • I would suggest setting a breakpoint at the trigger to see if it is a) triggering and b) triggering correctly (clearly it is not doing one of those things). The debugger is your friend - even if it is GDB ;-) – Clifford Jan 23 '22 at 13:27
  • It is surely triggering because otherwise it wouldn't display anything at all (see the code, I updated it). Sadly I have only mingw with GCC compiler and notepad++, so my only debug is console and only breakpoint is delay. Your big answer disappeared -- I tried to do `if(mused.output_buffer[i] <= TRIGGER_LEVEL && mused.output_buffer[i - 1] >= TRIGGER_LEVEL)` but the result is the same. I will check what value it sees when triggering. – LTVA Jan 23 '22 at 13:31
  • `[DEBUG] Trigger values: [i-1]: -571, [i]: -645` but ```int s1 = mused.output_buffer[i]; int s2 = mused.output_buffer[i - 1]; if((s1 < 0) && (s2 > 0) && (abs(s1 - s2) < 1000)) { //here comes the part with triggering if(mused.output_buffer[i] != 0) { debug("Trigger values: [i-1]: %d, [i]: %d", mused.output_buffer[i - 1], mused.output_buffer[i]); }``` – LTVA Jan 23 '22 at 14:00
  • If you are using MigGW then you can use GDB. Command line GDB is a bit painful but it can be used "visually" in IDE's such as Code::Blocks, Eclipse or Dev-C++ and a number of stand-alone GDB based debuggers such as https://www.gdbgui.com/ or https://sourceware.org/insight/index.php. There are really no excuses for not using a debugger one way or another - that is doing it the hard way. The debug information in the comment should be added to the question itself. – Clifford Jan 23 '22 at 14:41
  • The problem with debugging with code, if you have to get your debug code right (and in the right place outputting the right information). If yo are already writing bugs in the application code, what is the likelihood of flaws in the debug code too? In a debugger you can see _everything_ at _any time_ - not just what you chose to look at at the place you chose to look at it. – Clifford Jan 23 '22 at 14:46
  • Is the input a real or generated signal? Signal noise could result in a false trigger at the rising edge crossing. Testing samples at `i` and say `i - 20` or some proportion of the sample rate might be more "stable". – Clifford Jan 23 '22 at 14:58
  • It is generated signal so no problem with that. – LTVA Jan 24 '22 at 14:03

1 Answers1

0

During debugging, I simplified the code until it started working. Thanks Clifford.

I found a trigger index i (let's say it is array index 300). Modified it so that the oscilloscope was drawing lines from [(2 * i) + offset] to [(2 * i + 1) + offset], thus an incorrect picture was formed.

I used (2 * i), because I wanted long waves to fit into oscilloscope. I replaced it with drawing from [i + offset] to [i + 1 + offset] and that solved a problem.

Afterwards, I implemented "horizontal scale 0.5x properly.

The output waveform still jumps a little, but overall it holds it in place.

netskink
  • 4,033
  • 2
  • 34
  • 46
LTVA
  • 11
  • 5
  • 2
    Good to know, but this does not constitute an answer - either add it as a comment or include the solution to make in an answer. Answering and even accepting your own answer is valid. – Clifford Jan 23 '22 at 14:49
  • It says I have 17 hours to wait before I can accept my own answer – LTVA Jan 24 '22 at 14:02
  • 1
    You missed my point; this is not an answer. We don't know how you solved it. If you were to provide that, it may be that it may inspire an even better solution, to your benefit. – Clifford Jan 24 '22 at 22:50
  • 1
    Ok i tried to explain what exactly was wrong in my design. – LTVA Jan 25 '22 at 07:11