adafruit / Adafruit_TinyUSB_Arduino

Arduino library for TinyUSB
MIT License
465 stars 120 forks source link

add mouse tremor with Butterworth filter #290

Closed hathach closed 1 year ago

hathach commented 1 year ago

@ladyada Probably need more tune up with either coeffs, filter algo but seems to work with shaking hands.

ladyada commented 1 year ago

@atmakersbill hiya is this something you can try? requires a USB Host Feather

ATMakersBill commented 1 year ago

I will be happy to try it - on a side-note, I just got a note from @jfedor2 about his HID-remapper project and he's got a pre-release that I just tested on the Feather - it works great! And the first thing I did was implement a filter for essential tremors!

I was just going to email you about it :-)

To make that work, I added log() functions (ln, log, and log10) to the remapper's expressions and then made a NASTY expression on the cursor_x and cursor_y (RPN is still evil even after all these years) -1 0x00010030 input_state gt 0x00010030 input_state -10 mul log -1 mul mul 0x00010030 input_state 1 gt 0x00010030 input_state 10 mul log mul add 300 mul

So, I'd love to see another way to skin this cat!.. I'll take a look in about 1/2 hour

Update: here's a link the pre-release w/feather_host support https://github.com/jfedor2/hid-remapper/releases/tag/r2023-02-17

ATMakersBill commented 1 year ago

So, I was able to get the filter to compile & work on the new Feather Host - that in itself is awesome!

As for the filter, it doesn't seem to do much attenuation of the tremors. That may be that I have the coefficients wrong, or that it's dependent on the DPI of the mouse? I'm not sure.

However, I took the structure of the sketch and stuck in the logarithm filter that I've used (using two SAMD21s) and that works exactly as expected. I've pasted in the changed parts - just replace filter_report with what's below.

#define PRESCALE 8.0  
#define POSTSCALE 1.5
#define DEADZONE 1.0
int8_t log_filter(int8_t val)
{
  if (val < -1*DEADZONE)
  {
    return (int8_t) (-1.0 * POSTSCALE * logf(-1.0 * PRESCALE * (float)val));   
  }
  else if (val > DEADZONE)
  {
    return (int8_t) (POSTSCALE * logf(PRESCALE * (float)val));   
  }
  else
  {
    return val;
  }
}

void filter_report(hid_mouse_report_t const* report) {
  int8_t old_x = report->x;
  int8_t old_y = report->y;

  hid_mouse_report_t filtered_report = *report;
  filtered_report.x = log_filter(old_x);
  filtered_report.y = log_filter(old_y);

  Serial.printf("%d,%d,%d,%d\n", old_x, filtered_report.x, old_y, filtered_report.y);
  usb_hid.sendReport(0, &filtered_report, sizeof(filtered_report));
}

All that said, I love that any type of filtering like this is possible.

ladyada commented 1 year ago

ok can you create a PR for these changes? @BlitzCityDIY may make a video showing this off and would be good to have the right behavior :)

ATMakersBill commented 1 year ago

Sure - do you want me to add the separate example file I created so the Butterworth is still around to be fiddled with? Do you want me to replace @hathach's code? Or I can just shoot Liz the .ino file - this really isn't a change to the library at all, right?

ladyada commented 1 year ago

you can submit as a new example file here

ATMakersBill commented 1 year ago

Pull request created https://github.com/adafruit/Adafruit_TinyUSB_Arduino/pull/291

ATMakersBill commented 1 year ago

Poop - I didn't see the hidden file to have it only test on RP2040... you want me to resubmit?